Home | History | Annotate | Download | only in EGL
      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 __STDC_LIMIT_MACROS 1
     18 
     19 #include <string.h>
     20 
     21 #include "../egl_impl.h"
     22 
     23 #include "egl_cache.h"
     24 #include "egl_display.h"
     25 #include "egl_object.h"
     26 #include "egl_tls.h"
     27 #include "Loader.h"
     28 #include <cutils/properties.h>
     29 
     30 // ----------------------------------------------------------------------------
     31 namespace android {
     32 // ----------------------------------------------------------------------------
     33 
     34 static char const * const sVendorString     = "Android";
     35 static char const * const sVersionString    = "1.4 Android META-EGL";
     36 static char const * const sClientApiString  = "OpenGL_ES";
     37 
     38 extern char const * const gBuiltinExtensionString;
     39 extern char const * const gExtensionString;
     40 
     41 extern void setGLHooksThreadSpecific(gl_hooks_t const *value);
     42 
     43 // ----------------------------------------------------------------------------
     44 
     45 static bool findExtension(const char* exts, const char* name, size_t nameLen) {
     46     if (exts) {
     47         for (const char* match = strstr(exts, name); match; match = strstr(match + nameLen, name)) {
     48             if (match[nameLen] == '\0' || match[nameLen] == ' ') {
     49                 return true;
     50             }
     51         }
     52     }
     53     return false;
     54 }
     55 
     56 egl_display_t egl_display_t::sDisplay[NUM_DISPLAYS];
     57 
     58 egl_display_t::egl_display_t() :
     59     magic('_dpy'), finishOnSwap(false), traceGpuCompletion(false), refs(0), eglIsInitialized(false) {
     60 }
     61 
     62 egl_display_t::~egl_display_t() {
     63     magic = 0;
     64     egl_cache_t::get()->terminate();
     65 }
     66 
     67 egl_display_t* egl_display_t::get(EGLDisplay dpy) {
     68     uintptr_t index = uintptr_t(dpy)-1U;
     69     return (index >= NUM_DISPLAYS) ? NULL : &sDisplay[index];
     70 }
     71 
     72 void egl_display_t::addObject(egl_object_t* object) {
     73     Mutex::Autolock _l(lock);
     74     objects.add(object);
     75 }
     76 
     77 void egl_display_t::removeObject(egl_object_t* object) {
     78     Mutex::Autolock _l(lock);
     79     objects.remove(object);
     80 }
     81 
     82 bool egl_display_t::getObject(egl_object_t* object) const {
     83     Mutex::Autolock _l(lock);
     84     if (objects.indexOf(object) >= 0) {
     85         if (object->getDisplay() == this) {
     86             object->incRef();
     87             return true;
     88         }
     89     }
     90     return false;
     91 }
     92 
     93 EGLDisplay egl_display_t::getFromNativeDisplay(EGLNativeDisplayType disp) {
     94     if (uintptr_t(disp) >= NUM_DISPLAYS)
     95         return NULL;
     96 
     97     return sDisplay[uintptr_t(disp)].getDisplay(disp);
     98 }
     99 
    100 EGLDisplay egl_display_t::getDisplay(EGLNativeDisplayType display) {
    101 
    102     Mutex::Autolock _l(lock);
    103 
    104     // get our driver loader
    105     Loader& loader(Loader::getInstance());
    106 
    107     egl_connection_t* const cnx = &gEGLImpl;
    108     if (cnx->dso && disp.dpy == EGL_NO_DISPLAY) {
    109         EGLDisplay dpy = cnx->egl.eglGetDisplay(display);
    110         disp.dpy = dpy;
    111         if (dpy == EGL_NO_DISPLAY) {
    112             loader.close(cnx->dso);
    113             cnx->dso = NULL;
    114         }
    115     }
    116 
    117     return EGLDisplay(uintptr_t(display) + 1U);
    118 }
    119 
    120 EGLBoolean egl_display_t::initialize(EGLint *major, EGLint *minor) {
    121 
    122     {
    123         Mutex::Autolock _rf(refLock);
    124 
    125         refs++;
    126         if (refs > 1) {
    127             if (major != NULL)
    128                 *major = VERSION_MAJOR;
    129             if (minor != NULL)
    130                 *minor = VERSION_MINOR;
    131             while(!eglIsInitialized) refCond.wait(refLock);
    132             return EGL_TRUE;
    133         }
    134 
    135         while(eglIsInitialized) refCond.wait(refLock);
    136     }
    137 
    138     {
    139         Mutex::Autolock _l(lock);
    140 
    141         setGLHooksThreadSpecific(&gHooksNoContext);
    142 
    143         // initialize each EGL and
    144         // build our own extension string first, based on the extension we know
    145         // and the extension supported by our client implementation
    146 
    147         egl_connection_t* const cnx = &gEGLImpl;
    148         cnx->major = -1;
    149         cnx->minor = -1;
    150         if (cnx->dso) {
    151             EGLDisplay idpy = disp.dpy;
    152             if (cnx->egl.eglInitialize(idpy, &cnx->major, &cnx->minor)) {
    153                 //ALOGD("initialized dpy=%p, ver=%d.%d, cnx=%p",
    154                 //        idpy, cnx->major, cnx->minor, cnx);
    155 
    156                 // display is now initialized
    157                 disp.state = egl_display_t::INITIALIZED;
    158 
    159                 // get the query-strings for this display for each implementation
    160                 disp.queryString.vendor = cnx->egl.eglQueryString(idpy,
    161                         EGL_VENDOR);
    162                 disp.queryString.version = cnx->egl.eglQueryString(idpy,
    163                         EGL_VERSION);
    164                 disp.queryString.extensions = cnx->egl.eglQueryString(idpy,
    165                         EGL_EXTENSIONS);
    166                 disp.queryString.clientApi = cnx->egl.eglQueryString(idpy,
    167                         EGL_CLIENT_APIS);
    168 
    169             } else {
    170                 ALOGW("eglInitialize(%p) failed (%s)", idpy,
    171                         egl_tls_t::egl_strerror(cnx->egl.eglGetError()));
    172             }
    173         }
    174 
    175         // the query strings are per-display
    176         mVendorString.setTo(sVendorString);
    177         mVersionString.setTo(sVersionString);
    178         mClientApiString.setTo(sClientApiString);
    179 
    180         mExtensionString.setTo(gBuiltinExtensionString);
    181         char const* start = gExtensionString;
    182         do {
    183             // length of the extension name
    184             size_t len = strcspn(start, " ");
    185             if (len) {
    186                 // NOTE: we could avoid the copy if we had strnstr.
    187                 const String8 ext(start, len);
    188                 if (findExtension(disp.queryString.extensions, ext.string(),
    189                         len)) {
    190                     mExtensionString.append(ext + " ");
    191                 }
    192                 // advance to the next extension name, skipping the space.
    193                 start += len;
    194                 start += (*start == ' ') ? 1 : 0;
    195             }
    196         } while (*start != '\0');
    197 
    198         egl_cache_t::get()->initialize(this);
    199 
    200         char value[PROPERTY_VALUE_MAX];
    201         property_get("debug.egl.finish", value, "0");
    202         if (atoi(value)) {
    203             finishOnSwap = true;
    204         }
    205 
    206         property_get("debug.egl.traceGpuCompletion", value, "0");
    207         if (atoi(value)) {
    208             traceGpuCompletion = true;
    209         }
    210 
    211         if (major != NULL)
    212             *major = VERSION_MAJOR;
    213         if (minor != NULL)
    214             *minor = VERSION_MINOR;
    215 
    216         mHibernation.setDisplayValid(true);
    217     }
    218 
    219     {
    220         Mutex::Autolock _rf(refLock);
    221         eglIsInitialized = true;
    222         refCond.broadcast();
    223     }
    224 
    225     return EGL_TRUE;
    226 }
    227 
    228 EGLBoolean egl_display_t::terminate() {
    229 
    230     {
    231         Mutex::Autolock _rl(refLock);
    232         if (refs == 0) {
    233             /*
    234              * From the EGL spec (3.2):
    235              * "Termination of a display that has already been terminated,
    236              *  (...), is allowed, but the only effect of such a call is
    237              *  to return EGL_TRUE (...)
    238              */
    239             return EGL_TRUE;
    240         }
    241 
    242         // this is specific to Android, display termination is ref-counted.
    243         refs--;
    244         if (refs > 0) {
    245             return EGL_TRUE;
    246         }
    247     }
    248 
    249     EGLBoolean res = EGL_FALSE;
    250 
    251     {
    252         Mutex::Autolock _l(lock);
    253 
    254         egl_connection_t* const cnx = &gEGLImpl;
    255         if (cnx->dso && disp.state == egl_display_t::INITIALIZED) {
    256             if (cnx->egl.eglTerminate(disp.dpy) == EGL_FALSE) {
    257                 ALOGW("eglTerminate(%p) failed (%s)", disp.dpy,
    258                         egl_tls_t::egl_strerror(cnx->egl.eglGetError()));
    259             }
    260             // REVISIT: it's unclear what to do if eglTerminate() fails
    261             disp.state = egl_display_t::TERMINATED;
    262             res = EGL_TRUE;
    263         }
    264 
    265         mHibernation.setDisplayValid(false);
    266 
    267         // Reset the extension string since it will be regenerated if we get
    268         // reinitialized.
    269         mExtensionString.setTo("");
    270 
    271         // Mark all objects remaining in the list as terminated, unless
    272         // there are no reference to them, it which case, we're free to
    273         // delete them.
    274         size_t count = objects.size();
    275         ALOGW_IF(count, "eglTerminate() called w/ %zu objects remaining", count);
    276         for (size_t i=0 ; i<count ; i++) {
    277             egl_object_t* o = objects.itemAt(i);
    278             o->destroy();
    279         }
    280 
    281         // this marks all object handles are "terminated"
    282         objects.clear();
    283     }
    284 
    285     {
    286         Mutex::Autolock _rl(refLock);
    287         eglIsInitialized = false;
    288         refCond.broadcast();
    289     }
    290 
    291     return res;
    292 }
    293 
    294 void egl_display_t::loseCurrent(egl_context_t * cur_c)
    295 {
    296     if (cur_c) {
    297         egl_display_t* display = cur_c->getDisplay();
    298         if (display) {
    299             display->loseCurrentImpl(cur_c);
    300         }
    301     }
    302 }
    303 
    304 void egl_display_t::loseCurrentImpl(egl_context_t * cur_c)
    305 {
    306     // by construction, these are either 0 or valid (possibly terminated)
    307     // it should be impossible for these to be invalid
    308     ContextRef _cur_c(cur_c);
    309     SurfaceRef _cur_r(cur_c ? get_surface(cur_c->read) : NULL);
    310     SurfaceRef _cur_d(cur_c ? get_surface(cur_c->draw) : NULL);
    311 
    312     { // scope for the lock
    313         Mutex::Autolock _l(lock);
    314         cur_c->onLooseCurrent();
    315 
    316     }
    317 
    318     // This cannot be called with the lock held because it might end-up
    319     // calling back into EGL (in particular when a surface is destroyed
    320     // it calls ANativeWindow::disconnect
    321     _cur_c.release();
    322     _cur_r.release();
    323     _cur_d.release();
    324 }
    325 
    326 EGLBoolean egl_display_t::makeCurrent(egl_context_t* c, egl_context_t* cur_c,
    327         EGLSurface draw, EGLSurface read, EGLContext /*ctx*/,
    328         EGLSurface impl_draw, EGLSurface impl_read, EGLContext impl_ctx)
    329 {
    330     EGLBoolean result;
    331 
    332     // by construction, these are either 0 or valid (possibly terminated)
    333     // it should be impossible for these to be invalid
    334     ContextRef _cur_c(cur_c);
    335     SurfaceRef _cur_r(cur_c ? get_surface(cur_c->read) : NULL);
    336     SurfaceRef _cur_d(cur_c ? get_surface(cur_c->draw) : NULL);
    337 
    338     { // scope for the lock
    339         Mutex::Autolock _l(lock);
    340         if (c) {
    341             result = c->cnx->egl.eglMakeCurrent(
    342                     disp.dpy, impl_draw, impl_read, impl_ctx);
    343             if (result == EGL_TRUE) {
    344                 c->onMakeCurrent(draw, read);
    345                 if (!cur_c) {
    346                     mHibernation.incWakeCount(HibernationMachine::STRONG);
    347                 }
    348             }
    349         } else {
    350             result = cur_c->cnx->egl.eglMakeCurrent(
    351                     disp.dpy, impl_draw, impl_read, impl_ctx);
    352             if (result == EGL_TRUE) {
    353                 cur_c->onLooseCurrent();
    354                 mHibernation.decWakeCount(HibernationMachine::STRONG);
    355             }
    356         }
    357     }
    358 
    359     if (result == EGL_TRUE) {
    360         // This cannot be called with the lock held because it might end-up
    361         // calling back into EGL (in particular when a surface is destroyed
    362         // it calls ANativeWindow::disconnect
    363         _cur_c.release();
    364         _cur_r.release();
    365         _cur_d.release();
    366     }
    367 
    368     return result;
    369 }
    370 
    371 bool egl_display_t::haveExtension(const char* name, size_t nameLen) const {
    372     if (!nameLen) {
    373         nameLen = strlen(name);
    374     }
    375     return findExtension(mExtensionString.string(), name, nameLen);
    376 }
    377 
    378 // ----------------------------------------------------------------------------
    379 
    380 bool egl_display_t::HibernationMachine::incWakeCount(WakeRefStrength strength) {
    381     Mutex::Autolock _l(mLock);
    382     ALOGE_IF(mWakeCount < 0 || mWakeCount == INT32_MAX,
    383              "Invalid WakeCount (%d) on enter\n", mWakeCount);
    384 
    385     mWakeCount++;
    386     if (strength == STRONG)
    387         mAttemptHibernation = false;
    388 
    389     if (CC_UNLIKELY(mHibernating)) {
    390         ALOGV("Awakening\n");
    391         egl_connection_t* const cnx = &gEGLImpl;
    392 
    393         // These conditions should be guaranteed before entering hibernation;
    394         // we don't want to get into a state where we can't wake up.
    395         ALOGD_IF(!mDpyValid || !cnx->egl.eglAwakenProcessIMG,
    396                  "Invalid hibernation state, unable to awaken\n");
    397 
    398         if (!cnx->egl.eglAwakenProcessIMG()) {
    399             ALOGE("Failed to awaken EGL implementation\n");
    400             return false;
    401         }
    402         mHibernating = false;
    403     }
    404     return true;
    405 }
    406 
    407 void egl_display_t::HibernationMachine::decWakeCount(WakeRefStrength strength) {
    408     Mutex::Autolock _l(mLock);
    409     ALOGE_IF(mWakeCount <= 0, "Invalid WakeCount (%d) on leave\n", mWakeCount);
    410 
    411     mWakeCount--;
    412     if (strength == STRONG)
    413         mAttemptHibernation = true;
    414 
    415     if (mWakeCount == 0 && CC_UNLIKELY(mAttemptHibernation)) {
    416         egl_connection_t* const cnx = &gEGLImpl;
    417         mAttemptHibernation = false;
    418         if (mAllowHibernation && mDpyValid &&
    419                 cnx->egl.eglHibernateProcessIMG &&
    420                 cnx->egl.eglAwakenProcessIMG) {
    421             ALOGV("Hibernating\n");
    422             if (!cnx->egl.eglHibernateProcessIMG()) {
    423                 ALOGE("Failed to hibernate EGL implementation\n");
    424                 return;
    425             }
    426             mHibernating = true;
    427         }
    428     }
    429 }
    430 
    431 void egl_display_t::HibernationMachine::setDisplayValid(bool valid) {
    432     Mutex::Autolock _l(mLock);
    433     mDpyValid = valid;
    434 }
    435 
    436 // ----------------------------------------------------------------------------
    437 }; // namespace android
    438 // ----------------------------------------------------------------------------
    439