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