[Impeller] Delete command pools held by the CommandPoolRecyclerVK after decoding an image on the IO thread (flutter/engine#55398)

In the Vulkan backend, the CommandPoolRecyclerVK stores command pools that can be reused while rendering a frame.  For each frame, SurfaceContextVK::AcquireNextSurface calls CommandPoolRecyclerVK::Dispose to clear the cached pools.

However, the CommandPoolRecyclerVK's cache of pools is placed in thread-local storage.  So command pools used on the IO thread will not be cleaned up if frame rendering calls Dispose on the raster thread.

This PR adds a Context API that the IO thread can call after using a command pool in order to ensure that cached resources are disposed.

Fixes https://github.com/flutter/flutter/issues/155519
This commit is contained in:
Jason Simmons 2024-09-24 15:16:37 -07:00 committed by GitHub
parent d8edec8449
commit d333cf4224
8 changed files with 46 additions and 0 deletions

View File

@ -608,6 +608,10 @@ void ContextVK::InitializeCommonlyUsedShadersIfNeeded() const {
auto pass = builder.Build(GetDevice());
}
void ContextVK::DisposeThreadLocalCachedResources() {
command_pool_recycler_->Dispose();
}
const std::shared_ptr<YUVConversionLibraryVK>&
ContextVK::GetYUVConversionLibrary() const {
return yuv_conversion_library_;

View File

@ -167,8 +167,12 @@ class ContextVK final : public Context,
void RecordFrameEndTime() const;
// |Context|
void InitializeCommonlyUsedShadersIfNeeded() const override;
// |Context|
void DisposeThreadLocalCachedResources() override;
/// @brief Whether the Android Surface control based swapchain should be
/// disabled, even if the device is capable of supporting it.
bool GetShouldDisableSurfaceControlSwapchain() const;

View File

@ -72,6 +72,25 @@ TEST(ContextVKTest, DeletesCommandPoolsOnAllThreads) {
ASSERT_FALSE(weak_pool_thread.lock());
}
TEST(ContextVKTest, ThreadLocalCleanupDeletesCommandPool) {
std::shared_ptr<ContextVK> context = MockVulkanContextBuilder().Build();
fml::AutoResetWaitableEvent latch1, latch2;
std::weak_ptr<CommandPoolVK> weak_pool;
std::thread thread([&]() {
weak_pool = context->GetCommandPoolRecycler()->Get();
context->DisposeThreadLocalCachedResources();
latch1.Signal();
latch2.Wait();
});
latch1.Wait();
ASSERT_FALSE(weak_pool.lock());
latch2.Signal();
thread.join();
}
TEST(ContextVKTest, DeletePipelineAfterContext) {
std::shared_ptr<Pipeline<PipelineDescriptor>> pipeline;
std::shared_ptr<std::vector<std::string>> functions;

View File

@ -103,6 +103,10 @@ void SurfaceContextVK::InitializeCommonlyUsedShadersIfNeeded() const {
parent_->InitializeCommonlyUsedShadersIfNeeded();
}
void SurfaceContextVK::DisposeThreadLocalCachedResources() {
parent_->DisposeThreadLocalCachedResources();
}
const std::shared_ptr<ContextVK>& SurfaceContextVK::GetParent() const {
return parent_;
}

View File

@ -80,8 +80,12 @@ class SurfaceContextVK : public Context,
/// recreated on the next frame.
void UpdateSurfaceSize(const ISize& size) const;
// |Context|
void InitializeCommonlyUsedShadersIfNeeded() const override;
// |Context|
void DisposeThreadLocalCachedResources() override;
const vk::Device& GetDevice() const;
const std::shared_ptr<ContextVK>& GetParent() const;

View File

@ -196,6 +196,13 @@ class Context {
/// shader variants, as well as forcing driver initialization.
virtual void InitializeCommonlyUsedShadersIfNeeded() const {}
/// Dispose resources that are cached on behalf of the current thread.
///
/// Some backends such as Vulkan may cache resources that can be reused while
/// executing a rendering operation. This API can be called after the
/// operation completes in order to clear the cache.
virtual void DisposeThreadLocalCachedResources() {}
protected:
Context();

View File

@ -382,6 +382,8 @@ ImageDecoderImpeller::UnsafeUploadTextureToPrivate(
return std::make_pair(nullptr, decode_error);
}
context->DisposeThreadLocalCachedResources();
return std::make_pair(
impeller::DlImageImpeller::Make(std::move(result_texture)),
std::string());

View File

@ -188,6 +188,8 @@ void ImageEncodingImpeller::ConvertDlImageToSkImage(
.ok()) {
FML_LOG(ERROR) << "Failed to submit commands.";
}
impeller_context->DisposeThreadLocalCachedResources();
}
void ImageEncodingImpeller::ConvertImageToRaster(