1786 lines
70 KiB
C++
1786 lines
70 KiB
C++
/*
|
|
* 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;
|
|
int32_t* mIMapperHALVersion = nullptr;
|
|
|
|
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);
|
|
mIMapperHALVersion = (int32_t*)dlsym(so, "ANDROID_HAL_MAPPER_VERSION");
|
|
}
|
|
|
|
public:
|
|
AIMapper_loadIMapperFn getIMapperLoader() const { return mIMapperLoader; }
|
|
int32_t* getHalVersion() const { return mIMapperHALVersion; }
|
|
|
|
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, VersionChecks) {
|
|
ASSERT_NE(nullptr, getHalVersion()) << "Resolving ANDROID_HAL_MAPPER_VERSION symbol failed";
|
|
int32_t halVersion = *getHalVersion();
|
|
EXPECT_EQ(halVersion, AIMAPPER_VERSION_5) << "Unrecognized ANDROID_HAL_MAPPER_VERSION";
|
|
EXPECT_EQ(mapper()->version, AIMAPPER_VERSION_5) << "Unrecognized AIMapper::version";
|
|
EXPECT_EQ(halVersion, mapper()->version)
|
|
<< "AIMapper version & ANDROID_HAL_MAPPER_VERSION don't agree";
|
|
}
|
|
|
|
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, GetStride) {
|
|
auto buffer = allocateGeneric();
|
|
ASSERT_TRUE(buffer);
|
|
auto bufferHandle = buffer->import();
|
|
ASSERT_TRUE(bufferHandle);
|
|
auto value = getStandardMetadata<StandardMetadataType::STRIDE>(*bufferHandle);
|
|
ASSERT_TRUE(value.has_value());
|
|
EXPECT_EQ(buffer->stride(), *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,
|
|
StandardMetadataType::STRIDE,
|
|
};
|
|
|
|
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);
|
|
}); |