diff options
-rw-r--r-- | include/gui/BufferQueueConsumer.h | 3 | ||||
-rw-r--r-- | include/gui/BufferQueueCore.h | 5 | ||||
-rw-r--r-- | include/gui/ConsumerBase.h | 3 | ||||
-rw-r--r-- | include/gui/IGraphicBufferConsumer.h | 5 | ||||
-rw-r--r-- | libs/gui/BufferQueueConsumer.cpp | 6 | ||||
-rw-r--r-- | libs/gui/BufferQueueCore.cpp | 10 | ||||
-rw-r--r-- | libs/gui/ConsumerBase.cpp | 9 | ||||
-rw-r--r-- | libs/gui/IGraphicBufferConsumer.cpp | 22 | ||||
-rw-r--r-- | libs/gui/tests/BufferQueue_test.cpp | 77 |
9 files changed, 140 insertions, 0 deletions
diff --git a/include/gui/BufferQueueConsumer.h b/include/gui/BufferQueueConsumer.h index a9fce1ab5d..8ec0546c7e 100644 --- a/include/gui/BufferQueueConsumer.h +++ b/include/gui/BufferQueueConsumer.h @@ -140,6 +140,9 @@ public: virtual status_t getOccupancyHistory(bool forceFlush, std::vector<OccupancyTracker::Segment>* outHistory) override; + // See IGraphicBufferConsumer::discardFreeBuffers + virtual status_t discardFreeBuffers() override; + // dump our state in a String virtual void dump(String8& result, const char* prefix) const; diff --git a/include/gui/BufferQueueCore.h b/include/gui/BufferQueueCore.h index 98add9e71f..82bc121af3 100644 --- a/include/gui/BufferQueueCore.h +++ b/include/gui/BufferQueueCore.h @@ -125,6 +125,11 @@ private: // all slots, even if they're currently dequeued, queued, or acquired. void freeAllBuffersLocked(); + // discardFreeBuffersLocked releases all currently-free buffers held by the + // queue, in order to reduce the memory consumption of the queue to the + // minimum possible without discarding data. + void discardFreeBuffersLocked(); + // If delta is positive, makes more slots available. If negative, takes // away slots. Returns false if the request can't be met. bool adjustAvailableSlotsLocked(int delta); diff --git a/include/gui/ConsumerBase.h b/include/gui/ConsumerBase.h index d1f4cdd7d0..0490c3cc5b 100644 --- a/include/gui/ConsumerBase.h +++ b/include/gui/ConsumerBase.h @@ -89,6 +89,9 @@ public: status_t getOccupancyHistory(bool forceFlush, std::vector<OccupancyTracker::Segment>* outHistory); + // See IGraphicBufferConsumer::discardFreeBuffers + status_t discardFreeBuffers(); + private: ConsumerBase(const ConsumerBase&); void operator=(const ConsumerBase&); diff --git a/include/gui/IGraphicBufferConsumer.h b/include/gui/IGraphicBufferConsumer.h index 4915478837..3b10d78b58 100644 --- a/include/gui/IGraphicBufferConsumer.h +++ b/include/gui/IGraphicBufferConsumer.h @@ -272,6 +272,11 @@ public: virtual status_t getOccupancyHistory(bool forceFlush, std::vector<OccupancyTracker::Segment>* outHistory) = 0; + // discardFreeBuffers releases all currently-free buffers held by the queue, + // in order to reduce the memory consumption of the queue to the minimum + // possible without discarding data. + virtual status_t discardFreeBuffers() = 0; + // dump state into a string virtual void dump(String8& result, const char* prefix) const = 0; diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp index e8860d12fe..ca2a374e16 100644 --- a/libs/gui/BufferQueueConsumer.cpp +++ b/libs/gui/BufferQueueConsumer.cpp @@ -725,6 +725,12 @@ status_t BufferQueueConsumer::getOccupancyHistory(bool forceFlush, return NO_ERROR; } +status_t BufferQueueConsumer::discardFreeBuffers() { + Mutex::Autolock lock(mCore->mMutex); + mCore->discardFreeBuffersLocked(); + return NO_ERROR; +} + void BufferQueueConsumer::dump(String8& result, const char* prefix) const { const IPCThreadState* ipc = IPCThreadState::self(); const pid_t pid = ipc->getCallingPid(); diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp index 569b8f9d06..9cb9c62401 100644 --- a/libs/gui/BufferQueueCore.cpp +++ b/libs/gui/BufferQueueCore.cpp @@ -244,6 +244,16 @@ void BufferQueueCore::freeAllBuffersLocked() { VALIDATE_CONSISTENCY(); } +void BufferQueueCore::discardFreeBuffersLocked() { + for (int s : mFreeBuffers) { + mFreeSlots.insert(s); + clearBufferSlotLocked(s); + } + mFreeBuffers.clear(); + + VALIDATE_CONSISTENCY(); +} + bool BufferQueueCore::adjustAvailableSlotsLocked(int delta) { if (delta >= 0) { // If we're going to fail, do so before modifying anything diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp index 84965ef6da..a1bdf4a4a1 100644 --- a/libs/gui/ConsumerBase.cpp +++ b/libs/gui/ConsumerBase.cpp @@ -245,6 +245,15 @@ status_t ConsumerBase::getOccupancyHistory(bool forceFlush, return mConsumer->getOccupancyHistory(forceFlush, outHistory); } +status_t ConsumerBase::discardFreeBuffers() { + Mutex::Autolock _l(mMutex); + if (mAbandoned) { + CB_LOGE("discardFreeBuffers: ConsumerBase is abandoned!"); + return NO_INIT; + } + return mConsumer->discardFreeBuffers(); +} + void ConsumerBase::dump(String8& result) const { dump(result, ""); } diff --git a/libs/gui/IGraphicBufferConsumer.cpp b/libs/gui/IGraphicBufferConsumer.cpp index 7c4379f41e..c8eff00e3f 100644 --- a/libs/gui/IGraphicBufferConsumer.cpp +++ b/libs/gui/IGraphicBufferConsumer.cpp @@ -52,6 +52,7 @@ enum { SET_TRANSFORM_HINT, GET_SIDEBAND_STREAM, GET_OCCUPANCY_HISTORY, + DISCARD_FREE_BUFFERS, DUMP, }; @@ -286,6 +287,21 @@ public: return result; } + virtual status_t discardFreeBuffers() { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); + status_t error = remote()->transact(DISCARD_FREE_BUFFERS, data, &reply); + if (error != NO_ERROR) { + return error; + } + int32_t result = NO_ERROR; + error = reply.readInt32(&result); + if (error != NO_ERROR) { + return error; + } + return result; + } + virtual void dump(String8& result, const char* prefix) const { Parcel data, reply; data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); @@ -454,6 +470,12 @@ status_t BnGraphicBufferConsumer::onTransact( } return NO_ERROR; } + case DISCARD_FREE_BUFFERS: { + CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); + status_t result = discardFreeBuffers(); + status_t error = reply->writeInt32(result); + return error; + } case DUMP: { CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); String8 result = data.readString8(); diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp index 210ce8c18e..8a9eeeec21 100644 --- a/libs/gui/tests/BufferQueue_test.cpp +++ b/libs/gui/tests/BufferQueue_test.cpp @@ -990,4 +990,81 @@ TEST_F(BufferQueueTest, TestOccupancyHistory) { ASSERT_EQ(true, thirdSegment.usedThirdBuffer); } +TEST_F(BufferQueueTest, TestDiscardFreeBuffers) { + createBufferQueue(); + sp<DummyConsumer> dc(new DummyConsumer); + ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false)); + IGraphicBufferProducer::QueueBufferOutput output; + ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener, + NATIVE_WINDOW_API_CPU, false, &output)); + + int slot = BufferQueue::INVALID_BUFFER_SLOT; + sp<Fence> fence = Fence::NO_FENCE; + sp<GraphicBuffer> buffer = nullptr; + IGraphicBufferProducer::QueueBufferInput input(0ull, true, + HAL_DATASPACE_UNKNOWN, Rect::INVALID_RECT, + NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE); + BufferItem item{}; + + // Preallocate, dequeue, request, and cancel 4 buffers so we don't get + // BUFFER_NEEDS_REALLOCATION below + int slots[4] = {}; + mProducer->setMaxDequeuedBufferCount(4); + for (size_t i = 0; i < 4; ++i) { + status_t result = mProducer->dequeueBuffer(&slots[i], &fence, + 0, 0, 0, 0); + ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result); + ASSERT_EQ(OK, mProducer->requestBuffer(slots[i], &buffer)); + } + for (size_t i = 0; i < 4; ++i) { + ASSERT_EQ(OK, mProducer->cancelBuffer(slots[i], Fence::NO_FENCE)); + } + + // Get buffers in all states: dequeued, filled, acquired, free + + // Fill 3 buffers + ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0)); + ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); + ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0)); + ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); + ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0)); + ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); + // Dequeue 1 buffer + ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0)); + + // Acquire and free 1 buffer + ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); + ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, + EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); + // Acquire 1 buffer, leaving 1 filled buffer in queue + ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); + + // Now discard the free buffers + ASSERT_EQ(OK, mConsumer->discardFreeBuffers()); + + // Check no free buffers in dump + String8 dumpString; + mConsumer->dump(dumpString, nullptr); + + // Parse the dump to ensure that all buffer slots that are FREE also + // have a null GraphicBuffer + // Fragile - assumes the following format for the dump for a buffer entry: + // ":%p\][^:]*state=FREE" where %p is the buffer pointer in hex. + ssize_t idx = dumpString.find("state=FREE"); + while (idx != -1) { + ssize_t bufferPtrIdx = idx - 1; + while (bufferPtrIdx > 0) { + if (dumpString[bufferPtrIdx] == ':') { + bufferPtrIdx++; + break; + } + bufferPtrIdx--; + } + ASSERT_GT(bufferPtrIdx, 0) << "Can't parse queue dump to validate"; + ssize_t nullPtrIdx = dumpString.find("0x0]", bufferPtrIdx); + ASSERT_EQ(bufferPtrIdx, nullPtrIdx) << "Free buffer not discarded"; + idx = dumpString.find("FREE", idx + 1); + } +} + } // namespace android |