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

Commit d43ccab5 authored by Alec Mouri's avatar Alec Mouri
Browse files

Add extra tests for renderengine

* Test buffer binding methods
* Test caching, and fix a bug caught by these tests.

Test: librenderengine_test
Change-Id: I50623d3a3f0b77f541044354c70db9fb536e66c3
parent f0497270
Loading
Loading
Loading
Loading
+36 −7
Original line number Original line Diff line number Diff line
@@ -432,10 +432,15 @@ GLESRenderEngine::GLESRenderEngine(uint32_t featureFlags, EGLDisplay display, EG
}
}


GLESRenderEngine::~GLESRenderEngine() {
GLESRenderEngine::~GLESRenderEngine() {
    for (const auto& image : mFramebufferImageCache) {
    std::lock_guard<std::mutex> lock(mRenderingMutex);
        eglDestroyImageKHR(mEGLDisplay, image.second);
    unbindFrameBuffer(mDrawingBuffer.get());
    mDrawingBuffer = nullptr;
    while (!mFramebufferImageCache.empty()) {
        EGLImageKHR expired = mFramebufferImageCache.front().second;
        mFramebufferImageCache.pop_front();
        eglDestroyImageKHR(mEGLDisplay, expired);
    }
    }
    mFramebufferImageCache.clear();
    mImageCache.clear();
    eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
    eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
    eglTerminate(mEGLDisplay);
    eglTerminate(mEGLDisplay);
}
}
@@ -634,6 +639,9 @@ status_t GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName, sp<Graphi
status_t GLESRenderEngine::bindExternalTextureBufferLocked(uint32_t texName,
status_t GLESRenderEngine::bindExternalTextureBufferLocked(uint32_t texName,
                                                           sp<GraphicBuffer> buffer,
                                                           sp<GraphicBuffer> buffer,
                                                           sp<Fence> bufferFence) {
                                                           sp<Fence> bufferFence) {
    if (buffer == nullptr) {
        return BAD_VALUE;
    }
    ATRACE_CALL();
    ATRACE_CALL();
    auto cachedImage = mImageCache.find(buffer->getId());
    auto cachedImage = mImageCache.find(buffer->getId());


@@ -779,7 +787,7 @@ bool GLESRenderEngine::useProtectedContext(bool useProtectedContext) {
EGLImageKHR GLESRenderEngine::createFramebufferImageIfNeeded(ANativeWindowBuffer* nativeBuffer,
EGLImageKHR GLESRenderEngine::createFramebufferImageIfNeeded(ANativeWindowBuffer* nativeBuffer,
                                                             bool isProtected) {
                                                             bool isProtected) {
    sp<GraphicBuffer> graphicBuffer = GraphicBuffer::from(nativeBuffer);
    sp<GraphicBuffer> graphicBuffer = GraphicBuffer::from(nativeBuffer);
    uint32_t bufferId = graphicBuffer->getId();
    uint64_t bufferId = graphicBuffer->getId();
    for (const auto& image : mFramebufferImageCache) {
    for (const auto& image : mFramebufferImageCache) {
        if (image.first == bufferId) {
        if (image.first == bufferId) {
            return image.second;
            return image.second;
@@ -818,6 +826,11 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display,
        sync_wait(bufferFence.get(), -1);
        sync_wait(bufferFence.get(), -1);
    }
    }


    if (buffer == nullptr) {
        ALOGE("No output buffer provided. Aborting GPU composition.");
        return BAD_VALUE;
    }

    {
    {
        std::lock_guard<std::mutex> lock(mRenderingMutex);
        std::lock_guard<std::mutex> lock(mRenderingMutex);


@@ -914,10 +927,12 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display,
            }
            }
        }
        }


        if (drawFence != nullptr) {
            *drawFence = flush();
            *drawFence = flush();
        }
        // If flush failed or we don't support native fences, we need to force the
        // If flush failed or we don't support native fences, we need to force the
        // gl command stream to be executed.
        // gl command stream to be executed.
        if (drawFence->get() < 0) {
        if (drawFence == nullptr || drawFence->get() < 0) {
            bool success = finish();
            bool success = finish();
            if (!success) {
            if (!success) {
                ALOGE("Failed to flush RenderEngine commands");
                ALOGE("Failed to flush RenderEngine commands");
@@ -1343,6 +1358,20 @@ bool GLESRenderEngine::needsXYZTransformMatrix() const {
    return (isInputHdrDataSpace || isOutputHdrDataSpace) && inputTransfer != outputTransfer;
    return (isInputHdrDataSpace || isOutputHdrDataSpace) && inputTransfer != outputTransfer;
}
}


bool GLESRenderEngine::isImageCachedForTesting(uint64_t bufferId) {
    std::lock_guard<std::mutex> lock(mRenderingMutex);
    const auto& cachedImage = mImageCache.find(bufferId);
    return cachedImage != mImageCache.end();
}

bool GLESRenderEngine::isFramebufferImageCachedForTesting(uint64_t bufferId) {
    std::lock_guard<std::mutex> lock(mRenderingMutex);
    return std::any_of(mFramebufferImageCache.cbegin(), mFramebufferImageCache.cend(),
                       [=](std::pair<uint64_t, EGLImageKHR> image) {
                           return image.first == bufferId;
                       });
}

// FlushTracer implementation
// FlushTracer implementation
GLESRenderEngine::FlushTracer::FlushTracer(GLESRenderEngine* engine) : mEngine(engine) {
GLESRenderEngine::FlushTracer::FlushTracer(GLESRenderEngine* engine) : mEngine(engine) {
    mThread = std::thread(&GLESRenderEngine::FlushTracer::loop, this);
    mThread = std::thread(&GLESRenderEngine::FlushTracer::loop, this);
+7 −1
Original line number Original line Diff line number Diff line
@@ -56,7 +56,7 @@ public:
                     EGLDisplay display, EGLConfig config, EGLContext ctxt, EGLSurface dummy,
                     EGLDisplay display, EGLConfig config, EGLContext ctxt, EGLSurface dummy,
                     EGLContext protectedContext, EGLSurface protectedDummy,
                     EGLContext protectedContext, EGLSurface protectedDummy,
                     uint32_t imageCacheSize);
                     uint32_t imageCacheSize);
    ~GLESRenderEngine() override;
    ~GLESRenderEngine() override EXCLUDES(mRenderingMutex);


    std::unique_ptr<Framebuffer> createFramebuffer() override;
    std::unique_ptr<Framebuffer> createFramebuffer() override;
    std::unique_ptr<Image> createImage() override;
    std::unique_ptr<Image> createImage() override;
@@ -94,6 +94,12 @@ public:
    // Creates an output image for rendering to
    // Creates an output image for rendering to
    EGLImageKHR createFramebufferImageIfNeeded(ANativeWindowBuffer* nativeBuffer, bool isProtected);
    EGLImageKHR createFramebufferImageIfNeeded(ANativeWindowBuffer* nativeBuffer, bool isProtected);


    // Test-only methods
    // Returns true iff mImageCache contains an image keyed by bufferId
    bool isImageCachedForTesting(uint64_t bufferId) EXCLUDES(mRenderingMutex);
    // Returns true iff mFramebufferImageCache contains an image keyed by bufferId
    bool isFramebufferImageCachedForTesting(uint64_t bufferId) EXCLUDES(mRenderingMutex);

protected:
protected:
    Framebuffer* getFramebufferForDrawing() override;
    Framebuffer* getFramebufferForDrawing() override;
    void dump(std::string& result) override;
    void dump(std::string& result) override;
+101 −10
Original line number Original line Diff line number Diff line
@@ -19,6 +19,7 @@
#include <renderengine/RenderEngine.h>
#include <renderengine/RenderEngine.h>
#include <sync/sync.h>
#include <sync/sync.h>
#include <ui/PixelFormat.h>
#include <ui/PixelFormat.h>
#include "../gl/GLESRenderEngine.h"


constexpr int DEFAULT_DISPLAY_WIDTH = 128;
constexpr int DEFAULT_DISPLAY_WIDTH = 128;
constexpr int DEFAULT_DISPLAY_HEIGHT = 256;
constexpr int DEFAULT_DISPLAY_HEIGHT = 256;
@@ -27,6 +28,19 @@ constexpr int DEFAULT_DISPLAY_OFFSET = 64;
namespace android {
namespace android {


struct RenderEngineTest : public ::testing::Test {
struct RenderEngineTest : public ::testing::Test {
    static void SetUpTestSuite() {
        sRE = renderengine::gl::GLESRenderEngine::create(static_cast<int32_t>(
                                                                 ui::PixelFormat::RGBA_8888),
                                                         0, 1);
    }

    static void TearDownTestSuite() {
        // The ordering here is important - sCurrentBuffer must live longer
        // than RenderEngine to avoid a null reference on tear-down.
        sRE = nullptr;
        sCurrentBuffer = nullptr;
    }

    static sp<GraphicBuffer> allocateDefaultBuffer() {
    static sp<GraphicBuffer> allocateDefaultBuffer() {
        return new GraphicBuffer(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT,
        return new GraphicBuffer(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT,
                                 HAL_PIXEL_FORMAT_RGBA_8888, 1,
                                 HAL_PIXEL_FORMAT_RGBA_8888, 1,
@@ -101,12 +115,12 @@ struct RenderEngineTest : public ::testing::Test {
                    DEFAULT_DISPLAY_HEIGHT - DEFAULT_DISPLAY_OFFSET);
                    DEFAULT_DISPLAY_HEIGHT - DEFAULT_DISPLAY_OFFSET);
    }
    }


    static void invokeDraw(renderengine::DisplaySettings settings,
    void invokeDraw(renderengine::DisplaySettings settings,
                           std::vector<renderengine::LayerSettings> layers,
                    std::vector<renderengine::LayerSettings> layers, sp<GraphicBuffer> buffer) {
                           sp<GraphicBuffer> buffer) {
        base::unique_fd fence;
        base::unique_fd fence;
        status_t status = sRE->drawLayers(settings, layers, buffer->getNativeBuffer(),
        status_t status = sRE->drawLayers(settings, layers, buffer->getNativeBuffer(),
                                          base::unique_fd(), &fence);
                                          base::unique_fd(), &fence);
        sCurrentBuffer = buffer;


        int fd = fence.release();
        int fd = fence.release();
        if (fd >= 0) {
        if (fd >= 0) {
@@ -115,9 +129,12 @@ struct RenderEngineTest : public ::testing::Test {
        }
        }


        ASSERT_EQ(NO_ERROR, status);
        ASSERT_EQ(NO_ERROR, status);
        if (layers.size() > 0) {
            ASSERT_TRUE(sRE->isFramebufferImageCachedForTesting(buffer->getId()));
        }
    }
    }


    static void drawEmptyLayers() {
    void drawEmptyLayers() {
        renderengine::DisplaySettings settings;
        renderengine::DisplaySettings settings;
        std::vector<renderengine::LayerSettings> layers;
        std::vector<renderengine::LayerSettings> layers;
        // Meaningless buffer since we don't do any drawing
        // Meaningless buffer since we don't do any drawing
@@ -200,17 +217,22 @@ struct RenderEngineTest : public ::testing::Test {


    void clearRegion();
    void clearRegion();


    // Dumb hack to get aroud the fact that tear-down for renderengine isn't
    // Keep around the same renderengine object to save on initialization time.
    // well defined right now, so we can't create multiple instances
    // For now, exercise the GL backend directly so that some caching specifics
    static std::unique_ptr<renderengine::RenderEngine> sRE;
    // can be tested without changing the interface.
    static std::unique_ptr<renderengine::gl::GLESRenderEngine> sRE;
    // Dumb hack to avoid NPE in the EGL driver: the GraphicBuffer needs to
    // be freed *after* RenderEngine is destroyed, so that the EGL image is
    // destroyed first.
    static sp<GraphicBuffer> sCurrentBuffer;


    sp<GraphicBuffer> mBuffer;
    sp<GraphicBuffer> mBuffer;


    std::vector<uint32_t> mTexNames;
    std::vector<uint32_t> mTexNames;
};
};


std::unique_ptr<renderengine::RenderEngine> RenderEngineTest::sRE =
std::unique_ptr<renderengine::gl::GLESRenderEngine> RenderEngineTest::sRE = nullptr;
        renderengine::RenderEngine::create(static_cast<int32_t>(ui::PixelFormat::RGBA_8888), 0, 1);
sp<GraphicBuffer> RenderEngineTest::sCurrentBuffer = nullptr;


struct ColorSourceVariant {
struct ColorSourceVariant {
    static void fillColor(renderengine::LayerSettings& layer, half r, half g, half b,
    static void fillColor(renderengine::LayerSettings& layer, half r, half g, half b,
@@ -245,7 +267,7 @@ struct BufferSourceVariant {
                          RenderEngineTest* fixture) {
                          RenderEngineTest* fixture) {
        sp<GraphicBuffer> buf = RenderEngineTest::allocateSourceBuffer(1, 1);
        sp<GraphicBuffer> buf = RenderEngineTest::allocateSourceBuffer(1, 1);
        uint32_t texName;
        uint32_t texName;
        RenderEngineTest::sRE->genTextures(1, &texName);
        fixture->sRE->genTextures(1, &texName);
        fixture->mTexNames.push_back(texName);
        fixture->mTexNames.push_back(texName);


        uint8_t* pixels;
        uint8_t* pixels;
@@ -740,6 +762,38 @@ TEST_F(RenderEngineTest, drawLayers_noLayersToDraw) {
    drawEmptyLayers();
    drawEmptyLayers();
}
}


TEST_F(RenderEngineTest, drawLayers_nullOutputBuffer) {
    renderengine::DisplaySettings settings;
    std::vector<renderengine::LayerSettings> layers;
    renderengine::LayerSettings layer;
    layer.geometry.boundaries = fullscreenRect().toFloatRect();
    BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
    layers.push_back(layer);
    base::unique_fd fence;
    status_t status = sRE->drawLayers(settings, layers, nullptr, base::unique_fd(), &fence);

    ASSERT_EQ(BAD_VALUE, status);
}

TEST_F(RenderEngineTest, drawLayers_nullOutputFence) {
    renderengine::DisplaySettings settings;
    settings.physicalDisplay = fullscreenRect();
    settings.clip = fullscreenRect();

    std::vector<renderengine::LayerSettings> layers;
    renderengine::LayerSettings layer;
    layer.geometry.boundaries = fullscreenRect().toFloatRect();
    BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
    layer.alpha = 1.0;
    layers.push_back(layer);

    status_t status = sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(),
                                      base::unique_fd(), nullptr);
    sCurrentBuffer = mBuffer;
    ASSERT_EQ(NO_ERROR, status);
    expectBufferColor(fullscreenRect(), 255, 0, 0, 255);
}

TEST_F(RenderEngineTest, drawLayers_fillRedBuffer_colorSource) {
TEST_F(RenderEngineTest, drawLayers_fillRedBuffer_colorSource) {
    fillRedBuffer<ColorSourceVariant>();
    fillRedBuffer<ColorSourceVariant>();
}
}
@@ -912,4 +966,41 @@ TEST_F(RenderEngineTest, drawLayers_clearRegion) {
    clearRegion();
    clearRegion();
}
}


TEST_F(RenderEngineTest, drawLayers_fillsBufferAndCachesImages) {
    renderengine::DisplaySettings settings;
    settings.physicalDisplay = fullscreenRect();
    settings.clip = fullscreenRect();

    std::vector<renderengine::LayerSettings> layers;

    renderengine::LayerSettings layer;
    layer.geometry.boundaries = fullscreenRect().toFloatRect();
    BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);

    layers.push_back(layer);
    invokeDraw(settings, layers, mBuffer);
    uint64_t bufferId = layer.source.buffer.buffer->getId();
    EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId));
    sRE->unbindExternalTextureBuffer(bufferId);
    EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId));
}

TEST_F(RenderEngineTest, drawLayers_bindExternalBufferWithNullBuffer) {
    status_t result = sRE->bindExternalTextureBuffer(0, nullptr, nullptr);
    ASSERT_EQ(BAD_VALUE, result);
}

TEST_F(RenderEngineTest, drawLayers_bindExternalBufferCachesImages) {
    sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1);
    uint32_t texName;
    sRE->genTextures(1, &texName);
    mTexNames.push_back(texName);

    sRE->bindExternalTextureBuffer(texName, buf, nullptr);
    uint64_t bufferId = buf->getId();
    EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId));
    sRE->unbindExternalTextureBuffer(bufferId);
    EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId));
}

} // namespace android
} // namespace android