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