blob: 1e0c42771500754ea7f8a4e2a0e89644cbba88b1 [file] [log] [blame]
/*
* 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 = AServiceManager_openDeclaredPassthroughHal("mapper", mapperSuffix.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, GetUsage64) {
BufferDescriptorInfo info{
.name = {"VTS_TEMP"},
.width = 64,
.height = 64,
.layerCount = 1,
.format = PixelFormat::RGBA_8888,
.usage = BufferUsage::FRONT_BUFFER | BufferUsage::GPU_RENDER_TARGET |
BufferUsage::COMPOSER_OVERLAY | BufferUsage::GPU_TEXTURE,
.reservedSize = 0,
};
if (!isSupported(info)) {
GTEST_SKIP();
}
auto buffer = allocate(info);
auto bufferHandle = buffer->import();
auto value = getStandardMetadata<StandardMetadataType::USAGE>(*bufferHandle);
ASSERT_TRUE(value.has_value());
using T = std::underlying_type_t<BufferUsage>;
EXPECT_EQ(static_cast<T>(buffer->info().usage), static_cast<T>(*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);
});