Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 901b0f1

Browse files
[Impeller] cache for text shadows. (#166228)
Fixes #164303 Fixes #165116 When rendering a text shadow, cache the resulting snapshot for at least one frame. Only attempts to content-aware hash for single glyphs (Icons). in other cases the exact layout may be different enough that its not worth trying to cache.
1 parent e971379 commit 901b0f1

File tree

17 files changed

+504
-58
lines changed

17 files changed

+504
-58
lines changed

engine/src/flutter/ci/licenses_golden/licenses_flutter

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51252,6 +51252,8 @@ ORIGIN: ../../../flutter/impeller/entity/contents/sweep_gradient_contents.cc + .
5125251252
ORIGIN: ../../../flutter/impeller/entity/contents/sweep_gradient_contents.h + ../../../flutter/LICENSE
5125351253
ORIGIN: ../../../flutter/impeller/entity/contents/text_contents.cc + ../../../flutter/LICENSE
5125451254
ORIGIN: ../../../flutter/impeller/entity/contents/text_contents.h + ../../../flutter/LICENSE
51255+
ORIGIN: ../../../flutter/impeller/entity/contents/text_shadow_cache.cc + ../../../flutter/LICENSE
51256+
ORIGIN: ../../../flutter/impeller/entity/contents/text_shadow_cache.h + ../../../flutter/LICENSE
5125551257
ORIGIN: ../../../flutter/impeller/entity/contents/texture_contents.cc + ../../../flutter/LICENSE
5125651258
ORIGIN: ../../../flutter/impeller/entity/contents/texture_contents.h + ../../../flutter/LICENSE
5125751259
ORIGIN: ../../../flutter/impeller/entity/contents/tiled_texture_contents.cc + ../../../flutter/LICENSE
@@ -54240,6 +54242,8 @@ FILE: ../../../flutter/impeller/entity/contents/sweep_gradient_contents.cc
5424054242
FILE: ../../../flutter/impeller/entity/contents/sweep_gradient_contents.h
5424154243
FILE: ../../../flutter/impeller/entity/contents/text_contents.cc
5424254244
FILE: ../../../flutter/impeller/entity/contents/text_contents.h
54245+
FILE: ../../../flutter/impeller/entity/contents/text_shadow_cache.cc
54246+
FILE: ../../../flutter/impeller/entity/contents/text_shadow_cache.h
5424354247
FILE: ../../../flutter/impeller/entity/contents/texture_contents.cc
5424454248
FILE: ../../../flutter/impeller/entity/contents/texture_contents.h
5424554249
FILE: ../../../flutter/impeller/entity/contents/tiled_texture_contents.cc

engine/src/flutter/impeller/display_list/aiks_dl_text_unittests.cc

Lines changed: 124 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
#include "flutter/fml/build_config.h"
1414
#include "flutter/impeller/display_list/aiks_unittests.h"
1515
#include "flutter/testing/testing.h"
16+
#include "impeller/display_list/aiks_context.h"
17+
#include "impeller/display_list/dl_dispatcher.h"
18+
#include "impeller/entity/contents/content_context.h"
1619
#include "impeller/entity/contents/text_contents.h"
1720
#include "impeller/entity/entity.h"
1821
#include "impeller/geometry/matrix.h"
@@ -41,7 +44,8 @@ bool RenderTextInCanvasSkia(const std::shared_ptr<Context>& context,
4144
DisplayListBuilder& canvas,
4245
const std::string& text,
4346
const std::string_view& font_fixture,
44-
const TextRenderOptions& options = {}) {
47+
const TextRenderOptions& options = {},
48+
const std::optional<SkFont>& font = std::nullopt) {
4549
// Draw the baseline.
4650
DlPaint paint;
4751
paint.setColor(DlColor::kAqua().withAlpha(255 * 0.25));
@@ -54,17 +58,23 @@ bool RenderTextInCanvasSkia(const std::shared_ptr<Context>& context,
5458
canvas.DrawCircle(options.position, 5.0, paint);
5559

5660
// Construct the text blob.
57-
auto c_font_fixture = std::string(font_fixture);
58-
auto mapping = flutter::testing::OpenFixtureAsSkData(c_font_fixture.c_str());
59-
if (!mapping) {
60-
return false;
61-
}
62-
sk_sp<SkFontMgr> font_mgr = txt::GetDefaultFontManager();
63-
SkFont sk_font(font_mgr->makeFromData(mapping), options.font_size);
64-
if (options.is_subpixel) {
65-
sk_font.setSubpixel(true);
61+
SkFont selected_font;
62+
if (!font.has_value()) {
63+
auto c_font_fixture = std::string(font_fixture);
64+
auto mapping =
65+
flutter::testing::OpenFixtureAsSkData(c_font_fixture.c_str());
66+
if (!mapping) {
67+
return false;
68+
}
69+
sk_sp<SkFontMgr> font_mgr = txt::GetDefaultFontManager();
70+
selected_font = SkFont(font_mgr->makeFromData(mapping), options.font_size);
71+
if (options.is_subpixel) {
72+
selected_font.setSubpixel(true);
73+
}
74+
} else {
75+
selected_font = font.value();
6676
}
67-
auto blob = SkTextBlob::MakeFromString(text.c_str(), sk_font);
77+
auto blob = SkTextBlob::MakeFromString(text.c_str(), selected_font);
6878
if (!blob) {
6979
return false;
7080
}
@@ -731,5 +741,108 @@ TEST_P(AiksTest, TextContentsMismatchedTransformTest) {
731741
*render_pass));
732742
}
733743

744+
TEST_P(AiksTest, TextWithShadowCache) {
745+
DisplayListBuilder builder;
746+
builder.Scale(GetContentScale().x, GetContentScale().y);
747+
DlPaint paint;
748+
paint.setColor(DlColor::ARGB(1, 0.1, 0.1, 0.1));
749+
builder.DrawPaint(paint);
750+
751+
AiksContext aiks_context(GetContext(),
752+
std::make_shared<TypographerContextSkia>());
753+
// Cache empty
754+
EXPECT_EQ(aiks_context.GetContentContext()
755+
.GetTextShadowCache()
756+
.GetCacheSizeForTesting(),
757+
0u);
758+
759+
ASSERT_TRUE(RenderTextInCanvasSkia(
760+
GetContext(), builder, "Hello World", kFontFixture,
761+
TextRenderOptions{
762+
.color = DlColor::kBlue(),
763+
.filter = DlBlurMaskFilter::Make(DlBlurStyle::kNormal, 4)}));
764+
765+
DisplayListToTexture(builder.Build(), {400, 400}, aiks_context);
766+
767+
// Text should be cached.
768+
EXPECT_EQ(aiks_context.GetContentContext()
769+
.GetTextShadowCache()
770+
.GetCacheSizeForTesting(),
771+
1u);
772+
}
773+
774+
TEST_P(AiksTest, MultipleTextWithShadowCache) {
775+
DisplayListBuilder builder;
776+
builder.Scale(GetContentScale().x, GetContentScale().y);
777+
DlPaint paint;
778+
paint.setColor(DlColor::ARGB(1, 0.1, 0.1, 0.1));
779+
builder.DrawPaint(paint);
780+
781+
AiksContext aiks_context(GetContext(),
782+
std::make_shared<TypographerContextSkia>());
783+
// Cache empty
784+
EXPECT_EQ(aiks_context.GetContentContext()
785+
.GetTextShadowCache()
786+
.GetCacheSizeForTesting(),
787+
0u);
788+
789+
for (auto i = 0; i < 5; i++) {
790+
ASSERT_TRUE(RenderTextInCanvasSkia(
791+
GetContext(), builder, "Hello World", kFontFixture,
792+
TextRenderOptions{
793+
.color = DlColor::kBlue(),
794+
.filter = DlBlurMaskFilter::Make(DlBlurStyle::kNormal, 4)}));
795+
}
796+
797+
DisplayListToTexture(builder.Build(), {400, 400}, aiks_context);
798+
799+
// Text should be cached. Each text gets its own entry as we don't analyze the
800+
// strings.
801+
EXPECT_EQ(aiks_context.GetContentContext()
802+
.GetTextShadowCache()
803+
.GetCacheSizeForTesting(),
804+
5u);
805+
}
806+
807+
TEST_P(AiksTest, SingleIconShadowTest) {
808+
DisplayListBuilder builder;
809+
builder.Scale(GetContentScale().x, GetContentScale().y);
810+
DlPaint paint;
811+
paint.setColor(DlColor::ARGB(1, 0.1, 0.1, 0.1));
812+
builder.DrawPaint(paint);
813+
814+
AiksContext aiks_context(GetContext(),
815+
std::make_shared<TypographerContextSkia>());
816+
// Cache empty
817+
EXPECT_EQ(aiks_context.GetContentContext()
818+
.GetTextShadowCache()
819+
.GetCacheSizeForTesting(),
820+
0u);
821+
822+
// Create font instance outside loop so all draws use identical font instance.
823+
auto c_font_fixture = std::string(kFontFixture);
824+
auto mapping = flutter::testing::OpenFixtureAsSkData(c_font_fixture.c_str());
825+
ASSERT_TRUE(mapping);
826+
sk_sp<SkFontMgr> font_mgr = txt::GetDefaultFontManager();
827+
SkFont sk_font(font_mgr->makeFromData(mapping), 50);
828+
829+
for (auto i = 0; i < 10; i++) {
830+
ASSERT_TRUE(RenderTextInCanvasSkia(
831+
GetContext(), builder, "A", kFontFixture,
832+
TextRenderOptions{
833+
.color = DlColor::kBlue(),
834+
.filter = DlBlurMaskFilter::Make(DlBlurStyle::kNormal, 4)},
835+
sk_font));
836+
}
837+
838+
DisplayListToTexture(builder.Build(), {400, 400}, aiks_context);
839+
840+
// Text should be cached. All 10 glyphs use the same cache entry.
841+
EXPECT_EQ(aiks_context.GetContentContext()
842+
.GetTextShadowCache()
843+
.GetCacheSizeForTesting(),
844+
1u);
845+
}
846+
734847
} // namespace testing
735848
} // namespace impeller

engine/src/flutter/impeller/display_list/aiks_dl_unittests.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ TEST_P(AiksTest, CollapsedDrawPaintInSubpassBackdropFilter) {
7878

7979
TEST_P(AiksTest, ColorMatrixFilterSubpassCollapseOptimization) {
8080
DisplayListBuilder builder(DlRect::MakeSize(GetWindowSize()));
81+
builder.DrawPaint(DlPaint().setColor(DlColor::kWhite()));
8182

8283
const float matrix[20] = {
8384
-1.0, 0, 0, 1.0, 0, //

engine/src/flutter/impeller/display_list/canvas.cc

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
#include "impeller/base/validation.h"
2121
#include "impeller/core/formats.h"
2222
#include "impeller/display_list/color_filter.h"
23-
#include "impeller/display_list/dl_atlas_geometry.h"
2423
#include "impeller/display_list/image_filter.h"
2524
#include "impeller/display_list/skia_conversions.h"
2625
#include "impeller/entity/contents/atlas_contents.h"
@@ -32,6 +31,7 @@
3231
#include "impeller/entity/contents/line_contents.h"
3332
#include "impeller/entity/contents/solid_rrect_blur_contents.h"
3433
#include "impeller/entity/contents/text_contents.h"
34+
#include "impeller/entity/contents/text_shadow_cache.h"
3535
#include "impeller/entity/contents/texture_contents.h"
3636
#include "impeller/entity/contents/vertices_contents.h"
3737
#include "impeller/entity/geometry/circle_geometry.h"
@@ -1467,6 +1467,50 @@ bool Canvas::Restore() {
14671467
return true;
14681468
}
14691469

1470+
bool Canvas::AttemptBlurredTextOptimization(
1471+
const std::shared_ptr<TextFrame>& text_frame,
1472+
const std::shared_ptr<TextContents>& text_contents,
1473+
Entity& entity,
1474+
const Paint& paint) {
1475+
if (!paint.mask_blur_descriptor.has_value() || //
1476+
paint.image_filter != nullptr || //
1477+
paint.color_filter != nullptr || //
1478+
paint.invert_colors) {
1479+
return false;
1480+
}
1481+
1482+
// TODO(bdero): This mask blur application is a hack. It will always wind up
1483+
// doing a gaussian blur that affects the color source itself
1484+
// instead of just the mask. The color filter text support
1485+
// needs to be reworked in order to interact correctly with
1486+
// mask filters.
1487+
// https://github.com/flutter/flutter/issues/133297
1488+
std::shared_ptr<FilterContents> filter =
1489+
paint.mask_blur_descriptor->CreateMaskBlur(
1490+
FilterInput::Make(text_contents),
1491+
/*is_solid_color=*/true, GetCurrentTransform());
1492+
1493+
std::optional<Glyph> maybe_glyph = text_frame->AsSingleGlyph();
1494+
int64_t identifier = maybe_glyph.has_value()
1495+
? maybe_glyph.value().index
1496+
: reinterpret_cast<int64_t>(text_frame.get());
1497+
TextShadowCache::TextShadowCacheKey cache_key(
1498+
/*p_max_basis=*/entity.GetTransform().GetMaxBasisLengthXY(),
1499+
/*p_identifier=*/identifier,
1500+
/*p_is_single_glyph=*/maybe_glyph.has_value(),
1501+
/*p_font=*/text_frame->GetFont(),
1502+
/*p_sigma=*/paint.mask_blur_descriptor->sigma);
1503+
1504+
std::optional<Entity> result = renderer_.GetTextShadowCache().Lookup(
1505+
renderer_, entity, filter, cache_key);
1506+
if (result.has_value()) {
1507+
AddRenderEntityToCurrentPass(result.value(), /*reuse_depth=*/false);
1508+
return true;
1509+
} else {
1510+
return false;
1511+
}
1512+
}
1513+
14701514
void Canvas::DrawTextFrame(const std::shared_ptr<TextFrame>& text_frame,
14711515
Point position,
14721516
const Paint& paint) {
@@ -1491,15 +1535,12 @@ void Canvas::DrawTextFrame(const std::shared_ptr<TextFrame>& text_frame,
14911535
entity.SetTransform(GetCurrentTransform() *
14921536
Matrix::MakeTranslation(position));
14931537

1494-
// TODO(bdero): This mask blur application is a hack. It will always wind up
1495-
// doing a gaussian blur that affects the color source itself
1496-
// instead of just the mask. The color filter text support
1497-
// needs to be reworked in order to interact correctly with
1498-
// mask filters.
1499-
// https://github.com/flutter/flutter/issues/133297
1500-
entity.SetContents(paint.WithFilters(paint.WithMaskBlur(
1501-
std::move(text_contents), true, GetCurrentTransform())));
1538+
if (AttemptBlurredTextOptimization(text_frame, text_contents, entity,
1539+
paint)) {
1540+
return;
1541+
}
15021542

1543+
entity.SetContents(paint.WithFilters(std::move(text_contents)));
15031544
AddRenderEntityToCurrentPass(entity, false);
15041545
}
15051546

engine/src/flutter/impeller/display_list/canvas.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "impeller/display_list/paint.h"
1818
#include "impeller/entity/contents/atlas_contents.h"
1919
#include "impeller/entity/contents/clip_contents.h"
20+
#include "impeller/entity/contents/text_contents.h"
2021
#include "impeller/entity/entity.h"
2122
#include "impeller/entity/entity_pass_clip_stack.h"
2223
#include "impeller/entity/geometry/geometry.h"
@@ -368,6 +369,12 @@ class Canvas {
368369
const SamplerDescriptor& sampler,
369370
SourceRectConstraint src_rect_constraint);
370371

372+
bool AttemptBlurredTextOptimization(
373+
const std::shared_ptr<TextFrame>& text_frame,
374+
const std::shared_ptr<TextContents>& text_contents,
375+
Entity& entity,
376+
const Paint& paint);
377+
371378
RenderPass& GetCurrentRenderPass() const;
372379

373380
Canvas(const Canvas&) = delete;

engine/src/flutter/impeller/display_list/dl_dispatcher.cc

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "display_list/dl_sampling_options.h"
1414
#include "display_list/effects/dl_image_filter.h"
1515
#include "flutter/fml/logging.h"
16+
#include "fml/closure.h"
1617
#include "impeller/core/formats.h"
1718
#include "impeller/display_list/aiks_context.h"
1819
#include "impeller/display_list/canvas.h"
@@ -1333,15 +1334,19 @@ std::shared_ptr<Texture> DisplayListToTexture(
13331334
);
13341335
const auto& [data, count] = collector.TakeBackdropData();
13351336
impeller_dispatcher.SetBackdropData(data, count);
1337+
context.GetContentContext().GetTextShadowCache().MarkFrameStart();
1338+
fml::ScopedCleanupClosure cleanup([&] {
1339+
if (reset_host_buffer) {
1340+
context.GetContentContext().GetTransientsBuffer().Reset();
1341+
}
1342+
context.GetContentContext().GetTextShadowCache().MarkFrameEnd();
1343+
context.GetContentContext().GetLazyGlyphAtlas()->ResetTextFrames();
1344+
context.GetContext()->DisposeThreadLocalCachedResources();
1345+
});
1346+
13361347
display_list->Dispatch(impeller_dispatcher, sk_cull_rect);
13371348
impeller_dispatcher.FinishRecording();
13381349

1339-
if (reset_host_buffer) {
1340-
context.GetContentContext().GetTransientsBuffer().Reset();
1341-
}
1342-
context.GetContentContext().GetLazyGlyphAtlas()->ResetTextFrames();
1343-
context.GetContext()->DisposeThreadLocalCachedResources();
1344-
13451350
return target.GetRenderTargetTexture();
13461351
}
13471352

@@ -1366,11 +1371,16 @@ bool RenderToTarget(ContentContext& context,
13661371
);
13671372
const auto& [data, count] = collector.TakeBackdropData();
13681373
impeller_dispatcher.SetBackdropData(data, count);
1374+
context.GetTextShadowCache().MarkFrameStart();
1375+
fml::ScopedCleanupClosure cleanup([&] {
1376+
if (reset_host_buffer) {
1377+
context.GetTransientsBuffer().Reset();
1378+
}
1379+
context.GetTextShadowCache().MarkFrameEnd();
1380+
});
1381+
13691382
display_list->Dispatch(impeller_dispatcher, cull_rect);
13701383
impeller_dispatcher.FinishRecording();
1371-
if (reset_host_buffer) {
1372-
context.GetTransientsBuffer().Reset();
1373-
}
13741384
context.GetLazyGlyphAtlas()->ResetTextFrames();
13751385

13761386
return true;

engine/src/flutter/impeller/entity/BUILD.gn

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,8 @@ impeller_component("entity") {
179179
"contents/sweep_gradient_contents.h",
180180
"contents/text_contents.cc",
181181
"contents/text_contents.h",
182+
"contents/text_shadow_cache.cc",
183+
"contents/text_shadow_cache.h",
182184
"contents/texture_contents.cc",
183185
"contents/texture_contents.h",
184186
"contents/tiled_texture_contents.cc",

engine/src/flutter/impeller/entity/contents/content_context.cc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "impeller/core/texture_descriptor.h"
1414
#include "impeller/entity/contents/framebuffer_blend_contents.h"
1515
#include "impeller/entity/contents/pipelines.h"
16+
#include "impeller/entity/contents/text_shadow_cache.h"
1617
#include "impeller/entity/entity.h"
1718
#include "impeller/entity/render_target_cache.h"
1819
#include "impeller/renderer/command_buffer.h"
@@ -532,7 +533,8 @@ ContentContext::ContentContext(
532533
context_->GetResourceAllocator())
533534
: std::move(render_target_allocator)),
534535
host_buffer_(HostBuffer::Create(context_->GetResourceAllocator(),
535-
context_->GetIdleWaiter())) {
536+
context_->GetIdleWaiter())),
537+
text_shadow_cache_(std::make_unique<TextShadowCache>()) {
536538
if (!context_ || !context_->IsValid()) {
537539
return;
538540
}

0 commit comments

Comments
 (0)