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