From d06f050dca508b7da0fca86ecc3b733ed2c7df99 Mon Sep 17 00:00:00 2001 From: Ady Abraham Date: Tue, 21 Sep 2021 16:32:07 -0700 Subject: [PATCH] Add command buffer support for AidlComposer Bug: 198190384 Test: build Change-Id: Ia28cf992b0ab06099a9ea3f871925839e92e8dc9 --- graphics/composer/aidl/Android.bp | 19 + .../graphics/composer3/command-buffer.h | 895 ++++++++++++++++++ 2 files changed, 914 insertions(+) create mode 100644 graphics/composer/aidl/include/android/hardware/graphics/composer3/command-buffer.h diff --git a/graphics/composer/aidl/Android.bp b/graphics/composer/aidl/Android.bp index 5006185481..e33c6537e9 100644 --- a/graphics/composer/aidl/Android.bp +++ b/graphics/composer/aidl/Android.bp @@ -67,3 +67,22 @@ cc_library { ], export_include_dirs: ["include"], } + +cc_library_headers { + name: "android.hardware.graphics.composer3-command-buffer", + vendor_available: true, + shared_libs: [ + "android.hardware.graphics.composer3-V1-ndk", + "libbase", + "libfmq", + "libsync", + ], + static_libs: [ + "libaidlcommonsupport", + ], + export_shared_lib_headers: [ + "libfmq", + "libsync", + ], + export_include_dirs: ["include"], +} diff --git a/graphics/composer/aidl/include/android/hardware/graphics/composer3/command-buffer.h b/graphics/composer/aidl/include/android/hardware/graphics/composer3/command-buffer.h new file mode 100644 index 0000000000..fdbb8b3dac --- /dev/null +++ b/graphics/composer/aidl/include/android/hardware/graphics/composer3/command-buffer.h @@ -0,0 +1,895 @@ +/* + * 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. + */ + +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include + +using aidl::android::hardware::graphics::common::ColorTransform; +using aidl::android::hardware::graphics::common::Dataspace; +using aidl::android::hardware::graphics::common::FRect; +using aidl::android::hardware::graphics::common::Rect; +using aidl::android::hardware::graphics::common::Transform; + +using aidl::android::hardware::graphics::composer3::BlendMode; +using aidl::android::hardware::graphics::composer3::ClientTargetProperty; +using aidl::android::hardware::graphics::composer3::Color; +using aidl::android::hardware::graphics::composer3::Command; +using aidl::android::hardware::graphics::composer3::Composition; +using aidl::android::hardware::graphics::composer3::FloatColor; +using aidl::android::hardware::graphics::composer3::HandleIndex; +using aidl::android::hardware::graphics::composer3::PerFrameMetadata; +using aidl::android::hardware::graphics::composer3::PerFrameMetadataBlob; + +using aidl::android::hardware::common::NativeHandle; +using aidl::android::hardware::common::fmq::SynchronizedReadWrite; +using android::AidlMessageQueue; +using CommandQueueType = AidlMessageQueue; +using aidl::android::hardware::common::fmq::MQDescriptor; +using DescriptorType = MQDescriptor; + +namespace aidl::android::hardware::graphics::composer3 { + +// This class helps build a command queue. Note that all sizes/lengths are in +// units of uint32_t's. +class CommandWriterBase { + public: + CommandWriterBase(uint32_t initialMaxSize) : mDataMaxSize(initialMaxSize) { + mData = std::make_unique(mDataMaxSize); + reset(); + } + + virtual ~CommandWriterBase() { reset(); } + + void reset() { + mDataWritten = 0; + mCommandEnd = 0; + + // handles in mDataHandles are owned by the caller + mDataHandles.clear(); + + // handles in mTemporaryHandles are owned by the writer + for (auto handle : mTemporaryHandles) { + native_handle_close(handle); + native_handle_delete(handle); + } + mTemporaryHandles.clear(); + } + + Command getCommand(uint32_t offset) { + uint32_t val = (offset < mDataWritten) ? mData[offset] : 0; + return static_cast(val & static_cast(Command::OPCODE_MASK)); + } + + bool writeQueue(bool* outQueueChanged, int32_t* outCommandLength, + std::vector* outCommandHandles) { + if (mDataWritten == 0) { + *outQueueChanged = false; + *outCommandLength = 0; + outCommandHandles->clear(); + return true; + } + + // After data are written to the queue, it may not be read by the + // remote reader when + // + // - the writer does not send them (because of other errors) + // - the hwbinder transaction fails + // - the reader does not read them (because of other errors) + // + // Discard the stale data here. + size_t staleDataSize = mQueue ? mQueue->availableToRead() : 0; + if (staleDataSize > 0) { + ALOGW("discarding stale data from message queue"); + CommandQueueType::MemTransaction tx; + if (mQueue->beginRead(staleDataSize, &tx)) { + mQueue->commitRead(staleDataSize); + } + } + + // write data to queue, optionally resizing it + if (mQueue && (mDataMaxSize <= mQueue->getQuantumCount())) { + if (!mQueue->write(mData.get(), mDataWritten)) { + ALOGE("failed to write commands to message queue"); + return false; + } + + *outQueueChanged = false; + } else { + auto newQueue = std::make_unique(mDataMaxSize); + if (!newQueue->isValid() || !newQueue->write(mData.get(), mDataWritten)) { + ALOGE("failed to prepare a new message queue "); + return false; + } + + mQueue = std::move(newQueue); + *outQueueChanged = true; + } + + *outCommandLength = mDataWritten; + *outCommandHandles = std::move(mDataHandles); + + return true; + } + + DescriptorType getMQDescriptor() const { + return (mQueue) ? mQueue->dupeDesc() : DescriptorType{}; + } + + static constexpr uint16_t kSelectDisplayLength = 2; + void selectDisplay(int64_t display) { + beginCommand(Command::SELECT_DISPLAY, kSelectDisplayLength); + write64(display); + endCommand(); + } + + static constexpr uint16_t kSelectLayerLength = 2; + void selectLayer(int64_t layer) { + beginCommand(Command::SELECT_LAYER, kSelectLayerLength); + write64(layer); + endCommand(); + } + + static constexpr uint16_t kSetErrorLength = 2; + void setError(uint32_t location, int32_t error) { + beginCommand(Command::SET_ERROR, kSetErrorLength); + write(location); + writeSigned(error); + endCommand(); + } + + static constexpr uint32_t kPresentOrValidateDisplayResultLength = 1; + void setPresentOrValidateResult(uint32_t state) { + beginCommand(Command::SET_PRESENT_OR_VALIDATE_DISPLAY_RESULT, + kPresentOrValidateDisplayResultLength); + write(state); + endCommand(); + } + + void setChangedCompositionTypes(const std::vector& layers, + const std::vector& types) { + size_t totalLayers = std::min(layers.size(), types.size()); + size_t currentLayer = 0; + + while (currentLayer < totalLayers) { + size_t count = + std::min(totalLayers - currentLayer, static_cast(kMaxLength) / 3); + + beginCommand(Command::SET_CHANGED_COMPOSITION_TYPES, count * 3); + for (size_t i = 0; i < count; i++) { + write64(layers[currentLayer + i]); + writeSigned(static_cast(types[currentLayer + i])); + } + endCommand(); + + currentLayer += count; + } + } + + void setDisplayRequests(uint32_t displayRequestMask, const std::vector& layers, + const std::vector& layerRequestMasks) { + size_t totalLayers = std::min(layers.size(), layerRequestMasks.size()); + size_t currentLayer = 0; + + while (currentLayer < totalLayers) { + size_t count = + std::min(totalLayers - currentLayer, static_cast(kMaxLength - 1) / 3); + + beginCommand(Command::SET_DISPLAY_REQUESTS, 1 + count * 3); + write(displayRequestMask); + for (size_t i = 0; i < count; i++) { + write64(layers[currentLayer + i]); + write(static_cast(layerRequestMasks[currentLayer + i])); + } + endCommand(); + + currentLayer += count; + } + } + + static constexpr uint16_t kSetPresentFenceLength = 1; + void setPresentFence(int presentFence) { + beginCommand(Command::SET_PRESENT_FENCE, kSetPresentFenceLength); + writeFence(presentFence); + endCommand(); + } + + void setReleaseFences(const std::vector& layers, + const std::vector& releaseFences) { + size_t totalLayers = std::min(layers.size(), releaseFences.size()); + size_t currentLayer = 0; + + while (currentLayer < totalLayers) { + size_t count = + std::min(totalLayers - currentLayer, static_cast(kMaxLength) / 3); + + beginCommand(Command::SET_RELEASE_FENCES, count * 3); + for (size_t i = 0; i < count; i++) { + write64(layers[currentLayer + i]); + writeFence(releaseFences[currentLayer + i]); + } + endCommand(); + + currentLayer += count; + } + } + + static constexpr uint16_t kSetColorTransformLength = 17; + void setColorTransform(const float* matrix, ColorTransform hint) { + beginCommand(Command::SET_COLOR_TRANSFORM, kSetColorTransformLength); + for (int i = 0; i < 16; i++) { + writeFloat(matrix[i]); + } + writeSigned(static_cast(hint)); + endCommand(); + } + + void setClientTarget(uint32_t slot, const native_handle_t* target, int acquireFence, + Dataspace dataspace, const std::vector& damage) { + setClientTargetInternal(slot, target, acquireFence, static_cast(dataspace), + damage); + } + + static constexpr uint16_t kSetOutputBufferLength = 3; + void setOutputBuffer(uint32_t slot, const native_handle_t* buffer, int releaseFence) { + beginCommand(Command::SET_OUTPUT_BUFFER, kSetOutputBufferLength); + write(slot); + writeHandle(buffer, true); + writeFence(releaseFence); + endCommand(); + } + + static constexpr uint16_t kValidateDisplayLength = 0; + void validateDisplay() { + beginCommand(Command::VALIDATE_DISPLAY, kValidateDisplayLength); + endCommand(); + } + + static constexpr uint16_t kPresentOrValidateDisplayLength = 0; + void presentOrvalidateDisplay() { + beginCommand(Command::PRESENT_OR_VALIDATE_DISPLAY, kPresentOrValidateDisplayLength); + endCommand(); + } + + static constexpr uint16_t kAcceptDisplayChangesLength = 0; + void acceptDisplayChanges() { + beginCommand(Command::ACCEPT_DISPLAY_CHANGES, kAcceptDisplayChangesLength); + endCommand(); + } + + static constexpr uint16_t kPresentDisplayLength = 0; + void presentDisplay() { + beginCommand(Command::PRESENT_DISPLAY, kPresentDisplayLength); + endCommand(); + } + + static constexpr uint16_t kSetLayerCursorPositionLength = 2; + void setLayerCursorPosition(int32_t x, int32_t y) { + beginCommand(Command::SET_LAYER_CURSOR_POSITION, kSetLayerCursorPositionLength); + writeSigned(x); + writeSigned(y); + endCommand(); + } + + static constexpr uint16_t kSetLayerBufferLength = 3; + void setLayerBuffer(uint32_t slot, const native_handle_t* buffer, int acquireFence) { + beginCommand(Command::SET_LAYER_BUFFER, kSetLayerBufferLength); + write(slot); + writeHandle(buffer, true); + writeFence(acquireFence); + endCommand(); + } + + void setLayerSurfaceDamage(const std::vector& damage) { + bool doWrite = (damage.size() <= kMaxLength / 4); + size_t length = (doWrite) ? damage.size() * 4 : 0; + + beginCommand(Command::SET_LAYER_SURFACE_DAMAGE, length); + // When there are too many rectangles in the damage region and doWrite + // is false, we write no rectangle at all which means the entire + // layer is damaged. + if (doWrite) { + writeRegion(damage); + } + endCommand(); + } + + static constexpr uint16_t kSetLayerBlendModeLength = 1; + void setLayerBlendMode(BlendMode mode) { + beginCommand(Command::SET_LAYER_BLEND_MODE, kSetLayerBlendModeLength); + writeSigned(static_cast(mode)); + endCommand(); + } + + static constexpr uint16_t kSetLayerColorLength = 1; + void setLayerColor(Color color) { + beginCommand(Command::SET_LAYER_COLOR, kSetLayerColorLength); + writeColor(color); + endCommand(); + } + + static constexpr uint16_t kSetLayerCompositionTypeLength = 1; + void setLayerCompositionType(Composition type) { + beginCommand(Command::SET_LAYER_COMPOSITION_TYPE, kSetLayerCompositionTypeLength); + writeSigned(static_cast(type)); + endCommand(); + } + + static constexpr uint16_t kSetLayerDataspaceLength = 1; + void setLayerDataspace(Dataspace dataspace) { + setLayerDataspaceInternal(static_cast(dataspace)); + } + + static constexpr uint16_t kSetLayerDisplayFrameLength = 4; + void setLayerDisplayFrame(const Rect& frame) { + beginCommand(Command::SET_LAYER_DISPLAY_FRAME, kSetLayerDisplayFrameLength); + writeRect(frame); + endCommand(); + } + + static constexpr uint16_t kSetLayerPlaneAlphaLength = 1; + void setLayerPlaneAlpha(float alpha) { + beginCommand(Command::SET_LAYER_PLANE_ALPHA, kSetLayerPlaneAlphaLength); + writeFloat(alpha); + endCommand(); + } + + static constexpr uint16_t kSetLayerSidebandStreamLength = 1; + void setLayerSidebandStream(const native_handle_t* stream) { + beginCommand(Command::SET_LAYER_SIDEBAND_STREAM, kSetLayerSidebandStreamLength); + writeHandle(stream); + endCommand(); + } + + static constexpr uint16_t kSetLayerSourceCropLength = 4; + void setLayerSourceCrop(const FRect& crop) { + beginCommand(Command::SET_LAYER_SOURCE_CROP, kSetLayerSourceCropLength); + writeFRect(crop); + endCommand(); + } + + static constexpr uint16_t kSetLayerTransformLength = 1; + void setLayerTransform(Transform transform) { + beginCommand(Command::SET_LAYER_TRANSFORM, kSetLayerTransformLength); + writeSigned(static_cast(transform)); + endCommand(); + } + + void setLayerVisibleRegion(const std::vector& visible) { + bool doWrite = (visible.size() <= kMaxLength / 4); + size_t length = (doWrite) ? visible.size() * 4 : 0; + + beginCommand(Command::SET_LAYER_VISIBLE_REGION, length); + // When there are too many rectangles in the visible region and + // doWrite is false, we write no rectangle at all which means the + // entire layer is visible. + if (doWrite) { + writeRegion(visible); + } + endCommand(); + } + + static constexpr uint16_t kSetLayerZOrderLength = 1; + void setLayerZOrder(uint32_t z) { + beginCommand(Command::SET_LAYER_Z_ORDER, kSetLayerZOrderLength); + write(z); + endCommand(); + } + + void setLayerPerFrameMetadata(const std::vector& metadataVec) { + beginCommand(Command::SET_LAYER_PER_FRAME_METADATA, metadataVec.size() * 2); + for (const auto& metadata : metadataVec) { + writeSigned(static_cast(metadata.key)); + writeFloat(metadata.value); + } + endCommand(); + } + + static constexpr uint16_t kSetLayerColorTransformLength = 16; + void setLayerColorTransform(const float* matrix) { + beginCommand(Command::SET_LAYER_COLOR_TRANSFORM, kSetLayerColorTransformLength); + for (int i = 0; i < 16; i++) { + writeFloat(matrix[i]); + } + endCommand(); + } + + void setLayerPerFrameMetadataBlobs(const std::vector& metadata) { + // in units of uint32_t's + size_t commandLength = 0; + + if (metadata.size() > std::numeric_limits::max()) { + LOG_FATAL("too many metadata blobs - dynamic metadata size is too large"); + return; + } + + // space for numElements + commandLength += 1; + + for (auto metadataBlob : metadata) { + commandLength += 1; // key of metadata blob + commandLength += 1; // size information of metadata blob + + // metadata content size + size_t metadataSize = metadataBlob.blob.size() / sizeof(uint32_t); + commandLength += metadataSize; + commandLength += + (metadataBlob.blob.size() - (metadataSize * sizeof(uint32_t)) > 0) ? 1 : 0; + } + + if (commandLength > std::numeric_limits::max()) { + LOG_FATAL("dynamic metadata size is too large"); + return; + } + + // Blobs are written as: + // {numElements, key1, size1, blob1, key2, size2, blob2, key3, size3...} + uint16_t length = static_cast(commandLength); + beginCommand(Command::SET_LAYER_PER_FRAME_METADATA_BLOBS, length); + write(static_cast(metadata.size())); + for (auto metadataBlob : metadata) { + writeSigned(static_cast(metadataBlob.key)); + write(static_cast(metadataBlob.blob.size())); + writeBlob(static_cast(metadataBlob.blob.size()), metadataBlob.blob.data()); + } + endCommand(); + } + + static constexpr uint16_t kSetLayerFloatColorLength = 4; + void setLayerFloatColor(FloatColor color) { + beginCommand(Command::SET_LAYER_FLOAT_COLOR, kSetLayerFloatColorLength); + writeFloatColor(color); + endCommand(); + } + + static constexpr uint16_t kSetClientTargetPropertyLength = 2; + void setClientTargetProperty(const ClientTargetProperty& clientTargetProperty) { + beginCommand(Command::SET_CLIENT_TARGET_PROPERTY, kSetClientTargetPropertyLength); + writeSigned(static_cast(clientTargetProperty.pixelFormat)); + writeSigned(static_cast(clientTargetProperty.dataspace)); + endCommand(); + } + + void setLayerGenericMetadata(const std::string& key, const bool mandatory, + const std::vector& value) { + const size_t commandSize = 3 + sizeToElements(key.size()) + sizeToElements(value.size()); + if (commandSize > std::numeric_limits::max()) { + LOG_FATAL("Too much generic metadata (%zu elements)", commandSize); + return; + } + + beginCommand(Command::SET_LAYER_GENERIC_METADATA, static_cast(commandSize)); + write(key.size()); + writeBlob(key.size(), reinterpret_cast(key.c_str())); + write(mandatory); + write(value.size()); + writeBlob(value.size(), value.data()); + endCommand(); + } + + protected: + template + void beginCommand(T command, uint16_t length) { + beginCommandBase(static_cast(command), length); + } + + void setClientTargetInternal(uint32_t slot, const native_handle_t* target, int acquireFence, + int32_t dataspace, const std::vector& damage) { + bool doWrite = (damage.size() <= (kMaxLength - 4) / 4); + size_t length = 4 + ((doWrite) ? damage.size() * 4 : 0); + + beginCommand(Command::SET_CLIENT_TARGET, length); + write(slot); + writeHandle(target, true); + writeFence(acquireFence); + writeSigned(dataspace); + // When there are too many rectangles in the damage region and doWrite + // is false, we write no rectangle at all which means the entire + // client target is damaged. + if (doWrite) { + writeRegion(damage); + } + endCommand(); + } + + void setLayerDataspaceInternal(int32_t dataspace) { + beginCommand(Command::SET_LAYER_DATASPACE, kSetLayerDataspaceLength); + writeSigned(dataspace); + endCommand(); + } + + void beginCommandBase(Command command, uint16_t length) { + if (mCommandEnd) { + LOG_FATAL("endCommand was not called before command 0x%x", command); + } + + growData(1 + length); + write(static_cast(command) | length); + + mCommandEnd = mDataWritten + length; + } + + void endCommand() { + if (!mCommandEnd) { + LOG_FATAL("beginCommand was not called"); + } else if (mDataWritten > mCommandEnd) { + LOG_FATAL("too much data written"); + mDataWritten = mCommandEnd; + } else if (mDataWritten < mCommandEnd) { + LOG_FATAL("too little data written"); + while (mDataWritten < mCommandEnd) { + write(0); + } + } + + mCommandEnd = 0; + } + + void write(uint32_t val) { mData[mDataWritten++] = val; } + + void writeSigned(int32_t val) { memcpy(&mData[mDataWritten++], &val, sizeof(val)); } + + void writeFloat(float val) { memcpy(&mData[mDataWritten++], &val, sizeof(val)); } + + void write64(uint64_t val) { + uint32_t lo = static_cast(val & 0xffffffff); + uint32_t hi = static_cast(val >> 32); + write(lo); + write(hi); + } + + void writeRect(const Rect& rect) { + writeSigned(rect.left); + writeSigned(rect.top); + writeSigned(rect.right); + writeSigned(rect.bottom); + } + + void writeRegion(const std::vector& region) { + for (const auto& rect : region) { + writeRect(rect); + } + } + + void writeFRect(const FRect& rect) { + writeFloat(rect.left); + writeFloat(rect.top); + writeFloat(rect.right); + writeFloat(rect.bottom); + } + + void writeColor(const Color& color) { + write((color.r << 0) | (color.g << 8) | (color.b << 16) | (color.a << 24)); + } + + void writeFloatColor(const FloatColor& color) { + writeFloat(color.r); + writeFloat(color.g); + writeFloat(color.b); + writeFloat(color.a); + } + + void writeBlob(uint32_t length, const unsigned char* blob) { + memcpy(&mData[mDataWritten], blob, length); + uint32_t numElements = length / 4; + mDataWritten += numElements; + mDataWritten += (length - (numElements * 4) > 0) ? 1 : 0; + } + + // ownership of handle is not transferred + void writeHandle(const native_handle_t* handle, bool useCache) { + if (!handle) { + writeSigned( + static_cast((useCache) ? HandleIndex::CACHED : HandleIndex::EMPTY)); + return; + } + + mDataHandles.push_back(::android::makeToAidl(handle)); + writeSigned(mDataHandles.size() - 1); + } + + void writeHandle(const native_handle_t* handle) { writeHandle(handle, false); } + + // ownership of fence is transferred + void writeFence(int fence) { + native_handle_t* handle = nullptr; + if (fence >= 0) { + handle = getTemporaryHandle(1, 0); + if (handle) { + handle->data[0] = fence; + } else { + ALOGW("failed to get temporary handle for fence %d", fence); + sync_wait(fence, -1); + close(fence); + } + } + + writeHandle(handle); + } + + native_handle_t* getTemporaryHandle(int numFds, int numInts) { + native_handle_t* handle = native_handle_create(numFds, numInts); + if (handle) { + mTemporaryHandles.push_back(handle); + } + return handle; + } + + static constexpr uint16_t kMaxLength = std::numeric_limits::max(); + + std::unique_ptr mData; + uint32_t mDataWritten; + + private: + void growData(uint32_t grow) { + uint32_t newWritten = mDataWritten + grow; + if (newWritten < mDataWritten) { + LOG_ALWAYS_FATAL("buffer overflowed; data written %" PRIu32 ", growing by %" PRIu32, + mDataWritten, grow); + } + + if (newWritten <= mDataMaxSize) { + return; + } + + uint32_t newMaxSize = mDataMaxSize << 1; + if (newMaxSize < newWritten) { + newMaxSize = newWritten; + } + + auto newData = std::make_unique(newMaxSize); + std::copy_n(mData.get(), mDataWritten, newData.get()); + mDataMaxSize = newMaxSize; + mData = std::move(newData); + } + + uint32_t sizeToElements(uint32_t size) { return (size + 3) / 4; } + + uint32_t mDataMaxSize; + // end offset of the current command + uint32_t mCommandEnd; + + std::vector mDataHandles; + std::vector mTemporaryHandles; + + std::unique_ptr mQueue; +}; + +// This class helps parse a command queue. Note that all sizes/lengths are in +// units of uint32_t's. +class CommandReaderBase { + public: + CommandReaderBase() : mDataMaxSize(0) { reset(); } + + bool setMQDescriptor(const DescriptorType& descriptor) { + mQueue = std::make_unique(descriptor, false); + if (mQueue->isValid()) { + return true; + } else { + mQueue = nullptr; + return false; + } + } + + bool readQueue(int32_t commandLength, std::vector commandHandles) { + if (!mQueue) { + return false; + } + + auto quantumCount = mQueue->getQuantumCount(); + if (mDataMaxSize < quantumCount) { + mDataMaxSize = quantumCount; + mData = std::make_unique(mDataMaxSize); + } + + if (commandLength > mDataMaxSize || !mQueue->read(mData.get(), commandLength)) { + ALOGE("failed to read commands from message queue"); + return false; + } + + mDataSize = commandLength; + mDataRead = 0; + mCommandBegin = 0; + mCommandEnd = 0; + mDataHandles = std::move(commandHandles); + return true; + } + + void reset() { + mDataSize = 0; + mDataRead = 0; + mCommandBegin = 0; + mCommandEnd = 0; + mDataHandles.clear(); + } + + protected: + template + bool beginCommand(T* outCommand, uint16_t* outLength) { + return beginCommandBase(reinterpret_cast(outCommand), outLength); + } + + bool isEmpty() const { return (mDataRead >= mDataSize); } + + bool beginCommandBase(Command* outCommand, uint16_t* outLength) { + if (mCommandEnd) { + LOG_FATAL("endCommand was not called for last command"); + } + + constexpr uint32_t opcode_mask = static_cast(Command::OPCODE_MASK); + constexpr uint32_t length_mask = static_cast(Command::LENGTH_MASK); + + uint32_t val = read(); + *outCommand = static_cast(val & opcode_mask); + *outLength = static_cast(val & length_mask); + + if (mDataRead + *outLength > mDataSize) { + ALOGE("command 0x%x has invalid command length %" PRIu16, *outCommand, *outLength); + // undo the read() above + mDataRead--; + return false; + } + + mCommandEnd = mDataRead + *outLength; + + return true; + } + + void endCommand() { + if (!mCommandEnd) { + LOG_FATAL("beginCommand was not called"); + } else if (mDataRead > mCommandEnd) { + LOG_FATAL("too much data read"); + mDataRead = mCommandEnd; + } else if (mDataRead < mCommandEnd) { + LOG_FATAL("too little data read"); + mDataRead = mCommandEnd; + } + + mCommandBegin = mCommandEnd; + mCommandEnd = 0; + } + + uint32_t getCommandLoc() const { return mCommandBegin; } + + uint32_t read() { return mData[mDataRead++]; } + + int32_t readSigned() { + int32_t val; + memcpy(&val, &mData[mDataRead++], sizeof(val)); + return val; + } + + float readFloat() { + float val; + memcpy(&val, &mData[mDataRead++], sizeof(val)); + return val; + } + + uint64_t read64() { + uint32_t lo = read(); + uint32_t hi = read(); + return (static_cast(hi) << 32) | lo; + } + + Color readColor() { + uint32_t val = read(); + return Color{ + static_cast((val >> 0) & 0xff), + static_cast((val >> 8) & 0xff), + static_cast((val >> 16) & 0xff), + static_cast((val >> 24) & 0xff), + }; + } + + // ownership of handle is not transferred + const native_handle_t* readHandle(bool* outUseCache) { + const native_handle_t* handle = nullptr; + + int32_t index = readSigned(); + switch (index) { + case static_cast(HandleIndex::EMPTY): + *outUseCache = false; + break; + case static_cast(HandleIndex::CACHED): + *outUseCache = true; + break; + default: + if (static_cast(index) < mDataHandles.size()) { + handle = ::android::makeFromAidl(mDataHandles[index]); + } else { + ALOGE("invalid handle index %zu", static_cast(index)); + } + *outUseCache = false; + break; + } + + return handle; + } + + const native_handle_t* readHandle() { + bool useCache; + return readHandle(&useCache); + } + + // ownership of fence is transferred + int readFence() { + auto handle = readHandle(); + if (!handle || handle->numFds == 0) { + return -1; + } + + if (handle->numFds != 1) { + ALOGE("invalid fence handle with %d fds", handle->numFds); + return -1; + } + + int fd = dup(handle->data[0]); + if (fd < 0) { + ALOGW("failed to dup fence %d", handle->data[0]); + sync_wait(handle->data[0], -1); + fd = -1; + } + + return fd; + } + + std::unique_ptr mData; + uint32_t mDataRead; + + private: + std::unique_ptr mQueue; + uint32_t mDataMaxSize; + + uint32_t mDataSize; + + // begin/end offsets of the current command + uint32_t mCommandBegin; + uint32_t mCommandEnd; + + std::vector mDataHandles; +}; + +} // namespace aidl::android::hardware::graphics::composer3