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