From 05c868e74470d07913c2d81affdfa1d749218d81 Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Tue, 18 Mar 2025 12:26:11 -0700 Subject: [PATCH] [Impeller] tear down swapchain when backgrounding. (#165259) When we return to the foreground the swapchain is reconstructed anyway. Eagerly tearing the old one down releases memory faster. Highlighted by a regression in a customer: money benchmark. --- .../flutter/ci/licenses_golden/excluded_files | 1 + .../impeller/renderer/backend/vulkan/BUILD.gn | 1 + .../backend/vulkan/surface_context_vk.cc | 7 +++ .../backend/vulkan/surface_context_vk.h | 3 ++ .../vulkan/surface_context_vk_unittests.cc | 47 +++++++++++++++++++ .../android/android_surface_vk_impeller.cc | 2 +- 6 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 engine/src/flutter/impeller/renderer/backend/vulkan/surface_context_vk_unittests.cc diff --git a/engine/src/flutter/ci/licenses_golden/excluded_files b/engine/src/flutter/ci/licenses_golden/excluded_files index d632d105ea..4227a1f8b0 100644 --- a/engine/src/flutter/ci/licenses_golden/excluded_files +++ b/engine/src/flutter/ci/licenses_golden/excluded_files @@ -206,6 +206,7 @@ ../../../flutter/impeller/renderer/backend/vulkan/render_pass_cache_unittests.cc ../../../flutter/impeller/renderer/backend/vulkan/render_pass_vk_unittests.cc ../../../flutter/impeller/renderer/backend/vulkan/resource_manager_vk_unittests.cc +../../../flutter/impeller/renderer/backend/vulkan/surface_context_vk_unittests.cc ../../../flutter/impeller/renderer/backend/vulkan/swapchain/README.md ../../../flutter/impeller/renderer/backend/vulkan/swapchain/khr/README.md ../../../flutter/impeller/renderer/backend/vulkan/test diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/BUILD.gn b/engine/src/flutter/impeller/renderer/backend/vulkan/BUILD.gn index cc800da0bc..2f31d4f80e 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/BUILD.gn +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/BUILD.gn @@ -22,6 +22,7 @@ impeller_component("vulkan_unittests") { "render_pass_cache_unittests.cc", "render_pass_vk_unittests.cc", "resource_manager_vk_unittests.cc", + "surface_context_vk_unittests.cc", "test/gpu_tracer_unittests.cc", "test/mock_vulkan.cc", "test/mock_vulkan.h", diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/surface_context_vk.cc b/engine/src/flutter/impeller/renderer/backend/vulkan/surface_context_vk.cc index a96e5faf34..33cbc58e85 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/surface_context_vk.cc +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/surface_context_vk.cc @@ -72,6 +72,13 @@ bool SurfaceContextVK::SetWindowSurface(vk::UniqueSurfaceKHR surface, return SetSwapchain(SwapchainVK::Create(parent_, std::move(surface), size)); } +void SurfaceContextVK::TeardownSwapchain() { + // When background the application, tear down the swapchain to release memory + // from the images. When returning to the foreground, SetWindowSurface will be + // called which will re-create the swapchain. + swapchain_.reset(); +} + bool SurfaceContextVK::SetSwapchain(std::shared_ptr swapchain) { if (!swapchain || !swapchain->IsValid()) { VALIDATION_LOG << "Invalid swapchain."; diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/surface_context_vk.h b/engine/src/flutter/impeller/renderer/backend/vulkan/surface_context_vk.h index c629e65a53..9f0b08855d 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/surface_context_vk.h +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/surface_context_vk.h @@ -96,6 +96,9 @@ class SurfaceContextVK : public Context, /// recreated on the next frame. void UpdateSurfaceSize(const ISize& size) const; + /// @brief Can be called when the surface is destroyed to reduce memory usage. + void TeardownSwapchain(); + // |Context| void InitializeCommonlyUsedShadersIfNeeded() const override; diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/surface_context_vk_unittests.cc b/engine/src/flutter/impeller/renderer/backend/vulkan/surface_context_vk_unittests.cc new file mode 100644 index 0000000000..5cb2c7e83d --- /dev/null +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/surface_context_vk_unittests.cc @@ -0,0 +1,47 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include +#include + +#include "gtest/gtest.h" +#include "impeller/renderer/backend/vulkan/surface_context_vk.h" +#include "impeller/renderer/backend/vulkan/test/mock_vulkan.h" +#include "impeller/renderer/surface.h" + +namespace impeller { +namespace testing { + +namespace { +vk::UniqueSurfaceKHR CreateSurface(const ContextVK& context) { +#if FML_OS_DARWIN + impeller::vk::MetalSurfaceCreateInfoEXT createInfo = {}; + auto [result, surface] = + context.GetInstance().createMetalSurfaceEXTUnique(createInfo); + FML_DCHECK(result == vk::Result::eSuccess); + return std::move(surface); +#else + return {}; +#endif // FML_OS_DARWIN +} +} // namespace + +TEST(SurfaceContextVK, TearsDownSwapchain) { + SetSwapchainImageSize({100, 100}); + std::shared_ptr context = MockVulkanContextBuilder().Build(); + + vk::UniqueSurfaceKHR surface = CreateSurface(*context); + SurfaceContextVK surface_context(context); + + EXPECT_TRUE(surface_context.SetWindowSurface(std::move(surface), {100, 100})); + EXPECT_NE(surface_context.AcquireNextSurface(), nullptr); + + surface_context.TeardownSwapchain(); + + EXPECT_EQ(surface_context.AcquireNextSurface(), nullptr); +} + +} // namespace testing +} // namespace impeller diff --git a/engine/src/flutter/shell/platform/android/android_surface_vk_impeller.cc b/engine/src/flutter/shell/platform/android/android_surface_vk_impeller.cc index 2ecea59723..008724c72b 100644 --- a/engine/src/flutter/shell/platform/android/android_surface_vk_impeller.cc +++ b/engine/src/flutter/shell/platform/android/android_surface_vk_impeller.cc @@ -37,7 +37,7 @@ bool AndroidSurfaceVKImpeller::IsValid() const { } void AndroidSurfaceVKImpeller::TeardownOnScreenContext() { - // Nothing to do. + surface_context_vk_->TeardownSwapchain(); } std::unique_ptr AndroidSurfaceVKImpeller::CreateGPUSurface(