mirror of
https://github.com/Evolution-X/hardware_interfaces
synced 2026-02-01 11:36:00 +00:00
Merge "Add a few tests that FRONT_BUFFER is flushing properly" into tm-dev
This commit is contained in:
@@ -28,6 +28,7 @@ cc_test {
|
||||
defaults: [
|
||||
"VtsHalTargetTestDefaults",
|
||||
"use_libaidlvintf_gtest_helper_static",
|
||||
"hwui_defaults",
|
||||
],
|
||||
srcs: [
|
||||
"VtsHalGraphicsAllocatorAidl_TargetTest.cpp",
|
||||
@@ -46,10 +47,15 @@ cc_test {
|
||||
"libgui",
|
||||
"libhidlbase",
|
||||
"libvndksupport",
|
||||
"libnativewindow",
|
||||
],
|
||||
static_libs: [
|
||||
"libaidlcommonsupport",
|
||||
"libgtest",
|
||||
"libhwui",
|
||||
],
|
||||
header_libs: [
|
||||
"libhwui_internal_headers",
|
||||
],
|
||||
cflags: [
|
||||
"-Wall",
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#undef LOG_TAG
|
||||
#define LOG_TAG "VtsHalGraphicsAllocatorAidl_TargetTest"
|
||||
|
||||
#include <aidl/Vintf.h>
|
||||
@@ -28,6 +29,10 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <hidl/GtestPrinter.h>
|
||||
#include <hidl/ServiceManagement.h>
|
||||
#include <hwui/Bitmap.h>
|
||||
#include <renderthread/EglManager.h>
|
||||
#include <utils/GLUtils.h>
|
||||
#include <vndk/hardware_buffer.h>
|
||||
#include <initializer_list>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
@@ -38,6 +43,10 @@ using namespace aidl::android::hardware::graphics::common;
|
||||
using namespace android;
|
||||
using namespace android::hardware;
|
||||
using namespace android::hardware::graphics::mapper::V4_0;
|
||||
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;
|
||||
@@ -56,13 +65,15 @@ class BufferHandle {
|
||||
native_handle_t* mRawHandle;
|
||||
bool mImported = false;
|
||||
uint32_t mStride;
|
||||
const IMapper::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)
|
||||
: mMapper(mapper), mRawHandle(handle), mImported(imported), mStride(stride) {}
|
||||
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() {
|
||||
if (mRawHandle == nullptr) return;
|
||||
@@ -77,27 +88,47 @@ class BufferHandle {
|
||||
}
|
||||
|
||||
uint32_t stride() const { return mStride; }
|
||||
|
||||
AHardwareBuffer_Desc describe() const {
|
||||
return {
|
||||
.width = mInfo.width,
|
||||
.height = mInfo.height,
|
||||
.layers = mInfo.layerCount,
|
||||
.format = static_cast<uint32_t>(mInfo.format),
|
||||
.usage = mInfo.usage,
|
||||
.stride = stride(),
|
||||
.rfu0 = 0,
|
||||
.rfu1 = 0,
|
||||
};
|
||||
}
|
||||
|
||||
AHardwareBuffer* createAHardwareBuffer() const {
|
||||
auto desc = describe();
|
||||
AHardwareBuffer* buffer = nullptr;
|
||||
int err = AHardwareBuffer_createFromHandle(
|
||||
&desc, mRawHandle, AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_CLONE, &buffer);
|
||||
EXPECT_EQ(0, err) << "Failed to AHardwareBuffer_createFromHandle";
|
||||
return err ? nullptr : buffer;
|
||||
}
|
||||
};
|
||||
|
||||
class GraphicsAllocatorAidlTests
|
||||
: public ::testing::TestWithParam<std::tuple<std::string, std::string>> {
|
||||
class GraphicsTestsBase {
|
||||
private:
|
||||
std::shared_ptr<IAllocator> mAllocator;
|
||||
sp<IMapper> mMapper;
|
||||
|
||||
public:
|
||||
void SetUp() override {
|
||||
protected:
|
||||
void Initialize(std::string allocatorService, std::string mapperService) {
|
||||
mAllocator = IAllocator::fromBinder(
|
||||
ndk::SpAIBinder(AServiceManager_checkService(std::get<0>(GetParam()).c_str())));
|
||||
mMapper = IMapper::getService(std::get<1>(GetParam()));
|
||||
ndk::SpAIBinder(AServiceManager_checkService(allocatorService.c_str())));
|
||||
mMapper = IMapper::getService(mapperService);
|
||||
|
||||
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";
|
||||
}
|
||||
|
||||
void TearDown() override {}
|
||||
|
||||
public:
|
||||
BufferDescriptor createDescriptor(const IMapper::BufferDescriptorInfo& descriptorInfo) {
|
||||
BufferDescriptor descriptor;
|
||||
mMapper->createDescriptor(
|
||||
@@ -109,18 +140,7 @@ class GraphicsAllocatorAidlTests
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
native_handle_t* importBuffer(const hidl_handle& rawHandle) {
|
||||
native_handle_t* bufferHandle = nullptr;
|
||||
mMapper->importBuffer(rawHandle, [&](const auto& tmpError, const auto& tmpBuffer) {
|
||||
ASSERT_EQ(Error::NONE, tmpError)
|
||||
<< "failed to import buffer %p" << rawHandle.getNativeHandle();
|
||||
bufferHandle = static_cast<native_handle_t*>(tmpBuffer);
|
||||
});
|
||||
return bufferHandle;
|
||||
}
|
||||
|
||||
std::unique_ptr<BufferHandle> allocate(const IMapper::BufferDescriptorInfo& descriptorInfo,
|
||||
bool import = false) {
|
||||
std::unique_ptr<BufferHandle> allocate(const IMapper::BufferDescriptorInfo& descriptorInfo) {
|
||||
auto descriptor = createDescriptor(descriptorInfo);
|
||||
if (::testing::Test::HasFatalFailure()) {
|
||||
return nullptr;
|
||||
@@ -139,20 +159,81 @@ class GraphicsAllocatorAidlTests
|
||||
}
|
||||
return nullptr;
|
||||
} else {
|
||||
if (import) {
|
||||
native_handle_t* importedHandle = importBuffer(makeFromAidl(result.buffers[0]));
|
||||
if (importedHandle) {
|
||||
return std::make_unique<BufferHandle>(mMapper, importedHandle, true,
|
||||
result.stride);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
return std::make_unique<BufferHandle>(mMapper, dupFromAidl(result.buffers[0]),
|
||||
false, result.stride);
|
||||
}
|
||||
return std::make_unique<BufferHandle>(mMapper, dupFromAidl(result.buffers[0]), false,
|
||||
result.stride, descriptorInfo);
|
||||
}
|
||||
}
|
||||
|
||||
bool isSupported(const IMapper::BufferDescriptorInfo& descriptorInfo) {
|
||||
bool ret = false;
|
||||
EXPECT_TRUE(mMapper->isSupported(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>> {
|
||||
public:
|
||||
void SetUp() override { Initialize(std::get<0>(GetParam()), std::get<1>(GetParam())); }
|
||||
|
||||
void TearDown() override {}
|
||||
};
|
||||
|
||||
struct FlushMethod {
|
||||
std::string name;
|
||||
std::function<void(EglManager&)> func;
|
||||
};
|
||||
|
||||
class GraphicsFrontBufferTests
|
||||
: public GraphicsTestsBase,
|
||||
public ::testing::TestWithParam<std::tuple<std::string, 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;
|
||||
eglManager.initialize();
|
||||
}
|
||||
|
||||
void TearDown() override { eglManager.destroy(); }
|
||||
|
||||
void fillWithGpu(AHardwareBuffer* buffer, float red, float green, float blue, float alpha) {
|
||||
const EGLClientBuffer clientBuffer = eglGetNativeClientBufferANDROID(buffer);
|
||||
AutoEglImage eglImage(eglManager.eglDisplay(), clientBuffer);
|
||||
AutoSkiaGlTexture glTexture;
|
||||
AutoGLFramebuffer glFbo;
|
||||
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, eglImage.image);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
||||
glTexture.mTexture, 0);
|
||||
|
||||
AHardwareBuffer_Desc desc;
|
||||
AHardwareBuffer_describe(buffer, &desc);
|
||||
glViewport(0, 0, desc.width, desc.height);
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
glClearColor(red, green, blue, alpha);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
flush(eglManager);
|
||||
}
|
||||
|
||||
void fillWithGpu(AHardwareBuffer* buffer, /*RGBA*/ uint32_t color) {
|
||||
// Keep it simple for now
|
||||
static_assert(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__);
|
||||
float a = float((color >> 24) & 0xff) / 255.0f;
|
||||
float b = float((color >> 16) & 0xff) / 255.0f;
|
||||
float g = float((color >> 8) & 0xff) / 255.0f;
|
||||
float r = float((color)&0xff) / 255.0f;
|
||||
fillWithGpu(buffer, r, g, b, a);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_P(GraphicsAllocatorAidlTests, CreateDescriptorBasic) {
|
||||
@@ -181,9 +262,117 @@ TEST_P(GraphicsAllocatorAidlTests, CanAllocate) {
|
||||
EXPECT_GE(buffer->stride(), 64);
|
||||
}
|
||||
|
||||
TEST_P(GraphicsFrontBufferTests, FrontBufferGpuToCpu) {
|
||||
IMapper::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}),
|
||||
.reservedSize = 0,
|
||||
};
|
||||
const bool supported = isSupported(info);
|
||||
auto buffer = allocate(info);
|
||||
if (!supported) {
|
||||
ASSERT_EQ(nullptr, buffer.get())
|
||||
<< "Allocation succeeded, but IMapper::isSupported was false";
|
||||
} else {
|
||||
ASSERT_NE(nullptr, buffer.get()) << "Allocation failed, but IMapper::isSupported was true";
|
||||
}
|
||||
|
||||
AHardwareBuffer* ahb = buffer->createAHardwareBuffer();
|
||||
ASSERT_NE(nullptr, ahb);
|
||||
|
||||
// We draw 3 times with 3 different colors to ensure the flush is consistently flushing.
|
||||
// Particularly for glFlush() there's occasions where it seems something triggers a flush
|
||||
// to happen even though glFlush itself isn't consistently doing so, but for FRONT_BUFFER
|
||||
// bound buffers it is supposed to consistently flush.
|
||||
for (uint32_t color : {0xFF0000FFu, 0x00FF00FFu, 0x0000FFFFu}) {
|
||||
fillWithGpu(ahb, color);
|
||||
uint32_t* addr;
|
||||
ASSERT_EQ(0, AHardwareBuffer_lock(ahb, AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN, -1, nullptr,
|
||||
(void**)&addr));
|
||||
// Spot check a few pixels
|
||||
EXPECT_EQ(color, addr[0]);
|
||||
EXPECT_EQ(color, addr[32 + (32 * buffer->stride())]);
|
||||
AHardwareBuffer_unlock(ahb, nullptr);
|
||||
}
|
||||
|
||||
AHardwareBuffer_release(ahb);
|
||||
}
|
||||
|
||||
TEST_P(GraphicsFrontBufferTests, FrontBufferGpuToGpu) {
|
||||
IMapper::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}),
|
||||
.reservedSize = 0,
|
||||
};
|
||||
const bool supported = isSupported(info);
|
||||
auto buffer = allocate(info);
|
||||
if (!supported) {
|
||||
ASSERT_EQ(nullptr, buffer.get())
|
||||
<< "Allocation succeeded, but IMapper::isSupported was false";
|
||||
} else {
|
||||
ASSERT_NE(nullptr, buffer.get()) << "Allocation failed, but IMapper::isSupported was true";
|
||||
}
|
||||
|
||||
AHardwareBuffer* ahb = buffer->createAHardwareBuffer();
|
||||
ASSERT_NE(nullptr, ahb);
|
||||
|
||||
// We draw 3 times with 3 different colors to ensure the flush is consistently flushing.
|
||||
// Particularly for glFlush() there's occasions where it seems something triggers a flush
|
||||
// to happen even though glFlush itself isn't consistently doing so, but for FRONT_BUFFER
|
||||
// bound buffers it is supposed to consistently flush.
|
||||
for (uint32_t color : {0xFF0000FFu, 0x00FF00FFu, 0x0000FFFFu}) {
|
||||
fillWithGpu(ahb, color);
|
||||
sk_sp<Bitmap> hwBitmap = Bitmap::createFrom(ahb, SkColorSpace::MakeSRGB());
|
||||
SkBitmap cpuBitmap = hwBitmap->getSkBitmap();
|
||||
// Spot check a few pixels
|
||||
EXPECT_EQ(color, *cpuBitmap.getAddr32(0, 0));
|
||||
EXPECT_EQ(color, *cpuBitmap.getAddr32(16, 30));
|
||||
}
|
||||
|
||||
AHardwareBuffer_release(ahb);
|
||||
}
|
||||
|
||||
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<>);
|
||||
PrintInstanceTupleNameToString<>);
|
||||
|
||||
const auto FlushMethodsValues = testing::Values(
|
||||
FlushMethod{"glFinish", [](EglManager&) { glFinish(); }},
|
||||
FlushMethod{"glFlush",
|
||||
[](EglManager&) {
|
||||
glFlush();
|
||||
// Since the goal is to verify that glFlush() actually flushes, we can't
|
||||
// wait on any sort of fence since that will change behavior So instead we
|
||||
// just sleep & hope
|
||||
sleep(1);
|
||||
}},
|
||||
FlushMethod{"eglClientWaitSync", [](EglManager& eglManager) {
|
||||
EGLDisplay display = eglManager.eglDisplay();
|
||||
EGLSyncKHR fence = eglCreateSyncKHR(display, EGL_SYNC_FENCE_KHR, NULL);
|
||||
eglClientWaitSyncKHR(display, fence, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR,
|
||||
EGL_FOREVER_KHR);
|
||||
eglDestroySyncKHR(display, fence);
|
||||
}});
|
||||
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;
|
||||
return Sanitize(name);
|
||||
});
|
||||
Reference in New Issue
Block a user