| /* |
| * Copyright 2022 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. |
| */ |
| |
| #undef LOG_TAG |
| #define LOG_TAG "VtsHalGraphicsMapperStableC_TargetTest" |
| |
| #include <aidl/Vintf.h> |
| #include <aidl/android/hardware/graphics/allocator/AllocationError.h> |
| #include <aidl/android/hardware/graphics/allocator/AllocationResult.h> |
| #include <aidl/android/hardware/graphics/allocator/IAllocator.h> |
| #include <aidl/android/hardware/graphics/common/BufferUsage.h> |
| #include <aidl/android/hardware/graphics/common/PixelFormat.h> |
| #include <aidlcommonsupport/NativeHandle.h> |
| #include <android/binder_enums.h> |
| #include <android/binder_manager.h> |
| #include <android/dlext.h> |
| #include <android/hardware/graphics/mapper/IMapper.h> |
| #include <android/hardware/graphics/mapper/utils/IMapperMetadataTypes.h> |
| #include <gralloctypes/Gralloc4.h> |
| #include <hidl/GtestPrinter.h> |
| #include <system/graphics.h> |
| |
| #include <dlfcn.h> |
| #include <drm/drm_fourcc.h> |
| #include <gtest/gtest.h> |
| #include <vndksupport/linker.h> |
| #include <initializer_list> |
| #include <optional> |
| #include <string> |
| #include <tuple> |
| #include <vector> |
| |
| using namespace aidl::android::hardware::graphics::allocator; |
| using namespace aidl::android::hardware::graphics::common; |
| using namespace android; |
| using namespace android::hardware; |
| using namespace ::android::hardware::graphics::mapper; |
| |
| typedef AIMapper_Error (*AIMapper_loadIMapperFn)(AIMapper* _Nullable* _Nonnull outImplementation); |
| |
| inline constexpr BufferUsage operator|(BufferUsage lhs, BufferUsage rhs) { |
| using T = std::underlying_type_t<BufferUsage>; |
| return static_cast<BufferUsage>(static_cast<T>(lhs) | static_cast<T>(rhs)); |
| } |
| |
| inline BufferUsage& operator|=(BufferUsage& lhs, BufferUsage rhs) { |
| lhs = lhs | rhs; |
| return lhs; |
| } |
| |
| struct YCbCr { |
| android_ycbcr yCbCr; |
| int64_t horizontalSubSampling; |
| int64_t verticalSubSampling; |
| }; |
| |
| constexpr const char* STANDARD_METADATA_NAME = |
| "android.hardware.graphics.common.StandardMetadataType"; |
| |
| static bool isStandardMetadata(AIMapper_MetadataType metadataType) { |
| return strcmp(STANDARD_METADATA_NAME, metadataType.name) == 0; |
| } |
| |
| static std::string toString(const std::vector<StandardMetadataType> types) { |
| std::stringstream buf; |
| buf << "["; |
| for (auto type : types) { |
| buf << toString(type) << ", "; |
| } |
| buf.seekp(-2, buf.cur); |
| buf << "]"; |
| return buf.str(); |
| } |
| |
| class BufferHandle { |
| AIMapper* mIMapper; |
| buffer_handle_t mHandle = nullptr; |
| |
| public: |
| explicit BufferHandle(AIMapper* mapper, native_handle_t* rawHandle) : mIMapper(mapper) { |
| EXPECT_EQ(AIMAPPER_ERROR_NONE, mIMapper->v5.importBuffer(rawHandle, &mHandle)); |
| } |
| |
| explicit BufferHandle(BufferHandle&& other) { *this = std::move(other); } |
| |
| BufferHandle& operator=(BufferHandle&& other) noexcept { |
| reset(); |
| mIMapper = other.mIMapper; |
| mHandle = other.mHandle; |
| other.mHandle = nullptr; |
| return *this; |
| } |
| |
| ~BufferHandle() { reset(); } |
| |
| constexpr explicit operator bool() const noexcept { return mHandle != nullptr; } |
| |
| buffer_handle_t operator*() const noexcept { return mHandle; } |
| |
| void reset() { |
| if (mHandle != nullptr) { |
| EXPECT_EQ(AIMAPPER_ERROR_NONE, mIMapper->v5.freeBuffer(mHandle)); |
| mHandle = nullptr; |
| } |
| } |
| }; |
| |
| class BufferAllocation { |
| AIMapper* mIMapper; |
| native_handle_t* mRawHandle; |
| uint32_t mStride; |
| const BufferDescriptorInfo mInfo; |
| |
| public: |
| BufferAllocation(const BufferAllocation&) = delete; |
| void operator=(const BufferAllocation&) = delete; |
| |
| BufferAllocation(AIMapper* mapper, native_handle_t* handle, uint32_t stride, |
| const BufferDescriptorInfo& info) |
| : mIMapper(mapper), mRawHandle(handle), mStride(stride), mInfo(info) {} |
| |
| ~BufferAllocation() { |
| if (mRawHandle == nullptr) return; |
| |
| native_handle_close(mRawHandle); |
| native_handle_delete(mRawHandle); |
| } |
| |
| uint32_t stride() const { return mStride; } |
| const BufferDescriptorInfo& info() const { return mInfo; } |
| |
| BufferHandle import() { return BufferHandle{mIMapper, mRawHandle}; } |
| |
| const native_handle_t* rawHandle() const { return mRawHandle; } |
| }; |
| |
| class GraphicsTestsBase { |
| private: |
| friend class BufferAllocation; |
| int32_t mIAllocatorVersion = 1; |
| std::shared_ptr<IAllocator> mAllocator; |
| AIMapper* mIMapper = nullptr; |
| AIMapper_loadIMapperFn mIMapperLoader; |
| |
| protected: |
| void Initialize(std::shared_ptr<IAllocator> allocator) { |
| mAllocator = allocator; |
| ASSERT_NE(nullptr, mAllocator.get()) << "failed to get allocator service"; |
| ASSERT_TRUE(mAllocator->getInterfaceVersion(&mIAllocatorVersion).isOk()); |
| ASSERT_GE(mIAllocatorVersion, 2); |
| std::string mapperSuffix; |
| auto status = mAllocator->getIMapperLibrarySuffix(&mapperSuffix); |
| ASSERT_TRUE(status.isOk()) << "Failed to get IMapper library suffix"; |
| std::string lib_name = "mapper." + mapperSuffix + ".so"; |
| void* so = android_load_sphal_library(lib_name.c_str(), RTLD_LOCAL | RTLD_NOW); |
| ASSERT_NE(nullptr, so) << "Failed to load " << lib_name; |
| mIMapperLoader = (AIMapper_loadIMapperFn)dlsym(so, "AIMapper_loadIMapper"); |
| ASSERT_NE(nullptr, mIMapperLoader) << "AIMapper_locaIMapper missing from " << lib_name; |
| ASSERT_EQ(AIMAPPER_ERROR_NONE, mIMapperLoader(&mIMapper)); |
| ASSERT_NE(mIMapper, nullptr); |
| } |
| |
| public: |
| AIMapper_loadIMapperFn getIMapperLoader() const { return mIMapperLoader; } |
| |
| std::unique_ptr<BufferAllocation> allocate(const BufferDescriptorInfo& descriptorInfo) { |
| AllocationResult result; |
| ::ndk::ScopedAStatus status = mAllocator->allocate2(descriptorInfo, 1, &result); |
| if (!status.isOk()) { |
| status_t error = status.getExceptionCode(); |
| if (error == EX_SERVICE_SPECIFIC) { |
| error = status.getServiceSpecificError(); |
| EXPECT_NE(OK, error) << "Failed to set error properly"; |
| } else { |
| EXPECT_EQ(OK, error) << "Allocation transport failure"; |
| } |
| return nullptr; |
| } else { |
| return std::make_unique<BufferAllocation>(mIMapper, dupFromAidl(result.buffers[0]), |
| result.stride, descriptorInfo); |
| } |
| } |
| |
| std::unique_ptr<BufferAllocation> allocateGeneric() { |
| return allocate({ |
| .name = {"VTS_TEMP"}, |
| .width = 64, |
| .height = 64, |
| .layerCount = 1, |
| .format = PixelFormat::RGBA_8888, |
| .usage = BufferUsage::CPU_WRITE_OFTEN | BufferUsage::CPU_READ_OFTEN, |
| .reservedSize = 0, |
| }); |
| } |
| |
| bool isSupported(const BufferDescriptorInfo& descriptorInfo) { |
| bool ret = false; |
| EXPECT_TRUE(mAllocator->isSupported(descriptorInfo, &ret).isOk()); |
| return ret; |
| } |
| |
| AIMapper* mapper() const { return mIMapper; } |
| |
| template <StandardMetadataType T> |
| auto getStandardMetadata(buffer_handle_t bufferHandle) |
| -> decltype(StandardMetadata<T>::value::decode(nullptr, 0)) { |
| using Value = typename StandardMetadata<T>::value; |
| std::vector<uint8_t> buffer; |
| // Initial guess |
| buffer.resize(512); |
| int32_t sizeRequired = mapper()->v5.getStandardMetadata( |
| bufferHandle, static_cast<int64_t>(T), buffer.data(), buffer.size()); |
| if (sizeRequired < 0) { |
| EXPECT_EQ(-AIMAPPER_ERROR_UNSUPPORTED, sizeRequired) |
| << "Received something other than UNSUPPORTED from valid getStandardMetadata " |
| "call"; |
| return std::nullopt; |
| } |
| if (sizeRequired > buffer.size()) { |
| buffer.resize(sizeRequired); |
| sizeRequired = mapper()->v5.getStandardMetadata(bufferHandle, static_cast<int64_t>(T), |
| buffer.data(), buffer.size()); |
| } |
| if (sizeRequired < 0 || sizeRequired >= buffer.size()) { |
| ADD_FAILURE() << "getStandardMetadata failed, received " << sizeRequired |
| << " with buffer size " << buffer.size(); |
| // Generate a fail type |
| return std::nullopt; |
| } |
| return Value::decode(buffer.data(), sizeRequired); |
| } |
| |
| template <StandardMetadataType T> |
| AIMapper_Error setStandardMetadata(buffer_handle_t bufferHandle, |
| const typename StandardMetadata<T>::value_type& value) { |
| using Value = typename StandardMetadata<T>::value; |
| int32_t sizeRequired = Value::encode(value, nullptr, 0); |
| if (sizeRequired < 0) { |
| EXPECT_GE(sizeRequired, 0) << "Failed to calculate required size"; |
| return static_cast<AIMapper_Error>(-sizeRequired); |
| } |
| std::vector<uint8_t> buffer; |
| buffer.resize(sizeRequired); |
| sizeRequired = Value::encode(value, buffer.data(), buffer.size()); |
| if (sizeRequired < 0 || sizeRequired > buffer.size()) { |
| ADD_FAILURE() << "Failed to encode with calculated size " << sizeRequired |
| << "; buffer size" << buffer.size(); |
| return static_cast<AIMapper_Error>(-sizeRequired); |
| } |
| return mapper()->v5.setStandardMetadata(bufferHandle, static_cast<int64_t>(T), |
| buffer.data(), sizeRequired); |
| } |
| |
| void verifyRGBA8888PlaneLayouts(const std::vector<PlaneLayout>& planeLayouts) { |
| ASSERT_EQ(1, planeLayouts.size()); |
| |
| const auto& planeLayout = planeLayouts.front(); |
| |
| ASSERT_EQ(4, planeLayout.components.size()); |
| |
| int64_t offsetInBitsR = -1; |
| int64_t offsetInBitsG = -1; |
| int64_t offsetInBitsB = -1; |
| int64_t offsetInBitsA = -1; |
| |
| for (const auto& component : planeLayout.components) { |
| if (!gralloc4::isStandardPlaneLayoutComponentType(component.type)) { |
| continue; |
| } |
| EXPECT_EQ(8, component.sizeInBits); |
| if (component.type.value == gralloc4::PlaneLayoutComponentType_R.value) { |
| offsetInBitsR = component.offsetInBits; |
| } |
| if (component.type.value == gralloc4::PlaneLayoutComponentType_G.value) { |
| offsetInBitsG = component.offsetInBits; |
| } |
| if (component.type.value == gralloc4::PlaneLayoutComponentType_B.value) { |
| offsetInBitsB = component.offsetInBits; |
| } |
| if (component.type.value == gralloc4::PlaneLayoutComponentType_A.value) { |
| offsetInBitsA = component.offsetInBits; |
| } |
| } |
| |
| EXPECT_EQ(0, offsetInBitsR); |
| EXPECT_EQ(8, offsetInBitsG); |
| EXPECT_EQ(16, offsetInBitsB); |
| EXPECT_EQ(24, offsetInBitsA); |
| |
| EXPECT_EQ(0, planeLayout.offsetInBytes); |
| EXPECT_EQ(32, planeLayout.sampleIncrementInBits); |
| // Skip testing stride because any stride is valid |
| EXPECT_LE(planeLayout.widthInSamples * planeLayout.heightInSamples * 4, |
| planeLayout.totalSizeInBytes); |
| EXPECT_EQ(1, planeLayout.horizontalSubsampling); |
| EXPECT_EQ(1, planeLayout.verticalSubsampling); |
| } |
| |
| void fillRGBA8888(uint8_t* data, uint32_t height, size_t strideInBytes, size_t widthInBytes) { |
| for (uint32_t y = 0; y < height; y++) { |
| memset(data, y, widthInBytes); |
| data += strideInBytes; |
| } |
| } |
| |
| void verifyRGBA8888(const buffer_handle_t bufferHandle, const uint8_t* data, uint32_t height, |
| size_t strideInBytes, size_t widthInBytes) { |
| auto decodeResult = getStandardMetadata<StandardMetadataType::PLANE_LAYOUTS>(bufferHandle); |
| ASSERT_TRUE(decodeResult.has_value()); |
| const auto& planeLayouts = *decodeResult; |
| ASSERT_TRUE(planeLayouts.size() > 0); |
| |
| verifyRGBA8888PlaneLayouts(planeLayouts); |
| |
| for (uint32_t y = 0; y < height; y++) { |
| for (size_t i = 0; i < widthInBytes; i++) { |
| EXPECT_EQ(static_cast<uint8_t>(y), data[i]); |
| } |
| data += strideInBytes; |
| } |
| } |
| |
| void traverseYCbCrData(const android_ycbcr& yCbCr, int32_t width, int32_t height, |
| int64_t hSubsampling, int64_t vSubsampling, |
| std::function<void(uint8_t*, uint8_t)> traverseFuncion) { |
| auto yData = static_cast<uint8_t*>(yCbCr.y); |
| auto cbData = static_cast<uint8_t*>(yCbCr.cb); |
| auto crData = static_cast<uint8_t*>(yCbCr.cr); |
| auto yStride = yCbCr.ystride; |
| auto cStride = yCbCr.cstride; |
| auto chromaStep = yCbCr.chroma_step; |
| |
| for (uint32_t y = 0; y < height; y++) { |
| for (uint32_t x = 0; x < width; x++) { |
| auto val = static_cast<uint8_t>(height * y + x); |
| |
| traverseFuncion(yData + yStride * y + x, val); |
| |
| if (y % vSubsampling == 0 && x % hSubsampling == 0) { |
| uint32_t subSampleX = x / hSubsampling; |
| uint32_t subSampleY = y / vSubsampling; |
| const auto subSampleOffset = cStride * subSampleY + chromaStep * subSampleX; |
| const auto subSampleVal = |
| static_cast<uint8_t>(height * subSampleY + subSampleX); |
| |
| traverseFuncion(cbData + subSampleOffset, subSampleVal); |
| traverseFuncion(crData + subSampleOffset, subSampleVal + 1); |
| } |
| } |
| } |
| } |
| |
| void fillYCbCrData(const android_ycbcr& yCbCr, int32_t width, int32_t height, |
| int64_t hSubsampling, int64_t vSubsampling) { |
| traverseYCbCrData(yCbCr, width, height, hSubsampling, vSubsampling, |
| [](auto address, auto fillingData) { *address = fillingData; }); |
| } |
| |
| void verifyYCbCrData(const android_ycbcr& yCbCr, int32_t width, int32_t height, |
| int64_t hSubsampling, int64_t vSubsampling) { |
| traverseYCbCrData( |
| yCbCr, width, height, hSubsampling, vSubsampling, |
| [](auto address, auto expectedData) { EXPECT_EQ(*address, expectedData); }); |
| } |
| |
| constexpr uint64_t bitsToBytes(int64_t bits) { return bits / 8; } |
| constexpr uint64_t bytesToBits(int64_t bytes) { return bytes * 8; } |
| |
| void getAndroidYCbCr(buffer_handle_t bufferHandle, uint8_t* data, android_ycbcr* outYCbCr, |
| int64_t* hSubsampling, int64_t* vSubsampling) { |
| auto decodeResult = getStandardMetadata<StandardMetadataType::PLANE_LAYOUTS>(bufferHandle); |
| ASSERT_TRUE(decodeResult.has_value()); |
| const auto& planeLayouts = *decodeResult; |
| ASSERT_TRUE(planeLayouts.size() > 0); |
| |
| outYCbCr->y = nullptr; |
| outYCbCr->cb = nullptr; |
| outYCbCr->cr = nullptr; |
| outYCbCr->ystride = 0; |
| outYCbCr->cstride = 0; |
| outYCbCr->chroma_step = 0; |
| |
| for (const auto& planeLayout : planeLayouts) { |
| for (const auto& planeLayoutComponent : planeLayout.components) { |
| if (!gralloc4::isStandardPlaneLayoutComponentType(planeLayoutComponent.type)) { |
| continue; |
| } |
| ASSERT_EQ(0, planeLayoutComponent.offsetInBits % 8); |
| |
| uint8_t* tmpData = data + planeLayout.offsetInBytes + |
| bitsToBytes(planeLayoutComponent.offsetInBits); |
| uint64_t sampleIncrementInBytes; |
| |
| auto type = static_cast<PlaneLayoutComponentType>(planeLayoutComponent.type.value); |
| switch (type) { |
| case PlaneLayoutComponentType::Y: |
| ASSERT_EQ(nullptr, outYCbCr->y); |
| ASSERT_EQ(8, planeLayoutComponent.sizeInBits); |
| ASSERT_EQ(8, planeLayout.sampleIncrementInBits); |
| outYCbCr->y = tmpData; |
| outYCbCr->ystride = planeLayout.strideInBytes; |
| break; |
| |
| case PlaneLayoutComponentType::CB: |
| case PlaneLayoutComponentType::CR: |
| ASSERT_EQ(0, planeLayout.sampleIncrementInBits % 8); |
| |
| sampleIncrementInBytes = planeLayout.sampleIncrementInBits / 8; |
| ASSERT_TRUE(sampleIncrementInBytes == 1 || sampleIncrementInBytes == 2); |
| |
| if (outYCbCr->cstride == 0 && outYCbCr->chroma_step == 0) { |
| outYCbCr->cstride = planeLayout.strideInBytes; |
| outYCbCr->chroma_step = sampleIncrementInBytes; |
| } else { |
| ASSERT_EQ(outYCbCr->cstride, planeLayout.strideInBytes); |
| ASSERT_EQ(outYCbCr->chroma_step, sampleIncrementInBytes); |
| } |
| |
| if (*hSubsampling == 0 && *vSubsampling == 0) { |
| *hSubsampling = planeLayout.horizontalSubsampling; |
| *vSubsampling = planeLayout.verticalSubsampling; |
| } else { |
| ASSERT_EQ(*hSubsampling, planeLayout.horizontalSubsampling); |
| ASSERT_EQ(*vSubsampling, planeLayout.verticalSubsampling); |
| } |
| |
| if (type == PlaneLayoutComponentType::CB) { |
| ASSERT_EQ(nullptr, outYCbCr->cb); |
| outYCbCr->cb = tmpData; |
| } else { |
| ASSERT_EQ(nullptr, outYCbCr->cr); |
| outYCbCr->cr = tmpData; |
| } |
| break; |
| default: |
| break; |
| }; |
| } |
| } |
| |
| ASSERT_NE(nullptr, outYCbCr->y); |
| ASSERT_NE(nullptr, outYCbCr->cb); |
| ASSERT_NE(nullptr, outYCbCr->cr); |
| } |
| |
| YCbCr getAndroidYCbCr_P010(const native_handle_t* bufferHandle, uint8_t* data) { |
| YCbCr yCbCr_P010; |
| auto decodeResult = getStandardMetadata<StandardMetadataType::PLANE_LAYOUTS>(bufferHandle); |
| if (!decodeResult.has_value()) { |
| ADD_FAILURE() << "failed to get plane layout"; |
| return YCbCr{}; |
| } |
| const auto& planeLayouts = *decodeResult; |
| EXPECT_EQ(2, planeLayouts.size()); |
| EXPECT_EQ(1, planeLayouts[0].components.size()); |
| EXPECT_EQ(2, planeLayouts[1].components.size()); |
| |
| yCbCr_P010.yCbCr.y = nullptr; |
| yCbCr_P010.yCbCr.cb = nullptr; |
| yCbCr_P010.yCbCr.cr = nullptr; |
| yCbCr_P010.yCbCr.ystride = 0; |
| yCbCr_P010.yCbCr.cstride = 0; |
| yCbCr_P010.yCbCr.chroma_step = 0; |
| int64_t cb_offset = 0; |
| int64_t cr_offset = 0; |
| |
| for (const auto& planeLayout : planeLayouts) { |
| for (const auto& planeLayoutComponent : planeLayout.components) { |
| if (!gralloc4::isStandardPlaneLayoutComponentType(planeLayoutComponent.type)) { |
| continue; |
| } |
| |
| uint8_t* tmpData = data + planeLayout.offsetInBytes + |
| bitsToBytes(planeLayoutComponent.offsetInBits); |
| uint64_t sampleIncrementInBytes = 0; |
| auto type = static_cast<PlaneLayoutComponentType>(planeLayoutComponent.type.value); |
| switch (type) { |
| case PlaneLayoutComponentType::Y: |
| // For specs refer: |
| // https://docs.microsoft.com/en-us/windows/win32/medfound/10-bit-and-16-bit-yuv-video-formats |
| EXPECT_EQ(6, planeLayoutComponent.offsetInBits); |
| EXPECT_EQ(nullptr, yCbCr_P010.yCbCr.y); |
| EXPECT_EQ(10, planeLayoutComponent.sizeInBits); |
| EXPECT_EQ(16, planeLayout.sampleIncrementInBits); |
| |
| yCbCr_P010.yCbCr.y = tmpData; |
| yCbCr_P010.yCbCr.ystride = planeLayout.strideInBytes; |
| break; |
| |
| case PlaneLayoutComponentType::CB: |
| case PlaneLayoutComponentType::CR: |
| sampleIncrementInBytes = bitsToBytes(planeLayout.sampleIncrementInBits); |
| EXPECT_EQ(4, sampleIncrementInBytes); |
| |
| if (yCbCr_P010.yCbCr.cstride == 0 && yCbCr_P010.yCbCr.chroma_step == 0) { |
| yCbCr_P010.yCbCr.cstride = planeLayout.strideInBytes; |
| yCbCr_P010.yCbCr.chroma_step = sampleIncrementInBytes; |
| } else { |
| EXPECT_EQ(yCbCr_P010.yCbCr.cstride, planeLayout.strideInBytes); |
| EXPECT_EQ(yCbCr_P010.yCbCr.chroma_step, sampleIncrementInBytes); |
| } |
| |
| if (yCbCr_P010.horizontalSubSampling == 0 && |
| yCbCr_P010.verticalSubSampling == 0) { |
| yCbCr_P010.horizontalSubSampling = planeLayout.horizontalSubsampling; |
| yCbCr_P010.verticalSubSampling = planeLayout.verticalSubsampling; |
| } else { |
| EXPECT_EQ(yCbCr_P010.horizontalSubSampling, |
| planeLayout.horizontalSubsampling); |
| EXPECT_EQ(yCbCr_P010.verticalSubSampling, |
| planeLayout.verticalSubsampling); |
| } |
| |
| if (type == PlaneLayoutComponentType::CB) { |
| EXPECT_EQ(nullptr, yCbCr_P010.yCbCr.cb); |
| yCbCr_P010.yCbCr.cb = tmpData; |
| cb_offset = planeLayoutComponent.offsetInBits; |
| } else { |
| EXPECT_EQ(nullptr, yCbCr_P010.yCbCr.cr); |
| yCbCr_P010.yCbCr.cr = tmpData; |
| cr_offset = planeLayoutComponent.offsetInBits; |
| } |
| break; |
| default: |
| break; |
| }; |
| } |
| } |
| |
| EXPECT_EQ(cb_offset + bytesToBits(2), cr_offset); |
| EXPECT_NE(nullptr, yCbCr_P010.yCbCr.y); |
| EXPECT_NE(nullptr, yCbCr_P010.yCbCr.cb); |
| EXPECT_NE(nullptr, yCbCr_P010.yCbCr.cr); |
| return yCbCr_P010; |
| } |
| }; |
| |
| class GraphicsMapperStableCTests |
| : public GraphicsTestsBase, |
| public ::testing::TestWithParam<std::tuple<std::string, std::shared_ptr<IAllocator>>> { |
| public: |
| void SetUp() override { Initialize(std::get<1>(GetParam())); } |
| |
| void TearDown() override {} |
| }; |
| |
| TEST_P(GraphicsMapperStableCTests, AllV5CallbacksDefined) { |
| ASSERT_GE(mapper()->version, AIMAPPER_VERSION_5); |
| |
| EXPECT_TRUE(mapper()->v5.importBuffer); |
| EXPECT_TRUE(mapper()->v5.freeBuffer); |
| EXPECT_TRUE(mapper()->v5.getTransportSize); |
| EXPECT_TRUE(mapper()->v5.lock); |
| EXPECT_TRUE(mapper()->v5.unlock); |
| EXPECT_TRUE(mapper()->v5.flushLockedBuffer); |
| EXPECT_TRUE(mapper()->v5.rereadLockedBuffer); |
| EXPECT_TRUE(mapper()->v5.getMetadata); |
| EXPECT_TRUE(mapper()->v5.getStandardMetadata); |
| EXPECT_TRUE(mapper()->v5.setMetadata); |
| EXPECT_TRUE(mapper()->v5.setStandardMetadata); |
| EXPECT_TRUE(mapper()->v5.listSupportedMetadataTypes); |
| EXPECT_TRUE(mapper()->v5.dumpBuffer); |
| EXPECT_TRUE(mapper()->v5.getReservedRegion); |
| } |
| |
| TEST_P(GraphicsMapperStableCTests, DualLoadIsIdentical) { |
| ASSERT_GE(mapper()->version, AIMAPPER_VERSION_5); |
| AIMapper* secondMapper; |
| ASSERT_EQ(AIMAPPER_ERROR_NONE, getIMapperLoader()(&secondMapper)); |
| |
| EXPECT_EQ(secondMapper->v5.importBuffer, mapper()->v5.importBuffer); |
| EXPECT_EQ(secondMapper->v5.freeBuffer, mapper()->v5.freeBuffer); |
| EXPECT_EQ(secondMapper->v5.getTransportSize, mapper()->v5.getTransportSize); |
| EXPECT_EQ(secondMapper->v5.lock, mapper()->v5.lock); |
| EXPECT_EQ(secondMapper->v5.unlock, mapper()->v5.unlock); |
| EXPECT_EQ(secondMapper->v5.flushLockedBuffer, mapper()->v5.flushLockedBuffer); |
| EXPECT_EQ(secondMapper->v5.rereadLockedBuffer, mapper()->v5.rereadLockedBuffer); |
| EXPECT_EQ(secondMapper->v5.getMetadata, mapper()->v5.getMetadata); |
| EXPECT_EQ(secondMapper->v5.getStandardMetadata, mapper()->v5.getStandardMetadata); |
| EXPECT_EQ(secondMapper->v5.setMetadata, mapper()->v5.setMetadata); |
| EXPECT_EQ(secondMapper->v5.setStandardMetadata, mapper()->v5.setStandardMetadata); |
| EXPECT_EQ(secondMapper->v5.listSupportedMetadataTypes, mapper()->v5.listSupportedMetadataTypes); |
| EXPECT_EQ(secondMapper->v5.dumpBuffer, mapper()->v5.dumpBuffer); |
| EXPECT_EQ(secondMapper->v5.getReservedRegion, mapper()->v5.getReservedRegion); |
| } |
| |
| TEST_P(GraphicsMapperStableCTests, CanAllocate) { |
| auto buffer = allocate({ |
| .name = {"VTS_TEMP"}, |
| .width = 64, |
| .height = 64, |
| .layerCount = 1, |
| .format = PixelFormat::RGBA_8888, |
| .usage = BufferUsage::CPU_WRITE_OFTEN | BufferUsage::CPU_READ_OFTEN, |
| .reservedSize = 0, |
| }); |
| ASSERT_NE(nullptr, buffer.get()); |
| EXPECT_GE(buffer->stride(), 64); |
| } |
| |
| TEST_P(GraphicsMapperStableCTests, ImportFreeBuffer) { |
| auto buffer = allocate({ |
| .name = {"VTS_TEMP"}, |
| .width = 64, |
| .height = 64, |
| .layerCount = 1, |
| .format = PixelFormat::RGBA_8888, |
| .usage = BufferUsage::CPU_WRITE_OFTEN | BufferUsage::CPU_READ_OFTEN, |
| .reservedSize = 0, |
| }); |
| ASSERT_NE(nullptr, buffer.get()); |
| EXPECT_GE(buffer->stride(), 64); |
| |
| { |
| auto import1 = buffer->import(); |
| auto import2 = buffer->import(); |
| EXPECT_TRUE(import1); |
| EXPECT_TRUE(import2); |
| EXPECT_NE(*import1, *import2); |
| } |
| } |
| |
| /** |
| * Test IMapper::importBuffer and IMapper::freeBuffer cross mapper instances. |
| */ |
| TEST_P(GraphicsMapperStableCTests, ImportFreeBufferSingleton) { |
| auto buffer = allocate({ |
| .name = {"VTS_TEMP"}, |
| .width = 64, |
| .height = 64, |
| .layerCount = 1, |
| .format = PixelFormat::RGBA_8888, |
| .usage = BufferUsage::CPU_WRITE_OFTEN | BufferUsage::CPU_READ_OFTEN, |
| .reservedSize = 0, |
| }); |
| ASSERT_NE(nullptr, buffer.get()); |
| EXPECT_GE(buffer->stride(), 64); |
| |
| buffer_handle_t bufferHandle = nullptr; |
| ASSERT_EQ(AIMAPPER_ERROR_NONE, mapper()->v5.importBuffer(buffer->rawHandle(), &bufferHandle)); |
| ASSERT_NE(nullptr, bufferHandle); |
| |
| AIMapper* secondMapper; |
| ASSERT_EQ(AIMAPPER_ERROR_NONE, getIMapperLoader()(&secondMapper)); |
| ASSERT_EQ(AIMAPPER_ERROR_NONE, secondMapper->v5.freeBuffer(bufferHandle)); |
| } |
| |
| /** |
| * Test IMapper::importBuffer with invalid buffers. |
| */ |
| TEST_P(GraphicsMapperStableCTests, ImportBufferNegative) { |
| native_handle_t* invalidHandle = nullptr; |
| buffer_handle_t bufferHandle = nullptr; |
| EXPECT_EQ(AIMAPPER_ERROR_BAD_BUFFER, mapper()->v5.importBuffer(invalidHandle, &bufferHandle)) |
| << "importBuffer with nullptr did not fail with BAD_BUFFER"; |
| |
| invalidHandle = native_handle_create(0, 0); |
| EXPECT_EQ(AIMAPPER_ERROR_BAD_BUFFER, mapper()->v5.importBuffer(invalidHandle, &bufferHandle)) |
| << "importBuffer with invalid handle did not fail with BAD_BUFFER"; |
| native_handle_delete(invalidHandle); |
| } |
| |
| /** |
| * Test IMapper::freeBuffer with invalid buffers. |
| */ |
| TEST_P(GraphicsMapperStableCTests, FreeBufferNegative) { |
| native_handle_t* bufferHandle = nullptr; |
| EXPECT_EQ(AIMAPPER_ERROR_BAD_BUFFER, mapper()->v5.freeBuffer(bufferHandle)) |
| << "freeBuffer with nullptr did not fail with BAD_BUFFER"; |
| |
| bufferHandle = native_handle_create(0, 0); |
| EXPECT_EQ(AIMAPPER_ERROR_BAD_BUFFER, mapper()->v5.freeBuffer(bufferHandle)) |
| << "freeBuffer with invalid handle did not fail with BAD_BUFFER"; |
| native_handle_delete(bufferHandle); |
| |
| auto buffer = allocateGeneric(); |
| EXPECT_EQ(AIMAPPER_ERROR_BAD_BUFFER, mapper()->v5.freeBuffer(buffer->rawHandle())) |
| << "freeBuffer with un-imported handle did not fail with BAD_BUFFER"; |
| } |
| |
| /** |
| * Test IMapper::lock and IMapper::unlock. |
| */ |
| TEST_P(GraphicsMapperStableCTests, LockUnlockBasic) { |
| constexpr auto usage = BufferUsage::CPU_WRITE_OFTEN | BufferUsage::CPU_READ_OFTEN; |
| auto buffer = allocate({ |
| .name = {"VTS_TEMP"}, |
| .width = 64, |
| .height = 64, |
| .layerCount = 1, |
| .format = PixelFormat::RGBA_8888, |
| .usage = usage, |
| .reservedSize = 0, |
| }); |
| ASSERT_NE(nullptr, buffer.get()); |
| |
| // lock buffer for writing |
| const auto& info = buffer->info(); |
| const auto stride = buffer->stride(); |
| const ARect region{0, 0, info.width, info.height}; |
| auto handle = buffer->import(); |
| uint8_t* data = nullptr; |
| ASSERT_EQ(AIMAPPER_ERROR_NONE, |
| mapper()->v5.lock(*handle, static_cast<int64_t>(usage), region, -1, (void**)&data)); |
| |
| // RGBA_8888 |
| fillRGBA8888(data, info.height, stride * 4, info.width * 4); |
| |
| int releaseFence = -1; |
| ASSERT_EQ(AIMAPPER_ERROR_NONE, mapper()->v5.unlock(*handle, &releaseFence)); |
| |
| // lock again for reading |
| ASSERT_EQ(AIMAPPER_ERROR_NONE, mapper()->v5.lock(*handle, static_cast<int64_t>(usage), region, |
| releaseFence, (void**)&data)); |
| releaseFence = -1; |
| |
| ASSERT_NO_FATAL_FAILURE(verifyRGBA8888(*handle, data, info.height, stride * 4, info.width * 4)); |
| |
| releaseFence = -1; |
| ASSERT_EQ(AIMAPPER_ERROR_NONE, mapper()->v5.unlock(*handle, &releaseFence)); |
| if (releaseFence != -1) { |
| close(releaseFence); |
| } |
| } |
| |
| /** |
| * Test multiple operations associated with different color formats |
| */ |
| TEST_P(GraphicsMapperStableCTests, Lock_YCRCB_420_SP) { |
| BufferDescriptorInfo info{ |
| .name = {"VTS_TEMP"}, |
| .width = 64, |
| .height = 64, |
| .layerCount = 1, |
| .format = PixelFormat::YCRCB_420_SP, |
| .usage = BufferUsage::CPU_WRITE_OFTEN | BufferUsage::CPU_READ_OFTEN, |
| .reservedSize = 0, |
| }; |
| auto buffer = allocate(info); |
| if (!buffer) { |
| ASSERT_FALSE(isSupported(info)); |
| GTEST_SUCCEED() << "YCRCB_420_SP format is unsupported"; |
| return; |
| } |
| |
| // lock buffer for writing |
| const ARect region{0, 0, info.width, info.height}; |
| auto handle = buffer->import(); |
| uint8_t* data = nullptr; |
| ASSERT_EQ(AIMAPPER_ERROR_NONE, mapper()->v5.lock(*handle, static_cast<int64_t>(info.usage), |
| region, -1, (void**)&data)); |
| |
| android_ycbcr yCbCr; |
| int64_t hSubsampling = 0; |
| int64_t vSubsampling = 0; |
| ASSERT_NO_FATAL_FAILURE(getAndroidYCbCr(*handle, data, &yCbCr, &hSubsampling, &vSubsampling)); |
| |
| constexpr uint32_t kCbCrSubSampleFactor = 2; |
| ASSERT_EQ(kCbCrSubSampleFactor, hSubsampling); |
| ASSERT_EQ(kCbCrSubSampleFactor, vSubsampling); |
| |
| auto cbData = static_cast<uint8_t*>(yCbCr.cb); |
| auto crData = static_cast<uint8_t*>(yCbCr.cr); |
| ASSERT_EQ(crData + 1, cbData); |
| ASSERT_EQ(2, yCbCr.chroma_step); |
| |
| fillYCbCrData(yCbCr, info.width, info.height, hSubsampling, vSubsampling); |
| |
| int releaseFence = -1; |
| ASSERT_EQ(AIMAPPER_ERROR_NONE, mapper()->v5.unlock(*handle, &releaseFence)); |
| |
| // lock again for reading |
| ASSERT_EQ(AIMAPPER_ERROR_NONE, mapper()->v5.lock(*handle, static_cast<int64_t>(info.usage), |
| region, releaseFence, (void**)&data)); |
| releaseFence = -1; |
| |
| ASSERT_NO_FATAL_FAILURE(getAndroidYCbCr(*handle, data, &yCbCr, &hSubsampling, &vSubsampling)); |
| |
| verifyYCbCrData(yCbCr, info.width, info.height, hSubsampling, vSubsampling); |
| |
| releaseFence = -1; |
| ASSERT_EQ(AIMAPPER_ERROR_NONE, mapper()->v5.unlock(*handle, &releaseFence)); |
| if (releaseFence != -1) { |
| close(releaseFence); |
| } |
| } |
| |
| TEST_P(GraphicsMapperStableCTests, YV12SubsampleMetadata) { |
| BufferDescriptorInfo info{ |
| .name = {"VTS_TEMP"}, |
| .width = 64, |
| .height = 64, |
| .layerCount = 1, |
| .format = PixelFormat::YV12, |
| .usage = BufferUsage::CPU_WRITE_OFTEN | BufferUsage::CPU_READ_OFTEN, |
| .reservedSize = 0, |
| }; |
| auto buffer = allocate(info); |
| ASSERT_NE(nullptr, buffer.get()); |
| |
| // lock buffer for writing |
| const ARect region{0, 0, info.width, info.height}; |
| auto handle = buffer->import(); |
| uint8_t* data = nullptr; |
| ASSERT_EQ(AIMAPPER_ERROR_NONE, mapper()->v5.lock(*handle, static_cast<int64_t>(info.usage), |
| region, -1, (void**)&data)); |
| |
| auto decodeResult = getStandardMetadata<StandardMetadataType::PLANE_LAYOUTS>(*handle); |
| ASSERT_TRUE(decodeResult.has_value()); |
| const auto& planeLayouts = *decodeResult; |
| |
| ASSERT_EQ(3, planeLayouts.size()); |
| |
| auto yPlane = planeLayouts[0]; |
| auto crPlane = planeLayouts[1]; |
| auto cbPlane = planeLayouts[2]; |
| |
| constexpr uint32_t kCbCrSubSampleFactor = 2; |
| EXPECT_EQ(kCbCrSubSampleFactor, crPlane.horizontalSubsampling); |
| EXPECT_EQ(kCbCrSubSampleFactor, crPlane.verticalSubsampling); |
| |
| EXPECT_EQ(kCbCrSubSampleFactor, cbPlane.horizontalSubsampling); |
| EXPECT_EQ(kCbCrSubSampleFactor, cbPlane.verticalSubsampling); |
| |
| const long chromaSampleWidth = info.width / kCbCrSubSampleFactor; |
| const long chromaSampleHeight = info.height / kCbCrSubSampleFactor; |
| |
| EXPECT_EQ(info.width, yPlane.widthInSamples); |
| EXPECT_EQ(info.height, yPlane.heightInSamples); |
| |
| EXPECT_EQ(chromaSampleWidth, crPlane.widthInSamples); |
| EXPECT_EQ(chromaSampleHeight, crPlane.heightInSamples); |
| |
| EXPECT_EQ(chromaSampleWidth, cbPlane.widthInSamples); |
| EXPECT_EQ(chromaSampleHeight, cbPlane.heightInSamples); |
| |
| EXPECT_LE(crPlane.widthInSamples, crPlane.strideInBytes); |
| EXPECT_LE(cbPlane.widthInSamples, cbPlane.strideInBytes); |
| |
| int releaseFence = -1; |
| ASSERT_EQ(AIMAPPER_ERROR_NONE, mapper()->v5.unlock(*handle, &releaseFence)); |
| if (releaseFence != -1) { |
| close(releaseFence); |
| } |
| } |
| |
| TEST_P(GraphicsMapperStableCTests, Lock_YV12) { |
| BufferDescriptorInfo info{ |
| .name = {"VTS_TEMP"}, |
| .width = 64, |
| .height = 64, |
| .layerCount = 1, |
| .format = PixelFormat::YV12, |
| .usage = BufferUsage::CPU_WRITE_OFTEN | BufferUsage::CPU_READ_OFTEN, |
| .reservedSize = 0, |
| }; |
| auto buffer = allocate(info); |
| ASSERT_NE(nullptr, buffer.get()); |
| |
| // lock buffer for writing |
| const ARect region{0, 0, info.width, info.height}; |
| auto handle = buffer->import(); |
| uint8_t* data = nullptr; |
| ASSERT_EQ(AIMAPPER_ERROR_NONE, mapper()->v5.lock(*handle, static_cast<int64_t>(info.usage), |
| region, -1, (void**)&data)); |
| |
| android_ycbcr yCbCr; |
| int64_t hSubsampling = 0; |
| int64_t vSubsampling = 0; |
| ASSERT_NO_FATAL_FAILURE(getAndroidYCbCr(*handle, data, &yCbCr, &hSubsampling, &vSubsampling)); |
| |
| constexpr uint32_t kCbCrSubSampleFactor = 2; |
| ASSERT_EQ(kCbCrSubSampleFactor, hSubsampling); |
| ASSERT_EQ(kCbCrSubSampleFactor, vSubsampling); |
| |
| auto cbData = static_cast<uint8_t*>(yCbCr.cb); |
| auto crData = static_cast<uint8_t*>(yCbCr.cr); |
| ASSERT_EQ(crData + yCbCr.cstride * info.height / vSubsampling, cbData); |
| ASSERT_EQ(1, yCbCr.chroma_step); |
| |
| fillYCbCrData(yCbCr, info.width, info.height, hSubsampling, vSubsampling); |
| |
| int releaseFence = -1; |
| ASSERT_EQ(AIMAPPER_ERROR_NONE, mapper()->v5.unlock(*handle, &releaseFence)); |
| |
| // lock again for reading |
| ASSERT_EQ(AIMAPPER_ERROR_NONE, mapper()->v5.lock(*handle, static_cast<int64_t>(info.usage), |
| region, releaseFence, (void**)&data)); |
| releaseFence = -1; |
| |
| ASSERT_NO_FATAL_FAILURE(getAndroidYCbCr(*handle, data, &yCbCr, &hSubsampling, &vSubsampling)); |
| |
| verifyYCbCrData(yCbCr, info.width, info.height, hSubsampling, vSubsampling); |
| |
| ASSERT_EQ(AIMAPPER_ERROR_NONE, mapper()->v5.unlock(*handle, &releaseFence)); |
| if (releaseFence != -1) { |
| close(releaseFence); |
| } |
| } |
| |
| TEST_P(GraphicsMapperStableCTests, Lock_YCBCR_420_888) { |
| BufferDescriptorInfo info{ |
| .name = {"VTS_TEMP"}, |
| .width = 64, |
| .height = 64, |
| .layerCount = 1, |
| .format = PixelFormat::YCBCR_420_888, |
| .usage = BufferUsage::CPU_WRITE_OFTEN | BufferUsage::CPU_READ_OFTEN, |
| .reservedSize = 0, |
| }; |
| auto buffer = allocate(info); |
| ASSERT_NE(nullptr, buffer.get()); |
| |
| // lock buffer for writing |
| const ARect region{0, 0, info.width, info.height}; |
| auto handle = buffer->import(); |
| uint8_t* data = nullptr; |
| ASSERT_EQ(AIMAPPER_ERROR_NONE, mapper()->v5.lock(*handle, static_cast<int64_t>(info.usage), |
| region, -1, (void**)&data)); |
| |
| android_ycbcr yCbCr; |
| int64_t hSubsampling = 0; |
| int64_t vSubsampling = 0; |
| ASSERT_NO_FATAL_FAILURE(getAndroidYCbCr(*handle, data, &yCbCr, &hSubsampling, &vSubsampling)); |
| |
| constexpr uint32_t kCbCrSubSampleFactor = 2; |
| ASSERT_EQ(kCbCrSubSampleFactor, hSubsampling); |
| ASSERT_EQ(kCbCrSubSampleFactor, vSubsampling); |
| |
| fillYCbCrData(yCbCr, info.width, info.height, hSubsampling, vSubsampling); |
| |
| int releaseFence = -1; |
| ASSERT_EQ(AIMAPPER_ERROR_NONE, mapper()->v5.unlock(*handle, &releaseFence)); |
| |
| // lock again for reading |
| ASSERT_EQ(AIMAPPER_ERROR_NONE, mapper()->v5.lock(*handle, static_cast<int64_t>(info.usage), |
| region, releaseFence, (void**)&data)); |
| releaseFence = -1; |
| |
| ASSERT_NO_FATAL_FAILURE(getAndroidYCbCr(*handle, data, &yCbCr, &hSubsampling, &vSubsampling)); |
| |
| verifyYCbCrData(yCbCr, info.width, info.height, hSubsampling, vSubsampling); |
| |
| ASSERT_EQ(AIMAPPER_ERROR_NONE, mapper()->v5.unlock(*handle, &releaseFence)); |
| if (releaseFence != -1) { |
| close(releaseFence); |
| } |
| } |
| |
| TEST_P(GraphicsMapperStableCTests, Lock_RAW10) { |
| BufferDescriptorInfo info{ |
| .name = {"VTS_TEMP"}, |
| .width = 64, |
| .height = 64, |
| .layerCount = 1, |
| .format = PixelFormat::RAW10, |
| .usage = BufferUsage::CPU_WRITE_OFTEN | BufferUsage::CPU_READ_OFTEN, |
| .reservedSize = 0, |
| }; |
| auto buffer = allocate(info); |
| if (!buffer) { |
| ASSERT_FALSE(isSupported(info)); |
| GTEST_SUCCEED() << "RAW10 format is unsupported"; |
| return; |
| } |
| |
| // lock buffer for writing |
| const ARect region{0, 0, info.width, info.height}; |
| auto handle = buffer->import(); |
| uint8_t* data = nullptr; |
| ASSERT_EQ(AIMAPPER_ERROR_NONE, mapper()->v5.lock(*handle, static_cast<int64_t>(info.usage), |
| region, -1, (void**)&data)); |
| |
| auto decodeResult = getStandardMetadata<StandardMetadataType::PLANE_LAYOUTS>(*handle); |
| ASSERT_TRUE(decodeResult.has_value()); |
| const auto& planeLayouts = *decodeResult; |
| |
| ASSERT_EQ(1, planeLayouts.size()); |
| auto planeLayout = planeLayouts[0]; |
| |
| EXPECT_EQ(0, planeLayout.sampleIncrementInBits); |
| EXPECT_EQ(1, planeLayout.horizontalSubsampling); |
| EXPECT_EQ(1, planeLayout.verticalSubsampling); |
| |
| ASSERT_EQ(1, planeLayout.components.size()); |
| auto planeLayoutComponent = planeLayout.components[0]; |
| |
| EXPECT_EQ(PlaneLayoutComponentType::RAW, |
| static_cast<PlaneLayoutComponentType>(planeLayoutComponent.type.value)); |
| EXPECT_EQ(0, planeLayoutComponent.offsetInBits % 8); |
| EXPECT_EQ(-1, planeLayoutComponent.sizeInBits); |
| |
| int releaseFence = -1; |
| ASSERT_EQ(AIMAPPER_ERROR_NONE, mapper()->v5.unlock(*handle, &releaseFence)); |
| if (releaseFence != -1) { |
| close(releaseFence); |
| } |
| } |
| |
| TEST_P(GraphicsMapperStableCTests, Lock_RAW12) { |
| BufferDescriptorInfo info{ |
| .name = {"VTS_TEMP"}, |
| .width = 64, |
| .height = 64, |
| .layerCount = 1, |
| .format = PixelFormat::RAW12, |
| .usage = BufferUsage::CPU_WRITE_OFTEN | BufferUsage::CPU_READ_OFTEN, |
| .reservedSize = 0, |
| }; |
| auto buffer = allocate(info); |
| if (!buffer) { |
| ASSERT_FALSE(isSupported(info)); |
| GTEST_SUCCEED() << "RAW12 format is unsupported"; |
| return; |
| } |
| |
| // lock buffer for writing |
| const ARect region{0, 0, info.width, info.height}; |
| auto handle = buffer->import(); |
| uint8_t* data = nullptr; |
| ASSERT_EQ(AIMAPPER_ERROR_NONE, mapper()->v5.lock(*handle, static_cast<int64_t>(info.usage), |
| region, -1, (void**)&data)); |
| |
| auto decodeResult = getStandardMetadata<StandardMetadataType::PLANE_LAYOUTS>(*handle); |
| ASSERT_TRUE(decodeResult.has_value()); |
| const auto& planeLayouts = *decodeResult; |
| |
| ASSERT_EQ(1, planeLayouts.size()); |
| auto planeLayout = planeLayouts[0]; |
| |
| EXPECT_EQ(0, planeLayout.sampleIncrementInBits); |
| EXPECT_EQ(1, planeLayout.horizontalSubsampling); |
| EXPECT_EQ(1, planeLayout.verticalSubsampling); |
| |
| ASSERT_EQ(1, planeLayout.components.size()); |
| auto planeLayoutComponent = planeLayout.components[0]; |
| |
| EXPECT_EQ(PlaneLayoutComponentType::RAW, |
| static_cast<PlaneLayoutComponentType>(planeLayoutComponent.type.value)); |
| EXPECT_EQ(0, planeLayoutComponent.offsetInBits % 8); |
| EXPECT_EQ(-1, planeLayoutComponent.sizeInBits); |
| |
| int releaseFence = -1; |
| ASSERT_EQ(AIMAPPER_ERROR_NONE, mapper()->v5.unlock(*handle, &releaseFence)); |
| if (releaseFence != -1) { |
| close(releaseFence); |
| } |
| } |
| |
| TEST_P(GraphicsMapperStableCTests, Lock_YCBCR_P010) { |
| BufferDescriptorInfo info{ |
| .name = {"VTS_TEMP"}, |
| .width = 64, |
| .height = 64, |
| .layerCount = 1, |
| .format = PixelFormat::YCBCR_P010, |
| .usage = BufferUsage::CPU_WRITE_OFTEN | BufferUsage::CPU_READ_OFTEN, |
| .reservedSize = 0, |
| }; |
| auto buffer = allocate(info); |
| if (!buffer) { |
| ASSERT_FALSE(isSupported(info)); |
| GTEST_SUCCEED() << "YCBCR_P010 format is unsupported"; |
| return; |
| } |
| |
| // lock buffer for writing |
| const ARect region{0, 0, info.width, info.height}; |
| auto handle = buffer->import(); |
| uint8_t* data = nullptr; |
| ASSERT_EQ(AIMAPPER_ERROR_NONE, mapper()->v5.lock(*handle, static_cast<int64_t>(info.usage), |
| region, -1, (void**)&data)); |
| |
| YCbCr yCbCr; |
| ASSERT_NO_FATAL_FAILURE(yCbCr = getAndroidYCbCr_P010(*handle, data)); |
| |
| constexpr uint32_t kCbCrSubSampleFactor = 2; |
| ASSERT_EQ(kCbCrSubSampleFactor, yCbCr.horizontalSubSampling); |
| ASSERT_EQ(kCbCrSubSampleFactor, yCbCr.verticalSubSampling); |
| |
| ASSERT_EQ(0, info.height % 2); |
| |
| // fill the data |
| fillYCbCrData(yCbCr.yCbCr, info.width, info.height, yCbCr.horizontalSubSampling, |
| yCbCr.verticalSubSampling); |
| // verify the YCbCr data |
| verifyYCbCrData(yCbCr.yCbCr, info.width, info.height, yCbCr.horizontalSubSampling, |
| yCbCr.verticalSubSampling); |
| |
| int releaseFence = -1; |
| ASSERT_EQ(AIMAPPER_ERROR_NONE, mapper()->v5.unlock(*handle, &releaseFence)); |
| if (releaseFence != -1) { |
| close(releaseFence); |
| } |
| } |
| |
| TEST_P(GraphicsMapperStableCTests, LockBadAccessRegion) { |
| auto buffer = allocateGeneric(); |
| ASSERT_NE(nullptr, buffer); |
| const auto& info = buffer->info(); |
| |
| // lock buffer for writing |
| const ARect region{0, 0, info.width * 2, info.height * 2}; |
| auto handle = buffer->import(); |
| uint8_t* data = nullptr; |
| EXPECT_EQ(AIMAPPER_ERROR_BAD_VALUE, mapper()->v5.lock(*handle, static_cast<int64_t>(info.usage), |
| region, -1, (void**)&data)); |
| } |
| |
| TEST_P(GraphicsMapperStableCTests, UnlockNegative) { |
| native_handle_t* invalidHandle = nullptr; |
| int releaseFence = -1; |
| EXPECT_EQ(AIMAPPER_ERROR_BAD_BUFFER, mapper()->v5.unlock(invalidHandle, &releaseFence)) |
| << "unlock with nullptr did not fail with BAD_BUFFER"; |
| |
| invalidHandle = native_handle_create(0, 0); |
| EXPECT_EQ(AIMAPPER_ERROR_BAD_BUFFER, mapper()->v5.unlock(invalidHandle, &releaseFence)) |
| << "unlock with invalid handle did not fail with BAD_BUFFER"; |
| native_handle_delete(invalidHandle); |
| |
| auto buffer = allocateGeneric(); |
| EXPECT_EQ(AIMAPPER_ERROR_BAD_BUFFER, mapper()->v5.unlock(buffer->rawHandle(), &releaseFence)) |
| << "unlock with un-imported handle did not fail with BAD_BUFFER"; |
| } |
| |
| TEST_P(GraphicsMapperStableCTests, UnlockNotImported) { |
| int releaseFence = -1; |
| auto buffer = allocateGeneric(); |
| ASSERT_TRUE(buffer); |
| EXPECT_EQ(AIMAPPER_ERROR_BAD_BUFFER, mapper()->v5.unlock(buffer->rawHandle(), &releaseFence)) |
| << "unlock with un-imported handle did not fail with BAD_BUFFER"; |
| } |
| |
| TEST_P(GraphicsMapperStableCTests, UnlockNotLocked) { |
| int releaseFence = -1; |
| auto buffer = allocateGeneric(); |
| ASSERT_TRUE(buffer); |
| auto bufferHandle = buffer->import(); |
| ASSERT_TRUE(bufferHandle); |
| EXPECT_EQ(AIMAPPER_ERROR_BAD_BUFFER, mapper()->v5.unlock(*bufferHandle, &releaseFence)) |
| << "unlock with unlocked handle did not fail with BAD_BUFFER"; |
| } |
| |
| TEST_P(GraphicsMapperStableCTests, LockUnlockNested) { |
| auto buffer = allocateGeneric(); |
| ASSERT_TRUE(buffer); |
| auto bufferHandle = buffer->import(); |
| ASSERT_TRUE(bufferHandle); |
| const ARect region{0, 0, buffer->info().width, buffer->info().height}; |
| auto usage = static_cast<int64_t>(buffer->info().usage); |
| auto handle = buffer->import(); |
| uint8_t* data = nullptr; |
| EXPECT_EQ(AIMAPPER_ERROR_NONE, mapper()->v5.lock(*handle, usage, region, -1, (void**)&data)); |
| EXPECT_EQ(AIMAPPER_ERROR_NONE, mapper()->v5.lock(*handle, usage, region, -1, (void**)&data)) |
| << "Second lock failed"; |
| int releaseFence = -1; |
| EXPECT_EQ(AIMAPPER_ERROR_NONE, mapper()->v5.unlock(*handle, &releaseFence)); |
| if (releaseFence != -1) { |
| close(releaseFence); |
| releaseFence = -1; |
| } |
| EXPECT_EQ(AIMAPPER_ERROR_NONE, mapper()->v5.unlock(*handle, &releaseFence)) |
| << "Second unlock failed"; |
| if (releaseFence != -1) { |
| close(releaseFence); |
| releaseFence = -1; |
| } |
| EXPECT_EQ(AIMAPPER_ERROR_BAD_BUFFER, mapper()->v5.unlock(*handle, &releaseFence)) |
| << "Third, unmatched, unlock should have failed with BAD_BUFFER"; |
| } |
| |
| TEST_P(GraphicsMapperStableCTests, FlushRereadBasic) { |
| auto buffer = allocateGeneric(); |
| ASSERT_TRUE(buffer); |
| auto bufferHandle = buffer->import(); |
| ASSERT_TRUE(bufferHandle); |
| const auto& info = buffer->info(); |
| const auto stride = buffer->stride(); |
| const ARect region{0, 0, buffer->info().width, buffer->info().height}; |
| |
| auto writeHandle = buffer->import(); |
| auto readHandle = buffer->import(); |
| ASSERT_TRUE(writeHandle && readHandle); |
| |
| // lock buffer for writing |
| |
| uint8_t* writeData; |
| EXPECT_EQ(AIMAPPER_ERROR_NONE, |
| mapper()->v5.lock(*writeHandle, static_cast<uint64_t>(BufferUsage::CPU_WRITE_OFTEN), |
| region, -1, (void**)&writeData)); |
| |
| uint8_t* readData; |
| EXPECT_EQ(AIMAPPER_ERROR_NONE, |
| mapper()->v5.lock(*readHandle, static_cast<uint64_t>(BufferUsage::CPU_READ_OFTEN), |
| region, -1, (void**)&readData)); |
| |
| fillRGBA8888(writeData, info.height, stride * 4, info.width * 4); |
| |
| EXPECT_EQ(AIMAPPER_ERROR_NONE, mapper()->v5.flushLockedBuffer(*writeHandle)); |
| EXPECT_EQ(AIMAPPER_ERROR_NONE, mapper()->v5.rereadLockedBuffer(*readHandle)); |
| |
| ASSERT_NO_FATAL_FAILURE( |
| verifyRGBA8888(*readHandle, readData, info.height, stride * 4, info.width * 4)); |
| |
| int releaseFence = -1; |
| |
| EXPECT_EQ(AIMAPPER_ERROR_NONE, mapper()->v5.unlock(*readHandle, &releaseFence)); |
| if (releaseFence != -1) { |
| close(releaseFence); |
| releaseFence = -1; |
| } |
| |
| EXPECT_EQ(AIMAPPER_ERROR_NONE, mapper()->v5.unlock(*writeHandle, &releaseFence)); |
| if (releaseFence != -1) { |
| close(releaseFence); |
| releaseFence = -1; |
| } |
| } |
| |
| TEST_P(GraphicsMapperStableCTests, FlushLockedBufferBadBuffer) { |
| // Amazingly this is enough to make the compiler happy even though flushLockedBuffer |
| // is _Nonnull :shrug: |
| buffer_handle_t badBuffer = nullptr; |
| EXPECT_EQ(AIMAPPER_ERROR_BAD_BUFFER, mapper()->v5.flushLockedBuffer(badBuffer)); |
| } |
| |
| TEST_P(GraphicsMapperStableCTests, RereadLockedBufferBadBuffer) { |
| buffer_handle_t badBuffer = nullptr; |
| EXPECT_EQ(AIMAPPER_ERROR_BAD_BUFFER, mapper()->v5.rereadLockedBuffer(badBuffer)); |
| } |
| |
| TEST_P(GraphicsMapperStableCTests, GetBufferId) { |
| auto buffer = allocateGeneric(); |
| auto bufferHandle = buffer->import(); |
| auto bufferId = getStandardMetadata<StandardMetadataType::BUFFER_ID>(*bufferHandle); |
| ASSERT_TRUE(bufferId.has_value()); |
| |
| auto buffer2 = allocateGeneric(); |
| auto bufferHandle2 = buffer2->import(); |
| auto bufferId2 = getStandardMetadata<StandardMetadataType::BUFFER_ID>(*bufferHandle2); |
| ASSERT_TRUE(bufferId2.has_value()); |
| |
| EXPECT_NE(*bufferId, *bufferId2); |
| } |
| |
| TEST_P(GraphicsMapperStableCTests, GetName) { |
| auto buffer = allocate({ |
| .name = {"Hello, World!"}, |
| .width = 64, |
| .height = 64, |
| .layerCount = 1, |
| .format = PixelFormat::RGBA_8888, |
| .usage = BufferUsage::CPU_WRITE_OFTEN | BufferUsage::CPU_READ_OFTEN, |
| .reservedSize = 0, |
| }); |
| auto bufferHandle = buffer->import(); |
| auto name = getStandardMetadata<StandardMetadataType::NAME>(*bufferHandle); |
| ASSERT_TRUE(name.has_value()); |
| EXPECT_EQ(*name, "Hello, World!"); |
| } |
| |
| TEST_P(GraphicsMapperStableCTests, GetWidthHeight) { |
| auto buffer = allocate({ |
| .name = {"Hello, World!"}, |
| .width = 64, |
| .height = 128, |
| .layerCount = 1, |
| .format = PixelFormat::RGBA_8888, |
| .usage = BufferUsage::CPU_WRITE_OFTEN | BufferUsage::CPU_READ_OFTEN, |
| .reservedSize = 0, |
| }); |
| auto bufferHandle = buffer->import(); |
| auto value = getStandardMetadata<StandardMetadataType::WIDTH>(*bufferHandle); |
| ASSERT_TRUE(value.has_value()); |
| EXPECT_EQ(*value, 64); |
| value = getStandardMetadata<StandardMetadataType::HEIGHT>(*bufferHandle); |
| ASSERT_TRUE(value.has_value()); |
| EXPECT_EQ(*value, 128); |
| } |
| |
| TEST_P(GraphicsMapperStableCTests, GetLayerCount) { |
| auto buffer = allocateGeneric(); |
| auto bufferHandle = buffer->import(); |
| auto value = getStandardMetadata<StandardMetadataType::LAYER_COUNT>(*bufferHandle); |
| ASSERT_TRUE(value.has_value()); |
| EXPECT_EQ(*value, buffer->info().layerCount); |
| } |
| |
| TEST_P(GraphicsMapperStableCTests, GetPixelFormatRequested) { |
| auto buffer = allocateGeneric(); |
| auto bufferHandle = buffer->import(); |
| auto value = getStandardMetadata<StandardMetadataType::PIXEL_FORMAT_REQUESTED>(*bufferHandle); |
| ASSERT_TRUE(value.has_value()); |
| EXPECT_EQ(*value, buffer->info().format); |
| } |
| |
| TEST_P(GraphicsMapperStableCTests, GetPixelFormatFourCC) { |
| auto buffer = allocate({ |
| .name = {"Hello, World!"}, |
| .width = 64, |
| .height = 128, |
| .layerCount = 1, |
| .format = PixelFormat::RGBA_8888, |
| .usage = BufferUsage::CPU_WRITE_OFTEN | BufferUsage::CPU_READ_OFTEN, |
| .reservedSize = 0, |
| }); |
| { |
| auto bufferHandle = buffer->import(); |
| auto value = getStandardMetadata<StandardMetadataType::PIXEL_FORMAT_FOURCC>(*bufferHandle); |
| ASSERT_TRUE(value.has_value()); |
| EXPECT_EQ(*value, DRM_FORMAT_ABGR8888); |
| } |
| |
| buffer = allocate({ |
| .name = {"yv12"}, |
| .width = 64, |
| .height = 128, |
| .layerCount = 1, |
| .format = PixelFormat::YV12, |
| .usage = BufferUsage::CPU_WRITE_OFTEN | BufferUsage::CPU_READ_OFTEN, |
| .reservedSize = 0, |
| }); |
| { |
| auto bufferHandle = buffer->import(); |
| auto value = getStandardMetadata<StandardMetadataType::PIXEL_FORMAT_FOURCC>(*bufferHandle); |
| ASSERT_TRUE(value.has_value()); |
| EXPECT_EQ(*value, DRM_FORMAT_YVU420); |
| } |
| } |
| |
| TEST_P(GraphicsMapperStableCTests, GetPixelFormatModifier) { |
| auto buffer = allocateGeneric(); |
| auto bufferHandle = buffer->import(); |
| auto value = getStandardMetadata<StandardMetadataType::PIXEL_FORMAT_MODIFIER>(*bufferHandle); |
| ASSERT_TRUE(value.has_value()); |
| // Only the upper 8-bits are defined and is just the vendor ID, the lower 56 bits are |
| // then vendor specific. So there's not anything useful to assert here beyond just that |
| // we successfully queried a value |
| } |
| |
| TEST_P(GraphicsMapperStableCTests, GetUsage) { |
| auto buffer = allocateGeneric(); |
| auto bufferHandle = buffer->import(); |
| auto value = getStandardMetadata<StandardMetadataType::USAGE>(*bufferHandle); |
| ASSERT_TRUE(value.has_value()); |
| EXPECT_EQ(buffer->info().usage, *value); |
| } |
| |
| TEST_P(GraphicsMapperStableCTests, GetAllocationSize) { |
| auto buffer = allocateGeneric(); |
| auto bufferHandle = buffer->import(); |
| auto value = getStandardMetadata<StandardMetadataType::ALLOCATION_SIZE>(*bufferHandle); |
| ASSERT_TRUE(value.has_value()); |
| const auto estimatedSize = buffer->stride() * buffer->info().height * 4; |
| // This buffer has CPU usage, so we expect at least stride * height * 4 since it should be |
| // generally linear uncompressed. |
| EXPECT_GE(*value, estimatedSize) |
| << "Expected allocation size to be at least stride * height * 4bpp"; |
| // Might need refining, but hopefully this a generous-enough upper-bound? |
| EXPECT_LT(*value, estimatedSize * 2) |
| << "Expected allocation size to less than double stride * height * 4bpp"; |
| } |
| |
| TEST_P(GraphicsMapperStableCTests, GetProtectedContent) { |
| const BufferDescriptorInfo info{ |
| .name = {"prot8888"}, |
| .width = 64, |
| .height = 64, |
| .layerCount = 1, |
| .format = PixelFormat::RGBA_8888, |
| .usage = BufferUsage::PROTECTED | BufferUsage::COMPOSER_OVERLAY, |
| .reservedSize = 0, |
| }; |
| auto buffer = allocate(info); |
| if (!buffer) { |
| ASSERT_FALSE(isSupported(info)) |
| << "Allocation of trivial sized buffer failed, so isSupported() must be false"; |
| GTEST_SUCCEED() << "PROTECTED RGBA_8888 is unsupported"; |
| return; |
| } |
| auto bufferHandle = buffer->import(); |
| auto value = getStandardMetadata<StandardMetadataType::PROTECTED_CONTENT>(*bufferHandle); |
| ASSERT_TRUE(value.has_value()); |
| EXPECT_EQ(*value, 1); |
| } |
| |
| TEST_P(GraphicsMapperStableCTests, GetCompression) { |
| auto buffer = allocateGeneric(); |
| ASSERT_TRUE(buffer); |
| auto bufferHandle = buffer->import(); |
| ASSERT_TRUE(bufferHandle); |
| auto value = getStandardMetadata<StandardMetadataType::COMPRESSION>(*bufferHandle); |
| ASSERT_TRUE(value.has_value()); |
| EXPECT_EQ(gralloc4::Compression_None.name, value->name); |
| EXPECT_EQ(gralloc4::Compression_None.value, value->value); |
| } |
| |
| TEST_P(GraphicsMapperStableCTests, GetInterlaced) { |
| auto buffer = allocateGeneric(); |
| ASSERT_TRUE(buffer); |
| auto bufferHandle = buffer->import(); |
| ASSERT_TRUE(bufferHandle); |
| auto value = getStandardMetadata<StandardMetadataType::INTERLACED>(*bufferHandle); |
| ASSERT_TRUE(value.has_value()); |
| EXPECT_EQ(gralloc4::Interlaced_None.name, value->name); |
| EXPECT_EQ(gralloc4::Interlaced_None.value, value->value); |
| } |
| |
| TEST_P(GraphicsMapperStableCTests, GetChromaSiting) { |
| auto buffer = allocateGeneric(); |
| ASSERT_TRUE(buffer); |
| auto bufferHandle = buffer->import(); |
| ASSERT_TRUE(bufferHandle); |
| auto value = getStandardMetadata<StandardMetadataType::CHROMA_SITING>(*bufferHandle); |
| ASSERT_TRUE(value.has_value()); |
| EXPECT_EQ(gralloc4::ChromaSiting_None.name, value->name); |
| EXPECT_EQ(gralloc4::ChromaSiting_None.value, value->value); |
| } |
| |
| TEST_P(GraphicsMapperStableCTests, GetPlaneLayouts) { |
| auto buffer = allocateGeneric(); |
| ASSERT_TRUE(buffer); |
| auto bufferHandle = buffer->import(); |
| ASSERT_TRUE(bufferHandle); |
| auto value = getStandardMetadata<StandardMetadataType::PLANE_LAYOUTS>(*bufferHandle); |
| ASSERT_TRUE(value.has_value()); |
| ASSERT_NO_FATAL_FAILURE(verifyRGBA8888PlaneLayouts(*value)); |
| } |
| |
| TEST_P(GraphicsMapperStableCTests, GetCrop) { |
| auto buffer = allocateGeneric(); |
| ASSERT_TRUE(buffer); |
| auto bufferHandle = buffer->import(); |
| ASSERT_TRUE(bufferHandle); |
| auto value = getStandardMetadata<StandardMetadataType::CROP>(*bufferHandle); |
| ASSERT_TRUE(value.has_value()); |
| EXPECT_EQ(1, value->size()); |
| const Rect expected{0, 0, buffer->info().width, buffer->info().height}; |
| EXPECT_EQ(expected, value->at(0)); |
| } |
| |
| TEST_P(GraphicsMapperStableCTests, GetSetDataspace) { |
| auto buffer = allocateGeneric(); |
| ASSERT_TRUE(buffer); |
| auto bufferHandle = buffer->import(); |
| ASSERT_TRUE(bufferHandle); |
| auto value = getStandardMetadata<StandardMetadataType::DATASPACE>(*bufferHandle); |
| ASSERT_TRUE(value.has_value()); |
| EXPECT_EQ(Dataspace::UNKNOWN, *value); |
| EXPECT_EQ(AIMAPPER_ERROR_NONE, setStandardMetadata<StandardMetadataType::DATASPACE>( |
| *bufferHandle, Dataspace::DISPLAY_P3)); |
| value = getStandardMetadata<StandardMetadataType::DATASPACE>(*bufferHandle); |
| ASSERT_TRUE(value.has_value()); |
| EXPECT_EQ(Dataspace::DISPLAY_P3, *value); |
| } |
| |
| TEST_P(GraphicsMapperStableCTests, GetSetBlendMode) { |
| auto buffer = allocateGeneric(); |
| ASSERT_TRUE(buffer); |
| auto bufferHandle = buffer->import(); |
| ASSERT_TRUE(bufferHandle); |
| auto value = getStandardMetadata<StandardMetadataType::BLEND_MODE>(*bufferHandle); |
| ASSERT_TRUE(value.has_value()); |
| EXPECT_EQ(BlendMode::INVALID, *value); |
| EXPECT_EQ(AIMAPPER_ERROR_NONE, setStandardMetadata<StandardMetadataType::BLEND_MODE>( |
| *bufferHandle, BlendMode::COVERAGE)); |
| value = getStandardMetadata<StandardMetadataType::BLEND_MODE>(*bufferHandle); |
| ASSERT_TRUE(value.has_value()); |
| EXPECT_EQ(BlendMode::COVERAGE, *value); |
| } |
| |
| TEST_P(GraphicsMapperStableCTests, GetSetSmpte2086) { |
| auto buffer = allocateGeneric(); |
| ASSERT_TRUE(buffer); |
| auto bufferHandle = buffer->import(); |
| ASSERT_TRUE(bufferHandle); |
| auto value = getStandardMetadata<StandardMetadataType::SMPTE2086>(*bufferHandle); |
| ASSERT_TRUE(value.has_value()); |
| EXPECT_FALSE(value->has_value()); |
| |
| // TODO: Maybe use something resembling real values, but validation isn't supposed to happen |
| // here anyway so :shrug: |
| const Smpte2086 awesomeHdr{ |
| XyColor{1.f, 1.f}, XyColor{2.f, 2.f}, XyColor{3.f, 3.f}, |
| XyColor{400.f, 1000.f}, 100000.0f, 0.0001f, |
| }; |
| EXPECT_EQ(AIMAPPER_ERROR_NONE, |
| setStandardMetadata<StandardMetadataType::SMPTE2086>(*bufferHandle, awesomeHdr)); |
| value = getStandardMetadata<StandardMetadataType::SMPTE2086>(*bufferHandle); |
| ASSERT_TRUE(value.has_value()); |
| ASSERT_TRUE(value->has_value()); |
| EXPECT_EQ(awesomeHdr, *value); |
| |
| EXPECT_EQ(AIMAPPER_ERROR_NONE, |
| setStandardMetadata<StandardMetadataType::SMPTE2086>(*bufferHandle, std::nullopt)); |
| value = getStandardMetadata<StandardMetadataType::SMPTE2086>(*bufferHandle); |
| ASSERT_TRUE(value.has_value()); |
| EXPECT_FALSE(value->has_value()); |
| } |
| |
| TEST_P(GraphicsMapperStableCTests, GetCta861_3) { |
| auto buffer = allocateGeneric(); |
| ASSERT_TRUE(buffer); |
| auto bufferHandle = buffer->import(); |
| ASSERT_TRUE(bufferHandle); |
| auto value = getStandardMetadata<StandardMetadataType::CTA861_3>(*bufferHandle); |
| ASSERT_TRUE(value.has_value()); |
| EXPECT_FALSE(value->has_value()); |
| |
| const Cta861_3 genericHlgish{1000.f, 140.f}; |
| EXPECT_EQ(AIMAPPER_ERROR_NONE, |
| setStandardMetadata<StandardMetadataType::CTA861_3>(*bufferHandle, genericHlgish)); |
| value = getStandardMetadata<StandardMetadataType::CTA861_3>(*bufferHandle); |
| ASSERT_TRUE(value.has_value()); |
| ASSERT_TRUE(value->has_value()); |
| EXPECT_EQ(genericHlgish, *value); |
| |
| EXPECT_EQ(AIMAPPER_ERROR_NONE, |
| setStandardMetadata<StandardMetadataType::CTA861_3>(*bufferHandle, std::nullopt)); |
| value = getStandardMetadata<StandardMetadataType::CTA861_3>(*bufferHandle); |
| ASSERT_TRUE(value.has_value()); |
| EXPECT_FALSE(value->has_value()); |
| } |
| |
| TEST_P(GraphicsMapperStableCTests, GetSmpte2094_10) { |
| auto buffer = allocateGeneric(); |
| ASSERT_TRUE(buffer); |
| auto bufferHandle = buffer->import(); |
| ASSERT_TRUE(bufferHandle); |
| auto value = getStandardMetadata<StandardMetadataType::SMPTE2094_10>(*bufferHandle); |
| if (value.has_value()) { |
| EXPECT_FALSE(value->has_value()); |
| } |
| } |
| |
| TEST_P(GraphicsMapperStableCTests, GetSmpte2094_40) { |
| auto buffer = allocateGeneric(); |
| ASSERT_TRUE(buffer); |
| auto bufferHandle = buffer->import(); |
| ASSERT_TRUE(bufferHandle); |
| auto value = getStandardMetadata<StandardMetadataType::SMPTE2094_40>(*bufferHandle); |
| if (value.has_value()) { |
| EXPECT_FALSE(value->has_value()); |
| } |
| } |
| |
| TEST_P(GraphicsMapperStableCTests, SupportsRequiredGettersSetters) { |
| auto buffer = allocateGeneric(); |
| ASSERT_TRUE(buffer); |
| auto bufferHandle = buffer->import(); |
| ASSERT_TRUE(bufferHandle); |
| const AIMapper_MetadataTypeDescription* descriptions = nullptr; |
| size_t descriptionCount = 0; |
| ASSERT_EQ(AIMAPPER_ERROR_NONE, |
| mapper()->v5.listSupportedMetadataTypes(&descriptions, &descriptionCount)); |
| std::vector<StandardMetadataType> requiredGetters = { |
| StandardMetadataType::BUFFER_ID, |
| StandardMetadataType::NAME, |
| StandardMetadataType::WIDTH, |
| StandardMetadataType::HEIGHT, |
| StandardMetadataType::LAYER_COUNT, |
| StandardMetadataType::PIXEL_FORMAT_REQUESTED, |
| StandardMetadataType::PIXEL_FORMAT_FOURCC, |
| StandardMetadataType::PIXEL_FORMAT_MODIFIER, |
| StandardMetadataType::USAGE, |
| StandardMetadataType::ALLOCATION_SIZE, |
| StandardMetadataType::PROTECTED_CONTENT, |
| StandardMetadataType::COMPRESSION, |
| StandardMetadataType::INTERLACED, |
| StandardMetadataType::CHROMA_SITING, |
| StandardMetadataType::PLANE_LAYOUTS, |
| StandardMetadataType::CROP, |
| StandardMetadataType::DATASPACE, |
| StandardMetadataType::BLEND_MODE, |
| StandardMetadataType::SMPTE2086, |
| StandardMetadataType::CTA861_3, |
| }; |
| |
| std::vector<StandardMetadataType> requiredSetters = { |
| StandardMetadataType::DATASPACE, |
| StandardMetadataType::BLEND_MODE, |
| StandardMetadataType::SMPTE2086, |
| StandardMetadataType::CTA861_3, |
| }; |
| |
| for (int i = 0; i < descriptionCount; i++) { |
| const auto& it = descriptions[i]; |
| if (isStandardMetadata(it.metadataType)) { |
| EXPECT_GT(it.metadataType.value, static_cast<int64_t>(StandardMetadataType::INVALID)); |
| EXPECT_LT(it.metadataType.value, |
| ndk::internal::enum_values<StandardMetadataType>.size()); |
| |
| if (it.isGettable) { |
| std::erase(requiredGetters, |
| static_cast<StandardMetadataType>(it.metadataType.value)); |
| } |
| if (it.isSettable) { |
| std::erase(requiredSetters, |
| static_cast<StandardMetadataType>(it.metadataType.value)); |
| } |
| } else { |
| EXPECT_NE(nullptr, it.description) << "Non-standard metadata must have a description"; |
| int len = strlen(it.description); |
| EXPECT_GE(len, 0) << "Non-standard metadata must have a description"; |
| } |
| } |
| |
| EXPECT_EQ(0, requiredGetters.size()) << "Missing required getters" << toString(requiredGetters); |
| EXPECT_EQ(0, requiredSetters.size()) << "Missing required setters" << toString(requiredSetters); |
| } |
| |
| /* |
| * Test that verifies that if the optional StandardMetadataTypes have getters, they have |
| * the required setters as well |
| */ |
| TEST_P(GraphicsMapperStableCTests, CheckRequiredSettersIfHasGetters) { |
| auto buffer = allocateGeneric(); |
| ASSERT_TRUE(buffer); |
| auto bufferHandle = buffer->import(); |
| ASSERT_TRUE(bufferHandle); |
| const AIMapper_MetadataTypeDescription* descriptions = nullptr; |
| size_t descriptionCount = 0; |
| ASSERT_EQ(AIMAPPER_ERROR_NONE, |
| mapper()->v5.listSupportedMetadataTypes(&descriptions, &descriptionCount)); |
| |
| for (int i = 0; i < descriptionCount; i++) { |
| const auto& it = descriptions[i]; |
| if (isStandardMetadata(it.metadataType)) { |
| const auto type = static_cast<StandardMetadataType>(it.metadataType.value); |
| switch (type) { |
| case StandardMetadataType::SMPTE2094_10: |
| case StandardMetadataType::SMPTE2094_40: |
| if (it.isGettable) { |
| EXPECT_TRUE(it.isSettable) |
| << "Type " << toString(type) << " must be settable if gettable"; |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| } |
| } |
| |
| TEST_P(GraphicsMapperStableCTests, ListSupportedWorks) { |
| auto buffer = allocateGeneric(); |
| ASSERT_TRUE(buffer); |
| auto bufferHandle = buffer->import(); |
| ASSERT_TRUE(bufferHandle); |
| const AIMapper_MetadataTypeDescription* descriptions = nullptr; |
| size_t descriptionCount = 0; |
| ASSERT_EQ(AIMAPPER_ERROR_NONE, |
| mapper()->v5.listSupportedMetadataTypes(&descriptions, &descriptionCount)); |
| |
| std::vector<uint8_t> metadataBuffer; |
| auto get = [&](AIMapper_MetadataType metadataType) -> int32_t { |
| int32_t size = mapper()->v5.getMetadata(*bufferHandle, metadataType, nullptr, 0); |
| if (size >= 0) { |
| metadataBuffer.resize(size); |
| size = mapper()->v5.getMetadata(*bufferHandle, metadataType, metadataBuffer.data(), |
| metadataBuffer.size()); |
| EXPECT_EQ(size, metadataBuffer.size()); |
| } |
| return size; |
| }; |
| |
| for (int i = 0; i < descriptionCount; i++) { |
| const auto& it = descriptions[i]; |
| if (!isStandardMetadata(it.metadataType)) { |
| continue; |
| } |
| if (!it.isGettable) { |
| EXPECT_FALSE(it.isSettable) |
| << "StandardMetadata that isn't gettable must not be settable"; |
| continue; |
| } |
| EXPECT_GE(get(it.metadataType), 0) |
| << "Get failed for claimed supported getter of " |
| << toString(static_cast<StandardMetadataType>(it.metadataType.value)); |
| if (it.isSettable) { |
| EXPECT_EQ(AIMAPPER_ERROR_NONE, |
| mapper()->v5.setMetadata(*bufferHandle, it.metadataType, |
| metadataBuffer.data(), metadataBuffer.size())) |
| << "Failed to set metadata for " |
| << toString(static_cast<StandardMetadataType>(it.metadataType.value)); |
| } |
| } |
| } |
| |
| TEST_P(GraphicsMapperStableCTests, GetMetadataBadValue) { |
| auto get = [this](StandardMetadataType type) -> AIMapper_Error { |
| // This is a _Nonnull parameter, but this is enough obfuscation to fool the linter |
| buffer_handle_t buffer = nullptr; |
| int32_t ret = |
| mapper()->v5.getStandardMetadata(buffer, static_cast<int64_t>(type), nullptr, 0); |
| return (ret < 0) ? (AIMapper_Error)-ret : AIMAPPER_ERROR_NONE; |
| }; |
| |
| for (auto type : ndk::enum_range<StandardMetadataType>()) { |
| if (type == StandardMetadataType::INVALID) { |
| continue; |
| } |
| EXPECT_EQ(AIMAPPER_ERROR_BAD_BUFFER, get(type)) << "Wrong error for " << toString(type); |
| } |
| } |
| |
| TEST_P(GraphicsMapperStableCTests, GetUnsupportedMetadata) { |
| auto buffer = allocateGeneric(); |
| ASSERT_TRUE(buffer); |
| auto bufferHandle = buffer->import(); |
| ASSERT_TRUE(bufferHandle); |
| |
| int result = mapper()->v5.getMetadata(*bufferHandle, {"Fake", 1}, nullptr, 0); |
| EXPECT_EQ(AIMAPPER_ERROR_UNSUPPORTED, -result); |
| |
| result = mapper()->v5.getStandardMetadata( |
| *bufferHandle, static_cast<int64_t>(StandardMetadataType::INVALID), nullptr, 0); |
| EXPECT_EQ(AIMAPPER_ERROR_UNSUPPORTED, -result); |
| |
| constexpr int64_t unknownStandardType = ndk::internal::enum_values<StandardMetadataType>.size(); |
| result = mapper()->v5.getStandardMetadata(*bufferHandle, unknownStandardType, nullptr, 0); |
| EXPECT_EQ(AIMAPPER_ERROR_UNSUPPORTED, -result); |
| } |
| |
| std::vector<std::tuple<std::string, std::shared_ptr<IAllocator>>> getIAllocatorsAtLeastVersion( |
| int32_t minVersion) { |
| auto instanceNames = getAidlHalInstanceNames(IAllocator::descriptor); |
| std::vector<std::tuple<std::string, std::shared_ptr<IAllocator>>> filteredInstances; |
| filteredInstances.reserve(instanceNames.size()); |
| for (const auto& name : instanceNames) { |
| auto allocator = |
| IAllocator::fromBinder(ndk::SpAIBinder(AServiceManager_checkService(name.c_str()))); |
| int32_t version = 0; |
| if (allocator->getInterfaceVersion(&version).isOk()) { |
| if (version >= minVersion) { |
| filteredInstances.emplace_back(name, std::move(allocator)); |
| } |
| } |
| } |
| return filteredInstances; |
| } |
| |
| GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GraphicsMapperStableCTests); |
| INSTANTIATE_TEST_CASE_P(PerInstance, GraphicsMapperStableCTests, |
| testing::ValuesIn(getIAllocatorsAtLeastVersion(2)), |
| [](auto info) -> std::string { |
| std::string name = |
| std::to_string(info.index) + "/" + std::get<0>(info.param); |
| return Sanitize(name); |
| }); |