Merge "Add a few tests that FRONT_BUFFER is flushing properly" into tm-dev

This commit is contained in:
John Reck
2022-04-01 20:11:26 +00:00
committed by Android (Google) Code Review
2 changed files with 230 additions and 35 deletions

View File

@@ -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",

View File

@@ -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);
});