[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(
[&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);
});
}

View File

@ -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<EntityPassTarget> 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<Texture> 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;
}
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() {
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()) {

View File

@ -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;

View File

@ -56,10 +56,12 @@ std::unique_ptr<Canvas> CreateTestCanvas(
render_target.SetColorAttachment(color0, 0);
if (cull_rect.has_value()) {
return std::make_unique<Canvas>(context, render_target, requires_readback,
cull_rect.value());
return std::make_unique<Canvas>(
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) {

View File

@ -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<Texture> 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<Texture> DisplayListToTexture(
return target.GetRenderTargetTexture();
}
bool RenderToOnscreen(ContentContext& context,
bool RenderToTarget(ContentContext& context,
RenderTarget render_target,
const sk_sp<flutter::DisplayList>& display_list,
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(),
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) //

View File

@ -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<Texture> 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<flutter::DisplayList>& display_list,
SkIRect cull_rect,
bool reset_host_buffer);
bool reset_host_buffer,
bool is_onscreen = true);
} // namespace impeller

View File

@ -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 //
);
});
}

View File

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

View File

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

View File

@ -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<CommandBuffer> cmd_buffer) {
return EnqueueCommandBuffer(std::move(cmd_buffer));
}
} // namespace impeller

View File

@ -131,6 +131,10 @@ class ContextVK final : public Context,
// |Context|
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;

View File

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

View File

@ -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,14 +81,16 @@ SharedHandleVK<vk::RenderPass> RenderPassVK::CreateVKRenderPass(
const std::shared_ptr<CommandBufferVK>& command_buffer) const {
RenderPassBuilderVK builder;
render_target_.IterateAllColorAttachments(
[&](size_t bind_point, const ColorAttachment& attachment) -> bool {
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 //
attachment.store_action, //
/*current_layout=*/TextureVK::Cast(*attachment.texture).GetLayout() //
);
return true;
});
@ -179,6 +180,8 @@ RenderPassVK::RenderPassVK(const std::shared_ptr<const Context>& 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<vk::ClearValue, kMaxAttachments> clears;

View File

@ -129,6 +129,12 @@ bool SurfaceContextVK::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 {
return parent_->GetRuntimeStageBackend();
}

View File

@ -73,6 +73,9 @@ class SurfaceContextVK : public Context,
// |Context|
RuntimeStageBackend GetRuntimeStageBackend() const override;
// |Context|
bool SubmitOnscreen(std::shared_ptr<CommandBuffer> cmd_buffer) override;
// |Context|
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>
AHBSwapchainImplVK::SubmitSignalForPresentReady(
const std::shared_ptr<AHBTextureSourceVK>& 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;
}

View File

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

View File

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

View File

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

View File

@ -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<CommandBuffer> 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<CommandBuffer> 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<KHRSwapchainImageVK>& image,
uint32_t index) {
@ -393,7 +403,10 @@ bool KHRSwapchainImplVK::Present(
//----------------------------------------------------------------------------
/// Transition the image to color-attachment-optimal.
///
if (!sync->has_onscreen) {
sync->final_cmd_buffer = context.CreateCommandBuffer();
}
sync->has_onscreen = false;
if (!sync->final_cmd_buffer) {
return false;
}

View File

@ -7,7 +7,6 @@
#include <cstdint>
#include <memory>
#include <variant>
#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<CommandBuffer> cmd_buffer);
private:
std::weak_ptr<Context> context_;
vk::UniqueSurfaceKHR surface_;

View File

@ -39,6 +39,11 @@ void KHRSwapchainVK::UpdateSurfaceSize(const ISize& 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() {
if (!IsValid()) {
return nullptr;

View File

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

View File

@ -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<CommandBuffer> 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;

View File

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

View File

@ -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<CommandBuffer> cmd_buffer);
protected:
Context();

View File

@ -62,7 +62,7 @@ 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,
auto result = RenderToTarget(content_context, render_target, display_list,
skia_cull_rect, /*reset_host_buffer=*/true);
context_->GetContext()->ResetThreadLocalState();
return result;

View File

@ -116,7 +116,7 @@ std::unique_ptr<SurfaceFrame> 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(), //
return impeller::RenderToTarget(aiks_context->GetContentContext(), //
render_target, //
display_list, //
sk_cull_rect, //

View File

@ -169,7 +169,7 @@ std::unique_ptr<SurfaceFrame> 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(), //
auto render_result = impeller::RenderToTarget(aiks_context->GetContentContext(), //
surface->GetRenderTarget(), //
display_list, //
sk_cull_rect, //
@ -283,7 +283,7 @@ std::unique_ptr<SurfaceFrame> 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(), //
auto render_result = impeller::RenderToTarget(aiks_context->GetContentContext(), //
surface->GetRenderTarget(), //
display_list, //
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);
return impeller::RenderToOnscreen(aiks_context->GetContentContext(), //
return impeller::RenderToTarget(aiks_context->GetContentContext(), //
render_target, //
display_list, //
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);
return impeller::RenderToOnscreen(aiks_context->GetContentContext(), //
return impeller::RenderToTarget(aiks_context->GetContentContext(), //
render_target, //
display_list, //
sk_cull_rect, //

View File

@ -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(), //
return impeller::RenderToTarget(aiks_context->GetContentContext(), //
*impeller_target, //
display_list, //
sk_cull_rect, //
/*reset_host_buffer=*/true //
/*reset_host_buffer=*/true, //
/*is_onscreen=*/false //
);
}
#endif // IMPELLER_SUPPORTS_RENDERING