From 29a3e90879fd96404c971e7187cd0e05927bbce0 Mon Sep 17 00:00:00 2001 From: Dan Stoza Date: Fri, 20 Jun 2014 13:13:57 -0700 Subject: [PATCH] BufferQueue: Add allocateBuffers method This adds an allocateBuffers method to BufferQueue, which instructs it to allocate up to the maximum number of buffers allowed by the current configuration. The goal is that this method can be called ahead of render time, which will prevent dequeueBuffers from blocking in allocation and inducing jank. This interface is also plumbed up to the native Surface (and, in another change, up to the Java Surface and ThreadedRenderer). Bug: 11792166 Change-Id: I4aa96b4351ea1c95ed5db228ca3ef98303229c74 --- include/gui/BufferQueueProducer.h | 4 ++ include/gui/IGraphicBufferProducer.h | 13 +++++ include/gui/Surface.h | 10 ++++ libs/gui/BufferQueueProducer.cpp | 55 ++++++++++++++++++++++ libs/gui/IGraphicBufferProducer.cpp | 25 ++++++++++ libs/gui/Surface.cpp | 7 +++ .../DisplayHardware/VirtualDisplaySurface.cpp | 6 +++ .../DisplayHardware/VirtualDisplaySurface.h | 2 + services/surfaceflinger/MonitoredProducer.cpp | 5 ++ services/surfaceflinger/MonitoredProducer.h | 2 + 10 files changed, 129 insertions(+) diff --git a/include/gui/BufferQueueProducer.h b/include/gui/BufferQueueProducer.h index 9df3633596..fe8a308963 100644 --- a/include/gui/BufferQueueProducer.h +++ b/include/gui/BufferQueueProducer.h @@ -169,6 +169,10 @@ public: // handle if any. virtual status_t setSidebandStream(const sp& stream); + // See IGraphicBufferProducer::allocateBuffers + virtual void allocateBuffers(bool async, uint32_t width, uint32_t height, + uint32_t format, uint32_t usage); + private: // This is required by the IBinder::DeathRecipient interface virtual void binderDied(const wp& who); diff --git a/include/gui/IGraphicBufferProducer.h b/include/gui/IGraphicBufferProducer.h index d9e116b5e7..9b96b2b849 100644 --- a/include/gui/IGraphicBufferProducer.h +++ b/include/gui/IGraphicBufferProducer.h @@ -429,6 +429,19 @@ public: // Passing NULL or a different stream handle will detach the previous // handle if any. virtual status_t setSidebandStream(const sp& stream) = 0; + + // Allocates buffers based on the given dimensions/format. + // + // This function will allocate up to the maximum number of buffers + // permitted by the current BufferQueue configuration. It will use the + // given format, dimensions, and usage bits, which are interpreted in the + // same way as for dequeueBuffer, and the async flag must be set the same + // way as for dequeueBuffer to ensure that the correct number of buffers are + // allocated. This is most useful to avoid an allocation delay during + // dequeueBuffer. If there are already the maximum number of buffers + // allocated, this function has no effect. + virtual void allocateBuffers(bool async, uint32_t width, uint32_t height, + uint32_t format, uint32_t usage) = 0; }; // ---------------------------------------------------------------------------- diff --git a/include/gui/Surface.h b/include/gui/Surface.h index d8e9756943..dcfe74f45d 100644 --- a/include/gui/Surface.h +++ b/include/gui/Surface.h @@ -91,6 +91,16 @@ public: */ void setSidebandStream(const sp& stream); + /* Allocates buffers based on the current dimensions/format. + * + * This function will allocate up to the maximum number of buffers + * permitted by the current BufferQueue configuration. It will use the + * default format and dimensions. This is most useful to avoid an allocation + * delay during dequeueBuffer. If there are already the maximum number of + * buffers allocated, this function has no effect. + */ + void allocateBuffers(); + protected: virtual ~Surface(); diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp index 7017ddf813..70c3ff33e8 100644 --- a/libs/gui/BufferQueueProducer.cpp +++ b/libs/gui/BufferQueueProducer.cpp @@ -331,6 +331,7 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot, if (returnFlags & BUFFER_NEEDS_REALLOCATION) { status_t error; + BQ_LOGV("dequeueBuffer: allocating a new buffer for slot %d", *outSlot); sp graphicBuffer(mCore->mAllocator->createGraphicBuffer( width, height, format, usage, &error)); if (graphicBuffer == NULL) { @@ -852,6 +853,60 @@ status_t BufferQueueProducer::setSidebandStream(const sp& stream) return NO_ERROR; } +void BufferQueueProducer::allocateBuffers(bool async, uint32_t width, + uint32_t height, uint32_t format, uint32_t usage) { + Vector freeSlots; + + Mutex::Autolock lock(mCore->mMutex); + + int currentBufferCount = 0; + for (int slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) { + if (mSlots[slot].mGraphicBuffer != NULL) { + ++currentBufferCount; + } else { + if (mSlots[slot].mBufferState != BufferSlot::FREE) { + BQ_LOGE("allocateBuffers: slot %d without buffer is not FREE", + slot); + continue; + } + + freeSlots.push_front(slot); + } + } + + int maxBufferCount = mCore->getMaxBufferCountLocked(async); + BQ_LOGV("allocateBuffers: allocating from %d buffers up to %d buffers", + currentBufferCount, maxBufferCount); + for (; currentBufferCount < maxBufferCount; ++currentBufferCount) { + if (freeSlots.empty()) { + BQ_LOGE("allocateBuffers: ran out of free slots"); + return; + } + + width = width > 0 ? width : mCore->mDefaultWidth; + height = height > 0 ? height : mCore->mDefaultHeight; + format = format != 0 ? format : mCore->mDefaultBufferFormat; + usage |= mCore->mConsumerUsageBits; + + status_t result = NO_ERROR; + sp graphicBuffer(mCore->mAllocator->createGraphicBuffer( + width, height, format, usage, &result)); + if (result != NO_ERROR) { + BQ_LOGE("allocateBuffers: failed to allocate buffer (%u x %u, format" + " %u, usage %u)", width, height, format, usage); + return; + } + + int slot = freeSlots[freeSlots.size() - 1]; + mCore->freeBufferLocked(slot); // Clean up the slot first + mSlots[slot].mGraphicBuffer = graphicBuffer; + mSlots[slot].mFrameNumber = 0; + mSlots[slot].mFence = Fence::NO_FENCE; + BQ_LOGV("allocateBuffers: allocated a new buffer in slot %d", slot); + freeSlots.pop(); + } +} + void BufferQueueProducer::binderDied(const wp& /* who */) { // If we're here, it means that a producer we were connected to died. // We're guaranteed that we are still connected to it because we remove diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp index aa6acb9f47..8d9a800327 100644 --- a/libs/gui/IGraphicBufferProducer.cpp +++ b/libs/gui/IGraphicBufferProducer.cpp @@ -45,6 +45,7 @@ enum { CONNECT, DISCONNECT, SET_SIDEBAND_STREAM, + ALLOCATE_BUFFERS, }; class BpGraphicBufferProducer : public BpInterface @@ -252,6 +253,21 @@ public: } return result; } + + virtual void allocateBuffers(bool async, uint32_t width, uint32_t height, + uint32_t format, uint32_t usage) { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); + data.writeInt32(static_cast(async)); + data.writeInt32(static_cast(width)); + data.writeInt32(static_cast(height)); + data.writeInt32(static_cast(format)); + data.writeInt32(static_cast(usage)); + status_t result = remote()->transact(ALLOCATE_BUFFERS, data, &reply); + if (result != NO_ERROR) { + ALOGE("allocateBuffers failed to transact: %d", result); + } + } }; IMPLEMENT_META_INTERFACE(GraphicBufferProducer, "android.gui.IGraphicBufferProducer"); @@ -394,6 +410,15 @@ status_t BnGraphicBufferProducer::onTransact( reply->writeInt32(result); return NO_ERROR; } break; + case ALLOCATE_BUFFERS: + CHECK_INTERFACE(IGraphicBufferProducer, data, reply); + bool async = static_cast(data.readInt32()); + uint32_t width = static_cast(data.readInt32()); + uint32_t height = static_cast(data.readInt32()); + uint32_t format = static_cast(data.readInt32()); + uint32_t usage = static_cast(data.readInt32()); + allocateBuffers(async, width, height, format, usage); + return NO_ERROR; } return BBinder::onTransact(code, data, reply, flags); } diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index 88c45b2d1e..8cb9189f10 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -91,6 +91,13 @@ void Surface::setSidebandStream(const sp& stream) { mGraphicBufferProducer->setSidebandStream(stream); } +void Surface::allocateBuffers() { + uint32_t reqWidth = mReqWidth ? mReqWidth : mUserWidth; + uint32_t reqHeight = mReqHeight ? mReqHeight : mUserHeight; + mGraphicBufferProducer->allocateBuffers(mSwapIntervalZero, mReqWidth, + mReqHeight, mReqFormat, mReqUsage); +} + int Surface::hook_setSwapInterval(ANativeWindow* window, int interval) { Surface* c = getSelf(window); return c->setSwapInterval(interval); diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp index c415560321..3442c652cf 100644 --- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp +++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp @@ -486,6 +486,12 @@ status_t VirtualDisplaySurface::setSidebandStream(const sp& /*stre return INVALID_OPERATION; } +void VirtualDisplaySurface::allocateBuffers(bool /* async */, + uint32_t /* width */, uint32_t /* height */, uint32_t /* format */, + uint32_t /* usage */) { + // TODO: Should we actually allocate buffers for a virtual display? +} + void VirtualDisplaySurface::updateQueueBufferOutput( const QueueBufferOutput& qbo) { uint32_t w, h, transformHint, numPendingBuffers; diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h index 0ae9804ff9..5c00ab4585 100644 --- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h +++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h @@ -112,6 +112,8 @@ private: int api, bool producerControlledByApp, QueueBufferOutput* output); virtual status_t disconnect(int api); virtual status_t setSidebandStream(const sp& stream); + virtual void allocateBuffers(bool async, uint32_t width, uint32_t height, + uint32_t format, uint32_t usage); // // Utility methods diff --git a/services/surfaceflinger/MonitoredProducer.cpp b/services/surfaceflinger/MonitoredProducer.cpp index d0e81bc2ef..8739682ef5 100644 --- a/services/surfaceflinger/MonitoredProducer.cpp +++ b/services/surfaceflinger/MonitoredProducer.cpp @@ -105,6 +105,11 @@ status_t MonitoredProducer::setSidebandStream(const sp& stream) { return mProducer->setSidebandStream(stream); } +void MonitoredProducer::allocateBuffers(bool async, uint32_t width, + uint32_t height, uint32_t format, uint32_t usage) { + mProducer->allocateBuffers(async, width, height, format, usage); +} + IBinder* MonitoredProducer::onAsBinder() { return mProducer->asBinder().get(); } diff --git a/services/surfaceflinger/MonitoredProducer.h b/services/surfaceflinger/MonitoredProducer.h index f034e39efb..f6ccc51cbb 100644 --- a/services/surfaceflinger/MonitoredProducer.h +++ b/services/surfaceflinger/MonitoredProducer.h @@ -51,6 +51,8 @@ public: bool producerControlledByApp, QueueBufferOutput* output); virtual status_t disconnect(int api); virtual status_t setSidebandStream(const sp& stream); + virtual void allocateBuffers(bool async, uint32_t width, uint32_t height, + uint32_t format, uint32_t usage); virtual IBinder* onAsBinder(); private: -- 2.11.0