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