Merge "BufferQueue returns proper code on acquire"
diff --git a/cmds/dumpstate/utils.c b/cmds/dumpstate/utils.c
index 25505f8..4556505 100644
--- a/cmds/dumpstate/utils.c
+++ b/cmds/dumpstate/utils.c
@@ -383,7 +383,7 @@
}
/* create a new, empty traces.txt file to receive stack dumps */
- int fd = open(traces_path, O_CREAT | O_WRONLY | O_TRUNC, 0666); /* -rw-rw-rw- */
+ int fd = open(traces_path, O_CREAT | O_WRONLY | O_TRUNC | O_NOFOLLOW, 0666); /* -rw-rw-rw- */
if (fd < 0) {
fprintf(stderr, "%s: %s\n", traces_path, strerror(errno));
return NULL;
diff --git a/libs/utils/Trace.cpp b/libs/utils/Trace.cpp
index d532296..f7d8abe 100644
--- a/libs/utils/Trace.cpp
+++ b/libs/utils/Trace.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#define LOG_TAG "Trace"
+
#include <cutils/properties.h>
#include <utils/Log.h>
#include <utils/Trace.h>
diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h
index ca11863..2d41aa7 100644
--- a/opengl/include/EGL/eglext.h
+++ b/opengl/include/EGL/eglext.h
@@ -248,7 +248,6 @@
typedef EGLuint64NV (EGLAPIENTRYP PFNEGLGETSYSTEMTIMENVPROC)(void);
#endif
-
/* EGL_ANDROID_blob_cache
*/
#ifndef EGL_ANDROID_blob_cache
@@ -263,6 +262,14 @@
EGLSetBlobFuncANDROID set, EGLGetBlobFuncANDROID get);
#endif
+/* EGL_IMG_hibernate_process
+ */
+#ifndef EGL_IMG_hibernate_process
+#define EGL_IMG_hibernate_process 1
+typedef EGLBoolean (EGLAPIENTRYP PFEGLHIBERNATEPROCESSIMGPROC)(void);
+typedef EGLBoolean (EGLAPIENTRYP PFEGLAWAKENPROCESSIMGPROC)(void);
+#endif
+
#ifdef __cplusplus
}
#endif
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index e8a14d8..b658240 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -203,27 +203,27 @@
// ----------------------------------------------------------------------------
-egl_display_t* validate_display(EGLDisplay dpy) {
- egl_display_t * const dp = get_display(dpy);
+egl_display_ptr validate_display(EGLDisplay dpy) {
+ egl_display_ptr dp = get_display(dpy);
if (!dp)
- return setError(EGL_BAD_DISPLAY, (egl_display_t*)NULL);
+ return setError(EGL_BAD_DISPLAY, egl_display_ptr(NULL));
if (!dp->isReady())
- return setError(EGL_NOT_INITIALIZED, (egl_display_t*)NULL);
+ return setError(EGL_NOT_INITIALIZED, egl_display_ptr(NULL));
return dp;
}
-egl_connection_t* validate_display_config(EGLDisplay dpy, EGLConfig,
- egl_display_t const*& dp) {
- dp = validate_display(dpy);
+egl_display_ptr validate_display_connection(EGLDisplay dpy,
+ egl_connection_t*& cnx) {
+ cnx = NULL;
+ egl_display_ptr dp = validate_display(dpy);
if (!dp)
- return (egl_connection_t*) NULL;
-
- egl_connection_t* const cnx = &gEGLImpl;
+ return dp;
+ cnx = &gEGLImpl;
if (cnx->dso == 0) {
- return setError(EGL_BAD_CONFIG, (egl_connection_t*)NULL);
+ return setError(EGL_BAD_CONFIG, egl_display_ptr(NULL));
}
- return cnx;
+ return dp;
}
// ----------------------------------------------------------------------------
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index 8bfa16d..1bc4eb7 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -133,7 +133,7 @@
{
clearError();
- egl_display_t * const dp = get_display(dpy);
+ egl_display_ptr dp = get_display(dpy);
if (!dp) return setError(EGL_BAD_DISPLAY, EGL_FALSE);
EGLBoolean res = dp->initialize(major, minor);
@@ -149,7 +149,7 @@
clearError();
- egl_display_t* const dp = get_display(dpy);
+ egl_display_ptr dp = get_display(dpy);
if (!dp) return setError(EGL_BAD_DISPLAY, EGL_FALSE);
EGLBoolean res = dp->terminate();
@@ -167,7 +167,7 @@
{
clearError();
- egl_display_t const * const dp = validate_display(dpy);
+ const egl_display_ptr dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
if (num_config==0) {
@@ -192,7 +192,7 @@
{
clearError();
- egl_display_t const * const dp = validate_display(dpy);
+ const egl_display_ptr dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
if (num_config==0) {
@@ -215,9 +215,9 @@
{
clearError();
- egl_display_t const* dp = 0;
- egl_connection_t* cnx = validate_display_config(dpy, config, dp);
- if (!cnx) return EGL_FALSE;
+ egl_connection_t* cnx = NULL;
+ const egl_display_ptr dp = validate_display_connection(dpy, cnx);
+ if (!dp) return EGL_FALSE;
return cnx->egl.eglGetConfigAttrib(
dp->disp.dpy, config, attribute, value);
@@ -233,9 +233,9 @@
{
clearError();
- egl_display_t const* dp = 0;
- egl_connection_t* cnx = validate_display_config(dpy, config, dp);
- if (cnx) {
+ egl_connection_t* cnx = NULL;
+ egl_display_ptr dp = validate_display_connection(dpy, cnx);
+ if (dp) {
EGLDisplay iDpy = dp->disp.dpy;
EGLint format;
@@ -267,7 +267,8 @@
EGLSurface surface = cnx->egl.eglCreateWindowSurface(
iDpy, config, window, attrib_list);
if (surface != EGL_NO_SURFACE) {
- egl_surface_t* s = new egl_surface_t(dpy, config, window, surface, cnx);
+ egl_surface_t* s = new egl_surface_t(dp.get(), config, window,
+ surface, cnx);
return s;
}
@@ -284,13 +285,14 @@
{
clearError();
- egl_display_t const* dp = 0;
- egl_connection_t* cnx = validate_display_config(dpy, config, dp);
- if (cnx) {
+ egl_connection_t* cnx = NULL;
+ egl_display_ptr dp = validate_display_connection(dpy, cnx);
+ if (dp) {
EGLSurface surface = cnx->egl.eglCreatePixmapSurface(
dp->disp.dpy, config, pixmap, attrib_list);
if (surface != EGL_NO_SURFACE) {
- egl_surface_t* s = new egl_surface_t(dpy, config, NULL, surface, cnx);
+ egl_surface_t* s = new egl_surface_t(dp.get(), config, NULL,
+ surface, cnx);
return s;
}
}
@@ -302,13 +304,14 @@
{
clearError();
- egl_display_t const* dp = 0;
- egl_connection_t* cnx = validate_display_config(dpy, config, dp);
- if (cnx) {
+ egl_connection_t* cnx = NULL;
+ egl_display_ptr dp = validate_display_connection(dpy, cnx);
+ if (dp) {
EGLSurface surface = cnx->egl.eglCreatePbufferSurface(
dp->disp.dpy, config, attrib_list);
if (surface != EGL_NO_SURFACE) {
- egl_surface_t* s = new egl_surface_t(dpy, config, NULL, surface, cnx);
+ egl_surface_t* s = new egl_surface_t(dp.get(), config, NULL,
+ surface, cnx);
return s;
}
}
@@ -319,10 +322,10 @@
{
clearError();
- egl_display_t const * const dp = validate_display(dpy);
+ const egl_display_ptr dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
- SurfaceRef _s(dp, surface);
+ SurfaceRef _s(dp.get(), surface);
if (!_s.get())
return setError(EGL_BAD_SURFACE, EGL_FALSE);
@@ -339,10 +342,10 @@
{
clearError();
- egl_display_t const * const dp = validate_display(dpy);
+ const egl_display_ptr dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
- SurfaceRef _s(dp, surface);
+ SurfaceRef _s(dp.get(), surface);
if (!_s.get())
return setError(EGL_BAD_SURFACE, EGL_FALSE);
@@ -355,12 +358,12 @@
ATRACE_CALL();
clearError();
- egl_display_t const * const dp = validate_display(dpy);
+ const egl_display_ptr dp = validate_display(dpy);
if (!dp) {
return;
}
- SurfaceRef _s(dp, surface);
+ SurfaceRef _s(dp.get(), surface);
if (!_s.get()) {
setError(EGL_BAD_SURFACE, EGL_FALSE);
return;
@@ -381,9 +384,9 @@
{
clearError();
- egl_display_t const* dp = 0;
- egl_connection_t* cnx = validate_display_config(dpy, config, dp);
- if (cnx) {
+ egl_connection_t* cnx = NULL;
+ const egl_display_ptr dp = validate_display_connection(dpy, cnx);
+ if (dpy) {
if (share_list != EGL_NO_CONTEXT) {
egl_context_t* const c = get_context(share_list);
share_list = c->context;
@@ -406,7 +409,8 @@
}
};
}
- egl_context_t* c = new egl_context_t(dpy, context, config, cnx, version);
+ egl_context_t* c = new egl_context_t(dpy, context, config, cnx,
+ version);
#if EGL_TRACE
if (gEGLDebugLevel > 0)
GLTrace_eglCreateContext(version, c);
@@ -421,11 +425,11 @@
{
clearError();
- egl_display_t const * const dp = validate_display(dpy);
+ const egl_display_ptr dp = validate_display(dpy);
if (!dp)
return EGL_FALSE;
- ContextRef _c(dp, ctx);
+ ContextRef _c(dp.get(), ctx);
if (!_c.get())
return setError(EGL_BAD_CONTEXT, EGL_FALSE);
@@ -442,7 +446,7 @@
{
clearError();
- egl_display_t const * const dp = get_display(dpy);
+ egl_display_ptr dp = validate_display(dpy);
if (!dp) return setError(EGL_BAD_DISPLAY, EGL_FALSE);
// If ctx is not EGL_NO_CONTEXT, read is not EGL_NO_SURFACE, or draw is not
@@ -454,9 +458,9 @@
}
// get a reference to the object passed in
- ContextRef _c(dp, ctx);
- SurfaceRef _d(dp, draw);
- SurfaceRef _r(dp, read);
+ ContextRef _c(dp.get(), ctx);
+ SurfaceRef _d(dp.get(), draw);
+ SurfaceRef _r(dp.get(), read);
// validate the context (if not EGL_NO_CONTEXT)
if ((ctx != EGL_NO_CONTEXT) && !_c.get()) {
@@ -506,7 +510,7 @@
}
- EGLBoolean result = const_cast<egl_display_t*>(dp)->makeCurrent(c, cur_c,
+ EGLBoolean result = dp->makeCurrent(c, cur_c,
draw, read, ctx,
impl_draw, impl_read, impl_ctx);
@@ -538,10 +542,10 @@
{
clearError();
- egl_display_t const * const dp = validate_display(dpy);
+ const egl_display_ptr dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
- ContextRef _c(dp, ctx);
+ ContextRef _c(dp.get(), ctx);
if (!_c.get()) return setError(EGL_BAD_CONTEXT, EGL_FALSE);
egl_context_t * const c = get_context(ctx);
@@ -645,9 +649,11 @@
return NULL;
}
- // The EGL_ANDROID_blob_cache extension should not be exposed to
- // applications. It is used internally by the Android EGL layer.
- if (!strcmp(procname, "eglSetBlobCacheFuncsANDROID")) {
+ // These extensions should not be exposed to applications. They're used
+ // internally by the Android EGL layer.
+ if (!strcmp(procname, "eglSetBlobCacheFuncsANDROID") ||
+ !strcmp(procname, "eglHibernateProcessIMG") ||
+ !strcmp(procname, "eglAwakenProcessIMG")) {
return NULL;
}
@@ -715,15 +721,78 @@
return addr;
}
+class FrameCompletionThread : public Thread {
+public:
+
+ static void queueSync(EGLSyncKHR sync) {
+ static sp<FrameCompletionThread> thread(new FrameCompletionThread);
+ static bool running = false;
+ if (!running) {
+ thread->run("GPUFrameCompletion");
+ running = true;
+ }
+ {
+ Mutex::Autolock lock(thread->mMutex);
+ ScopedTrace st(ATRACE_TAG, String8::format("kicked off frame %d",
+ thread->mFramesQueued).string());
+ thread->mQueue.push_back(sync);
+ thread->mCondition.signal();
+ thread->mFramesQueued++;
+ ATRACE_INT("GPU Frames Outstanding", thread->mQueue.size());
+ }
+ }
+
+private:
+ FrameCompletionThread() : mFramesQueued(0), mFramesCompleted(0) {}
+
+ virtual bool threadLoop() {
+ EGLSyncKHR sync;
+ uint32_t frameNum;
+ {
+ Mutex::Autolock lock(mMutex);
+ while (mQueue.isEmpty()) {
+ mCondition.wait(mMutex);
+ }
+ sync = mQueue[0];
+ frameNum = mFramesCompleted;
+ }
+ EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ {
+ ScopedTrace st(ATRACE_TAG, String8::format("waiting for frame %d",
+ frameNum).string());
+ EGLint result = eglClientWaitSyncKHR(dpy, sync, 0, EGL_FOREVER_KHR);
+ if (result == EGL_FALSE) {
+ ALOGE("FrameCompletion: error waiting for fence: %#x", eglGetError());
+ } else if (result == EGL_TIMEOUT_EXPIRED_KHR) {
+ ALOGE("FrameCompletion: timeout waiting for fence");
+ }
+ eglDestroySyncKHR(dpy, sync);
+ }
+ {
+ Mutex::Autolock lock(mMutex);
+ mQueue.removeAt(0);
+ mFramesCompleted++;
+ ATRACE_INT("GPU Frames Outstanding", mQueue.size());
+ }
+ return true;
+ }
+
+ uint32_t mFramesQueued;
+ uint32_t mFramesCompleted;
+ Vector<EGLSyncKHR> mQueue;
+ Condition mCondition;
+ Mutex mMutex;
+};
+
EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface draw)
{
ATRACE_CALL();
clearError();
- egl_display_t const * const dp = validate_display(dpy);
+ const egl_display_ptr dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
- SurfaceRef _s(dp, draw);
+ SurfaceRef _s(dp.get(), draw);
if (!_s.get())
return setError(EGL_BAD_SURFACE, EGL_FALSE);
@@ -744,7 +813,19 @@
}
}
- return s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface);
+ EGLBoolean result = s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface);
+
+ if (CC_UNLIKELY(dp->traceGpuCompletion)) {
+ EGLSyncKHR sync = EGL_NO_SYNC_KHR;
+ {
+ sync = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, NULL);
+ }
+ if (sync != EGL_NO_SYNC_KHR) {
+ FrameCompletionThread::queueSync(sync);
+ }
+ }
+
+ return result;
}
EGLBoolean eglCopyBuffers( EGLDisplay dpy, EGLSurface surface,
@@ -752,10 +833,10 @@
{
clearError();
- egl_display_t const * const dp = validate_display(dpy);
+ const egl_display_ptr dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
- SurfaceRef _s(dp, surface);
+ SurfaceRef _s(dp.get(), surface);
if (!_s.get())
return setError(EGL_BAD_SURFACE, EGL_FALSE);
@@ -767,7 +848,7 @@
{
clearError();
- egl_display_t const * const dp = validate_display(dpy);
+ const egl_display_ptr dp = validate_display(dpy);
if (!dp) return (const char *) NULL;
switch (name) {
@@ -795,10 +876,10 @@
{
clearError();
- egl_display_t const * const dp = validate_display(dpy);
+ const egl_display_ptr dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
- SurfaceRef _s(dp, surface);
+ SurfaceRef _s(dp.get(), surface);
if (!_s.get())
return setError(EGL_BAD_SURFACE, EGL_FALSE);
@@ -815,10 +896,10 @@
{
clearError();
- egl_display_t const * const dp = validate_display(dpy);
+ const egl_display_ptr dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
- SurfaceRef _s(dp, surface);
+ SurfaceRef _s(dp.get(), surface);
if (!_s.get())
return setError(EGL_BAD_SURFACE, EGL_FALSE);
@@ -835,10 +916,10 @@
{
clearError();
- egl_display_t const * const dp = validate_display(dpy);
+ const egl_display_ptr dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
- SurfaceRef _s(dp, surface);
+ SurfaceRef _s(dp.get(), surface);
if (!_s.get())
return setError(EGL_BAD_SURFACE, EGL_FALSE);
@@ -854,7 +935,7 @@
{
clearError();
- egl_display_t const * const dp = validate_display(dpy);
+ const egl_display_ptr dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
EGLBoolean res = EGL_TRUE;
@@ -948,9 +1029,9 @@
{
clearError();
- egl_display_t const* dp = 0;
- egl_connection_t* cnx = validate_display_config(dpy, config, dp);
- if (!cnx) return EGL_FALSE;
+ egl_connection_t* cnx = NULL;
+ const egl_display_ptr dp = validate_display_connection(dpy, cnx);
+ if (!dp) return EGL_FALSE;
if (cnx->egl.eglCreatePbufferFromClientBuffer) {
return cnx->egl.eglCreatePbufferFromClientBuffer(
dp->disp.dpy, buftype, buffer, config, attrib_list);
@@ -967,10 +1048,10 @@
{
clearError();
- egl_display_t const * const dp = validate_display(dpy);
+ const egl_display_ptr dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
- SurfaceRef _s(dp, surface);
+ SurfaceRef _s(dp.get(), surface);
if (!_s.get())
return setError(EGL_BAD_SURFACE, EGL_FALSE);
@@ -986,10 +1067,10 @@
{
clearError();
- egl_display_t const * const dp = validate_display(dpy);
+ const egl_display_ptr dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
- SurfaceRef _s(dp, surface);
+ SurfaceRef _s(dp.get(), surface);
if (!_s.get())
return setError(EGL_BAD_SURFACE, EGL_FALSE);
@@ -1005,10 +1086,10 @@
{
clearError();
- egl_display_t const * const dp = validate_display(dpy);
+ const egl_display_ptr dp = validate_display(dpy);
if (!dp) return EGL_NO_IMAGE_KHR;
- ContextRef _c(dp, ctx);
+ ContextRef _c(dp.get(), ctx);
egl_context_t * const c = _c.get();
EGLImageKHR result = EGL_NO_IMAGE_KHR;
@@ -1026,7 +1107,7 @@
{
clearError();
- egl_display_t const * const dp = validate_display(dpy);
+ const egl_display_ptr dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
@@ -1045,7 +1126,7 @@
{
clearError();
- egl_display_t const * const dp = validate_display(dpy);
+ const egl_display_ptr dp = validate_display(dpy);
if (!dp) return EGL_NO_SYNC_KHR;
EGLSyncKHR result = EGL_NO_SYNC_KHR;
@@ -1060,7 +1141,7 @@
{
clearError();
- egl_display_t const * const dp = validate_display(dpy);
+ const egl_display_ptr dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
EGLBoolean result = EGL_FALSE;
@@ -1076,7 +1157,7 @@
{
clearError();
- egl_display_t const * const dp = validate_display(dpy);
+ const egl_display_ptr dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
EGLBoolean result = EGL_FALSE;
@@ -1093,7 +1174,7 @@
{
clearError();
- egl_display_t const * const dp = validate_display(dpy);
+ const egl_display_ptr dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
EGLBoolean result = EGL_FALSE;
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index b80afd6..2e08f24 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -14,6 +14,8 @@
** limitations under the License.
*/
+#define __STDC_LIMIT_MACROS 1
+
#include <string.h>
#include "egl_cache.h"
@@ -57,6 +59,7 @@
// extensions not exposed to applications but used by the ANDROID system
// "EGL_ANDROID_recordable " // mandatory
// "EGL_ANDROID_blob_cache " // strongly recommended
+// "EGL_IMG_hibernate_process " // optional
extern void initEglTraceLevel();
extern void initEglDebugLevel();
@@ -67,7 +70,8 @@
egl_display_t egl_display_t::sDisplay[NUM_DISPLAYS];
egl_display_t::egl_display_t() :
- magic('_dpy'), finishOnSwap(false), refs(0) {
+ magic('_dpy'), finishOnSwap(false), traceGpuCompletion(false), refs(0),
+ mWakeCount(0), mHibernating(false), mAttemptHibernation(false) {
}
egl_display_t::~egl_display_t() {
@@ -239,6 +243,11 @@
finishOnSwap = true;
}
+ property_get("debug.egl.traceGpuCompletion", value, "0");
+ if (atoi(value)) {
+ traceGpuCompletion = true;
+ }
+
refs++;
if (major != NULL)
*major = VERSION_MAJOR;
@@ -341,12 +350,18 @@
disp.dpy, impl_draw, impl_read, impl_ctx);
if (result == EGL_TRUE) {
c->onMakeCurrent(draw, read);
+ if (!cur_c) {
+ mWakeCount++;
+ mAttemptHibernation = false;
+ }
}
} else {
result = cur_c->cnx->egl.eglMakeCurrent(
disp.dpy, impl_draw, impl_read, impl_ctx);
if (result == EGL_TRUE) {
cur_c->onLooseCurrent();
+ mWakeCount--;
+ mAttemptHibernation = true;
}
}
}
@@ -363,6 +378,52 @@
return result;
}
+bool egl_display_t::enter() {
+ Mutex::Autolock _l(lock);
+ ALOGE_IF(mWakeCount < 0 || mWakeCount == INT32_MAX,
+ "Invalid WakeCount (%d) on enter\n", mWakeCount);
+ mWakeCount++;
+ if (CC_UNLIKELY(mHibernating)) {
+ ALOGV("Awakening\n");
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (!cnx->egl.eglAwakenProcessIMG()) {
+ ALOGE("Failed to awaken EGL implementation\n");
+ return false;
+ }
+ mHibernating = false;
+ }
+ return true;
+}
+
+void egl_display_t::leave() {
+ Mutex::Autolock _l(lock);
+ ALOGE_IF(mWakeCount <= 0, "Invalid WakeCount (%d) on leave\n", mWakeCount);
+ if (--mWakeCount == 0 && CC_UNLIKELY(mAttemptHibernation)) {
+ egl_connection_t* const cnx = &gEGLImpl;
+ mAttemptHibernation = false;
+ if (cnx->egl.eglHibernateProcessIMG && cnx->egl.eglAwakenProcessIMG) {
+ ALOGV("Hibernating\n");
+ if (!cnx->egl.eglHibernateProcessIMG()) {
+ ALOGE("Failed to hibernate EGL implementation\n");
+ return;
+ }
+ mHibernating = true;
+ }
+ }
+}
+
+void egl_display_t::onWindowSurfaceCreated() {
+ Mutex::Autolock _l(lock);
+ mWakeCount++;
+ mAttemptHibernation = false;
+}
+
+void egl_display_t::onWindowSurfaceDestroyed() {
+ Mutex::Autolock _l(lock);
+ mWakeCount--;
+ mAttemptHibernation = true;
+}
+
// ----------------------------------------------------------------------------
}; // namespace android
// ----------------------------------------------------------------------------
diff --git a/opengl/libs/EGL/egl_display.h b/opengl/libs/EGL/egl_display.h
index 43738ea..28607da 100644
--- a/opengl/libs/EGL/egl_display.h
+++ b/opengl/libs/EGL/egl_display.h
@@ -27,6 +27,7 @@
#include <GLES/gl.h>
#include <GLES/glext.h>
+#include <cutils/compiler.h>
#include <utils/SortedVector.h>
#include <utils/threads.h>
#include <utils/String8.h>
@@ -69,6 +70,16 @@
// add reference to this object. returns true if this is a valid object.
bool getObject(egl_object_t* object) const;
+ // These notifications allow the display to keep track of how many window
+ // surfaces exist, which it uses to decide whether to hibernate the
+ // underlying EGL implementation. They can be called by any thread without
+ // holding a lock, but must be called via egl_display_ptr to ensure
+ // proper hibernate/wakeup sequencing. If a surface destruction triggers
+ // hibernation, hibernation will be delayed at least until the calling
+ // thread's egl_display_ptr is destroyed.
+ void onWindowSurfaceCreated();
+ void onWindowSurfaceDestroyed();
+
static egl_display_t* get(EGLDisplay dpy);
static EGLDisplay getFromNativeDisplay(EGLNativeDisplayType disp);
@@ -107,9 +118,14 @@
public:
DisplayImpl disp;
- bool finishOnSwap;
+ bool finishOnSwap; // property: debug.egl.finish
+ bool traceGpuCompletion; // property: debug.egl.traceGpuCompletion
private:
+ friend class egl_display_ptr;
+ bool enter();
+ void leave();
+
uint32_t refs;
mutable Mutex lock;
SortedVector<egl_object_t*> objects;
@@ -117,19 +133,81 @@
String8 mVersionString;
String8 mClientApiString;
String8 mExtensionString;
+ int32_t mWakeCount;
+ bool mHibernating;
+ bool mAttemptHibernation;
};
// ----------------------------------------------------------------------------
-inline egl_display_t* get_display(EGLDisplay dpy) {
+// An egl_display_ptr is a kind of smart pointer for egl_display_t objects.
+// It doesn't refcount the egl_display_t, but does ensure that the underlying
+// EGL implementation is "awake" (not hibernating) and ready for use as long
+// as the egl_display_ptr exists.
+class egl_display_ptr {
+public:
+ explicit egl_display_ptr(egl_display_t* dpy): mDpy(dpy) {
+ if (mDpy) {
+ if (CC_UNLIKELY(!mDpy->enter())) {
+ mDpy = NULL;
+ }
+ }
+ }
+
+ // We only really need a C++11 move constructor, not a copy constructor.
+ // A move constructor would save an enter()/leave() pair on every EGL API
+ // call. But enabling -std=c++0x causes lots of errors elsewhere, so I
+ // can't use a move constructor until those are cleaned up.
+ //
+ // egl_display_ptr(egl_display_ptr&& other) {
+ // mDpy = other.mDpy;
+ // other.mDpy = NULL;
+ // }
+ //
+ egl_display_ptr(const egl_display_ptr& other): mDpy(other.mDpy) {
+ if (mDpy) {
+ mDpy->enter();
+ }
+ }
+
+ ~egl_display_ptr() {
+ if (mDpy) {
+ mDpy->leave();
+ }
+ }
+
+ const egl_display_t* operator->() const { return mDpy; }
+ egl_display_t* operator->() { return mDpy; }
+
+ const egl_display_t* get() const { return mDpy; }
+ egl_display_t* get() { return mDpy; }
+
+ operator bool() const { return mDpy != NULL; }
+
+private:
+ egl_display_t* mDpy;
+
+ // non-assignable
+ egl_display_ptr& operator=(const egl_display_ptr&);
+};
+
+// ----------------------------------------------------------------------------
+
+inline egl_display_ptr get_display(EGLDisplay dpy) {
+ return egl_display_ptr(egl_display_t::get(dpy));
+}
+
+// Does not ensure EGL is unhibernated. Use with caution: calls into the
+// underlying EGL implementation are not safe.
+inline egl_display_t* get_display_nowake(EGLDisplay dpy) {
return egl_display_t::get(dpy);
}
// ----------------------------------------------------------------------------
-egl_display_t* validate_display(EGLDisplay dpy);
-egl_connection_t* validate_display_config(EGLDisplay dpy,
- EGLConfig config, egl_display_t const*& dp);
+egl_display_ptr validate_display(EGLDisplay dpy);
+egl_display_ptr validate_display_connection(EGLDisplay dpy,
+ egl_connection_t*& cnx);
EGLBoolean validate_display_context(EGLDisplay dpy, EGLContext ctx);
EGLBoolean validate_display_surface(EGLDisplay dpy, EGLSurface surface);
diff --git a/opengl/libs/EGL/egl_entries.in b/opengl/libs/EGL/egl_entries.in
index bdd2a7e..9feb716 100644
--- a/opengl/libs/EGL/egl_entries.in
+++ b/opengl/libs/EGL/egl_entries.in
@@ -67,3 +67,8 @@
EGL_ENTRY(EGLuint64NV, eglGetSystemTimeFrequencyNV, void)
EGL_ENTRY(EGLuint64NV, eglGetSystemTimeNV, void)
+
+/* IMG extensions */
+
+EGL_ENTRY(EGLBoolean, eglHibernateProcessIMG, void)
+EGL_ENTRY(EGLBoolean, eglAwakenProcessIMG, void)
\ No newline at end of file
diff --git a/opengl/libs/EGL/egl_object.cpp b/opengl/libs/EGL/egl_object.cpp
index d0cbb31..aaa5e72 100644
--- a/opengl/libs/EGL/egl_object.cpp
+++ b/opengl/libs/EGL/egl_object.cpp
@@ -63,12 +63,33 @@
// ----------------------------------------------------------------------------
+egl_surface_t::egl_surface_t(egl_display_t* dpy, EGLConfig config,
+ EGLNativeWindowType win, EGLSurface surface,
+ egl_connection_t const* cnx) :
+ egl_object_t(dpy), surface(surface), config(config), win(win), cnx(cnx)
+{
+ if (win) {
+ getDisplay()->onWindowSurfaceCreated();
+ }
+}
+
+egl_surface_t::~egl_surface_t() {
+ ANativeWindow* const window = win.get();
+ if (window != NULL) {
+ native_window_set_buffers_format(window, 0);
+ if (native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL)) {
+ ALOGW("EGLNativeWindowType %p disconnect failed", window);
+ }
+ getDisplay()->onWindowSurfaceDestroyed();
+ }
+}
+
+// ----------------------------------------------------------------------------
+
egl_context_t::egl_context_t(EGLDisplay dpy, EGLContext context, EGLConfig config,
egl_connection_t const* cnx, int version) :
- egl_object_t(get_display(dpy)), dpy(dpy), context(context),
- config(config), read(0), draw(0), cnx(cnx),
- version(version)
-{
+ egl_object_t(get_display_nowake(dpy)), dpy(dpy), context(context),
+ config(config), read(0), draw(0), cnx(cnx), version(version) {
}
void egl_context_t::onLooseCurrent() {
diff --git a/opengl/libs/EGL/egl_object.h b/opengl/libs/EGL/egl_object.h
index 4d91f54..2a08424 100644
--- a/opengl/libs/EGL/egl_object.h
+++ b/opengl/libs/EGL/egl_object.h
@@ -127,24 +127,14 @@
class egl_surface_t : public egl_object_t {
protected:
- ~egl_surface_t() {
- ANativeWindow* const window = win.get();
- if (window != NULL) {
- native_window_set_buffers_format(window, 0);
- if (native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL)) {
- ALOGW("EGLNativeWindowType %p disconnect failed", window);
- }
- }
- }
+ ~egl_surface_t();
public:
typedef egl_object_t::LocalRef<egl_surface_t, EGLSurface> Ref;
- egl_surface_t(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win,
- EGLSurface surface, egl_connection_t const* cnx) :
- egl_object_t(get_display(dpy)), dpy(dpy), surface(surface),
- config(config), win(win), cnx(cnx) {
- }
- EGLDisplay dpy;
+ egl_surface_t(egl_display_t* dpy, EGLConfig config,
+ EGLNativeWindowType win, EGLSurface surface,
+ egl_connection_t const* cnx);
+
EGLSurface surface;
EGLConfig config;
sp<ANativeWindow> win;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index e742d3e..4e13377 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
@@ -23,6 +25,7 @@
#include <utils/Errors.h>
#include <utils/String8.h>
#include <utils/Thread.h>
+#include <utils/Trace.h>
#include <utils/Vector.h>
#include <hardware/hardware.h>
@@ -48,8 +51,9 @@
mNumOVLayers(0), mNumFBLayers(0),
mDpy(EGL_NO_DISPLAY), mSur(EGL_NO_SURFACE),
mEventHandler(handler),
- mRefreshPeriod(refreshPeriod)
+ mRefreshPeriod(refreshPeriod), mVSyncCount(0)
{
+ bool needVSyncThread = false;
int err = hw_get_module(HWC_HARDWARE_MODULE_ID, &mModule);
ALOGW_IF(err, "%s module not found", HWC_HARDWARE_MODULE_ID);
if (err == 0) {
@@ -64,12 +68,17 @@
mHwc->registerProcs(mHwc, &mCBContext.procs);
memset(mCBContext.procs.zero, 0, sizeof(mCBContext.procs.zero));
}
-
if (mHwc->common.version < HWC_DEVICE_API_VERSION_0_3) {
- // we don't have VSYNC support, we need to fake it
- mVSyncThread = new VSyncThread(*this);
+ needVSyncThread = true;
}
}
+ } else {
+ needVSyncThread = true;
+ }
+
+ if (needVSyncThread) {
+ // we don't have VSYNC support, we need to fake it
+ mVSyncThread = new VSyncThread(*this);
}
}
@@ -100,20 +109,20 @@
}
void HWComposer::vsync(int dpy, int64_t timestamp) {
+ ATRACE_INT("VSYNC", ++mVSyncCount&1);
mEventHandler.onVSyncReceived(dpy, timestamp);
}
status_t HWComposer::eventControl(int event, int enabled) {
status_t err = NO_ERROR;
- if (mHwc->common.version >= HWC_DEVICE_API_VERSION_0_3) {
+ if (mHwc && mHwc->common.version >= HWC_DEVICE_API_VERSION_0_3) {
err = mHwc->methods->eventControl(mHwc, event, enabled);
- } else {
- if (mVSyncThread != NULL) {
- mVSyncThread->setEnabled(enabled);
- } else {
- err = BAD_VALUE;
- }
}
+
+ if (err == NO_ERROR && mVSyncThread != NULL) {
+ mVSyncThread->setEnabled(enabled);
+ }
+
return err;
}
@@ -245,4 +254,58 @@
}
// ---------------------------------------------------------------------------
+
+HWComposer::VSyncThread::VSyncThread(HWComposer& hwc)
+ : mHwc(hwc), mEnabled(false),
+ mNextFakeVSync(0),
+ mRefreshPeriod(hwc.mRefreshPeriod)
+{
+}
+
+void HWComposer::VSyncThread::setEnabled(bool enabled) {
+ Mutex::Autolock _l(mLock);
+ mEnabled = enabled;
+ mCondition.signal();
+}
+
+void HWComposer::VSyncThread::onFirstRef() {
+ run("VSyncThread", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE);
+}
+
+bool HWComposer::VSyncThread::threadLoop() {
+ { // scope for lock
+ Mutex::Autolock _l(mLock);
+ while (!mEnabled) {
+ mCondition.wait(mLock);
+ }
+ }
+
+ const nsecs_t period = mRefreshPeriod;
+ const nsecs_t now = systemTime(CLOCK_MONOTONIC);
+ nsecs_t next_vsync = mNextFakeVSync;
+ nsecs_t sleep = next_vsync - now;
+ if (sleep < 0) {
+ // we missed, find where the next vsync should be
+ sleep = (period - ((now - next_vsync) % period));
+ next_vsync = now + sleep;
+ }
+ mNextFakeVSync = next_vsync + period;
+
+ struct timespec spec;
+ spec.tv_sec = next_vsync / 1000000000;
+ spec.tv_nsec = next_vsync % 1000000000;
+
+ int err;
+ do {
+ err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &spec, NULL);
+ } while (err<0 && errno == EINTR);
+
+ if (err == 0) {
+ mHwc.mEventHandler.onVSyncReceived(0, next_vsync);
+ }
+
+ return true;
+}
+
+// ---------------------------------------------------------------------------
}; // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 60a6367..7814594 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -97,56 +97,11 @@
bool mEnabled;
mutable nsecs_t mNextFakeVSync;
nsecs_t mRefreshPeriod;
-
- virtual void onFirstRef() {
- run("VSyncThread",
- PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE);
- }
-
- virtual bool threadLoop() {
- { // scope for lock
- Mutex::Autolock _l(mLock);
- while (!mEnabled) {
- mCondition.wait(mLock);
- }
- }
-
- const nsecs_t period = mRefreshPeriod;
- const nsecs_t now = systemTime(CLOCK_MONOTONIC);
- nsecs_t next_vsync = mNextFakeVSync;
- nsecs_t sleep = next_vsync - now;
- if (sleep < 0) {
- // we missed, find where the next vsync should be
- sleep = (period - ((now - next_vsync) % period));
- next_vsync = now + sleep;
- }
- mNextFakeVSync = next_vsync + period;
-
- struct timespec spec;
- spec.tv_sec = next_vsync / 1000000000;
- spec.tv_nsec = next_vsync % 1000000000;
-
- // NOTE: EINTR can happen with clock_nanosleep(), in case of
- // any error (including EINTR) we go through the condition's
- // test -- this is always correct and easy.
- if (::clock_nanosleep(CLOCK_MONOTONIC,
- TIMER_ABSTIME, &spec, NULL) == 0) {
- mHwc.mEventHandler.onVSyncReceived(0, next_vsync);
- }
- return true;
- }
-
+ virtual void onFirstRef();
+ virtual bool threadLoop();
public:
- VSyncThread(HWComposer& hwc) :
- mHwc(hwc), mEnabled(false),
- mNextFakeVSync(0),
- mRefreshPeriod(hwc.mRefreshPeriod) {
- }
- void setEnabled(bool enabled) {
- Mutex::Autolock _l(mLock);
- mEnabled = enabled;
- mCondition.signal();
- }
+ VSyncThread(HWComposer& hwc);
+ void setEnabled(bool enabled);
};
friend class VSyncThread;
@@ -187,6 +142,7 @@
cb_context mCBContext;
EventHandler& mEventHandler;
nsecs_t mRefreshPeriod;
+ size_t mVSyncCount;
sp<VSyncThread> mVSyncThread;
};
diff --git a/services/surfaceflinger/EventThread.cpp b/services/surfaceflinger/EventThread.cpp
index b05b7c2..cc44186 100644
--- a/services/surfaceflinger/EventThread.cpp
+++ b/services/surfaceflinger/EventThread.cpp
@@ -123,13 +123,9 @@
Mutex::Autolock _l(mLock);
do {
- // check if we have received a VSYNC event
- if (mVSyncTimestamp) {
- // we have a VSYNC event pending
- timestamp = mVSyncTimestamp;
- mVSyncTimestamp = 0;
- break;
- }
+ // latch VSYNC event if any
+ timestamp = mVSyncTimestamp;
+ mVSyncTimestamp = 0;
// check if we should be waiting for VSYNC events
bool waitForNextVsync = false;
@@ -144,17 +140,33 @@
}
}
- // enable or disable VSYNC events
- mHw.getHwComposer().eventControl(
- HWComposer::EVENT_VSYNC, waitForNextVsync);
+ if (timestamp) {
+ if (!waitForNextVsync) {
+ // we received a VSYNC but we have no clients
+ // don't report it, and disable VSYNC events
+ mHw.getHwComposer().eventControl(
+ HWComposer::EVENT_VSYNC, false);
+ } else {
+ // report VSYNC event
+ break;
+ }
+ } else {
+ // never disable VSYNC events immediately, instead
+ // we'll wait to receive the event and we'll
+ // reevaluate whether we need to dispatch it and/or
+ // disable VSYNC events then.
+ if (waitForNextVsync) {
+ // enable
+ mHw.getHwComposer().eventControl(
+ HWComposer::EVENT_VSYNC, true);
+ }
+ }
// wait for something to happen
mCondition.wait(mLock);
} while(true);
// process vsync event
-
- ATRACE_INT("VSYNC", mDeliveredEvents&1);
mDeliveredEvents++;
mLastVSyncTimestamp = timestamp;