IMapper 5 - the Stable C approach

Test: make VtsHalGraphicsMapperStableC_TargetTest VtsHalGraphicsAllocatorAidl_TargetTest

Change-Id: I910b27f388e3fb7261425dd4b2133885c05edd37
This commit is contained in:
John Reck 2022-11-15 16:29:21 -05:00
parent d2e68f1579
commit 48c546c7e9
15 changed files with 3769 additions and 100 deletions

View file

@ -268,7 +268,7 @@
</hal>
<hal format="aidl" optional="true">
<name>android.hardware.graphics.allocator</name>
<version>1</version>
<version>1-2</version>
<interface>
<name>IAllocator</name>
<instance>default</instance>
@ -282,7 +282,7 @@
<instance>default</instance>
</interface>
</hal>
<hal format="hidl" optional="false">
<hal format="hidl" optional="true">
<name>android.hardware.graphics.mapper</name>
<!-- New, non-Go devices should use 4.0, tested in vts_treble_vintf_vendor_test -->
<version>2.1</version>

View file

@ -19,14 +19,14 @@ package {
cc_defaults {
name: "android.hardware.graphics.allocator-ndk_static",
static_libs: [
"android.hardware.graphics.allocator-V1-ndk",
"android.hardware.graphics.allocator-V2-ndk",
],
}
cc_defaults {
name: "android.hardware.graphics.allocator-ndk_shared",
shared_libs: [
"android.hardware.graphics.allocator-V1-ndk",
"android.hardware.graphics.allocator-V2-ndk",
],
}

View file

@ -14,7 +14,7 @@ aidl_interface {
enabled: true,
support_system_process: true,
},
vndk_use_version: "1",
vndk_use_version: "2",
srcs: ["android/hardware/graphics/allocator/*.aidl"],
imports: [
"android.hardware.common-V2",

View file

@ -0,0 +1,44 @@
/*
* 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.
*/
///////////////////////////////////////////////////////////////////////////////
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
// two cases:
// 1). this is a frozen version file - do not edit this in any case.
// 2). this is a 'current' file. If you make a backwards compatible change to
// the interface (from the latest frozen version), the build system will
// prompt you to update this file with `m <name>-update-api`.
//
// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
// with such a backward incompatible change, it has a high risk of breaking
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.graphics.allocator;
@VintfStability
parcelable BufferDescriptorInfo {
byte[128] name;
int width;
int height;
int layerCount;
android.hardware.graphics.common.PixelFormat format = android.hardware.graphics.common.PixelFormat.UNSPECIFIED;
android.hardware.graphics.common.BufferUsage usage = android.hardware.graphics.common.BufferUsage.CPU_READ_NEVER;
long reservedSize;
}

View file

@ -34,5 +34,11 @@
package android.hardware.graphics.allocator;
@VintfStability
interface IAllocator {
/**
* @deprecated As of android.hardware.graphics.allocator-V2, this is deprecated & replaced with allocate2
*/
android.hardware.graphics.allocator.AllocationResult allocate(in byte[] descriptor, in int count);
android.hardware.graphics.allocator.AllocationResult allocate2(in android.hardware.graphics.allocator.BufferDescriptorInfo descriptor, in int count);
boolean isSupported(in android.hardware.graphics.allocator.BufferDescriptorInfo descriptor);
String getIMapperLibrarySuffix();
}

View file

@ -0,0 +1,65 @@
/*
* 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.
*/
package android.hardware.graphics.allocator;
import android.hardware.graphics.common.BufferUsage;
import android.hardware.graphics.common.PixelFormat;
@VintfStability
parcelable BufferDescriptorInfo {
/**
* The name of the buffer in ASCII. Useful for debugging/tracing.
*/
byte[128] name;
/**
* The width specifies how many columns of pixels must be in the
* allocated buffer, but does not necessarily represent the offset in
* columns between the same column in adjacent rows. The rows may be
* padded.
*/
int width;
/**
* The height specifies how many rows of pixels must be in the
* allocated buffer.
*/
int height;
/**
* The number of image layers that must be in the allocated buffer.
*/
int layerCount;
/**
* Buffer pixel format. See PixelFormat.aidl in graphics/common for
* valid values
*/
PixelFormat format = PixelFormat.UNSPECIFIED;
/**
* Buffer usage mask; valid flags can be found in the definition of
* BufferUsage.aidl in graphics/common
*/
BufferUsage usage = BufferUsage.CPU_READ_NEVER;
/**
* The size in bytes of the reserved region associated with the buffer.
* See getReservedRegion for more information.
*/
long reservedSize;
}

View file

@ -17,6 +17,7 @@
package android.hardware.graphics.allocator;
import android.hardware.graphics.allocator.AllocationResult;
import android.hardware.graphics.allocator.BufferDescriptorInfo;
@VintfStability
interface IAllocator {
@ -31,6 +32,43 @@ interface IAllocator {
* @param count The number of buffers to allocate.
* @return An AllocationResult containing the result of the allocation
* @throws AllocationError on failure
* @deprecated As of android.hardware.graphics.allocator-V2, this is deprecated & replaced with
* allocate2
*/
AllocationResult allocate(in byte[] descriptor, in int count);
/**
* Allocates buffers with the properties specified by the descriptor.
*
* Allocations should be optimized for usage bits provided in the
* descriptor.
*
* @param descriptor Properties of the buffers to allocate. This must be
* obtained from IMapper::createDescriptor().
* @param count The number of buffers to allocate.
* @return An AllocationResult containing the result of the allocation
* @throws AllocationError on failure
*/
AllocationResult allocate2(in BufferDescriptorInfo descriptor, in int count);
/**
* Test whether the given BufferDescriptorInfo is allocatable.
*
* If this function returns true, it means that a buffer with the given
* description can be allocated on this implementation, unless resource
* exhaustion occurs. If this function returns false, it means that the
* allocation of the given description will never succeed.
*
* @param description the description of the buffer
* @return supported whether the description is supported
*/
boolean isSupported(in BufferDescriptorInfo descriptor);
/**
* Retrieve the library suffix to load for the IMapper SP-HAL. This library must implement the
* IMapper stable-C interface (android/hardware/graphics/mapper/IMapper.h).
*
* The library that will attempt to be loaded is "/vendor/lib[64]/hw/mapper.<imapper_suffix>.so"
*/
String getIMapperLibrarySuffix();
}

View file

@ -55,6 +55,7 @@ cc_test {
],
header_libs: [
"libhwui_internal_headers",
"libimapper_stablec",
],
cflags: [
"-Wall",

View file

@ -25,7 +25,10 @@
#include <aidl/android/hardware/graphics/common/PixelFormat.h>
#include <aidlcommonsupport/NativeHandle.h>
#include <android/binder_manager.h>
#include <android/dlext.h>
#include <android/hardware/graphics/mapper/4.0/IMapper.h>
#include <android/hardware/graphics/mapper/IMapper.h>
#include <dlfcn.h>
#include <gtest/gtest.h>
#include <hidl/GtestPrinter.h>
#include <hidl/ServiceManagement.h>
@ -33,6 +36,7 @@
#include <renderthread/EglManager.h>
#include <utils/GLUtils.h>
#include <vndk/hardware_buffer.h>
#include <vndksupport/linker.h>
#include <initializer_list>
#include <optional>
#include <string>
@ -42,60 +46,70 @@ 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::V4_0;
using IMapper4 = android::hardware::graphics::mapper::V4_0::IMapper;
using Error = android::hardware::graphics::mapper::V4_0::Error;
using android::hardware::graphics::mapper::V4_0::BufferDescriptor;
using android::uirenderer::AutoEglImage;
using android::uirenderer::AutoGLFramebuffer;
using android::uirenderer::AutoSkiaGlTexture;
using android::uirenderer::renderthread::EglManager;
static constexpr uint64_t pack(const std::initializer_list<BufferUsage>& usages) {
uint64_t ret = 0;
for (const auto u : usages) {
ret |= static_cast<uint64_t>(u);
}
return ret;
typedef AIMapper_Error (*AIMapper_loadIMapperFn)(AIMapper* _Nullable* _Nonnull outImplementation);
inline 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));
}
static constexpr hardware::graphics::common::V1_2::PixelFormat cast(PixelFormat format) {
return static_cast<hardware::graphics::common::V1_2::PixelFormat>(format);
inline BufferUsage& operator|=(BufferUsage& lhs, BufferUsage rhs) {
lhs = lhs | rhs;
return lhs;
}
static IMapper4::BufferDescriptorInfo convert(const BufferDescriptorInfo& info) {
return IMapper4::BufferDescriptorInfo{
.name{reinterpret_cast<const char*>(info.name.data())},
.width = static_cast<uint32_t>(info.width),
.height = static_cast<uint32_t>(info.height),
.layerCount = static_cast<uint32_t>(info.layerCount),
.format = static_cast<hardware::graphics::common::V1_2::PixelFormat>(info.format),
.usage = static_cast<uint64_t>(info.usage),
.reservedSize = 0,
};
}
class GraphicsTestsBase;
class BufferHandle {
sp<IMapper> mMapper;
GraphicsTestsBase& mTestBase;
native_handle_t* mRawHandle;
bool mImported = false;
uint32_t mStride;
const IMapper::BufferDescriptorInfo mInfo;
const BufferDescriptorInfo mInfo;
BufferHandle(const BufferHandle&) = delete;
void operator=(const BufferHandle&) = delete;
public:
BufferHandle(const sp<IMapper> mapper, native_handle_t* handle, bool imported, uint32_t stride,
const IMapper::BufferDescriptorInfo& info)
: mMapper(mapper), mRawHandle(handle), mImported(imported), mStride(stride), mInfo(info) {}
BufferHandle(GraphicsTestsBase& testBase, native_handle_t* handle, bool imported,
uint32_t stride, const BufferDescriptorInfo& info)
: mTestBase(testBase),
mRawHandle(handle),
mImported(imported),
mStride(stride),
mInfo(info) {}
~BufferHandle() {
if (mRawHandle == nullptr) return;
if (mImported) {
Error error = mMapper->freeBuffer(mRawHandle);
EXPECT_EQ(Error::NONE, error) << "failed to free buffer " << mRawHandle;
} else {
native_handle_close(mRawHandle);
native_handle_delete(mRawHandle);
}
}
~BufferHandle();
uint32_t stride() const { return mStride; }
AHardwareBuffer_Desc describe() const {
return {
.width = mInfo.width,
.height = mInfo.height,
.layers = mInfo.layerCount,
.width = static_cast<uint32_t>(mInfo.width),
.height = static_cast<uint32_t>(mInfo.height),
.layers = static_cast<uint32_t>(mInfo.layerCount),
.format = static_cast<uint32_t>(mInfo.format),
.usage = mInfo.usage,
.usage = static_cast<uint64_t>(mInfo.usage),
.stride = stride(),
.rfu0 = 0,
.rfu1 = 0,
@ -114,25 +128,43 @@ class BufferHandle {
class GraphicsTestsBase {
private:
friend class BufferHandle;
int32_t mIAllocatorVersion = 1;
std::shared_ptr<IAllocator> mAllocator;
sp<IMapper> mMapper;
sp<IMapper4> mMapper4;
AIMapper* mAIMapper = nullptr;
protected:
void Initialize(std::string allocatorService, std::string mapperService) {
void Initialize(std::string allocatorService) {
mAllocator = IAllocator::fromBinder(
ndk::SpAIBinder(AServiceManager_checkService(allocatorService.c_str())));
mMapper = IMapper::getService(mapperService);
ASSERT_TRUE(mAllocator->getInterfaceVersion(&mIAllocatorVersion).isOk());
if (mIAllocatorVersion >= 2) {
std::string mapperSuffix;
auto status = mAllocator->getIMapperLibrarySuffix(&mapperSuffix);
ASSERT_TRUE(status.isOk());
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;
auto loadIMapper = (AIMapper_loadIMapperFn)dlsym(so, "AIMapper_loadIMapper");
ASSERT_NE(nullptr, loadIMapper) << "AIMapper_locaIMapper missing from " << lib_name;
ASSERT_EQ(AIMAPPER_ERROR_NONE, loadIMapper(&mAIMapper));
ASSERT_NE(mAIMapper, nullptr);
} else {
// Don't have IMapper 5, fall back to IMapper 4
mMapper4 = IMapper4::getService();
ASSERT_NE(nullptr, mMapper4.get()) << "failed to get mapper service";
ASSERT_FALSE(mMapper4->isRemote()) << "mapper is not in passthrough mode";
}
ASSERT_NE(nullptr, mAllocator.get()) << "failed to get allocator service";
ASSERT_NE(nullptr, mMapper.get()) << "failed to get mapper service";
ASSERT_FALSE(mMapper->isRemote()) << "mapper is not in passthrough mode";
}
public:
BufferDescriptor createDescriptor(const IMapper::BufferDescriptorInfo& descriptorInfo) {
private:
BufferDescriptor createDescriptor(const BufferDescriptorInfo& descriptorInfo) {
BufferDescriptor descriptor;
mMapper->createDescriptor(
descriptorInfo, [&](const auto& tmpError, const auto& tmpDescriptor) {
mMapper4->createDescriptor(
convert(descriptorInfo), [&](const auto& tmpError, const auto& tmpDescriptor) {
ASSERT_EQ(Error::NONE, tmpError) << "failed to create descriptor";
descriptor = tmpDescriptor;
});
@ -140,14 +172,22 @@ class GraphicsTestsBase {
return descriptor;
}
std::unique_ptr<BufferHandle> allocate(const IMapper::BufferDescriptorInfo& descriptorInfo) {
auto descriptor = createDescriptor(descriptorInfo);
if (::testing::Test::HasFatalFailure()) {
return nullptr;
}
public:
std::unique_ptr<BufferHandle> allocate(const BufferDescriptorInfo& descriptorInfo) {
AllocationResult result;
auto status = mAllocator->allocate(descriptor, 1, &result);
::ndk::ScopedAStatus status;
if (mIAllocatorVersion >= 2) {
status = mAllocator->allocate2(descriptorInfo, 1, &result);
} else {
auto descriptor = createDescriptor(descriptorInfo);
if (::testing::Test::HasFatalFailure()) {
return nullptr;
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
status = mAllocator->allocate(descriptor, 1, &result);
#pragma clang diagnostic pop // deprecation
}
if (!status.isOk()) {
status_t error = status.getExceptionCode();
if (error == EX_SERVICE_SPECIFIC) {
@ -158,28 +198,48 @@ class GraphicsTestsBase {
}
return nullptr;
} else {
return std::make_unique<BufferHandle>(mMapper, dupFromAidl(result.buffers[0]), false,
return std::make_unique<BufferHandle>(*this, dupFromAidl(result.buffers[0]), false,
result.stride, descriptorInfo);
}
}
bool isSupported(const IMapper::BufferDescriptorInfo& descriptorInfo) {
bool isSupported(const BufferDescriptorInfo& descriptorInfo) {
bool ret = false;
EXPECT_TRUE(mMapper->isSupported(descriptorInfo,
[&](auto error, bool supported) {
ASSERT_EQ(Error::NONE, error);
ret = supported;
})
.isOk());
if (mIAllocatorVersion >= 2) {
EXPECT_TRUE(mAllocator->isSupported(descriptorInfo, &ret).isOk());
} else {
EXPECT_TRUE(mMapper4->isSupported(convert(descriptorInfo),
[&](auto error, bool supported) {
ASSERT_EQ(Error::NONE, error);
ret = supported;
})
.isOk());
}
return ret;
}
};
class GraphicsAllocatorAidlTests
: public GraphicsTestsBase,
public ::testing::TestWithParam<std::tuple<std::string, std::string>> {
BufferHandle::~BufferHandle() {
if (mRawHandle == nullptr) return;
if (mImported) {
if (mTestBase.mAIMapper) {
AIMapper_Error error = mTestBase.mAIMapper->v5.freeBuffer(mRawHandle);
EXPECT_EQ(AIMAPPER_ERROR_NONE, error);
} else {
Error error = mTestBase.mMapper4->freeBuffer(mRawHandle);
EXPECT_EQ(Error::NONE, error) << "failed to free buffer " << mRawHandle;
}
} else {
native_handle_close(mRawHandle);
native_handle_delete(mRawHandle);
}
}
class GraphicsAllocatorAidlTests : public GraphicsTestsBase,
public ::testing::TestWithParam<std::string> {
public:
void SetUp() override { Initialize(std::get<0>(GetParam()), std::get<1>(GetParam())); }
void SetUp() override { Initialize(GetParam()); }
void TearDown() override {}
};
@ -191,22 +251,22 @@ struct FlushMethod {
class GraphicsFrontBufferTests
: public GraphicsTestsBase,
public ::testing::TestWithParam<std::tuple<std::string, std::string, FlushMethod>> {
public ::testing::TestWithParam<std::tuple<std::string, FlushMethod>> {
private:
EglManager eglManager;
std::function<void(EglManager&)> flush;
public:
void SetUp() override {
Initialize(std::get<0>(GetParam()), std::get<1>(GetParam()));
flush = std::get<2>(GetParam()).func;
Initialize(std::get<0>(GetParam()));
flush = std::get<1>(GetParam()).func;
eglManager.initialize();
}
void TearDown() override { eglManager.destroy(); }
void fillWithGpu(AHardwareBuffer* buffer, float red, float green, float blue, float alpha) {
const EGLClientBuffer clientBuffer = eglGetNativeClientBufferANDROID(buffer);
EGLClientBuffer clientBuffer = eglGetNativeClientBufferANDROID(buffer);
AutoEglImage eglImage(eglManager.eglDisplay(), clientBuffer);
AutoSkiaGlTexture glTexture;
AutoGLFramebuffer glFbo;
@ -235,26 +295,14 @@ class GraphicsFrontBufferTests
}
};
TEST_P(GraphicsAllocatorAidlTests, CreateDescriptorBasic) {
ASSERT_NO_FATAL_FAILURE(createDescriptor({
.name = "CPU_8888",
.width = 64,
.height = 64,
.layerCount = 1,
.format = cast(PixelFormat::RGBA_8888),
.usage = pack({BufferUsage::CPU_WRITE_OFTEN, BufferUsage::CPU_READ_OFTEN}),
.reservedSize = 0,
}));
}
TEST_P(GraphicsAllocatorAidlTests, CanAllocate) {
auto buffer = allocate({
.name = "CPU_8888",
.name = {"CPU_8888"},
.width = 64,
.height = 64,
.layerCount = 1,
.format = cast(PixelFormat::RGBA_8888),
.usage = pack({BufferUsage::CPU_WRITE_OFTEN, BufferUsage::CPU_READ_OFTEN}),
.format = PixelFormat::RGBA_8888,
.usage = BufferUsage::CPU_WRITE_OFTEN | BufferUsage::CPU_READ_OFTEN,
.reservedSize = 0,
});
ASSERT_NE(nullptr, buffer.get());
@ -262,14 +310,14 @@ TEST_P(GraphicsAllocatorAidlTests, CanAllocate) {
}
TEST_P(GraphicsFrontBufferTests, FrontBufferGpuToCpu) {
IMapper::BufferDescriptorInfo info{
.name = "CPU_8888",
BufferDescriptorInfo info{
.name = {"CPU_8888"},
.width = 64,
.height = 64,
.layerCount = 1,
.format = cast(PixelFormat::RGBA_8888),
.usage = pack({BufferUsage::GPU_RENDER_TARGET, BufferUsage::CPU_READ_OFTEN,
BufferUsage::FRONT_BUFFER}),
.format = PixelFormat::RGBA_8888,
.usage = BufferUsage::GPU_RENDER_TARGET | BufferUsage::CPU_READ_OFTEN |
BufferUsage::FRONT_BUFFER,
.reservedSize = 0,
};
const bool supported = isSupported(info);
@ -304,14 +352,14 @@ TEST_P(GraphicsFrontBufferTests, FrontBufferGpuToCpu) {
}
TEST_P(GraphicsFrontBufferTests, FrontBufferGpuToGpu) {
IMapper::BufferDescriptorInfo info{
.name = "CPU_8888",
BufferDescriptorInfo info{
.name = {"CPU_8888"},
.width = 64,
.height = 64,
.layerCount = 1,
.format = cast(PixelFormat::RGBA_8888),
.usage = pack({BufferUsage::GPU_RENDER_TARGET, BufferUsage::GPU_TEXTURE,
BufferUsage::FRONT_BUFFER}),
.format = PixelFormat::RGBA_8888,
.usage = BufferUsage::GPU_RENDER_TARGET | BufferUsage::GPU_TEXTURE |
BufferUsage::FRONT_BUFFER,
.reservedSize = 0,
};
const bool supported = isSupported(info);
@ -344,11 +392,9 @@ TEST_P(GraphicsFrontBufferTests, FrontBufferGpuToGpu) {
}
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GraphicsAllocatorAidlTests);
INSTANTIATE_TEST_CASE_P(
PerInstance, GraphicsAllocatorAidlTests,
testing::Combine(testing::ValuesIn(getAidlHalInstanceNames(IAllocator::descriptor)),
testing::ValuesIn(getAllHalInstanceNames(IMapper::descriptor))),
PrintInstanceTupleNameToString<>);
INSTANTIATE_TEST_CASE_P(PerInstance, GraphicsAllocatorAidlTests,
testing::ValuesIn(getAidlHalInstanceNames(IAllocator::descriptor)),
PrintInstanceNameToString);
const auto FlushMethodsValues = testing::Values(
FlushMethod{"glFinish", [](EglManager&) { glFinish(); }},
@ -362,7 +408,7 @@ const auto FlushMethodsValues = testing::Values(
}},
FlushMethod{"eglClientWaitSync", [](EglManager& eglManager) {
EGLDisplay display = eglManager.eglDisplay();
EGLSyncKHR fence = eglCreateSyncKHR(display, EGL_SYNC_FENCE_KHR, NULL);
EGLSyncKHR fence = eglCreateSyncKHR(display, EGL_SYNC_FENCE_KHR, nullptr);
eglClientWaitSyncKHR(display, fence, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR,
EGL_FOREVER_KHR);
eglDestroySyncKHR(display, fence);
@ -371,9 +417,8 @@ GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GraphicsFrontBufferTests);
INSTANTIATE_TEST_CASE_P(
PerInstance, GraphicsFrontBufferTests,
testing::Combine(testing::ValuesIn(getAidlHalInstanceNames(IAllocator::descriptor)),
testing::ValuesIn(getAllHalInstanceNames(IMapper::descriptor)),
FlushMethodsValues),
[](auto info) -> std::string {
std::string name = std::to_string(info.index) + "/" + std::get<2>(info.param).name;
std::string name = std::to_string(info.index) + "/" + std::get<1>(info.param).name;
return Sanitize(name);
});
});

View file

@ -0,0 +1,104 @@
/**
* Copyright (c) 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.
*/
package {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "hardware_interfaces_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["hardware_interfaces_license"],
}
cc_library_headers {
name: "libimapper_stablec",
export_include_dirs: ["include"],
vendor_available: true,
header_libs: [
"libarect_headers",
],
export_header_lib_headers: [
"libarect_headers",
],
}
cc_library_headers {
name: "libimapper_providerutils",
vendor_available: true,
export_include_dirs: ["implutils/include"],
header_libs: [
"libbase_headers",
"libimapper_stablec",
],
export_header_lib_headers: [
"libbase_headers",
"libimapper_stablec",
],
}
cc_test {
name: "libimapper_providerutils_tests",
defaults: [
"android.hardware.graphics.allocator-ndk_shared",
"android.hardware.graphics.common-ndk_shared",
],
header_libs: [
"libimapper_providerutils",
],
srcs: [
"implutils/impltests.cpp",
],
visibility: [":__subpackages__"],
cpp_std: "experimental",
}
cc_test {
name: "VtsHalGraphicsMapperStableC_TargetTest",
cpp_std: "experimental",
defaults: [
"VtsHalTargetTestDefaults",
"use_libaidlvintf_gtest_helper_static",
"android.hardware.graphics.allocator-ndk_shared",
"android.hardware.graphics.common-ndk_shared",
],
srcs: [
"vts/VtsHalGraphicsMapperStableC_TargetTest.cpp",
],
shared_libs: [
"libbinder_ndk",
"libbase",
"libsync",
"libvndksupport",
],
static_libs: [
"libaidlcommonsupport",
"libgralloctypes",
"libgtest",
],
header_libs: [
"libimapper_stablec",
"libimapper_providerutils",
],
cflags: [
"-Wall",
"-Werror",
],
test_suites: [
"general-tests",
"vts",
],
}

View file

@ -0,0 +1,314 @@
/*
* Copyright (C) 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.
*/
#include <gtest/gtest.h>
#include <android/hardware/graphics/mapper/utils/IMapperMetadataTypes.h>
#include <android/hardware/graphics/mapper/utils/IMapperProvider.h>
#include <vector>
using namespace ::android::hardware::graphics::mapper;
using namespace ::aidl::android::hardware::graphics::common;
// These tests are primarily interested in hitting all the different *types* that can be
// serialized/deserialized than in exhaustively testing all the StandardMetadataTypes.
// Exhaustive testing of the actual metadata types is relegated for IMapper's VTS suite
// where meaning & correctness of values are more narrowly defined (eg, read-only values)
TEST(Metadata, setGetBufferId) {
using BufferId = StandardMetadata<StandardMetadataType::BUFFER_ID>::value;
std::vector<char> buffer;
buffer.resize(12, 0);
*reinterpret_cast<int64_t*>(buffer.data()) = 42;
EXPECT_EQ(8, BufferId::encode(18, buffer.data(), 0));
EXPECT_EQ(42, *reinterpret_cast<int64_t*>(buffer.data()));
EXPECT_EQ(8, BufferId::encode(18, buffer.data(), buffer.size()));
EXPECT_EQ(18, *reinterpret_cast<int64_t*>(buffer.data()));
EXPECT_FALSE(BufferId::decode(buffer.data(), 0));
auto read = BufferId::decode(buffer.data(), buffer.size());
EXPECT_TRUE(read.has_value());
EXPECT_EQ(18, read.value_or(0));
}
TEST(Metadata, setGetDataspace) {
using DataspaceValue = StandardMetadata<StandardMetadataType::DATASPACE>::value;
using intType = std::underlying_type_t<Dataspace>;
std::vector<char> buffer;
buffer.resize(12, 0);
EXPECT_EQ(4, DataspaceValue::encode(Dataspace::BT2020, buffer.data(), 0));
EXPECT_EQ(0, *reinterpret_cast<intType*>(buffer.data()));
EXPECT_EQ(4, DataspaceValue::encode(Dataspace::BT2020, buffer.data(), buffer.size()));
EXPECT_EQ(static_cast<intType>(Dataspace::BT2020), *reinterpret_cast<intType*>(buffer.data()));
EXPECT_FALSE(DataspaceValue::decode(buffer.data(), 0));
auto read = DataspaceValue::decode(buffer.data(), buffer.size());
ASSERT_TRUE(read.has_value());
EXPECT_EQ(Dataspace::BT2020, *read);
}
TEST(Metadata, setGetValidName) {
using NameValue = StandardMetadata<StandardMetadataType::NAME>::value;
std::vector<char> buffer;
buffer.resize(100, 'a');
buffer[buffer.size() - 1] = '\0';
// len("Hello") + sizeof(int64)
constexpr int expectedSize = 5 + sizeof(int64_t);
EXPECT_EQ(expectedSize, NameValue::encode("Hello", buffer.data(), buffer.size()));
EXPECT_EQ(5, *reinterpret_cast<int64_t*>(buffer.data()));
// Verify didn't write past the end of the desired size
EXPECT_EQ('a', buffer[expectedSize]);
auto readValue = NameValue::decode(buffer.data(), buffer.size());
ASSERT_TRUE(readValue.has_value());
EXPECT_EQ(5, readValue->length());
EXPECT_EQ("Hello", *readValue);
}
TEST(Metadata, setGetInvalidName) {
using NameValue = StandardMetadata<StandardMetadataType::NAME>::value;
std::vector<char> buffer;
buffer.resize(12, 'a');
buffer[buffer.size() - 1] = '\0';
// len("This is a long string") + sizeof(int64)
constexpr int expectedSize = 21 + sizeof(int64_t);
EXPECT_EQ(expectedSize,
NameValue::encode("This is a long string", buffer.data(), buffer.size()));
EXPECT_EQ(21, *reinterpret_cast<int64_t*>(buffer.data()));
// Verify didn't write the too-long string
EXPECT_EQ('a', buffer[9]);
EXPECT_EQ('\0', buffer[buffer.size() - 1]);
auto readValue = NameValue::decode(buffer.data(), buffer.size());
EXPECT_FALSE(readValue.has_value());
readValue = NameValue::decode(buffer.data(), 0);
ASSERT_FALSE(readValue.has_value());
}
TEST(Metadata, wouldOverflowName) {
using NameValue = StandardMetadata<StandardMetadataType::NAME>::value;
std::vector<char> buffer(100, 0);
// int_max + sizeof(int64) overflows int32
std::string_view bad_string{"badbeef", std::numeric_limits<int32_t>::max()};
EXPECT_EQ(-AIMAPPER_ERROR_BAD_VALUE,
NameValue::encode(bad_string, buffer.data(), buffer.size()));
// check barely overflows
bad_string = std::string_view{"badbeef", std::numeric_limits<int32_t>::max() - 7};
EXPECT_EQ(-AIMAPPER_ERROR_BAD_VALUE,
NameValue::encode(bad_string, buffer.data(), buffer.size()));
}
TEST(Metadata, setGetCompression) {
using CompressionValue = StandardMetadata<StandardMetadataType::COMPRESSION>::value;
ExtendableType myCompression{"bestest_compression_ever", 42};
std::vector<char> buffer(100, '\0');
const int expectedSize = myCompression.name.length() + sizeof(int64_t) + sizeof(int64_t);
EXPECT_EQ(expectedSize, CompressionValue::encode(myCompression, buffer.data(), 0));
EXPECT_EQ(0, buffer[0]);
EXPECT_EQ(expectedSize, CompressionValue::encode(myCompression, buffer.data(), buffer.size()));
EXPECT_EQ(myCompression.name.length(), *reinterpret_cast<int64_t*>(buffer.data()));
EXPECT_FALSE(CompressionValue::decode(buffer.data(), 0).has_value());
auto read = CompressionValue::decode(buffer.data(), buffer.size());
ASSERT_TRUE(read.has_value());
EXPECT_EQ(myCompression, read.value());
}
TEST(Metadata, setGetPlaneLayout) {
using PlaneLayoutValue = StandardMetadata<StandardMetadataType::PLANE_LAYOUTS>::value;
PlaneLayout myPlaneLayout;
myPlaneLayout.offsetInBytes = 10;
myPlaneLayout.sampleIncrementInBits = 11;
myPlaneLayout.strideInBytes = 12;
myPlaneLayout.widthInSamples = 13;
myPlaneLayout.heightInSamples = 14;
myPlaneLayout.totalSizeInBytes = 15;
myPlaneLayout.horizontalSubsampling = 16;
myPlaneLayout.verticalSubsampling = 17;
myPlaneLayout.components.resize(3);
for (int i = 0; i < myPlaneLayout.components.size(); i++) {
auto& it = myPlaneLayout.components[i];
it.type = ExtendableType{"Plane ID", 40 + i};
it.offsetInBits = 20 + i;
it.sizeInBits = 30 + i;
}
std::vector<PlaneLayout> layouts{myPlaneLayout, PlaneLayout{}};
std::vector<char> buffer(5000, '\0');
constexpr int componentSize = 8 + (4 * sizeof(int64_t));
constexpr int firstLayoutSize = (8 + 1) * sizeof(int64_t) + (3 * componentSize);
constexpr int secondLayoutSize = (8 + 1) * sizeof(int64_t);
constexpr int expectedSize = firstLayoutSize + secondLayoutSize + sizeof(int64_t);
EXPECT_EQ(expectedSize, PlaneLayoutValue::encode(layouts, buffer.data(), 0));
EXPECT_EQ(0, buffer[0]);
EXPECT_EQ(expectedSize, PlaneLayoutValue::encode(layouts, buffer.data(), buffer.size()));
EXPECT_EQ(3, reinterpret_cast<int64_t*>(buffer.data())[1]);
EXPECT_EQ(8, reinterpret_cast<int64_t*>(buffer.data())[2]);
EXPECT_EQ(40, reinterpret_cast<int64_t*>(buffer.data())[4]);
EXPECT_EQ(31, reinterpret_cast<int64_t*>(buffer.data())[11]);
EXPECT_EQ(22, reinterpret_cast<int64_t*>(buffer.data())[15]);
EXPECT_EQ(10, reinterpret_cast<int64_t*>(buffer.data())[17]);
EXPECT_EQ(11, reinterpret_cast<int64_t*>(buffer.data())[18]);
EXPECT_FALSE(PlaneLayoutValue::decode(buffer.data(), 0).has_value());
auto read = PlaneLayoutValue::decode(buffer.data(), buffer.size());
ASSERT_TRUE(read.has_value());
EXPECT_EQ(layouts, *read);
}
TEST(Metadata, setGetRects) {
using RectsValue = StandardMetadata<StandardMetadataType::CROP>::value;
std::vector<uint8_t> buffer(500, 0);
std::vector<Rect> cropRects{2};
cropRects[0] = Rect{10, 11, 12, 13};
cropRects[1] = Rect{20, 21, 22, 23};
constexpr int expectedSize = sizeof(int64_t) + (8 * sizeof(int32_t));
EXPECT_EQ(expectedSize, RectsValue::encode(cropRects, buffer.data(), buffer.size()));
EXPECT_EQ(2, reinterpret_cast<int64_t*>(buffer.data())[0]);
EXPECT_EQ(10, reinterpret_cast<int32_t*>(buffer.data())[2]);
auto read = RectsValue::decode(buffer.data(), buffer.size());
ASSERT_TRUE(read.has_value());
EXPECT_EQ(cropRects.size(), read->size());
EXPECT_EQ(cropRects, *read);
}
TEST(Metadata, setGetSmpte2086) {
using Smpte2086Value = StandardMetadata<StandardMetadataType::SMPTE2086>::value;
Smpte2086 source;
source.minLuminance = 12.335f;
source.maxLuminance = 452.889f;
source.whitePoint = XyColor{-6.f, -9.f};
source.primaryRed = XyColor{.1f, .2f};
source.primaryGreen = XyColor{.3f, .4f};
source.primaryBlue = XyColor{.5f, .6f};
constexpr int expectedSize = 10 * sizeof(float);
std::vector<uint8_t> buffer(500, 0);
EXPECT_EQ(expectedSize, Smpte2086Value::encode(source, buffer.data(), buffer.size()));
auto read = Smpte2086Value::decode(buffer.data(), buffer.size());
ASSERT_TRUE(read.has_value());
ASSERT_TRUE(read->has_value());
EXPECT_EQ(source, read->value());
// A valid encoding of a nullopt
read = Smpte2086Value::decode(nullptr, 0);
ASSERT_TRUE(read.has_value());
EXPECT_FALSE(read->has_value());
}
TEST(Metadata, setGetCta861_3) {
using Cta861_3Value = StandardMetadata<StandardMetadataType::CTA861_3>::value;
Cta861_3 source;
source.maxFrameAverageLightLevel = 244.55f;
source.maxContentLightLevel = 202.202f;
constexpr int expectedSize = 2 * sizeof(float);
std::vector<uint8_t> buffer(500, 0);
EXPECT_EQ(expectedSize, Cta861_3Value::encode(source, buffer.data(), buffer.size()));
auto read = Cta861_3Value::decode(buffer.data(), buffer.size());
ASSERT_TRUE(read.has_value());
ASSERT_TRUE(read->has_value());
EXPECT_EQ(source, read->value());
// A valid encoding of a nullopt
read = Cta861_3Value::decode(nullptr, 0);
ASSERT_TRUE(read.has_value());
EXPECT_FALSE(read->has_value());
}
TEST(Metadata, setGetSmpte2094_10) {
using SMPTE2094_10Value = StandardMetadata<StandardMetadataType::SMPTE2094_10>::value;
std::vector<uint8_t> buffer(500, 0);
EXPECT_EQ(0, SMPTE2094_10Value::encode(std::nullopt, buffer.data(), buffer.size()));
auto read = SMPTE2094_10Value::decode(buffer.data(), 0);
ASSERT_TRUE(read.has_value());
EXPECT_FALSE(read->has_value());
const std::vector<uint8_t> emptyBuffer;
EXPECT_EQ(sizeof(int64_t),
SMPTE2094_10Value::encode(emptyBuffer, buffer.data(), buffer.size()));
read = SMPTE2094_10Value::decode(buffer.data(), buffer.size());
ASSERT_TRUE(read.has_value());
ASSERT_TRUE(read->has_value());
EXPECT_EQ(0, read->value().size());
const std::vector<uint8_t> simpleBuffer{0, 1, 2, 3, 4, 5};
EXPECT_EQ(sizeof(int64_t) + 6,
SMPTE2094_10Value::encode(simpleBuffer, buffer.data(), buffer.size()));
read = SMPTE2094_10Value::decode(buffer.data(), buffer.size());
ASSERT_TRUE(read.has_value());
ASSERT_TRUE(read->has_value());
EXPECT_EQ(6, read->value().size());
EXPECT_EQ(simpleBuffer, read->value());
}
TEST(MetadataProvider, bufferId) {
using BufferId = StandardMetadata<StandardMetadataType::BUFFER_ID>::value;
std::vector<uint8_t> buffer(500, 0);
int result = provideStandardMetadata(StandardMetadataType::BUFFER_ID, buffer.data(),
buffer.size(), []<StandardMetadataType T>(auto&& provide) {
if constexpr (T == StandardMetadataType::BUFFER_ID) {
return provide(42);
}
return 0;
});
EXPECT_EQ(8, result);
auto read = BufferId::decode(buffer.data(), buffer.size());
EXPECT_EQ(42, read.value_or(0));
}
TEST(MetadataProvider, allJumpsWork) {
const auto& values = ndk::internal::enum_values<StandardMetadataType>;
auto get = [](StandardMetadataType type) -> int {
return provideStandardMetadata(type, nullptr, 0, []<StandardMetadataType T>(auto&&) {
return static_cast<int>(T) + 100;
});
};
for (auto& type : values) {
const int expected = type == StandardMetadataType::INVALID ? -AIMAPPER_ERROR_UNSUPPORTED
: static_cast<int>(type) + 100;
EXPECT_EQ(expected, get(type));
}
}
TEST(MetadataProvider, invalid) {
int result = provideStandardMetadata(StandardMetadataType::INVALID, nullptr, 0,
[]<StandardMetadataType T>(auto&&) { return 10; });
EXPECT_EQ(-AIMAPPER_ERROR_UNSUPPORTED, result);
}
TEST(MetadataProvider, outOfBounds) {
int result = provideStandardMetadata(static_cast<StandardMetadataType>(-1), nullptr, 0,
[]<StandardMetadataType T>(auto&&) { return 10; });
EXPECT_EQ(-AIMAPPER_ERROR_UNSUPPORTED, result) << "-1 should have resulted in UNSUPPORTED";
result = provideStandardMetadata(static_cast<StandardMetadataType>(100), nullptr, 0,
[]<StandardMetadataType T>(auto&&) { return 10; });
EXPECT_EQ(-AIMAPPER_ERROR_UNSUPPORTED, result)
<< "100 (out of range) should have resulted in UNSUPPORTED";
}

View file

@ -0,0 +1,576 @@
/*
* Copyright (C) 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.
*/
#pragma once
#include <aidl/android/hardware/graphics/common/BlendMode.h>
#include <aidl/android/hardware/graphics/common/BufferUsage.h>
#include <aidl/android/hardware/graphics/common/Cta861_3.h>
#include <aidl/android/hardware/graphics/common/Dataspace.h>
#include <aidl/android/hardware/graphics/common/ExtendableType.h>
#include <aidl/android/hardware/graphics/common/PixelFormat.h>
#include <aidl/android/hardware/graphics/common/PlaneLayout.h>
#include <aidl/android/hardware/graphics/common/PlaneLayoutComponent.h>
#include <aidl/android/hardware/graphics/common/Rect.h>
#include <aidl/android/hardware/graphics/common/Smpte2086.h>
#include <aidl/android/hardware/graphics/common/StandardMetadataType.h>
#include <aidl/android/hardware/graphics/common/XyColor.h>
#include <android/hardware/graphics/mapper/IMapper.h>
#include <cinttypes>
#include <string_view>
#include <type_traits>
#include <vector>
namespace android::hardware::graphics::mapper {
using ::aidl::android::hardware::graphics::common::BlendMode;
using ::aidl::android::hardware::graphics::common::BufferUsage;
using ::aidl::android::hardware::graphics::common::Cta861_3;
using ::aidl::android::hardware::graphics::common::Dataspace;
using ::aidl::android::hardware::graphics::common::ExtendableType;
using ::aidl::android::hardware::graphics::common::PixelFormat;
using ::aidl::android::hardware::graphics::common::PlaneLayout;
using ::aidl::android::hardware::graphics::common::PlaneLayoutComponent;
using ::aidl::android::hardware::graphics::common::Rect;
using ::aidl::android::hardware::graphics::common::Smpte2086;
using ::aidl::android::hardware::graphics::common::StandardMetadataType;
using ::aidl::android::hardware::graphics::common::XyColor;
class MetadataWriter {
private:
uint8_t* _Nonnull mDest;
size_t mSizeRemaining = 0;
int32_t mDesiredSize = 0;
void* _Nullable reserve(size_t sizeToWrite) {
if (mDesiredSize < 0) {
// Error state
return nullptr;
}
if (__builtin_add_overflow(mDesiredSize, sizeToWrite, &mDesiredSize)) {
// Overflowed, abort writing any further data
mDesiredSize = -AIMAPPER_ERROR_BAD_VALUE;
mSizeRemaining = 0;
return nullptr;
}
if (sizeToWrite > mSizeRemaining) {
mSizeRemaining = 0;
return nullptr;
} else {
mSizeRemaining -= sizeToWrite;
uint8_t* whereToWrite = mDest;
mDest += sizeToWrite;
return whereToWrite;
}
}
public:
explicit MetadataWriter(void* _Nullable destBuffer, size_t destBufferSize)
: mDest(reinterpret_cast<uint8_t*>(destBuffer)), mSizeRemaining(destBufferSize) {}
int32_t desiredSize() const { return mDesiredSize; }
template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
MetadataWriter& write(T value) {
auto sizeToWrite = sizeof(T);
if (void* dest = reserve(sizeToWrite)) {
memcpy(dest, &value, sizeToWrite);
}
return *this;
}
MetadataWriter& write(float value) {
auto sizeToWrite = sizeof(float);
if (void* dest = reserve(sizeToWrite)) {
memcpy(dest, &value, sizeToWrite);
}
return *this;
}
MetadataWriter& write(const std::string_view& value) {
auto sizeToWrite = value.length();
write<int64_t>(sizeToWrite);
if (void* dest = reserve(sizeToWrite)) {
memcpy(dest, value.data(), sizeToWrite);
}
return *this;
}
MetadataWriter& write(const std::vector<uint8_t>& value) {
auto sizeToWrite = value.size();
write<int64_t>(sizeToWrite);
if (void* dest = reserve(sizeToWrite)) {
memcpy(dest, value.data(), sizeToWrite);
}
return *this;
}
MetadataWriter& write(const ExtendableType& value) {
return write(value.name).write(value.value);
}
MetadataWriter& write(const XyColor& value) { return write(value.x).write(value.y); }
};
class MetadataReader {
private:
const uint8_t* _Nonnull mSrc;
size_t mSizeRemaining = 0;
bool mOk = true;
const void* _Nullable advance(size_t size) {
if (mOk && mSizeRemaining >= size) {
const void* buf = mSrc;
mSrc += size;
mSizeRemaining -= size;
return buf;
}
mOk = false;
return nullptr;
}
public:
explicit MetadataReader(const void* _Nonnull metadata, size_t metadataSize)
: mSrc(reinterpret_cast<const uint8_t*>(metadata)), mSizeRemaining(metadataSize) {}
[[nodiscard]] size_t remaining() const { return mSizeRemaining; }
[[nodiscard]] bool ok() const { return mOk; }
template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
MetadataReader& read(T& dest) {
if (const void* src = advance(sizeof(T))) {
memcpy(&dest, src, sizeof(T));
}
return *this;
}
MetadataReader& read(float& dest) {
if (const void* src = advance(sizeof(float))) {
memcpy(&dest, src, sizeof(float));
}
return *this;
}
MetadataReader& read(std::string& dest) {
dest = readString();
return *this;
}
MetadataReader& read(ExtendableType& dest) {
dest.name = readString();
read(dest.value);
return *this;
}
MetadataReader& read(XyColor& dest) {
read(dest.x);
read(dest.y);
return *this;
}
template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
[[nodiscard]] std::optional<T> readInt() {
auto sizeToRead = sizeof(T);
if (const void* src = advance(sizeof(T))) {
T ret;
memcpy(&ret, src, sizeToRead);
return ret;
}
return std::nullopt;
}
[[nodiscard]] std::string_view readString() {
auto lengthOpt = readInt<int64_t>();
if (!lengthOpt) {
return std::string_view{};
}
size_t length = lengthOpt.value();
if (const void* src = advance(length)) {
return std::string_view{reinterpret_cast<const char*>(src), length};
}
return std::string_view{};
}
[[nodiscard]] std::optional<ExtendableType> readExtendable() {
ExtendableType ret;
ret.name = readString();
auto value = readInt<int64_t>();
if (value) {
ret.value = value.value();
return ret;
} else {
return std::nullopt;
}
}
[[nodiscard]] std::vector<uint8_t> readBuffer() {
std::vector<uint8_t> ret;
size_t length = readInt<int64_t>().value_or(0);
if (const void* src = advance(length)) {
ret.resize(length);
memcpy(ret.data(), src, length);
}
return ret;
}
};
template <typename T, class Enable = void>
struct MetadataValue {};
template <typename T>
struct MetadataValue<T, std::enable_if_t<std::is_integral_v<T>>> {
[[nodiscard]] static int32_t encode(T value, void* _Nullable destBuffer,
size_t destBufferSize) {
return MetadataWriter{destBuffer, destBufferSize}.write(value).desiredSize();
}
[[nodiscard]] static std::optional<T> decode(const void* _Nonnull metadata,
size_t metadataSize) {
return MetadataReader{metadata, metadataSize}.readInt<T>();
}
};
template <typename T>
struct MetadataValue<T, std::enable_if_t<std::is_enum_v<T>>> {
[[nodiscard]] static int32_t encode(T value, void* _Nullable destBuffer,
size_t destBufferSize) {
return MetadataWriter{destBuffer, destBufferSize}
.write(static_cast<std::underlying_type_t<T>>(value))
.desiredSize();
}
[[nodiscard]] static std::optional<T> decode(const void* _Nonnull metadata,
size_t metadataSize) {
std::underlying_type_t<T> temp;
return MetadataReader{metadata, metadataSize}.read(temp).ok()
? std::optional<T>(static_cast<T>(temp))
: std::nullopt;
}
};
template <>
struct MetadataValue<std::string> {
[[nodiscard]] static int32_t encode(const std::string_view& value, void* _Nullable destBuffer,
size_t destBufferSize) {
return MetadataWriter{destBuffer, destBufferSize}.write(value).desiredSize();
}
[[nodiscard]] static std::optional<std::string> decode(const void* _Nonnull metadata,
size_t metadataSize) {
auto reader = MetadataReader{metadata, metadataSize};
auto result = reader.readString();
return reader.ok() ? std::optional<std::string>{result} : std::nullopt;
}
};
template <>
struct MetadataValue<ExtendableType> {
static_assert(sizeof(int64_t) == sizeof(ExtendableType::value));
[[nodiscard]] static int32_t encode(const ExtendableType& value, void* _Nullable destBuffer,
size_t destBufferSize) {
return MetadataWriter{destBuffer, destBufferSize}.write(value).desiredSize();
}
[[nodiscard]] static std::optional<ExtendableType> decode(const void* _Nonnull metadata,
size_t metadataSize) {
return MetadataReader{metadata, metadataSize}.readExtendable();
}
};
template <>
struct MetadataValue<std::vector<PlaneLayout>> {
[[nodiscard]] static int32_t encode(const std::vector<PlaneLayout>& values,
void* _Nullable destBuffer, size_t destBufferSize) {
MetadataWriter writer{destBuffer, destBufferSize};
writer.write<int64_t>(values.size());
for (const auto& value : values) {
writer.write<int64_t>(value.components.size());
for (const auto& component : value.components) {
writer.write(component.type)
.write<int64_t>(component.offsetInBits)
.write<int64_t>(component.sizeInBits);
}
writer.write<int64_t>(value.offsetInBytes)
.write<int64_t>(value.sampleIncrementInBits)
.write<int64_t>(value.strideInBytes)
.write<int64_t>(value.widthInSamples)
.write<int64_t>(value.heightInSamples)
.write<int64_t>(value.totalSizeInBytes)
.write<int64_t>(value.horizontalSubsampling)
.write<int64_t>(value.verticalSubsampling);
}
return writer.desiredSize();
}
using DecodeResult = std::optional<std::vector<PlaneLayout>>;
[[nodiscard]] static DecodeResult decode(const void* _Nonnull metadata, size_t metadataSize) {
std::vector<PlaneLayout> values;
MetadataReader reader{metadata, metadataSize};
auto numPlanes = reader.readInt<int64_t>().value_or(0);
values.reserve(numPlanes);
for (int i = 0; i < numPlanes && reader.ok(); i++) {
PlaneLayout& value = values.emplace_back();
auto numPlaneComponents = reader.readInt<int64_t>().value_or(0);
value.components.reserve(numPlaneComponents);
for (int i = 0; i < numPlaneComponents && reader.ok(); i++) {
PlaneLayoutComponent& component = value.components.emplace_back();
reader.read(component.type)
.read<int64_t>(component.offsetInBits)
.read<int64_t>(component.sizeInBits);
}
reader.read<int64_t>(value.offsetInBytes)
.read<int64_t>(value.sampleIncrementInBits)
.read<int64_t>(value.strideInBytes)
.read<int64_t>(value.widthInSamples)
.read<int64_t>(value.heightInSamples)
.read<int64_t>(value.totalSizeInBytes)
.read<int64_t>(value.horizontalSubsampling)
.read<int64_t>(value.verticalSubsampling);
}
return reader.ok() ? DecodeResult{std::move(values)} : std::nullopt;
}
};
template <>
struct MetadataValue<std::vector<Rect>> {
[[nodiscard]] static int32_t encode(const std::vector<Rect>& value, void* _Nullable destBuffer,
size_t destBufferSize) {
MetadataWriter writer{destBuffer, destBufferSize};
writer.write<int64_t>(value.size());
for (auto& rect : value) {
writer.write<int32_t>(rect.left)
.write<int32_t>(rect.top)
.write<int32_t>(rect.right)
.write<int32_t>(rect.bottom);
}
return writer.desiredSize();
}
using DecodeResult = std::optional<std::vector<Rect>>;
[[nodiscard]] static DecodeResult decode(const void* _Nonnull metadata, size_t metadataSize) {
MetadataReader reader{metadata, metadataSize};
std::vector<Rect> value;
auto numRects = reader.readInt<int64_t>().value_or(0);
value.reserve(numRects);
for (int i = 0; i < numRects && reader.ok(); i++) {
Rect& rect = value.emplace_back();
reader.read<int32_t>(rect.left)
.read<int32_t>(rect.top)
.read<int32_t>(rect.right)
.read<int32_t>(rect.bottom);
}
return reader.ok() ? DecodeResult{std::move(value)} : std::nullopt;
}
};
template <>
struct MetadataValue<std::optional<Smpte2086>> {
[[nodiscard]] static int32_t encode(const std::optional<Smpte2086>& optValue,
void* _Nullable destBuffer, size_t destBufferSize) {
if (optValue.has_value()) {
const auto& value = *optValue;
return MetadataWriter{destBuffer, destBufferSize}
.write(value.primaryRed)
.write(value.primaryGreen)
.write(value.primaryBlue)
.write(value.whitePoint)
.write(value.maxLuminance)
.write(value.minLuminance)
.desiredSize();
} else {
return 0;
}
}
// Double optional because the value type itself is an optional<>
using DecodeResult = std::optional<std::optional<Smpte2086>>;
[[nodiscard]] static DecodeResult decode(const void* _Nullable metadata, size_t metadataSize) {
std::optional<Smpte2086> optValue{std::nullopt};
if (metadataSize > 0) {
Smpte2086 value;
MetadataReader reader{metadata, metadataSize};
reader.read(value.primaryRed)
.read(value.primaryGreen)
.read(value.primaryBlue)
.read(value.whitePoint)
.read(value.maxLuminance)
.read(value.minLuminance);
if (reader.ok()) {
optValue = std::move(value);
} else {
return std::nullopt;
}
}
return DecodeResult{std::move(optValue)};
}
};
template <>
struct MetadataValue<std::optional<Cta861_3>> {
[[nodiscard]] static int32_t encode(const std::optional<Cta861_3>& optValue,
void* _Nullable destBuffer, size_t destBufferSize) {
if (optValue.has_value()) {
const auto& value = *optValue;
return MetadataWriter{destBuffer, destBufferSize}
.write(value.maxContentLightLevel)
.write(value.maxFrameAverageLightLevel)
.desiredSize();
} else {
return 0;
}
}
// Double optional because the value type itself is an optional<>
using DecodeResult = std::optional<std::optional<Cta861_3>>;
[[nodiscard]] static DecodeResult decode(const void* _Nullable metadata, size_t metadataSize) {
std::optional<Cta861_3> optValue{std::nullopt};
if (metadataSize > 0) {
MetadataReader reader{metadata, metadataSize};
Cta861_3 value;
reader.read(value.maxContentLightLevel).read(value.maxFrameAverageLightLevel);
if (reader.ok()) {
optValue = std::move(value);
} else {
return std::nullopt;
}
}
return DecodeResult{std::move(optValue)};
}
};
template <>
struct MetadataValue<std::optional<std::vector<uint8_t>>> {
[[nodiscard]] static int32_t encode(const std::optional<std::vector<uint8_t>>& value,
void* _Nullable destBuffer, size_t destBufferSize) {
if (!value.has_value()) {
return 0;
}
return MetadataWriter{destBuffer, destBufferSize}.write(*value).desiredSize();
}
using DecodeResult = std::optional<std::optional<std::vector<uint8_t>>>;
[[nodiscard]] static DecodeResult decode(const void* _Nonnull metadata, size_t metadataSize) {
std::optional<std::vector<uint8_t>> optValue;
if (metadataSize > 0) {
MetadataReader reader{metadata, metadataSize};
auto value = reader.readBuffer();
if (reader.ok()) {
optValue = std::move(value);
} else {
return std::nullopt;
}
}
return DecodeResult{std::move(optValue)};
}
};
template <StandardMetadataType>
struct StandardMetadata {};
#define DEFINE_TYPE(name, typeArg) \
template <> \
struct StandardMetadata<StandardMetadataType::name> { \
using value_type = typeArg; \
using value = MetadataValue<value_type>; \
static_assert( \
StandardMetadataType::name == \
ndk::internal::enum_values<StandardMetadataType>[static_cast<size_t>( \
StandardMetadataType::name)], \
"StandardMetadataType must have equivalent value to index"); \
}
DEFINE_TYPE(BUFFER_ID, uint64_t);
DEFINE_TYPE(NAME, std::string);
DEFINE_TYPE(WIDTH, uint64_t);
DEFINE_TYPE(HEIGHT, uint64_t);
DEFINE_TYPE(LAYER_COUNT, uint64_t);
DEFINE_TYPE(PIXEL_FORMAT_REQUESTED, PixelFormat);
DEFINE_TYPE(PIXEL_FORMAT_FOURCC, uint32_t);
DEFINE_TYPE(PIXEL_FORMAT_MODIFIER, uint64_t);
DEFINE_TYPE(USAGE, BufferUsage);
DEFINE_TYPE(ALLOCATION_SIZE, uint64_t);
DEFINE_TYPE(PROTECTED_CONTENT, uint64_t);
DEFINE_TYPE(COMPRESSION, ExtendableType);
DEFINE_TYPE(INTERLACED, ExtendableType);
DEFINE_TYPE(CHROMA_SITING, ExtendableType);
DEFINE_TYPE(PLANE_LAYOUTS, std::vector<PlaneLayout>);
DEFINE_TYPE(CROP, std::vector<Rect>);
DEFINE_TYPE(DATASPACE, Dataspace);
DEFINE_TYPE(BLEND_MODE, BlendMode);
DEFINE_TYPE(SMPTE2086, std::optional<Smpte2086>);
DEFINE_TYPE(CTA861_3, std::optional<Cta861_3>);
DEFINE_TYPE(SMPTE2094_10, std::optional<std::vector<uint8_t>>);
DEFINE_TYPE(SMPTE2094_40, std::optional<std::vector<uint8_t>>);
#undef DEFINE_TYPE
template <typename F, std::size_t... I>
void invokeWithStandardMetadata(F&& f, StandardMetadataType type, std::index_sequence<I...>) {
// Setup the jump table, mapping from each type to a springboard that invokes the template
// function with the appropriate concrete type
using F_PTR = decltype(&f);
using THUNK = void (*)(F_PTR);
static constexpr auto jump = std::array<THUNK, sizeof...(I)>{[](F_PTR fp) {
constexpr StandardMetadataType type = ndk::internal::enum_values<StandardMetadataType>[I];
if constexpr (type != StandardMetadataType::INVALID) {
(*fp)(StandardMetadata<type>{});
}
}...};
auto index = static_cast<size_t>(type);
if (index >= 0 && index < jump.size()) {
jump[index](&f);
}
}
template <typename F, typename StandardMetadataSequence = std::make_index_sequence<
ndk::internal::enum_values<StandardMetadataType>.size()>>
int32_t provideStandardMetadata(StandardMetadataType type, void* _Nullable destBuffer,
size_t destBufferSize, F&& f) {
int32_t retVal = -AIMAPPER_ERROR_UNSUPPORTED;
invokeWithStandardMetadata(
[&]<StandardMetadataType T>(StandardMetadata<T>) {
retVal = f.template operator()<T>(
[&](const typename StandardMetadata<T>::value_type& value) -> int32_t {
return StandardMetadata<T>::value::encode(value, destBuffer,
destBufferSize);
});
},
type, StandardMetadataSequence{});
return retVal;
}
template <typename F, typename StandardMetadataSequence = std::make_index_sequence<
ndk::internal::enum_values<StandardMetadataType>.size()>>
AIMapper_Error applyStandardMetadata(StandardMetadataType type, const void* _Nonnull metadata,
size_t metadataSize, F&& f) {
AIMapper_Error retVal = AIMAPPER_ERROR_UNSUPPORTED;
invokeWithStandardMetadata(
[&]<StandardMetadataType T>(StandardMetadata<T>) {
auto value = StandardMetadata<T>::value::decode(metadata, metadataSize);
if (value.has_value()) {
retVal = f.template operator()<T>(std::move(*value));
} else {
retVal = AIMAPPER_ERROR_BAD_VALUE;
}
},
type, StandardMetadataSequence{});
return retVal;
}
} // namespace android::hardware::graphics::mapper

View file

@ -0,0 +1,222 @@
/*
* Copyright (C) 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.
*/
#pragma once
#include <android-base/unique_fd.h>
#include <android/hardware/graphics/mapper/IMapper.h>
#include <log/log.h>
#include <mutex>
#include <optional>
#include <type_traits>
/**
* Helper utilities for providing an IMapper-StableC implementation.
*/
namespace vendor::mapper {
/**
* Extend from this interface to provide Version 5 of the IMapper interface
*/
struct IMapperV5Impl {
static const auto version = AIMAPPER_VERSION_5;
virtual ~IMapperV5Impl() = default;
virtual AIMapper_Error importBuffer(const native_handle_t* _Nonnull handle,
buffer_handle_t _Nullable* _Nonnull outBufferHandle) = 0;
virtual AIMapper_Error freeBuffer(buffer_handle_t _Nonnull buffer) = 0;
virtual AIMapper_Error getTransportSize(buffer_handle_t _Nonnull buffer,
uint32_t* _Nonnull outNumFds,
uint32_t* _Nonnull outNumInts) = 0;
virtual AIMapper_Error lock(buffer_handle_t _Nonnull buffer, uint64_t cpuUsage,
ARect accessRegion, int acquireFence,
void* _Nullable* _Nonnull outData) = 0;
virtual AIMapper_Error unlock(buffer_handle_t _Nonnull buffer, int* _Nonnull releaseFence) = 0;
virtual AIMapper_Error flushLockedBuffer(buffer_handle_t _Nonnull buffer) = 0;
virtual AIMapper_Error rereadLockedBuffer(buffer_handle_t _Nonnull buffer) = 0;
virtual int32_t getMetadata(buffer_handle_t _Nonnull buffer, AIMapper_MetadataType metadataType,
void* _Nullable destBuffer, size_t destBufferSize) = 0;
virtual int32_t getStandardMetadata(buffer_handle_t _Nonnull buffer,
int64_t standardMetadataType, void* _Nullable destBuffer,
size_t destBufferSize) = 0;
virtual AIMapper_Error setMetadata(buffer_handle_t _Nonnull buffer,
AIMapper_MetadataType metadataType,
const void* _Nonnull metadata, size_t metadataSize) = 0;
virtual AIMapper_Error setStandardMetadata(buffer_handle_t _Nonnull buffer,
int64_t standardMetadataType,
const void* _Nonnull metadata,
size_t metadataSize) = 0;
virtual AIMapper_Error listSupportedMetadataTypes(
const AIMapper_MetadataTypeDescription* _Nullable* _Nonnull outDescriptionList,
size_t* _Nonnull outNumberOfDescriptions) = 0;
virtual AIMapper_Error dumpBuffer(buffer_handle_t _Nonnull bufferHandle,
AIMapper_DumpBufferCallback _Nonnull dumpBufferCallback,
void* _Null_unspecified context) = 0;
virtual AIMapper_Error dumpAllBuffers(
AIMapper_BeginDumpBufferCallback _Nonnull beginDumpBufferCallback,
AIMapper_DumpBufferCallback _Nonnull dumpBufferCallback,
void* _Null_unspecified context) = 0;
virtual AIMapper_Error getReservedRegion(buffer_handle_t _Nonnull buffer,
void* _Nullable* _Nonnull outReservedRegion,
uint64_t* _Nonnull outReservedSize) = 0;
};
namespace provider {
#ifndef __cpp_inline_variables
#error "Only C++17 & newer is supported; inline variables is missing"
#endif
inline void* _Nullable sIMapperInstance = nullptr;
} // namespace provider
template <typename IMPL>
class IMapperProvider {
private:
static_assert(IMPL::version >= AIMAPPER_VERSION_5, "Must be at least AIMAPPER_VERSION_5");
static_assert(std::is_final_v<IMPL>, "Implementation must be final");
static_assert(std::is_constructible_v<IMPL>, "Implementation must have a no-args constructor");
std::once_flag mLoadOnceFlag;
std::optional<IMPL> mImpl;
AIMapper mMapper = {};
static IMPL& impl() {
return *reinterpret_cast<IMapperProvider<IMPL>*>(provider::sIMapperInstance)->mImpl;
}
void bindV5() {
mMapper.v5 = {
.importBuffer = [](const native_handle_t* _Nonnull handle,
buffer_handle_t _Nullable* _Nonnull outBufferHandle)
-> AIMapper_Error { return impl().importBuffer(handle, outBufferHandle); },
.freeBuffer = [](buffer_handle_t _Nonnull buffer) -> AIMapper_Error {
return impl().freeBuffer(buffer);
},
.getTransportSize = [](buffer_handle_t _Nonnull buffer,
uint32_t* _Nonnull outNumFds,
uint32_t* _Nonnull outNumInts) -> AIMapper_Error {
return impl().getTransportSize(buffer, outNumFds, outNumInts);
},
.lock = [](buffer_handle_t _Nonnull buffer, uint64_t cpuUsage, ARect accessRegion,
int acquireFence, void* _Nullable* _Nonnull outData) -> AIMapper_Error {
return impl().lock(buffer, cpuUsage, accessRegion, acquireFence, outData);
},
.unlock = [](buffer_handle_t _Nonnull buffer, int* _Nonnull releaseFence)
-> AIMapper_Error { return impl().unlock(buffer, releaseFence); },
.flushLockedBuffer = [](buffer_handle_t _Nonnull buffer) -> AIMapper_Error {
return impl().flushLockedBuffer(buffer);
},
.rereadLockedBuffer = [](buffer_handle_t _Nonnull buffer) -> AIMapper_Error {
return impl().rereadLockedBuffer(buffer);
},
.getMetadata = [](buffer_handle_t _Nonnull buffer,
AIMapper_MetadataType metadataType, void* _Nullable destBuffer,
size_t destBufferSize) -> int32_t {
return impl().getMetadata(buffer, metadataType, destBuffer, destBufferSize);
},
.getStandardMetadata = [](buffer_handle_t _Nonnull buffer,
int64_t standardMetadataType, void* _Nullable destBuffer,
size_t destBufferSize) -> int32_t {
return impl().getStandardMetadata(buffer, standardMetadataType, destBuffer,
destBufferSize);
},
.setMetadata = [](buffer_handle_t _Nonnull buffer,
AIMapper_MetadataType metadataType, const void* _Nonnull metadata,
size_t metadataSize) -> AIMapper_Error {
return impl().setMetadata(buffer, metadataType, metadata, metadataSize);
},
.setStandardMetadata =
[](buffer_handle_t _Nonnull buffer, int64_t standardMetadataType,
const void* _Nonnull metadata, size_t metadataSize) -> AIMapper_Error {
return impl().setStandardMetadata(buffer, standardMetadataType, metadata,
metadataSize);
},
.listSupportedMetadataTypes =
[](const AIMapper_MetadataTypeDescription* _Nullable* _Nonnull outDescriptionList,
size_t* _Nonnull outNumberOfDescriptions) -> AIMapper_Error {
return impl().listSupportedMetadataTypes(outDescriptionList,
outNumberOfDescriptions);
},
.dumpBuffer = [](buffer_handle_t _Nonnull bufferHandle,
AIMapper_DumpBufferCallback _Nonnull dumpBufferCallback,
void* _Null_unspecified context) -> AIMapper_Error {
return impl().dumpBuffer(bufferHandle, dumpBufferCallback, context);
},
.dumpAllBuffers =
[](AIMapper_BeginDumpBufferCallback _Nonnull beginDumpBufferCallback,
AIMapper_DumpBufferCallback _Nonnull dumpBufferCallback,
void* _Null_unspecified context) {
return impl().dumpAllBuffers(beginDumpBufferCallback,
dumpBufferCallback, context);
},
.getReservedRegion = [](buffer_handle_t _Nonnull buffer,
void* _Nullable* _Nonnull outReservedRegion,
uint64_t* _Nonnull outReservedSize) -> AIMapper_Error {
return impl().getReservedRegion(buffer, outReservedRegion, outReservedSize);
},
};
}
public:
explicit IMapperProvider() = default;
AIMapper_Error load(AIMapper* _Nullable* _Nonnull outImplementation) {
std::call_once(mLoadOnceFlag, [this] {
LOG_ALWAYS_FATAL_IF(provider::sIMapperInstance != nullptr,
"AIMapper implementation already loaded!");
provider::sIMapperInstance = this;
mImpl.emplace();
mMapper.version = IMPL::version;
if (IMPL::version >= AIMAPPER_VERSION_5) {
bindV5();
}
});
*outImplementation = &mMapper;
return AIMAPPER_ERROR_NONE;
}
};
} // namespace vendor::mapper

View file

@ -0,0 +1,689 @@
/*
* Copyright (C) 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.
*/
/**
* IMapper Stable-C HAL interface
*
* This file represents the sphal interface between libui & the IMapper HAL implementation.
* A vendor implementation of this interface is retrieved by looking up the vendor imapper
* implementation library via the IAllocator AIDL interface.
*
* This interface is not intended for general use.
*/
#pragma once
#include <sys/cdefs.h>
#include <cinttypes>
#include <cstddef>
#include <type_traits>
#include <android/rect.h>
#include <cutils/native_handle.h>
__BEGIN_DECLS
/**
* AIMapper versioning
*
* IMapper versions 0-1 are pre-treble
* IMapper versions 2-4 are HIDL
* C-style AIMapper API starts at 5
*/
enum AIMapper_Version : uint32_t {
AIMAPPER_VERSION_5 = 5,
};
/**
* Possible AIMapper errors
* Values are the same as IMapper 4.0's Error type for simplicity
*/
enum AIMapper_Error : int32_t {
/**
* No error.
*/
AIMAPPER_ERROR_NONE = 0,
/**
* Invalid BufferDescriptor.
*/
AIMAPPER_ERROR_BAD_DESCRIPTOR = 1,
/**
* Invalid buffer handle.
*/
AIMAPPER_ERROR_BAD_BUFFER = 2,
/**
* Invalid HardwareBufferDescription.
*/
AIMAPPER_ERROR_BAD_VALUE = 3,
/**
* Resource unavailable.
*/
AIMAPPER_ERROR_NO_RESOURCES = 5,
/**
* Permanent failure.
*/
AIMAPPER_ERROR_UNSUPPORTED = 7,
};
/**
* MetadataType represents the different types of buffer metadata that could be
* associated with a buffer. It is used by IMapper to help get and set buffer metadata
* on the buffer's native handle.
*
* Standard buffer metadata will have the name field set to
* "android.hardware.graphics.common.StandardMetadataType" and will contain values
* from StandardMetadataType.aidl.
*
* Vendor-provided metadata should be prefixed with a "vendor.mycompanyname.*" namespace. It is
* recommended that the metadata follows the pattern of StandardMetadaType.aidl. That is, an
* aidl-defined enum with @VendorStability on it and the naming then matching that type such
* as "vendor.mycompanyname.graphics.common.MetadataType" with the value field then set to the
* aidl's enum value.
*
* Each company should create their own enum & namespace. The name
* field prevents values from different companies from colliding.
*/
typedef struct AIMapper_MetadataType {
const char* _Nonnull name;
int64_t value;
} AIMapper_MetadataType;
typedef struct AIMapper_MetadataTypeDescription {
/**
* The `name` of the metadataType must be valid for the lifetime of the process
*/
AIMapper_MetadataType metadataType;
/**
* description should contain a string representation of the MetadataType.
*
* For example: "MyExampleMetadataType is a 64-bit timestamp in nanoseconds
* that indicates when a buffer is decoded. It is set by the media HAL after
* a buffer is decoded. It is used by the display HAL for hardware
* synchronization".
*
* This field is required for any non-StandardMetadataTypes. For StandardMetadataTypes this
* field may be null. The lifetime of this pointer must be valid for the duration of the
* process (that is, a static const char*).
*/
const char* _Nullable description;
/**
* isGettable represents if the MetadataType can be get.
*/
bool isGettable;
/**
* isSettable represents if the MetadataType can be set.
*/
bool isSettable;
/** Reserved for future use; must be zero-initialized currently */
uint8_t reserved[32];
} AIMapper_MetadataTypeDescription;
/**
* Callback that is passed to dumpBuffer.
*
* @param context The caller-provided void* that was passed to dumpBuffer.
* @param metadataType The type of the metadata passed to the callback
* @param value A pointer to the value of the metadata. The lifetime of this pointer is only
* valid for the duration of the call
* @param valueSize The size of the value buffer.
*/
typedef void (*AIMapper_DumpBufferCallback)(void* _Null_unspecified context,
AIMapper_MetadataType metadataType,
const void* _Nonnull value, size_t valueSize);
/**
* Callback that is passed to dumpAllBuffers.
*
* Indicates that a buffer is about to be dumped. Will be followed by N calls to
* AIMapper_DumpBufferCallback for all the metadata for this buffer.
*
* @param context The caller-provided void* that was passed to dumpAllBuffers.
*/
typedef void (*AIMapper_BeginDumpBufferCallback)(void* _Null_unspecified context);
/**
* Implementation of AIMAPPER_VERSION_5
* All functions must not be null & must provide a valid implementation.
*/
typedef struct AIMapperV5 {
/**
* Imports a raw buffer handle to create an imported buffer handle for use
* with the rest of the mapper or with other in-process libraries.
*
* A buffer handle is considered raw when it is cloned (e.g., with
* `native_handle_clone()`) from another buffer handle locally, or when it
* is received from another HAL server/client or another process. A raw
* buffer handle must not be used to access the underlying graphic
* buffer. It must be imported to create an imported handle first.
*
* This function must at least validate the raw handle before creating the
* imported handle. It must also support importing the same raw handle
* multiple times to create multiple imported handles. The imported handle
* must be considered valid everywhere in the process, including in
* another instance of the mapper.
*
* Because of passthrough HALs, a raw buffer handle received from a HAL
* may actually have been imported in the process. importBuffer() must treat
* such a handle as if it is raw and must not return `BAD_BUFFER`. The
* returned handle is independent from the input handle as usual, and
* freeBuffer() must be called on it when it is no longer needed.
*
* @param handle Raw buffer handle to import.
* @param outBufferHandle The resulting imported buffer handle.
* @return Error status of the call, which may be
* - `NONE` upon success.
* - `BAD_BUFFER` if the raw handle is invalid.
* - `NO_RESOURCES` if the raw handle cannot be imported due to
* unavailability of resources.
*/
AIMapper_Error (*_Nonnull importBuffer)(const native_handle_t* _Nonnull handle,
buffer_handle_t _Nullable* _Nonnull outBufferHandle);
/**
* Frees a buffer handle. Buffer handles returned by importBuffer() must be
* freed with this function when no longer needed.
*
* This function must free up all resources allocated by importBuffer() for
* the imported handle. For example, if the imported handle was created
* with `native_handle_create()`, this function must call
* `native_handle_close()` and `native_handle_delete()`.
*
* @param buffer Imported buffer handle.
* @return error Error status of the call, which may be
* - `NONE` upon success.
* - `BAD_BUFFER` if the buffer is invalid.
*/
AIMapper_Error (*_Nonnull freeBuffer)(buffer_handle_t _Nonnull buffer);
/**
* Calculates the transport size of a buffer. An imported buffer handle is a
* raw buffer handle with the process-local runtime data appended. This
* function, for example, allows a caller to omit the process-local runtime
* data at the tail when serializing the imported buffer handle.
*
* Note that a client might or might not omit the process-local runtime data
* when sending an imported buffer handle. The mapper must support both
* cases on the receiving end.
*
* @param buffer Buffer to get the transport size from.
* @param outNumFds The number of file descriptors needed for transport.
* @param outNumInts The number of integers needed for transport.
* @return error Error status of the call, which may be
* - `NONE` upon success.
* - `BAD_BUFFER` if the buffer is invalid.
*/
AIMapper_Error (*_Nonnull getTransportSize)(buffer_handle_t _Nonnull buffer,
uint32_t* _Nonnull outNumFds,
uint32_t* _Nonnull outNumInts);
/**
* Locks the given buffer for the specified CPU usage.
*
* Locking the same buffer simultaneously from multiple threads is
* permitted, but if any of the threads attempt to lock the buffer for
* writing, the behavior is undefined, except that it must not cause
* process termination or block the client indefinitely. Leaving the
* buffer content in an indeterminate state or returning an error are both
* acceptable.
*
* 1D buffers (width = size in bytes, height = 1, pixel_format = BLOB) must
* "lock in place". The buffers must be directly accessible via mapping.
*
* The client must not modify the content of the buffer outside of
* @p accessRegion, and the device need not guarantee that content outside
* of @p accessRegion is valid for reading. The result of reading or writing
* outside of @p accessRegion is undefined, except that it must not cause
* process termination.
*
* An accessRegion of all-zeros means the entire buffer. That is, it is
* equivalent to '(0,0)-(buffer width, buffer height)'.
*
* This function can lock both single-planar and multi-planar formats. The caller
* should use get() to get information about the buffer they are locking.
* get() can be used to get information about the planes, offsets, stride,
* etc.
*
* This function must also work on buffers with
* `AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_*` if supported by the device, as well
* as with any other formats requested by multimedia codecs when they are
* configured with a flexible-YUV-compatible color format.
*
* On success, @p data must be filled with a pointer to the locked buffer
* memory. This address will represent the top-left corner of the entire
* buffer, even if @p accessRegion does not begin at the top-left corner.
*
* The locked buffer must adhere to the format requested at allocation time
* in the BufferDescriptorInfo.
*
* @param buffer Buffer to lock.
* @param cpuUsage CPU usage flags to request. See BufferUsage.aidl for possible values.
* @param accessRegion Portion of the buffer that the client intends to
* access.
* @param acquireFence Handle containing a file descriptor referring to a
* sync fence object, which will be signaled when it is safe for the
* mapper to lock the buffer. @p acquireFence may be an empty fence (-1) if
* it is already safe to lock. Ownership is passed to the callee and it is the
* implementations responsibility to ensure it is closed even when an error
* occurs.
* @param outData CPU-accessible pointer to the buffer data.
* @return error Error status of the call, which may be
* - `NONE` upon success.
* - `BAD_BUFFER` if the buffer is invalid or is incompatible with this
* function.
* - `BAD_VALUE` if @p cpuUsage is 0, contains non-CPU usage flags, or
* is incompatible with the buffer. Also if the @p accessRegion is
* outside the bounds of the buffer or the accessRegion is invalid.
* - `NO_RESOURCES` if the buffer cannot be locked at this time. Note
* that locking may succeed at a later time.
* @return data CPU-accessible pointer to the buffer data.
*/
AIMapper_Error (*_Nonnull lock)(buffer_handle_t _Nonnull buffer, uint64_t cpuUsage,
ARect accessRegion, int acquireFence,
void* _Nullable* _Nonnull outData);
/**
* Unlocks a buffer to indicate all CPU accesses to the buffer have
* completed.
*
* @param buffer Buffer to unlock.
* @param releaseFence Handle containing a file descriptor referring to a
* sync fence object. The sync fence object will be signaled when the
* mapper has completed any pending work. @p releaseFence may be an
* empty fence (-1).
* @return error Error status of the call, which may be
* - `NONE` upon success.
* - `BAD_BUFFER` if the buffer is invalid or not locked.
*/
AIMapper_Error (*_Nonnull unlock)(buffer_handle_t _Nonnull buffer, int* _Nonnull releaseFence);
/**
* Flushes the contents of a locked buffer.
*
* This function flushes the CPUs caches for the range of all the buffer's
* planes and metadata. This should behave similarly to unlock() except the
* buffer should remain mapped to the CPU.
*
* The client is still responsible for calling unlock() when it is done
* with all CPU accesses to the buffer.
*
* If non-CPU blocks are simultaneously writing the buffer, the locked
* copy should still be flushed but what happens is undefined except that
* it should not cause any crashes.
*
* @param buffer Buffer to flush.
* @return error Error status of the call, which may be
* - `NONE` upon success.
* - `BAD_BUFFER` if the buffer is invalid or not locked.
*/
AIMapper_Error (*_Nonnull flushLockedBuffer)(buffer_handle_t _Nonnull buffer);
/**
* Rereads the contents of a locked buffer.
*
* This should fetch the most recent copy of the locked buffer.
*
* It may reread locked copies of the buffer in other processes.
*
* The client is still responsible for calling unlock() when it is done
* with all CPU accesses to the buffer.
*
* @param buffer Buffer to reread.
* @return error Error status of the call, which may be
* - `NONE` upon success.
* - `BAD_BUFFER` if the buffer is invalid or not locked.
* - `NO_RESOURCES` if the buffer cannot be reread at this time. Note
* that rereading may succeed at a later time.
*/
AIMapper_Error (*_Nonnull rereadLockedBuffer)(buffer_handle_t _Nonnull buffer);
/**
* Description for get(...), set(...) and getFromBufferDescriptorInfo(...)
*
* ------------ Overview -----------------------------------
* Gralloc 4 adds support for getting and setting buffer metadata on a buffer.
*
* To get buffer metadata, the client passes in a buffer handle and a token that
* represents the type of buffer metadata they would like to get. IMapper returns
* a byte stream that contains the buffer metadata. To set the buffer metadata, the
* client passes in a buffer handle and a token that represents the type of buffer
* metadata they would like to set and a byte stream that contains the buffer metadata
* they are setting.
*
* Buffer metadata is global for a buffer. When the metadata is set on the buffer
* in a process, the updated metadata should be available to all other processes.
* Please see "Storing and Propagating Metadata" below for more details.
*
* The getter and setter functions have been optimized for easy vendor extension.
* They do not require a formal extension to add support for getting and setting
* vendor defined buffer metadata. See "Buffer Metadata Token" and
* "Buffer Metadata Stream" below for more details.
*
* ------------ Storing and Propagating Metadata -----------
* Buffer metadata must be global. Any changes to the metadata must be propagated
* to all other processes immediately. Vendors may chose how they would like support
* this functionality.
*
* We recommend supporting this functionality by allocating an extra page of shared
* memory and storing it in the buffer's native_handle_t. The buffer metadata can
* be stored in the extra page of shared memory. Set operations are automatically
* propagated to all other processes.
*
* ------------ Buffer Metadata Synchronization ------------
* There are no explicit buffer metadata synchronization primitives. Many devices
* before gralloc 4 already support getting and setting of global buffer metadata
* with no explicit synchronization primitives. Adding synchronization primitives
* would just add unnecessary complexity.
*
* The general rule is if a process has permission to write to a buffer, they
* have permission to write to the buffer's writable metadata. If a process has permission
* to read from a buffer, they have permission to read the buffer's metadata.
*
* There is one exception to this rule. Fences CANNOT be used to protect a buffer's
* metadata. A process should finish writing to a buffer's metadata before
* sending the buffer to another process that will read or write to the buffer.
* This exception is needed because sometimes userspace needs to read the
* buffer's metadata before the buffer's contents are ready.
*
* As a simple example: an app renders to a buffer and then displays the buffer.
* In this example when the app renders to the buffer, both the buffer and its
* metadata need to be updated. The app's process queues up its work on the GPU
* and gets back an acquire fence. The app's process must update the buffer's
* metadata before enqueuing the buffer to SurfaceFlinger. The app process CANNOT
* update the buffer's metadata after enqueuing the buffer. When HardwareComposer
* receives the buffer, it is immediately safe to read the buffer's metadata
* and use it to program the display driver. To read the buffer's contents,
* display driver must still wait on the acquire fence.
*
* ------------ Buffer Metadata Token ----------------------
* In order to allow arbitrary vendor defined metadata, the token used to access
* metadata is defined defined as a struct that has a string representing
* the enum type and an int that represents the enum value. The string protects
* different enum values from colliding.
*
* The token struct (MetadataType) is defined as a C struct since it
* is passed into a C function. The standard buffer metadata types are NOT
* defined as a C enum but instead as an AIDL enum to allow for broader usage across
* other HALs and libraries. By putting the enum in the
* stable AIDL (hardware/interfaces/graphics/common/aidl/android/hardware/
* graphics/common/StandardMetadataType.aidl), vendors will be able to optionally
* choose to support future standard buffer metadata types without upgrading
* IMapper versions. For more information see the description of "struct MetadataType".
*
* ------------ Buffer Metadata Stream ---------------------
* The buffer metadata is get and set as a void* buffer. By getting
* and setting buffer metadata as a generic buffer, vendors can use the standard
* getters and setter functions defined here. Vendors do NOT need to add their own
* getters and setter functions for each new type of buffer metadata.
*
* Converting buffer metadata into a byte stream can be non-trivial. For the standard
* buffer metadata types defined in StandardMetadataType.aidl, there are also
* support functions that will encode the buffer metadata into a byte stream
* and decode the buffer metadata from a byte stream. We STRONGLY recommend using
* these support functions. The framework will use them when getting and setting
* metadata. The support functions are defined in
* frameworks/native/libs/gralloc/types/include/gralloctypes/Gralloc4.h.
*/
/**
* Gets the buffer metadata for a given MetadataType.
*
* Buffer metadata can be changed after allocation so clients should avoid "caching"
* the buffer metadata. For example, if the video resolution changes and the buffers
* are not reallocated, several buffer metadata values may change without warning.
* Clients should not expect the values to be constant. They should requery them every
* frame. The only exception is buffer metadata that is determined at allocation
* time. For StandardMetadataType values, only BUFFER_ID, NAME, WIDTH,
* HEIGHT, LAYER_COUNT, PIXEL_FORMAT_REQUESTED and USAGE are safe to cache because
* they are determined at allocation time.
*
* @param buffer Buffer containing desired metadata
* @param metadataType MetadataType for the metadata value being queried
* @param destBuffer Pointer to a buffer in which to store the result of the get() call; if
* null, the computed output size or error must still be returned.
* @param destBufferSize How large the destBuffer buffer is. If destBuffer is null this must be
* 0.
* @return The number of bytes written to `destBuffer` or which would have been written
* if `destBufferSize` was large enough.
* A negative value indicates an error, which may be
* - `BAD_BUFFER` if the raw handle is invalid.
* - `UNSUPPORTED` when metadataType is unknown/unsupported.
* IMapper must support getting all StandardMetadataType.aidl values defined
* at the time the device first launches.
*/
int32_t (*_Nonnull getMetadata)(buffer_handle_t _Nonnull buffer,
AIMapper_MetadataType metadataType, void* _Nullable destBuffer,
size_t destBufferSize);
/**
* Gets the buffer metadata for a StandardMetadataType.
*
* This is equivalent to `getMetadata` when passed an AIMapper_MetadataType with name
* set to "android.hardware.graphics.common.StandardMetadataType"
*
* Buffer metadata can be changed after allocation so clients should avoid "caching"
* the buffer metadata. For example, if the video resolution changes and the buffers
* are not reallocated, several buffer metadata values may change without warning.
* Clients should not expect the values to be constant. They should requery them every
* frame. The only exception is buffer metadata that is determined at allocation
* time. For StandardMetadataType values, only BUFFER_ID, NAME, WIDTH,
* HEIGHT, LAYER_COUNT, PIXEL_FORMAT_REQUESTED and USAGE are safe to cache because
* they are determined at allocation time.
*
* @param buffer Buffer containing desired metadata
* @param standardMetadataType StandardMetadataType for the metadata value being queried
* @param destBuffer Pointer to a buffer in which to store the result of the get() call; if
* null, the computed output size or error must still be returned.
* @param destBufferSize How large the destBuffer buffer is. If destBuffer is null this must be
* 0.
* @return The number of bytes written to `destBuffer` or which would have been written
* if `destBufferSize` was large enough.
* A negative value indicates an error, which may be
* - `BAD_BUFFER` if the raw handle is invalid.
* - `UNSUPPORTED` when metadataType is unknown/unsupported.
* IMapper must support getting all StandardMetadataType.aidl values defined
* at the time the device first launches.
*/
int32_t (*_Nonnull getStandardMetadata)(buffer_handle_t _Nonnull buffer,
int64_t standardMetadataType,
void* _Nullable destBuffer, size_t destBufferSize);
/**
* Sets the global value for a given MetadataType.
*
* Metadata fields are not required to be settable. This function can
* return Error::UNSUPPORTED whenever it doesn't support setting a
* particular Metadata field.
*
* The framework will attempt to set the following StandardMetadataType
* values: DATASPACE, SMPTE2086, CTA861_3, SMPTE2094_40 and BLEND_MODE.
* We require everyone to support setting those fields. If a device's Composer
* implementation supports a field, it should be supported here. Over time these
* metadata fields will be moved out of Composer/BufferQueue/etc. and into the
* buffer's Metadata fields.
*
* @param buffer Buffer receiving desired metadata
* @param metadataType MetadataType for the metadata value being set
* @param metadata Pointer to a buffer of bytes representing the value associated with
* @param metadataSize The size of the metadata buffer
* @return error Error status of the call, which may be
* - `NONE` upon success.
* - `BAD_BUFFER` if the raw handle is invalid.
* - `BAD_VALUE` when the field is constant and can never be set (such as
* BUFFER_ID, NAME, WIDTH, HEIGHT, LAYER_COUNT, PIXEL_FORMAT_REQUESTED and
* USAGE)
* - `NO_RESOURCES` if the set cannot be fulfilled due to unavailability of
* resources.
* - `UNSUPPORTED` when metadataType is unknown/unsupported or setting
* it is unsupported. Unsupported should also be returned if the metadata
* is malformed.
*/
AIMapper_Error (*_Nonnull setMetadata)(buffer_handle_t _Nonnull buffer,
AIMapper_MetadataType metadataType,
const void* _Nonnull metadata, size_t metadataSize);
/**
* Sets the global value for a given MetadataType.
*
* This is equivalent to `setMetadata` when passed an AIMapper_MetadataType with name
* set to "android.hardware.graphics.common.StandardMetadataType"
*
* Metadata fields are not required to be settable. This function can
* return Error::UNSUPPORTED whenever it doesn't support setting a
* particular Metadata field.
*
* The framework will attempt to set the following StandardMetadataType
* values: DATASPACE, SMPTE2086, CTA861_3, SMPTE2094_40 and BLEND_MODE.
* We require everyone to support setting those fields. If a device's Composer
* implementation supports a field, it should be supported here. Over time these
* metadata fields will be moved out of Composer/BufferQueue/etc. and into the
* buffer's Metadata fields.
*
* @param buffer Buffer receiving desired metadata
* @param standardMetadataType StandardMetadataType for the metadata value being set
* @param metadata Pointer to a buffer of bytes representing the value associated with
* @param metadataSize The size of the metadata buffer
* @return error Error status of the call, which may be
* - `NONE` upon success.
* - `BAD_BUFFER` if the raw handle is invalid.
* - `BAD_VALUE` when the field is constant and can never be set (such as
* BUFFER_ID, NAME, WIDTH, HEIGHT, LAYER_COUNT, PIXEL_FORMAT_REQUESTED and
* USAGE)
* - `NO_RESOURCES` if the set cannot be fulfilled due to unavailability of
* resources.
* - `UNSUPPORTED` when metadataType is unknown/unsupported or setting
* it is unsupported. Unsupported should also be returned if the metadata
* is malformed.
*/
AIMapper_Error (*_Nonnull setStandardMetadata)(buffer_handle_t _Nonnull buffer,
int64_t standardMetadataType,
const void* _Nonnull metadata,
size_t metadataSize);
/**
* Lists all the MetadataTypes supported by IMapper as well as a description
* of each supported MetadataType. For StandardMetadataTypes, the description
* string can be left empty.
*
* This list is expected to be static & thus the returned array must be valid for the
* lifetime of the process.
*
* @param outDescriptionList The list of descriptions
* @param outNumberOfDescriptions How many descriptions are in `outDescriptionList`
* @return error Error status of the call, which may be
* - `NONE` upon success.
* - `UNSUPPORTED` if there's any error
*/
AIMapper_Error (*_Nonnull listSupportedMetadataTypes)(
const AIMapper_MetadataTypeDescription* _Nullable* _Nonnull outDescriptionList,
size_t* _Nonnull outNumberOfDescriptions);
/**
* Dumps a buffer's metadata.
*
* @param buffer The buffer to dump the metadata for
* @param dumpBufferCallback Callback that will be invoked for each of the metadata fields
* @param context A caller-provided context to be passed to the dumpBufferCallback
* @return error Error status of the call, which may be
* - `NONE` upon success.
* - `BAD_BUFFER` if the raw handle is invalid.
* - `NO_RESOURCES` if the get cannot be fulfilled due to unavailability of
* resources.
*/
AIMapper_Error (*_Nonnull dumpBuffer)(buffer_handle_t _Nonnull buffer,
AIMapper_DumpBufferCallback _Nonnull dumpBufferCallback,
void* _Null_unspecified context);
/**
* Dump the metadata for all imported buffers in the current process
*
* The HAL implementation should invoke beginDumpCallback before dumping a buffer's metadata,
* followed by N calls to dumpBufferCallback for that buffer's metadata fields. The call
* sequence should follow this pseudocode:
*
* for (auto buffer : gListOfImportedBuffers) {
* beginDumpCallback(context);
* for (auto metadata : buffer->allMetadata()) {
* dumpBufferCallback(context, metadata...);
* }
* }
*
* @param beginDumpCallback Signals that a buffer is about to be dumped
* @param dumpBufferCallback Callback that will be invoked for each of the metadata fields
* @param context A caller-provided context to be passed to beginDumpCallback and
* dumpBufferCallback
* @return error Error status of the call, which may be
* - `NONE` upon success.
* - `BAD_BUFFER` if the raw handle is invalid.
* - `NO_RESOURCES` if the get cannot be fulfilled due to unavailability of
* resources.
*/
AIMapper_Error (*_Nonnull dumpAllBuffers)(
AIMapper_BeginDumpBufferCallback _Nonnull beginDumpCallback,
AIMapper_DumpBufferCallback _Nonnull dumpBufferCallback,
void* _Null_unspecified context);
/**
* Returns the region of shared memory associated with the buffer that is
* reserved for client use.
*
* The shared memory may be allocated from any shared memory allocator.
* The shared memory must be CPU-accessible and virtually contiguous. The
* starting address must be word-aligned.
*
* This function may only be called after importBuffer() has been called by the
* client. The reserved region must remain accessible until freeBuffer() has
* been called. After freeBuffer() has been called, the client must not access
* the reserved region.
*
* This reserved memory may be used in future versions of Android to
* help clients implement backwards compatible features without requiring
* IAllocator/IMapper updates.
*
* @param buffer Imported buffer handle.
* @param outReservedRegion CPU-accessible pointer to the reserved region
* @param outReservedSize the size of the reservedRegion that was requested
* in the BufferDescriptorInfo.
* @return error Error status of the call, which may be
* - `NONE` upon success.
* - `BAD_BUFFER` if the buffer is invalid.
*/
AIMapper_Error (*_Nonnull getReservedRegion)(buffer_handle_t _Nonnull buffer,
void* _Nullable* _Nonnull outReservedRegion,
uint64_t* _Nonnull outReservedSize);
} AIMapperV5;
/**
* Return value for AIMapper_loadIMapper
*
* Note: This struct's size is not fixed and callers must never store it by-value as a result.
* Only fields up to those covered by `version` are allowed to be accessed.
*/
typedef struct AIMapper {
alignas(alignof(max_align_t)) AIMapper_Version version;
AIMapperV5 v5;
} AIMapper;
/**
* Loads the vendor-provided implementation of AIMapper
* @return Error status of the call.
* - `NONE` upon success
* - `UNSUPPORTED` if no implementation is available
*/
AIMapper_Error AIMapper_loadIMapper(AIMapper* _Nullable* _Nonnull outImplementation);
__END_DECLS

File diff suppressed because it is too large Load diff