Add a few tests that FRONT_BUFFER is flushing properly
Test: this
Bug: 217776226
Change-Id: I49e32391620e005700d39539ca47da64199067a0
diff --git a/graphics/allocator/aidl/vts/Android.bp b/graphics/allocator/aidl/vts/Android.bp
index d74129b..99ffb24 100644
--- a/graphics/allocator/aidl/vts/Android.bp
+++ b/graphics/allocator/aidl/vts/Android.bp
@@ -28,6 +28,7 @@
defaults: [
"VtsHalTargetTestDefaults",
"use_libaidlvintf_gtest_helper_static",
+ "hwui_defaults",
],
srcs: [
"VtsHalGraphicsAllocatorAidl_TargetTest.cpp",
@@ -46,10 +47,15 @@
"libgui",
"libhidlbase",
"libvndksupport",
+ "libnativewindow",
],
static_libs: [
"libaidlcommonsupport",
"libgtest",
+ "libhwui",
+ ],
+ header_libs: [
+ "libhwui_internal_headers",
],
cflags: [
"-Wall",
diff --git a/graphics/allocator/aidl/vts/VtsHalGraphicsAllocatorAidl_TargetTest.cpp b/graphics/allocator/aidl/vts/VtsHalGraphicsAllocatorAidl_TargetTest.cpp
index 784bc66..c9d058d 100644
--- a/graphics/allocator/aidl/vts/VtsHalGraphicsAllocatorAidl_TargetTest.cpp
+++ b/graphics/allocator/aidl/vts/VtsHalGraphicsAllocatorAidl_TargetTest.cpp
@@ -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 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 @@
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 @@
}
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 @@
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 @@
}
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 @@
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<>);
\ No newline at end of file
+ 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);
+ });
\ No newline at end of file