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 #include "c11/threads.h"
39 
40 #include "eglcontext.h"
41 #include "eglcurrent.h"
42 #include "eglsurface.h"
43 #include "egldisplay.h"
44 #include "egldriver.h"
45 #include "eglglobals.h"
46 #include "egllog.h"
47 #include "eglimage.h"
48 #include "eglsync.h"
49 
50 /* Includes for _eglNativePlatformDetectNativeDisplay */
51 #ifdef HAVE_MINCORE
52 #include <unistd.h>
53 #include <sys/mman.h>
54 #endif
55 #ifdef HAVE_WAYLAND_PLATFORM
56 #include <wayland-client.h>
57 #endif
58 #ifdef HAVE_DRM_PLATFORM
59 #include <gbm.h>
60 #endif
61 
62 
63 /**
64  * Map --with-egl-platforms names to platform types.
65  */
66 static const struct {
67    _EGLPlatformType platform;
68    const char *name;
69 } egl_platforms[_EGL_NUM_PLATFORMS] = {
70    { _EGL_PLATFORM_X11, "x11" },
71    { _EGL_PLATFORM_WAYLAND, "wayland" },
72    { _EGL_PLATFORM_DRM, "drm" },
73    { _EGL_PLATFORM_ANDROID, "android" },
74    { _EGL_PLATFORM_HAIKU, "haiku" },
75    { _EGL_PLATFORM_SURFACELESS, "surfaceless" },
76 };
77 
78 
79 /**
80  * Return the native platform by parsing EGL_PLATFORM.
81  */
82 static _EGLPlatformType
_eglGetNativePlatformFromEnv(void)83 _eglGetNativePlatformFromEnv(void)
84 {
85    _EGLPlatformType plat = _EGL_INVALID_PLATFORM;
86    const char *plat_name;
87    EGLint i;
88 
89    plat_name = getenv("EGL_PLATFORM");
90    /* try deprecated env variable */
91    if (!plat_name || !plat_name[0])
92       plat_name = getenv("EGL_DISPLAY");
93    if (!plat_name || !plat_name[0])
94       return _EGL_INVALID_PLATFORM;
95 
96    for (i = 0; i < _EGL_NUM_PLATFORMS; i++) {
97       if (strcmp(egl_platforms[i].name, plat_name) == 0) {
98          plat = egl_platforms[i].platform;
99          break;
100       }
101    }
102 
103    return plat;
104 }
105 
106 
107 /**
108  * Perform validity checks on a generic pointer.
109  */
110 static EGLBoolean
_eglPointerIsDereferencable(void * p)111 _eglPointerIsDereferencable(void *p)
112 {
113 #ifdef HAVE_MINCORE
114    uintptr_t addr = (uintptr_t) p;
115    unsigned char valid = 0;
116    const long page_size = getpagesize();
117 
118    if (p == NULL)
119       return EGL_FALSE;
120 
121    /* align addr to page_size */
122    addr &= ~(page_size - 1);
123 
124    if (mincore((void *) addr, page_size, &valid) < 0) {
125       _eglLog(_EGL_DEBUG, "mincore failed: %m");
126       return EGL_FALSE;
127    }
128 
129    return (valid & 0x01) == 0x01;
130 #else
131    return p != NULL;
132 #endif
133 }
134 
135 
136 /**
137  * Try detecting native platform with the help of native display characteristcs.
138  */
139 static _EGLPlatformType
_eglNativePlatformDetectNativeDisplay(void * nativeDisplay)140 _eglNativePlatformDetectNativeDisplay(void *nativeDisplay)
141 {
142    if (nativeDisplay == EGL_DEFAULT_DISPLAY)
143       return _EGL_INVALID_PLATFORM;
144 
145    if (_eglPointerIsDereferencable(nativeDisplay)) {
146       void *first_pointer = *(void **) nativeDisplay;
147 
148       (void) first_pointer; /* silence unused var warning */
149 
150 #ifdef HAVE_WAYLAND_PLATFORM
151       /* wl_display is a wl_proxy, which is a wl_object.
152        * wl_object's first element points to the interfacetype. */
153       if (first_pointer == &wl_display_interface)
154          return _EGL_PLATFORM_WAYLAND;
155 #endif
156 
157 #ifdef HAVE_DRM_PLATFORM
158       /* gbm has a pointer to its constructor as first element. */
159       if (first_pointer == gbm_create_device)
160          return _EGL_PLATFORM_DRM;
161 #endif
162 
163 #ifdef HAVE_X11_PLATFORM
164       /* If not matched to any other platform, fallback to x11. */
165       return _EGL_PLATFORM_X11;
166 #endif
167 
168 #ifdef HAVE_HAIKU_PLATFORM
169 	return _EGL_PLATFORM_HAIKU;
170 #endif
171    }
172 
173    return _EGL_INVALID_PLATFORM;
174 }
175 
176 
177 /**
178  * Return the native platform.  It is the platform of the EGL native types.
179  */
180 _EGLPlatformType
_eglGetNativePlatform(void * nativeDisplay)181 _eglGetNativePlatform(void *nativeDisplay)
182 {
183    static _EGLPlatformType native_platform;
184    char *detection_method;
185 
186    native_platform = _eglGetNativePlatformFromEnv();
187    detection_method = "environment overwrite";
188 
189    if (native_platform == _EGL_INVALID_PLATFORM) {
190       native_platform = _eglNativePlatformDetectNativeDisplay(nativeDisplay);
191       detection_method = "autodetected";
192    }
193 
194    if (native_platform == _EGL_INVALID_PLATFORM) {
195       native_platform = _EGL_NATIVE_PLATFORM;
196       detection_method = "build-time configuration";
197    }
198 
199    _eglLog(_EGL_DEBUG, "Native platform type: %s (%s)",
200            egl_platforms[native_platform].name, detection_method);
201 
202    return native_platform;
203 }
204 
205 
206 /**
207  * Finish display management.
208  */
209 void
_eglFiniDisplay(void)210 _eglFiniDisplay(void)
211 {
212    _EGLDisplay *dpyList, *dpy;
213 
214    /* atexit function is called with global mutex locked */
215    dpyList = _eglGlobal.DisplayList;
216    while (dpyList) {
217       EGLint i;
218 
219       /* pop list head */
220       dpy = dpyList;
221       dpyList = dpyList->Next;
222 
223       for (i = 0; i < _EGL_NUM_RESOURCES; i++) {
224          if (dpy->ResourceLists[i]) {
225             _eglLog(_EGL_DEBUG, "Display %p is destroyed with resources", dpy);
226             break;
227          }
228       }
229 
230       free(dpy);
231    }
232    _eglGlobal.DisplayList = NULL;
233 }
234 
235 
236 /**
237  * Find the display corresponding to the specified native display, or create a
238  * new one.
239  */
240 _EGLDisplay *
_eglFindDisplay(_EGLPlatformType plat,void * plat_dpy)241 _eglFindDisplay(_EGLPlatformType plat, void *plat_dpy)
242 {
243    _EGLDisplay *dpy;
244 
245    if (plat == _EGL_INVALID_PLATFORM)
246       return NULL;
247 
248    mtx_lock(_eglGlobal.Mutex);
249 
250    /* search the display list first */
251    dpy = _eglGlobal.DisplayList;
252    while (dpy) {
253       if (dpy->Platform == plat && dpy->PlatformDisplay == plat_dpy)
254          break;
255       dpy = dpy->Next;
256    }
257 
258    /* create a new display */
259    if (!dpy) {
260       dpy = calloc(1, sizeof(_EGLDisplay));
261       if (dpy) {
262          mtx_init(&dpy->Mutex, mtx_plain);
263          dpy->Platform = plat;
264          dpy->PlatformDisplay = plat_dpy;
265 
266          /* add to the display list */
267          dpy->Next = _eglGlobal.DisplayList;
268          _eglGlobal.DisplayList = dpy;
269       }
270    }
271 
272    mtx_unlock(_eglGlobal.Mutex);
273 
274    return dpy;
275 }
276 
277 
278 /**
279  * Destroy the contexts and surfaces that are linked to the display.
280  */
281 void
_eglReleaseDisplayResources(_EGLDriver * drv,_EGLDisplay * display)282 _eglReleaseDisplayResources(_EGLDriver *drv, _EGLDisplay *display)
283 {
284    _EGLResource *list;
285 
286    list = display->ResourceLists[_EGL_RESOURCE_CONTEXT];
287    while (list) {
288       _EGLContext *ctx = (_EGLContext *) list;
289       list = list->Next;
290 
291       _eglUnlinkContext(ctx);
292       drv->API.DestroyContext(drv, display, ctx);
293    }
294    assert(!display->ResourceLists[_EGL_RESOURCE_CONTEXT]);
295 
296    list = display->ResourceLists[_EGL_RESOURCE_SURFACE];
297    while (list) {
298       _EGLSurface *surf = (_EGLSurface *) list;
299       list = list->Next;
300 
301       _eglUnlinkSurface(surf);
302       drv->API.DestroySurface(drv, display, surf);
303    }
304    assert(!display->ResourceLists[_EGL_RESOURCE_SURFACE]);
305 
306    list = display->ResourceLists[_EGL_RESOURCE_IMAGE];
307    while (list) {
308       _EGLImage *image = (_EGLImage *) list;
309       list = list->Next;
310 
311       _eglUnlinkImage(image);
312       drv->API.DestroyImageKHR(drv, display, image);
313    }
314    assert(!display->ResourceLists[_EGL_RESOURCE_IMAGE]);
315 
316    list = display->ResourceLists[_EGL_RESOURCE_SYNC];
317    while (list) {
318       _EGLSync *sync = (_EGLSync *) list;
319       list = list->Next;
320 
321       _eglUnlinkSync(sync);
322       drv->API.DestroySyncKHR(drv, display, sync);
323    }
324    assert(!display->ResourceLists[_EGL_RESOURCE_SYNC]);
325 }
326 
327 
328 /**
329  * Free all the data hanging of an _EGLDisplay object, but not
330  * the object itself.
331  */
332 void
_eglCleanupDisplay(_EGLDisplay * disp)333 _eglCleanupDisplay(_EGLDisplay *disp)
334 {
335    if (disp->Configs) {
336       _eglDestroyArray(disp->Configs, free);
337       disp->Configs = NULL;
338    }
339 
340    /* XXX incomplete */
341 }
342 
343 
344 /**
345  * Return EGL_TRUE if the given handle is a valid handle to a display.
346  */
347 EGLBoolean
_eglCheckDisplayHandle(EGLDisplay dpy)348 _eglCheckDisplayHandle(EGLDisplay dpy)
349 {
350    _EGLDisplay *cur;
351 
352    mtx_lock(_eglGlobal.Mutex);
353    cur = _eglGlobal.DisplayList;
354    while (cur) {
355       if (cur == (_EGLDisplay *) dpy)
356          break;
357       cur = cur->Next;
358    }
359    mtx_unlock(_eglGlobal.Mutex);
360    return (cur != NULL);
361 }
362 
363 
364 /**
365  * Return EGL_TRUE if the given resource is valid.  That is, the display does
366  * own the resource.
367  */
368 EGLBoolean
_eglCheckResource(void * res,_EGLResourceType type,_EGLDisplay * dpy)369 _eglCheckResource(void *res, _EGLResourceType type, _EGLDisplay *dpy)
370 {
371    _EGLResource *list = dpy->ResourceLists[type];
372 
373    if (!res)
374       return EGL_FALSE;
375 
376    while (list) {
377       if (res == (void *) list) {
378          assert(list->Display == dpy);
379          break;
380       }
381       list = list->Next;
382    }
383 
384    return (list != NULL);
385 }
386 
387 
388 /**
389  * Initialize a display resource.  The size of the subclass object is
390  * specified.
391  *
392  * This is supposed to be called from the initializers of subclasses, such as
393  * _eglInitContext or _eglInitSurface.
394  */
395 void
_eglInitResource(_EGLResource * res,EGLint size,_EGLDisplay * dpy)396 _eglInitResource(_EGLResource *res, EGLint size, _EGLDisplay *dpy)
397 {
398    memset(res, 0, size);
399    res->Display = dpy;
400    res->RefCount = 1;
401 }
402 
403 
404 /**
405  * Increment reference count for the resource.
406  */
407 void
_eglGetResource(_EGLResource * res)408 _eglGetResource(_EGLResource *res)
409 {
410    assert(res && res->RefCount > 0);
411    /* hopefully a resource is always manipulated with its display locked */
412    res->RefCount++;
413 }
414 
415 
416 /**
417  * Decrement reference count for the resource.
418  */
419 EGLBoolean
_eglPutResource(_EGLResource * res)420 _eglPutResource(_EGLResource *res)
421 {
422    assert(res && res->RefCount > 0);
423    res->RefCount--;
424    return (!res->RefCount);
425 }
426 
427 
428 /**
429  * Link a resource to its display.
430  */
431 void
_eglLinkResource(_EGLResource * res,_EGLResourceType type)432 _eglLinkResource(_EGLResource *res, _EGLResourceType type)
433 {
434    assert(res->Display);
435 
436    res->IsLinked = EGL_TRUE;
437    res->Next = res->Display->ResourceLists[type];
438    res->Display->ResourceLists[type] = res;
439    _eglGetResource(res);
440 }
441 
442 
443 /**
444  * Unlink a linked resource from its display.
445  */
446 void
_eglUnlinkResource(_EGLResource * res,_EGLResourceType type)447 _eglUnlinkResource(_EGLResource *res, _EGLResourceType type)
448 {
449    _EGLResource *prev;
450 
451    prev = res->Display->ResourceLists[type];
452    if (prev != res) {
453       while (prev) {
454          if (prev->Next == res)
455             break;
456          prev = prev->Next;
457       }
458       assert(prev);
459       prev->Next = res->Next;
460    }
461    else {
462       res->Display->ResourceLists[type] = res->Next;
463    }
464 
465    res->Next = NULL;
466    res->IsLinked = EGL_FALSE;
467    _eglPutResource(res);
468 
469    /* We always unlink before destroy.  The driver still owns a reference */
470    assert(res->RefCount);
471 }
472 
473 #ifdef HAVE_X11_PLATFORM
474 static EGLBoolean
_eglParseX11DisplayAttribList(const EGLint * attrib_list)475 _eglParseX11DisplayAttribList(const EGLint *attrib_list)
476 {
477    int i;
478 
479    if (attrib_list == NULL) {
480       return EGL_TRUE;
481    }
482 
483    for (i = 0; attrib_list[i] != EGL_NONE; i += 2) {
484       EGLint attrib = attrib_list[i];
485       EGLint value = attrib_list[i + 1];
486 
487       /* EGL_EXT_platform_x11 recognizes exactly one attribute,
488        * EGL_PLATFORM_X11_SCREEN_EXT, which is optional.
489        *
490        * Mesa supports connecting to only the default screen, so we reject
491        * screen != 0.
492        */
493       if (attrib != EGL_PLATFORM_X11_SCREEN_EXT || value != 0) {
494          _eglError(EGL_BAD_ATTRIBUTE, "eglGetPlatformDisplay");
495          return EGL_FALSE;
496       }
497    }
498 
499    return EGL_TRUE;
500 }
501 
502 _EGLDisplay*
_eglGetX11Display(Display * native_display,const EGLint * attrib_list)503 _eglGetX11Display(Display *native_display,
504                   const EGLint *attrib_list)
505 {
506    if (!_eglParseX11DisplayAttribList(attrib_list)) {
507       return NULL;
508    }
509 
510    return _eglFindDisplay(_EGL_PLATFORM_X11, native_display);
511 }
512 #endif /* HAVE_X11_PLATFORM */
513 
514 #ifdef HAVE_DRM_PLATFORM
515 _EGLDisplay*
_eglGetGbmDisplay(struct gbm_device * native_display,const EGLint * attrib_list)516 _eglGetGbmDisplay(struct gbm_device *native_display,
517                   const EGLint *attrib_list)
518 {
519    /* EGL_MESA_platform_gbm recognizes no attributes. */
520    if (attrib_list != NULL && attrib_list[0] != EGL_NONE) {
521       _eglError(EGL_BAD_ATTRIBUTE, "eglGetPlatformDisplay");
522       return NULL;
523    }
524 
525    return _eglFindDisplay(_EGL_PLATFORM_DRM, native_display);
526 }
527 #endif /* HAVE_DRM_PLATFORM */
528 
529 #ifdef HAVE_WAYLAND_PLATFORM
530 _EGLDisplay*
_eglGetWaylandDisplay(struct wl_display * native_display,const EGLint * attrib_list)531 _eglGetWaylandDisplay(struct wl_display *native_display,
532                       const EGLint *attrib_list)
533 {
534    /* EGL_EXT_platform_wayland recognizes no attributes. */
535    if (attrib_list != NULL && attrib_list[0] != EGL_NONE) {
536       _eglError(EGL_BAD_ATTRIBUTE, "eglGetPlatformDisplay");
537       return NULL;
538    }
539 
540    return _eglFindDisplay(_EGL_PLATFORM_WAYLAND, native_display);
541 }
542 #endif /* HAVE_WAYLAND_PLATFORM */
543 
544 #ifdef HAVE_SURFACELESS_PLATFORM
545 _EGLDisplay*
_eglGetSurfacelessDisplay(void * native_display,const EGLint * attrib_list)546 _eglGetSurfacelessDisplay(void *native_display,
547                           const EGLint *attrib_list)
548 {
549    /* This platform has no native display. */
550    if (native_display != NULL) {
551       _eglError(EGL_BAD_PARAMETER, "eglGetPlatformDisplay");
552       return NULL;
553    }
554 
555    /* This platform recognizes no display attributes. */
556    if (attrib_list != NULL && attrib_list[0] != EGL_NONE) {
557       _eglError(EGL_BAD_ATTRIBUTE, "eglGetPlatformDisplay");
558       return NULL;
559    }
560 
561    return _eglFindDisplay(_EGL_PLATFORM_SURFACELESS, native_display);
562 }
563 #endif /* HAVE_SURFACELESS_PLATFORM */
564