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
This commit is contained in:
Jesse Hall 2012-06-14 14:45:17 -07:00
parent a74cbc0649
commit ef19414bd8
15 changed files with 112 additions and 23 deletions

View 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;

View 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

View 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

View File

@ -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

View 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) {

View 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) {

View 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");

View 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));
}

View 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;
}

View 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;

View 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;

View 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();

View 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

View 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.

View 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;