[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
This commit is contained in:
Jonah Williams 2025-01-06 12:26:08 -08:00 committed by GitHub
parent a9b3f6c042
commit ffc7ced2a0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
31 changed files with 186 additions and 82 deletions

View File

@ -50,14 +50,14 @@ bool AiksPlayground::OpenPlaygroundHere(
return Playground::OpenPlaygroundHere( return Playground::OpenPlaygroundHere(
[&renderer, &callback](RenderTarget& render_target) -> bool { [&renderer, &callback](RenderTarget& render_target) -> bool {
return RenderToOnscreen( return RenderToTarget(
renderer.GetContentContext(), // renderer.GetContentContext(), //
render_target, // render_target, //
callback(), // callback(), //
SkIRect::MakeWH(render_target.GetRenderTargetSize().width, SkIRect::MakeWH(render_target.GetRenderTargetSize().width,
render_target.GetRenderTargetSize().height), // render_target.GetRenderTargetSize().height), //
/*reset_host_buffer=*/true // /*reset_host_buffer=*/true, //
); /*is_onscreen=*/false);
}); });
} }

View File

@ -43,6 +43,7 @@
#include "impeller/geometry/color.h" #include "impeller/geometry/color.h"
#include "impeller/geometry/constants.h" #include "impeller/geometry/constants.h"
#include "impeller/geometry/path_builder.h" #include "impeller/geometry/path_builder.h"
#include "impeller/renderer/command_buffer.h"
namespace impeller { namespace impeller {
@ -162,9 +163,11 @@ static std::unique_ptr<EntityPassTarget> CreateRenderTarget(
Canvas::Canvas(ContentContext& renderer, Canvas::Canvas(ContentContext& renderer,
const RenderTarget& render_target, const RenderTarget& render_target,
bool is_onscreen,
bool requires_readback) bool requires_readback)
: renderer_(renderer), : renderer_(renderer),
render_target_(render_target), render_target_(render_target),
is_onscreen_(is_onscreen),
requires_readback_(requires_readback), requires_readback_(requires_readback),
clip_coverage_stack_(EntityPassClipStack( clip_coverage_stack_(EntityPassClipStack(
Rect::MakeSize(render_target.GetRenderTargetSize()))) { Rect::MakeSize(render_target.GetRenderTargetSize()))) {
@ -174,10 +177,12 @@ Canvas::Canvas(ContentContext& renderer,
Canvas::Canvas(ContentContext& renderer, Canvas::Canvas(ContentContext& renderer,
const RenderTarget& render_target, const RenderTarget& render_target,
bool is_onscreen,
bool requires_readback, bool requires_readback,
Rect cull_rect) Rect cull_rect)
: renderer_(renderer), : renderer_(renderer),
render_target_(render_target), render_target_(render_target),
is_onscreen_(is_onscreen),
requires_readback_(requires_readback), requires_readback_(requires_readback),
clip_coverage_stack_(EntityPassClipStack( clip_coverage_stack_(EntityPassClipStack(
Rect::MakeSize(render_target.GetRenderTargetSize()))) { Rect::MakeSize(render_target.GetRenderTargetSize()))) {
@ -187,10 +192,12 @@ Canvas::Canvas(ContentContext& renderer,
Canvas::Canvas(ContentContext& renderer, Canvas::Canvas(ContentContext& renderer,
const RenderTarget& render_target, const RenderTarget& render_target,
bool is_onscreen,
bool requires_readback, bool requires_readback,
IRect cull_rect) IRect cull_rect)
: renderer_(renderer), : renderer_(renderer),
render_target_(render_target), render_target_(render_target),
is_onscreen_(is_onscreen),
requires_readback_(requires_readback), requires_readback_(requires_readback),
clip_coverage_stack_(EntityPassClipStack( clip_coverage_stack_(EntityPassClipStack(
Rect::MakeSize(render_target.GetRenderTargetSize()))) { Rect::MakeSize(render_target.GetRenderTargetSize()))) {
@ -1658,7 +1665,7 @@ std::shared_ptr<Texture> Canvas::FlipBackdrop(Point global_pass_position,
return input_texture; return input_texture;
} }
bool Canvas::BlitToOnscreen() { bool Canvas::BlitToOnscreen(bool is_onscreen) {
auto command_buffer = renderer_.GetContext()->CreateCommandBuffer(); auto command_buffer = renderer_.GetContext()->CreateCommandBuffer();
command_buffer->SetLabel("EntityPass Root Command Buffer"); command_buffer->SetLabel("EntityPass Root Command Buffer");
auto offscreen_target = render_passes_.back() auto offscreen_target = render_passes_.back()
@ -1675,10 +1682,6 @@ bool Canvas::BlitToOnscreen() {
VALIDATION_LOG << "Failed to encode root pass blit command."; VALIDATION_LOG << "Failed to encode root pass blit command.";
return false; return false;
} }
if (!renderer_.GetContext()->EnqueueCommandBuffer(
std::move(command_buffer))) {
return false;
}
} else { } else {
auto render_pass = command_buffer->CreateRenderPass(render_target_); auto render_pass = command_buffer->CreateRenderPass(render_target_);
render_pass->SetLabel("EntityPass Root Render Pass"); render_pass->SetLabel("EntityPass Root Render Pass");
@ -1704,25 +1707,28 @@ bool Canvas::BlitToOnscreen() {
VALIDATION_LOG << "Failed to encode root pass command buffer."; VALIDATION_LOG << "Failed to encode root pass command buffer.";
return false; return false;
} }
if (!renderer_.GetContext()->EnqueueCommandBuffer(
std::move(command_buffer))) {
return false;
} }
if (is_onscreen) {
return renderer_.GetContext()->SubmitOnscreen(std::move(command_buffer));
} else {
return renderer_.GetContext()->EnqueueCommandBuffer(
std::move(command_buffer));
} }
return true;
} }
void Canvas::EndReplay() { void Canvas::EndReplay() {
FML_DCHECK(render_passes_.size() == 1u); FML_DCHECK(render_passes_.size() == 1u);
render_passes_.back().inline_pass_context->GetRenderPass(); 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(); backdrop_data_.clear();
// If requires_readback_ was true, then we rendered to an offscreen texture // 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 // instead of to the onscreen provided in the render target. Now we need to
// draw or blit the offscreen back to the onscreen. // draw or blit the offscreen back to the onscreen.
if (requires_readback_) { if (requires_readback_) {
BlitToOnscreen(); BlitToOnscreen(/*is_onscreen_=*/is_onscreen_);
} }
if (!renderer_.GetContext()->FlushCommandBuffers()) { if (!renderer_.GetContext()->FlushCommandBuffers()) {

View File

@ -123,15 +123,18 @@ class Canvas {
Canvas(ContentContext& renderer, Canvas(ContentContext& renderer,
const RenderTarget& render_target, const RenderTarget& render_target,
bool is_onscreen,
bool requires_readback); bool requires_readback);
explicit Canvas(ContentContext& renderer, explicit Canvas(ContentContext& renderer,
const RenderTarget& render_target, const RenderTarget& render_target,
bool is_onscreen,
bool requires_readback, bool requires_readback,
Rect cull_rect); Rect cull_rect);
explicit Canvas(ContentContext& renderer, explicit Canvas(ContentContext& renderer,
const RenderTarget& render_target, const RenderTarget& render_target,
bool is_onscreen,
bool requires_readback, bool requires_readback,
IRect cull_rect); IRect cull_rect);
@ -251,6 +254,7 @@ class Canvas {
private: private:
ContentContext& renderer_; ContentContext& renderer_;
RenderTarget render_target_; RenderTarget render_target_;
const bool is_onscreen_;
bool requires_readback_; bool requires_readback_;
EntityPassClipStack clip_coverage_stack_; EntityPassClipStack clip_coverage_stack_;
@ -318,7 +322,7 @@ class Canvas {
bool should_remove_texture = false, bool should_remove_texture = false,
bool should_use_onscreen = false); bool should_use_onscreen = false);
bool BlitToOnscreen(); bool BlitToOnscreen(bool is_onscreen = false);
size_t GetClipHeight() const; size_t GetClipHeight() const;

View File

@ -56,10 +56,12 @@ std::unique_ptr<Canvas> CreateTestCanvas(
render_target.SetColorAttachment(color0, 0); render_target.SetColorAttachment(color0, 0);
if (cull_rect.has_value()) { if (cull_rect.has_value()) {
return std::make_unique<Canvas>(context, render_target, requires_readback, return std::make_unique<Canvas>(
cull_rect.value()); context, render_target, /*is_onscreen=*/false,
/*requires_readback=*/requires_readback, cull_rect.value());
} }
return std::make_unique<Canvas>(context, render_target, requires_readback); return std::make_unique<Canvas>(context, render_target, /*is_onscreen=*/false,
/*requires_readback=*/requires_readback);
} }
TEST_P(AiksTest, TransformMultipliesCorrectly) { TEST_P(AiksTest, TransformMultipliesCorrectly) {

View File

@ -947,11 +947,13 @@ static bool RequiresReadbackForBlends(
CanvasDlDispatcher::CanvasDlDispatcher(ContentContext& renderer, CanvasDlDispatcher::CanvasDlDispatcher(ContentContext& renderer,
RenderTarget& render_target, RenderTarget& render_target,
bool is_onscreen,
bool has_root_backdrop_filter, bool has_root_backdrop_filter,
flutter::DlBlendMode max_root_blend_mode, flutter::DlBlendMode max_root_blend_mode,
IRect cull_rect) IRect cull_rect)
: canvas_(renderer, : canvas_(renderer,
render_target, render_target,
is_onscreen,
has_root_backdrop_filter || has_root_backdrop_filter ||
RequiresReadbackForBlends(renderer, max_root_blend_mode), RequiresReadbackForBlends(renderer, max_root_blend_mode),
cull_rect), cull_rect),
@ -1270,6 +1272,7 @@ std::shared_ptr<Texture> DisplayListToTexture(
impeller::CanvasDlDispatcher impeller_dispatcher( impeller::CanvasDlDispatcher impeller_dispatcher(
context.GetContentContext(), // context.GetContentContext(), //
target, // target, //
/*is_onscreen=*/false, //
display_list->root_has_backdrop_filter(), // display_list->root_has_backdrop_filter(), //
display_list->max_root_blend_mode(), // display_list->max_root_blend_mode(), //
impeller::IRect::MakeSize(size) // impeller::IRect::MakeSize(size) //
@ -1288,11 +1291,12 @@ std::shared_ptr<Texture> DisplayListToTexture(
return target.GetRenderTargetTexture(); return target.GetRenderTargetTexture();
} }
bool RenderToOnscreen(ContentContext& context, bool RenderToTarget(ContentContext& context,
RenderTarget render_target, RenderTarget render_target,
const sk_sp<flutter::DisplayList>& display_list, const sk_sp<flutter::DisplayList>& display_list,
SkIRect cull_rect, SkIRect cull_rect,
bool reset_host_buffer) { bool reset_host_buffer,
bool is_onscreen) {
Rect ip_cull_rect = Rect::MakeLTRB(cull_rect.left(), cull_rect.top(), Rect ip_cull_rect = Rect::MakeLTRB(cull_rect.left(), cull_rect.top(),
cull_rect.right(), cull_rect.bottom()); cull_rect.right(), cull_rect.bottom());
FirstPassDispatcher collector(context, impeller::Matrix(), ip_cull_rect); FirstPassDispatcher collector(context, impeller::Matrix(), ip_cull_rect);
@ -1301,6 +1305,7 @@ bool RenderToOnscreen(ContentContext& context,
impeller::CanvasDlDispatcher impeller_dispatcher( impeller::CanvasDlDispatcher impeller_dispatcher(
context, // context, //
render_target, // render_target, //
/*is_onscreen=*/is_onscreen, //
display_list->root_has_backdrop_filter(), // display_list->root_has_backdrop_filter(), //
display_list->max_root_blend_mode(), // display_list->max_root_blend_mode(), //
IRect::RoundOut(ip_cull_rect) // IRect::RoundOut(ip_cull_rect) //

View File

@ -254,6 +254,7 @@ class CanvasDlDispatcher : public DlDispatcherBase {
public: public:
CanvasDlDispatcher(ContentContext& renderer, CanvasDlDispatcher(ContentContext& renderer,
RenderTarget& render_target, RenderTarget& render_target,
bool is_onscreen,
bool has_root_backdrop_filter, bool has_root_backdrop_filter,
flutter::DlBlendMode max_root_blend_mode, flutter::DlBlendMode max_root_blend_mode,
IRect cull_rect); IRect cull_rect);
@ -390,11 +391,15 @@ std::shared_ptr<Texture> DisplayListToTexture(
bool reset_host_buffer = true, bool reset_host_buffer = true,
bool generate_mips = false); bool generate_mips = false);
/// Render the provided display list to the render target. /// @brief Render the provided display list to the render target.
bool RenderToOnscreen(ContentContext& context, RenderTarget 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<flutter::DisplayList>& display_list, const sk_sp<flutter::DisplayList>& display_list,
SkIRect cull_rect, SkIRect cull_rect,
bool reset_host_buffer); bool reset_host_buffer,
bool is_onscreen = true);
} // namespace impeller } // namespace impeller

View File

@ -45,13 +45,14 @@ bool DlPlayground::OpenPlaygroundHere(DisplayListPlaygroundCallback callback) {
wireframe = !wireframe; wireframe = !wireframe;
context.GetContentContext().SetWireframe(wireframe); context.GetContentContext().SetWireframe(wireframe);
} }
return RenderToOnscreen( return RenderToTarget(
context.GetContentContext(), // context.GetContentContext(), //
render_target, // render_target, //
callback(), // callback(), //
SkIRect::MakeWH(render_target.GetRenderTargetSize().width, SkIRect::MakeWH(render_target.GetRenderTargetSize().width,
render_target.GetRenderTargetSize().height), // render_target.GetRenderTargetSize().height), //
/*reset_host_buffer=*/true // /*reset_host_buffer=*/true, //
/*is_onscreen=*/false //
); );
}); });
} }

View File

@ -40,7 +40,7 @@ std::shared_ptr<Texture> InlinePassContext::GetTexture() {
return pass_target_.GetRenderTarget().GetRenderTargetTexture(); return pass_target_.GetRenderTarget().GetRenderTargetTexture();
} }
bool InlinePassContext::EndPass() { bool InlinePassContext::EndPass(bool is_onscreen) {
if (!IsActive()) { if (!IsActive()) {
return true; return true;
} }
@ -63,9 +63,13 @@ bool InlinePassContext::EndPass() {
} }
pass_ = nullptr; pass_ = nullptr;
if (is_onscreen) {
return renderer_.GetContext()->SubmitOnscreen(std::move(command_buffer_));
} else {
return renderer_.GetContext()->EnqueueCommandBuffer( return renderer_.GetContext()->EnqueueCommandBuffer(
std::move(command_buffer_)); std::move(command_buffer_));
} }
}
EntityPassTarget& InlinePassContext::GetPassTarget() const { EntityPassTarget& InlinePassContext::GetPassTarget() const {
return pass_target_; return pass_target_;

View File

@ -27,7 +27,7 @@ class InlinePassContext {
std::shared_ptr<Texture> GetTexture(); std::shared_ptr<Texture> GetTexture();
bool EndPass(); bool EndPass(bool is_onscreen = false);
EntityPassTarget& GetPassTarget() const; EntityPassTarget& GetPassTarget() const;

View File

@ -657,6 +657,10 @@ bool ContextVK::EnqueueCommandBuffer(
} }
bool ContextVK::FlushCommandBuffers() { bool ContextVK::FlushCommandBuffers() {
if (pending_command_buffers_.empty()) {
return true;
}
if (should_batch_cmd_buffers_) { if (should_batch_cmd_buffers_) {
bool result = GetCommandQueue()->Submit(pending_command_buffers_).ok(); bool result = GetCommandQueue()->Submit(pending_command_buffers_).ok();
pending_command_buffers_.clear(); pending_command_buffers_.clear();
@ -733,4 +737,8 @@ RuntimeStageBackend ContextVK::GetRuntimeStageBackend() const {
return RuntimeStageBackend::kVulkan; return RuntimeStageBackend::kVulkan;
} }
bool ContextVK::SubmitOnscreen(std::shared_ptr<CommandBuffer> cmd_buffer) {
return EnqueueCommandBuffer(std::move(cmd_buffer));
}
} // namespace impeller } // namespace impeller

View File

@ -131,6 +131,10 @@ class ContextVK final : public Context,
// |Context| // |Context|
const std::shared_ptr<const Capabilities>& GetCapabilities() const override; const std::shared_ptr<const Capabilities>& GetCapabilities() const override;
// |Context|
virtual bool SubmitOnscreen(
std::shared_ptr<CommandBuffer> cmd_buffer) override;
const std::shared_ptr<YUVConversionLibraryVK>& GetYUVConversionLibrary() const std::shared_ptr<YUVConversionLibraryVK>& GetYUVConversionLibrary()
const; const;

View File

@ -6,7 +6,6 @@
#include "impeller/core/formats.h" #include "impeller/core/formats.h"
#include "impeller/renderer/backend/vulkan/formats_vk.h" #include "impeller/renderer/backend/vulkan/formats_vk.h"
#include "vulkan/vulkan_enums.hpp"
namespace impeller { namespace impeller {

View File

@ -14,7 +14,6 @@
#include "impeller/core/formats.h" #include "impeller/core/formats.h"
#include "impeller/core/texture.h" #include "impeller/core/texture.h"
#include "impeller/core/vertex_buffer.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/command_buffer_vk.h"
#include "impeller/renderer/backend/vulkan/context_vk.h" #include "impeller/renderer/backend/vulkan/context_vk.h"
#include "impeller/renderer/backend/vulkan/device_buffer_vk.h" #include "impeller/renderer/backend/vulkan/device_buffer_vk.h"
@ -82,14 +81,16 @@ SharedHandleVK<vk::RenderPass> RenderPassVK::CreateVKRenderPass(
const std::shared_ptr<CommandBufferVK>& command_buffer) const { const std::shared_ptr<CommandBufferVK>& command_buffer) const {
RenderPassBuilderVK builder; RenderPassBuilderVK builder;
render_target_.IterateAllColorAttachments( render_target_.IterateAllColorAttachments([&](size_t bind_point,
[&](size_t bind_point, const ColorAttachment& attachment) -> bool { const ColorAttachment&
attachment) -> bool {
builder.SetColorAttachment( builder.SetColorAttachment(
bind_point, // bind_point, //
attachment.texture->GetTextureDescriptor().format, // attachment.texture->GetTextureDescriptor().format, //
attachment.texture->GetTextureDescriptor().sample_count, // attachment.texture->GetTextureDescriptor().sample_count, //
attachment.load_action, // attachment.load_action, //
attachment.store_action // attachment.store_action, //
/*current_layout=*/TextureVK::Cast(*attachment.texture).GetLayout() //
); );
return true; return true;
}); });
@ -179,6 +180,8 @@ RenderPassVK::RenderPassVK(const std::shared_ptr<const Context>& context,
if (resolve_image_vk_) { if (resolve_image_vk_) {
TextureVK::Cast(*resolve_image_vk_).SetCachedFramebuffer(framebuffer); TextureVK::Cast(*resolve_image_vk_).SetCachedFramebuffer(framebuffer);
TextureVK::Cast(*resolve_image_vk_).SetCachedRenderPass(render_pass_); TextureVK::Cast(*resolve_image_vk_).SetCachedRenderPass(render_pass_);
TextureVK::Cast(*resolve_image_vk_)
.SetLayoutWithoutEncoding(vk::ImageLayout::eGeneral);
} }
std::array<vk::ClearValue, kMaxAttachments> clears; std::array<vk::ClearValue, kMaxAttachments> clears;

View File

@ -129,6 +129,12 @@ bool SurfaceContextVK::FlushCommandBuffers() {
return parent_->FlushCommandBuffers(); return parent_->FlushCommandBuffers();
} }
bool SurfaceContextVK::SubmitOnscreen(
std::shared_ptr<CommandBuffer> cmd_buffer) {
swapchain_->AddFinalCommandBuffer(std::move(cmd_buffer));
return true;
}
RuntimeStageBackend SurfaceContextVK::GetRuntimeStageBackend() const { RuntimeStageBackend SurfaceContextVK::GetRuntimeStageBackend() const {
return parent_->GetRuntimeStageBackend(); return parent_->GetRuntimeStageBackend();
} }

View File

@ -73,6 +73,9 @@ class SurfaceContextVK : public Context,
// |Context| // |Context|
RuntimeStageBackend GetRuntimeStageBackend() const override; RuntimeStageBackend GetRuntimeStageBackend() const override;
// |Context|
bool SubmitOnscreen(std::shared_ptr<CommandBuffer> cmd_buffer) override;
// |Context| // |Context|
void Shutdown() override; void Shutdown() override;

View File

@ -192,6 +192,12 @@ bool AHBSwapchainImplVK::Present(
}); });
} }
void AHBSwapchainImplVK::AddFinalCommandBuffer(
std::shared_ptr<CommandBuffer> cmd_buffer) {
FML_DCHECK(!pending_cmd_buffer_);
pending_cmd_buffer_ = std::move(cmd_buffer);
}
std::shared_ptr<ExternalFenceVK> std::shared_ptr<ExternalFenceVK>
AHBSwapchainImplVK::SubmitSignalForPresentReady( AHBSwapchainImplVK::SubmitSignalForPresentReady(
const std::shared_ptr<AHBTextureSourceVK>& texture) const { const std::shared_ptr<AHBTextureSourceVK>& texture) const {
@ -204,7 +210,7 @@ AHBSwapchainImplVK::SubmitSignalForPresentReady(
return nullptr; return nullptr;
} }
auto command_buffer = context->CreateCommandBuffer(); auto command_buffer = pending_cmd_buffer_;
if (!command_buffer) { if (!command_buffer) {
return nullptr; return nullptr;
} }

View File

@ -89,6 +89,8 @@ class AHBSwapchainImplVK final
/// ///
std::unique_ptr<Surface> AcquireNextDrawable(); std::unique_ptr<Surface> AcquireNextDrawable();
void AddFinalCommandBuffer(std::shared_ptr<CommandBuffer> cmd_buffer);
private: private:
using AutoSemaSignaler = std::shared_ptr<fml::ScopedCleanupClosure>; using AutoSemaSignaler = std::shared_ptr<fml::ScopedCleanupClosure>;
@ -102,6 +104,7 @@ class AHBSwapchainImplVK final
std::shared_ptr<AHBTextureSourceVK> currently_displayed_texture_ std::shared_ptr<AHBTextureSourceVK> currently_displayed_texture_
IPLR_GUARDED_BY(currently_displayed_texture_mutex_); IPLR_GUARDED_BY(currently_displayed_texture_mutex_);
std::shared_ptr<fml::Semaphore> pending_presents_; std::shared_ptr<fml::Semaphore> pending_presents_;
std::shared_ptr<CommandBuffer> pending_cmd_buffer_;
bool is_valid_ = false; bool is_valid_ = false;
explicit AHBSwapchainImplVK( explicit AHBSwapchainImplVK(

View File

@ -66,6 +66,12 @@ vk::Format AHBSwapchainVK::GetSurfaceFormat() const {
: vk::Format::eUndefined; : vk::Format::eUndefined;
} }
// |SwapchainVK|
void AHBSwapchainVK::AddFinalCommandBuffer(
std::shared_ptr<CommandBuffer> cmd_buffer) const {
return impl_->AddFinalCommandBuffer(cmd_buffer);
}
// |SwapchainVK| // |SwapchainVK|
void AHBSwapchainVK::UpdateSurfaceSize(const ISize& size) { void AHBSwapchainVK::UpdateSurfaceSize(const ISize& size) {
if (impl_ && impl_->GetSize() == size) { if (impl_ && impl_->GetSize() == size) {

View File

@ -46,6 +46,10 @@ class AHBSwapchainVK final : public SwapchainVK {
// |SwapchainVK| // |SwapchainVK|
void UpdateSurfaceSize(const ISize& size) override; void UpdateSurfaceSize(const ISize& size) override;
// |SwapchainVK|
void AddFinalCommandBuffer(
std::shared_ptr<CommandBuffer> cmd_buffer) const override;
private: private:
friend class SwapchainVK; friend class SwapchainVK;

View File

@ -13,11 +13,12 @@
#include "impeller/renderer/backend/vulkan/gpu_tracer_vk.h" #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/khr/khr_swapchain_image_vk.h"
#include "impeller/renderer/backend/vulkan/swapchain/surface_vk.h" #include "impeller/renderer/backend/vulkan/swapchain/surface_vk.h"
#include "impeller/renderer/backend/vulkan/texture_vk.h"
#include "impeller/renderer/context.h" #include "impeller/renderer/context.h"
namespace impeller { namespace impeller {
static constexpr size_t kMaxFramesInFlight = 3u; static constexpr size_t kMaxFramesInFlight = 2u;
struct KHRFrameSynchronizerVK { struct KHRFrameSynchronizerVK {
vk::UniqueFence acquire; vk::UniqueFence acquire;
@ -25,6 +26,8 @@ struct KHRFrameSynchronizerVK {
vk::UniqueSemaphore present_ready; vk::UniqueSemaphore present_ready;
std::shared_ptr<CommandBuffer> final_cmd_buffer; std::shared_ptr<CommandBuffer> final_cmd_buffer;
bool is_valid = false; 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) { explicit KHRFrameSynchronizerVK(const vk::Device& device) {
auto acquire_res = device.createFenceUnique( auto acquire_res = device.createFenceUnique(
@ -378,6 +381,13 @@ KHRSwapchainImplVK::AcquireResult KHRSwapchainImplVK::AcquireNextDrawable() {
)}; )};
} }
void KHRSwapchainImplVK::AddFinalCommandBuffer(
std::shared_ptr<CommandBuffer> cmd_buffer) {
const auto& sync = synchronizers_[current_frame_];
sync->final_cmd_buffer = std::move(cmd_buffer);
sync->has_onscreen = true;
}
bool KHRSwapchainImplVK::Present( bool KHRSwapchainImplVK::Present(
const std::shared_ptr<KHRSwapchainImageVK>& image, const std::shared_ptr<KHRSwapchainImageVK>& image,
uint32_t index) { uint32_t index) {
@ -393,7 +403,10 @@ bool KHRSwapchainImplVK::Present(
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
/// Transition the image to color-attachment-optimal. /// Transition the image to color-attachment-optimal.
/// ///
if (!sync->has_onscreen) {
sync->final_cmd_buffer = context.CreateCommandBuffer(); sync->final_cmd_buffer = context.CreateCommandBuffer();
}
sync->has_onscreen = false;
if (!sync->final_cmd_buffer) { if (!sync->final_cmd_buffer) {
return false; return false;
} }

View File

@ -7,7 +7,6 @@
#include <cstdint> #include <cstdint>
#include <memory> #include <memory>
#include <variant>
#include "impeller/geometry/size.h" #include "impeller/geometry/size.h"
#include "impeller/renderer/backend/vulkan/swapchain/swapchain_transients_vk.h" #include "impeller/renderer/backend/vulkan/swapchain/swapchain_transients_vk.h"
@ -63,6 +62,8 @@ class KHRSwapchainImplVK final
const ISize& GetSize() const; const ISize& GetSize() const;
void AddFinalCommandBuffer(std::shared_ptr<CommandBuffer> cmd_buffer);
private: private:
std::weak_ptr<Context> context_; std::weak_ptr<Context> context_;
vk::UniqueSurfaceKHR surface_; vk::UniqueSurfaceKHR surface_;

View File

@ -39,6 +39,11 @@ void KHRSwapchainVK::UpdateSurfaceSize(const ISize& size) {
size_ = size; size_ = size;
} }
void KHRSwapchainVK::AddFinalCommandBuffer(
std::shared_ptr<CommandBuffer> cmd_buffer) const {
impl_->AddFinalCommandBuffer(std::move(cmd_buffer));
}
std::unique_ptr<Surface> KHRSwapchainVK::AcquireNextDrawable() { std::unique_ptr<Surface> KHRSwapchainVK::AcquireNextDrawable() {
if (!IsValid()) { if (!IsValid()) {
return nullptr; return nullptr;

View File

@ -37,6 +37,10 @@ class KHRSwapchainVK final : public SwapchainVK {
// |SwapchainVK| // |SwapchainVK|
void UpdateSurfaceSize(const ISize& size) override; void UpdateSurfaceSize(const ISize& size) override;
// |SwapchainVK|
void AddFinalCommandBuffer(
std::shared_ptr<CommandBuffer> cmd_buffer) const override;
private: private:
friend class SwapchainVK; friend class SwapchainVK;

View File

@ -10,6 +10,7 @@
#include "flutter/fml/build_config.h" #include "flutter/fml/build_config.h"
#include "impeller/geometry/size.h" #include "impeller/geometry/size.h"
#include "impeller/renderer/backend/vulkan/vk.h" #include "impeller/renderer/backend/vulkan/vk.h"
#include "impeller/renderer/command_buffer.h"
#include "impeller/renderer/context.h" #include "impeller/renderer/context.h"
#include "impeller/renderer/surface.h" #include "impeller/renderer/surface.h"
@ -52,6 +53,9 @@ class SwapchainVK {
virtual vk::Format GetSurfaceFormat() const = 0; virtual vk::Format GetSurfaceFormat() const = 0;
virtual void AddFinalCommandBuffer(
std::shared_ptr<CommandBuffer> cmd_buffer) const = 0;
/// @brief Mark the current swapchain configuration as dirty, forcing it to be /// @brief Mark the current swapchain configuration as dirty, forcing it to be
/// recreated on the next frame. /// recreated on the next frame.
virtual void UpdateSurfaceSize(const ISize& size) = 0; virtual void UpdateSurfaceSize(const ISize& size) = 0;

View File

@ -37,4 +37,8 @@ bool Context::AddTrackingFence(const std::shared_ptr<Texture>& texture) const {
return false; return false;
} }
bool Context::SubmitOnscreen(std::shared_ptr<CommandBuffer> cmd_buffer) {
return EnqueueCommandBuffer(std::move(cmd_buffer));
}
} // namespace impeller } // namespace impeller

View File

@ -242,6 +242,9 @@ class Context {
/// correct shader types. /// correct shader types.
virtual RuntimeStageBackend GetRuntimeStageBackend() const = 0; virtual RuntimeStageBackend GetRuntimeStageBackend() const = 0;
/// @brief Submit the command buffer that renders to the onscreen surface.
virtual bool SubmitOnscreen(std::shared_ptr<CommandBuffer> cmd_buffer);
protected: protected:
Context(); Context();

View File

@ -62,7 +62,7 @@ bool Surface::DrawDisplayList(const DisplayList& dl) const {
auto skia_cull_rect = auto skia_cull_rect =
SkIRect::MakeWH(cull_rect.GetWidth(), cull_rect.GetHeight()); SkIRect::MakeWH(cull_rect.GetWidth(), cull_rect.GetHeight());
auto result = RenderToOnscreen(content_context, render_target, display_list, auto result = RenderToTarget(content_context, render_target, display_list,
skia_cull_rect, /*reset_host_buffer=*/true); skia_cull_rect, /*reset_host_buffer=*/true);
context_->GetContext()->ResetThreadLocalState(); context_->GetContext()->ResetThreadLocalState();
return result; return result;

View File

@ -116,7 +116,7 @@ std::unique_ptr<SurfaceFrame> GPUSurfaceGLImpeller::AcquireFrame(
auto cull_rect = render_target.GetRenderTargetSize(); auto cull_rect = render_target.GetRenderTargetSize();
SkIRect sk_cull_rect = SkIRect::MakeWH(cull_rect.width, cull_rect.height); SkIRect sk_cull_rect = SkIRect::MakeWH(cull_rect.width, cull_rect.height);
return impeller::RenderToOnscreen(aiks_context->GetContentContext(), // return impeller::RenderToTarget(aiks_context->GetContentContext(), //
render_target, // render_target, //
display_list, // display_list, //
sk_cull_rect, // sk_cull_rect, //

View File

@ -169,7 +169,7 @@ std::unique_ptr<SurfaceFrame> GPUSurfaceMetalImpeller::AcquireFrameFromCAMetalLa
surface->SetFrameBoundary(surface_frame.submit_info().frame_boundary); surface->SetFrameBoundary(surface_frame.submit_info().frame_boundary);
const bool reset_host_buffer = 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(), // auto render_result = impeller::RenderToTarget(aiks_context->GetContentContext(), //
surface->GetRenderTarget(), // surface->GetRenderTarget(), //
display_list, // display_list, //
sk_cull_rect, // sk_cull_rect, //
@ -283,7 +283,7 @@ std::unique_ptr<SurfaceFrame> GPUSurfaceMetalImpeller::AcquireFrameFromMTLTextur
impeller::IRect cull_rect = surface->coverage(); impeller::IRect cull_rect = surface->coverage();
SkIRect sk_cull_rect = SkIRect::MakeWH(cull_rect.GetWidth(), cull_rect.GetHeight()); SkIRect sk_cull_rect = SkIRect::MakeWH(cull_rect.GetWidth(), cull_rect.GetHeight());
auto render_result = impeller::RenderToOnscreen(aiks_context->GetContentContext(), // auto render_result = impeller::RenderToTarget(aiks_context->GetContentContext(), //
surface->GetRenderTarget(), // surface->GetRenderTarget(), //
display_list, // display_list, //
sk_cull_rect, // sk_cull_rect, //

View File

@ -115,7 +115,7 @@ std::unique_ptr<SurfaceFrame> GPUSurfaceVulkanImpeller::AcquireFrame(
} }
SkIRect sk_cull_rect = SkIRect::MakeWH(cull_rect.width, cull_rect.height); SkIRect sk_cull_rect = SkIRect::MakeWH(cull_rect.width, cull_rect.height);
return impeller::RenderToOnscreen(aiks_context->GetContentContext(), // return impeller::RenderToTarget(aiks_context->GetContentContext(), //
render_target, // render_target, //
display_list, // display_list, //
sk_cull_rect, // sk_cull_rect, //
@ -212,7 +212,7 @@ std::unique_ptr<SurfaceFrame> GPUSurfaceVulkanImpeller::AcquireFrame(
} }
SkIRect sk_cull_rect = SkIRect::MakeWH(cull_rect.width, cull_rect.height); SkIRect sk_cull_rect = SkIRect::MakeWH(cull_rect.width, cull_rect.height);
return impeller::RenderToOnscreen(aiks_context->GetContentContext(), // return impeller::RenderToTarget(aiks_context->GetContentContext(), //
render_target, // render_target, //
display_list, // display_list, //
sk_cull_rect, // sk_cull_rect, //

View File

@ -133,11 +133,12 @@ bool EmbedderExternalView::Render(const EmbedderRenderTarget& render_target,
SkIRect sk_cull_rect = SkIRect sk_cull_rect =
SkIRect::MakeWH(cull_rect.GetWidth(), cull_rect.GetHeight()); SkIRect::MakeWH(cull_rect.GetWidth(), cull_rect.GetHeight());
return impeller::RenderToOnscreen(aiks_context->GetContentContext(), // return impeller::RenderToTarget(aiks_context->GetContentContext(), //
*impeller_target, // *impeller_target, //
display_list, // display_list, //
sk_cull_rect, // sk_cull_rect, //
/*reset_host_buffer=*/true // /*reset_host_buffer=*/true, //
/*is_onscreen=*/false //
); );
} }
#endif // IMPELLER_SUPPORTS_RENDERING #endif // IMPELLER_SUPPORTS_RENDERING