Home | History | Annotate | Download | only in gles_android_wrapper
      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