From 38e37ce9879b2efe4890a8d09f4207dc5686a1bd Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Wed, 11 Sep 2024 11:14:05 -0700 Subject: [PATCH] [Impeller] hash less text stuff per frame for text rendering. (#55060) The Glyph color and stroke property are only required for stroked text or COLR text, in all other cases its a no-op. We can do the text hashing faster if we let these properties be optional. ~Also as an experiment switches to absl containers which should be more reasonably performant than std containers.~ required more changes, will try again later --- impeller/entity/contents/text_contents.cc | 7 +- .../backends/skia/typographer_context_skia.cc | 25 +++-- impeller/typographer/font_glyph_pair.h | 102 ++++++++++-------- impeller/typographer/glyph_atlas.h | 12 ++- impeller/typographer/text_frame.cc | 6 +- impeller/typographer/text_run.h | 2 +- 6 files changed, 90 insertions(+), 64 deletions(-) diff --git a/impeller/entity/contents/text_contents.cc b/impeller/entity/contents/text_contents.cc index 6dde4e65b6d7e..8254902e8df09 100644 --- a/impeller/entity/contents/text_contents.cc +++ b/impeller/entity/contents/text_contents.cc @@ -222,8 +222,11 @@ bool TextContents::Render(const ContentContext& renderer, Point subpixel = TextFrame::ComputeSubpixelPosition( glyph_position, font.GetAxisAlignment(), offset_, scale_); std::optional> maybe_atlas_glyph_bounds = - font_atlas->FindGlyphBounds( - SubpixelGlyph{glyph_position.glyph, subpixel, properties_}); + font_atlas->FindGlyphBounds(SubpixelGlyph{ + glyph_position.glyph, subpixel, + (properties_.stroke || frame_->HasColor()) + ? std::optional(properties_) + : std::nullopt}); if (!maybe_atlas_glyph_bounds.has_value()) { VALIDATION_LOG << "Could not find glyph position in the atlas."; continue; diff --git a/impeller/typographer/backends/skia/typographer_context_skia.cc b/impeller/typographer/backends/skia/typographer_context_skia.cc index 653377315c5c4..a86e2ee6b8b41 100644 --- a/impeller/typographer/backends/skia/typographer_context_skia.cc +++ b/impeller/typographer/backends/skia/typographer_context_skia.cc @@ -208,7 +208,7 @@ static void DrawGlyph(SkCanvas* canvas, const ScaledFont& scaled_font, const SubpixelGlyph& glyph, const Rect& scaled_bounds, - const GlyphProperties& prop, + const std::optional& prop, bool has_color) { const auto& metrics = scaled_font.font.GetMetrics(); SkGlyphID glyph_id = glyph.glyph.index; @@ -222,18 +222,17 @@ static void DrawGlyph(SkCanvas* canvas, sk_font.setSubpixel(true); sk_font.setSize(sk_font.getSize() * scaled_font.scale); - auto glyph_color = - has_color ? glyph.properties.color.ToARGB() : SK_ColorBLACK; + auto glyph_color = prop.has_value() ? prop->color.ToARGB() : SK_ColorBLACK; SkPaint glyph_paint; glyph_paint.setColor(glyph_color); glyph_paint.setBlendMode(SkBlendMode::kSrc); - if (prop.stroke) { + if (prop.has_value() && prop->stroke) { glyph_paint.setStroke(true); - glyph_paint.setStrokeWidth(prop.stroke_width * scaled_font.scale); - glyph_paint.setStrokeCap(ToSkiaCap(glyph.properties.stroke_cap)); - glyph_paint.setStrokeJoin(ToSkiaJoin(glyph.properties.stroke_join)); - glyph_paint.setStrokeMiter(prop.stroke_miter * scaled_font.scale); + glyph_paint.setStrokeWidth(prop->stroke_width * scaled_font.scale); + glyph_paint.setStrokeCap(ToSkiaCap(prop->stroke_cap)); + glyph_paint.setStrokeJoin(ToSkiaJoin(prop->stroke_join)); + glyph_paint.setStrokeMiter(prop->stroke_miter * scaled_font.scale); } canvas->save(); canvas->translate(glyph.subpixel_offset.x, glyph.subpixel_offset.y); @@ -384,12 +383,12 @@ static Rect ComputeGlyphSize(const SkFont& font, Scalar scale) { SkRect scaled_bounds; SkPaint glyph_paint; - if (glyph.properties.stroke) { + if (glyph.properties.has_value() && glyph.properties->stroke) { glyph_paint.setStroke(true); - glyph_paint.setStrokeWidth(glyph.properties.stroke_width * scale); - glyph_paint.setStrokeCap(ToSkiaCap(glyph.properties.stroke_cap)); - glyph_paint.setStrokeJoin(ToSkiaJoin(glyph.properties.stroke_join)); - glyph_paint.setStrokeMiter(glyph.properties.stroke_miter * scale); + glyph_paint.setStrokeWidth(glyph.properties->stroke_width * scale); + glyph_paint.setStrokeCap(ToSkiaCap(glyph.properties->stroke_cap)); + glyph_paint.setStrokeJoin(ToSkiaJoin(glyph.properties->stroke_join)); + glyph_paint.setStrokeMiter(glyph.properties->stroke_miter * scale); } font.getBounds(&glyph.glyph.index, 1, &scaled_bounds, &glyph_paint); diff --git a/impeller/typographer/font_glyph_pair.h b/impeller/typographer/font_glyph_pair.h index c5d3c508f4994..d4c12fa4195ee 100644 --- a/impeller/typographer/font_glyph_pair.h +++ b/impeller/typographer/font_glyph_pair.h @@ -8,6 +8,7 @@ #include #include +#include "fml/hash_combine.h" #include "impeller/geometry/color.h" #include "impeller/geometry/path.h" #include "impeller/geometry/point.h" @@ -32,6 +33,19 @@ struct GlyphProperties { struct ScaledFont { Font font; Scalar scale; + + struct Hash { + constexpr std::size_t operator()(const impeller::ScaledFont& sf) const { + return fml::HashCombine(sf.font.GetHash(), sf.scale); + } + }; + + struct Equal { + constexpr bool operator()(const impeller::ScaledFont& lhs, + const impeller::ScaledFont& rhs) const { + return lhs.font.IsEqual(rhs.font) && lhs.scale == rhs.scale; + } + }; }; //------------------------------------------------------------------------------ @@ -40,18 +54,58 @@ struct ScaledFont { struct SubpixelGlyph { Glyph glyph; Point subpixel_offset; - GlyphProperties properties; + std::optional properties; SubpixelGlyph(Glyph p_glyph, Point p_subpixel_offset, - GlyphProperties p_properties) + std::optional p_properties) : glyph(p_glyph), subpixel_offset(p_subpixel_offset), properties(p_properties) {} + + struct Hash { + constexpr std::size_t operator()(const impeller::SubpixelGlyph& sg) const { + if (!sg.properties.has_value()) { + return fml::HashCombine(sg.glyph.index, sg.subpixel_offset.x, + sg.subpixel_offset.y); + } + return fml::HashCombine( + sg.glyph.index, sg.subpixel_offset.x, sg.subpixel_offset.y, + sg.properties->color.ToARGB(), sg.properties->stroke, + sg.properties->stroke_cap, sg.properties->stroke_join, + sg.properties->stroke_miter, sg.properties->stroke_width); + } + }; + + struct Equal { + constexpr bool operator()(const impeller::SubpixelGlyph& lhs, + const impeller::SubpixelGlyph& rhs) const { + if (!lhs.properties.has_value() && !rhs.properties.has_value()) { + return lhs.glyph.index == rhs.glyph.index && + lhs.glyph.type == rhs.glyph.type && + lhs.subpixel_offset == rhs.subpixel_offset; + } + return lhs.glyph.index == rhs.glyph.index && + lhs.glyph.type == rhs.glyph.type && + lhs.subpixel_offset == rhs.subpixel_offset && + lhs.properties.has_value() && rhs.properties.has_value() && + lhs.properties->color.ToARGB() == rhs.properties->color.ToARGB() && + lhs.properties->stroke == rhs.properties->stroke && + lhs.properties->stroke_cap == rhs.properties->stroke_cap && + lhs.properties->stroke_join == rhs.properties->stroke_join && + lhs.properties->stroke_miter == rhs.properties->stroke_miter && + lhs.properties->stroke_width == rhs.properties->stroke_width; + } + }; }; using FontGlyphMap = - std::unordered_map>; + std::unordered_map, + ScaledFont::Hash, + ScaledFont::Equal>; //------------------------------------------------------------------------------ /// @brief A font along with a glyph in that font rendered at a particular @@ -66,46 +120,4 @@ struct FontGlyphPair { } // namespace impeller -template <> -struct std::hash { - constexpr std::size_t operator()(const impeller::ScaledFont& sf) const { - return fml::HashCombine(sf.font.GetHash(), sf.scale); - } -}; - -template <> -struct std::equal_to { - constexpr bool operator()(const impeller::ScaledFont& lhs, - const impeller::ScaledFont& rhs) const { - return lhs.font.IsEqual(rhs.font) && lhs.scale == rhs.scale; - } -}; - -template <> -struct std::hash { - constexpr std::size_t operator()(const impeller::SubpixelGlyph& sg) const { - return fml::HashCombine( - sg.glyph.index, sg.subpixel_offset.x, sg.subpixel_offset.y, - sg.properties.color.ToARGB(), sg.properties.stroke, - sg.properties.stroke_cap, sg.properties.stroke_join, - sg.properties.stroke_miter, sg.properties.stroke_width); - } -}; - -template <> -struct std::equal_to { - constexpr bool operator()(const impeller::SubpixelGlyph& lhs, - const impeller::SubpixelGlyph& rhs) const { - return lhs.glyph.index == rhs.glyph.index && - lhs.glyph.type == rhs.glyph.type && - lhs.subpixel_offset == rhs.subpixel_offset && - lhs.properties.color.ToARGB() == rhs.properties.color.ToARGB() && - lhs.properties.stroke == rhs.properties.stroke && - lhs.properties.stroke_cap == rhs.properties.stroke_cap && - lhs.properties.stroke_join == rhs.properties.stroke_join && - lhs.properties.stroke_miter == rhs.properties.stroke_miter && - lhs.properties.stroke_width == rhs.properties.stroke_width; - } -}; - #endif // FLUTTER_IMPELLER_TYPOGRAPHER_FONT_GLYPH_PAIR_H_ diff --git a/impeller/typographer/glyph_atlas.h b/impeller/typographer/glyph_atlas.h index b94fe90e6a59b..587ece46f045e 100644 --- a/impeller/typographer/glyph_atlas.h +++ b/impeller/typographer/glyph_atlas.h @@ -136,7 +136,11 @@ class GlyphAtlas { const Type type_; std::shared_ptr texture_; - std::unordered_map font_atlas_map_; + std::unordered_map + font_atlas_map_; GlyphAtlas(const GlyphAtlas&) = delete; @@ -214,7 +218,11 @@ class FontGlyphAtlas { private: friend class GlyphAtlas; - std::unordered_map> positions_; + std::unordered_map, + SubpixelGlyph::Hash, + SubpixelGlyph::Equal> + positions_; FontGlyphAtlas(const FontGlyphAtlas&) = delete; diff --git a/impeller/typographer/text_frame.cc b/impeller/typographer/text_frame.cc index a0ce36d683a23..b715616fb4d87 100644 --- a/impeller/typographer/text_frame.cc +++ b/impeller/typographer/text_frame.cc @@ -89,6 +89,10 @@ void TextFrame::CollectUniqueFontGlyphPairs( Scalar scale, Point offset, const GlyphProperties& properties) const { + std::optional lookup = + (properties.stroke || HasColor()) + ? std::optional(properties) + : std::nullopt; for (const TextRun& run : GetRuns()) { const Font& font = run.GetFont(); auto rounded_scale = @@ -98,7 +102,7 @@ void TextFrame::CollectUniqueFontGlyphPairs( run.GetGlyphPositions()) { Point subpixel = ComputeSubpixelPosition( glyph_position, font.GetAxisAlignment(), offset, scale); - set.emplace(glyph_position.glyph, subpixel, properties); + set.emplace(glyph_position.glyph, subpixel, lookup); } } } diff --git a/impeller/typographer/text_run.h b/impeller/typographer/text_run.h index 6bec4d19187a2..76da59ff6fcd6 100644 --- a/impeller/typographer/text_run.h +++ b/impeller/typographer/text_run.h @@ -7,7 +7,7 @@ #include -#include "impeller/geometry/matrix.h" +#include "impeller/geometry/point.h" #include "impeller/typographer/font.h" #include "impeller/typographer/glyph.h"