summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Jim Shargo <jshargo@google.com> 2024-10-02 19:31:53 +0000
committer Jim Shargo <jshargo@google.com> 2024-12-12 23:35:54 +0000
commit2e614a493cbfbc34430993ee158378fba235a3b2 (patch)
tree570b42003e2a86a156ee1907b673dd91cf4d8374
parent8c6afcf151af438342729f2399c43560ae1f353c (diff)
libgui: Add support for unlimited slot BufferQueues
BufferQueues can now be of unlimited size, according to the wishes of the producer. We add four new methods: - IGBC::allowUnlimitedSlots, which permits the IGBP to call extendSlotCount - IGBP::extendSlotCount, which increases the total available slot count to a fixed number and notifies the consumer via ICL::onSlotCountChanged - ICL::onSlotCountChanged, which notifies the consumer to resize its personal slot vector - IGBC::getReleasedBuffersExtented, which is like getReleasedBuffers but with an arbitrary sized bitvector instead of a fixed 64 bit vector The internal representation of the slots in BufferQueueCore is now a vector instead of an array, and can grow (but not shrink). The only consumers of these new APIs are intented to be Surface and ConsumerBase. Everything else is being migrated away from IGBP/IGBC anyway. This is part of go/warren-buffers. Bug: 341359814 Flag: com.android.graphics.libgui.flags.wb_unlimited_slots Test: new tests, old tests Change-Id: I0df872b9d6f9273854cc07a88d29b65451e1832a
-rw-r--r--libs/gui/BufferQueue.cpp9
-rw-r--r--libs/gui/BufferQueueConsumer.cpp104
-rw-r--r--libs/gui/BufferQueueCore.cpp39
-rw-r--r--libs/gui/BufferQueueProducer.cpp76
-rw-r--r--libs/gui/ConsumerBase.cpp11
-rw-r--r--libs/gui/IConsumerListener.cpp18
-rw-r--r--libs/gui/IGraphicBufferConsumer.cpp34
-rw-r--r--libs/gui/IGraphicBufferProducer.cpp32
-rw-r--r--libs/gui/include/gui/BufferQueue.h3
-rw-r--r--libs/gui/include/gui/BufferQueueConsumer.h15
-rw-r--r--libs/gui/include/gui/BufferQueueCore.h23
-rw-r--r--libs/gui/include/gui/BufferQueueDefs.h7
-rw-r--r--libs/gui/include/gui/BufferQueueProducer.h5
-rw-r--r--libs/gui/include/gui/ConsumerBase.h8
-rw-r--r--libs/gui/include/gui/IConsumerListener.h10
-rw-r--r--libs/gui/include/gui/IGraphicBufferConsumer.h40
-rw-r--r--libs/gui/include/gui/IGraphicBufferProducer.h45
-rw-r--r--libs/gui/tests/Android.bp1
-rw-r--r--libs/gui/tests/BufferQueue_test.cpp217
19 files changed, 653 insertions, 44 deletions
diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp
index b0f6e69115..f1374e23fd 100644
--- a/libs/gui/BufferQueue.cpp
+++ b/libs/gui/BufferQueue.cpp
@@ -108,6 +108,15 @@ void BufferQueue::ProxyConsumerListener::onSetFrameRate(float frameRate, int8_t
}
#endif
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+void BufferQueue::ProxyConsumerListener::onSlotCountChanged(int slotCount) {
+ sp<ConsumerListener> listener(mConsumerListener.promote());
+ if (listener != nullptr) {
+ listener->onSlotCountChanged(slotCount);
+ }
+}
+#endif
+
void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
sp<IGraphicBufferConsumer>* outConsumer,
bool consumerIsSurfaceFlinger) {
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index 9855b5bca4..f0125868ae 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -341,9 +341,9 @@ status_t BufferQueueConsumer::detachBuffer(int slot) {
return BAD_VALUE;
}
- if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
- BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)",
- slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
+ const int totalSlotCount = mCore->getTotalSlotCountLocked();
+ if (slot < 0 || slot >= totalSlotCount) {
+ BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)", slot, totalSlotCount);
return BAD_VALUE;
} else if (!mSlots[slot].mBufferState.isAcquired()) {
BQ_LOGE("detachBuffer: slot %d is not owned by the consumer "
@@ -483,10 +483,13 @@ status_t BufferQueueConsumer::releaseBuffer(int slot, uint64_t frameNumber,
ATRACE_CALL();
ATRACE_BUFFER_INDEX(slot);
- if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS ||
- releaseFence == nullptr) {
- BQ_LOGE("releaseBuffer: slot %d out of range or fence %p NULL", slot,
- releaseFence.get());
+ const int totalSlotCount = mCore->getTotalSlotCountLocked();
+ if (slot < 0 || slot >= totalSlotCount) {
+ BQ_LOGE("releaseBuffer: slot index %d out of range [0, %d)", slot, totalSlotCount);
+ return BAD_VALUE;
+ }
+ if (releaseFence == nullptr) {
+ BQ_LOGE("releaseBuffer: slot %d fence %p NULL", slot, releaseFence.get());
return BAD_VALUE;
}
@@ -515,6 +518,13 @@ status_t BufferQueueConsumer::releaseBuffer(int slot, uint64_t frameNumber,
{ // Autolock scope
std::lock_guard<std::mutex> lock(mCore->mMutex);
+ const int totalSlotCount = mCore->getTotalSlotCountLocked();
+ if (slot < 0 || slot >= totalSlotCount || releaseFence == nullptr) {
+ BQ_LOGE("releaseBuffer: slot %d out of range [0, %d) or fence %p NULL", slot,
+ totalSlotCount, releaseFence.get());
+ return BAD_VALUE;
+ }
+
// If the frame number has changed because the buffer has been reallocated,
// we can ignore this releaseBuffer for the old buffer.
// Ignore this for the shared buffer where the frame number can easily
@@ -661,6 +671,43 @@ status_t BufferQueueConsumer::getReleasedBuffers(uint64_t *outSlotMask) {
return NO_ERROR;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+status_t BufferQueueConsumer::getReleasedBuffersExtended(std::vector<bool>* outSlotMask) {
+ ATRACE_CALL();
+
+ if (outSlotMask == nullptr) {
+ BQ_LOGE("getReleasedBuffersExtended: outSlotMask may not be NULL");
+ return BAD_VALUE;
+ }
+
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
+
+ if (mCore->mIsAbandoned) {
+ BQ_LOGE("getReleasedBuffersExtended: BufferQueue has been abandoned");
+ return NO_INIT;
+ }
+
+ const int totalSlotCount = mCore->getTotalSlotCountLocked();
+ outSlotMask->resize(totalSlotCount);
+ for (int s = 0; s < totalSlotCount; ++s) {
+ (*outSlotMask)[s] = !mSlots[s].mAcquireCalled;
+ }
+
+ // Remove from the mask queued buffers for which acquire has been called,
+ // since the consumer will not receive their buffer addresses and so must
+ // retain their cached information
+ BufferQueueCore::Fifo::iterator current(mCore->mQueue.begin());
+ while (current != mCore->mQueue.end()) {
+ if (current->mAcquireCalled) {
+ (*outSlotMask)[current->mSlot] = false;
+ }
+ ++current;
+ }
+
+ return NO_ERROR;
+}
+#endif
+
status_t BufferQueueConsumer::setDefaultBufferSize(uint32_t width,
uint32_t height) {
ATRACE_CALL();
@@ -679,6 +726,28 @@ status_t BufferQueueConsumer::setDefaultBufferSize(uint32_t width,
return NO_ERROR;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+status_t BufferQueueConsumer::allowUnlimitedSlots(bool allowUnlimitedSlots) {
+ ATRACE_CALL();
+ BQ_LOGV("allowUnlimitedSlots: %d", allowUnlimitedSlots);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
+
+ if (mCore->mIsAbandoned) {
+ BQ_LOGE("allowUnlimitedSlots: BufferQueue has been abandoned");
+ return NO_INIT;
+ }
+
+ if (mCore->mConnectedApi != BufferQueueCore::NO_CONNECTED_API) {
+ BQ_LOGE("allowUnlimitedSlots: BufferQueue already connected");
+ return INVALID_OPERATION;
+ }
+
+ mCore->mAllowExtendedSlotCount = allowUnlimitedSlots;
+
+ return OK;
+}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+
status_t BufferQueueConsumer::setMaxBufferCount(int bufferCount) {
ATRACE_CALL();
@@ -718,16 +787,23 @@ status_t BufferQueueConsumer::setMaxAcquiredBufferCount(
int maxAcquiredBuffers) {
ATRACE_FORMAT("%s(%d)", __func__, maxAcquiredBuffers);
- if (maxAcquiredBuffers < 1 ||
- maxAcquiredBuffers > BufferQueueCore::MAX_MAX_ACQUIRED_BUFFERS) {
- BQ_LOGE("setMaxAcquiredBufferCount: invalid count %d",
- maxAcquiredBuffers);
- return BAD_VALUE;
- }
-
sp<IConsumerListener> listener;
{ // Autolock scope
std::unique_lock<std::mutex> lock(mCore->mMutex);
+
+ // We reserve two slots in order to guarantee that the producer and
+ // consumer can run asynchronously.
+ int maxMaxAcquiredBuffers =
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ mCore->getTotalSlotCountLocked() - 2;
+#else
+ BufferQueueCore::MAX_MAX_ACQUIRED_BUFFERS;
+#endif
+ if (maxAcquiredBuffers < 1 || maxAcquiredBuffers > maxMaxAcquiredBuffers) {
+ BQ_LOGE("setMaxAcquiredBufferCount: invalid count %d", maxAcquiredBuffers);
+ return BAD_VALUE;
+ }
+
mCore->waitWhileAllocatingLocked(lock);
if (mCore->mIsAbandoned) {
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index 5a093995b4..6c79904475 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -38,6 +38,8 @@
#include <system/window.h>
+#include <ui/BufferQueueDefs.h>
+
namespace android {
// Macros for include BufferQueueCore information in log messages
@@ -97,7 +99,11 @@ BufferQueueCore::BufferQueueCore()
mConnectedProducerListener(),
mBufferReleasedCbEnabled(false),
mBufferAttachedCbEnabled(false),
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ mSlots(BufferQueueDefs::NUM_BUFFER_SLOTS),
+#else
mSlots(),
+#endif
mQueue(),
mFreeSlots(),
mFreeBuffers(),
@@ -111,6 +117,9 @@ BufferQueueCore::BufferQueueCore()
mDefaultWidth(1),
mDefaultHeight(1),
mDefaultBufferDataSpace(HAL_DATASPACE_UNKNOWN),
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ mAllowExtendedSlotCount(false),
+#endif
mMaxBufferCount(BufferQueueDefs::NUM_BUFFER_SLOTS),
mMaxAcquiredBufferCount(1),
mMaxDequeuedBufferCount(1),
@@ -221,6 +230,14 @@ void BufferQueueCore::dumpState(const String8& prefix, String8* outResult) const
}
}
+int BufferQueueCore::getTotalSlotCountLocked() const {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ return mAllowExtendedSlotCount ? mMaxBufferCount : BufferQueueDefs::NUM_BUFFER_SLOTS;
+#else
+ return BufferQueueDefs::NUM_BUFFER_SLOTS;
+#endif
+}
+
int BufferQueueCore::getMinUndequeuedBufferCountLocked() const {
// If dequeueBuffer is allowed to error out, we don't have to add an
// extra buffer.
@@ -253,6 +270,26 @@ int BufferQueueCore::getMaxBufferCountLocked() const {
return maxBufferCount;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+status_t BufferQueueCore::extendSlotCountLocked(int size) {
+ int previousSize = (int)mSlots.size();
+ if (previousSize > size) {
+ return BAD_VALUE;
+ }
+ if (previousSize == size) {
+ return NO_ERROR;
+ }
+
+ mSlots.resize(size);
+ for (int i = previousSize; i < size; i++) {
+ mUnusedSlots.push_back(i);
+ }
+
+ mMaxBufferCount = size;
+ return NO_ERROR;
+}
+#endif
+
void BufferQueueCore::clearBufferSlotLocked(int slot) {
BQ_LOGV("clearBufferSlotLocked: slot %d", slot);
@@ -383,7 +420,7 @@ void BufferQueueCore::notifyBufferReleased() const {
void BufferQueueCore::validateConsistencyLocked() const {
static const useconds_t PAUSE_TIME = 0;
int allocatedSlots = 0;
- for (int slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) {
+ for (int slot = 0; slot < getTotalSlotCountLocked(); ++slot) {
bool isInFreeSlots = mFreeSlots.count(slot) != 0;
bool isInFreeBuffers =
std::find(mFreeBuffers.cbegin(), mFreeBuffers.cend(), slot) !=
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 2e7cef0847..c241482827 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -40,6 +40,7 @@
#include <gui/TraceUtils.h>
#include <private/gui/BufferQueueThreadState.h>
+#include <utils/Errors.h>
#include <utils/Log.h>
#include <utils/Trace.h>
@@ -108,9 +109,9 @@ status_t BufferQueueProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
return NO_INIT;
}
- if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
- BQ_LOGE("requestBuffer: slot index %d out of range [0, %d)",
- slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
+ int maxSlot = mCore->getTotalSlotCountLocked();
+ if (slot < 0 || slot >= maxSlot) {
+ BQ_LOGE("requestBuffer: slot index %d out of range [0, %d)", slot, maxSlot);
return BAD_VALUE;
} else if (!mSlots[slot].mBufferState.isDequeued()) {
BQ_LOGE("requestBuffer: slot %d is not owned by the producer "
@@ -123,6 +124,49 @@ status_t BufferQueueProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
return NO_ERROR;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+status_t BufferQueueProducer::extendSlotCount(int size) {
+ ATRACE_CALL();
+
+ sp<IConsumerListener> listener;
+ {
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
+ BQ_LOGV("extendSlotCount: size %d", size);
+
+ if (mCore->mIsAbandoned) {
+ BQ_LOGE("extendSlotCount: BufferQueue has been abandoned");
+ return NO_INIT;
+ }
+
+ if (!mCore->mAllowExtendedSlotCount) {
+ BQ_LOGE("extendSlotCount: Consumer did not allow unlimited slots");
+ return INVALID_OPERATION;
+ }
+
+ int maxBeforeExtension = mCore->mMaxBufferCount;
+
+ if (size == maxBeforeExtension) {
+ return NO_ERROR;
+ }
+
+ if (size < maxBeforeExtension) {
+ return BAD_VALUE;
+ }
+
+ if (status_t ret = mCore->extendSlotCountLocked(size); ret != OK) {
+ return ret;
+ }
+ listener = mCore->mConsumerListener;
+ }
+
+ if (listener) {
+ listener->onSlotCountChanged(size);
+ }
+
+ return NO_ERROR;
+}
+#endif
+
status_t BufferQueueProducer::setMaxDequeuedBufferCount(
int maxDequeuedBuffers) {
int maxBufferCount;
@@ -170,9 +214,10 @@ status_t BufferQueueProducer::setMaxDequeuedBufferCount(int maxDequeuedBuffers,
int bufferCount = mCore->getMinUndequeuedBufferCountLocked();
bufferCount += maxDequeuedBuffers;
- if (bufferCount > BufferQueueDefs::NUM_BUFFER_SLOTS) {
+ if (bufferCount > mCore->getTotalSlotCountLocked()) {
BQ_LOGE("setMaxDequeuedBufferCount: bufferCount %d too large "
- "(max %d)", bufferCount, BufferQueueDefs::NUM_BUFFER_SLOTS);
+ "(max %d)",
+ bufferCount, mCore->getTotalSlotCountLocked());
return BAD_VALUE;
}
@@ -756,9 +801,9 @@ status_t BufferQueueProducer::detachBuffer(int slot) {
return BAD_VALUE;
}
- if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
- BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)",
- slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
+ const int totalSlotCount = mCore->getTotalSlotCountLocked();
+ if (slot < 0 || slot >= totalSlotCount) {
+ BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)", slot, totalSlotCount);
return BAD_VALUE;
} else if (!mSlots[slot].mBufferState.isDequeued()) {
// TODO(http://b/140581935): This message is BQ_LOGW because it
@@ -993,9 +1038,9 @@ status_t BufferQueueProducer::queueBuffer(int slot,
return NO_INIT;
}
- if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
- BQ_LOGE("queueBuffer: slot index %d out of range [0, %d)",
- slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
+ const int totalSlotCount = mCore->getTotalSlotCountLocked();
+ if (slot < 0 || slot >= totalSlotCount) {
+ BQ_LOGE("queueBuffer: slot index %d out of range [0, %d)", slot, totalSlotCount);
return BAD_VALUE;
} else if (!mSlots[slot].mBufferState.isDequeued()) {
BQ_LOGE("queueBuffer: slot %d is not owned by the producer "
@@ -1239,9 +1284,9 @@ status_t BufferQueueProducer::cancelBuffer(int slot, const sp<Fence>& fence) {
return BAD_VALUE;
}
- if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
- BQ_LOGE("cancelBuffer: slot index %d out of range [0, %d)", slot,
- BufferQueueDefs::NUM_BUFFER_SLOTS);
+ const int totalSlotCount = mCore->getTotalSlotCountLocked();
+ if (slot < 0 || slot >= totalSlotCount) {
+ BQ_LOGE("cancelBuffer: slot index %d out of range [0, %d)", slot, totalSlotCount);
return BAD_VALUE;
} else if (!mSlots[slot].mBufferState.isDequeued()) {
BQ_LOGE("cancelBuffer: slot %d is not owned by the producer "
@@ -1409,6 +1454,9 @@ status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener,
output->nextFrameNumber = mCore->mFrameCounter + 1;
output->bufferReplaced = false;
output->maxBufferCount = mCore->mMaxBufferCount;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ output->isSlotExpansionAllowed = mCore->mAllowExtendedSlotCount;
+#endif
if (listener != nullptr) {
// Set up a death notification so that we can disconnect
diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp
index 602bba8dab..e772f44a4f 100644
--- a/libs/gui/ConsumerBase.cpp
+++ b/libs/gui/ConsumerBase.cpp
@@ -254,6 +254,17 @@ void ConsumerBase::onBuffersReleased() {
void ConsumerBase::onSidebandStreamChanged() {
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+void ConsumerBase::onSlotCountChanged(int slotCount) {
+ CB_LOGV("onSlotCountChanged: %d", slotCount);
+ Mutex::Autolock lock(mMutex);
+
+ if (slotCount > (int)mSlots.size()) {
+ mSlots.resize(slotCount);
+ }
+}
+#endif
+
void ConsumerBase::abandon() {
CB_LOGV("abandon");
Mutex::Autolock lock(mMutex);
diff --git a/libs/gui/IConsumerListener.cpp b/libs/gui/IConsumerListener.cpp
index f3bd90cffb..939db594ec 100644
--- a/libs/gui/IConsumerListener.cpp
+++ b/libs/gui/IConsumerListener.cpp
@@ -31,7 +31,8 @@ enum class Tag : uint32_t {
ON_FRAME_DEQUEUED,
ON_FRAME_CANCELLED,
ON_FRAME_DETACHED,
- LAST = ON_FRAME_DETACHED,
+ ON_SLOT_COUNT_CHANGED,
+ LAST = ON_SLOT_COUNT_CHANGED,
};
} // Anonymous namespace
@@ -85,6 +86,14 @@ public:
FrameEventHistoryDelta* /*outDelta*/) override {
LOG_ALWAYS_FATAL("IConsumerListener::addAndGetFrameTimestamps cannot be proxied");
}
+
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ void onSlotCountChanged(int slotCount) override {
+ callRemoteAsync<
+ decltype(&IConsumerListener::onSlotCountChanged)>(Tag::ON_SLOT_COUNT_CHANGED,
+ slotCount);
+ }
+#endif
};
// Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see
@@ -116,6 +125,13 @@ status_t BnConsumerListener::onTransact(uint32_t code, const Parcel& data, Parce
return callLocalAsync(data, reply, &IConsumerListener::onFrameCancelled);
case Tag::ON_FRAME_DETACHED:
return callLocalAsync(data, reply, &IConsumerListener::onFrameDetached);
+ case Tag::ON_SLOT_COUNT_CHANGED: {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ return callLocalAsync(data, reply, &IConsumerListener::onSlotCountChanged);
+#else
+ return INVALID_OPERATION;
+#endif
+ }
}
}
diff --git a/libs/gui/IGraphicBufferConsumer.cpp b/libs/gui/IGraphicBufferConsumer.cpp
index 282957b940..c1b65689d6 100644
--- a/libs/gui/IGraphicBufferConsumer.cpp
+++ b/libs/gui/IGraphicBufferConsumer.cpp
@@ -16,6 +16,7 @@
#include <gui/IGraphicBufferConsumer.h>
+#include <com_android_graphics_libgui_flags.h>
#include <gui/BufferItem.h>
#include <gui/IConsumerListener.h>
@@ -24,6 +25,7 @@
#include <ui/Fence.h>
#include <ui/GraphicBuffer.h>
+#include <utils/Errors.h>
#include <utils/NativeHandle.h>
#include <utils/String8.h>
#include <cstdint>
@@ -53,7 +55,9 @@ enum class Tag : uint32_t {
GET_OCCUPANCY_HISTORY,
DISCARD_FREE_BUFFERS,
DUMP_STATE,
- LAST = DUMP_STATE,
+ ALLOW_UNLIMITED_SLOTS,
+ GET_RELEASED_BUFFERS_EXTENDED,
+ LAST = GET_RELEASED_BUFFERS_EXTENDED,
};
} // Anonymous namespace
@@ -104,11 +108,25 @@ public:
return callRemote<Signature>(Tag::GET_RELEASED_BUFFERS, slotMask);
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ status_t getReleasedBuffersExtended(std::vector<bool>* slotMask) override {
+ using Signature = decltype(&IGraphicBufferConsumer::getReleasedBuffersExtended);
+ return callRemote<Signature>(Tag::GET_RELEASED_BUFFERS_EXTENDED, slotMask);
+ }
+#endif
+
status_t setDefaultBufferSize(uint32_t width, uint32_t height) override {
using Signature = decltype(&IGraphicBufferConsumer::setDefaultBufferSize);
return callRemote<Signature>(Tag::SET_DEFAULT_BUFFER_SIZE, width, height);
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ status_t allowUnlimitedSlots(bool allowUnlimitedSlots) override {
+ using Signature = decltype(&IGraphicBufferConsumer::allowUnlimitedSlots);
+ return callRemote<Signature>(Tag::ALLOW_UNLIMITED_SLOTS, allowUnlimitedSlots);
+ }
+#endif
+
status_t setMaxBufferCount(int bufferCount) override {
using Signature = decltype(&IGraphicBufferConsumer::setMaxBufferCount);
return callRemote<Signature>(Tag::SET_MAX_BUFFER_COUNT, bufferCount);
@@ -228,6 +246,20 @@ status_t BnGraphicBufferConsumer::onTransact(uint32_t code, const Parcel& data,
using Signature = status_t (IGraphicBufferConsumer::*)(const String8&, String8*) const;
return callLocal<Signature>(data, reply, &IGraphicBufferConsumer::dumpState);
}
+ case Tag::GET_RELEASED_BUFFERS_EXTENDED: {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ return callLocal(data, reply, &IGraphicBufferConsumer::getReleasedBuffersExtended);
+#else
+ return INVALID_OPERATION;
+#endif
+ }
+ case Tag::ALLOW_UNLIMITED_SLOTS: {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ return callLocal(data, reply, &IGraphicBufferConsumer::allowUnlimitedSlots);
+#else
+ return INVALID_OPERATION;
+#endif
+ }
}
}
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index 09144806ee..9f71eb16e7 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -81,6 +81,7 @@ enum {
GET_LAST_QUEUED_BUFFER2,
SET_FRAME_RATE,
SET_ADDITIONAL_OPTIONS,
+ SET_MAX_BUFER_COUNT_EXTENDED,
};
class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer>
@@ -149,6 +150,20 @@ public:
return result;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ status_t extendSlotCount(int size) override {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+ data.writeInt32(size);
+ status_t result = remote()->transact(SET_MAX_BUFER_COUNT_EXTENDED, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.readInt32();
+ return result;
+ }
+#endif
+
virtual status_t setAsyncMode(bool async) {
Parcel data, reply;
data.writeInterfaceToken(
@@ -981,6 +996,14 @@ IMPLEMENT_HYBRID_META_INTERFACE(GraphicBufferProducer,
// ----------------------------------------------------------------------
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+status_t IGraphicBufferProducer::extendSlotCount(int size) {
+ // No-op for IGBP other than BufferQueue.
+ (void)size;
+ return INVALID_OPERATION;
+}
+#endif
+
status_t IGraphicBufferProducer::setLegacyBufferDrop(bool drop) {
// No-op for IGBP other than BufferQueue.
(void) drop;
@@ -1582,6 +1605,15 @@ status_t BnGraphicBufferProducer::onTransact(
return NO_ERROR;
}
#endif
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ case SET_MAX_BUFER_COUNT_EXTENDED: {
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+ int size = data.readInt32();
+ status_t result = extendSlotCount(size);
+ reply->writeInt32(result);
+ return NO_ERROR;
+ }
+#endif
}
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/gui/include/gui/BufferQueue.h b/libs/gui/include/gui/BufferQueue.h
index 0948c4d076..f1c75d3a45 100644
--- a/libs/gui/include/gui/BufferQueue.h
+++ b/libs/gui/include/gui/BufferQueue.h
@@ -76,6 +76,9 @@ public:
void onSetFrameRate(float frameRate, int8_t compatibility,
int8_t changeFrameRateStrategy) override;
#endif
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ void onSlotCountChanged(int slotCount) override;
+#endif
private:
// mConsumerListener is a weak reference to the IConsumerListener. This is
// the raison d'etre of ProxyConsumerListener.
diff --git a/libs/gui/include/gui/BufferQueueConsumer.h b/libs/gui/include/gui/BufferQueueConsumer.h
index 6aa801ab86..e00c44eb70 100644
--- a/libs/gui/include/gui/BufferQueueConsumer.h
+++ b/libs/gui/include/gui/BufferQueueConsumer.h
@@ -96,11 +96,26 @@ public:
// This should be called from the onBuffersReleased() callback.
virtual status_t getReleasedBuffers(uint64_t* outSlotMask);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ // getReleasedBuffers sets the values pointed to by outSlotMask to the bits
+ // indicating which buffer slots have been released by the BufferQueue
+ // but have not yet been released by the consumer.
+ //
+ // This should be called from the onBuffersReleased() callback when
+ // allowUnlimitedSlots has been called.
+ virtual status_t getReleasedBuffersExtended(std::vector<bool>* outSlotMask) override;
+#endif
+
// setDefaultBufferSize is used to set the size of buffers returned by
// dequeueBuffer when a width and height of zero is requested. Default
// is 1x1.
virtual status_t setDefaultBufferSize(uint32_t width, uint32_t height);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ // see IGraphicBufferConsumer::allowUnlimitedSlots
+ virtual status_t allowUnlimitedSlots(bool allowUnlimitedSlots) override;
+#endif
+
// see IGraphicBufferConsumer::setMaxBufferCount
virtual status_t setMaxBufferCount(int bufferCount);
diff --git a/libs/gui/include/gui/BufferQueueCore.h b/libs/gui/include/gui/BufferQueueCore.h
index 77cdf2c9f3..7f92a46053 100644
--- a/libs/gui/include/gui/BufferQueueCore.h
+++ b/libs/gui/include/gui/BufferQueueCore.h
@@ -32,10 +32,11 @@
#include <utils/Trace.h>
#include <utils/Vector.h>
+#include <condition_variable>
#include <list>
-#include <set>
#include <mutex>
-#include <condition_variable>
+#include <set>
+#include <vector>
#define ATRACE_BUFFER_INDEX(index) \
do { \
@@ -91,6 +92,10 @@ private:
// Dump our state in a string
void dumpState(const String8& prefix, String8* outResult) const;
+ // getTotalSlotCountLocked returns the total number of slots in use by the
+ // buffer queue at this time.
+ int getTotalSlotCountLocked() const;
+
// getMinUndequeuedBufferCountLocked returns the minimum number of buffers
// that must remain in a state other than DEQUEUED. The async parameter
// tells whether we're in asynchronous mode.
@@ -120,6 +125,10 @@ private:
int getMaxBufferCountLocked(bool asyncMode,
bool dequeueBufferCannotBlock, int maxBufferCount) const;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ // This resizes mSlots to the given size, but only if it's increasing.
+ status_t extendSlotCountLocked(int size);
+#endif
// clearBufferSlotLocked frees the GraphicBuffer and sync resources for the
// given slot.
void clearBufferSlotLocked(int slot);
@@ -204,7 +213,7 @@ private:
// mConnectedProducerListener will not trigger onBufferAttached() callback.
bool mBufferAttachedCbEnabled;
- // mSlots is an array of buffer slots that must be mirrored on the producer
+ // mSlots is a collection of buffer slots that must be mirrored on the producer
// side. This allows buffer ownership to be transferred between the producer
// and consumer without sending a GraphicBuffer over Binder. The entire
// array is initialized to NULL at construction time, and buffers are
@@ -266,8 +275,14 @@ private:
// is specified.
android_dataspace mDefaultBufferDataSpace;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ // mAllowExtendedSlotCount is set by the consumer to permit the producer to
+ // request an unlimited number of slots.
+ bool mAllowExtendedSlotCount;
+#endif
+
// mMaxBufferCount is the limit on the number of buffers that will be
- // allocated at one time. This limit can be set by the consumer.
+ // allocated at one time.
int mMaxBufferCount;
// mMaxAcquiredBufferCount is the number of buffers that the consumer may
diff --git a/libs/gui/include/gui/BufferQueueDefs.h b/libs/gui/include/gui/BufferQueueDefs.h
index ffafb49615..42cf439450 100644
--- a/libs/gui/include/gui/BufferQueueDefs.h
+++ b/libs/gui/include/gui/BufferQueueDefs.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_GUI_BUFFERQUEUECOREDEFS_H
#define ANDROID_GUI_BUFFERQUEUECOREDEFS_H
+#include <com_android_graphics_libgui_flags.h>
#include <gui/BufferSlot.h>
#include <ui/BufferQueueDefs.h>
@@ -24,7 +25,11 @@ namespace android {
class BufferQueueCore;
namespace BufferQueueDefs {
- typedef BufferSlot SlotsType[NUM_BUFFER_SLOTS];
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ typedef std::vector<BufferSlot> SlotsType;
+#else
+ typedef BufferSlot SlotsType[NUM_BUFFER_SLOTS];
+#endif
} // namespace BufferQueueDefs
} // namespace android
diff --git a/libs/gui/include/gui/BufferQueueProducer.h b/libs/gui/include/gui/BufferQueueProducer.h
index 086ce7ce56..50abadb922 100644
--- a/libs/gui/include/gui/BufferQueueProducer.h
+++ b/libs/gui/include/gui/BufferQueueProducer.h
@@ -47,6 +47,11 @@ public:
// flags indicating that previously-returned buffers are no longer valid.
virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ // see IGraphicsBufferProducer::extendSlotCount
+ virtual status_t extendSlotCount(int size) override;
+#endif
+
// see IGraphicsBufferProducer::setMaxDequeuedBufferCount
virtual status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers);
diff --git a/libs/gui/include/gui/ConsumerBase.h b/libs/gui/include/gui/ConsumerBase.h
index e976aa48be..5cd19c1583 100644
--- a/libs/gui/include/gui/ConsumerBase.h
+++ b/libs/gui/include/gui/ConsumerBase.h
@@ -185,7 +185,9 @@ protected:
virtual void onFrameDetached(const uint64_t bufferId) override;
virtual void onBuffersReleased() override;
virtual void onSidebandStreamChanged() override;
-
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ virtual void onSlotCountChanged(int slotCount) override;
+#endif
virtual int getSlotForBufferLocked(const sp<GraphicBuffer>& buffer);
virtual status_t detachBufferLocked(int slotIndex);
@@ -284,7 +286,11 @@ protected:
// slot that has not yet been used. The buffer allocated to a slot will also
// be replaced if the requested buffer usage or geometry differs from that
// of the buffer allocated to a slot.
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ std::vector<Slot> mSlots;
+#else
Slot mSlots[BufferQueueDefs::NUM_BUFFER_SLOTS];
+#endif
// mAbandoned indicates that the BufferQueue will no longer be used to
// consume images buffers pushed to it using the IGraphicBufferProducer
diff --git a/libs/gui/include/gui/IConsumerListener.h b/libs/gui/include/gui/IConsumerListener.h
index 51d3959de7..1695aae876 100644
--- a/libs/gui/include/gui/IConsumerListener.h
+++ b/libs/gui/include/gui/IConsumerListener.h
@@ -98,6 +98,16 @@ public:
virtual void onSetFrameRate(float /*frameRate*/, int8_t /*compatibility*/,
int8_t /*changeFrameRateStrategy*/) {}
#endif
+
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ // Notifies the consumer that IGraphicBufferProducer::extendSlotCount has
+ // been called and the total slot count has increased.
+ //
+ // This will only ever be called if
+ // IGraphicBufferConsumer::allowUnlimitedSlots has been called on the
+ // consumer.
+ virtual void onSlotCountChanged(int /* slotCount */) {}
+#endif
};
#ifndef NO_BINDER
diff --git a/libs/gui/include/gui/IGraphicBufferConsumer.h b/libs/gui/include/gui/IGraphicBufferConsumer.h
index 18f5488173..56eb291335 100644
--- a/libs/gui/include/gui/IGraphicBufferConsumer.h
+++ b/libs/gui/include/gui/IGraphicBufferConsumer.h
@@ -16,6 +16,7 @@
#pragma once
+#include <com_android_graphics_libgui_flags.h>
#include <gui/OccupancyTracker.h>
#include <binder/IInterface.h>
@@ -35,6 +36,10 @@ class Fence;
class GraphicBuffer;
class IConsumerListener;
class NativeHandle;
+
+/*
+ * See IGraphicBufferProducer for details on SLOT_COUNT.
+ */
#ifndef NO_BINDER
class IGraphicBufferConsumer : public IInterface {
public:
@@ -92,7 +97,7 @@ public:
//
// Return of a value other than NO_ERROR means an error has occurred:
// * BAD_VALUE - the given slot number is invalid, either because it is out of the range
- // [0, NUM_BUFFER_SLOTS) or because the slot it refers to is not
+ // [0, SLOT_COUNT) or because the slot it refers to is not
// currently acquired.
virtual status_t detachBuffer(int slot) = 0;
@@ -173,6 +178,19 @@ public:
// * NO_INIT - the BufferQueue has been abandoned.
virtual status_t getReleasedBuffers(uint64_t* slotMask) = 0;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ // getReleasedBuffersExtended for each slot, sets slotMask[slot] to 1 if it
+ // corresponds to a released buffer slot. In particular, a released buffer
+ // is one that has been released by the BufferQueue but has not yet been
+ // released by the consumer.
+ //
+ // This should be called from the onBuffersReleased() callback.
+ //
+ // Return of a value other than NO_ERROR means an error has occurred:
+ // * NO_INIT - the BufferQueue has been abandoned.
+ virtual status_t getReleasedBuffersExtended(std::vector<bool>* slotMask) = 0;
+#endif
+
// setDefaultBufferSize is used to set the size of buffers returned by dequeueBuffer when a
// width and height of zero is requested. Default is 1x1.
//
@@ -180,6 +198,26 @@ public:
// * BAD_VALUE - either w or h was zero
virtual status_t setDefaultBufferSize(uint32_t w, uint32_t h) = 0;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ // allowUnlimitedSlots allows the producer to set the upper bound on slots.
+ //
+ // Must be called before the producer is connected. If the producer
+ // increases the slot count, an IConsumerListener::onSlotCountChanged
+ // update is sent.
+ //
+ // This can not be used with setMaxBufferCount. Calls after
+ // setMaxBufferCount will fail and calls to setMaxBufferCount after setting
+ // this to true will fail.
+ //
+ // Return of a value other than NO_ERROR means an error has occurred:
+ // * NO_INIT - the BufferQueue has been abandoned
+ // * INVALID_OPERATION - one of the following errors has occurred:
+ // * Producer has been connected
+ // * setMaxBufferCount has been called and shrunk the
+ // BufferQueue.
+ virtual status_t allowUnlimitedSlots(bool allowUnlimitedSlots) = 0;
+#endif
+
// setMaxBufferCount sets the maximum value for the number of buffers used in the BufferQueue
// (the initial default is NUM_BUFFER_SLOTS). If a call to setMaxAcquiredBufferCount (by the
// consumer), or a call to setAsyncMode or setMaxDequeuedBufferCount (by the producer), would
diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h
index a42ddc466c..7accca6298 100644
--- a/libs/gui/include/gui/IGraphicBufferProducer.h
+++ b/libs/gui/include/gui/IGraphicBufferProducer.h
@@ -72,6 +72,14 @@ using HGraphicBufferProducerV2_0 =
* dequeueBuffer() to get an empty buffer, fills it with data, then
* calls queueBuffer() to make it available to the consumer.
*
+ * BufferQueues have a size, which we'll refer to in other comments as
+ * SLOT_COUNT. Its default is 64 (NUM_BUFFER_SLOTS). It can be adjusted by
+ * the IGraphicBufferConsumer::setMaxBufferCount, or when
+ * IGraphicBufferConsumer::allowUnlimitedSlots is set to true, by
+ * IGraphicBufferProducer::extendSlotCount. The actual number of buffers in use
+ * is a function of various configurations, including whether we're in single
+ * buffer mode, the maximum dequeuable/aquirable buffers, and SLOT_COUNT.
+ *
* This class was previously called ISurfaceTexture.
*/
#ifndef NO_BINDER
@@ -106,7 +114,7 @@ public:
// slot->buffer mapping so that it's not necessary to transfer a
// GraphicBuffer for every dequeue operation.
//
- // The slot must be in the range of [0, NUM_BUFFER_SLOTS).
+ // The slot must be in the range of [0, SLOT_COUNT).
//
// Return of a value other than NO_ERROR means an error has occurred:
// * NO_INIT - the buffer queue has been abandoned or the producer is not
@@ -116,6 +124,30 @@ public:
// * buffer specified by the slot is not dequeued
virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf) = 0;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ // extendSlotCount sets the maximum slot count (SLOT_COUNT) to the given
+ // size. This feature must be enabled by the consumer to function via
+ // IGraphicBufferConsumer::allowUnlimitedSlots. This must be called before
+ // the producer connects.
+ //
+ // After calling this, any slot can be returned in the [0, size) range.
+ // Callers are responsible for the allocation of the appropriate slots
+ // array for their own buffer cache.
+ //
+ // On success, the consumer is notified (so that it can increase its own
+ // slot cache).
+ //
+ // Return of a value other than NO_ERROR means that an error has occurred:
+ // * NO_INIT - the buffer queue has been abandoned
+ // * INVALID_OPERATION - one of the following conditions has occurred:
+ // * The producer is connected already
+ // * The consumer didn't call allowUnlimitedSlots
+ // * BAD_VALUE - The value is smaller than the previous max size
+ // (initialized to 64, then whatever the last call to this
+ // was)
+ virtual status_t extendSlotCount(int size);
+#endif
+
// setMaxDequeuedBufferCount sets the maximum number of buffers that can be
// dequeued by the producer at one time. If this method succeeds, any new
// buffer slots will be both unallocated and owned by the BufferQueue object
@@ -129,7 +161,7 @@ public:
// will result in a BAD_VALUE error.
//
// The buffer count should be at least 1 (inclusive), but at most
- // (NUM_BUFFER_SLOTS - the minimum undequeued buffer count) (exclusive). The
+ // (SLOT_COUNT - the minimum undequeued buffer count) (exclusive). The
// minimum undequeued buffer count can be obtained by calling
// query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS).
//
@@ -239,8 +271,8 @@ public:
// * NO_INIT - the buffer queue has been abandoned or the producer is not
// connected.
// * BAD_VALUE - the given slot number is invalid, either because it is
- // out of the range [0, NUM_BUFFER_SLOTS), or because the slot
- // it refers to is not currently dequeued and requested.
+ // out of the range [0, SLOT_COUNT), or because the slot it
+ // refers to is not currently dequeued and requested.
virtual status_t detachBuffer(int slot) = 0;
// detachNextBuffer is equivalent to calling dequeueBuffer, requestBuffer,
@@ -415,6 +447,7 @@ public:
FrameEventHistoryDelta frameTimestamps;
bool bufferReplaced{false};
int maxBufferCount{BufferQueueDefs::NUM_BUFFER_SLOTS};
+ bool isSlotExpansionAllowed{false};
status_t result{NO_ERROR};
};
@@ -430,7 +463,7 @@ public:
// below). Any other properties (zero point, etc)
// are client-dependent, and should be documented by the client.
//
- // The slot must be in the range of [0, NUM_BUFFER_SLOTS).
+ // The slot must be in the range of [0, SLOT_COUNT).
//
// Upon success, the output will be filled with meaningful values
// (refer to the documentation below).
@@ -460,7 +493,7 @@ public:
//
// The buffer is not queued for use by the consumer.
//
- // The slot must be in the range of [0, NUM_BUFFER_SLOTS).
+ // The slot must be in the range of [0, SLOT_COUNT).
//
// The buffer will not be overwritten until the fence signals. The fence
// will usually be the one obtained from dequeueBuffer.
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
index f07747f32f..87051a7aac 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -55,6 +55,7 @@ cc_test {
"-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_BQ_EXTENDEDALLOCATE=true",
"-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_WB_CONSUMER_BASE_OWNS_BQ=true",
"-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_WB_PLATFORM_API_IMPROVEMENTS=true",
+ "-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_WB_UNLIMITED_SLOTS=true",
],
srcs: [
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index 16060990bd..6138306dbe 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -20,6 +20,8 @@
#include "Constants.h"
#include "MockConsumer.h"
+#include <EGL/egl.h>
+
#include <gui/BufferItem.h>
#include <gui/BufferItemConsumer.h>
#include <gui/BufferQueue.h>
@@ -44,7 +46,9 @@
#include <gtest/gtest.h>
#include <future>
+#include <optional>
#include <thread>
+#include <unordered_map>
#include <com_android_graphics_libgui_flags.h>
@@ -1612,4 +1616,217 @@ TEST_F(BufferQueueTest, PassesThroughPictureProfileHandle) {
}
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+struct MockUnlimitedSlotConsumer : public MockConsumer {
+ virtual void onSlotCountChanged(int size) override { mSize = size; }
+
+ std::optional<int> mSize;
+};
+
+TEST_F(BufferQueueTest, UnlimitedSlots_FailsWhenNotAllowed) {
+ createBufferQueue();
+
+ sp<MockUnlimitedSlotConsumer> mc = sp<MockUnlimitedSlotConsumer>::make();
+ EXPECT_EQ(OK, mConsumer->consumerConnect(mc, false));
+
+ EXPECT_EQ(INVALID_OPERATION, mProducer->extendSlotCount(64));
+ EXPECT_EQ(INVALID_OPERATION, mProducer->extendSlotCount(32));
+ EXPECT_EQ(INVALID_OPERATION, mProducer->extendSlotCount(128));
+
+ EXPECT_EQ(std::nullopt, mc->mSize);
+}
+
+TEST_F(BufferQueueTest, UnlimitedSlots_OnlyAllowedForExtensions) {
+ createBufferQueue();
+
+ sp<MockUnlimitedSlotConsumer> consumerListener = sp<MockUnlimitedSlotConsumer>::make();
+ EXPECT_EQ(OK, mConsumer->consumerConnect(consumerListener, false));
+ EXPECT_EQ(OK, mConsumer->allowUnlimitedSlots(true));
+
+ EXPECT_EQ(BAD_VALUE, mProducer->extendSlotCount(32));
+ EXPECT_EQ(OK, mProducer->extendSlotCount(64));
+ EXPECT_EQ(OK, mProducer->extendSlotCount(128));
+ EXPECT_EQ(128, *consumerListener->mSize);
+
+ EXPECT_EQ(OK, mProducer->extendSlotCount(128));
+ EXPECT_EQ(BAD_VALUE, mProducer->extendSlotCount(127));
+}
+
+class BufferQueueUnlimitedTest : public BufferQueueTest {
+protected:
+ static constexpr auto kMaxBufferCount = 128;
+ static constexpr auto kAcquirableBufferCount = 2;
+ static constexpr auto kDequeableBufferCount = kMaxBufferCount - kAcquirableBufferCount;
+
+ virtual void SetUp() override {
+ BufferQueueTest::SetUp();
+
+ createBufferQueue();
+ setUpConsumer();
+ setUpProducer();
+ }
+
+ void setUpConsumer() {
+ EXPECT_EQ(OK, mConsumer->consumerConnect(mConsumerListener, false));
+
+ EXPECT_EQ(OK, mConsumer->allowUnlimitedSlots(true));
+ EXPECT_EQ(OK, mConsumer->setConsumerUsageBits(GraphicBuffer::USAGE_SW_READ_OFTEN));
+ EXPECT_EQ(OK, mConsumer->setDefaultBufferSize(10, 10));
+ EXPECT_EQ(OK, mConsumer->setDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888));
+ EXPECT_EQ(OK, mConsumer->setMaxAcquiredBufferCount(kAcquirableBufferCount));
+ }
+
+ void setUpProducer() {
+ EXPECT_EQ(OK, mProducer->extendSlotCount(kMaxBufferCount));
+
+ IGraphicBufferProducer::QueueBufferOutput output;
+ EXPECT_EQ(OK,
+ mProducer->connect(mProducerListener, NATIVE_WINDOW_API_CPU,
+ /*producerControlledByApp*/ true, &output));
+
+ ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(kDequeableBufferCount));
+ ASSERT_EQ(OK, mProducer->allowAllocation(true));
+ }
+
+ std::unordered_map<int, sp<Fence>> dequeueAll() {
+ std::unordered_map<int, sp<Fence>> slotsToFences;
+
+ for (int i = 0; i < kDequeableBufferCount; ++i) {
+ int slot;
+ sp<Fence> fence;
+ sp<GraphicBuffer> buffer;
+
+ status_t ret =
+ mProducer->dequeueBuffer(&slot, &fence, /*w*/ 0, /*h*/ 0, /*format*/ 0,
+ /*uint64_t*/ 0,
+ /*outBufferAge*/ nullptr, /*outTimestamps*/ nullptr);
+ if (ret & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) {
+ EXPECT_EQ(OK, mProducer->requestBuffer(slot, &buffer))
+ << "Unable to request buffer for slot " << slot;
+ }
+ EXPECT_FALSE(slotsToFences.contains(slot));
+ slotsToFences.emplace(slot, fence);
+ }
+ EXPECT_EQ(kDequeableBufferCount, (int)slotsToFences.size());
+ return slotsToFences;
+ }
+
+ sp<MockUnlimitedSlotConsumer> mConsumerListener = sp<MockUnlimitedSlotConsumer>::make();
+ sp<StubProducerListener> mProducerListener = sp<StubProducerListener>::make();
+};
+
+TEST_F(BufferQueueUnlimitedTest, ExpandOverridesConsumerMaxBuffers) {
+ createBufferQueue();
+ setUpConsumer();
+ EXPECT_EQ(OK, mConsumer->setMaxBufferCount(10));
+
+ setUpProducer();
+
+ EXPECT_EQ(kDequeableBufferCount, (int)dequeueAll().size());
+}
+
+TEST_F(BufferQueueUnlimitedTest, CanDetachAll) {
+ auto slotsToFences = dequeueAll();
+ for (auto& [slot, fence] : slotsToFences) {
+ EXPECT_EQ(OK, mProducer->detachBuffer(slot));
+ }
+}
+
+TEST_F(BufferQueueUnlimitedTest, CanCancelAll) {
+ auto slotsToFences = dequeueAll();
+ for (auto& [slot, fence] : slotsToFences) {
+ EXPECT_EQ(OK, mProducer->cancelBuffer(slot, fence));
+ }
+}
+
+TEST_F(BufferQueueUnlimitedTest, CanAcquireAndReleaseAll) {
+ auto slotsToFences = dequeueAll();
+ for (auto& [slot, fence] : slotsToFences) {
+ IGraphicBufferProducer::QueueBufferInput input;
+ input.fence = fence;
+
+ IGraphicBufferProducer::QueueBufferOutput output;
+ EXPECT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+
+ BufferItem buffer;
+ EXPECT_EQ(OK, mConsumer->acquireBuffer(&buffer, 0));
+ EXPECT_EQ(OK,
+ mConsumer->releaseBuffer(buffer.mSlot, buffer.mFrameNumber, EGL_NO_DISPLAY,
+ EGL_NO_SYNC, buffer.mFence));
+ }
+}
+
+TEST_F(BufferQueueUnlimitedTest, CanAcquireAndDetachAll) {
+ auto slotsToFences = dequeueAll();
+ for (auto& [slot, fence] : slotsToFences) {
+ IGraphicBufferProducer::QueueBufferInput input;
+ input.fence = fence;
+
+ IGraphicBufferProducer::QueueBufferOutput output;
+ EXPECT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+
+ BufferItem buffer;
+ EXPECT_EQ(OK, mConsumer->acquireBuffer(&buffer, 0));
+ EXPECT_EQ(OK, mConsumer->detachBuffer(buffer.mSlot));
+ }
+}
+
+TEST_F(BufferQueueUnlimitedTest, GetReleasedBuffersExtended) {
+ // First, acquire and release all the buffers so the consumer "knows" about
+ // them
+ auto slotsToFences = dequeueAll();
+
+ std::vector<bool> releasedSlots;
+ EXPECT_EQ(OK, mConsumer->getReleasedBuffersExtended(&releasedSlots));
+ for (auto& [slot, _] : slotsToFences) {
+ EXPECT_TRUE(releasedSlots[slot])
+ << "Slots that haven't been acquired will show up as released.";
+ }
+ for (auto& [slot, fence] : slotsToFences) {
+ IGraphicBufferProducer::QueueBufferInput input;
+ input.fence = fence;
+
+ IGraphicBufferProducer::QueueBufferOutput output;
+ EXPECT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+
+ BufferItem buffer;
+ EXPECT_EQ(OK, mConsumer->acquireBuffer(&buffer, 0));
+ EXPECT_EQ(OK,
+ mConsumer->releaseBuffer(buffer.mSlot, buffer.mFrameNumber, EGL_NO_DISPLAY,
+ EGL_NO_SYNC_KHR, buffer.mFence));
+ }
+
+ EXPECT_EQ(OK, mConsumer->getReleasedBuffersExtended(&releasedSlots));
+ for (auto& [slot, _] : slotsToFences) {
+ EXPECT_FALSE(releasedSlots[slot])
+ << "Slots that have been acquired will show up as not released.";
+ }
+
+ // Then, alternatively cancel and detach (release) buffers. Only detached
+ // buffers should be returned by getReleasedBuffersExtended
+ slotsToFences = dequeueAll();
+ std::set<int> cancelledSlots;
+ std::set<int> detachedSlots;
+ bool cancel;
+ for (auto& [slot, fence] : slotsToFences) {
+ if (cancel) {
+ EXPECT_EQ(OK, mProducer->cancelBuffer(slot, fence));
+ cancelledSlots.insert(slot);
+ } else {
+ EXPECT_EQ(OK, mProducer->detachBuffer(slot));
+ detachedSlots.insert(slot);
+ }
+ cancel = !cancel;
+ }
+
+ EXPECT_EQ(OK, mConsumer->getReleasedBuffersExtended(&releasedSlots));
+ for (int slot : detachedSlots) {
+ EXPECT_TRUE(releasedSlots[slot]) << "Slots that are detached are released.";
+ }
+ for (int slot : cancelledSlots) {
+ EXPECT_FALSE(releasedSlots[slot])
+ << "Slots that are still held in the queue are not released.";
+ }
+}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
} // namespace android