C2VdaBqBlockPool: Remove spare buffer polling

Originally, C2VdaBqBlockPool polls one more spare buffer for
preventing bufferqueue returns INVALID_OPERATION when fetching blocks.
At ag/12268246, we treat INVALID_OPERATION as timed out. So we could
stop polling one more spare buffer.

Reference: go/codec2-vdabq-blockpool "Spare Buffer Mechanism" section

Bug: 146409777
Test: pass arc.VideoDecodeAccel.h264_vm
Change-Id: Id8a66edff1d118ce1ca4c1f89b53d0bfbfce6bb3
diff --git a/plugin_store/C2VdaBqBlockPool.cpp b/plugin_store/C2VdaBqBlockPool.cpp
index 170387e..b85377e 100644
--- a/plugin_store/C2VdaBqBlockPool.cpp
+++ b/plugin_store/C2VdaBqBlockPool.cpp
@@ -26,13 +26,8 @@
 
 // The wait time for acquire fence in milliseconds.
 constexpr int kFenceWaitTimeMs = 10;
-// The timeout delay range for dequeuing spare buffer delay time in microseconds.
-constexpr int kDequeueSpareMinDelayUs = 500;
-constexpr int kDequeueSpareMaxDelayUs = 16 * 1000;
 // The timeout limit of acquiring lock of timed_mutex in milliseconds.
 constexpr std::chrono::milliseconds kTimedMutexTimeoutMs = std::chrono::milliseconds(500);
-// The max retry times for fetchSpareBufferSlot timeout.
-constexpr int32_t kFetchSpareBufferMaxRetries = 10;
 
 }  // namespace
 
@@ -368,23 +363,6 @@
 private:
     friend struct C2VdaBqBlockPoolData;
 
-    // The exponential rate control calculator with factor of 2. Per increase() call will double the
-    // value until it reaches maximum. reset() will set value to the minimum.
-    class ExpRateControlCalculator {
-    public:
-        ExpRateControlCalculator(int min, int max) : kMinValue(min), kMaxValue(max), mValue(min) {}
-        ExpRateControlCalculator() = delete;
-
-        void reset() { mValue = kMinValue; }
-        void increase() { mValue = std::min(kMaxValue, mValue << 1); }
-        int value() const { return mValue; }
-
-    private:
-        const int kMinValue;
-        const int kMaxValue;
-        int mValue;
-    };
-
     // Requested buffer formats.
     struct BufferFormat {
         BufferFormat(uint32_t width, uint32_t height, uint32_t pixelFormat,
@@ -401,19 +379,12 @@
     // For C2VdaBqBlockPoolData to detach corresponding slot buffer from BufferQueue.
     void detachBuffer(uint64_t producerId, int32_t slotId);
 
-    // Fetches a spare slot index by dequeueing and requesting one extra buffer from producer. The
-    // spare buffer slot guarantees at least one buffer to be dequeued in producer, so as to prevent
-    // the invalid operation for producer of the attempt to dequeue buffers exceeded the maximal
-    // dequeued buffer count.
-    // This function should be called after the last requested buffer is fetched in
-    // fetchGraphicBlock(), or in the beginning of switchProducer(). Block pool should store the
-    // slot index into |mSpareSlot| and cancel the buffer immediately.
-    // The generation number and usage of the spare buffer will be recorded in |generation| and
-    // |usage|, which will be useful later in switchProducer().
-    c2_status_t fetchSpareBufferSlot(H2BGraphicBufferProducer* const producer, uint32_t width,
-                                     uint32_t height, uint32_t pixelFormat,
-                                     C2AndroidMemoryUsage androidUsage, uint32_t* generation,
-                                     uint64_t* usage);
+    // Queries the generation and usage flags from the given producer by dequeuing and requesting a
+    // buffer (the buffer is then detached and freed).
+    c2_status_t queryGenerationAndUsage(H2BGraphicBufferProducer* const producer, uint32_t width,
+                                        uint32_t height, uint32_t pixelFormat,
+                                        C2AndroidMemoryUsage androidUsage, uint32_t* generation,
+                                        uint64_t* usage);
 
     // Switches producer and transfers allocated buffers from old producer to the new one.
     bool switchProducer(H2BGraphicBufferProducer* const newProducer, uint64_t newProducerId);
@@ -442,14 +413,10 @@
     std::map<int32_t, std::shared_ptr<C2GraphicAllocation>> mSlotAllocations;
     // Number of buffers requested on requestNewBufferSet() call.
     size_t mBuffersRequested;
-    // The slot index of spare buffer.
-    int32_t mSpareSlot;
     // Currently requested buffer formats.
     BufferFormat mBufferFormat;
     // The map recorded the slot indices from old producer to new producer.
     std::map<int32_t, int32_t> mProducerChangeSlotMap;
-    // The rate control calculator for the delay of dequeueing spare buffer.
-    ExpRateControlCalculator mSpareDequeueDelayUs;
     // The counter for representing the buffer count in client. Only used in producer switching
     // case. It will be reset in switchProducer(), and accumulated in updateGraphicBlock() routine.
     uint32_t mBuffersInClient = 0u;
@@ -462,9 +429,7 @@
 C2VdaBqBlockPool::Impl::Impl(const std::shared_ptr<C2Allocator>& allocator)
       : mAllocator(allocator),
         mAllocateBuffersLock(mConfigureProducerAndAllocateBuffersMutex, std::defer_lock),
-        mBuffersRequested(0u),
-        mSpareSlot(-1),
-        mSpareDequeueDelayUs(kDequeueSpareMinDelayUs, kDequeueSpareMaxDelayUs) {}
+        mBuffersRequested(0u) {}
 
 c2_status_t C2VdaBqBlockPool::Impl::fetchGraphicBlock(
         uint32_t width, uint32_t height, uint32_t format, C2MemoryUsage usage,
@@ -538,23 +503,6 @@
 
     auto iter = mSlotAllocations.find(slot);
     if (iter == mSlotAllocations.end()) {
-        if (slot == mSpareSlot) {
-            // The dequeued slot is the spare buffer, we don't use this buffer for decoding and must
-            // cancel it after the delay time. Other working buffers may be available and pushed to
-            // free buffer queue in producer during the delay.
-            ALOGV("dequeued spare slot, cancel it after a wait time delay (%d)...",
-                  mSpareDequeueDelayUs.value());
-            ::usleep(mSpareDequeueDelayUs.value());  // wait for retry
-            // Double the delay time if spare buffer still be dequeued the next time. This could
-            // prevent block pool keeps aggressively dequeueing spare buffer while other buffers are
-            // not available yet.
-            mSpareDequeueDelayUs.increase();
-
-            if (mProducer->cancelBuffer(slot, fence) != android::NO_ERROR) {
-                return C2_CORRUPTED;
-            }
-            return C2_TIMED_OUT;
-        }
         if (mSlotAllocations.size() >= mBuffersRequested) {
             // The dequeued slot has a pre-allocated buffer whose size and format is as same as
             // currently requested (but was not dequeued during allocation cycle). Just detach it to
@@ -604,22 +552,6 @@
 
         mSlotAllocations[slot] = std::move(alloc);
         if (mSlotAllocations.size() == mBuffersRequested) {
-            // Allocate one spare buffer after allocating enough buffers requested by client.
-            uint32_t generation;
-            uint64_t usage;
-
-            err = C2_TIMED_OUT;
-            for (int32_t retriesLeft = kFetchSpareBufferMaxRetries;
-                 err == C2_TIMED_OUT && retriesLeft >= 0; retriesLeft--) {
-                err = fetchSpareBufferSlot(mProducer.get(), width, height, pixelFormat,
-                                           androidUsage, &generation, &usage);
-            }
-            if (err != C2_OK) {
-                ALOGE("fetchSpareBufferSlot failed after %d retries: %d",
-                      kFetchSpareBufferMaxRetries, err);
-                return err;
-            }
-
             // Already allocated enough buffers, set allowAllocation to false to restrict the
             // eligible slots to allocated ones for future dequeue.
             status = mProducer->allowAllocation(false);
@@ -633,20 +565,16 @@
         }
     }
 
-    // Reset spare dequeue delay time once we have dequeued a working buffer.
-    mSpareDequeueDelayUs.reset();
-
     auto poolData = std::make_shared<C2VdaBqBlockPoolData>(mProducerId, slot, shared_from_this());
     *block = _C2BlockFactory::CreateGraphicBlock(mSlotAllocations[slot], std::move(poolData));
     return C2_OK;
 }
 
-c2_status_t C2VdaBqBlockPool::Impl::fetchSpareBufferSlot(H2BGraphicBufferProducer* const producer,
-                                                         uint32_t width, uint32_t height,
-                                                         uint32_t pixelFormat,
-                                                         C2AndroidMemoryUsage androidUsage,
-                                                         uint32_t* generation, uint64_t* usage) {
-    ALOGV("fetchSpareBufferSlot");
+c2_status_t C2VdaBqBlockPool::Impl::queryGenerationAndUsage(
+        H2BGraphicBufferProducer* const producer, uint32_t width, uint32_t height,
+        uint32_t pixelFormat, C2AndroidMemoryUsage androidUsage, uint32_t* generation,
+        uint64_t* usage) {
+    ALOGV("queryGenerationAndUsage");
     sp<Fence> fence = new Fence();
     int32_t status;
     int32_t slot;
@@ -677,8 +605,8 @@
     sp<GraphicBuffer> slotBuffer = new GraphicBuffer();
     status = producer->requestBuffer(slot, &slotBuffer);
 
-    // Cancel this buffer anyway.
-    if (producer->cancelBuffer(slot, fence) != android::NO_ERROR) {
+    // Detach and delete the temporary buffer.
+    if (producer->detachBuffer(slot) != android::NO_ERROR) {
         return C2_CORRUPTED;
     }
 
@@ -689,11 +617,8 @@
 
     // Get generation number and usage from the slot buffer.
     *usage = slotBuffer->getUsage();
-    ALOGV("Obtained from spare buffer: generation = %u, usage = %" PRIu64 "", *generation, *usage);
-
-    mSpareSlot = slot;
-    mSpareDequeueDelayUs.reset();
-    ALOGV("Spare slot index = %d", mSpareSlot);
+    *generation = slotBuffer->getGenerationNumber();
+    ALOGV("Obtained from temp buffer: generation = %u, usage = %" PRIu64 "", *generation, *usage);
     return C2_OK;
 }
 
@@ -750,9 +675,7 @@
     // The remained slot indices in |mSlotAllocations| now are still dequeued (un-available).
     // maxDequeuedBufferCount should be set to "new requested buffer count" + "still dequeued buffer
     // count" to make sure it has enough available slots to request buffer from.
-    // Moreover, one extra buffer count is added for fetching spare buffer slot index.
-    status_t status =
-            mProducer->setMaxDequeuedBufferCount(bufferCount + mSlotAllocations.size() + 1);
+    status_t status = mProducer->setMaxDequeuedBufferCount(bufferCount + mSlotAllocations.size());
     if (status != android::NO_ERROR) {
         return asC2Error(status);
     }
@@ -762,7 +685,6 @@
     mSlotAllocations.clear();
     mProducerChangeSlotMap.clear();
     mBuffersRequested = static_cast<size_t>(bufferCount);
-    mSpareSlot = -1;
 
     status = mProducer->allowAllocation(true);
     if (status != android::NO_ERROR) {
@@ -820,16 +742,15 @@
 
     // Set maxDequeuedBufferCount to new producer.
     // Just like requestNewBufferSet(), maxDequeuedBufferCount should be set to "requested buffer
-    // count" + "buffer count in client" + 1 (spare buffer) to make sure it has enough available
-    // slots to request buffer from.
+    // count" + "buffer count in client" to make sure it has enough available slots to request
+    // buffers from.
     // "Requested buffer count" could be obtained by the size of |mSlotAllocations|. However, it is
     // not able to know "buffer count in client" in blockpool's aspect. The alternative solution is
     // to set the worse case first, which is equal to the size of |mSlotAllocations|. And in the end
     // of updateGraphicBlock() routine, we could get the arbitrary "buffer count in client" by
     // counting the calls of updateGraphicBlock(willCancel=true). Then we set maxDequeuedBufferCount
     // again to the correct value.
-    if (newProducer->setMaxDequeuedBufferCount(mSlotAllocations.size() * 2 + 1) !=
-        android::NO_ERROR) {
+    if (newProducer->setMaxDequeuedBufferCount(mSlotAllocations.size() * 2) != android::NO_ERROR) {
         return false;
     }
 
@@ -841,16 +762,15 @@
         return false;
     }
 
-    // Fetch spare buffer slot from new producer first, this step also allows us to obtain the
-    // generation number and usage of new producer. While attaching buffers, generation number and
-    // usage must be aligned to the producer.
+    // Get a buffer from the new producer to get the generation number and usage of new producer.
+    // While attaching buffers, generation number and usage must be aligned to the producer.
     uint32_t newGeneration;
     uint64_t newUsage;
-    c2_status_t err = fetchSpareBufferSlot(newProducer, mBufferFormat.mWidth, mBufferFormat.mHeight,
-                                           mBufferFormat.mPixelFormat, mBufferFormat.mUsage,
-                                           &newGeneration, &newUsage);
+    c2_status_t err = queryGenerationAndUsage(newProducer, mBufferFormat.mWidth,
+                                              mBufferFormat.mHeight, mBufferFormat.mPixelFormat,
+                                              mBufferFormat.mUsage, &newGeneration, &newUsage);
     if (err != C2_OK) {
-        ALOGE("fetchSpareBufferSlot failed: %d", err);
+        ALOGE("queryGenerationAndUsage failed: %d", err);
         return false;
     }
 
@@ -868,7 +788,7 @@
         native_handle_t* grallocHandle =
                 android::UnwrapNativeCodec2GrallocHandle(iter->second->handle());
 
-        // Update generation number and usage from newly-allocated spare buffer.
+        // Update generation number and usage.
         sp<GraphicBuffer> graphicBuffer =
                 new GraphicBuffer(grallocHandle, GraphicBuffer::CLONE_HANDLE, width, height, format,
                                   1, newUsage, stride);
@@ -965,10 +885,10 @@
     if (mProducerChangeSlotMap.empty()) {
         // The updateGraphicBlock() routine is about to finish.
         // Set the correct maxDequeuedBufferCount to producer, which is "requested buffer count" +
-        // "buffer count in client" + 1 (spare buffer).
+        // "buffer count in client".
         ALOGV("Requested buffer count: %zu, buffer count in client: %u", mSlotAllocations.size(),
               mBuffersInClient);
-        if (mProducer->setMaxDequeuedBufferCount(mSlotAllocations.size() + mBuffersInClient + 1) !=
+        if (mProducer->setMaxDequeuedBufferCount(mSlotAllocations.size() + mBuffersInClient) !=
             android::NO_ERROR) {
             return C2_CORRUPTED;
         }