OSDN Git Service

Transfer HWC release fences to BufferQueue
authorJesse Hall <jessehall@google.com>
Thu, 14 Jun 2012 21:45:17 +0000 (14:45 -0700)
committerJesse Hall <jessehall@google.com>
Fri, 22 Jun 2012 05:21:12 +0000 (22:21 -0700)
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

15 files changed:
include/gui/BufferQueue.h
include/gui/SurfaceTexture.h
include/ui/Fence.h
libs/gui/Android.mk
libs/gui/BufferQueue.cpp
libs/gui/CpuConsumer.cpp
libs/gui/SurfaceTexture.cpp
libs/ui/Fence.cpp
services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
services/surfaceflinger/DisplayHardware/HWComposer.cpp
services/surfaceflinger/DisplayHardware/HWComposer.h
services/surfaceflinger/Layer.cpp
services/surfaceflinger/Layer.h
services/surfaceflinger/LayerBase.h
services/surfaceflinger/SurfaceFlinger.cpp

index 04a2f52..6db2c87 100644 (file)
@@ -23,6 +23,7 @@
 #include <gui/IGraphicBufferAlloc.h>
 #include <gui/ISurfaceTexture.h>
 
+#include <ui/Fence.h>
 #include <ui/GraphicBuffer.h>
 
 #include <utils/String8.h>
@@ -218,7 +219,8 @@ public:
     //
     // Note that the dependencies on EGL will be removed once we switch to using
     // the Android HW Sync HAL.
-    status_t releaseBuffer(int buf, EGLDisplay display, EGLSyncKHR fence);
+    status_t releaseBuffer(int buf, EGLDisplay display, EGLSyncKHR fence,
+            const sp<Fence>& releaseFence = Fence::NO_FENCE);
 
     // consumerConnect connects a consumer to the BufferQueue.  Only one
     // consumer may be connected, and when that consumer disconnects the
@@ -378,6 +380,10 @@ private:
         // on a compile-time option) set to a new sync object in updateTexImage.
         EGLSyncKHR mFence;
 
+        // mReleaseFence is a fence which must signal before the contents of
+        // the buffer associated with this buffer slot may be overwritten.
+        sp<Fence> mReleaseFence;
+
         // Indicates whether this buffer has been seen by a consumer yet
         bool mAcquireCalled;
 
index 2635e2f..622724e 100644 (file)
@@ -91,6 +91,14 @@ public:
     // target texture belongs is bound to the calling thread.
     status_t updateTexImage();
 
+    // setReleaseFence stores a fence file descriptor that will signal when the
+    // current buffer is no longer being read. This fence will be returned to
+    // the producer when the current buffer is released by updateTexImage().
+    // Multiple fences can be set for a given buffer; they will be merged into
+    // a single union fence. The SurfaceTexture will close the file descriptor
+    // when finished with it.
+    void setReleaseFence(int fenceFd);
+
     // setBufferCountServer set the buffer count. If the client has requested
     // a buffer count using setBufferCount, the server-buffer count will
     // take effect once the client sets the count back to zero.
@@ -349,6 +357,13 @@ private:
         // to EGL_NO_SYNC_KHR when the buffer is created and (optionally, based
         // on a compile-time option) set to a new sync object in updateTexImage.
         EGLSyncKHR mFence;
+
+        // mReleaseFence is a fence which will signal when the buffer
+        // associated with this buffer slot is no longer being used by the
+        // consumer and can be overwritten. The buffer can be dequeued before
+        // the fence signals; the producer is responsible for delaying writes
+        // until it signals.
+        sp<Fence> mReleaseFence;
     };
 
     // mEglDisplay is the EGLDisplay with which this SurfaceTexture is currently
index 19d1830..195f2e9 100644 (file)
@@ -39,6 +39,7 @@ class Fence
     : public LightRefBase<Fence>, public Flattenable
 {
 public:
+    static const sp<Fence> NO_FENCE;
 
     // Construct a new Fence object with an invalid file descriptor.  This
     // should be done when the Fence object will be set up by unflattening
index 8fc96cf..1cda14e 100644 (file)
@@ -25,14 +25,15 @@ LOCAL_SRC_FILES:= \
        CpuConsumer.cpp
 
 LOCAL_SHARED_LIBRARIES := \
-       libcutils \
-       libutils \
        libbinder \
+       libcutils \
+       libEGL \
+       libGLESv2 \
        libhardware \
        libhardware_legacy \
+       libsync \
        libui \
-       libEGL \
-       libGLESv2 \
+       libutils \
 
 
 LOCAL_MODULE:= libgui
index a2e08c0..23ac882 100644 (file)
@@ -307,6 +307,7 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
     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 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
         usage |= mConsumerUsageBits;
 
         int found = -1;
-        int foundSync = -1;
         int dequeuedCount = 0;
         bool tryAgain = true;
         while (tryAgain) {
@@ -369,7 +369,6 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
 
             // 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 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
                         bool isOlder = mSlots[i].mFrameNumber <
                                 mSlots[found].mFrameNumber;
                         if (found < 0 || isOlder) {
-                            foundSync = i;
                             found = i;
                         }
                     }
@@ -484,6 +482,7 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
             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 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
 
         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 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
         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 @@ void BufferQueue::freeBufferLocked(int i) {
         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::acquireBuffer(BufferItem *buffer) {
 }
 
 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 @@ status_t BufferQueue::releaseBuffer(int buf, EGLDisplay display,
 
     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) {
index 48a54c7..bf2539f 100644 (file)
@@ -107,7 +107,8 @@ status_t CpuConsumer::lockNextBuffer(LockedBuffer *nativeBuffer) {
     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 @@ status_t CpuConsumer::unlockBuffer(const LockedBuffer &nativeBuffer) {
         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) {
index 55be4bc..8ef885b 100644 (file)
@@ -236,8 +236,10 @@ status_t SurfaceTexture::updateTexImage(BufferRejecter* rejecter) {
         // 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 @@ status_t SurfaceTexture::updateTexImage(BufferRejecter* rejecter) {
         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 @@ status_t SurfaceTexture::updateTexImage(BufferRejecter* rejecter) {
         // 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 @@ status_t SurfaceTexture::updateTexImage(BufferRejecter* rejecter) {
     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");
index 993585b..5c17d10 100644 (file)
@@ -26,6 +26,8 @@
 
 namespace android {
 
+const sp<Fence> Fence::NO_FENCE = sp<Fence>();
+
 Fence::Fence() :
     mFenceFd(-1) {
 }
@@ -55,7 +57,7 @@ sp<Fence> Fence::merge(const String8& name, const sp<Fence>& f1,
     if (result == -1) {
         ALOGE("merge: sync_merge returned an error: %s (%d)", strerror(-errno),
                 errno);
-        return sp<Fence>();
+        return NO_FENCE;
     }
     return sp<Fence>(new Fence(result));
 }
index 02ec86e..02d2b10 100644 (file)
@@ -109,7 +109,7 @@ void FramebufferSurface::onFirstRef() {
                     self->fbDev->post(self->fbDev, self->mBuffers[item.mBuf]->handle);
                     if (self->mCurrentBufferIndex >= 0) {
                         self->mBufferQueue->releaseBuffer(self->mCurrentBufferIndex,
-                                EGL_NO_DISPLAY, EGL_NO_SYNC_KHR);
+                                EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE);
                     }
                     self->mCurrentBufferIndex = item.mBuf;
                 }
index 385be63..7c09ab2 100644 (file)
@@ -346,6 +346,10 @@ public:
     virtual uint32_t getHints() const {
         return getLayer()->hints;
     }
+    virtual int getAndResetReleaseFenceFd() {
+        // not supported on VERSION_03
+        return -1;
+    }
 
     virtual void setDefaultState() {
         getLayer()->compositionType = HWC_FRAMEBUFFER;
@@ -407,6 +411,11 @@ public:
     virtual uint32_t getHints() const {
         return getLayer()->hints;
     }
+    virtual int getAndResetReleaseFenceFd() {
+        int fd = getLayer()->releaseFenceFd;
+        getLayer()->releaseFenceFd = -1;
+        return fd;
+    }
 
     virtual void setDefaultState() {
         getLayer()->compositionType = HWC_FRAMEBUFFER;
index 05c6f6d..cb4c2db 100644 (file)
@@ -100,6 +100,7 @@ public:
     public:
         virtual int32_t getCompositionType() const = 0;
         virtual uint32_t getHints() const = 0;
+        virtual int getAndResetReleaseFenceFd() = 0;
         virtual void setDefaultState() = 0;
         virtual void setSkip(bool skip) = 0;
         virtual void setBlending(uint32_t blending) = 0;
index 890bcb4..64e4b5f 100644 (file)
@@ -71,7 +71,11 @@ Layer::Layer(SurfaceFlinger* flinger,
     glGenTextures(1, &mTextureName);
 }
 
-void Layer::onLayerDisplayed() {
+void Layer::onLayerDisplayed(HWComposer::HWCLayerInterface* layer) {
+    if (layer) {
+        mSurfaceTexture->setReleaseFence(layer->getAndResetReleaseFenceFd());
+    }
+
     if (mFrameLatencyNeeded) {
         const DisplayHardware& hw(graphicPlane(0).displayHardware());
         mFrameStats[mFrameLatencyOffset].timestamp = mSurfaceTexture->getTimestamp();
index 7a164aa..32456f4 100644 (file)
@@ -82,7 +82,7 @@ public:
     // LayerBaseClient interface
     virtual wp<IBinder> getSurfaceTextureBinder() const;
 
-    virtual void onLayerDisplayed();
+    virtual void onLayerDisplayed(HWComposer::HWCLayerInterface* layer);
     virtual bool onPreComposition();
 
     // only for debugging
index 4d5d1e4..698cdb8 100644 (file)
@@ -210,7 +210,7 @@ public:
 
     /** called after page-flip
      */
-    virtual void onLayerDisplayed() { }
+    virtual void onLayerDisplayed(HWComposer::HWCLayerInterface* layer) { }
 
     /** called before composition.
      * returns true if the layer has pending updates.
index e059f1c..981d694 100644 (file)
@@ -472,8 +472,17 @@ void SurfaceFlinger::postFramebuffer()
     hw.flip(mSwapRegion);
 
     size_t numLayers = mVisibleLayersSortedByZ.size();
-    for (size_t i = 0; i < numLayers; i++) {
-        mVisibleLayersSortedByZ[i]->onLayerDisplayed();
+    HWComposer& hwc(graphicPlane(0).displayHardware().getHwComposer());
+    if (hwc.initCheck() == NO_ERROR) {
+        HWComposer::LayerListIterator cur = hwc.begin();
+        const HWComposer::LayerListIterator end = hwc.end();
+        for (size_t i = 0; cur != end && i < numLayers; ++i, ++cur) {
+            mVisibleLayersSortedByZ[i]->onLayerDisplayed(&*cur);
+        }
+    } else {
+        for (size_t i = 0; i < numLayers; i++) {
+            mVisibleLayersSortedByZ[i]->onLayerDisplayed(NULL);
+        }
     }
 
     mLastSwapBufferTime = systemTime() - now;