1 /**************************************************************************
2  *
3  * Copyright 2008 VMware, Inc.
4  * Copyright 2009-2010 Chia-I Wu <olvaffe@gmail.com>
5  * Copyright 2010-2011 LunarG, Inc.
6  * All Rights Reserved.
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining a
9  * copy of this software and associated documentation files (the
10  * "Software"), to deal in the Software without restriction, including
11  * without limitation the rights to use, copy, modify, merge, publish,
12  * distribute, sub license, and/or sell copies of the Software, and to
13  * permit persons to whom the Software is furnished to do so, subject to
14  * the following conditions:
15  *
16  * The above copyright notice and this permission notice (including the
17  * next paragraph) shall be included in all copies or substantial portions
18  * of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
23  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26  * DEALINGS IN THE SOFTWARE.
27  *
28  **************************************************************************/
29 
30 
31 /**
32  * Functions related to EGLDisplay.
33  */
34 
35 #include <assert.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #ifdef _WIN32
39 #include <io.h>
40 #else
41 #include <unistd.h>
42 #endif
43 #include <fcntl.h>
44 #include "c11/threads.h"
45 #include "util/macros.h"
46 #include "util/os_file.h"
47 #include "util/u_atomic.h"
48 
49 #include "eglcontext.h"
50 #include "eglcurrent.h"
51 #include "eglsurface.h"
52 #include "egldevice.h"
53 #include "egldisplay.h"
54 #include "egldriver.h"
55 #include "eglglobals.h"
56 #include "egllog.h"
57 #include "eglimage.h"
58 #include "eglsync.h"
59 
60 /* Includes for _eglNativePlatformDetectNativeDisplay */
61 #ifdef HAVE_WAYLAND_PLATFORM
62 #include <wayland-client.h>
63 #endif
64 #ifdef HAVE_DRM_PLATFORM
65 #include <gbm.h>
66 #endif
67 #ifdef HAVE_WINDOWS_PLATFORM
68 #include <windows.h>
69 #endif
70 
71 
72 /**
73  * Map build-system platform names to platform types.
74  */
75 static const struct {
76    _EGLPlatformType platform;
77    const char *name;
78 } egl_platforms[] = {
79    { _EGL_PLATFORM_X11, "x11" },
80    { _EGL_PLATFORM_XCB, "xcb" },
81    { _EGL_PLATFORM_WAYLAND, "wayland" },
82    { _EGL_PLATFORM_DRM, "drm" },
83    { _EGL_PLATFORM_ANDROID, "android" },
84    { _EGL_PLATFORM_HAIKU, "haiku" },
85    { _EGL_PLATFORM_SURFACELESS, "surfaceless" },
86    { _EGL_PLATFORM_DEVICE, "device" },
87    { _EGL_PLATFORM_WINDOWS, "windows" },
88 };
89 
90 
91 /**
92  * Return the native platform by parsing EGL_PLATFORM.
93  */
94 static _EGLPlatformType
_eglGetNativePlatformFromEnv(void)95 _eglGetNativePlatformFromEnv(void)
96 {
97    _EGLPlatformType plat = _EGL_INVALID_PLATFORM;
98    const char *plat_name;
99    EGLint i;
100 
101    static_assert(ARRAY_SIZE(egl_platforms) == _EGL_NUM_PLATFORMS,
102                  "Missing platform");
103 
104    plat_name = getenv("EGL_PLATFORM");
105    /* try deprecated env variable */
106    if (!plat_name || !plat_name[0])
107       plat_name = getenv("EGL_DISPLAY");
108    if (!plat_name || !plat_name[0])
109       return _EGL_INVALID_PLATFORM;
110 
111    for (i = 0; i < ARRAY_SIZE(egl_platforms); i++) {
112       if (strcmp(egl_platforms[i].name, plat_name) == 0) {
113          plat = egl_platforms[i].platform;
114          break;
115       }
116    }
117 
118    if (plat == _EGL_INVALID_PLATFORM)
119       _eglLog(_EGL_WARNING, "invalid EGL_PLATFORM given");
120 
121    return plat;
122 }
123 
124 
125 /**
126  * Try detecting native platform with the help of native display characteristcs.
127  */
128 static _EGLPlatformType
_eglNativePlatformDetectNativeDisplay(void * nativeDisplay)129 _eglNativePlatformDetectNativeDisplay(void *nativeDisplay)
130 {
131    if (nativeDisplay == EGL_DEFAULT_DISPLAY)
132       return _EGL_INVALID_PLATFORM;
133 
134 #ifdef HAVE_WINDOWS_PLATFORM
135    if (GetObjectType(nativeDisplay) == OBJ_DC)
136       return _EGL_PLATFORM_WINDOWS;
137 #endif
138 
139 #if defined(HAVE_WAYLAND_PLATFORM) || defined(HAVE_DRM_PLATFORM)
140    if (_eglPointerIsDereferencable(nativeDisplay)) {
141       void *first_pointer = *(void **) nativeDisplay;
142 
143 #ifdef HAVE_WAYLAND_PLATFORM
144       /* wl_display is a wl_proxy, which is a wl_object.
145        * wl_object's first element points to the interfacetype. */
146       if (first_pointer == &wl_display_interface)
147          return _EGL_PLATFORM_WAYLAND;
148 #endif
149 
150 #ifdef HAVE_DRM_PLATFORM
151       /* gbm has a pointer to its constructor as first element. */
152       if (first_pointer == gbm_create_device)
153          return _EGL_PLATFORM_DRM;
154 #endif
155    }
156 #endif
157 
158    return _EGL_INVALID_PLATFORM;
159 }
160 
161 
162 /**
163  * Return the native platform.  It is the platform of the EGL native types.
164  */
165 _EGLPlatformType
_eglGetNativePlatform(void * nativeDisplay)166 _eglGetNativePlatform(void *nativeDisplay)
167 {
168    _EGLPlatformType detected_platform = _eglGetNativePlatformFromEnv();
169    const char *detection_method = "environment";
170 
171    if (detected_platform == _EGL_INVALID_PLATFORM) {
172       detected_platform = _eglNativePlatformDetectNativeDisplay(nativeDisplay);
173       detection_method = "autodetected";
174    }
175 
176    if (detected_platform == _EGL_INVALID_PLATFORM) {
177       detected_platform = _EGL_NATIVE_PLATFORM;
178       detection_method = "build-time configuration";
179    }
180 
181    _eglLog(_EGL_DEBUG, "Native platform type: %s (%s)",
182            egl_platforms[detected_platform].name, detection_method);
183 
184    return detected_platform;
185 }
186 
187 
188 /**
189  * Finish display management.
190  */
191 void
_eglFiniDisplay(void)192 _eglFiniDisplay(void)
193 {
194    _EGLDisplay *dispList, *disp;
195 
196    /* atexit function is called with global mutex locked */
197    dispList = _eglGlobal.DisplayList;
198    while (dispList) {
199       EGLint i;
200 
201       /* pop list head */
202       disp = dispList;
203       dispList = dispList->Next;
204 
205       for (i = 0; i < _EGL_NUM_RESOURCES; i++) {
206          if (disp->ResourceLists[i]) {
207             _eglLog(_EGL_DEBUG, "Display %p is destroyed with resources", disp);
208             break;
209          }
210       }
211 
212 
213       /* The fcntl() code in _eglGetDeviceDisplay() ensures that valid fd >= 3,
214        * and invalid one is 0.
215        */
216       if (disp->Options.fd)
217          close(disp->Options.fd);
218 
219       free(disp->Options.Attribs);
220       free(disp);
221    }
222    _eglGlobal.DisplayList = NULL;
223 }
224 
225 static EGLBoolean
_eglSameAttribs(const EGLAttrib * a,const EGLAttrib * b)226 _eglSameAttribs(const EGLAttrib *a, const EGLAttrib *b)
227 {
228    size_t na = _eglNumAttribs(a);
229    size_t nb = _eglNumAttribs(b);
230 
231    /* different numbers of attributes must be different */
232    if (na != nb)
233       return EGL_FALSE;
234 
235    /* both lists NULL are the same */
236    if (!a && !b)
237       return EGL_TRUE;
238 
239    /* otherwise, compare the lists */
240    return memcmp(a, b, na * sizeof(a[0])) == 0 ? EGL_TRUE : EGL_FALSE;
241 }
242 
243 /**
244  * Find the display corresponding to the specified native display, or create a
245  * new one. EGL 1.5 says:
246  *
247  *     Multiple calls made to eglGetPlatformDisplay with the same parameters
248  *     will return the same EGLDisplay handle.
249  *
250  * We read this extremely strictly, and treat a call with NULL attribs as
251  * different from a call with attribs only equal to { EGL_NONE }. Similarly
252  * we do not sort the attribute list, so even if all attribute _values_ are
253  * identical, different attribute orders will be considered different
254  * parameters.
255  */
256 _EGLDisplay *
_eglFindDisplay(_EGLPlatformType plat,void * plat_dpy,const EGLAttrib * attrib_list)257 _eglFindDisplay(_EGLPlatformType plat, void *plat_dpy,
258                 const EGLAttrib *attrib_list)
259 {
260    _EGLDisplay *disp;
261    size_t num_attribs;
262 
263    if (plat == _EGL_INVALID_PLATFORM)
264       return NULL;
265 
266    mtx_lock(_eglGlobal.Mutex);
267 
268    /* search the display list first */
269    for (disp = _eglGlobal.DisplayList; disp; disp = disp->Next) {
270       if (disp->Platform == plat && disp->PlatformDisplay == plat_dpy &&
271           _eglSameAttribs(disp->Options.Attribs, attrib_list))
272          goto out;
273    }
274 
275    /* create a new display */
276    assert(!disp);
277    disp = calloc(1, sizeof(_EGLDisplay));
278    if (!disp)
279       goto out;
280 
281    mtx_init(&disp->Mutex, mtx_plain);
282    disp->Platform = plat;
283    disp->PlatformDisplay = plat_dpy;
284    num_attribs = _eglNumAttribs(attrib_list);
285    if (num_attribs) {
286       disp->Options.Attribs = calloc(num_attribs, sizeof(EGLAttrib));
287       if (!disp->Options.Attribs) {
288          free(disp);
289          disp = NULL;
290          goto out;
291       }
292       memcpy(disp->Options.Attribs, attrib_list,
293              num_attribs * sizeof(EGLAttrib));
294    }
295 
296    /* add to the display list */
297    disp->Next = _eglGlobal.DisplayList;
298    _eglGlobal.DisplayList = disp;
299 
300 out:
301    mtx_unlock(_eglGlobal.Mutex);
302 
303    return disp;
304 }
305 
306 
307 /**
308  * Destroy the contexts and surfaces that are linked to the display.
309  */
310 void
_eglReleaseDisplayResources(_EGLDisplay * display)311 _eglReleaseDisplayResources(_EGLDisplay *display)
312 {
313    _EGLResource *list;
314    const _EGLDriver *drv = display->Driver;
315 
316    list = display->ResourceLists[_EGL_RESOURCE_CONTEXT];
317    while (list) {
318       _EGLContext *ctx = (_EGLContext *) list;
319       list = list->Next;
320 
321       _eglUnlinkContext(ctx);
322       drv->DestroyContext(display, ctx);
323    }
324    assert(!display->ResourceLists[_EGL_RESOURCE_CONTEXT]);
325 
326    list = display->ResourceLists[_EGL_RESOURCE_SURFACE];
327    while (list) {
328       _EGLSurface *surf = (_EGLSurface *) list;
329       list = list->Next;
330 
331       _eglUnlinkSurface(surf);
332       drv->DestroySurface(display, surf);
333    }
334    assert(!display->ResourceLists[_EGL_RESOURCE_SURFACE]);
335 
336    list = display->ResourceLists[_EGL_RESOURCE_IMAGE];
337    while (list) {
338       _EGLImage *image = (_EGLImage *) list;
339       list = list->Next;
340 
341       _eglUnlinkImage(image);
342       drv->DestroyImageKHR(display, image);
343    }
344    assert(!display->ResourceLists[_EGL_RESOURCE_IMAGE]);
345 
346    list = display->ResourceLists[_EGL_RESOURCE_SYNC];
347    while (list) {
348       _EGLSync *sync = (_EGLSync *) list;
349       list = list->Next;
350 
351       _eglUnlinkSync(sync);
352       drv->DestroySyncKHR(display, sync);
353    }
354    assert(!display->ResourceLists[_EGL_RESOURCE_SYNC]);
355 }
356 
357 
358 /**
359  * Free all the data hanging of an _EGLDisplay object, but not
360  * the object itself.
361  */
362 void
_eglCleanupDisplay(_EGLDisplay * disp)363 _eglCleanupDisplay(_EGLDisplay *disp)
364 {
365    if (disp->Configs) {
366       _eglDestroyArray(disp->Configs, free);
367       disp->Configs = NULL;
368    }
369 
370    /* XXX incomplete */
371 }
372 
373 
374 /**
375  * Return EGL_TRUE if the given handle is a valid handle to a display.
376  */
377 EGLBoolean
_eglCheckDisplayHandle(EGLDisplay dpy)378 _eglCheckDisplayHandle(EGLDisplay dpy)
379 {
380    _EGLDisplay *cur;
381 
382    mtx_lock(_eglGlobal.Mutex);
383    cur = _eglGlobal.DisplayList;
384    while (cur) {
385       if (cur == (_EGLDisplay *) dpy)
386          break;
387       cur = cur->Next;
388    }
389    mtx_unlock(_eglGlobal.Mutex);
390    return (cur != NULL);
391 }
392 
393 
394 /**
395  * Return EGL_TRUE if the given resource is valid.  That is, the display does
396  * own the resource.
397  */
398 EGLBoolean
_eglCheckResource(void * res,_EGLResourceType type,_EGLDisplay * disp)399 _eglCheckResource(void *res, _EGLResourceType type, _EGLDisplay *disp)
400 {
401    _EGLResource *list = disp->ResourceLists[type];
402 
403    if (!res)
404       return EGL_FALSE;
405 
406    while (list) {
407       if (res == (void *) list) {
408          assert(list->Display == disp);
409          break;
410       }
411       list = list->Next;
412    }
413 
414    return (list != NULL);
415 }
416 
417 
418 /**
419  * Initialize a display resource.  The size of the subclass object is
420  * specified.
421  *
422  * This is supposed to be called from the initializers of subclasses, such as
423  * _eglInitContext or _eglInitSurface.
424  */
425 void
_eglInitResource(_EGLResource * res,EGLint size,_EGLDisplay * disp)426 _eglInitResource(_EGLResource *res, EGLint size, _EGLDisplay *disp)
427 {
428    memset(res, 0, size);
429    res->Display = disp;
430    res->RefCount = 1;
431 }
432 
433 
434 /**
435  * Increment reference count for the resource.
436  */
437 void
_eglGetResource(_EGLResource * res)438 _eglGetResource(_EGLResource *res)
439 {
440    assert(res && res->RefCount > 0);
441    /* hopefully a resource is always manipulated with its display locked */
442    res->RefCount++;
443 }
444 
445 
446 /**
447  * Decrement reference count for the resource.
448  */
449 EGLBoolean
_eglPutResource(_EGLResource * res)450 _eglPutResource(_EGLResource *res)
451 {
452    assert(res && res->RefCount > 0);
453    res->RefCount--;
454    return (!res->RefCount);
455 }
456 
457 
458 /**
459  * Link a resource to its display.
460  */
461 void
_eglLinkResource(_EGLResource * res,_EGLResourceType type)462 _eglLinkResource(_EGLResource *res, _EGLResourceType type)
463 {
464    assert(res->Display);
465 
466    res->IsLinked = EGL_TRUE;
467    res->Next = res->Display->ResourceLists[type];
468    res->Display->ResourceLists[type] = res;
469    _eglGetResource(res);
470 }
471 
472 
473 /**
474  * Unlink a linked resource from its display.
475  */
476 void
_eglUnlinkResource(_EGLResource * res,_EGLResourceType type)477 _eglUnlinkResource(_EGLResource *res, _EGLResourceType type)
478 {
479    _EGLResource *prev;
480 
481    prev = res->Display->ResourceLists[type];
482    if (prev != res) {
483       while (prev) {
484          if (prev->Next == res)
485             break;
486          prev = prev->Next;
487       }
488       assert(prev);
489       prev->Next = res->Next;
490    }
491    else {
492       res->Display->ResourceLists[type] = res->Next;
493    }
494 
495    res->Next = NULL;
496    res->IsLinked = EGL_FALSE;
497    _eglPutResource(res);
498 
499    /* We always unlink before destroy.  The driver still owns a reference */
500    assert(res->RefCount);
501 }
502 
503 #ifdef HAVE_X11_PLATFORM
504 _EGLDisplay*
_eglGetX11Display(Display * native_display,const EGLAttrib * attrib_list)505 _eglGetX11Display(Display *native_display,
506                   const EGLAttrib *attrib_list)
507 {
508    /* EGL_EXT_platform_x11 recognizes exactly one attribute,
509     * EGL_PLATFORM_X11_SCREEN_EXT, which is optional.
510     */
511    if (attrib_list != NULL) {
512       for (int i = 0; attrib_list[i] != EGL_NONE; i += 2) {
513          if (attrib_list[i] != EGL_PLATFORM_X11_SCREEN_EXT) {
514             _eglError(EGL_BAD_ATTRIBUTE, "eglGetPlatformDisplay");
515             return NULL;
516          }
517       }
518    }
519    return _eglFindDisplay(_EGL_PLATFORM_X11, native_display, attrib_list);
520 }
521 #endif /* HAVE_X11_PLATFORM */
522 
523 #ifdef HAVE_XCB_PLATFORM
524 _EGLDisplay*
_eglGetXcbDisplay(xcb_connection_t * native_display,const EGLAttrib * attrib_list)525 _eglGetXcbDisplay(xcb_connection_t *native_display,
526                   const EGLAttrib *attrib_list)
527 {
528    /* EGL_EXT_platform_xcb recognizes exactly one attribute,
529     * EGL_PLATFORM_XCB_SCREEN_EXT, which is optional.
530     */
531    if (attrib_list != NULL) {
532       for (int i = 0; attrib_list[i] != EGL_NONE; i += 2) {
533          if (attrib_list[i] != EGL_PLATFORM_XCB_SCREEN_EXT) {
534             _eglError(EGL_BAD_ATTRIBUTE, "eglGetPlatformDisplay");
535             return NULL;
536          }
537       }
538    }
539 
540    return _eglFindDisplay(_EGL_PLATFORM_XCB, native_display, attrib_list);
541 }
542 #endif /* HAVE_XCB_PLATFORM */
543 
544 #ifdef HAVE_DRM_PLATFORM
545 _EGLDisplay*
_eglGetGbmDisplay(struct gbm_device * native_display,const EGLAttrib * attrib_list)546 _eglGetGbmDisplay(struct gbm_device *native_display,
547                   const EGLAttrib *attrib_list)
548 {
549    /* EGL_MESA_platform_gbm recognizes no attributes. */
550    if (attrib_list != NULL && attrib_list[0] != EGL_NONE) {
551       _eglError(EGL_BAD_ATTRIBUTE, "eglGetPlatformDisplay");
552       return NULL;
553    }
554 
555    return _eglFindDisplay(_EGL_PLATFORM_DRM, native_display, attrib_list);
556 }
557 #endif /* HAVE_DRM_PLATFORM */
558 
559 #ifdef HAVE_WAYLAND_PLATFORM
560 _EGLDisplay*
_eglGetWaylandDisplay(struct wl_display * native_display,const EGLAttrib * attrib_list)561 _eglGetWaylandDisplay(struct wl_display *native_display,
562                       const EGLAttrib *attrib_list)
563 {
564    /* EGL_EXT_platform_wayland recognizes no attributes. */
565    if (attrib_list != NULL && attrib_list[0] != EGL_NONE) {
566       _eglError(EGL_BAD_ATTRIBUTE, "eglGetPlatformDisplay");
567       return NULL;
568    }
569 
570    return _eglFindDisplay(_EGL_PLATFORM_WAYLAND, native_display, attrib_list);
571 }
572 #endif /* HAVE_WAYLAND_PLATFORM */
573 
574 _EGLDisplay*
_eglGetSurfacelessDisplay(void * native_display,const EGLAttrib * attrib_list)575 _eglGetSurfacelessDisplay(void *native_display,
576                           const EGLAttrib *attrib_list)
577 {
578    /* This platform has no native display. */
579    if (native_display != NULL) {
580       _eglError(EGL_BAD_PARAMETER, "eglGetPlatformDisplay");
581       return NULL;
582    }
583 
584    /* This platform recognizes no display attributes. */
585    if (attrib_list != NULL && attrib_list[0] != EGL_NONE) {
586       _eglError(EGL_BAD_ATTRIBUTE, "eglGetPlatformDisplay");
587       return NULL;
588    }
589 
590    return _eglFindDisplay(_EGL_PLATFORM_SURFACELESS, native_display,
591                           attrib_list);
592 }
593 
594 #ifdef HAVE_ANDROID_PLATFORM
595 _EGLDisplay*
_eglGetAndroidDisplay(void * native_display,const EGLAttrib * attrib_list)596 _eglGetAndroidDisplay(void *native_display,
597                           const EGLAttrib *attrib_list)
598 {
599 
600    /* This platform recognizes no display attributes. */
601    if (attrib_list != NULL && attrib_list[0] != EGL_NONE) {
602       _eglError(EGL_BAD_ATTRIBUTE, "eglGetPlatformDisplay");
603       return NULL;
604    }
605 
606    return _eglFindDisplay(_EGL_PLATFORM_ANDROID, native_display,
607                           attrib_list);
608 }
609 #endif /* HAVE_ANDROID_PLATFORM */
610 
611 _EGLDisplay*
_eglGetDeviceDisplay(void * native_display,const EGLAttrib * attrib_list)612 _eglGetDeviceDisplay(void *native_display,
613                      const EGLAttrib *attrib_list)
614 {
615    _EGLDevice *dev;
616    _EGLDisplay *display;
617    int fd = -1;
618 
619    dev = _eglLookupDevice(native_display);
620    if (!dev) {
621       _eglError(EGL_BAD_PARAMETER, "eglGetPlatformDisplay");
622       return NULL;
623    }
624 
625    if (attrib_list) {
626       for (int i = 0; attrib_list[i] != EGL_NONE; i += 2) {
627          EGLAttrib attrib = attrib_list[i];
628          EGLAttrib value = attrib_list[i + 1];
629 
630          /* EGL_EXT_platform_device does not recognize any attributes,
631           * EGL_EXT_device_drm adds the optional EGL_DRM_MASTER_FD_EXT.
632           */
633 
634          if (!_eglDeviceSupports(dev, _EGL_DEVICE_DRM) ||
635              attrib != EGL_DRM_MASTER_FD_EXT) {
636             _eglError(EGL_BAD_ATTRIBUTE, "eglGetPlatformDisplay");
637             return NULL;
638          }
639 
640          fd = (int) value;
641       }
642    }
643 
644    display = _eglFindDisplay(_EGL_PLATFORM_DEVICE, native_display, attrib_list);
645    if (!display) {
646       _eglError(EGL_BAD_ALLOC, "eglGetPlatformDisplay");
647       return NULL;
648    }
649 
650    /* If the fd is explicitly provided and we did not dup() it yet, do so.
651     * The spec mandates that we do so, since we'll need it past the
652     * eglGetPlatformDispay call.
653     *
654     * The new fd is guaranteed to be 3 or greater.
655     */
656    if (fd != -1 && display->Options.fd == 0) {
657       display->Options.fd = os_dupfd_cloexec(fd);
658       if (display->Options.fd == -1) {
659          /* Do not (really) need to teardown the display */
660          _eglError(EGL_BAD_ALLOC, "eglGetPlatformDisplay");
661          return NULL;
662       }
663    }
664 
665    return display;
666 }
667