diff options
-rw-r--r-- | libs/gui/Android.bp | 3 | ||||
-rw-r--r-- | libs/gui/BatchBufferOps.cpp | 125 | ||||
-rw-r--r-- | libs/gui/IGraphicBufferProducer.cpp | 348 | ||||
-rw-r--r-- | libs/gui/IGraphicBufferProducerFlattenables.cpp | 413 | ||||
-rw-r--r-- | libs/gui/QueueBufferInputOutput.cpp | 159 | ||||
-rw-r--r-- | libs/gui/include/gui/IGraphicBufferProducer.h | 174 | ||||
-rw-r--r-- | libs/gui/tests/IGraphicBufferProducer_test.cpp | 363 |
7 files changed, 1389 insertions, 196 deletions
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index af9ef06b86..c5a18558a6 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -161,6 +161,7 @@ cc_library_static { filegroup { name: "libgui_bufferqueue_sources", srcs: [ + "BatchBufferOps.cpp", "BufferItem.cpp", "BufferQueue.cpp", "BufferQueueConsumer.cpp", @@ -171,7 +172,7 @@ filegroup { "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 0000000000..60aceb1bbd --- /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 ad00939976..c1f9b85229 100644 --- a/libs/gui/IGraphicBufferProducer.cpp +++ b/libs/gui/IGraphicBufferProducer.cpp @@ -74,6 +74,13 @@ enum { 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ status_t BnGraphicBufferProducer::onTransact( 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 @@ status_t BnGraphicBufferProducer::onTransact( 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 @@ status_t BnGraphicBufferProducer::onTransact( 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 @@ status_t BnGraphicBufferProducer::onTransact( 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 @@ status_t BnGraphicBufferProducer::onTransact( 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 @@ status_t BnGraphicBufferProducer::onTransact( 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 @@ status_t BnGraphicBufferProducer::onTransact( 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 @@ status_t BnGraphicBufferProducer::onTransact( 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 @@ status_t BnGraphicBufferProducer::onTransact( 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 0000000000..c8b9b6751d --- /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 30f0ef6785..0000000000 --- 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 45e0a134ba..c3b92622b6 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 @@ public: 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 @@ public: // 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 @@ public: if (outGetFrameTimestamps) { *outGetFrameTimestamps = getFrameTimestamps; } + if (outSlot) { + *outSlot = slot; + } } // Flattenable protocol @@ -357,6 +372,7 @@ public: sp<Fence> fence; Region surfaceDamage; bool getFrameTimestamps{false}; + int slot{-1}; HdrMetadata hdrMetadata; }; @@ -385,6 +401,7 @@ public: 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 @@ public: // 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 @@ public: // 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 15bd32d354..2af2fe1f91 100644 --- a/libs/gui/tests/IGraphicBufferProducer_test.cpp +++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp @@ -70,6 +70,9 @@ namespace { 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 @@ protected: 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 @@ protected: crop, scalingMode, transform, - fence); + fence, + stickyTransform, + getTimestamps, + slot); } QueueBufferInputBuilder& setTimestamp(int64_t timestamp) { @@ -204,6 +213,21 @@ protected: 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 @@ protected: int scalingMode; uint32_t transform; sp<Fence> fence; - }; // struct QueueBufferInputBuilder - - // To easily store dequeueBuffer results into containers - struct DequeueBufferResult { + uint32_t stickyTransform; + bool getTimestamps; int slot; - sp<Fence> fence; - }; + }; // struct QueueBufferInputBuilder - 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 @@ TEST_P(IGraphicBufferProducerTest, Query_Succeeds) { 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 @@ TEST_P(IGraphicBufferProducerTest, Query_ReturnsError) { 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 @@ TEST_P(IGraphicBufferProducerTest, Queue_Succeeds) { // 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 @@ TEST_P(IGraphicBufferProducerTest, Queue_ReturnsError) { // 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 @@ TEST_P(IGraphicBufferProducerTest, Queue_ReturnsError) { { 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 @@ TEST_P(IGraphicBufferProducerTest, Queue_ReturnsError) { 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 @@ TEST_P(IGraphicBufferProducerTest, Queue_ReturnsError) { 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 @@ TEST_P(IGraphicBufferProducerTest, Queue_ReturnsError) { // 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 @@ TEST_P(IGraphicBufferProducerTest, CancelBuffer_DoesntCrash) { // 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 @@ TEST_P(IGraphicBufferProducerTest, SetMaxDequeuedBufferCount_Succeeds) { << "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 @@ TEST_P(IGraphicBufferProducerTest, SetMaxDequeuedBufferCount_Succeeds) { 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 @@ TEST_P(IGraphicBufferProducerTest, DetachThenAttach_Succeeds) { 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 |