From ffc7ced2a0892e6f0f1b2b617b3c6fe7e78a3c37 Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Mon, 6 Jan 2025 12:26:08 -0800 Subject: [PATCH] [Impeller] protect onscreen cmd buffer with render ready semaphore. (#161140) Ensures that the onscreen command buffer is blocked via the render ready semaphore, instead of just the layout transition. I can confirm this fixes the rendering artifacts on the Samsung a50 - which I suspect are the same as the issues these other devices are having. Essentially, without the semaphore protecting the onscreen pass we can end up writing to the color attachment while it is still being read. * https://github.com/flutter/flutter/issues/160522 * https://github.com/flutter/flutter/issues/160370 --- .../impeller/display_list/aiks_playground.cc | 6 ++-- .../flutter/impeller/display_list/canvas.cc | 30 +++++++++++-------- .../flutter/impeller/display_list/canvas.h | 6 +++- .../impeller/display_list/canvas_unittests.cc | 8 +++-- .../impeller/display_list/dl_dispatcher.cc | 15 ++++++---- .../impeller/display_list/dl_dispatcher.h | 11 +++++-- .../impeller/display_list/dl_playground.cc | 5 ++-- .../impeller/entity/inline_pass_context.cc | 10 +++++-- .../impeller/entity/inline_pass_context.h | 2 +- .../renderer/backend/vulkan/context_vk.cc | 8 +++++ .../renderer/backend/vulkan/context_vk.h | 4 +++ .../backend/vulkan/render_pass_builder_vk.cc | 1 - .../renderer/backend/vulkan/render_pass_vk.cc | 27 +++++++++-------- .../backend/vulkan/surface_context_vk.cc | 6 ++++ .../backend/vulkan/surface_context_vk.h | 3 ++ .../swapchain/ahb/ahb_swapchain_impl_vk.cc | 8 ++++- .../swapchain/ahb/ahb_swapchain_impl_vk.h | 3 ++ .../vulkan/swapchain/ahb/ahb_swapchain_vk.cc | 6 ++++ .../vulkan/swapchain/ahb/ahb_swapchain_vk.h | 4 +++ .../swapchain/khr/khr_swapchain_impl_vk.cc | 17 +++++++++-- .../swapchain/khr/khr_swapchain_impl_vk.h | 3 +- .../vulkan/swapchain/khr/khr_swapchain_vk.cc | 5 ++++ .../vulkan/swapchain/khr/khr_swapchain_vk.h | 4 +++ .../backend/vulkan/swapchain/swapchain_vk.h | 4 +++ .../src/flutter/impeller/renderer/context.cc | 4 +++ .../src/flutter/impeller/renderer/context.h | 3 ++ .../impeller/toolkit/interop/surface.cc | 4 +-- .../shell/gpu/gpu_surface_gl_impeller.cc | 10 +++---- .../shell/gpu/gpu_surface_metal_impeller.mm | 20 ++++++------- .../shell/gpu/gpu_surface_vulkan_impeller.cc | 20 ++++++------- .../embedder/embedder_external_view.cc | 11 +++---- 31 files changed, 186 insertions(+), 82 deletions(-) diff --git a/engine/src/flutter/impeller/display_list/aiks_playground.cc b/engine/src/flutter/impeller/display_list/aiks_playground.cc index 6b2ac34a9f..4bd350c0b4 100644 --- a/engine/src/flutter/impeller/display_list/aiks_playground.cc +++ b/engine/src/flutter/impeller/display_list/aiks_playground.cc @@ -50,14 +50,14 @@ bool AiksPlayground::OpenPlaygroundHere( return Playground::OpenPlaygroundHere( [&renderer, &callback](RenderTarget& render_target) -> bool { - return RenderToOnscreen( + return RenderToTarget( renderer.GetContentContext(), // render_target, // callback(), // SkIRect::MakeWH(render_target.GetRenderTargetSize().width, render_target.GetRenderTargetSize().height), // - /*reset_host_buffer=*/true // - ); + /*reset_host_buffer=*/true, // + /*is_onscreen=*/false); }); } diff --git a/engine/src/flutter/impeller/display_list/canvas.cc b/engine/src/flutter/impeller/display_list/canvas.cc index 04629516ec..0f29cab864 100644 --- a/engine/src/flutter/impeller/display_list/canvas.cc +++ b/engine/src/flutter/impeller/display_list/canvas.cc @@ -43,6 +43,7 @@ #include "impeller/geometry/color.h" #include "impeller/geometry/constants.h" #include "impeller/geometry/path_builder.h" +#include "impeller/renderer/command_buffer.h" namespace impeller { @@ -162,9 +163,11 @@ static std::unique_ptr CreateRenderTarget( Canvas::Canvas(ContentContext& renderer, const RenderTarget& render_target, + bool is_onscreen, bool requires_readback) : renderer_(renderer), render_target_(render_target), + is_onscreen_(is_onscreen), requires_readback_(requires_readback), clip_coverage_stack_(EntityPassClipStack( Rect::MakeSize(render_target.GetRenderTargetSize()))) { @@ -174,10 +177,12 @@ Canvas::Canvas(ContentContext& renderer, Canvas::Canvas(ContentContext& renderer, const RenderTarget& render_target, + bool is_onscreen, bool requires_readback, Rect cull_rect) : renderer_(renderer), render_target_(render_target), + is_onscreen_(is_onscreen), requires_readback_(requires_readback), clip_coverage_stack_(EntityPassClipStack( Rect::MakeSize(render_target.GetRenderTargetSize()))) { @@ -187,10 +192,12 @@ Canvas::Canvas(ContentContext& renderer, Canvas::Canvas(ContentContext& renderer, const RenderTarget& render_target, + bool is_onscreen, bool requires_readback, IRect cull_rect) : renderer_(renderer), render_target_(render_target), + is_onscreen_(is_onscreen), requires_readback_(requires_readback), clip_coverage_stack_(EntityPassClipStack( Rect::MakeSize(render_target.GetRenderTargetSize()))) { @@ -1658,7 +1665,7 @@ std::shared_ptr Canvas::FlipBackdrop(Point global_pass_position, return input_texture; } -bool Canvas::BlitToOnscreen() { +bool Canvas::BlitToOnscreen(bool is_onscreen) { auto command_buffer = renderer_.GetContext()->CreateCommandBuffer(); command_buffer->SetLabel("EntityPass Root Command Buffer"); auto offscreen_target = render_passes_.back() @@ -1675,10 +1682,6 @@ bool Canvas::BlitToOnscreen() { VALIDATION_LOG << "Failed to encode root pass blit command."; return false; } - if (!renderer_.GetContext()->EnqueueCommandBuffer( - std::move(command_buffer))) { - return false; - } } else { auto render_pass = command_buffer->CreateRenderPass(render_target_); render_pass->SetLabel("EntityPass Root Render Pass"); @@ -1704,25 +1707,28 @@ bool Canvas::BlitToOnscreen() { VALIDATION_LOG << "Failed to encode root pass command buffer."; return false; } - if (!renderer_.GetContext()->EnqueueCommandBuffer( - std::move(command_buffer))) { - return false; - } } - return true; + + if (is_onscreen) { + return renderer_.GetContext()->SubmitOnscreen(std::move(command_buffer)); + } else { + return renderer_.GetContext()->EnqueueCommandBuffer( + std::move(command_buffer)); + } } void Canvas::EndReplay() { FML_DCHECK(render_passes_.size() == 1u); render_passes_.back().inline_pass_context->GetRenderPass(); - render_passes_.back().inline_pass_context->EndPass(); + render_passes_.back().inline_pass_context->EndPass( + /*is_onscreen=*/!requires_readback_ && is_onscreen_); backdrop_data_.clear(); // If requires_readback_ was true, then we rendered to an offscreen texture // instead of to the onscreen provided in the render target. Now we need to // draw or blit the offscreen back to the onscreen. if (requires_readback_) { - BlitToOnscreen(); + BlitToOnscreen(/*is_onscreen_=*/is_onscreen_); } if (!renderer_.GetContext()->FlushCommandBuffers()) { diff --git a/engine/src/flutter/impeller/display_list/canvas.h b/engine/src/flutter/impeller/display_list/canvas.h index d92c6d79a8..0b5e76331e 100644 --- a/engine/src/flutter/impeller/display_list/canvas.h +++ b/engine/src/flutter/impeller/display_list/canvas.h @@ -123,15 +123,18 @@ class Canvas { Canvas(ContentContext& renderer, const RenderTarget& render_target, + bool is_onscreen, bool requires_readback); explicit Canvas(ContentContext& renderer, const RenderTarget& render_target, + bool is_onscreen, bool requires_readback, Rect cull_rect); explicit Canvas(ContentContext& renderer, const RenderTarget& render_target, + bool is_onscreen, bool requires_readback, IRect cull_rect); @@ -251,6 +254,7 @@ class Canvas { private: ContentContext& renderer_; RenderTarget render_target_; + const bool is_onscreen_; bool requires_readback_; EntityPassClipStack clip_coverage_stack_; @@ -318,7 +322,7 @@ class Canvas { bool should_remove_texture = false, bool should_use_onscreen = false); - bool BlitToOnscreen(); + bool BlitToOnscreen(bool is_onscreen = false); size_t GetClipHeight() const; diff --git a/engine/src/flutter/impeller/display_list/canvas_unittests.cc b/engine/src/flutter/impeller/display_list/canvas_unittests.cc index 09dad0565d..ee0f8b2e93 100644 --- a/engine/src/flutter/impeller/display_list/canvas_unittests.cc +++ b/engine/src/flutter/impeller/display_list/canvas_unittests.cc @@ -56,10 +56,12 @@ std::unique_ptr CreateTestCanvas( render_target.SetColorAttachment(color0, 0); if (cull_rect.has_value()) { - return std::make_unique(context, render_target, requires_readback, - cull_rect.value()); + return std::make_unique( + context, render_target, /*is_onscreen=*/false, + /*requires_readback=*/requires_readback, cull_rect.value()); } - return std::make_unique(context, render_target, requires_readback); + return std::make_unique(context, render_target, /*is_onscreen=*/false, + /*requires_readback=*/requires_readback); } TEST_P(AiksTest, TransformMultipliesCorrectly) { diff --git a/engine/src/flutter/impeller/display_list/dl_dispatcher.cc b/engine/src/flutter/impeller/display_list/dl_dispatcher.cc index 2059ff11b1..487d2a8551 100644 --- a/engine/src/flutter/impeller/display_list/dl_dispatcher.cc +++ b/engine/src/flutter/impeller/display_list/dl_dispatcher.cc @@ -947,11 +947,13 @@ static bool RequiresReadbackForBlends( CanvasDlDispatcher::CanvasDlDispatcher(ContentContext& renderer, RenderTarget& render_target, + bool is_onscreen, bool has_root_backdrop_filter, flutter::DlBlendMode max_root_blend_mode, IRect cull_rect) : canvas_(renderer, render_target, + is_onscreen, has_root_backdrop_filter || RequiresReadbackForBlends(renderer, max_root_blend_mode), cull_rect), @@ -1270,6 +1272,7 @@ std::shared_ptr DisplayListToTexture( impeller::CanvasDlDispatcher impeller_dispatcher( context.GetContentContext(), // target, // + /*is_onscreen=*/false, // display_list->root_has_backdrop_filter(), // display_list->max_root_blend_mode(), // impeller::IRect::MakeSize(size) // @@ -1288,11 +1291,12 @@ std::shared_ptr DisplayListToTexture( return target.GetRenderTargetTexture(); } -bool RenderToOnscreen(ContentContext& context, - RenderTarget render_target, - const sk_sp& display_list, - SkIRect cull_rect, - bool reset_host_buffer) { +bool RenderToTarget(ContentContext& context, + RenderTarget render_target, + const sk_sp& display_list, + SkIRect cull_rect, + bool reset_host_buffer, + bool is_onscreen) { Rect ip_cull_rect = Rect::MakeLTRB(cull_rect.left(), cull_rect.top(), cull_rect.right(), cull_rect.bottom()); FirstPassDispatcher collector(context, impeller::Matrix(), ip_cull_rect); @@ -1301,6 +1305,7 @@ bool RenderToOnscreen(ContentContext& context, impeller::CanvasDlDispatcher impeller_dispatcher( context, // render_target, // + /*is_onscreen=*/is_onscreen, // display_list->root_has_backdrop_filter(), // display_list->max_root_blend_mode(), // IRect::RoundOut(ip_cull_rect) // diff --git a/engine/src/flutter/impeller/display_list/dl_dispatcher.h b/engine/src/flutter/impeller/display_list/dl_dispatcher.h index ba43f1c15c..8d69eab149 100644 --- a/engine/src/flutter/impeller/display_list/dl_dispatcher.h +++ b/engine/src/flutter/impeller/display_list/dl_dispatcher.h @@ -254,6 +254,7 @@ class CanvasDlDispatcher : public DlDispatcherBase { public: CanvasDlDispatcher(ContentContext& renderer, RenderTarget& render_target, + bool is_onscreen, bool has_root_backdrop_filter, flutter::DlBlendMode max_root_blend_mode, IRect cull_rect); @@ -390,11 +391,15 @@ std::shared_ptr DisplayListToTexture( bool reset_host_buffer = true, bool generate_mips = false); -/// Render the provided display list to the render target. -bool RenderToOnscreen(ContentContext& context, RenderTarget render_target, +/// @brief Render the provided display list to the render target. +/// +/// If [is_onscreen] is true, then the onscreen command buffer will be +/// submitted via Context::SubmitOnscreen. +bool RenderToTarget(ContentContext& context, RenderTarget render_target, const sk_sp& display_list, SkIRect cull_rect, - bool reset_host_buffer); + bool reset_host_buffer, + bool is_onscreen = true); } // namespace impeller diff --git a/engine/src/flutter/impeller/display_list/dl_playground.cc b/engine/src/flutter/impeller/display_list/dl_playground.cc index 9a0561230d..587f270f8e 100644 --- a/engine/src/flutter/impeller/display_list/dl_playground.cc +++ b/engine/src/flutter/impeller/display_list/dl_playground.cc @@ -45,13 +45,14 @@ bool DlPlayground::OpenPlaygroundHere(DisplayListPlaygroundCallback callback) { wireframe = !wireframe; context.GetContentContext().SetWireframe(wireframe); } - return RenderToOnscreen( + return RenderToTarget( context.GetContentContext(), // render_target, // callback(), // SkIRect::MakeWH(render_target.GetRenderTargetSize().width, render_target.GetRenderTargetSize().height), // - /*reset_host_buffer=*/true // + /*reset_host_buffer=*/true, // + /*is_onscreen=*/false // ); }); } diff --git a/engine/src/flutter/impeller/entity/inline_pass_context.cc b/engine/src/flutter/impeller/entity/inline_pass_context.cc index 82d94a614e..e8ea9487cc 100644 --- a/engine/src/flutter/impeller/entity/inline_pass_context.cc +++ b/engine/src/flutter/impeller/entity/inline_pass_context.cc @@ -40,7 +40,7 @@ std::shared_ptr InlinePassContext::GetTexture() { return pass_target_.GetRenderTarget().GetRenderTargetTexture(); } -bool InlinePassContext::EndPass() { +bool InlinePassContext::EndPass(bool is_onscreen) { if (!IsActive()) { return true; } @@ -63,8 +63,12 @@ bool InlinePassContext::EndPass() { } pass_ = nullptr; - return renderer_.GetContext()->EnqueueCommandBuffer( - std::move(command_buffer_)); + if (is_onscreen) { + return renderer_.GetContext()->SubmitOnscreen(std::move(command_buffer_)); + } else { + return renderer_.GetContext()->EnqueueCommandBuffer( + std::move(command_buffer_)); + } } EntityPassTarget& InlinePassContext::GetPassTarget() const { diff --git a/engine/src/flutter/impeller/entity/inline_pass_context.h b/engine/src/flutter/impeller/entity/inline_pass_context.h index d475313057..30acc888a6 100644 --- a/engine/src/flutter/impeller/entity/inline_pass_context.h +++ b/engine/src/flutter/impeller/entity/inline_pass_context.h @@ -27,7 +27,7 @@ class InlinePassContext { std::shared_ptr GetTexture(); - bool EndPass(); + bool EndPass(bool is_onscreen = false); EntityPassTarget& GetPassTarget() const; diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/context_vk.cc b/engine/src/flutter/impeller/renderer/backend/vulkan/context_vk.cc index 027a18726c..e2c42562f0 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/context_vk.cc +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/context_vk.cc @@ -657,6 +657,10 @@ bool ContextVK::EnqueueCommandBuffer( } bool ContextVK::FlushCommandBuffers() { + if (pending_command_buffers_.empty()) { + return true; + } + if (should_batch_cmd_buffers_) { bool result = GetCommandQueue()->Submit(pending_command_buffers_).ok(); pending_command_buffers_.clear(); @@ -733,4 +737,8 @@ RuntimeStageBackend ContextVK::GetRuntimeStageBackend() const { return RuntimeStageBackend::kVulkan; } +bool ContextVK::SubmitOnscreen(std::shared_ptr cmd_buffer) { + return EnqueueCommandBuffer(std::move(cmd_buffer)); +} + } // namespace impeller diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/context_vk.h b/engine/src/flutter/impeller/renderer/backend/vulkan/context_vk.h index 08ed6569b0..fa3282e9d1 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/context_vk.h +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/context_vk.h @@ -131,6 +131,10 @@ class ContextVK final : public Context, // |Context| const std::shared_ptr& GetCapabilities() const override; + // |Context| + virtual bool SubmitOnscreen( + std::shared_ptr cmd_buffer) override; + const std::shared_ptr& GetYUVConversionLibrary() const; diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/render_pass_builder_vk.cc b/engine/src/flutter/impeller/renderer/backend/vulkan/render_pass_builder_vk.cc index 8dfa1ed472..0b6837a9ac 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/render_pass_builder_vk.cc +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/render_pass_builder_vk.cc @@ -6,7 +6,6 @@ #include "impeller/core/formats.h" #include "impeller/renderer/backend/vulkan/formats_vk.h" -#include "vulkan/vulkan_enums.hpp" namespace impeller { diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/render_pass_vk.cc b/engine/src/flutter/impeller/renderer/backend/vulkan/render_pass_vk.cc index 43a68f7dfc..f946cf3571 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/render_pass_vk.cc +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/render_pass_vk.cc @@ -14,7 +14,6 @@ #include "impeller/core/formats.h" #include "impeller/core/texture.h" #include "impeller/core/vertex_buffer.h" -#include "impeller/renderer/backend/vulkan/barrier_vk.h" #include "impeller/renderer/backend/vulkan/command_buffer_vk.h" #include "impeller/renderer/backend/vulkan/context_vk.h" #include "impeller/renderer/backend/vulkan/device_buffer_vk.h" @@ -82,17 +81,19 @@ SharedHandleVK RenderPassVK::CreateVKRenderPass( const std::shared_ptr& command_buffer) const { RenderPassBuilderVK builder; - render_target_.IterateAllColorAttachments( - [&](size_t bind_point, const ColorAttachment& attachment) -> bool { - builder.SetColorAttachment( - bind_point, // - attachment.texture->GetTextureDescriptor().format, // - attachment.texture->GetTextureDescriptor().sample_count, // - attachment.load_action, // - attachment.store_action // - ); - return true; - }); + render_target_.IterateAllColorAttachments([&](size_t bind_point, + const ColorAttachment& + attachment) -> bool { + builder.SetColorAttachment( + bind_point, // + attachment.texture->GetTextureDescriptor().format, // + attachment.texture->GetTextureDescriptor().sample_count, // + attachment.load_action, // + attachment.store_action, // + /*current_layout=*/TextureVK::Cast(*attachment.texture).GetLayout() // + ); + return true; + }); if (auto depth = render_target_.GetDepthAttachment(); depth.has_value()) { builder.SetDepthStencilAttachment( @@ -179,6 +180,8 @@ RenderPassVK::RenderPassVK(const std::shared_ptr& context, if (resolve_image_vk_) { TextureVK::Cast(*resolve_image_vk_).SetCachedFramebuffer(framebuffer); TextureVK::Cast(*resolve_image_vk_).SetCachedRenderPass(render_pass_); + TextureVK::Cast(*resolve_image_vk_) + .SetLayoutWithoutEncoding(vk::ImageLayout::eGeneral); } std::array clears; 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 0783762666..a96e5faf34 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 @@ -129,6 +129,12 @@ bool SurfaceContextVK::FlushCommandBuffers() { return parent_->FlushCommandBuffers(); } +bool SurfaceContextVK::SubmitOnscreen( + std::shared_ptr cmd_buffer) { + swapchain_->AddFinalCommandBuffer(std::move(cmd_buffer)); + return true; +} + RuntimeStageBackend SurfaceContextVK::GetRuntimeStageBackend() const { return parent_->GetRuntimeStageBackend(); } 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 488dc19f90..c629e65a53 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 @@ -73,6 +73,9 @@ class SurfaceContextVK : public Context, // |Context| RuntimeStageBackend GetRuntimeStageBackend() const override; + // |Context| + bool SubmitOnscreen(std::shared_ptr cmd_buffer) override; + // |Context| void Shutdown() override; diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_impl_vk.cc b/engine/src/flutter/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_impl_vk.cc index f3b84056e4..721f08da00 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_impl_vk.cc +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_impl_vk.cc @@ -192,6 +192,12 @@ bool AHBSwapchainImplVK::Present( }); } +void AHBSwapchainImplVK::AddFinalCommandBuffer( + std::shared_ptr cmd_buffer) { + FML_DCHECK(!pending_cmd_buffer_); + pending_cmd_buffer_ = std::move(cmd_buffer); +} + std::shared_ptr AHBSwapchainImplVK::SubmitSignalForPresentReady( const std::shared_ptr& texture) const { @@ -204,7 +210,7 @@ AHBSwapchainImplVK::SubmitSignalForPresentReady( return nullptr; } - auto command_buffer = context->CreateCommandBuffer(); + auto command_buffer = pending_cmd_buffer_; if (!command_buffer) { return nullptr; } diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_impl_vk.h b/engine/src/flutter/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_impl_vk.h index 1c546fc544..f44e8b15f8 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_impl_vk.h +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_impl_vk.h @@ -89,6 +89,8 @@ class AHBSwapchainImplVK final /// std::unique_ptr AcquireNextDrawable(); + void AddFinalCommandBuffer(std::shared_ptr cmd_buffer); + private: using AutoSemaSignaler = std::shared_ptr; @@ -102,6 +104,7 @@ class AHBSwapchainImplVK final std::shared_ptr currently_displayed_texture_ IPLR_GUARDED_BY(currently_displayed_texture_mutex_); std::shared_ptr pending_presents_; + std::shared_ptr pending_cmd_buffer_; bool is_valid_ = false; explicit AHBSwapchainImplVK( diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_vk.cc b/engine/src/flutter/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_vk.cc index ddfdece798..892f73cd0c 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_vk.cc +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_vk.cc @@ -66,6 +66,12 @@ vk::Format AHBSwapchainVK::GetSurfaceFormat() const { : vk::Format::eUndefined; } +// |SwapchainVK| +void AHBSwapchainVK::AddFinalCommandBuffer( + std::shared_ptr cmd_buffer) const { + return impl_->AddFinalCommandBuffer(cmd_buffer); +} + // |SwapchainVK| void AHBSwapchainVK::UpdateSurfaceSize(const ISize& size) { if (impl_ && impl_->GetSize() == size) { diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_vk.h b/engine/src/flutter/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_vk.h index ddbcd13a52..7b0dcb42fd 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_vk.h +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_vk.h @@ -46,6 +46,10 @@ class AHBSwapchainVK final : public SwapchainVK { // |SwapchainVK| void UpdateSurfaceSize(const ISize& size) override; + // |SwapchainVK| + void AddFinalCommandBuffer( + std::shared_ptr cmd_buffer) const override; + private: friend class SwapchainVK; diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_impl_vk.cc b/engine/src/flutter/impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_impl_vk.cc index aeebeb38e8..ee6ba61c72 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_impl_vk.cc +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_impl_vk.cc @@ -13,11 +13,12 @@ #include "impeller/renderer/backend/vulkan/gpu_tracer_vk.h" #include "impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_image_vk.h" #include "impeller/renderer/backend/vulkan/swapchain/surface_vk.h" +#include "impeller/renderer/backend/vulkan/texture_vk.h" #include "impeller/renderer/context.h" namespace impeller { -static constexpr size_t kMaxFramesInFlight = 3u; +static constexpr size_t kMaxFramesInFlight = 2u; struct KHRFrameSynchronizerVK { vk::UniqueFence acquire; @@ -25,6 +26,8 @@ struct KHRFrameSynchronizerVK { vk::UniqueSemaphore present_ready; std::shared_ptr final_cmd_buffer; bool is_valid = false; + // Whether the renderer attached an onscreen command buffer to render to. + bool has_onscreen = false; explicit KHRFrameSynchronizerVK(const vk::Device& device) { auto acquire_res = device.createFenceUnique( @@ -378,6 +381,13 @@ KHRSwapchainImplVK::AcquireResult KHRSwapchainImplVK::AcquireNextDrawable() { )}; } +void KHRSwapchainImplVK::AddFinalCommandBuffer( + std::shared_ptr cmd_buffer) { + const auto& sync = synchronizers_[current_frame_]; + sync->final_cmd_buffer = std::move(cmd_buffer); + sync->has_onscreen = true; +} + bool KHRSwapchainImplVK::Present( const std::shared_ptr& image, uint32_t index) { @@ -393,7 +403,10 @@ bool KHRSwapchainImplVK::Present( //---------------------------------------------------------------------------- /// Transition the image to color-attachment-optimal. /// - sync->final_cmd_buffer = context.CreateCommandBuffer(); + if (!sync->has_onscreen) { + sync->final_cmd_buffer = context.CreateCommandBuffer(); + } + sync->has_onscreen = false; if (!sync->final_cmd_buffer) { return false; } diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_impl_vk.h b/engine/src/flutter/impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_impl_vk.h index 049ab11426..a110f5ed79 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_impl_vk.h +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_impl_vk.h @@ -7,7 +7,6 @@ #include #include -#include #include "impeller/geometry/size.h" #include "impeller/renderer/backend/vulkan/swapchain/swapchain_transients_vk.h" @@ -63,6 +62,8 @@ class KHRSwapchainImplVK final const ISize& GetSize() const; + void AddFinalCommandBuffer(std::shared_ptr cmd_buffer); + private: std::weak_ptr context_; vk::UniqueSurfaceKHR surface_; diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_vk.cc b/engine/src/flutter/impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_vk.cc index 0877e5541e..9a1cc0e32e 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_vk.cc +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_vk.cc @@ -39,6 +39,11 @@ void KHRSwapchainVK::UpdateSurfaceSize(const ISize& size) { size_ = size; } +void KHRSwapchainVK::AddFinalCommandBuffer( + std::shared_ptr cmd_buffer) const { + impl_->AddFinalCommandBuffer(std::move(cmd_buffer)); +} + std::unique_ptr KHRSwapchainVK::AcquireNextDrawable() { if (!IsValid()) { return nullptr; diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_vk.h b/engine/src/flutter/impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_vk.h index 21acdfd47a..8ac8b75a03 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_vk.h +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_vk.h @@ -37,6 +37,10 @@ class KHRSwapchainVK final : public SwapchainVK { // |SwapchainVK| void UpdateSurfaceSize(const ISize& size) override; + // |SwapchainVK| + void AddFinalCommandBuffer( + std::shared_ptr cmd_buffer) const override; + private: friend class SwapchainVK; diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/swapchain/swapchain_vk.h b/engine/src/flutter/impeller/renderer/backend/vulkan/swapchain/swapchain_vk.h index 5de25d1469..5cff164440 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/swapchain/swapchain_vk.h +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/swapchain/swapchain_vk.h @@ -10,6 +10,7 @@ #include "flutter/fml/build_config.h" #include "impeller/geometry/size.h" #include "impeller/renderer/backend/vulkan/vk.h" +#include "impeller/renderer/command_buffer.h" #include "impeller/renderer/context.h" #include "impeller/renderer/surface.h" @@ -52,6 +53,9 @@ class SwapchainVK { virtual vk::Format GetSurfaceFormat() const = 0; + virtual void AddFinalCommandBuffer( + std::shared_ptr cmd_buffer) const = 0; + /// @brief Mark the current swapchain configuration as dirty, forcing it to be /// recreated on the next frame. virtual void UpdateSurfaceSize(const ISize& size) = 0; diff --git a/engine/src/flutter/impeller/renderer/context.cc b/engine/src/flutter/impeller/renderer/context.cc index b7922caf21..a741ce1bb9 100644 --- a/engine/src/flutter/impeller/renderer/context.cc +++ b/engine/src/flutter/impeller/renderer/context.cc @@ -37,4 +37,8 @@ bool Context::AddTrackingFence(const std::shared_ptr& texture) const { return false; } +bool Context::SubmitOnscreen(std::shared_ptr cmd_buffer) { + return EnqueueCommandBuffer(std::move(cmd_buffer)); +} + } // namespace impeller diff --git a/engine/src/flutter/impeller/renderer/context.h b/engine/src/flutter/impeller/renderer/context.h index 71bad51128..8f6cd853dd 100644 --- a/engine/src/flutter/impeller/renderer/context.h +++ b/engine/src/flutter/impeller/renderer/context.h @@ -242,6 +242,9 @@ class Context { /// correct shader types. virtual RuntimeStageBackend GetRuntimeStageBackend() const = 0; + /// @brief Submit the command buffer that renders to the onscreen surface. + virtual bool SubmitOnscreen(std::shared_ptr cmd_buffer); + protected: Context(); diff --git a/engine/src/flutter/impeller/toolkit/interop/surface.cc b/engine/src/flutter/impeller/toolkit/interop/surface.cc index b5e7f72824..3d78e1a949 100644 --- a/engine/src/flutter/impeller/toolkit/interop/surface.cc +++ b/engine/src/flutter/impeller/toolkit/interop/surface.cc @@ -62,8 +62,8 @@ bool Surface::DrawDisplayList(const DisplayList& dl) const { auto skia_cull_rect = SkIRect::MakeWH(cull_rect.GetWidth(), cull_rect.GetHeight()); - auto result = RenderToOnscreen(content_context, render_target, display_list, - skia_cull_rect, /*reset_host_buffer=*/true); + auto result = RenderToTarget(content_context, render_target, display_list, + skia_cull_rect, /*reset_host_buffer=*/true); context_->GetContext()->ResetThreadLocalState(); return result; } diff --git a/engine/src/flutter/shell/gpu/gpu_surface_gl_impeller.cc b/engine/src/flutter/shell/gpu/gpu_surface_gl_impeller.cc index 138709e88d..18938f41ce 100644 --- a/engine/src/flutter/shell/gpu/gpu_surface_gl_impeller.cc +++ b/engine/src/flutter/shell/gpu/gpu_surface_gl_impeller.cc @@ -116,11 +116,11 @@ std::unique_ptr GPUSurfaceGLImpeller::AcquireFrame( auto cull_rect = render_target.GetRenderTargetSize(); SkIRect sk_cull_rect = SkIRect::MakeWH(cull_rect.width, cull_rect.height); - return impeller::RenderToOnscreen(aiks_context->GetContentContext(), // - render_target, // - display_list, // - sk_cull_rect, // - /*reset_host_buffer=*/true // + return impeller::RenderToTarget(aiks_context->GetContentContext(), // + render_target, // + display_list, // + sk_cull_rect, // + /*reset_host_buffer=*/true // ); return true; }; diff --git a/engine/src/flutter/shell/gpu/gpu_surface_metal_impeller.mm b/engine/src/flutter/shell/gpu/gpu_surface_metal_impeller.mm index b35d509f14..e231f7be81 100644 --- a/engine/src/flutter/shell/gpu/gpu_surface_metal_impeller.mm +++ b/engine/src/flutter/shell/gpu/gpu_surface_metal_impeller.mm @@ -169,11 +169,11 @@ std::unique_ptr GPUSurfaceMetalImpeller::AcquireFrameFromCAMetalLa surface->SetFrameBoundary(surface_frame.submit_info().frame_boundary); const bool reset_host_buffer = surface_frame.submit_info().frame_boundary; - auto render_result = impeller::RenderToOnscreen(aiks_context->GetContentContext(), // - surface->GetRenderTarget(), // - display_list, // - sk_cull_rect, // - /*reset_host_buffer=*/reset_host_buffer // + auto render_result = impeller::RenderToTarget(aiks_context->GetContentContext(), // + surface->GetRenderTarget(), // + display_list, // + sk_cull_rect, // + /*reset_host_buffer=*/reset_host_buffer // ); if (!render_result) { return false; @@ -283,11 +283,11 @@ std::unique_ptr GPUSurfaceMetalImpeller::AcquireFrameFromMTLTextur impeller::IRect cull_rect = surface->coverage(); SkIRect sk_cull_rect = SkIRect::MakeWH(cull_rect.GetWidth(), cull_rect.GetHeight()); - auto render_result = impeller::RenderToOnscreen(aiks_context->GetContentContext(), // - surface->GetRenderTarget(), // - display_list, // - sk_cull_rect, // - /*reset_host_buffer=*/true // + auto render_result = impeller::RenderToTarget(aiks_context->GetContentContext(), // + surface->GetRenderTarget(), // + display_list, // + sk_cull_rect, // + /*reset_host_buffer=*/true // ); if (!render_result) { FML_LOG(ERROR) << "Failed to render Impeller frame"; diff --git a/engine/src/flutter/shell/gpu/gpu_surface_vulkan_impeller.cc b/engine/src/flutter/shell/gpu/gpu_surface_vulkan_impeller.cc index c0a3fb7344..114861aace 100644 --- a/engine/src/flutter/shell/gpu/gpu_surface_vulkan_impeller.cc +++ b/engine/src/flutter/shell/gpu/gpu_surface_vulkan_impeller.cc @@ -115,11 +115,11 @@ std::unique_ptr GPUSurfaceVulkanImpeller::AcquireFrame( } SkIRect sk_cull_rect = SkIRect::MakeWH(cull_rect.width, cull_rect.height); - return impeller::RenderToOnscreen(aiks_context->GetContentContext(), // - render_target, // - display_list, // - sk_cull_rect, // - /*reset_host_buffer=*/true // + return impeller::RenderToTarget(aiks_context->GetContentContext(), // + render_target, // + display_list, // + sk_cull_rect, // + /*reset_host_buffer=*/true // ); }; @@ -212,11 +212,11 @@ std::unique_ptr GPUSurfaceVulkanImpeller::AcquireFrame( } SkIRect sk_cull_rect = SkIRect::MakeWH(cull_rect.width, cull_rect.height); - return impeller::RenderToOnscreen(aiks_context->GetContentContext(), // - render_target, // - display_list, // - sk_cull_rect, // - /*reset_host_buffer=*/true // + return impeller::RenderToTarget(aiks_context->GetContentContext(), // + render_target, // + display_list, // + sk_cull_rect, // + /*reset_host_buffer=*/true // ); }; diff --git a/engine/src/flutter/shell/platform/embedder/embedder_external_view.cc b/engine/src/flutter/shell/platform/embedder/embedder_external_view.cc index 99ce249597..3d58ec6667 100644 --- a/engine/src/flutter/shell/platform/embedder/embedder_external_view.cc +++ b/engine/src/flutter/shell/platform/embedder/embedder_external_view.cc @@ -133,11 +133,12 @@ bool EmbedderExternalView::Render(const EmbedderRenderTarget& render_target, SkIRect sk_cull_rect = SkIRect::MakeWH(cull_rect.GetWidth(), cull_rect.GetHeight()); - return impeller::RenderToOnscreen(aiks_context->GetContentContext(), // - *impeller_target, // - display_list, // - sk_cull_rect, // - /*reset_host_buffer=*/true // + return impeller::RenderToTarget(aiks_context->GetContentContext(), // + *impeller_target, // + display_list, // + sk_cull_rect, // + /*reset_host_buffer=*/true, // + /*is_onscreen=*/false // ); } #endif // IMPELLER_SUPPORTS_RENDERING