diff --git a/impeller/renderer/backend/vulkan/context_vk.cc b/impeller/renderer/backend/vulkan/context_vk.cc index 2dfcb236fbbee..b67f8d1bac052 100644 --- a/impeller/renderer/backend/vulkan/context_vk.cc +++ b/impeller/renderer/backend/vulkan/context_vk.cc @@ -608,6 +608,10 @@ void ContextVK::InitializeCommonlyUsedShadersIfNeeded() const { auto pass = builder.Build(GetDevice()); } +void ContextVK::DisposeThreadLocalCachedResources() { + command_pool_recycler_->Dispose(); +} + const std::shared_ptr& ContextVK::GetYUVConversionLibrary() const { return yuv_conversion_library_; diff --git a/impeller/renderer/backend/vulkan/context_vk.h b/impeller/renderer/backend/vulkan/context_vk.h index 3d9b03e7020a7..bfdd8bdddcce4 100644 --- a/impeller/renderer/backend/vulkan/context_vk.h +++ b/impeller/renderer/backend/vulkan/context_vk.h @@ -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; diff --git a/impeller/renderer/backend/vulkan/context_vk_unittests.cc b/impeller/renderer/backend/vulkan/context_vk_unittests.cc index 1cfe8d15ddd86..629f88daa20ce 100644 --- a/impeller/renderer/backend/vulkan/context_vk_unittests.cc +++ b/impeller/renderer/backend/vulkan/context_vk_unittests.cc @@ -72,6 +72,25 @@ TEST(ContextVKTest, DeletesCommandPoolsOnAllThreads) { ASSERT_FALSE(weak_pool_thread.lock()); } +TEST(ContextVKTest, ThreadLocalCleanupDeletesCommandPool) { + std::shared_ptr context = MockVulkanContextBuilder().Build(); + + fml::AutoResetWaitableEvent latch1, latch2; + std::weak_ptr 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; std::shared_ptr> functions; diff --git a/impeller/renderer/backend/vulkan/surface_context_vk.cc b/impeller/renderer/backend/vulkan/surface_context_vk.cc index 9feabd38b843a..912c74b03c056 100644 --- a/impeller/renderer/backend/vulkan/surface_context_vk.cc +++ b/impeller/renderer/backend/vulkan/surface_context_vk.cc @@ -103,6 +103,10 @@ void SurfaceContextVK::InitializeCommonlyUsedShadersIfNeeded() const { parent_->InitializeCommonlyUsedShadersIfNeeded(); } +void SurfaceContextVK::DisposeThreadLocalCachedResources() { + parent_->DisposeThreadLocalCachedResources(); +} + const std::shared_ptr& SurfaceContextVK::GetParent() const { return parent_; } diff --git a/impeller/renderer/backend/vulkan/surface_context_vk.h b/impeller/renderer/backend/vulkan/surface_context_vk.h index e80d12e795045..7389d95c286bc 100644 --- a/impeller/renderer/backend/vulkan/surface_context_vk.h +++ b/impeller/renderer/backend/vulkan/surface_context_vk.h @@ -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& GetParent() const; diff --git a/impeller/renderer/context.h b/impeller/renderer/context.h index 7c05ce92264c3..8345e7446be5e 100644 --- a/impeller/renderer/context.h +++ b/impeller/renderer/context.h @@ -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(); diff --git a/lib/ui/painting/image_decoder_impeller.cc b/lib/ui/painting/image_decoder_impeller.cc index 565e034545a74..4fe7f283e154b 100644 --- a/lib/ui/painting/image_decoder_impeller.cc +++ b/lib/ui/painting/image_decoder_impeller.cc @@ -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()); diff --git a/lib/ui/painting/image_encoding_impeller.cc b/lib/ui/painting/image_encoding_impeller.cc index 1dddb456fac58..a504de0a22e0c 100644 --- a/lib/ui/painting/image_encoding_impeller.cc +++ b/lib/ui/painting/image_encoding_impeller.cc @@ -188,6 +188,8 @@ void ImageEncodingImpeller::ConvertDlImageToSkImage( .ok()) { FML_LOG(ERROR) << "Failed to submit commands."; } + + impeller_context->DisposeThreadLocalCachedResources(); } void ImageEncodingImpeller::ConvertImageToRaster(