From 61e04b92bdeafc6fca89052d14dab1bd0c384a71 Mon Sep 17 00:00:00 2001 From: Jamie Gennis Date: Sun, 9 Sep 2012 17:48:42 -0700 Subject: [PATCH] SurfaceTexture: use eglWaitSync This change adds a compile-option to use eglWaitSyncANDROID to ensure that texturing operations that access the current buffer of a SurfaceTexture do not occur until the buffer is completely written. It also moves this synchronization into a new SurfaceTexture method called doGLFenceWait and changes SurfaceFlinger's Layer class to use that method rather than performing its own wait on the fence. Change-Id: I70afa88086ca7ff49a80e3cd03d423767db7cb88 --- include/gui/SurfaceTexture.h | 9 ++++ libs/gui/SurfaceTexture.cpp | 74 ++++++++++++++++++++++++++++++- services/surfaceflinger/Layer.cpp | 8 ++-- 3 files changed, 84 insertions(+), 7 deletions(-) diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h index 80a010b53..6e5a478b1 100644 --- a/include/gui/SurfaceTexture.h +++ b/include/gui/SurfaceTexture.h @@ -77,6 +77,9 @@ public: // // This call may only be made while the OpenGL ES context to which the // target texture belongs is bound to the calling thread. + // + // After calling this method the doGLFenceWait method must be called + // before issuing OpenGL ES commands that access the texture contents. status_t updateTexImage(); // setReleaseFence stores a fence file descriptor that will signal when the @@ -154,6 +157,12 @@ public: // ready to be read from. sp getCurrentFence() const; + // doGLFenceWait inserts a wait command into the OpenGL ES command stream + // to ensure that it is safe for future OpenGL ES commands to access the + // current texture buffer. This must be called each time updateTexImage + // is called before issuing OpenGL ES commands that access the texture. + status_t doGLFenceWait() const; + // isSynchronousMode returns whether the SurfaceTexture is currently in // synchronous mode. bool isSynchronousMode() const; diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp index c999080cc..e21b65d6a 100644 --- a/libs/gui/SurfaceTexture.cpp +++ b/libs/gui/SurfaceTexture.cpp @@ -48,9 +48,19 @@ #ifdef USE_FENCE_SYNC #error "USE_NATIVE_FENCE_SYNC and USE_FENCE_SYNC are incompatible" #endif -const bool useNativeFenceSync = true; +static const bool useNativeFenceSync = true; #else -const bool useNativeFenceSync = false; +static const bool useNativeFenceSync = false; +#endif + +// This compile option makes SurfaceTexture use the EGL_ANDROID_sync_wait +// extension to insert server-side waits into the GLES command stream. This +// feature requires the EGL_ANDROID_native_fence_sync and +// EGL_ANDROID_wait_sync extensions. +#ifdef USE_WAIT_SYNC +static const bool useWaitSync = true; +#else +static const bool useWaitSync = false; #endif // This compile option makes SurfaceTexture use the EGL_KHR_fence_sync extension @@ -725,6 +735,66 @@ sp SurfaceTexture::getCurrentFence() const { return mCurrentFence; } +status_t SurfaceTexture::doGLFenceWait() const { + Mutex::Autolock lock(mMutex); + + EGLDisplay dpy = eglGetCurrentDisplay(); + EGLContext ctx = eglGetCurrentContext(); + + if (mEglDisplay != dpy || mEglDisplay == EGL_NO_DISPLAY) { + ST_LOGE("doGLFenceWait: invalid current EGLDisplay"); + return INVALID_OPERATION; + } + + if (mEglContext != ctx || mEglContext == EGL_NO_CONTEXT) { + ST_LOGE("doGLFenceWait: invalid current EGLContext"); + return INVALID_OPERATION; + } + + if (mCurrentFence != NULL) { + if (useWaitSync) { + // Create an EGLSyncKHR from the current fence. + int fenceFd = mCurrentFence->dup(); + if (fenceFd == -1) { + ST_LOGE("doGLFenceWait: error dup'ing fence fd: %d", errno); + return -errno; + } + EGLint attribs[] = { + EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd, + EGL_NONE + }; + EGLSyncKHR sync = eglCreateSyncKHR(dpy, + EGL_SYNC_NATIVE_FENCE_ANDROID, attribs); + if (sync == EGL_NO_SYNC_KHR) { + close(fenceFd); + ST_LOGE("doGLFenceWait: error creating EGL fence: %#x", + eglGetError()); + return UNKNOWN_ERROR; + } + + // XXX: The spec draft is inconsistent as to whether this should + // return an EGLint or void. Ignore the return value for now, as + // it's not strictly needed. + eglWaitSyncANDROID(dpy, sync, 0); + EGLint eglErr = eglGetError(); + eglDestroySyncKHR(dpy, sync); + if (eglErr != EGL_SUCCESS) { + ST_LOGE("doGLFenceWait: error waiting for EGL fence: %#x", + eglErr); + return UNKNOWN_ERROR; + } + } else { + status_t err = mCurrentFence->wait(Fence::TIMEOUT_NEVER); + if (err != NO_ERROR) { + ST_LOGE("doGLFenceWait: error waiting for fence: %d", err); + return err; + } + } + } + + return NO_ERROR; +} + bool SurfaceTexture::isSynchronousMode() const { Mutex::Autolock lock(mMutex); return mBufferQueue->isSynchronousMode(); diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index f39de4a17..e78059df3 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -333,11 +333,9 @@ void Layer::onDraw(const sp& hw, const Region& clip) const return; } - // TODO: replace this with a server-side wait - sp fence = mSurfaceTexture->getCurrentFence(); - if (fence.get()) { - status_t err = fence->wait(Fence::TIMEOUT_NEVER); - ALOGW_IF(err != OK, "Layer::onDraw: failed waiting for fence: %d", err); + status_t err = mSurfaceTexture->doGLFenceWait(); + if (err != OK) { + ALOGE("onDraw: failed waiting for fence: %d", err); // Go ahead and draw the buffer anyway; no matter what we do the screen // is probably going to have something visibly wrong. }