Transfer HWC release fences to BufferQueue

After a HWC set, each SurfaceFlinger Layer retrieves the release fence
HWC returned and gives it to the layer's SurfaceTexture. The
SurfaceTexture accumulates the fences into a merged fence until the
next updateTexImage, then passes the merged fence to the BufferQueue
in releaseBuffer.

In a follow-on change, BufferQueue will return the fence along with
the buffer slot in dequeueBuffer. For now, dequeueBuffer waits for the
fence to signal before returning.

The releaseFence default value for BufferQueue::releaseBuffer() is
temporary to avoid transient build breaks with a multi-project
checkin. It'll disappear in the next change.

Change-Id: Iaa9a0d5775235585d9cbf453d3a64623d08013d9
diff --git a/libs/gui/Android.mk b/libs/gui/Android.mk
index 8fc96cf..1cda14e 100644
--- a/libs/gui/Android.mk
+++ b/libs/gui/Android.mk
@@ -25,14 +25,15 @@
 	CpuConsumer.cpp
 
 LOCAL_SHARED_LIBRARIES := \
-	libcutils \
-	libutils \
 	libbinder \
-	libhardware \
-	libhardware_legacy \
-	libui \
+	libcutils \
 	libEGL \
 	libGLESv2 \
+	libhardware \
+	libhardware_legacy \
+	libsync \
+	libui \
+	libutils \
 
 
 LOCAL_MODULE:= libgui
diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp
index a2e08c0..23ac882 100644
--- a/libs/gui/BufferQueue.cpp
+++ b/libs/gui/BufferQueue.cpp
@@ -307,6 +307,7 @@
     status_t returnFlags(OK);
     EGLDisplay dpy = EGL_NO_DISPLAY;
     EGLSyncKHR fence = EGL_NO_SYNC_KHR;
+    sp<Fence> releaseFence;
 
     { // Scope for the lock
         Mutex::Autolock lock(mMutex);
@@ -318,7 +319,6 @@
         usage |= mConsumerUsageBits;
 
         int found = -1;
-        int foundSync = -1;
         int dequeuedCount = 0;
         bool tryAgain = true;
         while (tryAgain) {
@@ -369,7 +369,6 @@
 
             // look for a free buffer to give to the client
             found = INVALID_BUFFER_SLOT;
-            foundSync = INVALID_BUFFER_SLOT;
             dequeuedCount = 0;
             for (int i = 0; i < mBufferCount; i++) {
                 const int state = mSlots[i].mBufferState;
@@ -393,7 +392,6 @@
                         bool isOlder = mSlots[i].mFrameNumber <
                                 mSlots[found].mFrameNumber;
                         if (found < 0 || isOlder) {
-                            foundSync = i;
                             found = i;
                         }
                     }
@@ -484,6 +482,7 @@
             mSlots[buf].mGraphicBuffer = graphicBuffer;
             mSlots[buf].mRequestBufferCalled = false;
             mSlots[buf].mFence = EGL_NO_SYNC_KHR;
+            mSlots[buf].mReleaseFence.clear();
             mSlots[buf].mEglDisplay = EGL_NO_DISPLAY;
 
             returnFlags |= ISurfaceTexture::BUFFER_NEEDS_REALLOCATION;
@@ -491,7 +490,9 @@
 
         dpy = mSlots[buf].mEglDisplay;
         fence = mSlots[buf].mFence;
+        releaseFence = mSlots[buf].mReleaseFence;
         mSlots[buf].mFence = EGL_NO_SYNC_KHR;
+        mSlots[buf].mReleaseFence.clear();
     }  // end lock scope
 
     if (fence != EGL_NO_SYNC_KHR) {
@@ -507,6 +508,16 @@
         eglDestroySyncKHR(dpy, fence);
     }
 
+    if (releaseFence.get()) {
+        int err = releaseFence->wait(1000);
+        if (err == -ETIME) {
+            ALOGE("dequeueBuffer: timeout waiting for release fence");
+        } else if (err != NO_ERROR) {
+            ALOGE("dequeueBuffer: error waiting for sync fence: %d", err);
+        }
+        releaseFence.clear();
+    }
+
     ST_LOGV("dequeueBuffer: returning slot=%d buf=%p flags=%#x", *outBuf,
             mSlots[*outBuf].mGraphicBuffer->handle, returnFlags);
 
@@ -846,6 +857,7 @@
         eglDestroySyncKHR(mSlots[i].mEglDisplay, mSlots[i].mFence);
         mSlots[i].mFence = EGL_NO_SYNC_KHR;
     }
+    mSlots[i].mReleaseFence.clear();
 }
 
 void BufferQueue::freeAllBuffersLocked() {
@@ -896,7 +908,7 @@
 }
 
 status_t BufferQueue::releaseBuffer(int buf, EGLDisplay display,
-        EGLSyncKHR fence) {
+        EGLSyncKHR fence, const sp<Fence>& releaseFence) {
     ATRACE_CALL();
     ATRACE_BUFFER_INDEX(buf);
 
@@ -908,6 +920,7 @@
 
     mSlots[buf].mEglDisplay = display;
     mSlots[buf].mFence = fence;
+    mSlots[buf].mReleaseFence = releaseFence;
 
     // The buffer can now only be released if its in the acquired state
     if (mSlots[buf].mBufferState == BufferSlot::ACQUIRED) {
diff --git a/libs/gui/CpuConsumer.cpp b/libs/gui/CpuConsumer.cpp
index 48a54c7..bf2539f 100644
--- a/libs/gui/CpuConsumer.cpp
+++ b/libs/gui/CpuConsumer.cpp
@@ -107,7 +107,8 @@
     if (b.mGraphicBuffer != NULL) {
         if (mBufferPointers[buf] != NULL) {
             CC_LOGE("Reallocation of buffer %d while in consumer use!", buf);
-            mBufferQueue->releaseBuffer(buf, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR);
+            mBufferQueue->releaseBuffer(buf, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR,
+                    Fence::NO_FENCE);
             return BAD_VALUE;
         }
         mBufferSlot[buf] = b.mGraphicBuffer;
@@ -161,7 +162,8 @@
         CC_LOGE("%s: Unable to unlock graphic buffer %d", __FUNCTION__, buf);
         return err;
     }
-    err = mBufferQueue->releaseBuffer(buf, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR);
+    err = mBufferQueue->releaseBuffer(buf, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR,
+            Fence::NO_FENCE);
     if (err == BufferQueue::STALE_BUFFER_SLOT) {
         freeBufferLocked(buf);
     } else if (err != OK) {
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index 55be4bc..8ef885b 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -236,8 +236,10 @@
         // not accept this buffer. this is used by SurfaceFlinger to
         // reject buffers which have the wrong size
         if (rejecter && rejecter->reject(mEGLSlots[buf].mGraphicBuffer, item)) {
-            mBufferQueue->releaseBuffer(buf, dpy, mEGLSlots[buf].mFence);
+            mBufferQueue->releaseBuffer(buf, dpy, mEGLSlots[buf].mFence,
+                    mEGLSlots[buf].mReleaseFence);
             mEGLSlots[buf].mFence = EGL_NO_SYNC_KHR;
+            mEGLSlots[buf].mReleaseFence.clear();
             glBindTexture(mTexTarget, mTexName);
             return NO_ERROR;
         }
@@ -284,8 +286,10 @@
         if (err != NO_ERROR) {
             // Release the buffer we just acquired.  It's not safe to
             // release the old buffer, so instead we just drop the new frame.
-            mBufferQueue->releaseBuffer(buf, dpy, mEGLSlots[buf].mFence);
+            mBufferQueue->releaseBuffer(buf, dpy, mEGLSlots[buf].mFence,
+                    mEGLSlots[buf].mReleaseFence);
             mEGLSlots[buf].mFence = EGL_NO_SYNC_KHR;
+            mEGLSlots[buf].mReleaseFence.clear();
             return err;
         }
 
@@ -297,9 +301,10 @@
         // release old buffer
         if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
             status_t status = mBufferQueue->releaseBuffer(mCurrentTexture, dpy,
-                    mEGLSlots[mCurrentTexture].mFence);
-
+                    mEGLSlots[mCurrentTexture].mFence,
+                    mEGLSlots[mCurrentTexture].mReleaseFence);
             mEGLSlots[mCurrentTexture].mFence = EGL_NO_SYNC_KHR;
+            mEGLSlots[mCurrentTexture].mReleaseFence.clear();
             if (status == BufferQueue::STALE_BUFFER_SLOT) {
                 freeBufferLocked(mCurrentTexture);
             } else if (status != NO_ERROR) {
@@ -328,6 +333,27 @@
     return err;
 }
 
+void SurfaceTexture::setReleaseFence(int fenceFd) {
+    if (fenceFd == -1)
+        return;
+    sp<Fence> fence(new Fence(fenceFd));
+    if (!mEGLSlots[mCurrentTexture].mReleaseFence.get()) {
+        mEGLSlots[mCurrentTexture].mReleaseFence = fence;
+    } else {
+        sp<Fence> mergedFence = Fence::merge(
+                String8("SurfaceTexture merged release"),
+                mEGLSlots[mCurrentTexture].mReleaseFence, fence);
+        if (mergedFence.get()) {
+            ALOGE("failed to merge release fences");
+            // synchronization is broken, the best we can do is hope fences
+            // signal in order so the new fence will act like a union
+            mEGLSlots[mCurrentTexture].mReleaseFence = fence;
+        } else {
+            mEGLSlots[mCurrentTexture].mReleaseFence = mergedFence;
+        }
+    }
+}
+
 status_t SurfaceTexture::detachFromContext() {
     ATRACE_CALL();
     ST_LOGV("detachFromContext");