diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp index 8bfa16d06..8df7d3937 100644 --- a/opengl/libs/EGL/eglApi.cpp +++ b/opengl/libs/EGL/eglApi.cpp @@ -715,6 +715,69 @@ __eglMustCastToProperFunctionPointerType eglGetProcAddress(const char *procname) return addr; } +class FrameCompletionThread : public Thread { +public: + + static void queueSync(EGLSyncKHR sync) { + static sp 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 mQueue; + Condition mCondition; + Mutex mMutex; +}; + EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface draw) { ATRACE_CALL(); @@ -744,7 +807,19 @@ EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface draw) } } - 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, diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp index b80afd6c4..6b6f8cf41 100644 --- a/opengl/libs/EGL/egl_display.cpp +++ b/opengl/libs/EGL/egl_display.cpp @@ -67,7 +67,7 @@ extern void setGLHooksThreadSpecific(gl_hooks_t const *value); 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) { } egl_display_t::~egl_display_t() { @@ -239,6 +239,11 @@ EGLBoolean egl_display_t::initialize(EGLint *major, EGLint *minor) { finishOnSwap = true; } + property_get("debug.egl.traceGpuCompletion", value, "0"); + if (atoi(value)) { + traceGpuCompletion = true; + } + refs++; if (major != NULL) *major = VERSION_MAJOR; diff --git a/opengl/libs/EGL/egl_display.h b/opengl/libs/EGL/egl_display.h index 43738eac5..3e22efa52 100644 --- a/opengl/libs/EGL/egl_display.h +++ b/opengl/libs/EGL/egl_display.h @@ -107,7 +107,8 @@ private: public: DisplayImpl disp; - bool finishOnSwap; + bool finishOnSwap; // property: debug.egl.finish + bool traceGpuCompletion; // property: debug.egl.traceGpuCompletion private: uint32_t refs;