eglApi.cpp revision 519191670e37bdfa7f686fb3d3ee84fafe9ad0cb
1/* 2 ** Copyright 2007, The Android Open Source Project 3 ** 4 ** Licensed under the Apache License, Version 2.0 (the "License"); 5 ** you may not use this file except in compliance with the License. 6 ** You may obtain a copy of the License at 7 ** 8 ** http://www.apache.org/licenses/LICENSE-2.0 9 ** 10 ** Unless required by applicable law or agreed to in writing, software 11 ** distributed under the License is distributed on an "AS IS" BASIS, 12 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 ** See the License for the specific language governing permissions and 14 ** limitations under the License. 15 */ 16 17#define ATRACE_TAG ATRACE_TAG_GRAPHICS 18 19#include <ctype.h> 20#include <stdlib.h> 21#include <string.h> 22 23#include <hardware/gralloc.h> 24#include <system/window.h> 25 26#include <EGL/egl.h> 27#include <EGL/eglext.h> 28#include <GLES/gl.h> 29#include <GLES/glext.h> 30 31#include <cutils/log.h> 32#include <cutils/atomic.h> 33#include <cutils/compiler.h> 34#include <cutils/properties.h> 35#include <cutils/memory.h> 36 37#include <utils/KeyedVector.h> 38#include <utils/SortedVector.h> 39#include <utils/String8.h> 40#include <utils/Trace.h> 41 42#include "egl_impl.h" 43#include "egl_tls.h" 44#include "glestrace.h" 45#include "hooks.h" 46 47#include "egl_display.h" 48#include "egl_impl.h" 49#include "egl_object.h" 50#include "egl_tls.h" 51#include "egldefs.h" 52 53using namespace android; 54 55// ---------------------------------------------------------------------------- 56 57#define EGL_VERSION_HW_ANDROID 0x3143 58 59struct extention_map_t { 60 const char* name; 61 __eglMustCastToProperFunctionPointerType address; 62}; 63 64static const extention_map_t sExtentionMap[] = { 65 { "eglLockSurfaceKHR", 66 (__eglMustCastToProperFunctionPointerType)&eglLockSurfaceKHR }, 67 { "eglUnlockSurfaceKHR", 68 (__eglMustCastToProperFunctionPointerType)&eglUnlockSurfaceKHR }, 69 { "eglCreateImageKHR", 70 (__eglMustCastToProperFunctionPointerType)&eglCreateImageKHR }, 71 { "eglDestroyImageKHR", 72 (__eglMustCastToProperFunctionPointerType)&eglDestroyImageKHR }, 73 { "eglGetSystemTimeFrequencyNV", 74 (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeFrequencyNV }, 75 { "eglGetSystemTimeNV", 76 (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeNV }, 77}; 78 79// accesses protected by sExtensionMapMutex 80static DefaultKeyedVector<String8, __eglMustCastToProperFunctionPointerType> sGLExtentionMap; 81static int sGLExtentionSlot = 0; 82static pthread_mutex_t sExtensionMapMutex = PTHREAD_MUTEX_INITIALIZER; 83 84static void(*findProcAddress(const char* name, 85 const extention_map_t* map, size_t n))() { 86 for (uint32_t i=0 ; i<n ; i++) { 87 if (!strcmp(name, map[i].name)) { 88 return map[i].address; 89 } 90 } 91 return NULL; 92} 93 94// ---------------------------------------------------------------------------- 95 96namespace android { 97extern void setGLHooksThreadSpecific(gl_hooks_t const *value); 98extern EGLBoolean egl_init_drivers(); 99extern const __eglMustCastToProperFunctionPointerType gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS]; 100extern int getEGLDebugLevel(); 101extern void setEGLDebugLevel(int level); 102extern gl_hooks_t gHooksTrace; 103} // namespace android; 104 105// ---------------------------------------------------------------------------- 106 107static inline void clearError() { egl_tls_t::clearError(); } 108static inline EGLContext getContext() { return egl_tls_t::getContext(); } 109 110// ---------------------------------------------------------------------------- 111 112EGLDisplay eglGetDisplay(EGLNativeDisplayType display) 113{ 114 clearError(); 115 116 uint32_t index = uint32_t(display); 117 if (index >= NUM_DISPLAYS) { 118 return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY); 119 } 120 121 if (egl_init_drivers() == EGL_FALSE) { 122 return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY); 123 } 124 125 EGLDisplay dpy = egl_display_t::getFromNativeDisplay(display); 126 return dpy; 127} 128 129// ---------------------------------------------------------------------------- 130// Initialization 131// ---------------------------------------------------------------------------- 132 133EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor) 134{ 135 clearError(); 136 137 egl_display_ptr dp = get_display(dpy); 138 if (!dp) return setError(EGL_BAD_DISPLAY, EGL_FALSE); 139 140 EGLBoolean res = dp->initialize(major, minor); 141 142 return res; 143} 144 145EGLBoolean eglTerminate(EGLDisplay dpy) 146{ 147 // NOTE: don't unload the drivers b/c some APIs can be called 148 // after eglTerminate() has been called. eglTerminate() only 149 // terminates an EGLDisplay, not a EGL itself. 150 151 clearError(); 152 153 egl_display_ptr dp = get_display(dpy); 154 if (!dp) return setError(EGL_BAD_DISPLAY, EGL_FALSE); 155 156 EGLBoolean res = dp->terminate(); 157 158 return res; 159} 160 161// ---------------------------------------------------------------------------- 162// configuration 163// ---------------------------------------------------------------------------- 164 165EGLBoolean eglGetConfigs( EGLDisplay dpy, 166 EGLConfig *configs, 167 EGLint config_size, EGLint *num_config) 168{ 169 clearError(); 170 171 const egl_display_ptr dp = validate_display(dpy); 172 if (!dp) return EGL_FALSE; 173 174 if (num_config==0) { 175 return setError(EGL_BAD_PARAMETER, EGL_FALSE); 176 } 177 178 EGLBoolean res = EGL_FALSE; 179 *num_config = 0; 180 181 egl_connection_t* const cnx = &gEGLImpl; 182 if (cnx->dso) { 183 res = cnx->egl.eglGetConfigs( 184 dp->disp.dpy, configs, config_size, num_config); 185 } 186 187 return res; 188} 189 190EGLBoolean eglChooseConfig( EGLDisplay dpy, const EGLint *attrib_list, 191 EGLConfig *configs, EGLint config_size, 192 EGLint *num_config) 193{ 194 clearError(); 195 196 const egl_display_ptr dp = validate_display(dpy); 197 if (!dp) return EGL_FALSE; 198 199 if (num_config==0) { 200 return setError(EGL_BAD_PARAMETER, EGL_FALSE); 201 } 202 203 EGLBoolean res = EGL_FALSE; 204 *num_config = 0; 205 206 egl_connection_t* const cnx = &gEGLImpl; 207 if (cnx->dso) { 208 if (attrib_list) { 209 char value[PROPERTY_VALUE_MAX]; 210 property_get("debug.egl.force_msaa", value, "false"); 211 212 if (!strcmp(value, "true")) { 213 size_t attribCount = 0; 214 EGLint attrib = attrib_list[0]; 215 216 // Only enable MSAA if the context is OpenGL ES 2.0 and 217 // if no caveat is requested 218 const EGLint *attribRendererable = NULL; 219 const EGLint *attribCaveat = NULL; 220 221 // Count the number of attributes and look for 222 // EGL_RENDERABLE_TYPE and EGL_CONFIG_CAVEAT 223 while (attrib != EGL_NONE) { 224 attrib = attrib_list[attribCount]; 225 switch (attrib) { 226 case EGL_RENDERABLE_TYPE: 227 attribRendererable = &attrib_list[attribCount]; 228 break; 229 case EGL_CONFIG_CAVEAT: 230 attribCaveat = &attrib_list[attribCount]; 231 break; 232 } 233 attribCount++; 234 } 235 236 if (attribRendererable && attribRendererable[1] == EGL_OPENGL_ES2_BIT && 237 (!attribCaveat || attribCaveat[1] != EGL_NONE)) { 238 239 // Insert 2 extra attributes to force-enable MSAA 4x 240 EGLint aaAttribs[attribCount + 4]; 241 aaAttribs[0] = EGL_SAMPLE_BUFFERS; 242 aaAttribs[1] = 1; 243 aaAttribs[2] = EGL_SAMPLES; 244 aaAttribs[3] = 4; 245 246 memcpy(&aaAttribs[4], attrib_list, attribCount * sizeof(EGLint)); 247 248 EGLint numConfigAA; 249 EGLBoolean resAA = cnx->egl.eglChooseConfig( 250 dp->disp.dpy, aaAttribs, configs, config_size, &numConfigAA); 251 252 if (resAA == EGL_TRUE && numConfigAA > 0) { 253 ALOGD("Enabling MSAA 4x"); 254 *num_config = numConfigAA; 255 return resAA; 256 } 257 } 258 } 259 } 260 261 res = cnx->egl.eglChooseConfig( 262 dp->disp.dpy, attrib_list, configs, config_size, num_config); 263 } 264 return res; 265} 266 267EGLBoolean eglGetConfigAttrib(EGLDisplay dpy, EGLConfig config, 268 EGLint attribute, EGLint *value) 269{ 270 clearError(); 271 272 egl_connection_t* cnx = NULL; 273 const egl_display_ptr dp = validate_display_connection(dpy, cnx); 274 if (!dp) return EGL_FALSE; 275 276 return cnx->egl.eglGetConfigAttrib( 277 dp->disp.dpy, config, attribute, value); 278} 279 280// ---------------------------------------------------------------------------- 281// surfaces 282// ---------------------------------------------------------------------------- 283 284EGLSurface eglCreateWindowSurface( EGLDisplay dpy, EGLConfig config, 285 NativeWindowType window, 286 const EGLint *attrib_list) 287{ 288 clearError(); 289 290 egl_connection_t* cnx = NULL; 291 egl_display_ptr dp = validate_display_connection(dpy, cnx); 292 if (dp) { 293 EGLDisplay iDpy = dp->disp.dpy; 294 EGLint format; 295 296 if (native_window_api_connect(window, NATIVE_WINDOW_API_EGL) != OK) { 297 ALOGE("EGLNativeWindowType %p already connected to another API", 298 window); 299 return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE); 300 } 301 302 // set the native window's buffers format to match this config 303 if (cnx->egl.eglGetConfigAttrib(iDpy, 304 config, EGL_NATIVE_VISUAL_ID, &format)) { 305 if (format != 0) { 306 int err = native_window_set_buffers_format(window, format); 307 if (err != 0) { 308 ALOGE("error setting native window pixel format: %s (%d)", 309 strerror(-err), err); 310 native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL); 311 return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE); 312 } 313 } 314 } 315 316 // the EGL spec requires that a new EGLSurface default to swap interval 317 // 1, so explicitly set that on the window here. 318 ANativeWindow* anw = reinterpret_cast<ANativeWindow*>(window); 319 anw->setSwapInterval(anw, 1); 320 321 EGLSurface surface = cnx->egl.eglCreateWindowSurface( 322 iDpy, config, window, attrib_list); 323 if (surface != EGL_NO_SURFACE) { 324 egl_surface_t* s = new egl_surface_t(dp.get(), config, window, 325 surface, cnx); 326 return s; 327 } 328 329 // EGLSurface creation failed 330 native_window_set_buffers_format(window, 0); 331 native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL); 332 } 333 return EGL_NO_SURFACE; 334} 335 336EGLSurface eglCreatePixmapSurface( EGLDisplay dpy, EGLConfig config, 337 NativePixmapType pixmap, 338 const EGLint *attrib_list) 339{ 340 clearError(); 341 342 egl_connection_t* cnx = NULL; 343 egl_display_ptr dp = validate_display_connection(dpy, cnx); 344 if (dp) { 345 EGLSurface surface = cnx->egl.eglCreatePixmapSurface( 346 dp->disp.dpy, config, pixmap, attrib_list); 347 if (surface != EGL_NO_SURFACE) { 348 egl_surface_t* s = new egl_surface_t(dp.get(), config, NULL, 349 surface, cnx); 350 return s; 351 } 352 } 353 return EGL_NO_SURFACE; 354} 355 356EGLSurface eglCreatePbufferSurface( EGLDisplay dpy, EGLConfig config, 357 const EGLint *attrib_list) 358{ 359 clearError(); 360 361 egl_connection_t* cnx = NULL; 362 egl_display_ptr dp = validate_display_connection(dpy, cnx); 363 if (dp) { 364 EGLSurface surface = cnx->egl.eglCreatePbufferSurface( 365 dp->disp.dpy, config, attrib_list); 366 if (surface != EGL_NO_SURFACE) { 367 egl_surface_t* s = new egl_surface_t(dp.get(), config, NULL, 368 surface, cnx); 369 return s; 370 } 371 } 372 return EGL_NO_SURFACE; 373} 374 375EGLBoolean eglDestroySurface(EGLDisplay dpy, EGLSurface surface) 376{ 377 clearError(); 378 379 const egl_display_ptr dp = validate_display(dpy); 380 if (!dp) return EGL_FALSE; 381 382 SurfaceRef _s(dp.get(), surface); 383 if (!_s.get()) 384 return setError(EGL_BAD_SURFACE, EGL_FALSE); 385 386 egl_surface_t * const s = get_surface(surface); 387 EGLBoolean result = s->cnx->egl.eglDestroySurface(dp->disp.dpy, s->surface); 388 if (result == EGL_TRUE) { 389 _s.terminate(); 390 } 391 return result; 392} 393 394EGLBoolean eglQuerySurface( EGLDisplay dpy, EGLSurface surface, 395 EGLint attribute, EGLint *value) 396{ 397 clearError(); 398 399 const egl_display_ptr dp = validate_display(dpy); 400 if (!dp) return EGL_FALSE; 401 402 SurfaceRef _s(dp.get(), surface); 403 if (!_s.get()) 404 return setError(EGL_BAD_SURFACE, EGL_FALSE); 405 406 egl_surface_t const * const s = get_surface(surface); 407 return s->cnx->egl.eglQuerySurface( 408 dp->disp.dpy, s->surface, attribute, value); 409} 410 411void EGLAPI eglBeginFrame(EGLDisplay dpy, EGLSurface surface) { 412 ATRACE_CALL(); 413 clearError(); 414 415 const egl_display_ptr dp = validate_display(dpy); 416 if (!dp) { 417 return; 418 } 419 420 SurfaceRef _s(dp.get(), surface); 421 if (!_s.get()) { 422 setError(EGL_BAD_SURFACE, EGL_FALSE); 423 return; 424 } 425 426 int64_t timestamp = systemTime(SYSTEM_TIME_MONOTONIC); 427 428 egl_surface_t const * const s = get_surface(surface); 429 native_window_set_buffers_timestamp(s->win.get(), timestamp); 430} 431 432// ---------------------------------------------------------------------------- 433// Contexts 434// ---------------------------------------------------------------------------- 435 436EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config, 437 EGLContext share_list, const EGLint *attrib_list) 438{ 439 clearError(); 440 441 egl_connection_t* cnx = NULL; 442 const egl_display_ptr dp = validate_display_connection(dpy, cnx); 443 if (dpy) { 444 if (share_list != EGL_NO_CONTEXT) { 445 egl_context_t* const c = get_context(share_list); 446 share_list = c->context; 447 } 448 EGLContext context = cnx->egl.eglCreateContext( 449 dp->disp.dpy, config, share_list, attrib_list); 450 if (context != EGL_NO_CONTEXT) { 451 // figure out if it's a GLESv1 or GLESv2 452 int version = 0; 453 if (attrib_list) { 454 while (*attrib_list != EGL_NONE) { 455 GLint attr = *attrib_list++; 456 GLint value = *attrib_list++; 457 if (attr == EGL_CONTEXT_CLIENT_VERSION) { 458 if (value == 1) { 459 version = egl_connection_t::GLESv1_INDEX; 460 } else if (value == 2) { 461 version = egl_connection_t::GLESv2_INDEX; 462 } 463 } 464 }; 465 } 466 egl_context_t* c = new egl_context_t(dpy, context, config, cnx, 467 version); 468#if EGL_TRACE 469 if (getEGLDebugLevel() > 0) 470 GLTrace_eglCreateContext(version, c); 471#endif 472 return c; 473 } else { 474 EGLint error = eglGetError(); 475 ALOGE_IF(error == EGL_SUCCESS, 476 "eglCreateContext(%p, %p, %p, %p) returned EGL_NO_CONTEXT " 477 "but no EGL error!", 478 dpy, config, share_list, attrib_list); 479 } 480 } 481 return EGL_NO_CONTEXT; 482} 483 484EGLBoolean eglDestroyContext(EGLDisplay dpy, EGLContext ctx) 485{ 486 clearError(); 487 488 const egl_display_ptr dp = validate_display(dpy); 489 if (!dp) 490 return EGL_FALSE; 491 492 ContextRef _c(dp.get(), ctx); 493 if (!_c.get()) 494 return setError(EGL_BAD_CONTEXT, EGL_FALSE); 495 496 egl_context_t * const c = get_context(ctx); 497 EGLBoolean result = c->cnx->egl.eglDestroyContext(dp->disp.dpy, c->context); 498 if (result == EGL_TRUE) { 499 _c.terminate(); 500 } 501 return result; 502} 503 504EGLBoolean eglMakeCurrent( EGLDisplay dpy, EGLSurface draw, 505 EGLSurface read, EGLContext ctx) 506{ 507 clearError(); 508 509 egl_display_ptr dp = validate_display(dpy); 510 if (!dp) return setError(EGL_BAD_DISPLAY, EGL_FALSE); 511 512 // If ctx is not EGL_NO_CONTEXT, read is not EGL_NO_SURFACE, or draw is not 513 // EGL_NO_SURFACE, then an EGL_NOT_INITIALIZED error is generated if dpy is 514 // a valid but uninitialized display. 515 if ( (ctx != EGL_NO_CONTEXT) || (read != EGL_NO_SURFACE) || 516 (draw != EGL_NO_SURFACE) ) { 517 if (!dp->isReady()) return setError(EGL_NOT_INITIALIZED, EGL_FALSE); 518 } 519 520 // get a reference to the object passed in 521 ContextRef _c(dp.get(), ctx); 522 SurfaceRef _d(dp.get(), draw); 523 SurfaceRef _r(dp.get(), read); 524 525 // validate the context (if not EGL_NO_CONTEXT) 526 if ((ctx != EGL_NO_CONTEXT) && !_c.get()) { 527 // EGL_NO_CONTEXT is valid 528 return EGL_FALSE; 529 } 530 531 // these are the underlying implementation's object 532 EGLContext impl_ctx = EGL_NO_CONTEXT; 533 EGLSurface impl_draw = EGL_NO_SURFACE; 534 EGLSurface impl_read = EGL_NO_SURFACE; 535 536 // these are our objects structs passed in 537 egl_context_t * c = NULL; 538 egl_surface_t const * d = NULL; 539 egl_surface_t const * r = NULL; 540 541 // these are the current objects structs 542 egl_context_t * cur_c = get_context(getContext()); 543 544 if (ctx != EGL_NO_CONTEXT) { 545 c = get_context(ctx); 546 impl_ctx = c->context; 547 } else { 548 // no context given, use the implementation of the current context 549 if (cur_c == NULL) { 550 // no current context 551 if (draw != EGL_NO_SURFACE || read != EGL_NO_SURFACE) { 552 // calling eglMakeCurrent( ..., !=0, !=0, EGL_NO_CONTEXT); 553 return setError(EGL_BAD_MATCH, EGL_FALSE); 554 } 555 // not an error, there is just no current context. 556 return EGL_TRUE; 557 } 558 } 559 560 // retrieve the underlying implementation's draw EGLSurface 561 if (draw != EGL_NO_SURFACE) { 562 d = get_surface(draw); 563 impl_draw = d->surface; 564 } 565 566 // retrieve the underlying implementation's read EGLSurface 567 if (read != EGL_NO_SURFACE) { 568 r = get_surface(read); 569 impl_read = r->surface; 570 } 571 572 573 EGLBoolean result = dp->makeCurrent(c, cur_c, 574 draw, read, ctx, 575 impl_draw, impl_read, impl_ctx); 576 577 if (result == EGL_TRUE) { 578 if (c) { 579 setGLHooksThreadSpecific(c->cnx->hooks[c->version]); 580 egl_tls_t::setContext(ctx); 581#if EGL_TRACE 582 if (getEGLDebugLevel() > 0) 583 GLTrace_eglMakeCurrent(c->version, c->cnx->hooks[c->version], ctx); 584#endif 585 _c.acquire(); 586 _r.acquire(); 587 _d.acquire(); 588 } else { 589 setGLHooksThreadSpecific(&gHooksNoContext); 590 egl_tls_t::setContext(EGL_NO_CONTEXT); 591 } 592 } else { 593 // this will ALOGE the error 594 result = setError(c->cnx->egl.eglGetError(), EGL_FALSE); 595 } 596 return result; 597} 598 599 600EGLBoolean eglQueryContext( EGLDisplay dpy, EGLContext ctx, 601 EGLint attribute, EGLint *value) 602{ 603 clearError(); 604 605 const egl_display_ptr dp = validate_display(dpy); 606 if (!dp) return EGL_FALSE; 607 608 ContextRef _c(dp.get(), ctx); 609 if (!_c.get()) return setError(EGL_BAD_CONTEXT, EGL_FALSE); 610 611 egl_context_t * const c = get_context(ctx); 612 return c->cnx->egl.eglQueryContext( 613 dp->disp.dpy, c->context, attribute, value); 614 615} 616 617EGLContext eglGetCurrentContext(void) 618{ 619 // could be called before eglInitialize(), but we wouldn't have a context 620 // then, and this function would correctly return EGL_NO_CONTEXT. 621 622 clearError(); 623 624 EGLContext ctx = getContext(); 625 return ctx; 626} 627 628EGLSurface eglGetCurrentSurface(EGLint readdraw) 629{ 630 // could be called before eglInitialize(), but we wouldn't have a context 631 // then, and this function would correctly return EGL_NO_SURFACE. 632 633 clearError(); 634 635 EGLContext ctx = getContext(); 636 if (ctx) { 637 egl_context_t const * const c = get_context(ctx); 638 if (!c) return setError(EGL_BAD_CONTEXT, EGL_NO_SURFACE); 639 switch (readdraw) { 640 case EGL_READ: return c->read; 641 case EGL_DRAW: return c->draw; 642 default: return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE); 643 } 644 } 645 return EGL_NO_SURFACE; 646} 647 648EGLDisplay eglGetCurrentDisplay(void) 649{ 650 // could be called before eglInitialize(), but we wouldn't have a context 651 // then, and this function would correctly return EGL_NO_DISPLAY. 652 653 clearError(); 654 655 EGLContext ctx = getContext(); 656 if (ctx) { 657 egl_context_t const * const c = get_context(ctx); 658 if (!c) return setError(EGL_BAD_CONTEXT, EGL_NO_SURFACE); 659 return c->dpy; 660 } 661 return EGL_NO_DISPLAY; 662} 663 664EGLBoolean eglWaitGL(void) 665{ 666 clearError(); 667 668 egl_connection_t* const cnx = &gEGLImpl; 669 if (!cnx->dso) 670 return setError(EGL_BAD_CONTEXT, EGL_FALSE); 671 672 return cnx->egl.eglWaitGL(); 673} 674 675EGLBoolean eglWaitNative(EGLint engine) 676{ 677 clearError(); 678 679 egl_connection_t* const cnx = &gEGLImpl; 680 if (!cnx->dso) 681 return setError(EGL_BAD_CONTEXT, EGL_FALSE); 682 683 return cnx->egl.eglWaitNative(engine); 684} 685 686EGLint eglGetError(void) 687{ 688 EGLint err = EGL_SUCCESS; 689 egl_connection_t* const cnx = &gEGLImpl; 690 if (cnx->dso) { 691 err = cnx->egl.eglGetError(); 692 } 693 if (err == EGL_SUCCESS) { 694 err = egl_tls_t::getError(); 695 } 696 return err; 697} 698 699__eglMustCastToProperFunctionPointerType eglGetProcAddress(const char *procname) 700{ 701 // eglGetProcAddress() could be the very first function called 702 // in which case we must make sure we've initialized ourselves, this 703 // happens the first time egl_get_display() is called. 704 705 clearError(); 706 707 if (egl_init_drivers() == EGL_FALSE) { 708 setError(EGL_BAD_PARAMETER, NULL); 709 return NULL; 710 } 711 712 // These extensions should not be exposed to applications. They're used 713 // internally by the Android EGL layer. 714 if (!strcmp(procname, "eglSetBlobCacheFuncsANDROID") || 715 !strcmp(procname, "eglDupNativeFenceFDANDROID") || 716 !strcmp(procname, "eglWaitSyncANDROID") || 717 !strcmp(procname, "eglHibernateProcessIMG") || 718 !strcmp(procname, "eglAwakenProcessIMG")) { 719 return NULL; 720 } 721 722 __eglMustCastToProperFunctionPointerType addr; 723 addr = findProcAddress(procname, sExtentionMap, NELEM(sExtentionMap)); 724 if (addr) return addr; 725 726 727 // this protects accesses to sGLExtentionMap and sGLExtentionSlot 728 pthread_mutex_lock(&sExtensionMapMutex); 729 730 /* 731 * Since eglGetProcAddress() is not associated to anything, it needs 732 * to return a function pointer that "works" regardless of what 733 * the current context is. 734 * 735 * For this reason, we return a "forwarder", a small stub that takes 736 * care of calling the function associated with the context 737 * currently bound. 738 * 739 * We first look for extensions we've already resolved, if we're seeing 740 * this extension for the first time, we go through all our 741 * implementations and call eglGetProcAddress() and record the 742 * result in the appropriate implementation hooks and return the 743 * address of the forwarder corresponding to that hook set. 744 * 745 */ 746 747 const String8 name(procname); 748 addr = sGLExtentionMap.valueFor(name); 749 const int slot = sGLExtentionSlot; 750 751 ALOGE_IF(slot >= MAX_NUMBER_OF_GL_EXTENSIONS, 752 "no more slots for eglGetProcAddress(\"%s\")", 753 procname); 754 755#if EGL_TRACE 756 gl_hooks_t *debugHooks = GLTrace_getGLHooks(); 757#endif 758 759 if (!addr && (slot < MAX_NUMBER_OF_GL_EXTENSIONS)) { 760 bool found = false; 761 762 egl_connection_t* const cnx = &gEGLImpl; 763 if (cnx->dso && cnx->egl.eglGetProcAddress) { 764 // Extensions are independent of the bound context 765 addr = 766 cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[slot] = 767 cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] = 768#if EGL_TRACE 769 debugHooks->ext.extensions[slot] = 770 gHooksTrace.ext.extensions[slot] = 771#endif 772 cnx->egl.eglGetProcAddress(procname); 773 if (addr) found = true; 774 } 775 776 if (found) { 777#if USE_FAST_TLS_KEY 778 addr = gExtensionForwarders[slot]; 779#endif 780 sGLExtentionMap.add(name, addr); 781 sGLExtentionSlot++; 782 } 783 } 784 785 pthread_mutex_unlock(&sExtensionMapMutex); 786 return addr; 787} 788 789class FrameCompletionThread : public Thread { 790public: 791 792 static void queueSync(EGLSyncKHR sync) { 793 static sp<FrameCompletionThread> thread(new FrameCompletionThread); 794 static bool running = false; 795 if (!running) { 796 thread->run("GPUFrameCompletion"); 797 running = true; 798 } 799 { 800 Mutex::Autolock lock(thread->mMutex); 801 ScopedTrace st(ATRACE_TAG, String8::format("kicked off frame %d", 802 thread->mFramesQueued).string()); 803 thread->mQueue.push_back(sync); 804 thread->mCondition.signal(); 805 thread->mFramesQueued++; 806 ATRACE_INT("GPU Frames Outstanding", thread->mQueue.size()); 807 } 808 } 809 810private: 811 FrameCompletionThread() : mFramesQueued(0), mFramesCompleted(0) {} 812 813 virtual bool threadLoop() { 814 EGLSyncKHR sync; 815 uint32_t frameNum; 816 { 817 Mutex::Autolock lock(mMutex); 818 while (mQueue.isEmpty()) { 819 mCondition.wait(mMutex); 820 } 821 sync = mQueue[0]; 822 frameNum = mFramesCompleted; 823 } 824 EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); 825 { 826 ScopedTrace st(ATRACE_TAG, String8::format("waiting for frame %d", 827 frameNum).string()); 828 EGLint result = eglClientWaitSyncKHR(dpy, sync, 0, EGL_FOREVER_KHR); 829 if (result == EGL_FALSE) { 830 ALOGE("FrameCompletion: error waiting for fence: %#x", eglGetError()); 831 } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { 832 ALOGE("FrameCompletion: timeout waiting for fence"); 833 } 834 eglDestroySyncKHR(dpy, sync); 835 } 836 { 837 Mutex::Autolock lock(mMutex); 838 mQueue.removeAt(0); 839 mFramesCompleted++; 840 ATRACE_INT("GPU Frames Outstanding", mQueue.size()); 841 } 842 return true; 843 } 844 845 uint32_t mFramesQueued; 846 uint32_t mFramesCompleted; 847 Vector<EGLSyncKHR> mQueue; 848 Condition mCondition; 849 Mutex mMutex; 850}; 851 852EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface draw) 853{ 854 ATRACE_CALL(); 855 clearError(); 856 857 const egl_display_ptr dp = validate_display(dpy); 858 if (!dp) return EGL_FALSE; 859 860 SurfaceRef _s(dp.get(), draw); 861 if (!_s.get()) 862 return setError(EGL_BAD_SURFACE, EGL_FALSE); 863 864#if EGL_TRACE 865 gl_hooks_t const *trace_hooks = getGLTraceThreadSpecific(); 866 if (getEGLDebugLevel() > 0) { 867 if (trace_hooks == NULL) { 868 if (GLTrace_start() < 0) { 869 ALOGE("Disabling Tracer for OpenGL ES"); 870 setEGLDebugLevel(0); 871 } else { 872 // switch over to the trace version of hooks 873 EGLContext ctx = egl_tls_t::getContext(); 874 egl_context_t * const c = get_context(ctx); 875 if (c) { 876 setGLHooksThreadSpecific(c->cnx->hooks[c->version]); 877 GLTrace_eglMakeCurrent(c->version, c->cnx->hooks[c->version], ctx); 878 } 879 } 880 } 881 882 GLTrace_eglSwapBuffers(dpy, draw); 883 } else if (trace_hooks != NULL) { 884 // tracing is now disabled, so switch back to the non trace version 885 EGLContext ctx = egl_tls_t::getContext(); 886 egl_context_t * const c = get_context(ctx); 887 if (c) setGLHooksThreadSpecific(c->cnx->hooks[c->version]); 888 GLTrace_stop(); 889 } 890#endif 891 892 egl_surface_t const * const s = get_surface(draw); 893 894 if (CC_UNLIKELY(dp->finishOnSwap)) { 895 uint32_t pixel; 896 egl_context_t * const c = get_context( egl_tls_t::getContext() ); 897 if (c) { 898 // glReadPixels() ensures that the frame is complete 899 s->cnx->hooks[c->version]->gl.glReadPixels(0,0,1,1, 900 GL_RGBA,GL_UNSIGNED_BYTE,&pixel); 901 } 902 } 903 904 EGLBoolean result = s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface); 905 906 if (CC_UNLIKELY(dp->traceGpuCompletion)) { 907 EGLSyncKHR sync = EGL_NO_SYNC_KHR; 908 { 909 sync = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, NULL); 910 } 911 if (sync != EGL_NO_SYNC_KHR) { 912 FrameCompletionThread::queueSync(sync); 913 } 914 } 915 916 return result; 917} 918 919EGLBoolean eglCopyBuffers( EGLDisplay dpy, EGLSurface surface, 920 NativePixmapType target) 921{ 922 clearError(); 923 924 const egl_display_ptr dp = validate_display(dpy); 925 if (!dp) return EGL_FALSE; 926 927 SurfaceRef _s(dp.get(), surface); 928 if (!_s.get()) 929 return setError(EGL_BAD_SURFACE, EGL_FALSE); 930 931 egl_surface_t const * const s = get_surface(surface); 932 return s->cnx->egl.eglCopyBuffers(dp->disp.dpy, s->surface, target); 933} 934 935const char* eglQueryString(EGLDisplay dpy, EGLint name) 936{ 937 clearError(); 938 939 const egl_display_ptr dp = validate_display(dpy); 940 if (!dp) return (const char *) NULL; 941 942 switch (name) { 943 case EGL_VENDOR: 944 return dp->getVendorString(); 945 case EGL_VERSION: 946 return dp->getVersionString(); 947 case EGL_EXTENSIONS: 948 return dp->getExtensionString(); 949 case EGL_CLIENT_APIS: 950 return dp->getClientApiString(); 951 case EGL_VERSION_HW_ANDROID: 952 return dp->disp.queryString.version; 953 } 954 return setError(EGL_BAD_PARAMETER, (const char *)0); 955} 956 957 958// ---------------------------------------------------------------------------- 959// EGL 1.1 960// ---------------------------------------------------------------------------- 961 962EGLBoolean eglSurfaceAttrib( 963 EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value) 964{ 965 clearError(); 966 967 const egl_display_ptr dp = validate_display(dpy); 968 if (!dp) return EGL_FALSE; 969 970 SurfaceRef _s(dp.get(), surface); 971 if (!_s.get()) 972 return setError(EGL_BAD_SURFACE, EGL_FALSE); 973 974 egl_surface_t const * const s = get_surface(surface); 975 if (s->cnx->egl.eglSurfaceAttrib) { 976 return s->cnx->egl.eglSurfaceAttrib( 977 dp->disp.dpy, s->surface, attribute, value); 978 } 979 return setError(EGL_BAD_SURFACE, EGL_FALSE); 980} 981 982EGLBoolean eglBindTexImage( 983 EGLDisplay dpy, EGLSurface surface, EGLint buffer) 984{ 985 clearError(); 986 987 const egl_display_ptr dp = validate_display(dpy); 988 if (!dp) return EGL_FALSE; 989 990 SurfaceRef _s(dp.get(), surface); 991 if (!_s.get()) 992 return setError(EGL_BAD_SURFACE, EGL_FALSE); 993 994 egl_surface_t const * const s = get_surface(surface); 995 if (s->cnx->egl.eglBindTexImage) { 996 return s->cnx->egl.eglBindTexImage( 997 dp->disp.dpy, s->surface, buffer); 998 } 999 return setError(EGL_BAD_SURFACE, EGL_FALSE); 1000} 1001 1002EGLBoolean eglReleaseTexImage( 1003 EGLDisplay dpy, EGLSurface surface, EGLint buffer) 1004{ 1005 clearError(); 1006 1007 const egl_display_ptr dp = validate_display(dpy); 1008 if (!dp) return EGL_FALSE; 1009 1010 SurfaceRef _s(dp.get(), surface); 1011 if (!_s.get()) 1012 return setError(EGL_BAD_SURFACE, EGL_FALSE); 1013 1014 egl_surface_t const * const s = get_surface(surface); 1015 if (s->cnx->egl.eglReleaseTexImage) { 1016 return s->cnx->egl.eglReleaseTexImage( 1017 dp->disp.dpy, s->surface, buffer); 1018 } 1019 return setError(EGL_BAD_SURFACE, EGL_FALSE); 1020} 1021 1022EGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint interval) 1023{ 1024 clearError(); 1025 1026 const egl_display_ptr dp = validate_display(dpy); 1027 if (!dp) return EGL_FALSE; 1028 1029 EGLBoolean res = EGL_TRUE; 1030 egl_connection_t* const cnx = &gEGLImpl; 1031 if (cnx->dso && cnx->egl.eglSwapInterval) { 1032 res = cnx->egl.eglSwapInterval(dp->disp.dpy, interval); 1033 } 1034 1035 return res; 1036} 1037 1038 1039// ---------------------------------------------------------------------------- 1040// EGL 1.2 1041// ---------------------------------------------------------------------------- 1042 1043EGLBoolean eglWaitClient(void) 1044{ 1045 clearError(); 1046 1047 egl_connection_t* const cnx = &gEGLImpl; 1048 if (!cnx->dso) 1049 return setError(EGL_BAD_CONTEXT, EGL_FALSE); 1050 1051 EGLBoolean res; 1052 if (cnx->egl.eglWaitClient) { 1053 res = cnx->egl.eglWaitClient(); 1054 } else { 1055 res = cnx->egl.eglWaitGL(); 1056 } 1057 return res; 1058} 1059 1060EGLBoolean eglBindAPI(EGLenum api) 1061{ 1062 clearError(); 1063 1064 if (egl_init_drivers() == EGL_FALSE) { 1065 return setError(EGL_BAD_PARAMETER, EGL_FALSE); 1066 } 1067 1068 // bind this API on all EGLs 1069 EGLBoolean res = EGL_TRUE; 1070 egl_connection_t* const cnx = &gEGLImpl; 1071 if (cnx->dso && cnx->egl.eglBindAPI) { 1072 res = cnx->egl.eglBindAPI(api); 1073 } 1074 return res; 1075} 1076 1077EGLenum eglQueryAPI(void) 1078{ 1079 clearError(); 1080 1081 if (egl_init_drivers() == EGL_FALSE) { 1082 return setError(EGL_BAD_PARAMETER, EGL_FALSE); 1083 } 1084 1085 egl_connection_t* const cnx = &gEGLImpl; 1086 if (cnx->dso && cnx->egl.eglQueryAPI) { 1087 return cnx->egl.eglQueryAPI(); 1088 } 1089 1090 // or, it can only be OpenGL ES 1091 return EGL_OPENGL_ES_API; 1092} 1093 1094EGLBoolean eglReleaseThread(void) 1095{ 1096 clearError(); 1097 1098 // If there is context bound to the thread, release it 1099 egl_display_t::loseCurrent(get_context(getContext())); 1100 1101 egl_connection_t* const cnx = &gEGLImpl; 1102 if (cnx->dso && cnx->egl.eglReleaseThread) { 1103 cnx->egl.eglReleaseThread(); 1104 } 1105 1106 egl_tls_t::clearTLS(); 1107#if EGL_TRACE 1108 if (getEGLDebugLevel() > 0) 1109 GLTrace_eglReleaseThread(); 1110#endif 1111 return EGL_TRUE; 1112} 1113 1114EGLSurface eglCreatePbufferFromClientBuffer( 1115 EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer, 1116 EGLConfig config, const EGLint *attrib_list) 1117{ 1118 clearError(); 1119 1120 egl_connection_t* cnx = NULL; 1121 const egl_display_ptr dp = validate_display_connection(dpy, cnx); 1122 if (!dp) return EGL_FALSE; 1123 if (cnx->egl.eglCreatePbufferFromClientBuffer) { 1124 return cnx->egl.eglCreatePbufferFromClientBuffer( 1125 dp->disp.dpy, buftype, buffer, config, attrib_list); 1126 } 1127 return setError(EGL_BAD_CONFIG, EGL_NO_SURFACE); 1128} 1129 1130// ---------------------------------------------------------------------------- 1131// EGL_EGLEXT_VERSION 3 1132// ---------------------------------------------------------------------------- 1133 1134EGLBoolean eglLockSurfaceKHR(EGLDisplay dpy, EGLSurface surface, 1135 const EGLint *attrib_list) 1136{ 1137 clearError(); 1138 1139 const egl_display_ptr dp = validate_display(dpy); 1140 if (!dp) return EGL_FALSE; 1141 1142 SurfaceRef _s(dp.get(), surface); 1143 if (!_s.get()) 1144 return setError(EGL_BAD_SURFACE, EGL_FALSE); 1145 1146 egl_surface_t const * const s = get_surface(surface); 1147 if (s->cnx->egl.eglLockSurfaceKHR) { 1148 return s->cnx->egl.eglLockSurfaceKHR( 1149 dp->disp.dpy, s->surface, attrib_list); 1150 } 1151 return setError(EGL_BAD_DISPLAY, EGL_FALSE); 1152} 1153 1154EGLBoolean eglUnlockSurfaceKHR(EGLDisplay dpy, EGLSurface surface) 1155{ 1156 clearError(); 1157 1158 const egl_display_ptr dp = validate_display(dpy); 1159 if (!dp) return EGL_FALSE; 1160 1161 SurfaceRef _s(dp.get(), surface); 1162 if (!_s.get()) 1163 return setError(EGL_BAD_SURFACE, EGL_FALSE); 1164 1165 egl_surface_t const * const s = get_surface(surface); 1166 if (s->cnx->egl.eglUnlockSurfaceKHR) { 1167 return s->cnx->egl.eglUnlockSurfaceKHR(dp->disp.dpy, s->surface); 1168 } 1169 return setError(EGL_BAD_DISPLAY, EGL_FALSE); 1170} 1171 1172EGLImageKHR eglCreateImageKHR(EGLDisplay dpy, EGLContext ctx, EGLenum target, 1173 EGLClientBuffer buffer, const EGLint *attrib_list) 1174{ 1175 clearError(); 1176 1177 const egl_display_ptr dp = validate_display(dpy); 1178 if (!dp) return EGL_NO_IMAGE_KHR; 1179 1180 ContextRef _c(dp.get(), ctx); 1181 egl_context_t * const c = _c.get(); 1182 1183 EGLImageKHR result = EGL_NO_IMAGE_KHR; 1184 egl_connection_t* const cnx = &gEGLImpl; 1185 if (cnx->dso && cnx->egl.eglCreateImageKHR) { 1186 result = cnx->egl.eglCreateImageKHR( 1187 dp->disp.dpy, 1188 c ? c->context : EGL_NO_CONTEXT, 1189 target, buffer, attrib_list); 1190 } 1191 return result; 1192} 1193 1194EGLBoolean eglDestroyImageKHR(EGLDisplay dpy, EGLImageKHR img) 1195{ 1196 clearError(); 1197 1198 const egl_display_ptr dp = validate_display(dpy); 1199 if (!dp) return EGL_FALSE; 1200 1201 EGLBoolean result = EGL_FALSE; 1202 egl_connection_t* const cnx = &gEGLImpl; 1203 if (cnx->dso && cnx->egl.eglDestroyImageKHR) { 1204 result = cnx->egl.eglDestroyImageKHR(dp->disp.dpy, img); 1205 } 1206 return result; 1207} 1208 1209// ---------------------------------------------------------------------------- 1210// EGL_EGLEXT_VERSION 5 1211// ---------------------------------------------------------------------------- 1212 1213 1214EGLSyncKHR eglCreateSyncKHR(EGLDisplay dpy, EGLenum type, const EGLint *attrib_list) 1215{ 1216 clearError(); 1217 1218 const egl_display_ptr dp = validate_display(dpy); 1219 if (!dp) return EGL_NO_SYNC_KHR; 1220 1221 EGLSyncKHR result = EGL_NO_SYNC_KHR; 1222 egl_connection_t* const cnx = &gEGLImpl; 1223 if (cnx->dso && cnx->egl.eglCreateSyncKHR) { 1224 result = cnx->egl.eglCreateSyncKHR(dp->disp.dpy, type, attrib_list); 1225 } 1226 return result; 1227} 1228 1229EGLBoolean eglDestroySyncKHR(EGLDisplay dpy, EGLSyncKHR sync) 1230{ 1231 clearError(); 1232 1233 const egl_display_ptr dp = validate_display(dpy); 1234 if (!dp) return EGL_FALSE; 1235 1236 EGLBoolean result = EGL_FALSE; 1237 egl_connection_t* const cnx = &gEGLImpl; 1238 if (cnx->dso && cnx->egl.eglDestroySyncKHR) { 1239 result = cnx->egl.eglDestroySyncKHR(dp->disp.dpy, sync); 1240 } 1241 return result; 1242} 1243 1244EGLint eglClientWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, 1245 EGLint flags, EGLTimeKHR timeout) 1246{ 1247 clearError(); 1248 1249 const egl_display_ptr dp = validate_display(dpy); 1250 if (!dp) return EGL_FALSE; 1251 1252 EGLBoolean result = EGL_FALSE; 1253 egl_connection_t* const cnx = &gEGLImpl; 1254 if (cnx->dso && cnx->egl.eglClientWaitSyncKHR) { 1255 result = cnx->egl.eglClientWaitSyncKHR( 1256 dp->disp.dpy, sync, flags, timeout); 1257 } 1258 return result; 1259} 1260 1261EGLBoolean eglGetSyncAttribKHR(EGLDisplay dpy, EGLSyncKHR sync, 1262 EGLint attribute, EGLint *value) 1263{ 1264 clearError(); 1265 1266 const egl_display_ptr dp = validate_display(dpy); 1267 if (!dp) return EGL_FALSE; 1268 1269 EGLBoolean result = EGL_FALSE; 1270 egl_connection_t* const cnx = &gEGLImpl; 1271 if (cnx->dso && cnx->egl.eglGetSyncAttribKHR) { 1272 result = cnx->egl.eglGetSyncAttribKHR( 1273 dp->disp.dpy, sync, attribute, value); 1274 } 1275 return result; 1276} 1277 1278// ---------------------------------------------------------------------------- 1279// ANDROID extensions 1280// ---------------------------------------------------------------------------- 1281 1282EGLint eglDupNativeFenceFDANDROID(EGLDisplay dpy, EGLSyncKHR sync) 1283{ 1284 clearError(); 1285 1286 const egl_display_ptr dp = validate_display(dpy); 1287 if (!dp) return EGL_NO_NATIVE_FENCE_FD_ANDROID; 1288 1289 EGLint result = EGL_NO_NATIVE_FENCE_FD_ANDROID; 1290 egl_connection_t* const cnx = &gEGLImpl; 1291 if (cnx->dso && cnx->egl.eglDupNativeFenceFDANDROID) { 1292 result = cnx->egl.eglDupNativeFenceFDANDROID(dp->disp.dpy, sync); 1293 } 1294 return result; 1295} 1296 1297EGLint eglWaitSyncANDROID(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags) 1298{ 1299 clearError(); 1300 1301 const egl_display_ptr dp = validate_display(dpy); 1302 if (!dp) return EGL_NO_NATIVE_FENCE_FD_ANDROID; 1303 1304 EGLint result = EGL_FALSE; 1305 egl_connection_t* const cnx = &gEGLImpl; 1306 if (cnx->dso && cnx->egl.eglWaitSyncANDROID) { 1307 result = cnx->egl.eglWaitSyncANDROID(dp->disp.dpy, sync, flags); 1308 } 1309 return result; 1310} 1311 1312// ---------------------------------------------------------------------------- 1313// NVIDIA extensions 1314// ---------------------------------------------------------------------------- 1315EGLuint64NV eglGetSystemTimeFrequencyNV() 1316{ 1317 clearError(); 1318 1319 if (egl_init_drivers() == EGL_FALSE) { 1320 return setError(EGL_BAD_PARAMETER, EGL_FALSE); 1321 } 1322 1323 EGLuint64NV ret = 0; 1324 egl_connection_t* const cnx = &gEGLImpl; 1325 1326 if (cnx->dso && cnx->egl.eglGetSystemTimeFrequencyNV) { 1327 return cnx->egl.eglGetSystemTimeFrequencyNV(); 1328 } 1329 1330 return setErrorQuiet(EGL_BAD_DISPLAY, 0); 1331} 1332 1333EGLuint64NV eglGetSystemTimeNV() 1334{ 1335 clearError(); 1336 1337 if (egl_init_drivers() == EGL_FALSE) { 1338 return setError(EGL_BAD_PARAMETER, EGL_FALSE); 1339 } 1340 1341 EGLuint64NV ret = 0; 1342 egl_connection_t* const cnx = &gEGLImpl; 1343 1344 if (cnx->dso && cnx->egl.eglGetSystemTimeNV) { 1345 return cnx->egl.eglGetSystemTimeNV(); 1346 } 1347 1348 return setErrorQuiet(EGL_BAD_DISPLAY, 0); 1349} 1350