1 /* 2 * Copyright (C) 2011 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 // 18 // WARNING -------------------------- WARNING 19 // This code meant to be used for testing purposes only. It is not production 20 // level quality. 21 // Use on your own risk !! 22 // 23 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <string.h> 27 #include <dlfcn.h> 28 #include "egl_dispatch.h" 29 #include "egl_ftable.h" 30 #include <cutils/process_name.h> 31 #include <cutils/log.h> 32 #include "ServerConnection.h" 33 #include "ThreadInfo.h" 34 #include <pthread.h> 35 #include "gl_wrapper_context.h" 36 #include "gl2_wrapper_context.h" 37 38 #define GLES_EMUL_TARGETS_FILE "/system/etc/gles_emul.cfg" 39 // implementation libraries; 40 #define GLESv1_enc_LIB "/system/lib/libGLESv1_enc.so" 41 #define GLESv2_enc_LIB "/system/lib/libGLESv2_enc.so" 42 #define GLES_android_LIB "/system/lib/egl/libGLES_android.so" 43 // driver libraries; 44 #define GLESv1_DRIVER "/system/lib/egl/libGLESv1_CM_emul.so" 45 #define GLESv2_DRIVER "/system/lib/egl/libGLESv2_emul.so" 46 47 48 static struct egl_dispatch *s_dispatch = NULL; 49 pthread_once_t dispatchTablesInitialized = PTHREAD_ONCE_INIT; 50 51 static bool s_needEncode = false; 52 53 static gl_wrapper_context_t *g_gl_dispatch = NULL; 54 static gl2_wrapper_context_t *g_gl2_dispatch = NULL; 55 56 template <class T> 57 int initApi(const char *driverLibName, const char *implLibName, T **dispatchTable, T *(*accessor)()) 58 { 59 void *driverLib = dlopen(driverLibName, RTLD_NOW | RTLD_LOCAL); 60 if (driverLib == NULL) { 61 ALOGE("failed to load %s : %s\n", driverLibName, dlerror()); 62 return -1; 63 } 64 65 typedef T *(*createFcn_t)(void *, T *(*accessor)()); 66 createFcn_t createFcn; 67 createFcn = (createFcn_t) dlsym(driverLib, "createFromLib"); 68 if (createFcn == NULL) { 69 ALOGE("failed to load createFromLib constructor function\n"); 70 return -1; 71 } 72 73 void *implLib = dlopen(implLibName, RTLD_NOW | RTLD_LOCAL); 74 if (implLib == NULL) { 75 ALOGE("couldn't open %s", implLibName); 76 return -2; 77 } 78 *dispatchTable = createFcn(implLib, accessor); 79 if (*dispatchTable == NULL) { 80 return -3; 81 } 82 83 // XXX - we do close the impl library since it doesn't have data, as far as we concern. 84 dlclose(implLib); 85 86 // XXX - we do not dlclose the driver library, so its not initialized when 87 // later loaded by android - is this required? 88 ALOGD("loading %s into %s complete\n", implLibName, driverLibName); 89 return 0; 90 91 } 92 93 static gl_wrapper_context_t *getGLContext() 94 { 95 return g_gl_dispatch; 96 } 97 98 static gl2_wrapper_context_t *getGL2Context() 99 { 100 return g_gl2_dispatch; 101 } 102 103 const char *getProcName() 104 { 105 static const char *procname = NULL; 106 107 if (procname == NULL) { 108 const char *str = get_process_name(); 109 if (strcmp(str, "unknown") != 0) { 110 procname = str; 111 } else { 112 // we need to obtain our process name from the command line; 113 FILE *fp = fopen("/proc/self/cmdline", "rt"); 114 if (fp == NULL) { 115 ALOGE("couldn't open /proc/self/cmdline\n"); 116 return NULL; 117 } 118 119 char line[1000]; 120 if (fgets(line, sizeof(line), fp) == NULL) { 121 ALOGE("couldn't read the self cmdline from \n"); 122 fclose(fp); 123 return NULL; 124 } 125 fclose(fp); 126 127 if (line[0] == '\0') { 128 ALOGE("cmdline is empty\n"); 129 return NULL; 130 } 131 132 //obtain the basename; 133 line[sizeof(line) - 1] = '\0'; 134 char *p = line; 135 while (*p != '\0' && 136 *p != '\t' && 137 *p != ' ' && 138 *p != '\n') { 139 p++; 140 } 141 142 *p = '\0'; p--; 143 while (p > line && *p != '/') p--; 144 if (*p == '/') p++; 145 procname = strdup(p); 146 } 147 } 148 149 return procname; 150 } 151 152 153 154 bool isNeedEncode() 155 { 156 const char *procname = getProcName(); 157 if (procname == NULL) return false; 158 ALOGD("isNeedEncode? for %s\n", procname); 159 // check on our whitelist 160 FILE *fp = fopen(GLES_EMUL_TARGETS_FILE, "rt"); 161 if (fp == NULL) { 162 ALOGE("couldn't open %s\n", GLES_EMUL_TARGETS_FILE); 163 return false; 164 } 165 166 char line[100]; 167 bool found = false; 168 size_t procnameLen = strlen(procname); 169 170 while (fgets(line, sizeof(line), fp) != NULL) { 171 if (strlen(line) >= procnameLen && 172 !strncmp(procname, line, procnameLen)) { 173 char c = line[procnameLen]; 174 if (c == '\0' || c == ' ' || c == '\t' || c == '\n') { 175 found = true; 176 ALOGD("should use encoder for %s\n", procname); 177 break; 178 } 179 } 180 } 181 fclose(fp); 182 return found; 183 } 184 185 void initDispatchTables() 186 { 187 // 188 // Load our back-end implementation of EGL/GLES 189 // 190 ALOGD("Loading egl dispatch for %s\n", getProcName()); 191 192 void *gles_android = dlopen("/system/lib/egl/libGLES_android.so", RTLD_NOW | RTLD_LOCAL); 193 if (!gles_android) { 194 fprintf(stderr,"FATAL ERROR: Could not load libGLES_android lib\n"); 195 exit(-1); 196 } 197 198 // 199 // Load back-end EGL implementation library 200 // 201 s_dispatch = create_egl_dispatch( gles_android ); 202 if (!s_dispatch) { 203 fprintf(stderr,"FATAL ERROR: Could not create egl dispatch\n"); 204 exit(-1); 205 } 206 207 // 208 // initialize gles 209 // 210 s_needEncode = isNeedEncode(); 211 void *gles_encoder = NULL; 212 if (s_needEncode) { 213 // initialize a connection to the server, and the GLESv1/v2 encoders; 214 ServerConnection * connection = ServerConnection::s_getServerConnection(); 215 if (connection == NULL) { 216 ALOGE("couldn't create server connection\n"); 217 s_needEncode = false; 218 } 219 } 220 221 // init dispatch tabels for GLESv1 & GLESv2 222 if (s_needEncode) { 223 // XXX - we do not check the retrun value because there isn't much we can do here on failure. 224 225 if (initApi<gl_wrapper_context_t>(GLESv1_DRIVER, GLESv1_enc_LIB, &g_gl_dispatch, getGLContext) < 0) { 226 // fallback to android on faluire 227 s_needEncode = false; 228 } else { 229 initApi<gl2_wrapper_context_t>(GLESv2_DRIVER, GLESv2_enc_LIB, &g_gl2_dispatch, getGL2Context); 230 } 231 } 232 233 if (!s_needEncode) { 234 ALOGD("Initializing native opengl for %s\n", getProcName()); 235 initApi<gl_wrapper_context_t>(GLESv1_DRIVER, GLES_android_LIB, &g_gl_dispatch, getGLContext); 236 // try to initialize gl2 from GLES, though its probably going to fail 237 initApi<gl2_wrapper_context_t>(GLESv2_DRIVER, GLES_android_LIB, &g_gl2_dispatch, getGL2Context); 238 } 239 } 240 241 static struct egl_dispatch *getDispatch() 242 { 243 pthread_once(&dispatchTablesInitialized, initDispatchTables); 244 return s_dispatch; 245 } 246 247 __eglMustCastToProperFunctionPointerType eglGetProcAddress(const char *procname) 248 { 249 250 // search in EGL function table 251 for (int i=0; i<egl_num_funcs; i++) { 252 if (!strcmp(egl_funcs_by_name[i].name, procname)) { 253 return (__eglMustCastToProperFunctionPointerType)egl_funcs_by_name[i].proc; 254 } 255 } 256 257 // we do not support eglGetProcAddress for GLESv1 & GLESv2. The loader 258 // should be able to find this function through dynamic loading. 259 return NULL; 260 } 261 262 //////////////// Path through functions ////////// 263 264 EGLint eglGetError() 265 { 266 return getDispatch()->eglGetError(); 267 } 268 269 EGLDisplay eglGetDisplay(EGLNativeDisplayType display_id) 270 { 271 return getDispatch()->eglGetDisplay(display_id); 272 } 273 274 EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor) 275 { 276 return getDispatch()->eglInitialize(dpy, major, minor); 277 } 278 279 EGLBoolean eglTerminate(EGLDisplay dpy) 280 { 281 return getDispatch()->eglTerminate(dpy); 282 } 283 284 const char* eglQueryString(EGLDisplay dpy, EGLint name) 285 { 286 return getDispatch()->eglQueryString(dpy, name); 287 } 288 289 EGLBoolean eglGetConfigs(EGLDisplay dpy, EGLConfig *configs, EGLint config_size, EGLint *num_config) 290 { 291 return getDispatch()->eglGetConfigs(dpy, configs, config_size, num_config); 292 } 293 294 static EGLint * filter_es2_bit(const EGLint *attrib_list, bool *isES2) 295 { 296 if (attrib_list == NULL) { 297 if (isES2 != NULL) *isES2 = false; 298 return NULL; 299 } 300 301 EGLint *attribs = NULL; 302 int nAttribs = 0; 303 while(attrib_list[nAttribs] != EGL_NONE) nAttribs++; 304 nAttribs++; 305 306 attribs = new EGLint[nAttribs]; 307 memcpy(attribs, attrib_list, nAttribs * sizeof(EGLint)); 308 if (isES2 != NULL) *isES2 = false; 309 310 // scan the attribute list for ES2 request and replace with ES1. 311 for (int i = 0; i < nAttribs; i++) { 312 if (attribs[i] == EGL_RENDERABLE_TYPE) { 313 if (attribs[i + 1] & EGL_OPENGL_ES2_BIT) { 314 attribs[i + 1] &= ~EGL_OPENGL_ES2_BIT; 315 attribs[i + 1] |= EGL_OPENGL_ES_BIT; 316 ALOGD("removing ES2 bit 0x%x\n", attribs[i + 1]); 317 if (isES2 != NULL) *isES2 = true; 318 } 319 } 320 } 321 return attribs; 322 } 323 324 EGLBoolean eglChooseConfig(EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config) 325 { 326 EGLBoolean res; 327 if (s_needEncode) { 328 EGLint *attribs = filter_es2_bit(attrib_list, NULL); 329 res = getDispatch()->eglChooseConfig(dpy, 330 attribs, 331 configs, 332 config_size, 333 num_config); 334 ALOGD("eglChooseConfig: %d configs found\n", *num_config); 335 if (*num_config == 0 && attribs != NULL) { 336 ALOGD("requested attributes:\n"); 337 for (int i = 0; attribs[i] != EGL_NONE; i++) { 338 ALOGD("%d: 0x%x\n", i, attribs[i]); 339 } 340 } 341 342 delete attribs; 343 } else { 344 res = getDispatch()->eglChooseConfig(dpy, attrib_list, configs, config_size, num_config); 345 } 346 return res; 347 } 348 349 EGLBoolean eglGetConfigAttrib(EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint *value) 350 { 351 if (s_needEncode && attribute == EGL_RENDERABLE_TYPE) { 352 *value = EGL_OPENGL_ES_BIT | EGL_OPENGL_ES2_BIT; 353 return EGL_TRUE; 354 } else { 355 return getDispatch()->eglGetConfigAttrib(dpy, config, attribute, value); 356 } 357 } 358 359 EGLSurface eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const EGLint *attrib_list) 360 { 361 EGLSurface surface = getDispatch()->eglCreateWindowSurface(dpy, config, win, attrib_list); 362 if (surface != EGL_NO_SURFACE) { 363 ServerConnection *server; 364 if (s_needEncode && (server = ServerConnection::s_getServerConnection()) != NULL) { 365 server->utEnc()->createSurface(server->utEnc(), getpid(), (uint32_t)surface); 366 } 367 } 368 return surface; 369 } 370 371 EGLSurface eglCreatePbufferSurface(EGLDisplay dpy, EGLConfig config, const EGLint *attrib_list) 372 { 373 EGLSurface surface = getDispatch()->eglCreatePbufferSurface(dpy, config, attrib_list); 374 if (surface != EGL_NO_SURFACE) { 375 ServerConnection *server; 376 if (s_needEncode && (server = ServerConnection::s_getServerConnection()) != NULL) { 377 server->utEnc()->createSurface(server->utEnc(), getpid(), (uint32_t)surface); 378 } 379 } 380 return surface; 381 } 382 383 EGLSurface eglCreatePixmapSurface(EGLDisplay dpy, EGLConfig config, EGLNativePixmapType pixmap, const EGLint *attrib_list) 384 { 385 EGLSurface surface = getDispatch()->eglCreatePixmapSurface(dpy, config, pixmap, attrib_list); 386 if (surface != EGL_NO_SURFACE) { 387 ServerConnection *server; 388 if (s_needEncode && (server = ServerConnection::s_getServerConnection()) != NULL) { 389 server->utEnc()->createSurface(server->utEnc(), getpid(), (uint32_t)surface); 390 } 391 } 392 return surface; 393 } 394 395 EGLBoolean eglDestroySurface(EGLDisplay dpy, EGLSurface surface) 396 { 397 EGLBoolean res = getDispatch()->eglDestroySurface(dpy, surface); 398 if (res && surface != EGL_NO_SURFACE) { 399 ServerConnection *server; 400 if (s_needEncode && (server = ServerConnection::s_getServerConnection()) != NULL) { 401 server->utEnc()->destroySurface(server->utEnc(), getpid(), (uint32_t)surface); 402 } 403 } 404 return res; 405 } 406 407 EGLBoolean eglQuerySurface(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint *value) 408 { 409 EGLBoolean res = getDispatch()->eglQuerySurface(dpy, surface, attribute, value); 410 if (res && attribute == EGL_RENDERABLE_TYPE) { 411 *value |= EGL_OPENGL_ES2_BIT; 412 } 413 return res; 414 } 415 416 EGLBoolean eglBindAPI(EGLenum api) 417 { 418 return getDispatch()->eglBindAPI(api); 419 } 420 421 EGLenum eglQueryAPI() 422 { 423 return getDispatch()->eglQueryAPI(); 424 } 425 426 EGLBoolean eglWaitClient() 427 { 428 return getDispatch()->eglWaitClient(); 429 } 430 431 EGLBoolean eglReleaseThread() 432 { 433 return getDispatch()->eglReleaseThread(); 434 } 435 436 EGLSurface eglCreatePbufferFromClientBuffer(EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer, EGLConfig config, const EGLint *attrib_list) 437 { 438 return getDispatch()->eglCreatePbufferFromClientBuffer(dpy, buftype, buffer, config, attrib_list); 439 } 440 441 EGLBoolean eglSurfaceAttrib(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value) 442 { 443 return getDispatch()->eglSurfaceAttrib(dpy, surface, attribute, value); 444 } 445 446 EGLBoolean eglBindTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer) 447 { 448 return getDispatch()->eglBindTexImage(dpy, surface, buffer); 449 } 450 451 EGLBoolean eglReleaseTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer) 452 { 453 return getDispatch()->eglReleaseTexImage(dpy, surface, buffer); 454 } 455 456 EGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint interval) 457 { 458 return getDispatch()->eglSwapInterval(dpy, interval); 459 } 460 461 EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config, EGLContext share_context, const EGLint *attrib_list) 462 { 463 464 EGLContext share = share_context; 465 if (share) share = ((EGLWrapperContext *)share_context)->aglContext; 466 467 // check if are ES2, and convert it to ES1. 468 int nAttribs = 0; 469 if (attrib_list != NULL) { 470 while(attrib_list[nAttribs] != EGL_NONE) { 471 nAttribs++; 472 } 473 nAttribs++; 474 } 475 476 EGLint *attrib = NULL; 477 if (nAttribs > 0) { 478 attrib = new EGLint[nAttribs]; 479 memcpy(attrib, attrib_list, nAttribs * sizeof(EGLint)); 480 } 481 482 int version = 1; 483 for (int i = 0; i < nAttribs; i++) { 484 if (attrib[i] == EGL_CONTEXT_CLIENT_VERSION && 485 attrib[i + 1] == 2) { 486 version = 2; 487 attrib[i + 1] = 1; // replace to version 1 488 } 489 } 490 491 EGLContext ctx = getDispatch()->eglCreateContext(dpy, config, share, attrib); 492 delete attrib; 493 EGLWrapperContext *wctx = new EGLWrapperContext(ctx, version); 494 if (ctx != EGL_NO_CONTEXT) { 495 ServerConnection *server; 496 if (s_needEncode && (server = ServerConnection::s_getServerConnection()) != NULL) { 497 wctx->clientState = new GLClientState(); 498 server->utEnc()->createContext(server->utEnc(), getpid(), 499 (uint32_t)wctx, 500 (uint32_t)(share_context == EGL_NO_CONTEXT ? 0 : share_context), wctx->version); 501 } 502 } 503 return (EGLContext)wctx; 504 } 505 506 EGLBoolean eglDestroyContext(EGLDisplay dpy, EGLContext ctx) 507 { 508 EGLWrapperContext *wctx = (EGLWrapperContext *)ctx; 509 EGLBoolean res = EGL_FALSE; 510 511 if (ctx && ctx != EGL_NO_CONTEXT) { 512 res = getDispatch()->eglDestroyContext(dpy, wctx->aglContext); 513 if (res) { 514 EGLThreadInfo *ti = getEGLThreadInfo(); 515 ServerConnection *server; 516 if (s_needEncode && (server = ServerConnection::s_getServerConnection())) { 517 server->utEnc()->destroyContext(ti->serverConn->utEnc(), getpid(), (uint32_t)ctx); 518 } 519 if (ti->currentContext == wctx) ti->currentContext = NULL; 520 delete wctx; 521 } 522 } 523 524 return res; 525 } 526 527 EGLBoolean eglMakeCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx) 528 { 529 EGLWrapperContext *wctx = (EGLWrapperContext *)ctx; 530 EGLContext aglContext = (ctx == EGL_NO_CONTEXT ? EGL_NO_CONTEXT : wctx->aglContext); 531 EGLThreadInfo *ti = getEGLThreadInfo(); 532 EGLBoolean res = getDispatch()->eglMakeCurrent(dpy, draw, read, aglContext); 533 if (res ) { 534 // NOTE - we do get a pointer to the server connection, (rather then using ti->serverConn) 535 // for cases that this is the first egl call of the current thread. 536 537 ServerConnection *server; 538 if (s_needEncode && (server = ServerConnection::s_getServerConnection())) { 539 server->utEnc()->makeCurrentContext(server->utEnc(), getpid(), 540 (uint32_t) (draw == EGL_NO_SURFACE ? 0 : draw), 541 (uint32_t) (read == EGL_NO_SURFACE ? 0 : read), 542 (uint32_t) (ctx == EGL_NO_CONTEXT ? 0 : ctx)); 543 server->glEncoder()->setClientState( wctx ? wctx->clientState : NULL ); 544 server->gl2Encoder()->setClientState( wctx ? wctx->clientState : NULL ); 545 } 546 547 // set current context in our thread info 548 ti->currentContext = wctx; 549 } 550 return res; 551 552 } 553 554 EGLContext eglGetCurrentContext() 555 { 556 EGLThreadInfo *ti = getEGLThreadInfo(); 557 return (ti->currentContext ? ti->currentContext : EGL_NO_CONTEXT); 558 } 559 560 EGLSurface eglGetCurrentSurface(EGLint readdraw) 561 { 562 return getDispatch()->eglGetCurrentSurface(readdraw); 563 } 564 565 EGLDisplay eglGetCurrentDisplay() 566 { 567 return getDispatch()->eglGetCurrentDisplay(); 568 } 569 570 EGLBoolean eglQueryContext(EGLDisplay dpy, EGLContext ctx, EGLint attribute, EGLint *value) 571 { 572 EGLWrapperContext *wctx = (EGLWrapperContext *)ctx; 573 if (wctx) { 574 if (attribute == EGL_CONTEXT_CLIENT_VERSION) { 575 *value = wctx->version; 576 return EGL_TRUE; 577 } else { 578 return getDispatch()->eglQueryContext(dpy, wctx->aglContext, attribute, value); 579 } 580 } 581 else { 582 return EGL_BAD_CONTEXT; 583 } 584 } 585 586 EGLBoolean eglWaitGL() 587 { 588 return getDispatch()->eglWaitGL(); 589 } 590 591 EGLBoolean eglWaitNative(EGLint engine) 592 { 593 return getDispatch()->eglWaitNative(engine); 594 } 595 596 EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface surface) 597 { 598 ServerConnection *server; 599 if (s_needEncode && (server = ServerConnection::s_getServerConnection()) != NULL) { 600 server->utEnc()->swapBuffers(server->utEnc(), getpid(), (uint32_t)surface); 601 server->glEncoder()->flush(); 602 server->gl2Encoder()->flush(); 603 return 1; 604 } 605 return getDispatch()->eglSwapBuffers(dpy, surface); 606 } 607 608 EGLBoolean eglCopyBuffers(EGLDisplay dpy, EGLSurface surface, EGLNativePixmapType target) 609 { 610 return getDispatch()->eglCopyBuffers(dpy, surface, target); 611 } 612 613 EGLBoolean eglLockSurfaceKHR(EGLDisplay display, EGLSurface surface, const EGLint *attrib_list) 614 { 615 return getDispatch()->eglLockSurfaceKHR(display, surface, attrib_list); 616 } 617 618 EGLBoolean eglUnlockSurfaceKHR(EGLDisplay display, EGLSurface surface) 619 { 620 return getDispatch()->eglUnlockSurfaceKHR(display, surface); 621 } 622 623 EGLImageKHR eglCreateImageKHR(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list) 624 { 625 EGLWrapperContext *wctx = (EGLWrapperContext *)ctx; 626 EGLContext aglContext = (wctx ? wctx->aglContext : EGL_NO_CONTEXT); 627 return getDispatch()->eglCreateImageKHR(dpy, aglContext, target, buffer, attrib_list); 628 } 629 630 EGLBoolean eglDestroyImageKHR(EGLDisplay dpy, EGLImageKHR image) 631 { 632 return getDispatch()->eglDestroyImageKHR(dpy, image); 633 } 634 635 EGLSyncKHR eglCreateSyncKHR(EGLDisplay dpy, EGLenum type, const EGLint *attrib_list) 636 { 637 return getDispatch()->eglCreateSyncKHR(dpy, type, attrib_list); 638 } 639 640 EGLBoolean eglDestroySyncKHR(EGLDisplay dpy, EGLSyncKHR sync) 641 { 642 return getDispatch()->eglDestroySyncKHR(dpy, sync); 643 } 644 645 EGLint eglClientWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout) 646 { 647 return getDispatch()->eglClientWaitSyncKHR(dpy, sync, flags, timeout); 648 } 649 650 EGLBoolean eglSignalSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLenum mode) 651 { 652 return getDispatch()->eglSignalSyncKHR(dpy, sync, mode); 653 } 654 655 EGLBoolean eglGetSyncAttribKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint attribute, EGLint *value) 656 { 657 return getDispatch()->eglGetSyncAttribKHR(dpy, sync, attribute, value); 658 } 659 660 EGLBoolean eglSetSwapRectangleANDROID(EGLDisplay dpy, EGLSurface draw, EGLint left, EGLint top, EGLint width, EGLint height) 661 { 662 return getDispatch()->eglSetSwapRectangleANDROID(dpy, draw, left, top, width, height); 663 } 664