SF: Clean framebuffer cache on hotplug
This CL cleans the framebuffer cache when a hotplug event
for already connected display is processed. This way when we
destruct the DisplayDevice the framebuffers will be completely
deallocated, so they can later be reallocated.
Test: 1. unplug HDMI
2. adb shell dumpsys SurfaceFlinger | grep FramebufferSurface
verify only 3 framebuffers are allocated
3. plug HDMI
5. adb shell dumpsys SurfaceFlinger | grep FramebufferSurface
verify only 3 framebuffers are allocated
Test: Using dev SurfaceFlinger backdoor (1036) which injects hotplug:
adb shell service call SurfaceFlinger 1036 && \
adb shell dumpsys SurfaceFlinger | grep FramebufferSurface
Bug: 160112047
Bug: 167670568
Change-Id: I96282bec439ebf413dcd5d57f6b884eccef19c5f
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 3090d19..42e74df 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -451,15 +451,10 @@
GLESRenderEngine::~GLESRenderEngine() {
// Destroy the image manager first.
mImageManager = nullptr;
+ cleanFramebufferCache();
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);
- DEBUG_EGL_IMAGE_TRACKER_DESTROY();
- }
eglDestroyImageKHR(mEGLDisplay, mPlaceholderImage);
mImageCache.clear();
eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
@@ -965,6 +960,7 @@
// Bind the texture to placeholder 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;
@@ -972,6 +968,20 @@
return true;
}
+void GLESRenderEngine::cleanFramebufferCache() {
+ std::lock_guard<std::mutex> lock(mFramebufferImageCacheMutex);
+ // Bind the texture to placeholder so that backing image data can be freed.
+ GLFramebuffer* glFramebuffer = static_cast<GLFramebuffer*>(getFramebufferForDrawing());
+ glFramebuffer->allocateBuffers(1, 1, mPlaceholderDrawBuffer);
+
+ while (!mFramebufferImageCache.empty()) {
+ EGLImageKHR expired = mFramebufferImageCache.front().second;
+ mFramebufferImageCache.pop_front();
+ eglDestroyImageKHR(mEGLDisplay, expired);
+ DEBUG_EGL_IMAGE_TRACKER_DESTROY();
+ }
+}
+
void GLESRenderEngine::checkErrors() const {
checkErrors(nullptr);
}
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index 2a98bd8..8f0df2c 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -135,6 +135,7 @@
status_t bindFrameBuffer(Framebuffer* framebuffer);
void unbindFrameBuffer(Framebuffer* framebuffer);
void bindExternalTextureImage(uint32_t texName, const Image& image);
+ void cleanFramebufferCache() EXCLUDES(mFramebufferImageCacheMutex) override;
// A data space is considered HDR data space if it has BT2020 color space
// with PQ or HLG transfer function.
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index 6db2af8..a0e7ab7 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -178,6 +178,7 @@
const std::vector<const LayerSettings*>& layers,
const sp<GraphicBuffer>& buffer, const bool useFramebufferCache,
base::unique_fd&& bufferFence, base::unique_fd* drawFence) = 0;
+ virtual void cleanFramebufferCache() = 0;
protected:
friend class threaded::RenderEngineThreaded;
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index 3fd125e..0b80d88 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -60,6 +60,7 @@
status_t(const DisplaySettings&, const std::vector<const LayerSettings*>&,
const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
base::unique_fd*));
+ MOCK_METHOD0(cleanFramebufferCache, void());
};
} // namespace mock
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index dc47070..9b79943 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -322,6 +322,21 @@
return resultFuture.get();
}
+void RenderEngineThreaded::cleanFramebufferCache() {
+ std::promise<void> resultPromise;
+ std::future<void> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::cleanFramebufferCache");
+ instance.cleanFramebufferCache();
+ resultPromise.set_value();
+ });
+ }
+ mCondition.notify_one();
+ resultFuture.wait();
+}
+
} // namespace threaded
} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
index 96a49b3..b02cdd9 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.h
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -65,6 +65,8 @@
const sp<GraphicBuffer>& buffer, const bool useFramebufferCache,
base::unique_fd&& bufferFence, base::unique_fd* drawFence) override;
+ void cleanFramebufferCache() override;
+
private:
void threadMain(CreateInstanceFactory factory);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 6478497..9d35a3f 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2684,6 +2684,7 @@
const sp<IBinder> drawingBinder = IInterface::asBinder(drawingState.surface);
if (currentBinder != drawingBinder || currentState.sequenceId != drawingState.sequenceId) {
// changing the surface is like destroying and recreating the DisplayDevice
+ getRenderEngine().cleanFramebufferCache();
if (const auto display = getDisplayDeviceLocked(displayToken)) {
display->disconnect();
}