diff --git a/include/gui/BufferQueue.h b/include/gui/BufferQueue.h index 04a2f5295..6db2c8778 100644 --- a/include/gui/BufferQueue.h +++ b/include/gui/BufferQueue.h @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -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& 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 mReleaseFence; + // Indicates whether this buffer has been seen by a consumer yet bool mAcquireCalled; diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h index 2635e2f3a..622724e5f 100644 --- a/include/gui/SurfaceTexture.h +++ b/include/gui/SurfaceTexture.h @@ -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 mReleaseFence; }; // mEglDisplay is the EGLDisplay with which this SurfaceTexture is currently diff --git a/include/ui/Fence.h b/include/ui/Fence.h index 19d1830a9..195f2e9e3 100644 --- a/include/ui/Fence.h +++ b/include/ui/Fence.h @@ -39,6 +39,7 @@ class Fence : public LightRefBase, public Flattenable { public: + static const sp 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 diff --git a/libs/gui/Android.mk b/libs/gui/Android.mk index 8fc96cff1..1cda14e44 100644 --- a/libs/gui/Android.mk +++ b/libs/gui/Android.mk @@ -25,14 +25,15 @@ LOCAL_SRC_FILES:= \ 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 a2e08c036..23ac88212 100644 --- a/libs/gui/BufferQueue.cpp +++ b/libs/gui/BufferQueue.cpp @@ -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 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& 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) { diff --git a/libs/gui/CpuConsumer.cpp b/libs/gui/CpuConsumer.cpp index 48a54c77e..bf2539fb6 100644 --- a/libs/gui/CpuConsumer.cpp +++ b/libs/gui/CpuConsumer.cpp @@ -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) { diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp index 55be4bcf2..8ef885b3e 100644 --- a/libs/gui/SurfaceTexture.cpp +++ b/libs/gui/SurfaceTexture.cpp @@ -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(new Fence(fenceFd)); + if (!mEGLSlots[mCurrentTexture].mReleaseFence.get()) { + mEGLSlots[mCurrentTexture].mReleaseFence = fence; + } else { + sp 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"); diff --git a/libs/ui/Fence.cpp b/libs/ui/Fence.cpp index 993585b03..5c17d10e7 100644 --- a/libs/ui/Fence.cpp +++ b/libs/ui/Fence.cpp @@ -26,6 +26,8 @@ namespace android { +const sp Fence::NO_FENCE = sp(); + Fence::Fence() : mFenceFd(-1) { } @@ -55,7 +57,7 @@ sp Fence::merge(const String8& name, const sp& f1, if (result == -1) { ALOGE("merge: sync_merge returned an error: %s (%d)", strerror(-errno), errno); - return sp(); + return NO_FENCE; } return sp(new Fence(result)); } diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp index 02ec86ec4..02d2b102b 100644 --- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp +++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp @@ -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; } diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index 385be63b5..7c09ab2f0 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -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; diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index 05c6f6ddd..cb4c2db8a 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -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; diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 890bcb446..64e4b5f44 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -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(); diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 7a164aa4e..32456f453 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -82,7 +82,7 @@ public: // LayerBaseClient interface virtual wp getSurfaceTextureBinder() const; - virtual void onLayerDisplayed(); + virtual void onLayerDisplayed(HWComposer::HWCLayerInterface* layer); virtual bool onPreComposition(); // only for debugging diff --git a/services/surfaceflinger/LayerBase.h b/services/surfaceflinger/LayerBase.h index 4d5d1e402..698cdb839 100644 --- a/services/surfaceflinger/LayerBase.h +++ b/services/surfaceflinger/LayerBase.h @@ -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. diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index e059f1ca5..981d69424 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -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;