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

Commit 5e1371af authored by Alec Mouri's avatar Alec Mouri Committed by Android (Google) Code Review
Browse files

Merge changes I50623d3a,Ic18b7192

* changes:
  Add extra tests for renderengine
  Add locking within RenderEngine during rendering
parents aba72b6d d43ccab5
Loading
Loading
Loading
Loading
+132 −91
Original line number Diff line number Diff line
@@ -432,10 +432,15 @@ GLESRenderEngine::GLESRenderEngine(uint32_t featureFlags, EGLDisplay display, EG
}

GLESRenderEngine::~GLESRenderEngine() {
    for (const auto& image : mFramebufferImageCache) {
        eglDestroyImageKHR(mEGLDisplay, image.second);
    std::lock_guard<std::mutex> lock(mRenderingMutex);
    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);
    eglTerminate(mEGLDisplay);
}
@@ -627,6 +632,16 @@ void GLESRenderEngine::bindExternalTextureImage(uint32_t texName, const Image& i

status_t GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer,
                                                     sp<Fence> bufferFence) {
    std::lock_guard<std::mutex> lock(mRenderingMutex);
    return bindExternalTextureBufferLocked(texName, buffer, bufferFence);
}

status_t GLESRenderEngine::bindExternalTextureBufferLocked(uint32_t texName,
                                                           sp<GraphicBuffer> buffer,
                                                           sp<Fence> bufferFence) {
    if (buffer == nullptr) {
        return BAD_VALUE;
    }
    ATRACE_CALL();
    auto cachedImage = mImageCache.find(buffer->getId());

@@ -675,6 +690,7 @@ status_t GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName, sp<Graphi
}

void GLESRenderEngine::unbindExternalTextureBuffer(uint64_t bufferId) {
    std::lock_guard<std::mutex> lock(mRenderingMutex);
    const auto& cachedImage = mImageCache.find(bufferId);
    if (cachedImage != mImageCache.end()) {
        ALOGV("Destroying image for buffer: %" PRIu64, bufferId);
@@ -771,7 +787,7 @@ bool GLESRenderEngine::useProtectedContext(bool useProtectedContext) {
EGLImageKHR GLESRenderEngine::createFramebufferImageIfNeeded(ANativeWindowBuffer* nativeBuffer,
                                                             bool isProtected) {
    sp<GraphicBuffer> graphicBuffer = GraphicBuffer::from(nativeBuffer);
    uint32_t bufferId = graphicBuffer->getId();
    uint64_t bufferId = graphicBuffer->getId();
    for (const auto& image : mFramebufferImageCache) {
        if (image.first == bufferId) {
            return image.second;
@@ -810,6 +826,14 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display,
        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);

        BindNativeBufferAsFramebuffer fbo(*this, buffer);

        if (fbo.getStatus() != NO_ERROR) {
@@ -861,7 +885,7 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display,
                isOpaque = layer.source.buffer.isOpaque;

                sp<GraphicBuffer> gBuf = layer.source.buffer.buffer;
            bindExternalTextureBuffer(layer.source.buffer.textureName, gBuf,
                bindExternalTextureBufferLocked(layer.source.buffer.textureName, gBuf,
                                                layer.source.buffer.fence);

                usePremultipliedAlpha = layer.source.buffer.usePremultipliedAlpha;
@@ -903,10 +927,12 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display,
            }
        }

        if (drawFence != nullptr) {
            *drawFence = flush();
        }
        // If flush failed or we don't support native fences, we need to force the
        // gl command stream to be executed.
    if (drawFence->get() < 0) {
        if (drawFence == nullptr || drawFence->get() < 0) {
            bool success = finish();
            if (!success) {
                ALOGE("Failed to flush RenderEngine commands");
@@ -918,6 +944,7 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display,
        }

        checkErrors();
    }
    return NO_ERROR;
}

@@ -1331,6 +1358,20 @@ bool GLESRenderEngine::needsXYZTransformMatrix() const {
    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
GLESRenderEngine::FlushTracer::FlushTracer(GLESRenderEngine* engine) : mEngine(engine) {
    mThread = std::thread(&GLESRenderEngine::FlushTracer::loop, this);
+22 −8
Original line number Diff line number Diff line
@@ -56,7 +56,7 @@ public:
                     EGLDisplay display, EGLConfig config, EGLContext ctxt, EGLSurface dummy,
                     EGLContext protectedContext, EGLSurface protectedDummy,
                     uint32_t imageCacheSize);
    ~GLESRenderEngine() override;
    ~GLESRenderEngine() override EXCLUDES(mRenderingMutex);

    std::unique_ptr<Framebuffer> createFramebuffer() override;
    std::unique_ptr<Image> createImage() override;
@@ -74,8 +74,9 @@ public:
    void genTextures(size_t count, uint32_t* names) override;
    void deleteTextures(size_t count, uint32_t const* names) override;
    void bindExternalTextureImage(uint32_t texName, const Image& image) override;
    status_t bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer, sp<Fence> fence);
    void unbindExternalTextureBuffer(uint64_t bufferId);
    status_t bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer, sp<Fence> fence)
            EXCLUDES(mRenderingMutex);
    void unbindExternalTextureBuffer(uint64_t bufferId) EXCLUDES(mRenderingMutex);
    status_t bindFrameBuffer(Framebuffer* framebuffer) override;
    void unbindFrameBuffer(Framebuffer* framebuffer) override;
    void checkErrors() const override;
@@ -85,7 +86,7 @@ public:
    bool useProtectedContext(bool useProtectedContext) override;
    status_t drawLayers(const DisplaySettings& display, const std::vector<LayerSettings>& layers,
                        ANativeWindowBuffer* buffer, base::unique_fd&& bufferFence,
                        base::unique_fd* drawFence) override;
                        base::unique_fd* drawFence) EXCLUDES(mRenderingMutex) override;

    // internal to RenderEngine
    EGLDisplay getEGLDisplay() const { return mEGLDisplay; }
@@ -93,6 +94,12 @@ public:
    // Creates an output image for rendering to
    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:
    Framebuffer* getFramebufferForDrawing() override;
    void dump(std::string& result) override;
@@ -197,10 +204,17 @@ private:
    const bool mUseColorManagement = false;

    // Cache of GL images that we'll store per GraphicBuffer ID
    // TODO: Layer should call back on destruction instead to clean this up,
    // as it has better system utilization at the potential expense of a
    // more complicated interface.
    std::unordered_map<uint64_t, std::unique_ptr<Image>> mImageCache;
    std::unordered_map<uint64_t, std::unique_ptr<Image>> mImageCache GUARDED_BY(mRenderingMutex);
    // Mutex guarding rendering operations, so that:
    // 1. GL operations aren't interleaved, and
    // 2. Internal state related to rendering that is potentially modified by
    // multiple threads is guaranteed thread-safe.
    std::mutex mRenderingMutex;

    // See bindExternalTextureBuffer above, but requiring that mRenderingMutex
    // is held.
    status_t bindExternalTextureBufferLocked(uint32_t texName, sp<GraphicBuffer> buffer,
                                             sp<Fence> fence) REQUIRES(mRenderingMutex);

    std::unique_ptr<Framebuffer> mDrawingBuffer;

+101 −10
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
#include <renderengine/RenderEngine.h>
#include <sync/sync.h>
#include <ui/PixelFormat.h>
#include "../gl/GLESRenderEngine.h"

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

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() {
        return new GraphicBuffer(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT,
                                 HAL_PIXEL_FORMAT_RGBA_8888, 1,
@@ -101,12 +115,12 @@ struct RenderEngineTest : public ::testing::Test {
                    DEFAULT_DISPLAY_HEIGHT - DEFAULT_DISPLAY_OFFSET);
    }

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

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

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

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

    void clearRegion();

    // Dumb hack to get aroud the fact that tear-down for renderengine isn't
    // well defined right now, so we can't create multiple instances
    static std::unique_ptr<renderengine::RenderEngine> sRE;
    // Keep around the same renderengine object to save on initialization time.
    // For now, exercise the GL backend directly so that some caching specifics
    // 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;

    std::vector<uint32_t> mTexNames;
};

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

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

        uint8_t* pixels;
@@ -740,6 +762,38 @@ TEST_F(RenderEngineTest, drawLayers_noLayersToDraw) {
    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) {
    fillRedBuffer<ColorSourceVariant>();
}
@@ -912,4 +966,41 @@ TEST_F(RenderEngineTest, drawLayers_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