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