Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7c62346f authored by John Reck's avatar John Reck Committed by Android (Google) Code Review
Browse files

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

parents f14d5d22 ddd4329e
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -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",
+224 −35
Original line number Diff line number Diff line
@@ -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,
        };
    }

class GraphicsAllocatorAidlTests
    : public ::testing::TestWithParam<std::tuple<std::string, std::string>> {
    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 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,19 +159,80 @@ 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;
            return std::make_unique<BufferHandle>(mMapper, dupFromAidl(result.buffers[0]), false,
                                                  result.stride, descriptorInfo);
        }
            } else {
                return std::make_unique<BufferHandle>(mMapper, dupFromAidl(result.buffers[0]),
                                                      false, result.stride);
    }

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

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

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