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