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 */ 75static 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 */ 94static _EGLPlatformType 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 */ 128static _EGLPlatformType 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 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 */ 191void 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 225static EGLBoolean 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 * 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 300out: 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 */ 310void 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 */ 362void 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 */ 377EGLBoolean 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 */ 398EGLBoolean 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 */ 425void 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 */ 437void 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 */ 449EGLBoolean 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 */ 461void 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 */ 476void 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* 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* 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* 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* 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* 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* 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* 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