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

Commit 4dde178d authored by Alec Mouri's avatar Alec Mouri
Browse files

[RenderEngine] Rebind output texture when unbinding framebuffer

Without this patch, once drawLayers() completes we'd hold onto the
backing image data longer than intended since it's bound to sibling
texture. This would cause screenshot buffers to be live for longer than
intended. This patch rebinds the framebuffer's output texture to a dummy
buffer so that we can free memory sooner.

We perform this rebind on the main thread in
SurfaceFlinger::postComposition, to minimize the amount of expected time
we steal for composition due to this cleanup. We also only do this
cleanup if the most recent draw fence had fired, since the driver will
need to wait for GPU work to complete before freeing resources which can
kill performance.

Bug: 140158384
Test: screencap and check dumpsys
Test: systrace when during screen rotation
Test: systrace when toggling GPU composition on and off
Test: librenderengine_test, libcompositionengine_test
Change-Id: I0aa39e62f03a09f6db000c2abdbb1d0711127fc3
Merged-In: I0aa39e62f03a09f6db000c2abdbb1d0711127fc3
parent 9bb4cfb0
Loading
Loading
Loading
Loading
+26 −1
Original line number Diff line number Diff line
@@ -904,6 +904,25 @@ void GLESRenderEngine::unbindFrameBuffer(Framebuffer* /* framebuffer */) {
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
}

bool GLESRenderEngine::cleanupPostRender() {
    ATRACE_CALL();

    if (mPriorResourcesCleaned ||
        (mLastDrawFence != nullptr && mLastDrawFence->getStatus() != Fence::Status::Signaled)) {
        // If we don't have a prior frame needing cleanup, then don't do anything.
        return false;
    }

    // Bind the texture to dummy data so that backing image data can be freed.
    GLFramebuffer* glFramebuffer = static_cast<GLFramebuffer*>(getFramebufferForDrawing());
    glFramebuffer->allocateBuffers(1, 1, mPlaceholderDrawBuffer);
    // Release the cached fence here, so that we don't churn reallocations when
    // we could no-op repeated calls of this method instead.
    mLastDrawFence = nullptr;
    mPriorResourcesCleaned = true;
    return true;
}

void GLESRenderEngine::checkErrors() const {
    checkErrors(nullptr);
}
@@ -1189,7 +1208,13 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display,
            // us bad parameters, or we messed up our shader generation).
            return INVALID_OPERATION;
        }
        mLastDrawFence = nullptr;
    } else {
        // The caller takes ownership of drawFence, so we need to duplicate the
        // fd here.
        mLastDrawFence = new Fence(dup(drawFence->get()));
    }
    mPriorResourcesCleaned = false;

    checkErrors();
    return NO_ERROR;
+12 −1
Original line number Diff line number Diff line
@@ -17,7 +17,6 @@
#ifndef SF_GLESRENDERENGINE_H_
#define SF_GLESRENDERENGINE_H_

#include <stdint.h>
#include <condition_variable>
#include <deque>
#include <mutex>
@@ -76,6 +75,7 @@ public:
                        const std::vector<const LayerSettings*>& layers,
                        ANativeWindowBuffer* buffer, const bool useFramebufferCache,
                        base::unique_fd&& bufferFence, base::unique_fd* drawFence) override;
    bool cleanupPostRender() override;

    EGLDisplay getEGLDisplay() const { return mEGLDisplay; }
    // Creates an output image for rendering to
@@ -231,6 +231,17 @@ private:
    std::mutex mRenderingMutex;

    std::unique_ptr<Framebuffer> mDrawingBuffer;
    // this is a 1x1 RGB buffer, but over-allocate in case a driver wants more
    // memory or if it needs to satisfy alignment requirements. In this case:
    // assume that each channel requires 4 bytes, and add 3 additional bytes to
    // ensure that we align on a word. Allocating 16 bytes will provide a
    // guarantee that we don't clobber memory.
    uint32_t mPlaceholderDrawBuffer[4];
    sp<Fence> mLastDrawFence;
    // Store a separate boolean checking if prior resources were cleaned up, as
    // devices that don't support native sync fences can't rely on a last draw
    // fence that doesn't exist.
    bool mPriorResourcesCleaned = true;

    // Blur effect processor, only instantiated when a layer requests it.
    BlurFilter* mBlurFilter = nullptr;
+2 −2
Original line number Diff line number Diff line
@@ -68,11 +68,11 @@ bool GLFramebuffer::setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, boo
    return true;
}

void GLFramebuffer::allocateBuffers(uint32_t width, uint32_t height) {
void GLFramebuffer::allocateBuffers(uint32_t width, uint32_t height, void* data) {
    ATRACE_CALL();

    glBindTexture(GL_TEXTURE_2D, mTextureName);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
+1 −1
Original line number Diff line number Diff line
@@ -39,7 +39,7 @@ public:

    bool setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected,
                               const bool useFramebufferCache) override;
    void allocateBuffers(uint32_t width, uint32_t height);
    void allocateBuffers(uint32_t width, uint32_t height, void* data = nullptr);
    EGLImageKHR getEGLImage() const { return mEGLImage; }
    uint32_t getTextureName() const { return mTextureName; }
    uint32_t getFramebufferName() const { return mFramebufferName; }
+8 −0
Original line number Diff line number Diff line
@@ -111,6 +111,14 @@ public:
    // Returns NO_ERROR when binds successfully, NO_MEMORY when there's no memory for allocation.
    virtual status_t bindFrameBuffer(Framebuffer* framebuffer) = 0;
    virtual void unbindFrameBuffer(Framebuffer* framebuffer) = 0;
    // Clean-up method that should be called on the main thread after the
    // drawFence returned by drawLayers fires. This method will free up
    // resources used by the most recently drawn frame. If the frame is still
    // being drawn, then this call is silently ignored.
    //
    // Returns true if resources were cleaned up, and false if we didn't need to
    // do any work.
    virtual bool cleanupPostRender() = 0;

    // queries
    virtual size_t getMaxTextureSize() const = 0;
Loading