diff options
author | TreeHugger Robot <treehugger-gerrit@google.com> | 2020-08-26 18:32:28 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2020-08-26 18:32:28 +0000 |
commit | 0e0c5f6302b55b931c9cc6f68b496bf19178a1fa (patch) | |
tree | 9cedb8ff39d710315ca3845b5512e84bd71440f1 | |
parent | 7f8989b50b3276b33ab98a7adf2143155d5c9298 (diff) | |
parent | 1846e8a3a540575001dbc1736a8df0bf2659006f (diff) | |
download | native-0e0c5f6302b55b931c9cc6f68b496bf19178a1fa.tar.gz |
Merge "Toggle for RenderEngine to cleanup texture memory." into rvc-qpr-dev
-rw-r--r-- | libs/renderengine/gl/GLESRenderEngine.cpp | 58 | ||||
-rw-r--r-- | libs/renderengine/gl/GLESRenderEngine.h | 15 | ||||
-rw-r--r-- | libs/renderengine/include/renderengine/RenderEngine.h | 13 | ||||
-rw-r--r-- | libs/renderengine/include/renderengine/mock/RenderEngine.h | 2 | ||||
-rw-r--r-- | libs/renderengine/tests/RenderEngineTest.cpp | 41 |
5 files changed, 122 insertions, 7 deletions
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp index 92e7e715ea..0285c2f6f0 100644 --- a/libs/renderengine/gl/GLESRenderEngine.cpp +++ b/libs/renderengine/gl/GLESRenderEngine.cpp @@ -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 dummy data 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(), diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h index 42b8537b94..d5254a8a80 100644 --- a/libs/renderengine/gl/GLESRenderEngine.h +++ b/libs/renderengine/gl/GLESRenderEngine.h @@ -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 diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h index e06e1287c1..74bc44b22f 100644 --- a/libs/renderengine/include/renderengine/RenderEngine.h +++ b/libs/renderengine/include/renderengine/RenderEngine.h @@ -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; diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h index df0f17a6d5..101cbb70fd 100644 --- a/libs/renderengine/include/renderengine/mock/RenderEngine.h +++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h @@ -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*)); diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp index 16a8a0decb..f577eb3dc7 100644 --- a/libs/renderengine/tests/RenderEngineTest.cpp +++ b/libs/renderengine/tests/RenderEngineTest.cpp @@ -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 |