egl/display: only detect the platform once
[mesa.git] / src / egl / main / egldisplay.c
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-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
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
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
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
181 _eglGetNativePlatform(void *nativeDisplay)
182 {
183 static _EGLPlatformType native_platform = _EGL_INVALID_PLATFORM;
184
185 if (native_platform == _EGL_INVALID_PLATFORM) {
186 const char *detection_method;
187
188 native_platform = _eglGetNativePlatformFromEnv();
189 detection_method = "environment overwrite";
190
191 if (native_platform == _EGL_INVALID_PLATFORM) {
192 native_platform = _eglNativePlatformDetectNativeDisplay(nativeDisplay);
193 detection_method = "autodetected";
194 }
195
196 if (native_platform == _EGL_INVALID_PLATFORM) {
197 native_platform = _EGL_NATIVE_PLATFORM;
198 detection_method = "build-time configuration";
199 }
200
201 _eglLog(_EGL_DEBUG, "Native platform type: %s (%s)",
202 egl_platforms[native_platform].name, detection_method);
203 }
204
205 return native_platform;
206 }
207
208
209 /**
210 * Finish display management.
211 */
212 void
213 _eglFiniDisplay(void)
214 {
215 _EGLDisplay *dpyList, *dpy;
216
217 /* atexit function is called with global mutex locked */
218 dpyList = _eglGlobal.DisplayList;
219 while (dpyList) {
220 EGLint i;
221
222 /* pop list head */
223 dpy = dpyList;
224 dpyList = dpyList->Next;
225
226 for (i = 0; i < _EGL_NUM_RESOURCES; i++) {
227 if (dpy->ResourceLists[i]) {
228 _eglLog(_EGL_DEBUG, "Display %p is destroyed with resources", dpy);
229 break;
230 }
231 }
232
233 free(dpy);
234 }
235 _eglGlobal.DisplayList = NULL;
236 }
237
238
239 /**
240 * Find the display corresponding to the specified native display, or create a
241 * new one.
242 */
243 _EGLDisplay *
244 _eglFindDisplay(_EGLPlatformType plat, void *plat_dpy)
245 {
246 _EGLDisplay *dpy;
247
248 if (plat == _EGL_INVALID_PLATFORM)
249 return NULL;
250
251 mtx_lock(_eglGlobal.Mutex);
252
253 /* search the display list first */
254 dpy = _eglGlobal.DisplayList;
255 while (dpy) {
256 if (dpy->Platform == plat && dpy->PlatformDisplay == plat_dpy)
257 break;
258 dpy = dpy->Next;
259 }
260
261 /* create a new display */
262 if (!dpy) {
263 dpy = calloc(1, sizeof(_EGLDisplay));
264 if (dpy) {
265 mtx_init(&dpy->Mutex, mtx_plain);
266 dpy->Platform = plat;
267 dpy->PlatformDisplay = plat_dpy;
268
269 /* add to the display list */
270 dpy->Next = _eglGlobal.DisplayList;
271 _eglGlobal.DisplayList = dpy;
272 }
273 }
274
275 mtx_unlock(_eglGlobal.Mutex);
276
277 return dpy;
278 }
279
280
281 /**
282 * Destroy the contexts and surfaces that are linked to the display.
283 */
284 void
285 _eglReleaseDisplayResources(_EGLDriver *drv, _EGLDisplay *display)
286 {
287 _EGLResource *list;
288
289 list = display->ResourceLists[_EGL_RESOURCE_CONTEXT];
290 while (list) {
291 _EGLContext *ctx = (_EGLContext *) list;
292 list = list->Next;
293
294 _eglUnlinkContext(ctx);
295 drv->API.DestroyContext(drv, display, ctx);
296 }
297 assert(!display->ResourceLists[_EGL_RESOURCE_CONTEXT]);
298
299 list = display->ResourceLists[_EGL_RESOURCE_SURFACE];
300 while (list) {
301 _EGLSurface *surf = (_EGLSurface *) list;
302 list = list->Next;
303
304 _eglUnlinkSurface(surf);
305 drv->API.DestroySurface(drv, display, surf);
306 }
307 assert(!display->ResourceLists[_EGL_RESOURCE_SURFACE]);
308
309 list = display->ResourceLists[_EGL_RESOURCE_IMAGE];
310 while (list) {
311 _EGLImage *image = (_EGLImage *) list;
312 list = list->Next;
313
314 _eglUnlinkImage(image);
315 drv->API.DestroyImageKHR(drv, display, image);
316 }
317 assert(!display->ResourceLists[_EGL_RESOURCE_IMAGE]);
318
319 list = display->ResourceLists[_EGL_RESOURCE_SYNC];
320 while (list) {
321 _EGLSync *sync = (_EGLSync *) list;
322 list = list->Next;
323
324 _eglUnlinkSync(sync);
325 drv->API.DestroySyncKHR(drv, display, sync);
326 }
327 assert(!display->ResourceLists[_EGL_RESOURCE_SYNC]);
328 }
329
330
331 /**
332 * Free all the data hanging of an _EGLDisplay object, but not
333 * the object itself.
334 */
335 void
336 _eglCleanupDisplay(_EGLDisplay *disp)
337 {
338 if (disp->Configs) {
339 _eglDestroyArray(disp->Configs, free);
340 disp->Configs = NULL;
341 }
342
343 /* XXX incomplete */
344 }
345
346
347 /**
348 * Return EGL_TRUE if the given handle is a valid handle to a display.
349 */
350 EGLBoolean
351 _eglCheckDisplayHandle(EGLDisplay dpy)
352 {
353 _EGLDisplay *cur;
354
355 mtx_lock(_eglGlobal.Mutex);
356 cur = _eglGlobal.DisplayList;
357 while (cur) {
358 if (cur == (_EGLDisplay *) dpy)
359 break;
360 cur = cur->Next;
361 }
362 mtx_unlock(_eglGlobal.Mutex);
363 return (cur != NULL);
364 }
365
366
367 /**
368 * Return EGL_TRUE if the given resource is valid. That is, the display does
369 * own the resource.
370 */
371 EGLBoolean
372 _eglCheckResource(void *res, _EGLResourceType type, _EGLDisplay *dpy)
373 {
374 _EGLResource *list = dpy->ResourceLists[type];
375
376 if (!res)
377 return EGL_FALSE;
378
379 while (list) {
380 if (res == (void *) list) {
381 assert(list->Display == dpy);
382 break;
383 }
384 list = list->Next;
385 }
386
387 return (list != NULL);
388 }
389
390
391 /**
392 * Initialize a display resource. The size of the subclass object is
393 * specified.
394 *
395 * This is supposed to be called from the initializers of subclasses, such as
396 * _eglInitContext or _eglInitSurface.
397 */
398 void
399 _eglInitResource(_EGLResource *res, EGLint size, _EGLDisplay *dpy)
400 {
401 memset(res, 0, size);
402 res->Display = dpy;
403 res->RefCount = 1;
404 }
405
406
407 /**
408 * Increment reference count for the resource.
409 */
410 void
411 _eglGetResource(_EGLResource *res)
412 {
413 assert(res && res->RefCount > 0);
414 /* hopefully a resource is always manipulated with its display locked */
415 res->RefCount++;
416 }
417
418
419 /**
420 * Decrement reference count for the resource.
421 */
422 EGLBoolean
423 _eglPutResource(_EGLResource *res)
424 {
425 assert(res && res->RefCount > 0);
426 res->RefCount--;
427 return (!res->RefCount);
428 }
429
430
431 /**
432 * Link a resource to its display.
433 */
434 void
435 _eglLinkResource(_EGLResource *res, _EGLResourceType type)
436 {
437 assert(res->Display);
438
439 res->IsLinked = EGL_TRUE;
440 res->Next = res->Display->ResourceLists[type];
441 res->Display->ResourceLists[type] = res;
442 _eglGetResource(res);
443 }
444
445
446 /**
447 * Unlink a linked resource from its display.
448 */
449 void
450 _eglUnlinkResource(_EGLResource *res, _EGLResourceType type)
451 {
452 _EGLResource *prev;
453
454 prev = res->Display->ResourceLists[type];
455 if (prev != res) {
456 while (prev) {
457 if (prev->Next == res)
458 break;
459 prev = prev->Next;
460 }
461 assert(prev);
462 prev->Next = res->Next;
463 }
464 else {
465 res->Display->ResourceLists[type] = res->Next;
466 }
467
468 res->Next = NULL;
469 res->IsLinked = EGL_FALSE;
470 _eglPutResource(res);
471
472 /* We always unlink before destroy. The driver still owns a reference */
473 assert(res->RefCount);
474 }
475
476 #ifdef HAVE_X11_PLATFORM
477 static EGLBoolean
478 _eglParseX11DisplayAttribList(_EGLDisplay *display, const EGLint *attrib_list)
479 {
480 int i;
481
482 if (attrib_list == NULL) {
483 return EGL_TRUE;
484 }
485
486 for (i = 0; attrib_list[i] != EGL_NONE; i += 2) {
487 EGLint attrib = attrib_list[i];
488 EGLint value = attrib_list[i + 1];
489
490 /* EGL_EXT_platform_x11 recognizes exactly one attribute,
491 * EGL_PLATFORM_X11_SCREEN_EXT, which is optional.
492 */
493 if (attrib == EGL_PLATFORM_X11_SCREEN_EXT) {
494 display->Options.Platform = (void *)(uintptr_t)value;
495 } else {
496 _eglError(EGL_BAD_ATTRIBUTE, "eglGetPlatformDisplay");
497 return EGL_FALSE;
498 }
499 }
500
501 return EGL_TRUE;
502 }
503
504 _EGLDisplay*
505 _eglGetX11Display(Display *native_display,
506 const EGLint *attrib_list)
507 {
508 _EGLDisplay *display = _eglFindDisplay(_EGL_PLATFORM_X11,
509 native_display);
510
511 if (!display) {
512 _eglError(EGL_BAD_ALLOC, "eglGetPlatformDisplay");
513 return NULL;
514 }
515
516 if (!_eglParseX11DisplayAttribList(display, attrib_list)) {
517 return NULL;
518 }
519
520 return display;
521 }
522 #endif /* HAVE_X11_PLATFORM */
523
524 #ifdef HAVE_DRM_PLATFORM
525 _EGLDisplay*
526 _eglGetGbmDisplay(struct gbm_device *native_display,
527 const EGLint *attrib_list)
528 {
529 /* EGL_MESA_platform_gbm recognizes no attributes. */
530 if (attrib_list != NULL && attrib_list[0] != EGL_NONE) {
531 _eglError(EGL_BAD_ATTRIBUTE, "eglGetPlatformDisplay");
532 return NULL;
533 }
534
535 return _eglFindDisplay(_EGL_PLATFORM_DRM, native_display);
536 }
537 #endif /* HAVE_DRM_PLATFORM */
538
539 #ifdef HAVE_WAYLAND_PLATFORM
540 _EGLDisplay*
541 _eglGetWaylandDisplay(struct wl_display *native_display,
542 const EGLint *attrib_list)
543 {
544 /* EGL_EXT_platform_wayland recognizes no attributes. */
545 if (attrib_list != NULL && attrib_list[0] != EGL_NONE) {
546 _eglError(EGL_BAD_ATTRIBUTE, "eglGetPlatformDisplay");
547 return NULL;
548 }
549
550 return _eglFindDisplay(_EGL_PLATFORM_WAYLAND, native_display);
551 }
552 #endif /* HAVE_WAYLAND_PLATFORM */
553
554 #ifdef HAVE_SURFACELESS_PLATFORM
555 _EGLDisplay*
556 _eglGetSurfacelessDisplay(void *native_display,
557 const EGLint *attrib_list)
558 {
559 /* This platform has no native display. */
560 if (native_display != NULL) {
561 _eglError(EGL_BAD_PARAMETER, "eglGetPlatformDisplay");
562 return NULL;
563 }
564
565 /* This platform recognizes no display attributes. */
566 if (attrib_list != NULL && attrib_list[0] != EGL_NONE) {
567 _eglError(EGL_BAD_ATTRIBUTE, "eglGetPlatformDisplay");
568 return NULL;
569 }
570
571 return _eglFindDisplay(_EGL_PLATFORM_SURFACELESS, native_display);
572 }
573 #endif /* HAVE_SURFACELESS_PLATFORM */