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
diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h
index 80a010b..6e5a478 100644
--- a/include/gui/SurfaceTexture.h
+++ b/include/gui/SurfaceTexture.h
@@ -77,6 +77,9 @@
     //
     // 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 @@
     // ready to be read from.
     sp<Fence> 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 c999080..e21b65d 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 @@
     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 f39de4a..e78059d 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -333,11 +333,9 @@
         return;
     }
 
-    // TODO: replace this with a server-side wait
-    sp<Fence> 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.
     }