Add batching to IGBP functions
The batching functions has a default implementation that calls
into the implementation of corresponding non-batched version.
Bug: 113788435
Bug: 137244088
Test: atest libgui_test
Change-Id: I068a05b8bfc7c64f893649dec2ef2233f16a22f6
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index af9ef06..c5a1855 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -161,6 +161,7 @@
filegroup {
name: "libgui_bufferqueue_sources",
srcs: [
+ "BatchBufferOps.cpp",
"BufferItem.cpp",
"BufferQueue.cpp",
"BufferQueueConsumer.cpp",
@@ -171,7 +172,7 @@
"FrameTimestamps.cpp",
"GLConsumerUtils.cpp",
"HdrMetadata.cpp",
- "QueueBufferInputOutput.cpp",
+ "IGraphicBufferProducerFlattenables.cpp",
"bufferqueue/1.0/Conversion.cpp",
"bufferqueue/1.0/H2BProducerListener.cpp",
"bufferqueue/1.0/WProducerListener.cpp",
diff --git a/libs/gui/BatchBufferOps.cpp b/libs/gui/BatchBufferOps.cpp
new file mode 100644
index 0000000..60aceb1
--- /dev/null
+++ b/libs/gui/BatchBufferOps.cpp
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+
+#define LOG_TAG "IGBPBatchOps"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+//#define LOG_NDEBUG 0
+
+#include <gui/IGraphicBufferProducer.h>
+
+namespace android {
+
+/**
+ * Default implementation of batched buffer operations. These default
+ * implementations call into the non-batched version of the same operation.
+ */
+
+status_t IGraphicBufferProducer::requestBuffers(
+ const std::vector<int32_t>& slots,
+ std::vector<RequestBufferOutput>* outputs) {
+ outputs->clear();
+ outputs->reserve(slots.size());
+ for (int32_t slot : slots) {
+ RequestBufferOutput& output = outputs->emplace_back();
+ output.result = requestBuffer(static_cast<int>(slot),
+ &output.buffer);
+ }
+ return NO_ERROR;
+}
+
+status_t IGraphicBufferProducer::dequeueBuffers(
+ const std::vector<DequeueBufferInput>& inputs,
+ std::vector<DequeueBufferOutput>* outputs) {
+ outputs->clear();
+ outputs->reserve(inputs.size());
+ for (const DequeueBufferInput& input : inputs) {
+ DequeueBufferOutput& output = outputs->emplace_back();
+ output.result = dequeueBuffer(
+ &output.slot,
+ &output.fence,
+ input.width,
+ input.height,
+ input.format,
+ input.usage,
+ &output.bufferAge,
+ input.getTimestamps ? &output.timestamps.emplace() : nullptr);
+ }
+ return NO_ERROR;
+}
+
+status_t IGraphicBufferProducer::detachBuffers(
+ const std::vector<int32_t>& slots,
+ std::vector<status_t>* results) {
+ results->clear();
+ results->reserve(slots.size());
+ for (int32_t slot : slots) {
+ results->emplace_back(detachBuffer(slot));
+ }
+ return NO_ERROR;
+}
+
+status_t IGraphicBufferProducer::attachBuffers(
+ const std::vector<sp<GraphicBuffer>>& buffers,
+ std::vector<AttachBufferOutput>* outputs) {
+ outputs->clear();
+ outputs->reserve(buffers.size());
+ for (const sp<GraphicBuffer>& buffer : buffers) {
+ AttachBufferOutput& output = outputs->emplace_back();
+ output.result = attachBuffer(&output.slot, buffer);
+ }
+ return NO_ERROR;
+}
+
+status_t IGraphicBufferProducer::queueBuffers(
+ const std::vector<QueueBufferInput>& inputs,
+ std::vector<QueueBufferOutput>* outputs) {
+ outputs->clear();
+ outputs->reserve(inputs.size());
+ for (const QueueBufferInput& input : inputs) {
+ QueueBufferOutput& output = outputs->emplace_back();
+ output.result = queueBuffer(input.slot, input, &output);
+ }
+ return NO_ERROR;
+}
+
+status_t IGraphicBufferProducer::cancelBuffers(
+ const std::vector<CancelBufferInput>& inputs,
+ std::vector<status_t>* results) {
+ results->clear();
+ results->reserve(inputs.size());
+ for (const CancelBufferInput& input : inputs) {
+ results->emplace_back() = cancelBuffer(input.slot, input.fence);
+ }
+ return NO_ERROR;
+}
+
+status_t IGraphicBufferProducer::query(const std::vector<int32_t> inputs,
+ std::vector<QueryOutput>* outputs) {
+ outputs->clear();
+ outputs->reserve(inputs.size());
+ for (int32_t input : inputs) {
+ QueryOutput& output = outputs->emplace_back();
+ int value{};
+ output.result = static_cast<status_t>(
+ query(static_cast<int>(input), &value));
+ output.value = static_cast<int64_t>(value);
+ }
+ return NO_ERROR;
+}
+
+} // namespace android
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index ad00939..c1f9b85 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -74,6 +74,13 @@
GET_CONSUMER_USAGE,
SET_LEGACY_BUFFER_DROP,
SET_AUTO_PREROTATION,
+ REQUEST_BUFFERS,
+ DEQUEUE_BUFFERS,
+ DETACH_BUFFERS,
+ ATTACH_BUFFERS,
+ QUEUE_BUFFERS,
+ CANCEL_BUFFERS,
+ QUERY_MULTIPLE,
};
class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer>
@@ -90,7 +97,7 @@
Parcel data, reply;
data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
data.writeInt32(bufferIdx);
- status_t result =remote()->transact(REQUEST_BUFFER, data, &reply);
+ status_t result = remote()->transact(REQUEST_BUFFER, data, &reply);
if (result != NO_ERROR) {
return result;
}
@@ -107,6 +114,27 @@
return result;
}
+ virtual status_t requestBuffers(
+ const std::vector<int32_t>& slots,
+ std::vector<RequestBufferOutput>* outputs) override {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+ data.writeInt32Vector(slots);
+ status_t result = remote()->transact(REQUEST_BUFFERS, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.resizeOutVector(outputs);
+ for (RequestBufferOutput& output : *outputs) {
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.read(output);
+ }
+
+ return result;
+ }
+
virtual status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers) {
Parcel data, reply;
data.writeInterfaceToken(
@@ -183,6 +211,29 @@
return result;
}
+ virtual status_t dequeueBuffers(
+ const std::vector<DequeueBufferInput>& inputs,
+ std::vector<DequeueBufferOutput>* outputs) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+ data.writeVectorSize(inputs);
+ for (const auto& input : inputs) {
+ data.write(input);
+ }
+ status_t result = remote()->transact(DEQUEUE_BUFFERS, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.resizeOutVector(outputs);
+ for (auto& output : *outputs) {
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.read(output);
+ }
+ return result;
+ }
+
virtual status_t detachBuffer(int slot) {
Parcel data, reply;
data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
@@ -195,6 +246,19 @@
return result;
}
+ virtual status_t detachBuffers(const std::vector<int32_t>& slots,
+ std::vector<status_t>* results) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+ data.writeInt32Vector(slots);
+ status_t result = remote()->transact(DETACH_BUFFERS, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.readInt32Vector(results);
+ return result;
+ }
+
virtual status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer,
sp<Fence>* outFence) {
if (outBuffer == nullptr) {
@@ -256,6 +320,39 @@
return result;
}
+ virtual status_t attachBuffers(
+ const std::vector<sp<GraphicBuffer>>& buffers,
+ std::vector<AttachBufferOutput>* outputs) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+ data.writeVectorSize(buffers);
+ for (const sp<GraphicBuffer>& buffer : buffers) {
+ data.write(*buffer.get());
+ }
+ status_t result = remote()->transact(ATTACH_BUFFERS, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.resizeOutVector(outputs);
+ for (AttachBufferOutput& output : *outputs) {
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.read(output);
+ }
+ if (result == NO_ERROR) {
+ for (AttachBufferOutput& output : *outputs) {
+ if (output.result == NO_ERROR && output.slot < 0) {
+ ALOGE("attachBuffers returned invalid slot %d",
+ output.slot);
+ android_errorWriteLog(0x534e4554, "37478824");
+ output.result = UNKNOWN_ERROR;
+ }
+ }
+ }
+ return result;
+ }
+
virtual status_t queueBuffer(int buf,
const QueueBufferInput& input, QueueBufferOutput* output) {
Parcel data, reply;
@@ -278,6 +375,28 @@
return result;
}
+ virtual status_t queueBuffers(const std::vector<QueueBufferInput>& inputs,
+ std::vector<QueueBufferOutput>* outputs) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+ data.writeVectorSize(inputs);
+ for (const QueueBufferInput& input : inputs) {
+ data.write(input);
+ }
+ status_t result = remote()->transact(QUEUE_BUFFERS, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.resizeOutVector(outputs);
+ for (QueueBufferOutput& output : *outputs) {
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.read(output);
+ }
+ return result;
+ }
+
virtual status_t cancelBuffer(int buf, const sp<Fence>& fence) {
Parcel data, reply;
data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
@@ -291,6 +410,23 @@
return result;
}
+ virtual status_t cancelBuffers(
+ const std::vector<CancelBufferInput>& inputs,
+ std::vector<status_t>* results) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+ data.writeVectorSize(inputs);
+ for (const CancelBufferInput& input : inputs) {
+ data.write(input);
+ }
+ status_t result = remote()->transact(CANCEL_BUFFERS, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.readInt32Vector(results);
+ return result;
+ }
+
virtual int query(int what, int* value) {
Parcel data, reply;
data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
@@ -304,6 +440,25 @@
return result;
}
+ virtual status_t query(const std::vector<int32_t> inputs,
+ std::vector<QueryOutput>* outputs) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+ data.writeInt32Vector(inputs);
+ status_t result = remote()->transact(QUERY_MULTIPLE, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.resizeOutVector(outputs);
+ for (QueryOutput& output : *outputs) {
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.read(output);
+ }
+ return result;
+ }
+
virtual status_t connect(const sp<IProducerListener>& listener,
int api, bool producerControlledByApp, QueueBufferOutput* output) {
Parcel data, reply;
@@ -576,6 +731,12 @@
return mBase->requestBuffer(slot, buf);
}
+ status_t requestBuffers(
+ const std::vector<int32_t>& slots,
+ std::vector<RequestBufferOutput>* outputs) override {
+ return mBase->requestBuffers(slots, outputs);
+ }
+
status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers) override {
return mBase->setMaxDequeuedBufferCount(maxDequeuedBuffers);
}
@@ -590,10 +751,21 @@
return mBase->dequeueBuffer(slot, fence, w, h, format, usage, outBufferAge, outTimestamps);
}
+ status_t dequeueBuffers(
+ const std::vector<DequeueBufferInput>& inputs,
+ std::vector<DequeueBufferOutput>* outputs) override {
+ return mBase->dequeueBuffers(inputs, outputs);
+ }
+
status_t detachBuffer(int slot) override {
return mBase->detachBuffer(slot);
}
+ status_t detachBuffers(const std::vector<int32_t>& slots,
+ std::vector<status_t>* results) override {
+ return mBase->detachBuffers(slots, results);
+ }
+
status_t detachNextBuffer(
sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) override {
return mBase->detachNextBuffer(outBuffer, outFence);
@@ -604,6 +776,12 @@
return mBase->attachBuffer(outSlot, buffer);
}
+ status_t attachBuffers(
+ const std::vector<sp<GraphicBuffer>>& buffers,
+ std::vector<AttachBufferOutput>* outputs) override {
+ return mBase->attachBuffers(buffers, outputs);
+ }
+
status_t queueBuffer(
int slot,
const QueueBufferInput& input,
@@ -611,14 +789,30 @@
return mBase->queueBuffer(slot, input, output);
}
+ status_t queueBuffers(const std::vector<QueueBufferInput>& inputs,
+ std::vector<QueueBufferOutput>* outputs) override {
+ return mBase->queueBuffers(inputs, outputs);
+ }
+
status_t cancelBuffer(int slot, const sp<Fence>& fence) override {
return mBase->cancelBuffer(slot, fence);
}
+ status_t cancelBuffers(
+ const std::vector<CancelBufferInput>& inputs,
+ std::vector<status_t>* results) override {
+ return mBase->cancelBuffers(inputs, results);
+ }
+
int query(int what, int* value) override {
return mBase->query(what, value);
}
+ status_t query(const std::vector<int32_t> inputs,
+ std::vector<QueryOutput>* outputs) override {
+ return mBase->query(inputs, outputs);
+ }
+
status_t connect(
const sp<IProducerListener>& listener,
int api, bool producerControlledByApp,
@@ -789,7 +983,7 @@
switch(code) {
case REQUEST_BUFFER: {
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
- int bufferIdx = data.readInt32();
+ int bufferIdx = data.readInt32();
sp<GraphicBuffer> buffer;
int result = requestBuffer(bufferIdx, &buffer);
reply->writeInt32(buffer != nullptr);
@@ -799,6 +993,24 @@
reply->writeInt32(result);
return NO_ERROR;
}
+ case REQUEST_BUFFERS: {
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+ std::vector<int32_t> slots;
+ std::vector<RequestBufferOutput> outputs;
+ status_t result = data.readInt32Vector(&slots);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ (void)requestBuffers(slots, &outputs);
+ result = reply->writeVectorSize(outputs);
+ for (const RequestBufferOutput& output : outputs) {
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply->write(output);
+ }
+ return result;
+ }
case SET_MAX_DEQUEUED_BUFFER_COUNT: {
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
int maxDequeuedBuffers = data.readInt32();
@@ -841,6 +1053,30 @@
reply->writeInt32(result);
return NO_ERROR;
}
+ case DEQUEUE_BUFFERS: {
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+ std::vector<DequeueBufferInput> inputs;
+ std::vector<DequeueBufferOutput> outputs;
+ status_t result = data.resizeOutVector(&inputs);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ for (DequeueBufferInput& input : inputs) {
+ result = data.read(input);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ }
+ (void)dequeueBuffers(inputs, &outputs);
+ result = reply->writeVectorSize(outputs);
+ for (const DequeueBufferOutput& output : outputs) {
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply->write(output);
+ }
+ return result;
+ }
case DETACH_BUFFER: {
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
int slot = data.readInt32();
@@ -848,6 +1084,17 @@
reply->writeInt32(result);
return NO_ERROR;
}
+ case DETACH_BUFFERS: {
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+ std::vector<int32_t> slots;
+ std::vector<status_t> results;
+ status_t result = data.readInt32Vector(&slots);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ (void)detachBuffers(slots, &results);
+ return reply->writeInt32Vector(results);
+ }
case DETACH_NEXT_BUFFER: {
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
sp<GraphicBuffer> buffer;
@@ -878,6 +1125,31 @@
reply->writeInt32(result);
return NO_ERROR;
}
+ case ATTACH_BUFFERS: {
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+ std::vector<sp<GraphicBuffer>> buffers;
+ status_t result = data.resizeOutVector(&buffers);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ for (sp<GraphicBuffer>& buffer : buffers) {
+ buffer = new GraphicBuffer();
+ result = data.read(*buffer.get());
+ if (result != NO_ERROR) {
+ return result;
+ }
+ }
+ std::vector<AttachBufferOutput> outputs;
+ (void)attachBuffers(buffers, &outputs);
+ result = reply->writeVectorSize(outputs);
+ for (const AttachBufferOutput& output : outputs) {
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply->write(output);
+ }
+ return result;
+ }
case QUEUE_BUFFER: {
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
@@ -890,6 +1162,30 @@
return NO_ERROR;
}
+ case QUEUE_BUFFERS: {
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+ std::vector<QueueBufferInput> inputs;
+ status_t result = data.resizeOutVector(&inputs);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ for (QueueBufferInput& input : inputs) {
+ result = data.read(input);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ }
+ std::vector<QueueBufferOutput> outputs;
+ (void)queueBuffers(inputs, &outputs);
+ result = reply->writeVectorSize(outputs);
+ for (const QueueBufferOutput& output : outputs) {
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply->write(output);
+ }
+ return result;
+ }
case CANCEL_BUFFER: {
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
int buf = data.readInt32();
@@ -901,6 +1197,26 @@
reply->writeInt32(result);
return NO_ERROR;
}
+ case CANCEL_BUFFERS: {
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+ std::vector<CancelBufferInput> inputs;
+ status_t result = data.resizeOutVector(&inputs);
+ for (CancelBufferInput& input : inputs) {
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = data.read(input);
+ }
+ if (result != NO_ERROR) {
+ return result;
+ }
+ std::vector<status_t> results;
+ result = cancelBuffers(inputs, &results);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ return reply->writeInt32Vector(results);
+ }
case QUERY: {
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
int value = 0;
@@ -910,6 +1226,27 @@
reply->writeInt32(res);
return NO_ERROR;
}
+ case QUERY_MULTIPLE: {
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+ std::vector<int32_t> inputs;
+ status_t result = data.readInt32Vector(&inputs);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ std::vector<QueryOutput> outputs;
+ result = query(inputs, &outputs);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply->writeVectorSize(outputs);
+ for (const QueryOutput& output : outputs) {
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply->write(output);
+ }
+ return result;
+ }
case CONNECT: {
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
sp<IProducerListener> listener;
@@ -1083,11 +1420,4 @@
return BBinder::onTransact(code, data, reply, flags);
}
-// ----------------------------------------------------------------------------
-
-IGraphicBufferProducer::QueueBufferInput::QueueBufferInput(const Parcel& parcel) {
- parcel.read(*this);
-}
-
-
}; // namespace android
diff --git a/libs/gui/IGraphicBufferProducerFlattenables.cpp b/libs/gui/IGraphicBufferProducerFlattenables.cpp
new file mode 100644
index 0000000..c8b9b67
--- /dev/null
+++ b/libs/gui/IGraphicBufferProducerFlattenables.cpp
@@ -0,0 +1,413 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+#include <gui/IGraphicBufferProducer.h>
+
+namespace android {
+
+constexpr size_t IGraphicBufferProducer::QueueBufferInput::minFlattenedSize() {
+ return sizeof(timestamp) +
+ sizeof(isAutoTimestamp) +
+ sizeof(dataSpace) +
+ sizeof(crop) +
+ sizeof(scalingMode) +
+ sizeof(transform) +
+ sizeof(stickyTransform) +
+ sizeof(getFrameTimestamps) +
+ sizeof(slot);
+}
+
+size_t IGraphicBufferProducer::QueueBufferInput::getFlattenedSize() const {
+ return minFlattenedSize() +
+ fence->getFlattenedSize() +
+ surfaceDamage.getFlattenedSize() +
+ hdrMetadata.getFlattenedSize();
+}
+
+size_t IGraphicBufferProducer::QueueBufferInput::getFdCount() const {
+ return fence->getFdCount();
+}
+
+status_t IGraphicBufferProducer::QueueBufferInput::flatten(
+ void*& buffer, size_t& size, int*& fds, size_t& count) const
+{
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::write(buffer, size, timestamp);
+ FlattenableUtils::write(buffer, size, isAutoTimestamp);
+ FlattenableUtils::write(buffer, size, dataSpace);
+ FlattenableUtils::write(buffer, size, crop);
+ FlattenableUtils::write(buffer, size, scalingMode);
+ FlattenableUtils::write(buffer, size, transform);
+ FlattenableUtils::write(buffer, size, stickyTransform);
+ FlattenableUtils::write(buffer, size, getFrameTimestamps);
+
+ status_t result = fence->flatten(buffer, size, fds, count);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = surfaceDamage.flatten(buffer, size);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ FlattenableUtils::advance(buffer, size, surfaceDamage.getFlattenedSize());
+ result = hdrMetadata.flatten(buffer, size);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ FlattenableUtils::advance(buffer, size, hdrMetadata.getFlattenedSize());
+ FlattenableUtils::write(buffer, size, slot);
+ return NO_ERROR;
+}
+
+status_t IGraphicBufferProducer::QueueBufferInput::unflatten(
+ void const*& buffer, size_t& size, int const*& fds, size_t& count)
+{
+ if (size < minFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::read(buffer, size, timestamp);
+ FlattenableUtils::read(buffer, size, isAutoTimestamp);
+ FlattenableUtils::read(buffer, size, dataSpace);
+ FlattenableUtils::read(buffer, size, crop);
+ FlattenableUtils::read(buffer, size, scalingMode);
+ FlattenableUtils::read(buffer, size, transform);
+ FlattenableUtils::read(buffer, size, stickyTransform);
+ FlattenableUtils::read(buffer, size, getFrameTimestamps);
+
+ fence = new Fence();
+ status_t result = fence->unflatten(buffer, size, fds, count);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = surfaceDamage.unflatten(buffer, size);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ FlattenableUtils::advance(buffer, size, surfaceDamage.getFlattenedSize());
+ result = hdrMetadata.unflatten(buffer, size);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ FlattenableUtils::advance(buffer, size, hdrMetadata.getFlattenedSize());
+ FlattenableUtils::read(buffer, size, slot);
+ return NO_ERROR;
+}
+
+////////////////////////////////////////////////////////////////////////
+constexpr size_t IGraphicBufferProducer::QueueBufferOutput::minFlattenedSize() {
+ return sizeof(width) + sizeof(height) + sizeof(transformHint) + sizeof(numPendingBuffers) +
+ sizeof(nextFrameNumber) + sizeof(bufferReplaced) + sizeof(maxBufferCount) +
+ sizeof(result);
+}
+size_t IGraphicBufferProducer::QueueBufferOutput::getFlattenedSize() const {
+ return minFlattenedSize() + frameTimestamps.getFlattenedSize();
+}
+
+size_t IGraphicBufferProducer::QueueBufferOutput::getFdCount() const {
+ return frameTimestamps.getFdCount();
+}
+
+status_t IGraphicBufferProducer::QueueBufferOutput::flatten(
+ void*& buffer, size_t& size, int*& fds, size_t& count) const
+{
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::write(buffer, size, width);
+ FlattenableUtils::write(buffer, size, height);
+ FlattenableUtils::write(buffer, size, transformHint);
+ FlattenableUtils::write(buffer, size, numPendingBuffers);
+ FlattenableUtils::write(buffer, size, nextFrameNumber);
+ FlattenableUtils::write(buffer, size, bufferReplaced);
+ FlattenableUtils::write(buffer, size, maxBufferCount);
+
+ status_t result = frameTimestamps.flatten(buffer, size, fds, count);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ FlattenableUtils::write(buffer, size, result);
+ return NO_ERROR;
+}
+
+status_t IGraphicBufferProducer::QueueBufferOutput::unflatten(
+ void const*& buffer, size_t& size, int const*& fds, size_t& count)
+{
+ if (size < minFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::read(buffer, size, width);
+ FlattenableUtils::read(buffer, size, height);
+ FlattenableUtils::read(buffer, size, transformHint);
+ FlattenableUtils::read(buffer, size, numPendingBuffers);
+ FlattenableUtils::read(buffer, size, nextFrameNumber);
+ FlattenableUtils::read(buffer, size, bufferReplaced);
+ FlattenableUtils::read(buffer, size, maxBufferCount);
+
+ status_t result = frameTimestamps.unflatten(buffer, size, fds, count);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ FlattenableUtils::read(buffer, size, result);
+ return NO_ERROR;
+}
+
+////////////////////////////////////////////////////////////////////////
+constexpr size_t IGraphicBufferProducer::RequestBufferOutput::minFlattenedSize() {
+ return sizeof(result) +
+ sizeof(int32_t); // IsBufferNull
+}
+
+size_t IGraphicBufferProducer::RequestBufferOutput::getFlattenedSize() const {
+ return minFlattenedSize() + (buffer == nullptr ? 0 : buffer->getFlattenedSize());
+}
+
+size_t IGraphicBufferProducer::RequestBufferOutput::getFdCount() const {
+ return (buffer == nullptr ? 0 : buffer->getFdCount());
+}
+
+status_t IGraphicBufferProducer::RequestBufferOutput::flatten(
+ void*& fBuffer, size_t& size, int*& fds, size_t& count) const {
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::write(fBuffer, size, result);
+ const int32_t isBufferNull = (buffer == nullptr ? 1 : 0);
+ FlattenableUtils::write(fBuffer, size, isBufferNull);
+
+ if (!isBufferNull) {
+ status_t status = buffer->flatten(fBuffer, size, fds, count);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ }
+ return NO_ERROR;
+}
+
+status_t IGraphicBufferProducer::RequestBufferOutput::unflatten(
+ void const*& fBuffer, size_t& size, int const*& fds, size_t& count) {
+ if (size < minFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::read(fBuffer, size, result);
+ int32_t isBufferNull = 0;
+ FlattenableUtils::read(fBuffer, size, isBufferNull);
+ buffer = new GraphicBuffer();
+ if (!isBufferNull) {
+ status_t status = buffer->unflatten(fBuffer, size, fds, count);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ }
+ return NO_ERROR;
+}
+
+////////////////////////////////////////////////////////////////////////
+
+size_t IGraphicBufferProducer::DequeueBufferInput::getFlattenedSize() const {
+ return sizeof(width) + sizeof(height) + sizeof(format) + sizeof(usage) +
+ sizeof(int32_t/*getTimestamps*/);
+}
+
+status_t IGraphicBufferProducer::DequeueBufferInput::flatten(void* buffer, size_t size) const {
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+ FlattenableUtils::write(buffer, size, width);
+ FlattenableUtils::write(buffer, size, height);
+ FlattenableUtils::write(buffer, size, format);
+ FlattenableUtils::write(buffer, size, usage);
+ const int32_t getTimestampsInt = (getTimestamps ? 1 : 0);
+ FlattenableUtils::write(buffer, size, getTimestampsInt);
+
+ return NO_ERROR;
+}
+
+status_t IGraphicBufferProducer::DequeueBufferInput::unflatten(void const* buffer, size_t size) {
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::read(buffer, size, width);
+ FlattenableUtils::read(buffer, size, height);
+ FlattenableUtils::read(buffer, size, format);
+ FlattenableUtils::read(buffer, size, usage);
+ int32_t getTimestampsInt = 0;
+ FlattenableUtils::read(buffer, size, getTimestampsInt);
+ getTimestamps = (getTimestampsInt == 1);
+
+ return NO_ERROR;
+}
+
+////////////////////////////////////////////////////////////////////////
+
+constexpr size_t IGraphicBufferProducer::DequeueBufferOutput::minFlattenedSize() {
+ return sizeof(result) + sizeof(slot) + sizeof(bufferAge) + sizeof(int32_t/*hasTimestamps*/);
+}
+
+size_t IGraphicBufferProducer::DequeueBufferOutput::getFlattenedSize() const {
+ return minFlattenedSize() +
+ fence->getFlattenedSize() +
+ (timestamps.has_value() ? timestamps->getFlattenedSize() : 0);
+}
+
+size_t IGraphicBufferProducer::DequeueBufferOutput::getFdCount() const {
+ return fence->getFdCount() +
+ (timestamps.has_value() ? timestamps->getFdCount() : 0);
+}
+
+status_t IGraphicBufferProducer::DequeueBufferOutput::flatten(
+ void*& buffer, size_t& size, int*& fds, size_t& count) const {
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::write(buffer, size, result);
+ FlattenableUtils::write(buffer, size, slot);
+ FlattenableUtils::write(buffer, size, bufferAge);
+ status_t status = fence->flatten(buffer, size, fds, count);
+ if (status != NO_ERROR) {
+ return result;
+ }
+ const int32_t hasTimestamps = timestamps.has_value() ? 1 : 0;
+ FlattenableUtils::write(buffer, size, hasTimestamps);
+ if (timestamps.has_value()) {
+ status = timestamps->flatten(buffer, size, fds, count);
+ }
+ return status;
+}
+
+status_t IGraphicBufferProducer::DequeueBufferOutput::unflatten(
+ void const*& buffer, size_t& size, int const*& fds, size_t& count) {
+ if (size < minFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::read(buffer, size, result);
+ FlattenableUtils::read(buffer, size, slot);
+ FlattenableUtils::read(buffer, size, bufferAge);
+
+ fence = new Fence();
+ status_t status = fence->unflatten(buffer, size, fds, count);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ int32_t hasTimestamps = 0;
+ FlattenableUtils::read(buffer, size, hasTimestamps);
+ if (hasTimestamps) {
+ timestamps.emplace();
+ status = timestamps->unflatten(buffer, size, fds, count);
+ }
+ return status;
+}
+
+////////////////////////////////////////////////////////////////////////
+
+size_t IGraphicBufferProducer::AttachBufferOutput::getFlattenedSize() const {
+ return sizeof(result) + sizeof(slot);
+}
+
+status_t IGraphicBufferProducer::AttachBufferOutput::flatten(void* buffer, size_t size) const {
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+ FlattenableUtils::write(buffer, size, result);
+ FlattenableUtils::write(buffer, size, slot);
+
+ return NO_ERROR;
+}
+
+status_t IGraphicBufferProducer::AttachBufferOutput::unflatten(void const* buffer, size_t size) {
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+ FlattenableUtils::read(buffer, size, result);
+ FlattenableUtils::read(buffer, size, slot);
+
+ return NO_ERROR;
+}
+
+////////////////////////////////////////////////////////////////////////
+
+constexpr size_t IGraphicBufferProducer::CancelBufferInput::minFlattenedSize() {
+ return sizeof(slot);
+}
+
+size_t IGraphicBufferProducer::CancelBufferInput::getFlattenedSize() const {
+ return minFlattenedSize() + fence->getFlattenedSize();
+}
+
+size_t IGraphicBufferProducer::CancelBufferInput::getFdCount() const {
+ return fence->getFdCount();
+}
+
+status_t IGraphicBufferProducer::CancelBufferInput::flatten(
+ void*& buffer, size_t& size, int*& fds, size_t& count) const {
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::write(buffer, size, slot);
+ return fence->flatten(buffer, size, fds, count);
+}
+
+status_t IGraphicBufferProducer::CancelBufferInput::unflatten(
+ void const*& buffer, size_t& size, int const*& fds, size_t& count) {
+ if (size < minFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::read(buffer, size, slot);
+
+ fence = new Fence();
+ return fence->unflatten(buffer, size, fds, count);
+}
+
+////////////////////////////////////////////////////////////////////////
+
+size_t IGraphicBufferProducer::QueryOutput::getFlattenedSize() const {
+ return sizeof(result) + sizeof(value);
+}
+
+status_t IGraphicBufferProducer::QueryOutput::flatten(void* buffer, size_t size) const {
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+ FlattenableUtils::write(buffer, size, result);
+ FlattenableUtils::write(buffer, size, value);
+
+ return NO_ERROR;
+}
+
+status_t IGraphicBufferProducer::QueryOutput::unflatten(void const* buffer, size_t size) {
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+ FlattenableUtils::read(buffer, size, result);
+ FlattenableUtils::read(buffer, size, value);
+
+ return NO_ERROR;
+}
+
+} // namespace android
diff --git a/libs/gui/QueueBufferInputOutput.cpp b/libs/gui/QueueBufferInputOutput.cpp
deleted file mode 100644
index 30f0ef6..0000000
--- a/libs/gui/QueueBufferInputOutput.cpp
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Copyright 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <inttypes.h>
-
-#define LOG_TAG "QueueBufferInputOutput"
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-//#define LOG_NDEBUG 0
-
-#include <gui/IGraphicBufferProducer.h>
-
-namespace android {
-
-constexpr size_t IGraphicBufferProducer::QueueBufferInput::minFlattenedSize() {
- return sizeof(timestamp) +
- sizeof(isAutoTimestamp) +
- sizeof(dataSpace) +
- sizeof(crop) +
- sizeof(scalingMode) +
- sizeof(transform) +
- sizeof(stickyTransform) +
- sizeof(getFrameTimestamps);
-}
-
-IGraphicBufferProducer::QueueBufferInput::QueueBufferInput(const Parcel& parcel) {
- parcel.read(*this);
-}
-
-size_t IGraphicBufferProducer::QueueBufferInput::getFlattenedSize() const {
- return minFlattenedSize() +
- fence->getFlattenedSize() +
- surfaceDamage.getFlattenedSize() +
- hdrMetadata.getFlattenedSize();
-}
-
-size_t IGraphicBufferProducer::QueueBufferInput::getFdCount() const {
- return fence->getFdCount();
-}
-
-status_t IGraphicBufferProducer::QueueBufferInput::flatten(
- void*& buffer, size_t& size, int*& fds, size_t& count) const
-{
- if (size < getFlattenedSize()) {
- return NO_MEMORY;
- }
-
- FlattenableUtils::write(buffer, size, timestamp);
- FlattenableUtils::write(buffer, size, isAutoTimestamp);
- FlattenableUtils::write(buffer, size, dataSpace);
- FlattenableUtils::write(buffer, size, crop);
- FlattenableUtils::write(buffer, size, scalingMode);
- FlattenableUtils::write(buffer, size, transform);
- FlattenableUtils::write(buffer, size, stickyTransform);
- FlattenableUtils::write(buffer, size, getFrameTimestamps);
-
- status_t result = fence->flatten(buffer, size, fds, count);
- if (result != NO_ERROR) {
- return result;
- }
- result = surfaceDamage.flatten(buffer, size);
- if (result != NO_ERROR) {
- return result;
- }
- FlattenableUtils::advance(buffer, size, surfaceDamage.getFlattenedSize());
- return hdrMetadata.flatten(buffer, size);
-}
-
-status_t IGraphicBufferProducer::QueueBufferInput::unflatten(
- void const*& buffer, size_t& size, int const*& fds, size_t& count)
-{
- if (size < minFlattenedSize()) {
- return NO_MEMORY;
- }
-
- FlattenableUtils::read(buffer, size, timestamp);
- FlattenableUtils::read(buffer, size, isAutoTimestamp);
- FlattenableUtils::read(buffer, size, dataSpace);
- FlattenableUtils::read(buffer, size, crop);
- FlattenableUtils::read(buffer, size, scalingMode);
- FlattenableUtils::read(buffer, size, transform);
- FlattenableUtils::read(buffer, size, stickyTransform);
- FlattenableUtils::read(buffer, size, getFrameTimestamps);
-
- fence = new Fence();
- status_t result = fence->unflatten(buffer, size, fds, count);
- if (result != NO_ERROR) {
- return result;
- }
- result = surfaceDamage.unflatten(buffer, size);
- if (result != NO_ERROR) {
- return result;
- }
- FlattenableUtils::advance(buffer, size, surfaceDamage.getFlattenedSize());
- return hdrMetadata.unflatten(buffer, size);
-}
-
-////////////////////////////////////////////////////////////////////////
-constexpr size_t IGraphicBufferProducer::QueueBufferOutput::minFlattenedSize() {
- return sizeof(width) + sizeof(height) + sizeof(transformHint) + sizeof(numPendingBuffers) +
- sizeof(nextFrameNumber) + sizeof(bufferReplaced) + sizeof(maxBufferCount);
-}
-size_t IGraphicBufferProducer::QueueBufferOutput::getFlattenedSize() const {
- return minFlattenedSize() + frameTimestamps.getFlattenedSize();
-}
-
-size_t IGraphicBufferProducer::QueueBufferOutput::getFdCount() const {
- return frameTimestamps.getFdCount();
-}
-
-status_t IGraphicBufferProducer::QueueBufferOutput::flatten(
- void*& buffer, size_t& size, int*& fds, size_t& count) const
-{
- if (size < getFlattenedSize()) {
- return NO_MEMORY;
- }
-
- FlattenableUtils::write(buffer, size, width);
- FlattenableUtils::write(buffer, size, height);
- FlattenableUtils::write(buffer, size, transformHint);
- FlattenableUtils::write(buffer, size, numPendingBuffers);
- FlattenableUtils::write(buffer, size, nextFrameNumber);
- FlattenableUtils::write(buffer, size, bufferReplaced);
- FlattenableUtils::write(buffer, size, maxBufferCount);
-
- return frameTimestamps.flatten(buffer, size, fds, count);
-}
-
-status_t IGraphicBufferProducer::QueueBufferOutput::unflatten(
- void const*& buffer, size_t& size, int const*& fds, size_t& count)
-{
- if (size < minFlattenedSize()) {
- return NO_MEMORY;
- }
-
- FlattenableUtils::read(buffer, size, width);
- FlattenableUtils::read(buffer, size, height);
- FlattenableUtils::read(buffer, size, transformHint);
- FlattenableUtils::read(buffer, size, numPendingBuffers);
- FlattenableUtils::read(buffer, size, nextFrameNumber);
- FlattenableUtils::read(buffer, size, bufferReplaced);
- FlattenableUtils::read(buffer, size, maxBufferCount);
-
- return frameTimestamps.unflatten(buffer, size, fds, count);
-}
-
-} // namespace android
diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h
index 45e0a13..c3b9262 100644
--- a/libs/gui/include/gui/IGraphicBufferProducer.h
+++ b/libs/gui/include/gui/IGraphicBufferProducer.h
@@ -38,6 +38,9 @@
#include <android/hardware/graphics/bufferqueue/1.0/IGraphicBufferProducer.h>
#include <android/hardware/graphics/bufferqueue/2.0/IGraphicBufferProducer.h>
+#include <optional>
+#include <vector>
+
namespace android {
// ----------------------------------------------------------------------------
@@ -289,8 +292,9 @@
const sp<GraphicBuffer>& buffer) = 0;
struct QueueBufferInput : public Flattenable<QueueBufferInput> {
- friend class Flattenable<QueueBufferInput>;
- explicit inline QueueBufferInput(const Parcel& parcel);
+ explicit inline QueueBufferInput(const Parcel& parcel) {
+ parcel.read(*this);
+ }
// timestamp - a monotonically increasing value in nanoseconds
// isAutoTimestamp - if the timestamp was synthesized at queue time
@@ -304,21 +308,29 @@
// camera mode).
// getFrameTimestamps - whether or not the latest frame timestamps
// should be retrieved from the consumer.
+ // slot - the slot index to queue. This is used only by queueBuffers().
+ // queueBuffer() ignores this value and uses the argument `slot`
+ // instead.
inline QueueBufferInput(int64_t _timestamp, bool _isAutoTimestamp,
android_dataspace _dataSpace, const Rect& _crop,
int _scalingMode, uint32_t _transform, const sp<Fence>& _fence,
- uint32_t _sticky = 0, bool _getFrameTimestamps = false)
+ uint32_t _sticky = 0, bool _getFrameTimestamps = false,
+ int _slot = -1)
: timestamp(_timestamp), isAutoTimestamp(_isAutoTimestamp),
dataSpace(_dataSpace), crop(_crop), scalingMode(_scalingMode),
- transform(_transform), stickyTransform(_sticky), fence(_fence),
- surfaceDamage(), getFrameTimestamps(_getFrameTimestamps) { }
+ transform(_transform), stickyTransform(_sticky),
+ fence(_fence), surfaceDamage(),
+ getFrameTimestamps(_getFrameTimestamps), slot(_slot) { }
+
+ QueueBufferInput() = default;
inline void deflate(int64_t* outTimestamp, bool* outIsAutoTimestamp,
android_dataspace* outDataSpace,
Rect* outCrop, int* outScalingMode,
uint32_t* outTransform, sp<Fence>* outFence,
uint32_t* outStickyTransform = nullptr,
- bool* outGetFrameTimestamps = nullptr) const {
+ bool* outGetFrameTimestamps = nullptr,
+ int* outSlot = nullptr) const {
*outTimestamp = timestamp;
*outIsAutoTimestamp = bool(isAutoTimestamp);
*outDataSpace = dataSpace;
@@ -332,6 +344,9 @@
if (outGetFrameTimestamps) {
*outGetFrameTimestamps = getFrameTimestamps;
}
+ if (outSlot) {
+ *outSlot = slot;
+ }
}
// Flattenable protocol
@@ -357,6 +372,7 @@
sp<Fence> fence;
Region surfaceDamage;
bool getFrameTimestamps{false};
+ int slot{-1};
HdrMetadata hdrMetadata;
};
@@ -385,6 +401,7 @@
FrameEventHistoryDelta frameTimestamps;
bool bufferReplaced{false};
int maxBufferCount{0};
+ status_t result{NO_ERROR};
};
// queueBuffer indicates that the client has finished filling in the
@@ -404,6 +421,10 @@
// Upon success, the output will be filled with meaningful values
// (refer to the documentation below).
//
+ // Note: QueueBufferInput::slot was added to QueueBufferInput to be used by
+ // queueBuffers(), the batched version of queueBuffer(). The non-batched
+ // method (queueBuffer()) uses `slot` and ignores `input.slot`.
+ //
// 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
// connected.
@@ -639,6 +660,147 @@
// the width and height used for dequeueBuffer will be additionally swapped.
virtual status_t setAutoPrerotation(bool autoPrerotation);
+ struct RequestBufferOutput : public Flattenable<RequestBufferOutput> {
+ RequestBufferOutput() = default;
+
+ // Flattenable protocol
+ static constexpr size_t minFlattenedSize();
+ size_t getFlattenedSize() const;
+ size_t getFdCount() const;
+ status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const;
+ status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count);
+
+ status_t result;
+ sp<GraphicBuffer> buffer;
+ };
+
+ // Batched version of requestBuffer().
+ // This method behaves like a sequence of requestBuffer() calls.
+ // The return value of the batched method will only be about the
+ // transaction. For a local call, the return value will always be NO_ERROR.
+ virtual status_t requestBuffers(
+ const std::vector<int32_t>& slots,
+ std::vector<RequestBufferOutput>* outputs);
+
+ struct DequeueBufferInput : public LightFlattenable<DequeueBufferInput> {
+ DequeueBufferInput() = default;
+
+ // LightFlattenable protocol
+ inline bool isFixedSize() const { return true; }
+ size_t getFlattenedSize() const;
+ status_t flatten(void* buffer, size_t size) const;
+ status_t unflatten(void const* buffer, size_t size);
+
+ uint32_t width;
+ uint32_t height;
+ PixelFormat format;
+ uint64_t usage;
+ bool getTimestamps;
+ };
+
+ struct DequeueBufferOutput : public Flattenable<DequeueBufferOutput> {
+ DequeueBufferOutput() = default;
+
+ // Flattenable protocol
+ static constexpr size_t minFlattenedSize();
+ size_t getFlattenedSize() const;
+ size_t getFdCount() const;
+ status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const;
+ status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count);
+
+ status_t result;
+ int slot = -1;
+ sp<Fence> fence = Fence::NO_FENCE;
+ uint64_t bufferAge;
+ std::optional<FrameEventHistoryDelta> timestamps;
+ };
+
+ // Batched version of dequeueBuffer().
+ // This method behaves like a sequence of dequeueBuffer() calls.
+ // The return value of the batched method will only be about the
+ // transaction. For a local call, the return value will always be NO_ERROR.
+ virtual status_t dequeueBuffers(
+ const std::vector<DequeueBufferInput>& inputs,
+ std::vector<DequeueBufferOutput>* outputs);
+
+ // Batched version of detachBuffer().
+ // This method behaves like a sequence of detachBuffer() calls.
+ // The return value of the batched method will only be about the
+ // transaction. For a local call, the return value will always be NO_ERROR.
+ virtual status_t detachBuffers(const std::vector<int32_t>& slots,
+ std::vector<status_t>* results);
+
+
+ struct AttachBufferOutput : public LightFlattenable<AttachBufferOutput> {
+ AttachBufferOutput() = default;
+
+ // LightFlattenable protocol
+ inline bool isFixedSize() const { return true; }
+ size_t getFlattenedSize() const;
+ status_t flatten(void* buffer, size_t size) const;
+ status_t unflatten(void const* buffer, size_t size);
+
+ status_t result;
+ int slot;
+ };
+ // Batched version of attachBuffer().
+ // This method behaves like a sequence of attachBuffer() calls.
+ // The return value of the batched method will only be about the
+ // transaction. For a local call, the return value will always be NO_ERROR.
+ virtual status_t attachBuffers(
+ const std::vector<sp<GraphicBuffer>>& buffers,
+ std::vector<AttachBufferOutput>* outputs);
+
+ // Batched version of queueBuffer().
+ // This method behaves like a sequence of queueBuffer() calls.
+ // The return value of the batched method will only be about the
+ // transaction. For a local call, the return value will always be NO_ERROR.
+ //
+ // Note: QueueBufferInput::slot was added to QueueBufferInput to include the
+ // `slot` input argument of the non-batched method queueBuffer().
+ virtual status_t queueBuffers(const std::vector<QueueBufferInput>& inputs,
+ std::vector<QueueBufferOutput>* outputs);
+
+ struct CancelBufferInput : public Flattenable<CancelBufferInput> {
+ CancelBufferInput() = default;
+
+ // Flattenable protocol
+ static constexpr size_t minFlattenedSize();
+ size_t getFlattenedSize() const;
+ size_t getFdCount() const;
+ status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const;
+ status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count);
+
+ int slot;
+ sp<Fence> fence;
+ };
+ // Batched version of cancelBuffer().
+ // This method behaves like a sequence of cancelBuffer() calls.
+ // The return value of the batched method will only be about the
+ // transaction. For a local call, the return value will always be NO_ERROR.
+ virtual status_t cancelBuffers(
+ const std::vector<CancelBufferInput>& inputs,
+ std::vector<status_t>* results);
+
+ struct QueryOutput : public LightFlattenable<QueryOutput> {
+ QueryOutput() = default;
+
+ // LightFlattenable protocol
+ inline bool isFixedSize() const { return true; }
+ size_t getFlattenedSize() const;
+ status_t flatten(void* buffer, size_t size) const;
+ status_t unflatten(void const* buffer, size_t size);
+
+ status_t result;
+ int64_t value;
+ };
+ // Batched version of query().
+ // This method behaves like a sequence of query() calls.
+ // The return value of the batched method will only be about the
+ // transaction. For a local call, the return value will always be NO_ERROR.
+ virtual status_t query(const std::vector<int32_t> inputs,
+ std::vector<QueryOutput>* outputs);
+
#ifndef NO_BINDER
// Static method exports any IGraphicBufferProducer object to a parcel. It
// handles null producer as well.
diff --git a/libs/gui/tests/IGraphicBufferProducer_test.cpp b/libs/gui/tests/IGraphicBufferProducer_test.cpp
index 15bd32d..2af2fe1 100644
--- a/libs/gui/tests/IGraphicBufferProducer_test.cpp
+++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp
@@ -70,6 +70,9 @@
const int QUEUE_BUFFER_INPUT_SCALING_MODE = 0;
const int QUEUE_BUFFER_INPUT_TRANSFORM = 0;
const sp<Fence> QUEUE_BUFFER_INPUT_FENCE = Fence::NO_FENCE;
+ const uint32_t QUEUE_BUFFER_INPUT_STICKY_TRANSFORM = 0;
+ const bool QUEUE_BUFFER_INPUT_GET_TIMESTAMPS = 0;
+ const int QUEUE_BUFFER_INPUT_SLOT = -1;
// Enums to control which IGraphicBufferProducer backend to test.
enum IGraphicBufferProducerTestCode {
@@ -156,6 +159,9 @@
scalingMode = QUEUE_BUFFER_INPUT_SCALING_MODE;
transform = QUEUE_BUFFER_INPUT_TRANSFORM;
fence = QUEUE_BUFFER_INPUT_FENCE;
+ stickyTransform = QUEUE_BUFFER_INPUT_STICKY_TRANSFORM;
+ getTimestamps = QUEUE_BUFFER_INPUT_GET_TIMESTAMPS;
+ slot = QUEUE_BUFFER_INPUT_SLOT;
}
IGraphicBufferProducer::QueueBufferInput build() {
@@ -166,7 +172,10 @@
crop,
scalingMode,
transform,
- fence);
+ fence,
+ stickyTransform,
+ getTimestamps,
+ slot);
}
QueueBufferInputBuilder& setTimestamp(int64_t timestamp) {
@@ -204,6 +213,21 @@
return *this;
}
+ QueueBufferInputBuilder& setStickyTransform(uint32_t stickyTransform) {
+ this->stickyTransform = stickyTransform;
+ return *this;
+ }
+
+ QueueBufferInputBuilder& setGetTimestamps(bool getTimestamps) {
+ this->getTimestamps = getTimestamps;
+ return *this;
+ }
+
+ QueueBufferInputBuilder& setSlot(int slot) {
+ this->slot = slot;
+ return *this;
+ }
+
private:
int64_t timestamp;
bool isAutoTimestamp;
@@ -212,17 +236,17 @@
int scalingMode;
uint32_t transform;
sp<Fence> fence;
+ uint32_t stickyTransform;
+ bool getTimestamps;
+ int slot;
}; // struct QueueBufferInputBuilder
- // To easily store dequeueBuffer results into containers
- struct DequeueBufferResult {
- int slot;
- sp<Fence> fence;
- };
-
- status_t dequeueBuffer(uint32_t w, uint32_t h, uint32_t format, uint32_t usage, DequeueBufferResult* result) {
- return mProducer->dequeueBuffer(&result->slot, &result->fence, w, h, format, usage,
- nullptr, nullptr);
+ status_t dequeueBuffer(uint32_t w, uint32_t h, uint32_t format, uint32_t usage,
+ IGraphicBufferProducer::DequeueBufferOutput* result) {
+ result->result =
+ mProducer->dequeueBuffer(&result->slot, &result->fence, w, h, format, usage,
+ &result->bufferAge, nullptr);
+ return result->result;
}
void setupDequeueRequestBuffer(int *slot, sp<Fence> *fence,
@@ -336,6 +360,27 @@
EXPECT_OK(mProducer->query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, &value));
EXPECT_EQ(DEFAULT_CONSUMER_USAGE_BITS, value);
+ { // Test the batched version
+ std::vector<int32_t> inputs = {
+ NATIVE_WINDOW_WIDTH,
+ NATIVE_WINDOW_HEIGHT,
+ NATIVE_WINDOW_FORMAT,
+ NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+ NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND,
+ NATIVE_WINDOW_CONSUMER_USAGE_BITS };
+ using QueryOutput = IGraphicBufferProducer::QueryOutput;
+ std::vector<QueryOutput> outputs;
+ EXPECT_OK(mProducer->query(inputs, &outputs));
+ EXPECT_EQ(DEFAULT_WIDTH, static_cast<uint32_t>(outputs[0].value));
+ EXPECT_EQ(DEFAULT_HEIGHT, static_cast<uint32_t>(outputs[1].value));
+ EXPECT_EQ(DEFAULT_FORMAT, outputs[2].value);
+ EXPECT_LE(0, outputs[3].value);
+ EXPECT_FALSE(outputs[4].value);
+ EXPECT_EQ(DEFAULT_CONSUMER_USAGE_BITS, outputs[5].value);
+ for (const QueryOutput& output : outputs) {
+ EXPECT_OK(output.result);
+ }
+ }
}
TEST_P(IGraphicBufferProducerTest, Query_ReturnsError) {
@@ -358,6 +403,24 @@
EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_TRANSFORM_HINT, &value));
// TODO: Consider documented the above enums as unsupported or make a new enum for IGBP
+ { // Test the batched version
+ std::vector<int32_t> inputs = {
+ -1,
+ static_cast<int32_t>(0xDEADBEEF),
+ NATIVE_WINDOW_QUERY_LAST_OFF_BY_ONE,
+ NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER,
+ NATIVE_WINDOW_CONCRETE_TYPE,
+ NATIVE_WINDOW_DEFAULT_WIDTH,
+ NATIVE_WINDOW_DEFAULT_HEIGHT,
+ NATIVE_WINDOW_TRANSFORM_HINT};
+ using QueryOutput = IGraphicBufferProducer::QueryOutput;
+ std::vector<QueryOutput> outputs;
+ EXPECT_OK(mProducer->query(inputs, &outputs));
+ for (const QueryOutput& output : outputs) {
+ EXPECT_EQ(BAD_VALUE, output.result);
+ }
+ }
+
// Value was NULL
EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_FORMAT, /*value*/nullptr));
@@ -416,29 +479,113 @@
// Buffer was not in the dequeued state
EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output));
+
+ { // Test batched methods
+ constexpr size_t BATCH_SIZE = 4;
+
+ ASSERT_OK(mProducer->setMaxDequeuedBufferCount(BATCH_SIZE));
+ // Dequeue
+ using DequeueBufferInput = IGraphicBufferProducer::DequeueBufferInput;
+ using DequeueBufferOutput = IGraphicBufferProducer::DequeueBufferOutput;
+ DequeueBufferInput dequeueInput;
+ dequeueInput.width = DEFAULT_WIDTH;
+ dequeueInput.height = DEFAULT_HEIGHT;
+ dequeueInput.format = DEFAULT_FORMAT;
+ dequeueInput.usage = TEST_PRODUCER_USAGE_BITS;
+ dequeueInput.getTimestamps = false;
+ std::vector<DequeueBufferInput> dequeueInputs(BATCH_SIZE, dequeueInput);
+ std::vector<DequeueBufferOutput> dequeueOutputs;
+ EXPECT_OK(mProducer->dequeueBuffers(dequeueInputs, &dequeueOutputs));
+ ASSERT_EQ(dequeueInputs.size(), dequeueOutputs.size());
+
+ // Request
+ std::vector<int32_t> requestInputs;
+ requestInputs.reserve(BATCH_SIZE);
+ for (const DequeueBufferOutput& dequeueOutput : dequeueOutputs) {
+ ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
+ dequeueOutput.result);
+ requestInputs.emplace_back(dequeueOutput.slot);
+ }
+ using RequestBufferOutput = IGraphicBufferProducer::RequestBufferOutput;
+ std::vector<RequestBufferOutput> requestOutputs;
+ EXPECT_OK(mProducer->requestBuffers(requestInputs, &requestOutputs));
+ ASSERT_EQ(requestInputs.size(), requestOutputs.size());
+ for (const RequestBufferOutput& requestOutput : requestOutputs) {
+ EXPECT_OK(requestOutput.result);
+ }
+
+ // Queue
+ using QueueBufferInput = IGraphicBufferProducer::QueueBufferInput;
+ using QueueBufferOutput = IGraphicBufferProducer::QueueBufferOutput;
+ std::vector<QueueBufferInput> queueInputs;
+ queueInputs.reserve(BATCH_SIZE);
+ for (const DequeueBufferOutput& dequeueOutput : dequeueOutputs) {
+ queueInputs.emplace_back(CreateBufferInput()).slot =
+ dequeueOutput.slot;
+ }
+ std::vector<QueueBufferOutput> queueOutputs;
+ EXPECT_OK(mProducer->queueBuffers(queueInputs, &queueOutputs));
+ ASSERT_EQ(queueInputs.size(), queueOutputs.size());
+ for (const QueueBufferOutput& queueOutput : queueOutputs) {
+ EXPECT_OK(queueOutput.result);
+ }
+
+ // Re-queue
+ EXPECT_OK(mProducer->queueBuffers(queueInputs, &queueOutputs));
+ ASSERT_EQ(queueInputs.size(), queueOutputs.size());
+ for (const QueueBufferOutput& queueOutput : queueOutputs) {
+ EXPECT_EQ(BAD_VALUE, queueOutput.result);
+ }
+ }
}
TEST_P(IGraphicBufferProducerTest, Queue_ReturnsError) {
ASSERT_NO_FATAL_FAILURE(ConnectProducer());
+ using QueueBufferInput = IGraphicBufferProducer::QueueBufferInput;
+ using QueueBufferOutput = IGraphicBufferProducer::QueueBufferOutput;
// Invalid slot number
{
// A generic "valid" input
- IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
- IGraphicBufferProducer::QueueBufferOutput output;
+ QueueBufferInput input = CreateBufferInput();
+ QueueBufferOutput output;
EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(/*slot*/-1, input, &output));
EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(/*slot*/0xDEADBEEF, input, &output));
EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(BufferQueue::NUM_BUFFER_SLOTS,
input, &output));
+
+ { // Test with the batched version
+ constexpr size_t BATCH_SIZE = 16;
+ input.slot = -1;
+ std::vector<QueueBufferInput> inputs(BATCH_SIZE, input);
+ std::vector<QueueBufferOutput> outputs;
+ EXPECT_OK(mProducer->queueBuffers(inputs, &outputs));
+ ASSERT_EQ(inputs.size(), outputs.size());
+ for (const QueueBufferOutput& output : outputs) {
+ EXPECT_EQ(BAD_VALUE, output.result);
+ }
+ }
}
// Slot was not in the dequeued state (all slots start out in Free state)
{
- IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
- IGraphicBufferProducer::QueueBufferOutput output;
+ QueueBufferInput input = CreateBufferInput();
+ QueueBufferOutput output;
EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(/*slot*/0, input, &output));
+
+ { // Test with the batched version
+ constexpr size_t BATCH_SIZE = 16;
+ input.slot = 0;
+ std::vector<QueueBufferInput> inputs(BATCH_SIZE, input);
+ std::vector<QueueBufferOutput> outputs;
+ EXPECT_OK(mProducer->queueBuffers(inputs, &outputs));
+ ASSERT_EQ(inputs.size(), outputs.size());
+ for (const QueueBufferOutput& output : outputs) {
+ EXPECT_EQ(BAD_VALUE, output.result);
+ }
+ }
}
// Put the slot into the "dequeued" state for the rest of the test
@@ -453,10 +600,22 @@
// Slot was enqueued without requesting a buffer
{
- IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
- IGraphicBufferProducer::QueueBufferOutput output;
+ QueueBufferInput input = CreateBufferInput();
+ QueueBufferOutput output;
EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output));
+
+ { // Test with the batched version
+ constexpr size_t BATCH_SIZE = 16;
+ input.slot = dequeuedSlot;
+ std::vector<QueueBufferInput> inputs(BATCH_SIZE, input);
+ std::vector<QueueBufferOutput> outputs;
+ EXPECT_OK(mProducer->queueBuffers(inputs, &outputs));
+ ASSERT_EQ(inputs.size(), outputs.size());
+ for (const QueueBufferOutput& output : outputs) {
+ EXPECT_EQ(BAD_VALUE, output.result);
+ }
+ }
}
// Request the buffer so that the rest of the tests don't fail on earlier checks.
@@ -467,11 +626,23 @@
{
sp<Fence> nullFence = nullptr;
- IGraphicBufferProducer::QueueBufferInput input =
+ QueueBufferInput input =
QueueBufferInputBuilder().setFence(nullFence).build();
- IGraphicBufferProducer::QueueBufferOutput output;
+ QueueBufferOutput output;
EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output));
+
+ { // Test with the batched version
+ constexpr size_t BATCH_SIZE = 16;
+ input.slot = dequeuedSlot;
+ std::vector<QueueBufferInput> inputs(BATCH_SIZE, input);
+ std::vector<QueueBufferOutput> outputs;
+ EXPECT_OK(mProducer->queueBuffers(inputs, &outputs));
+ ASSERT_EQ(inputs.size(), outputs.size());
+ for (const QueueBufferOutput& output : outputs) {
+ EXPECT_EQ(BAD_VALUE, output.result);
+ }
+ }
}
// Scaling mode was unknown
@@ -482,9 +653,33 @@
EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output));
+ { // Test with the batched version
+ constexpr size_t BATCH_SIZE = 16;
+ input.slot = dequeuedSlot;
+ std::vector<QueueBufferInput> inputs(BATCH_SIZE, input);
+ std::vector<QueueBufferOutput> outputs;
+ EXPECT_OK(mProducer->queueBuffers(inputs, &outputs));
+ ASSERT_EQ(inputs.size(), outputs.size());
+ for (const QueueBufferOutput& output : outputs) {
+ EXPECT_EQ(BAD_VALUE, output.result);
+ }
+ }
+
input = QueueBufferInputBuilder().setScalingMode(0xDEADBEEF).build();
EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output));
+
+ { // Test with the batched version
+ constexpr size_t BATCH_SIZE = 16;
+ input.slot = dequeuedSlot;
+ std::vector<QueueBufferInput> inputs(BATCH_SIZE, input);
+ std::vector<QueueBufferOutput> outputs;
+ EXPECT_OK(mProducer->queueBuffers(inputs, &outputs));
+ ASSERT_EQ(inputs.size(), outputs.size());
+ for (const QueueBufferOutput& output : outputs) {
+ EXPECT_EQ(BAD_VALUE, output.result);
+ }
+ }
}
// Crop rect is out of bounds of the buffer dimensions
@@ -495,6 +690,18 @@
IGraphicBufferProducer::QueueBufferOutput output;
EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output));
+
+ { // Test with the batched version
+ constexpr size_t BATCH_SIZE = 16;
+ input.slot = dequeuedSlot;
+ std::vector<QueueBufferInput> inputs(BATCH_SIZE, input);
+ std::vector<QueueBufferOutput> outputs;
+ EXPECT_OK(mProducer->queueBuffers(inputs, &outputs));
+ ASSERT_EQ(inputs.size(), outputs.size());
+ for (const QueueBufferOutput& output : outputs) {
+ EXPECT_EQ(BAD_VALUE, output.result);
+ }
+ }
}
// Abandon the buffer queue so that the last test fails
@@ -507,6 +714,18 @@
// TODO(b/73267953): Make BufferHub honor producer and consumer connection.
EXPECT_EQ(NO_INIT, mProducer->queueBuffer(dequeuedSlot, input, &output));
+
+ { // Test with the batched version
+ constexpr size_t BATCH_SIZE = 16;
+ input.slot = dequeuedSlot;
+ std::vector<QueueBufferInput> inputs(BATCH_SIZE, input);
+ std::vector<QueueBufferOutput> outputs;
+ EXPECT_OK(mProducer->queueBuffers(inputs, &outputs));
+ ASSERT_EQ(inputs.size(), outputs.size());
+ for (const QueueBufferOutput& output : outputs) {
+ EXPECT_EQ(NO_INIT, output.result);
+ }
+ }
}
}
@@ -525,6 +744,44 @@
// No return code, but at least test that it doesn't blow up...
// TODO: add a return code
mProducer->cancelBuffer(dequeuedSlot, dequeuedFence);
+
+ { // Test batched methods
+ constexpr size_t BATCH_SIZE = 4;
+ ASSERT_OK(mProducer->setMaxDequeuedBufferCount(BATCH_SIZE));
+
+ // Dequeue
+ using DequeueBufferInput = IGraphicBufferProducer::DequeueBufferInput;
+ using DequeueBufferOutput = IGraphicBufferProducer::DequeueBufferOutput;
+ DequeueBufferInput dequeueInput;
+ dequeueInput.width = DEFAULT_WIDTH;
+ dequeueInput.height = DEFAULT_HEIGHT;
+ dequeueInput.format = DEFAULT_FORMAT;
+ dequeueInput.usage = TEST_PRODUCER_USAGE_BITS;
+ dequeueInput.getTimestamps = false;
+ std::vector<DequeueBufferInput> dequeueInputs(BATCH_SIZE, dequeueInput);
+ std::vector<DequeueBufferOutput> dequeueOutputs;
+ EXPECT_OK(mProducer->dequeueBuffers(dequeueInputs, &dequeueOutputs));
+ ASSERT_EQ(dequeueInputs.size(), dequeueOutputs.size());
+
+ // Cancel
+ using CancelBufferInput = IGraphicBufferProducer::CancelBufferInput;
+ std::vector<CancelBufferInput> cancelInputs;
+ cancelInputs.reserve(BATCH_SIZE);
+ for (const DequeueBufferOutput& dequeueOutput : dequeueOutputs) {
+ ASSERT_EQ(OK,
+ ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
+ dequeueOutput.result);
+ CancelBufferInput& cancelInput = cancelInputs.emplace_back();
+ cancelInput.slot = dequeueOutput.slot;
+ cancelInput.fence = dequeueOutput.fence;
+ }
+ std::vector<status_t> cancelOutputs;
+ EXPECT_OK(mProducer->cancelBuffers(cancelInputs, &cancelOutputs));
+ ASSERT_EQ(cancelInputs.size(), cancelOutputs.size());
+ for (status_t result : cancelOutputs) {
+ EXPECT_OK(result);
+ }
+ }
}
TEST_P(IGraphicBufferProducerTest, SetMaxDequeuedBufferCount_Succeeds) {
@@ -541,11 +798,11 @@
<< "bufferCount: " << minBuffers;
// Should now be able to dequeue up to minBuffers times
- DequeueBufferResult result;
+ IGraphicBufferProducer::DequeueBufferOutput result;
for (int i = 0; i < minBuffers; ++i) {
EXPECT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
(dequeueBuffer(DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
- TEST_PRODUCER_USAGE_BITS, &result)))
+ TEST_PRODUCER_USAGE_BITS, &result)))
<< "iteration: " << i << ", slot: " << result.slot;
}
@@ -558,7 +815,6 @@
ASSERT_OK(mProducer->requestBuffer(result.slot, &buffer));
ASSERT_OK(mProducer->queueBuffer(result.slot, input, &output));
-
// Should now be able to dequeue up to maxBuffers times
int dequeuedSlot = -1;
sp<Fence> dequeuedFence;
@@ -794,6 +1050,71 @@
EXPECT_OK(mProducer->attachBuffer(&slot, buffer));
EXPECT_OK(buffer->initCheck());
+
+ ASSERT_OK(mProducer->detachBuffer(slot));
+
+ { // Test batched methods
+ constexpr size_t BATCH_SIZE = 4;
+ ASSERT_OK(mProducer->setMaxDequeuedBufferCount(BATCH_SIZE));
+
+ // Dequeue
+ using DequeueBufferInput = IGraphicBufferProducer::DequeueBufferInput;
+ using DequeueBufferOutput = IGraphicBufferProducer::DequeueBufferOutput;
+ DequeueBufferInput dequeueInput;
+ dequeueInput.width = DEFAULT_WIDTH;
+ dequeueInput.height = DEFAULT_HEIGHT;
+ dequeueInput.format = DEFAULT_FORMAT;
+ dequeueInput.usage = TEST_PRODUCER_USAGE_BITS;
+ dequeueInput.getTimestamps = false;
+ std::vector<DequeueBufferInput> dequeueInputs(BATCH_SIZE, dequeueInput);
+ std::vector<DequeueBufferOutput> dequeueOutputs;
+ EXPECT_OK(mProducer->dequeueBuffers(dequeueInputs, &dequeueOutputs));
+ ASSERT_EQ(dequeueInputs.size(), dequeueOutputs.size());
+
+ // Request
+ std::vector<int32_t> requestInputs;
+ requestInputs.reserve(BATCH_SIZE);
+ for (const DequeueBufferOutput& dequeueOutput : dequeueOutputs) {
+ ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
+ dequeueOutput.result);
+ requestInputs.emplace_back(dequeueOutput.slot);
+ }
+ using RequestBufferOutput = IGraphicBufferProducer::RequestBufferOutput;
+ std::vector<RequestBufferOutput> requestOutputs;
+ EXPECT_OK(mProducer->requestBuffers(requestInputs, &requestOutputs));
+ ASSERT_EQ(requestInputs.size(), requestOutputs.size());
+ for (const RequestBufferOutput& requestOutput : requestOutputs) {
+ EXPECT_OK(requestOutput.result);
+ }
+
+ // Detach
+ std::vector<int32_t> detachInputs;
+ detachInputs.reserve(BATCH_SIZE);
+ for (const DequeueBufferOutput& dequeueOutput : dequeueOutputs) {
+ detachInputs.emplace_back(dequeueOutput.slot);
+ }
+ std::vector<status_t> detachOutputs;
+ EXPECT_OK(mProducer->detachBuffers(detachInputs, &detachOutputs));
+ ASSERT_EQ(detachInputs.size(), detachOutputs.size());
+ for (status_t result : detachOutputs) {
+ EXPECT_OK(result);
+ }
+
+ // Attach
+ using AttachBufferOutput = IGraphicBufferProducer::AttachBufferOutput;
+ std::vector<sp<GraphicBuffer>> attachInputs;
+ attachInputs.reserve(BATCH_SIZE);
+ for (const RequestBufferOutput& requestOutput : requestOutputs) {
+ attachInputs.emplace_back(requestOutput.buffer);
+ }
+ std::vector<AttachBufferOutput> attachOutputs;
+ EXPECT_OK(mProducer->attachBuffers(attachInputs, &attachOutputs));
+ ASSERT_EQ(attachInputs.size(), attachOutputs.size());
+ for (const AttachBufferOutput& attachOutput : attachOutputs) {
+ EXPECT_OK(attachOutput.result);
+ EXPECT_NE(-1, attachOutput.slot);
+ }
+ }
}
#if USE_BUFFER_HUB_AS_BUFFER_QUEUE