surfaceflinger: add more sync operations to RenderEngine

Add RenderEngine::finish and RenderEngine::waitFence.  Rework flush
not to fall back to finish.

Test: SurfaceFlinger_test
Change-Id: I2e5738f72b4aa1186d45d23cab9055f96d90ff23
diff --git a/services/surfaceflinger/RenderEngine/GLExtensions.cpp b/services/surfaceflinger/RenderEngine/GLExtensions.cpp
index b7f8664..81af21f 100644
--- a/services/surfaceflinger/RenderEngine/GLExtensions.cpp
+++ b/services/surfaceflinger/RenderEngine/GLExtensions.cpp
@@ -89,6 +89,16 @@
         hasEGLExtension("EGL_KHR_no_config_context")) {
         mHasNoConfigContext = true;
     }
+
+    if (hasEGLExtension("EGL_ANDROID_native_fence_sync")) {
+        mHasNativeFenceSync = true;
+    }
+    if (hasEGLExtension("EGL_KHR_fence_sync")) {
+        mHasFenceSync = true;
+    }
+    if (hasEGLExtension("EGL_KHR_wait_sync")) {
+        mHasWaitSync = true;
+    }
 }
 
 char const* GLExtensions::getEGLVersion() const {
diff --git a/services/surfaceflinger/RenderEngine/GLExtensions.h b/services/surfaceflinger/RenderEngine/GLExtensions.h
index 47f2f67..1ec0fd9 100644
--- a/services/surfaceflinger/RenderEngine/GLExtensions.h
+++ b/services/surfaceflinger/RenderEngine/GLExtensions.h
@@ -36,6 +36,9 @@
     friend class Singleton<GLExtensions>;
 
     bool mHasNoConfigContext = false;
+    bool mHasNativeFenceSync = false;
+    bool mHasFenceSync = false;
+    bool mHasWaitSync = false;
 
     String8 mVendor;
     String8 mRenderer;
@@ -57,6 +60,9 @@
 
 public:
     bool hasNoConfigContext() const { return mHasNoConfigContext; }
+    bool hasNativeFenceSync() const { return mHasNativeFenceSync; }
+    bool hasFenceSync() const { return mHasFenceSync; }
+    bool hasWaitSync() const { return mHasWaitSync; }
 
     void initWithGLStrings(GLubyte const* vendor, GLubyte const* renderer, GLubyte const* version,
                            GLubyte const* extensions);
diff --git a/services/surfaceflinger/RenderEngine/RenderEngine.cpp b/services/surfaceflinger/RenderEngine/RenderEngine.cpp
index d3bda17..d5471ad 100644
--- a/services/surfaceflinger/RenderEngine/RenderEngine.cpp
+++ b/services/surfaceflinger/RenderEngine/RenderEngine.cpp
@@ -169,6 +169,88 @@
     eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
 }
 
+base::unique_fd RenderEngine::flush() {
+    if (!GLExtensions::getInstance().hasNativeFenceSync()) {
+        return base::unique_fd();
+    }
+
+    EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, NULL);
+    if (sync == EGL_NO_SYNC_KHR) {
+        ALOGW("failed to create EGL native fence sync: %#x", eglGetError());
+        return base::unique_fd();
+    }
+
+    // native fence fd will not be populated until flush() is done.
+    glFlush();
+
+    // get the fence fd
+    base::unique_fd fenceFd(eglDupNativeFenceFDANDROID(mEGLDisplay, sync));
+    eglDestroySyncKHR(mEGLDisplay, sync);
+    if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
+        ALOGW("failed to dup EGL native fence sync: %#x", eglGetError());
+    }
+
+    return fenceFd;
+}
+
+bool RenderEngine::finish() {
+    if (!GLExtensions::getInstance().hasFenceSync()) {
+        ALOGW("no synchronization support");
+        return false;
+    }
+
+    EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_FENCE_KHR, NULL);
+    if (sync == EGL_NO_SYNC_KHR) {
+        ALOGW("failed to create EGL fence sync: %#x", eglGetError());
+        return false;
+    }
+
+    EGLint result = eglClientWaitSyncKHR(mEGLDisplay, sync, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR,
+                                         2000000000 /*2 sec*/);
+    EGLint error = eglGetError();
+    eglDestroySyncKHR(mEGLDisplay, sync);
+    if (result != EGL_CONDITION_SATISFIED_KHR) {
+        if (result == EGL_TIMEOUT_EXPIRED_KHR) {
+            ALOGW("fence wait timed out");
+        } else {
+            ALOGW("error waiting on EGL fence: %#x", error);
+        }
+        return false;
+    }
+
+    return true;
+}
+
+bool RenderEngine::waitFence(base::unique_fd fenceFd) {
+    if (!GLExtensions::getInstance().hasNativeFenceSync() ||
+        !GLExtensions::getInstance().hasWaitSync()) {
+        return false;
+    }
+
+    EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd, EGL_NONE};
+    EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs);
+    if (sync == EGL_NO_SYNC_KHR) {
+        ALOGE("failed to create EGL native fence sync: %#x", eglGetError());
+        return false;
+    }
+
+    // fenceFd is now owned by EGLSync
+    (void)fenceFd.release();
+
+    // 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.
+    eglWaitSyncKHR(mEGLDisplay, sync, 0);
+    EGLint error = eglGetError();
+    eglDestroySyncKHR(mEGLDisplay, sync);
+    if (error != EGL_SUCCESS) {
+        ALOGE("failed to wait for EGL native fence sync: %#x", error);
+        return false;
+    }
+
+    return true;
+}
+
 void RenderEngine::checkErrors() const {
     do {
         // there could be more than one error flag
@@ -220,52 +302,6 @@
     drawMesh(mesh);
 }
 
-int RenderEngine::flush(bool wait) {
-    // Attempt to create a sync khr object that can produce a sync point. If that
-    // isn't available, create a non-dupable sync object in the fallback path and
-    // wait on it directly.
-    EGLSyncKHR sync;
-    if (!wait) {
-        EGLint syncFd = EGL_NO_NATIVE_FENCE_FD_ANDROID;
-
-        sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, NULL);
-        if (sync != EGL_NO_SYNC_KHR) {
-            // native fence fd will not be populated until flush() is done.
-            glFlush();
-
-            // get the sync fd
-            syncFd = eglDupNativeFenceFDANDROID(mEGLDisplay, sync);
-            if (syncFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
-                ALOGW("failed to dup sync khr object");
-            }
-
-            eglDestroySyncKHR(mEGLDisplay, sync);
-        }
-
-        if (syncFd != EGL_NO_NATIVE_FENCE_FD_ANDROID) {
-            return syncFd;
-        }
-    }
-
-    // fallback or explicit wait
-    sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_FENCE_KHR, NULL);
-    if (sync != EGL_NO_SYNC_KHR) {
-        EGLint result = eglClientWaitSyncKHR(mEGLDisplay, sync, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR,
-                                             2000000000 /*2 sec*/);
-        EGLint eglErr = eglGetError();
-        if (result == EGL_TIMEOUT_EXPIRED_KHR) {
-            ALOGW("fence wait timed out");
-        } else {
-            ALOGW_IF(eglErr != EGL_SUCCESS, "error waiting on EGL fence: %#x", eglErr);
-        }
-        eglDestroySyncKHR(mEGLDisplay, sync);
-    } else {
-        ALOGW("error creating EGL fence: %#x", eglGetError());
-    }
-
-    return -1;
-}
-
 void RenderEngine::clearWithColor(float red, float green, float blue, float alpha) {
     glClearColor(red, green, blue, alpha);
     glClear(GL_COLOR_BUFFER_BIT);
diff --git a/services/surfaceflinger/RenderEngine/RenderEngine.h b/services/surfaceflinger/RenderEngine/RenderEngine.h
index 3847347..32c669f 100644
--- a/services/surfaceflinger/RenderEngine/RenderEngine.h
+++ b/services/surfaceflinger/RenderEngine/RenderEngine.h
@@ -25,6 +25,7 @@
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
 #include <Transform.h>
+#include <android-base/unique_fd.h>
 #include <gui/SurfaceControl.h>
 #include <math/mat4.h>
 
@@ -82,9 +83,20 @@
     // dump the extension strings. always call the base class.
     virtual void dump(String8& result);
 
+    // synchronization
+
+    // flush submits RenderEngine command stream for execution and returns a
+    // native fence fd that is signaled when the execution has completed.  It
+    // returns -1 on errors.
+    base::unique_fd flush();
+    // finish waits until RenderEngine command stream has been executed.  It
+    // returns false on errors.
+    bool finish();
+    // waitFence inserts a wait on an external fence fd to RenderEngine
+    // command stream.  It returns false on errors.
+    bool waitFence(base::unique_fd fenceFd);
+
     // helpers
-    // flush returns -1 or a valid native fence fd owned by the caller
-    int flush(bool wait);
     void clearWithColor(float red, float green, float blue, float alpha);
     void fillRegionWithColor(const Region& region, uint32_t height, float red, float green,
                              float blue, float alpha);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 44d5ce4..a5b92b5 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -4538,9 +4538,10 @@
     // dependent on the context's EGLConfig.
     renderScreenImplLocked(renderArea, traverseLayers, true, useIdentityTransform);
 
-    *outSyncFd = getRenderEngine().flush(DEBUG_SCREENSHOTS);
-
     if (DEBUG_SCREENSHOTS) {
+        getRenderEngine().finish();
+        *outSyncFd = -1;
+
         const auto reqWidth = renderArea.getReqWidth();
         const auto reqHeight = renderArea.getReqHeight();
 
@@ -4548,6 +4549,12 @@
         getRenderEngine().readPixels(0, 0, reqWidth, reqHeight, pixels);
         checkScreenshot(reqWidth, reqHeight, reqWidth, pixels, traverseLayers);
         delete [] pixels;
+    } else {
+        base::unique_fd syncFd = getRenderEngine().flush();
+        if (syncFd < 0) {
+            getRenderEngine().finish();
+        }
+        *outSyncFd = syncFd.release();
     }
 
     return NO_ERROR;
@@ -4618,4 +4625,4 @@
 
 #if defined(__gl2_h_)
 #error "don't include gl2/gl2.h in this file"
-#endif
\ No newline at end of file
+#endif