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