1from __future__ import print_function
2import os, logging, pprint
3
4log = logging.getLogger(__name__)
5if not os.environ.get("PYOPENGL_PLATFORM"):
6    os.environ["PYOPENGL_PLATFORM"] = "egl"
7if "DISPLAY" in os.environ:
8    del os.environ["DISPLAY"]
9import logging, contextlib
10from functools import wraps
11
12# from OpenGL.GL import *
13from OpenGL.EGL import *
14from OpenGL.EGL import debug
15from OpenGL import arrays
16from OpenGL.EGL import gbmdevice
17from OpenGL.EGL.MESA import platform_gbm
18from OpenGL.EGL.EXT import platform_device, platform_base, device_base
19
20API_MAP = {
21    EGL_OPENGL_BIT: EGL_OPENGL_API,
22    EGL_OPENGL_ES2_BIT: EGL_OPENGL_ES_API,
23    EGL_OPENGL_ES_BIT: EGL_OPENGL_ES_API,
24}
25
26
27def platformDisplay(device):
28    """Get platform display from device specifier
29
30    device -- EGLDeviceEXT, gbm card* path, or gbm card* ordinal (index)
31
32    returns display, created_device (or None if passed in)
33    raises RuntimeError if we can't create the display
34    """
35    created_device = display = None
36    if isinstance(device, (str, int)):
37        created_device = device = gbmdevice.open_device(device)
38    if eglGetPlatformDisplay:
39        display = eglGetPlatformDisplay(
40            platform_device.EGL_PLATFORM_DEVICE_EXT
41            if isinstance(device, EGLDeviceEXT)
42            else platform_gbm.EGL_PLATFORM_GBM_MESA,
43            device,
44            ctypes.c_void_p(0),
45        )
46        if display == EGL_NO_DISPLAY:
47            raise RuntimeError("Unable to create EGL display on %s" % (display))
48    else:
49        raise RuntimeError("eglGetPlatformDisplay has no implementation")
50    if not created_device:
51        try:
52            name = get_device_name(device)
53            if name is not None:
54                log.debug("DRM Name: %s", name)
55        except EGLError:
56            log.debug("Unable to retrieve the DRM name")
57    return display, created_device
58
59
60def gbmPlatformSurface(display, config, platform_device, width, height):
61    """Create a GBM platform surface for display with config on platform_device
62
63    returns egl_surface, gbm_surface
64    """
65    visual = EGLint()
66    eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, visual)
67    platform_surface = gbmdevice.create_surface(
68        platform_device,
69        width,
70        height,
71        format=visual.value,
72        flags=gbmdevice.GBM_BO_USE_RENDERING,
73    )
74    if not platform_surface:
75        raise RuntimeError("Unable to allocate a gbm surface")
76    surface = eglCreatePlatformWindowSurface(display, config, platform_surface, None)
77    if surface == EGL_NO_SURFACE:
78        log.error("Failed to create the EGL surface on the GBM surface")
79        raise RuntimeError("Platform window surface creation failure")
80    return surface, platform_surface
81
82
83def choose_config(display, attributes):
84    """utility to choose config for the display based on attributes"""
85    num_configs = EGLint()
86    configs = (EGLConfig * 1)()
87    local_attributes = arrays.GLintArray.asArray(attributes)
88    success = eglChooseConfig(display, local_attributes, configs, 1, num_configs)
89    if not success:
90        raise NoConfig("Unable to complete config filtering", attributes)
91    if not num_configs:
92        raise NoConfig(
93            "No compatible configs found", attributes,
94        )
95    return configs[0]
96
97
98@contextlib.contextmanager
99def egl_context(
100    width=256,
101    height=256,
102    api=EGL_OPENGL_BIT,
103    attributes=(
104        EGL_BLUE_SIZE,
105        8,
106        EGL_RED_SIZE,
107        8,
108        EGL_GREEN_SIZE,
109        8,
110        EGL_DEPTH_SIZE,
111        24,
112        EGL_COLOR_BUFFER_TYPE,
113        EGL_RGB_BUFFER,
114        # EGL_CONFIG_CAVEAT, EGL_NONE, # Don't allow slow/non-conformant
115    ),
116    pbuffer=False,
117    device=None,
118    output="output.ppm",
119):
120    """Setup a context for rendering"""
121    major, minor = EGLint(), EGLint()
122    created_device = platform_surface = surface = None
123    if device is None:
124        display = eglGetDisplay(EGL_DEFAULT_DISPLAY)
125        if display == EGL_NO_DISPLAY:
126            raise RuntimeError(EGL_NO_DISPLAY, "Could not create default display")
127    else:
128        display, created_device = platformDisplay(device)
129    try:
130        # print("Display: %s"%(display.address,))
131        try:
132            eglInitialize(display, major, minor)
133        except EGLError as err:
134            log.warning("eglInitilise failure on %s: %s", display, err.err)
135            raise NoEGLSupport(display)
136        log.debug(
137            "Available configs:\n%s",
138            debug.format_debug_configs(debug.debug_configs(display)),
139        )
140
141        # for config in configs[:num_configs.value]:
142        #     log.debug("Config: %s",pprint.pformat(debug.debug_config(display,config)))
143        local_attributes = list(attributes[:])
144        if pbuffer:
145            local_attributes.extend(
146                [EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,]
147            )
148        else:
149            local_attributes.extend(
150                [EGL_SURFACE_TYPE, EGL_WINDOW_BIT,]
151            )
152        local_attributes.extend(
153            [EGL_CONFORMANT, api, EGL_NONE,]  # end of list
154        )
155        config = choose_config(display, local_attributes,)
156        log.debug(
157            "Selected config:\n%s",
158            debug.format_debug_configs(debug.debug_configs(display, configs=[config])),
159        )
160        surface_attributes = [
161            EGL_WIDTH,
162            width,
163            EGL_HEIGHT,
164            height,
165            EGL_NONE,
166        ]
167        if pbuffer:
168            surface = eglCreatePbufferSurface(display, config, surface_attributes,)
169        else:
170            surface, platform_surface = gbmPlatformSurface(
171                display, config, created_device, width, height
172            )
173        eglBindAPI(API_MAP[api])
174        ctx = eglCreateContext(display, config, EGL_NO_CONTEXT, None)
175        if ctx == EGL_NO_CONTEXT:
176            raise RuntimeError("Unable to create context")
177        eglMakeCurrent(display, surface, surface, ctx)
178        yield display, ctx, surface
179        if output:
180            log.debug("Doing readpixels for writing buffer")
181            from OpenGL import arrays
182
183            content = arrays.GLubyteArray.zeros((width, height, 3))
184            if api == EGL_OPENGL_BIT:
185                from OpenGL.GL import glReadPixels, GL_UNSIGNED_BYTE, GL_RGB
186            elif api == EGL_OPENGL_ES3_BIT:
187                from OpenGL.GLES3 import glReadPixels, GL_UNSIGNED_BYTE, GL_RGB
188            elif api == EGL_OPENGL_ES2_BIT:
189                from OpenGL.GLES2 import glReadPixels, GL_UNSIGNED_BYTE, GL_RGB
190            elif api == EGL_OPENGL_ES_BIT:
191                from OpenGL.GLES1 import glReadPixels, GL_UNSIGNED_BYTE, GL_RGB
192            content = glReadPixels(
193                0, 0, width, height, GL_RGB, type=GL_UNSIGNED_BYTE, array=content
194            )
195
196            debug.write_ppm(content, output)
197            # glFinish()
198    finally:
199        if display:
200            eglMakeCurrent(display, None, None, None)
201            if surface:
202                eglDestroySurface(display, surface)
203            eglTerminate(display)
204        if platform_surface:
205            gbmdevice.gbm.gbm_surface_destroy(platform_surface)
206        if created_device:
207            gbmdevice.close_device(created_device)
208
209
210class NoEGLSupport(Exception):
211    """Raised if we could not initialise an egl context"""
212
213
214class NoConfig(Exception):
215    """Raised if we did not find any configs"""
216
217
218def debug_info(setup):
219    from OpenGL.GL import (
220        glClearColor,
221        glClear,
222        GL_COLOR_BUFFER_BIT,
223        GL_DEPTH_BUFFER_BIT,
224        glGetString,
225        GL_VENDOR,
226        GL_EXTENSIONS,
227        glFinish,
228    )
229
230    display, ctx, surface = setup
231    glClearColor(1.0, 1.0, 1.0, 1.0)
232    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
233    log.info("Vendor: %s", glGetString(GL_VENDOR))
234    log.info("Extensions: %s", glGetString(GL_EXTENSIONS))
235    glFinish()
236
237def get_device_name(device):
238    """Try to get the display's DRM device name
239
240    This is almost certainly not going to work on
241    anything other than Linux
242    """
243    from OpenGL.EGL.EXT.device_query import (
244        eglQueryDeviceStringEXT,
245    )
246    from OpenGL.EGL.EXT.device_drm import (
247        EGL_DRM_DEVICE_FILE_EXT,
248    )
249    if eglQueryDeviceStringEXT:
250        name = eglQueryDeviceStringEXT(
251            device,
252            EGL_DRM_DEVICE_FILE_EXT
253        )
254        return name.decode('ascii',errors='ignore')
255    return None
256
257
258def main():
259    # NOTE: having two different implementations here is
260    # likely somewhat broken due to the
261    # OpenGL functions having retrieved their
262    # function pointers during the first pass and then
263    # trying to run them against the second
264    for device in sorted(device_base.egl_get_devices(), key=lambda x: x.address):
265        log.info("Starting tests with: %s", device)
266        try:
267            with egl_context(device=device, pbuffer=True) as setup:
268                debug_info(setup)
269        except (NoEGLSupport, NoConfig) as err:
270            log.info("Cannot configure: %s", err)
271        except (EGLError, RuntimeError):
272            log.exception("Failed during: %s", device)
273    for device in gbmdevice.enumerate_devices():
274        log.info("Starting tests with: %s", device)
275        try:
276            with egl_context(device=device, pbuffer=False) as setup:
277                debug_info(setup)
278        except (NoEGLSupport, NoConfig) as err:
279            log.info("Cannot configure: %s", err)
280        except (EGLError, RuntimeError):
281            log.exception("Failed during: %s", device)
282
283
284if __name__ == "__main__":
285    logging.basicConfig(level=logging.DEBUG)
286    main()
287