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#include "eglDisplay.h"
17#include "HostConnection.h"
18#include <dlfcn.h>
19
20static const int systemEGLVersionMajor = 1;
21static const int systemEGLVersionMinor = 4;
22static const char systemEGLVendor[] = "Google Android emulator";
23
24// list of extensions supported by this EGL implementation
25//  NOTE that each extension name should be suffixed with space
26static const char systemStaticEGLExtensions[] =
27            "EGL_ANDROID_image_native_buffer "
28            "EGL_KHR_fence_sync ";
29
30// list of extensions supported by this EGL implementation only if supported
31// on the host implementation.
32//  NOTE that each extension name should be suffixed with space
33static const char systemDynamicEGLExtensions[] =
34            "EGL_KHR_image_base "
35            "EGL_KHR_gl_texture_2d_image ";
36
37
38static void *s_gles_lib = NULL;
39static void *s_gles2_lib = NULL;
40
41// The following function will be called when we (libEGL)
42// gets unloaded
43// At this point we want to unload the gles libraries we
44// might have loaded during initialization
45static void __attribute__ ((destructor)) do_on_unload(void)
46{
47    if (s_gles_lib) {
48        dlclose(s_gles_lib);
49    }
50
51    if (s_gles2_lib) {
52        dlclose(s_gles2_lib);
53    }
54}
55
56eglDisplay::eglDisplay() :
57    m_initialized(false),
58    m_major(0),
59    m_minor(0),
60    m_hostRendererVersion(0),
61    m_numConfigs(0),
62    m_numConfigAttribs(0),
63    m_attribs(DefaultKeyedVector<EGLint, EGLint>(ATTRIBUTE_NONE)),
64    m_configs(NULL),
65    m_gles_iface(NULL),
66    m_gles2_iface(NULL),
67    m_versionString(NULL),
68    m_vendorString(NULL),
69    m_extensionString(NULL)
70{
71    pthread_mutex_init(&m_lock, NULL);
72}
73
74eglDisplay::~eglDisplay()
75{
76    pthread_mutex_destroy(&m_lock);
77}
78
79bool eglDisplay::initialize(EGLClient_eglInterface *eglIface)
80{
81    pthread_mutex_lock(&m_lock);
82    if (!m_initialized) {
83
84        //
85        // load GLES client API
86        //
87        m_gles_iface = loadGLESClientAPI("/system/lib/egl/libGLESv1_CM_emulation.so",
88                                         eglIface,
89                                         &s_gles_lib);
90        if (!m_gles_iface) {
91            pthread_mutex_unlock(&m_lock);
92            ALOGE("Failed to load gles1 iface");
93            return false;
94        }
95
96#ifdef WITH_GLES2
97        m_gles2_iface = loadGLESClientAPI("/system/lib/egl/libGLESv2_emulation.so",
98                                          eglIface,
99                                          &s_gles2_lib);
100        // Note that if loading gles2 failed, we can still run with no
101        // GLES2 support, having GLES2 is not mandatory.
102#endif
103
104        //
105        // establish connection with the host
106        //
107        HostConnection *hcon = HostConnection::get();
108        if (!hcon) {
109            pthread_mutex_unlock(&m_lock);
110            ALOGE("Failed to establish connection with the host\n");
111            return false;
112        }
113
114        //
115        // get renderControl encoder instance
116        //
117        renderControl_encoder_context_t *rcEnc = hcon->rcEncoder();
118        if (!rcEnc) {
119            pthread_mutex_unlock(&m_lock);
120            ALOGE("Failed to get renderControl encoder instance");
121            return false;
122        }
123
124        //
125        // Query host reneder and EGL version
126        //
127        m_hostRendererVersion = rcEnc->rcGetRendererVersion(rcEnc);
128        EGLint status = rcEnc->rcGetEGLVersion(rcEnc, &m_major, &m_minor);
129        if (status != EGL_TRUE) {
130            // host EGL initialization failed !!
131            pthread_mutex_unlock(&m_lock);
132            return false;
133        }
134
135        //
136        // Take minimum version beween what we support and what the host support
137        //
138        if (m_major > systemEGLVersionMajor) {
139            m_major = systemEGLVersionMajor;
140            m_minor = systemEGLVersionMinor;
141        }
142        else if (m_major == systemEGLVersionMajor &&
143                 m_minor > systemEGLVersionMinor) {
144            m_minor = systemEGLVersionMinor;
145        }
146
147        //
148        // Query the host for the set of configs
149        //
150        m_numConfigs = rcEnc->rcGetNumConfigs(rcEnc, (uint32_t*)&m_numConfigAttribs);
151        if (m_numConfigs <= 0 || m_numConfigAttribs <= 0) {
152            // just sanity check - should never happen
153            pthread_mutex_unlock(&m_lock);
154            return false;
155        }
156
157        uint32_t nInts = m_numConfigAttribs * (m_numConfigs + 1);
158        EGLint tmp_buf[nInts];
159        m_configs = new EGLint[nInts-m_numConfigAttribs];
160        if (!m_configs) {
161            pthread_mutex_unlock(&m_lock);
162            return false;
163        }
164
165        //EGLint n = rcEnc->rcGetConfigs(rcEnc, nInts*sizeof(EGLint), m_configs);
166        EGLint n = rcEnc->rcGetConfigs(rcEnc, nInts*sizeof(EGLint), (GLuint*)tmp_buf);
167        if (n != m_numConfigs) {
168            pthread_mutex_unlock(&m_lock);
169            return false;
170        }
171
172        //Fill the attributes vector.
173        //The first m_numConfigAttribs values of tmp_buf are the actual attributes enums.
174        for (int i=0; i<m_numConfigAttribs; i++) {
175            m_attribs.add(tmp_buf[i], i);
176        }
177
178        //Copy the actual configs data to m_configs
179        memcpy(m_configs, tmp_buf + m_numConfigAttribs, m_numConfigs*m_numConfigAttribs*sizeof(EGLint));
180
181        m_initialized = true;
182    }
183    pthread_mutex_unlock(&m_lock);
184
185    processConfigs();
186
187    return true;
188}
189
190void eglDisplay::processConfigs()
191{
192    for (int i=0; i<m_numConfigs; i++) {
193        EGLConfig config = (EGLConfig)i;
194        //Setup the EGL_NATIVE_VISUAL_ID attribute
195        PixelFormat format;
196        if (getConfigNativePixelFormat(config, &format)) {
197            setConfigAttrib(config, EGL_NATIVE_VISUAL_ID, format);
198        }
199    }
200}
201
202void eglDisplay::terminate()
203{
204    pthread_mutex_lock(&m_lock);
205    if (m_initialized) {
206        m_initialized = false;
207        delete [] m_configs;
208        m_configs = NULL;
209
210        if (m_versionString) {
211            free(m_versionString);
212            m_versionString = NULL;
213        }
214        if (m_vendorString) {
215            free(m_vendorString);
216            m_vendorString = NULL;
217        }
218        if (m_extensionString) {
219            free(m_extensionString);
220            m_extensionString = NULL;
221        }
222    }
223    pthread_mutex_unlock(&m_lock);
224}
225
226EGLClient_glesInterface *eglDisplay::loadGLESClientAPI(const char *libName,
227                                                       EGLClient_eglInterface *eglIface,
228                                                       void **libHandle)
229{
230    void *lib = dlopen(libName, RTLD_NOW);
231    if (!lib) {
232        ALOGE("Failed to dlopen %s", libName);
233        return NULL;
234    }
235
236    init_emul_gles_t init_gles_func = (init_emul_gles_t)dlsym(lib,"init_emul_gles");
237    if (!init_gles_func) {
238        ALOGE("Failed to find init_emul_gles");
239        dlclose((void*)lib);
240        return NULL;
241    }
242
243    *libHandle = lib;
244    return (*init_gles_func)(eglIface);
245}
246
247static char *queryHostEGLString(EGLint name)
248{
249    HostConnection *hcon = HostConnection::get();
250    if (hcon) {
251        renderControl_encoder_context_t *rcEnc = hcon->rcEncoder();
252        if (rcEnc) {
253            int n = rcEnc->rcQueryEGLString(rcEnc, name, NULL, 0);
254            if (n < 0) {
255                // allocate space for the string with additional
256                // space charachter to be suffixed at the end.
257                char *str = (char *)malloc(-n+2);
258                n = rcEnc->rcQueryEGLString(rcEnc, name, str, -n);
259                if (n > 0) {
260                    // add extra space at end of string which will be
261                    // needed later when filtering the extension list.
262                    strcat(str, " ");
263                    return str;
264                }
265
266                free(str);
267            }
268        }
269    }
270
271    return NULL;
272}
273
274static bool findExtInList(const char* token, int tokenlen, const char* list)
275{
276    const char* p = list;
277    while (*p != '\0') {
278        const char* q = strchr(p, ' ');
279        if (q == NULL) {
280            /* should not happen, list must be space-terminated */
281            break;
282        }
283        if (tokenlen == (q - p) && !memcmp(token, p, tokenlen)) {
284            return true;  /* found it */
285        }
286        p = q+1;
287    }
288    return false;  /* not found */
289}
290
291static char *buildExtensionString()
292{
293    //Query host extension string
294    char *hostExt = queryHostEGLString(EGL_EXTENSIONS);
295    if (!hostExt || (hostExt[1] == '\0')) {
296        // no extensions on host - only static extension list supported
297        return strdup(systemStaticEGLExtensions);
298    }
299
300    //
301    // Filter host extension list to include only extensions
302    // we can support (in the systemDynamicEGLExtensions list)
303    //
304    char *ext = (char *)hostExt;
305    char *c = ext;
306    char *insert = ext;
307    while(*c != '\0') {
308        if (*c == ' ') {
309            int len = c - ext;
310            if (findExtInList(ext, len, systemDynamicEGLExtensions)) {
311                if (ext != insert) {
312                    memcpy(insert, ext, len+1); // including space
313                }
314                insert += (len + 1);
315            }
316            ext = c + 1;
317        }
318        c++;
319    }
320    *insert = '\0';
321
322    int n = strlen(hostExt);
323    if (n > 0) {
324        char *str;
325        asprintf(&str,"%s%s", systemStaticEGLExtensions, hostExt);
326        free((char*)hostExt);
327        return str;
328    }
329    else {
330        free((char*)hostExt);
331        return strdup(systemStaticEGLExtensions);
332    }
333}
334
335const char *eglDisplay::queryString(EGLint name)
336{
337    if (name == EGL_CLIENT_APIS) {
338        return "OpenGL_ES";
339    }
340    else if (name == EGL_VERSION) {
341        pthread_mutex_lock(&m_lock);
342        if (m_versionString) {
343            pthread_mutex_unlock(&m_lock);
344            return m_versionString;
345        }
346
347        // build version string
348        asprintf(&m_versionString, "%d.%d", m_major, m_minor);
349        pthread_mutex_unlock(&m_lock);
350
351        return m_versionString;
352    }
353    else if (name == EGL_VENDOR) {
354        pthread_mutex_lock(&m_lock);
355        if (m_vendorString) {
356            pthread_mutex_unlock(&m_lock);
357            return m_vendorString;
358        }
359
360        // build vendor string
361        const char *hostVendor = queryHostEGLString(EGL_VENDOR);
362
363        if (hostVendor) {
364            asprintf(&m_vendorString, "%s Host: %s",
365                                     systemEGLVendor, hostVendor);
366            free((char*)hostVendor);
367        }
368        else {
369            m_vendorString = (char *)systemEGLVendor;
370        }
371        pthread_mutex_unlock(&m_lock);
372
373        return m_vendorString;
374    }
375    else if (name == EGL_EXTENSIONS) {
376        pthread_mutex_lock(&m_lock);
377        if (m_extensionString) {
378            pthread_mutex_unlock(&m_lock);
379            return m_extensionString;
380        }
381
382        // build extension string
383        m_extensionString = buildExtensionString();
384        pthread_mutex_unlock(&m_lock);
385
386        return m_extensionString;
387    }
388    else {
389        ALOGE("[%s] Unknown name %d\n", __FUNCTION__, name);
390        return NULL;
391    }
392}
393
394/* To get the value of attribute <a> of config <c> use the following formula:
395 * value = *(m_configs + (int)c*m_numConfigAttribs + a);
396 */
397EGLBoolean eglDisplay::getAttribValue(EGLConfig config, EGLint attribIdx, EGLint * value)
398{
399    if (attribIdx == ATTRIBUTE_NONE)
400    {
401        ALOGE("[%s] Bad attribute idx\n", __FUNCTION__);
402        return EGL_FALSE;
403    }
404    *value = *(m_configs + (int)config*m_numConfigAttribs + attribIdx);
405    return EGL_TRUE;
406}
407
408EGLBoolean eglDisplay::getConfigAttrib(EGLConfig config, EGLint attrib, EGLint * value)
409{
410    //Though it seems that valueFor() is thread-safe, we don't take chanses
411    pthread_mutex_lock(&m_lock);
412    EGLBoolean ret = getAttribValue(config, m_attribs.valueFor(attrib), value);
413    pthread_mutex_unlock(&m_lock);
414    return ret;
415}
416
417void eglDisplay::dumpConfig(EGLConfig config)
418{
419    EGLint value = 0;
420    DBG("^^^^^^^^^^ dumpConfig %d ^^^^^^^^^^^^^^^^^^", (int)config);
421    for (int i=0; i<m_numConfigAttribs; i++) {
422        getAttribValue(config, i, &value);
423        DBG("{%d}[%d] %d\n", (int)config, i, value);
424    }
425}
426
427/* To set the value of attribute <a> of config <c> use the following formula:
428 * *(m_configs + (int)c*m_numConfigAttribs + a) = value;
429 */
430EGLBoolean eglDisplay::setAttribValue(EGLConfig config, EGLint attribIdx, EGLint value)
431{
432    if (attribIdx == ATTRIBUTE_NONE)
433    {
434        ALOGE("[%s] Bad attribute idx\n", __FUNCTION__);
435        return EGL_FALSE;
436    }
437    *(m_configs + (int)config*m_numConfigAttribs + attribIdx) = value;
438    return EGL_TRUE;
439}
440
441EGLBoolean eglDisplay::setConfigAttrib(EGLConfig config, EGLint attrib, EGLint value)
442{
443    //Though it seems that valueFor() is thread-safe, we don't take chanses
444    pthread_mutex_lock(&m_lock);
445    EGLBoolean ret = setAttribValue(config, m_attribs.valueFor(attrib), value);
446    pthread_mutex_unlock(&m_lock);
447    return ret;
448}
449
450
451EGLBoolean eglDisplay::getConfigNativePixelFormat(EGLConfig config, PixelFormat * format)
452{
453    EGLint redSize, blueSize, greenSize, alphaSize;
454
455    if ( !(getAttribValue(config, m_attribs.valueFor(EGL_RED_SIZE), &redSize) &&
456        getAttribValue(config, m_attribs.valueFor(EGL_BLUE_SIZE), &blueSize) &&
457        getAttribValue(config, m_attribs.valueFor(EGL_GREEN_SIZE), &greenSize) &&
458        getAttribValue(config, m_attribs.valueFor(EGL_ALPHA_SIZE), &alphaSize)) )
459    {
460        ALOGE("Couldn't find value for one of the pixel format attributes");
461        return EGL_FALSE;
462    }
463
464    //calculate the GL internal format
465    if ((redSize==8)&&(greenSize==8)&&(blueSize==8)&&(alphaSize==8)) *format = PIXEL_FORMAT_RGBA_8888; //XXX: BGR?
466    else if ((redSize==8)&&(greenSize==8)&&(blueSize==8)&&(alphaSize==0)) *format = PIXEL_FORMAT_RGBX_8888; //XXX or PIXEL_FORMAT_RGB_888
467    else if ((redSize==5)&&(greenSize==6)&&(blueSize==5)&&(alphaSize==0)) *format = PIXEL_FORMAT_RGB_565;
468    else if ((redSize==5)&&(greenSize==5)&&(blueSize==5)&&(alphaSize==1)) *format = PIXEL_FORMAT_RGBA_5551;
469    else if ((redSize==4)&&(greenSize==4)&&(blueSize==4)&&(alphaSize==4)) *format = PIXEL_FORMAT_RGBA_4444;
470    else {
471        return EGL_FALSE;
472    }
473    return EGL_TRUE;
474}
475EGLBoolean eglDisplay::getConfigGLPixelFormat(EGLConfig config, GLenum * format)
476{
477    EGLint redSize, blueSize, greenSize, alphaSize;
478
479    if ( !(getAttribValue(config, m_attribs.valueFor(EGL_RED_SIZE), &redSize) &&
480        getAttribValue(config, m_attribs.valueFor(EGL_BLUE_SIZE), &blueSize) &&
481        getAttribValue(config, m_attribs.valueFor(EGL_GREEN_SIZE), &greenSize) &&
482        getAttribValue(config, m_attribs.valueFor(EGL_ALPHA_SIZE), &alphaSize)) )
483    {
484        ALOGE("Couldn't find value for one of the pixel format attributes");
485        return EGL_FALSE;
486    }
487
488    //calculate the GL internal format
489    if ((redSize==8)&&(blueSize==8)&&(blueSize==8)&&(alphaSize==8)) *format = GL_RGBA;
490    else if ((redSize==8)&&(greenSize==8)&&(blueSize==8)&&(alphaSize==0)) *format = GL_RGB;
491    else if ((redSize==5)&&(greenSize==6)&&(blueSize==5)&&(alphaSize==0)) *format = GL_RGB565_OES;
492    else if ((redSize==5)&&(greenSize==5)&&(blueSize==5)&&(alphaSize==1)) *format = GL_RGB5_A1_OES;
493    else if ((redSize==4)&&(greenSize==4)&&(blueSize==4)&&(alphaSize==4)) *format = GL_RGBA4_OES;
494    else return EGL_FALSE;
495
496    return EGL_TRUE;
497}
498