[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.
This commit is contained in:
Jonah Williams 2024-09-24 16:37:06 -07:00 committed by GitHub
parent d68ee6e60e
commit dd7948b2fe
10 changed files with 111 additions and 22 deletions

View File

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

View File

@ -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<flutter::DisplayList> 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<Texture> DisplayListToTexture(
const sk_sp<flutter::DisplayList>& display_list,
ISize size,
@ -1553,8 +1610,8 @@ std::shared_ptr<Texture> 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,

View File

@ -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<Matrix> stack_;
// note: cull rects are always in the global coordinate space.
std::vector<Rect> cull_rect_state_;
bool has_image_filter_ = false;
Paint paint_;
};

View File

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

View File

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

View File

@ -73,8 +73,11 @@ sk_sp<DlImage> 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,

View File

@ -120,8 +120,9 @@ std::unique_ptr<SurfaceFrame> 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(

View File

@ -171,8 +171,10 @@ std::unique_ptr<SurfaceFrame> 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<SurfaceFrame> 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(

View File

@ -81,8 +81,9 @@ std::unique_ptr<SurfaceFrame> 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(

View File

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