libgui: Add getLastQueuedBuffer to BufferQueue
Adds the ability to get the last buffer queued to a BufferQueue plus
its acquire fence.
Bug: 27708453
Change-Id: Iee39475740b40c854a5f46178b2934fd930e61b8
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index cdece73..052de3d 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -200,6 +200,10 @@
}
mSlots[slot].mFence = Fence::NO_FENCE;
mSlots[slot].mEglDisplay = EGL_NO_DISPLAY;
+
+ if (mLastQueuedSlot == slot) {
+ mLastQueuedSlot = INVALID_BUFFER_SLOT;
+ }
}
void BufferQueueCore::freeAllBuffersLocked() {
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index bb8d39b..4c3bcd9 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -867,6 +867,7 @@
mCore->mBufferHasBeenQueued = true;
mCore->mDequeueCondition.broadcast();
+ mCore->mLastQueuedSlot = slot;
output->inflate(mCore->mDefaultWidth, mCore->mDefaultHeight,
mCore->mTransformHint,
@@ -909,8 +910,8 @@
// third. In the event that frames take varying time, this makes a
// small trade-off in favor of latency rather than throughput.
mLastQueueBufferFence->waitForever("Throttling EGL Production");
- mLastQueueBufferFence = fence;
}
+ mLastQueueBufferFence = fence;
return NO_ERROR;
}
@@ -1356,6 +1357,24 @@
return NO_ERROR;
}
+status_t BufferQueueProducer::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
+ sp<Fence>* outFence) {
+ ATRACE_CALL();
+ BQ_LOGV("getLastQueuedBuffer");
+
+ Mutex::Autolock lock(mCore->mMutex);
+ if (mCore->mLastQueuedSlot == BufferItem::INVALID_BUFFER_SLOT) {
+ *outBuffer = nullptr;
+ *outFence = Fence::NO_FENCE;
+ return NO_ERROR;
+ }
+
+ *outBuffer = mSlots[mCore->mLastQueuedSlot].mGraphicBuffer;
+ *outFence = mLastQueueBufferFence;
+
+ return NO_ERROR;
+}
+
void BufferQueueProducer::binderDied(const wp<android::IBinder>& /* 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 7cdb8f4..c36fcad 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -54,6 +54,7 @@
SET_SHARED_BUFFER_MODE,
SET_AUTO_REFRESH,
SET_DEQUEUE_TIMEOUT,
+ GET_LAST_QUEUED_BUFFER,
};
class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer>
@@ -379,6 +380,37 @@
}
return reply.readInt32();
}
+
+ virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
+ sp<Fence>* outFence) override {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+ status_t result = remote()->transact(GET_LAST_QUEUED_BUFFER, data,
+ &reply);
+ if (result != NO_ERROR) {
+ ALOGE("getLastQueuedBuffer failed to transact: %d", result);
+ return result;
+ }
+ result = reply.readInt32();
+ if (result != NO_ERROR) {
+ return result;
+ }
+ sp<GraphicBuffer> buffer(new GraphicBuffer);
+ result = reply.read(*buffer);
+ if (result != NO_ERROR) {
+ ALOGE("getLastQueuedBuffer failed to read buffer: %d", result);
+ return result;
+ }
+ sp<Fence> fence(new Fence);
+ result = reply.read(*fence);
+ if (result != NO_ERROR) {
+ ALOGE("getLastQueuedBuffer failed to read fence: %d", result);
+ return result;
+ }
+ *outBuffer = buffer;
+ *outFence = fence;
+ return result;
+ }
};
// Out-of-line virtual method definition to trigger vtable emission in this
@@ -590,6 +622,27 @@
reply->writeInt32(result);
return NO_ERROR;
}
+ case GET_LAST_QUEUED_BUFFER: {
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+ sp<GraphicBuffer> buffer(nullptr);
+ sp<Fence> fence(Fence::NO_FENCE);
+ status_t result = getLastQueuedBuffer(&buffer, &fence);
+ reply->writeInt32(result);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply->write(*buffer);
+ if (result != NO_ERROR) {
+ ALOGE("getLastQueuedBuffer failed to write buffer: %d", result);
+ return result;
+ }
+ result = reply->write(*fence);
+ if (result != NO_ERROR) {
+ ALOGE("getLastQueuedBuffer failed to write fence: %d", result);
+ return result;
+ }
+ return NO_ERROR;
+ }
}
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 5efc333..b304633 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -127,6 +127,11 @@
return mGraphicBufferProducer->setDequeueTimeout(timeout);
}
+status_t Surface::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
+ sp<Fence>* outFence) {
+ return mGraphicBufferProducer->getLastQueuedBuffer(outBuffer, outFence);
+}
+
int Surface::hook_setSwapInterval(ANativeWindow* window, int interval) {
Surface* c = getSelf(window);
return c->setSwapInterval(interval);
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index 82df9a9..9876d94 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -796,4 +796,56 @@
ASSERT_EQ(OK, mProducer->attachBuffer(&slot, buffer));
}
+TEST_F(BufferQueueTest, CanRetrieveLastQueuedBuffer) {
+ 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));
+
+ // Dequeue and queue the first buffer, storing the handle
+ int slot = BufferQueue::INVALID_BUFFER_SLOT;
+ sp<Fence> fence;
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ sp<GraphicBuffer> firstBuffer;
+ ASSERT_EQ(OK, mProducer->requestBuffer(slot, &firstBuffer));
+
+ IGraphicBufferProducer::QueueBufferInput input(0ull, true,
+ HAL_DATASPACE_UNKNOWN, Rect::INVALID_RECT,
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE);
+ ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+
+ // Dequeue a second buffer
+ slot = BufferQueue::INVALID_BUFFER_SLOT;
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ sp<GraphicBuffer> secondBuffer;
+ ASSERT_EQ(OK, mProducer->requestBuffer(slot, &secondBuffer));
+
+ // Ensure it's a new buffer
+ ASSERT_NE(firstBuffer->getNativeBuffer()->handle,
+ secondBuffer->getNativeBuffer()->handle);
+
+ // Queue the second buffer
+ ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+
+ // Acquire and release both buffers
+ for (size_t i = 0; i < 2; ++i) {
+ BufferItem item;
+ 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));
+ }
+
+ // Make sure we got the second buffer back
+ sp<GraphicBuffer> returnedBuffer;
+ sp<Fence> returnedFence;
+ ASSERT_EQ(OK,
+ mProducer->getLastQueuedBuffer(&returnedBuffer, &returnedFence));
+ ASSERT_EQ(secondBuffer->getNativeBuffer()->handle,
+ returnedBuffer->getNativeBuffer()->handle);
+}
+
} // namespace android