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