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

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

Merge "Toggle for RenderEngine to cleanup texture memory." into rvc-qpr-dev-plus-aosp

parents 73b83e32 56f295e6
Loading
Loading
Loading
Loading
+57 −1
Original line number Diff line number Diff line
@@ -409,6 +409,23 @@ GLESRenderEngine::GLESRenderEngine(const RenderEngineCreationArgs& args, EGLDisp
    mImageManager = std::make_unique<ImageManager>(this);
    mImageManager->initThread();
    mDrawingBuffer = createFramebuffer();
    sp<GraphicBuffer> buf =
            new GraphicBuffer(1, 1, PIXEL_FORMAT_RGBA_8888, 1,
                              GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE, "placeholder");

    const status_t err = buf->initCheck();
    if (err != OK) {
        ALOGE("Error allocating placeholder buffer: %d", err);
        return;
    }
    mPlaceholderBuffer = buf.get();
    EGLint attributes[] = {
            EGL_NONE,
    };
    mPlaceholderImage = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
                                          mPlaceholderBuffer, attributes);
    ALOGE_IF(mPlaceholderImage == EGL_NO_IMAGE_KHR, "Failed to create placeholder image: %#x",
             eglGetError());
}

GLESRenderEngine::~GLESRenderEngine() {
@@ -423,6 +440,7 @@ GLESRenderEngine::~GLESRenderEngine() {
        eglDestroyImageKHR(mEGLDisplay, expired);
        DEBUG_EGL_IMAGE_TRACKER_DESTROY();
    }
    eglDestroyImageKHR(mEGLDisplay, mPlaceholderImage);
    mImageCache.clear();
    eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
    eglTerminate(mEGLDisplay);
@@ -589,6 +607,9 @@ void GLESRenderEngine::genTextures(size_t count, uint32_t* names) {
}

void GLESRenderEngine::deleteTextures(size_t count, uint32_t const* names) {
    for (int i = 0; i < count; ++i) {
        mTextureView.erase(names[i]);
    }
    glDeleteTextures(count, names);
}

@@ -646,6 +667,7 @@ status_t GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName,
        }

        bindExternalTextureImage(texName, *cachedImage->second);
        mTextureView.insert_or_assign(texName, buffer->getId());
    }

    // Wait for the new buffer to be ready.
@@ -887,7 +909,7 @@ void GLESRenderEngine::unbindFrameBuffer(Framebuffer* /*framebuffer*/) {
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
}

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

    if (mPriorResourcesCleaned ||
@@ -896,6 +918,30 @@ bool GLESRenderEngine::cleanupPostRender() {
        return false;
    }

    // This is a bit of a band-aid fix for FrameCaptureProcessor, as we should
    // not need to keep memory around if we don't need to do so.
    if (mode == CleanupMode::CLEAN_ALL) {
        // TODO: SurfaceFlinger memory utilization may benefit from resetting
        // texture bindings as well. Assess if it does and there's no performance regression
        // when rebinding the same image data to the same texture, and if so then its mode
        // behavior can be tweaked.
        if (mPlaceholderImage != EGL_NO_IMAGE_KHR) {
            for (auto [textureName, bufferId] : mTextureView) {
                if (bufferId && mPlaceholderImage != EGL_NO_IMAGE_KHR) {
                    glBindTexture(GL_TEXTURE_EXTERNAL_OES, textureName);
                    glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES,
                                                 static_cast<GLeglImageOES>(mPlaceholderImage));
                    mTextureView[textureName] = std::nullopt;
                    checkErrors();
                }
            }
        }
        {
            std::lock_guard<std::mutex> lock(mRenderingMutex);
            mImageCache.clear();
        }
    }

    // Bind the texture to placeholder so that backing image data can be freed.
    GLFramebuffer* glFramebuffer = static_cast<GLFramebuffer*>(getFramebufferForDrawing());
    glFramebuffer->allocateBuffers(1, 1, mPlaceholderDrawBuffer);
@@ -1616,6 +1662,16 @@ bool GLESRenderEngine::isImageCachedForTesting(uint64_t bufferId) {
    return cachedImage != mImageCache.end();
}

bool GLESRenderEngine::isTextureNameKnownForTesting(uint32_t texName) {
    const auto& entry = mTextureView.find(texName);
    return entry != mTextureView.end();
}

std::optional<uint64_t> GLESRenderEngine::getBufferIdForTextureNameForTesting(uint32_t texName) {
    const auto& entry = mTextureView.find(texName);
    return entry != mTextureView.end() ? entry->second : std::nullopt;
}

bool GLESRenderEngine::isFramebufferImageCachedForTesting(uint64_t bufferId) {
    std::lock_guard<std::mutex> lock(mFramebufferImageCacheMutex);
    return std::any_of(mFramebufferImageCache.cbegin(), mFramebufferImageCache.cend(),
+14 −1
Original line number Diff line number Diff line
@@ -75,7 +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;
    bool cleanupPostRender(CleanupMode mode) override;

    EGLDisplay getEGLDisplay() const { return mEGLDisplay; }
    // Creates an output image for rendering to
@@ -86,6 +86,12 @@ public:
    // Test-only methods
    // Returns true iff mImageCache contains an image keyed by bufferId
    bool isImageCachedForTesting(uint64_t bufferId) EXCLUDES(mRenderingMutex);
    // Returns true iff texName was previously generated by RenderEngine and was
    // not destroyed.
    bool isTextureNameKnownForTesting(uint32_t texName);
    // Returns the buffer ID of the content bound to texName, or nullopt if no
    // such mapping exists.
    std::optional<uint64_t> getBufferIdForTextureNameForTesting(uint32_t texName);
    // Returns true iff mFramebufferImageCache contains an image keyed by bufferId
    bool isFramebufferImageCachedForTesting(uint64_t bufferId)
            EXCLUDES(mFramebufferImageCacheMutex);
@@ -224,6 +230,8 @@ private:

    // Cache of GL images that we'll store per GraphicBuffer ID
    std::unordered_map<uint64_t, std::unique_ptr<Image>> mImageCache GUARDED_BY(mRenderingMutex);
    std::unordered_map<uint32_t, std::optional<uint64_t>> mTextureView;

    // Mutex guarding rendering operations, so that:
    // 1. GL operations aren't interleaved, and
    // 2. Internal state related to rendering that is potentially modified by
@@ -237,6 +245,11 @@ private:
    // ensure that we align on a word. Allocating 16 bytes will provide a
    // guarantee that we don't clobber memory.
    uint32_t mPlaceholderDrawBuffer[4];
    // Placeholder buffer and image, similar to mPlaceholderDrawBuffer, but
    // instead these are intended for cleaning up texture memory with the
    // GL_TEXTURE_EXTERNAL_OES target.
    ANativeWindowBuffer* mPlaceholderBuffer = nullptr;
    EGLImage mPlaceholderImage = EGL_NO_IMAGE_KHR;
    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
+12 −1
Original line number Diff line number Diff line
@@ -111,14 +111,25 @@ 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;

    enum class CleanupMode {
        CLEAN_OUTPUT_RESOURCES,
        CLEAN_ALL,
    };
    // 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.
    //
    // If mode is CLEAN_OUTPUT_RESOURCES, then only resources related to the
    // output framebuffer are cleaned up, including the sibling texture.
    //
    // If mode is CLEAN_ALL, then we also cleanup resources related to any input
    // buffers.
    //
    // Returns true if resources were cleaned up, and false if we didn't need to
    // do any work.
    virtual bool cleanupPostRender() = 0;
    virtual bool cleanupPostRender(CleanupMode mode = CleanupMode::CLEAN_OUTPUT_RESOURCES) = 0;

    // queries
    virtual size_t getMaxTextureSize() const = 0;
+1 −1
Original line number Diff line number Diff line
@@ -56,7 +56,7 @@ public:
    MOCK_CONST_METHOD0(isProtected, bool());
    MOCK_CONST_METHOD0(supportsProtectedContent, bool());
    MOCK_METHOD1(useProtectedContext, bool(bool));
    MOCK_METHOD0(cleanupPostRender, bool());
    MOCK_METHOD1(cleanupPostRender, bool(CleanupMode mode));
    MOCK_METHOD6(drawLayers,
                 status_t(const DisplaySettings&, const std::vector<const LayerSettings*>&,
                          ANativeWindowBuffer*, const bool, base::unique_fd&&, base::unique_fd*));
+38 −3
Original line number Diff line number Diff line
@@ -81,6 +81,7 @@ struct RenderEngineTest : public ::testing::Test {
        }
        for (uint32_t texName : mTexNames) {
            sRE->deleteTextures(1, &texName);
            EXPECT_FALSE(sRE->isTextureNameKnownForTesting(texName));
        }
    }

@@ -1424,10 +1425,44 @@ TEST_F(RenderEngineTest, cleanupPostRender_cleansUpOnce) {
    if (fd >= 0) {
        sync_wait(fd, -1);
    }

    // Only cleanup the first time.
    EXPECT_TRUE(sRE->cleanupPostRender());
    EXPECT_FALSE(sRE->cleanupPostRender());
    EXPECT_TRUE(sRE->cleanupPostRender(
            renderengine::RenderEngine::CleanupMode::CLEAN_OUTPUT_RESOURCES));
    EXPECT_FALSE(sRE->cleanupPostRender(
            renderengine::RenderEngine::CleanupMode::CLEAN_OUTPUT_RESOURCES));
}

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

    std::vector<const 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);

    base::unique_fd fence;
    sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), true, base::unique_fd(), &fence);

    const int fd = fence.get();
    if (fd >= 0) {
        sync_wait(fd, -1);
    }

    uint64_t bufferId = layer.source.buffer.buffer->getId();
    uint32_t texName = layer.source.buffer.textureName;
    EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId));
    EXPECT_EQ(bufferId, sRE->getBufferIdForTextureNameForTesting(texName));

    EXPECT_TRUE(sRE->cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL));

    // Now check that our view of memory is good.
    EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId));
    EXPECT_EQ(std::nullopt, sRE->getBufferIdForTextureNameForTesting(bufferId));
    EXPECT_TRUE(sRE->isTextureNameKnownForTesting(texName));
}

} // namespace android