• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
findExtension(const char * exts,const char * name,size_t nameLen)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 
egl_display_t()58 egl_display_t::egl_display_t() :
59     magic('_dpy'), finishOnSwap(false), traceGpuCompletion(false), refs(0), eglIsInitialized(false) {
60 }
61 
~egl_display_t()62 egl_display_t::~egl_display_t() {
63     magic = 0;
64     egl_cache_t::get()->terminate();
65 }
66 
get(EGLDisplay dpy)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 
addObject(egl_object_t * object)72 void egl_display_t::addObject(egl_object_t* object) {
73     Mutex::Autolock _l(lock);
74     objects.add(object);
75 }
76 
removeObject(egl_object_t * object)77 void egl_display_t::removeObject(egl_object_t* object) {
78     Mutex::Autolock _l(lock);
79     objects.remove(object);
80 }
81 
getObject(egl_object_t * object) const82 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 
getFromNativeDisplay(EGLNativeDisplayType disp)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 
getDisplay(EGLNativeDisplayType display)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 
initialize(EGLint * major,EGLint * minor)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 
terminate()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 
loseCurrent(egl_context_t * cur_c)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 
loseCurrentImpl(egl_context_t * cur_c)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 
makeCurrent(egl_context_t * c,egl_context_t * cur_c,EGLSurface draw,EGLSurface read,EGLContext,EGLSurface impl_draw,EGLSurface impl_read,EGLContext impl_ctx)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 
haveExtension(const char * name,size_t nameLen) const371 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 
incWakeCount(WakeRefStrength strength)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 
decWakeCount(WakeRefStrength strength)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 
setDisplayValid(bool valid)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