From dd7948b2fe38458644db6bf2a2386272e2419068 Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Tue, 24 Sep 2024 16:37:06 -0700 Subject: [PATCH] [Impeller] add basic culling checks during text frame dispatcher. (flutter/engine#55168) Fixes https://github.com/flutter/flutter/issues/155133 Dl dispatching still relies on cull rects computed during that dispatch process. Make sure that the text frame dispatcher doesn't populate text frames that are way offscreen. This culling is more conservative than the rendering dispatcher. We'd need to do some refactoring so the logic isn't repeated multiple times. --- .../flutter/impeller/aiks/aiks_playground.cc | 5 +- .../impeller/display_list/dl_dispatcher.cc | 67 +++++++++++++++++-- .../impeller/display_list/dl_dispatcher.h | 16 ++++- .../impeller/display_list/dl_playground.cc | 5 +- .../impeller/toolkit/interop/surface.cc | 3 +- .../common/snapshot_controller_impeller.cc | 7 +- .../shell/gpu/gpu_surface_gl_impeller.cc | 5 +- .../shell/gpu/gpu_surface_metal_impeller.mm | 12 ++-- .../shell/gpu/gpu_surface_vulkan_impeller.cc | 5 +- .../embedder/embedder_external_view.cc | 8 ++- 10 files changed, 111 insertions(+), 22 deletions(-) diff --git a/engine/src/flutter/impeller/aiks/aiks_playground.cc b/engine/src/flutter/impeller/aiks/aiks_playground.cc index 882e0d66a99..6ad968a2764 100644 --- a/engine/src/flutter/impeller/aiks/aiks_playground.cc +++ b/engine/src/flutter/impeller/aiks/aiks_playground.cc @@ -90,7 +90,10 @@ bool AiksPlayground::OpenPlaygroundHere( [&renderer, &callback](RenderTarget& render_target) -> bool { #if EXPERIMENTAL_CANVAS auto display_list = callback(); - TextFrameDispatcher collector(renderer.GetContentContext(), Matrix()); + TextFrameDispatcher collector(renderer.GetContentContext(), // + Matrix(), // + Rect::MakeMaximum() // + ); display_list->Dispatch(collector); ExperimentalDlDispatcher impeller_dispatcher( diff --git a/engine/src/flutter/impeller/display_list/dl_dispatcher.cc b/engine/src/flutter/impeller/display_list/dl_dispatcher.cc index d58a6339aaf..2e1870da193 100644 --- a/engine/src/flutter/impeller/display_list/dl_dispatcher.cc +++ b/engine/src/flutter/impeller/display_list/dl_dispatcher.cc @@ -1368,22 +1368,46 @@ Canvas& ExperimentalDlDispatcher::GetCanvas() { //// Text Frame Dispatcher TextFrameDispatcher::TextFrameDispatcher(const ContentContext& renderer, - const Matrix& initial_matrix) - : renderer_(renderer), matrix_(initial_matrix) {} + const Matrix& initial_matrix, + const Rect cull_rect) + : renderer_(renderer), matrix_(initial_matrix) { + cull_rect_state_.push_back(cull_rect); +} + +TextFrameDispatcher::~TextFrameDispatcher() { + FML_DCHECK(cull_rect_state_.size() == 1); +} void TextFrameDispatcher::save() { stack_.emplace_back(matrix_); + cull_rect_state_.push_back(cull_rect_state_.back()); } void TextFrameDispatcher::saveLayer(const DlRect& bounds, const flutter::SaveLayerOptions options, const flutter::DlImageFilter* backdrop) { save(); + + // This dispatcher does not track enough state to accurately compute + // cull rects with image filters. + auto global_cull_rect = cull_rect_state_.back(); + if (has_image_filter_ || global_cull_rect.IsMaximum()) { + cull_rect_state_.back() = Rect::MakeMaximum(); + } else { + auto global_save_bounds = bounds.TransformBounds(matrix_); + auto new_cull_rect = global_cull_rect.Intersection(global_save_bounds); + if (new_cull_rect.has_value()) { + cull_rect_state_.back() = new_cull_rect.value(); + } else { + cull_rect_state_.back() = Rect::MakeLTRB(0, 0, 0, 0); + } + } } void TextFrameDispatcher::restore() { matrix_ = stack_.back(); stack_.pop_back(); + cull_rect_state_.pop_back(); } void TextFrameDispatcher::translate(DlScalar tx, DlScalar ty) { @@ -1459,6 +1483,15 @@ void TextFrameDispatcher::drawTextFrame( ); } +const Rect TextFrameDispatcher::GetCurrentLocalCullingBounds() const { + auto cull_rect = cull_rect_state_.back(); + if (!cull_rect.IsEmpty() && !cull_rect.IsMaximum()) { + Matrix inverse = matrix_.Invert(); + cull_rect = cull_rect.TransformBounds(inverse); + } + return cull_rect; +} + void TextFrameDispatcher::drawDisplayList( const sk_sp display_list, DlScalar opacity) { @@ -1466,9 +1499,24 @@ void TextFrameDispatcher::drawDisplayList( save(); Paint old_paint = paint_; paint_ = Paint{}; - display_list->Dispatch(*this); + bool old_has_image_filter = has_image_filter_; + has_image_filter_ = false; + + Rect local_cull_bounds = GetCurrentLocalCullingBounds(); + if (local_cull_bounds.IsMaximum()) { + display_list->Dispatch(*this); + } else if (!local_cull_bounds.IsEmpty()) { + IRect cull_rect = IRect::RoundOut(local_cull_bounds); + display_list->Dispatch(*this, SkIRect::MakeLTRB(cull_rect.GetLeft(), // + cull_rect.GetTop(), // + cull_rect.GetRight(), // + cull_rect.GetBottom() // + )); + } + restore(); paint_ = old_paint; + has_image_filter_ = old_has_image_filter; FML_DCHECK(stack_depth == stack_.size()); } @@ -1522,6 +1570,15 @@ void TextFrameDispatcher::setStrokeJoin(flutter::DlStrokeJoin join) { } } +// |flutter::DlOpReceiver| +void TextFrameDispatcher::setImageFilter(const flutter::DlImageFilter* filter) { + if (filter == nullptr) { + has_image_filter_ = false; + } else { + has_image_filter_ = true; + } +} + std::shared_ptr DisplayListToTexture( const sk_sp& display_list, ISize size, @@ -1553,8 +1610,8 @@ std::shared_ptr DisplayListToTexture( } SkIRect sk_cull_rect = SkIRect::MakeWH(size.width, size.height); - impeller::TextFrameDispatcher collector(context.GetContentContext(), - impeller::Matrix()); + impeller::TextFrameDispatcher collector( + context.GetContentContext(), impeller::Matrix(), Rect::MakeSize(size)); display_list->Dispatch(collector, sk_cull_rect); impeller::ExperimentalDlDispatcher impeller_dispatcher( context.GetContentContext(), target, diff --git a/engine/src/flutter/impeller/display_list/dl_dispatcher.h b/engine/src/flutter/impeller/display_list/dl_dispatcher.h index 55694fd61aa..61f8efd5a9d 100644 --- a/engine/src/flutter/impeller/display_list/dl_dispatcher.h +++ b/engine/src/flutter/impeller/display_list/dl_dispatcher.h @@ -14,6 +14,8 @@ #include "impeller/aiks/experimental_canvas.h" #include "impeller/aiks/paint.h" #include "impeller/entity/contents/content_context.h" +#include "impeller/geometry/color.h" +#include "impeller/geometry/rect.h" namespace impeller { @@ -330,7 +332,11 @@ class TextFrameDispatcher : public flutter::IgnoreAttributeDispatchHelper, public flutter::IgnoreDrawDispatchHelper { public: TextFrameDispatcher(const ContentContext& renderer, - const Matrix& initial_matrix); + const Matrix& initial_matrix, + const Rect cull_rect); + + ~TextFrameDispatcher(); + void save() override; void saveLayer(const DlRect& bounds, @@ -386,10 +392,18 @@ class TextFrameDispatcher : public flutter::IgnoreAttributeDispatchHelper, // |flutter::DlOpReceiver| void setStrokeJoin(flutter::DlStrokeJoin join) override; + // |flutter::DlOpReceiver| + void setImageFilter(const flutter::DlImageFilter* filter) override; + private: + const Rect GetCurrentLocalCullingBounds() const; + const ContentContext& renderer_; Matrix matrix_; std::vector stack_; + // note: cull rects are always in the global coordinate space. + std::vector cull_rect_state_; + bool has_image_filter_ = false; Paint paint_; }; diff --git a/engine/src/flutter/impeller/display_list/dl_playground.cc b/engine/src/flutter/impeller/display_list/dl_playground.cc index cf74a095465..ee3ee438028 100644 --- a/engine/src/flutter/impeller/display_list/dl_playground.cc +++ b/engine/src/flutter/impeller/display_list/dl_playground.cc @@ -49,7 +49,10 @@ bool DlPlayground::OpenPlaygroundHere(DisplayListPlaygroundCallback callback) { auto list = callback(); #if EXPERIMENTAL_CANVAS - TextFrameDispatcher collector(context.GetContentContext(), Matrix()); + TextFrameDispatcher collector(context.GetContentContext(), // + Matrix(), // + Rect::MakeMaximum() // + ); list->Dispatch(collector); ExperimentalDlDispatcher impeller_dispatcher( diff --git a/engine/src/flutter/impeller/toolkit/interop/surface.cc b/engine/src/flutter/impeller/toolkit/interop/surface.cc index 20a2f4c4188..d37fad66407 100644 --- a/engine/src/flutter/impeller/toolkit/interop/surface.cc +++ b/engine/src/flutter/impeller/toolkit/interop/surface.cc @@ -61,7 +61,8 @@ bool Surface::DrawDisplayList(const DisplayList& dl) const { const auto cull_rect = IRect::MakeSize(surface_->GetSize()); auto skia_cull_rect = SkIRect::MakeWH(cull_rect.GetWidth(), cull_rect.GetHeight()); - impeller::TextFrameDispatcher collector(content_context, impeller::Matrix{}); + impeller::TextFrameDispatcher collector(content_context, impeller::Matrix{}, + Rect::MakeSize(surface_->GetSize())); display_list->Dispatch(collector, skia_cull_rect); impeller::ExperimentalDlDispatcher impeller_dispatcher( diff --git a/engine/src/flutter/shell/common/snapshot_controller_impeller.cc b/engine/src/flutter/shell/common/snapshot_controller_impeller.cc index 2b06c04e4d1..89583f9eafc 100644 --- a/engine/src/flutter/shell/common/snapshot_controller_impeller.cc +++ b/engine/src/flutter/shell/common/snapshot_controller_impeller.cc @@ -73,8 +73,11 @@ sk_sp DoMakeRasterSnapshot( ); } - impeller::TextFrameDispatcher collector(context->GetContentContext(), - impeller::Matrix()); + impeller::TextFrameDispatcher collector( + context->GetContentContext(), // + impeller::Matrix(), // + impeller::Rect::MakeSize(render_target_size) // + ); display_list->Dispatch(collector, SkIRect::MakeSize(size)); impeller::ExperimentalDlDispatcher impeller_dispatcher( context->GetContentContext(), target, 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 cc4d74b3cfb..7542d040fb9 100644 --- a/engine/src/flutter/shell/gpu/gpu_surface_gl_impeller.cc +++ b/engine/src/flutter/shell/gpu/gpu_surface_gl_impeller.cc @@ -120,8 +120,9 @@ std::unique_ptr GPUSurfaceGLImpeller::AcquireFrame( #if EXPERIMENTAL_CANVAS auto skia_cull_rect = SkIRect::MakeWH(cull_rect.width, cull_rect.height); - impeller::TextFrameDispatcher collector(aiks_context->GetContentContext(), - impeller::Matrix()); + impeller::TextFrameDispatcher collector( + aiks_context->GetContentContext(), impeller::Matrix(), + impeller::Rect::MakeSize(cull_rect)); display_list->Dispatch(collector, skia_cull_rect); impeller::ExperimentalDlDispatcher impeller_dispatcher( 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 6e8c43763e0..a842e50a7eb 100644 --- a/engine/src/flutter/shell/gpu/gpu_surface_metal_impeller.mm +++ b/engine/src/flutter/shell/gpu/gpu_surface_metal_impeller.mm @@ -171,8 +171,10 @@ std::unique_ptr GPUSurfaceMetalImpeller::AcquireFrameFromCAMetalLa surface->SetFrameBoundary(surface_frame.submit_info().frame_boundary); #if EXPERIMENTAL_CANVAS - impeller::TextFrameDispatcher collector(aiks_context->GetContentContext(), - impeller::Matrix()); + impeller::TextFrameDispatcher collector(aiks_context->GetContentContext(), // + impeller::Matrix(), // + impeller::Rect::MakeSize(cull_rect.GetSize()) // + ); display_list->Dispatch(collector, sk_cull_rect); impeller::ExperimentalDlDispatcher impeller_dispatcher( @@ -299,8 +301,10 @@ std::unique_ptr GPUSurfaceMetalImpeller::AcquireFrameFromMTLTextur impeller::IRect cull_rect = surface->coverage(); SkIRect sk_cull_rect = SkIRect::MakeWH(cull_rect.GetWidth(), cull_rect.GetHeight()); #if EXPERIMENTAL_CANVAS - impeller::TextFrameDispatcher collector(aiks_context->GetContentContext(), - impeller::Matrix()); + impeller::TextFrameDispatcher collector(aiks_context->GetContentContext(), // + impeller::Matrix(), // + impeller::Rect::MakeSize(cull_rect.GetSize()) // + ); display_list->Dispatch(collector, sk_cull_rect); impeller::RenderTarget render_target = surface->GetTargetRenderPassDescriptor(); impeller::ExperimentalDlDispatcher impeller_dispatcher( 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 263c3db64bf..d04937edc6b 100644 --- a/engine/src/flutter/shell/gpu/gpu_surface_vulkan_impeller.cc +++ b/engine/src/flutter/shell/gpu/gpu_surface_vulkan_impeller.cc @@ -81,8 +81,9 @@ std::unique_ptr GPUSurfaceVulkanImpeller::AcquireFrame( } #if EXPERIMENTAL_CANVAS - impeller::TextFrameDispatcher collector(aiks_context->GetContentContext(), - impeller::Matrix()); + impeller::TextFrameDispatcher collector( + aiks_context->GetContentContext(), impeller::Matrix(), + impeller::Rect::MakeSize(cull_rect)); display_list->Dispatch(collector, SkIRect::MakeWH(cull_rect.width, cull_rect.height)); impeller::ExperimentalDlDispatcher impeller_dispatcher( 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 1111e8a7a83..9019a417f09 100644 --- a/engine/src/flutter/shell/platform/embedder/embedder_external_view.cc +++ b/engine/src/flutter/shell/platform/embedder/embedder_external_view.cc @@ -134,9 +134,11 @@ bool EmbedderExternalView::Render(const EmbedderRenderTarget& render_target, impeller::IRect::MakeSize(impeller_target->GetRenderTargetSize()); SkIRect sk_cull_rect = SkIRect::MakeWH(cull_rect.GetWidth(), cull_rect.GetHeight()); - - impeller::TextFrameDispatcher collector(aiks_context->GetContentContext(), - impeller::Matrix()); + impeller::TextFrameDispatcher collector( + aiks_context->GetContentContext(), // + impeller::Matrix(), // + impeller::Rect::MakeSize(cull_rect.GetSize()) // + ); display_list->Dispatch(collector, sk_cull_rect); impeller::ExperimentalDlDispatcher impeller_dispatcher(