Skip to content

Commit b63eee2

Browse files
[Impeller] Reorganize the glyph atlas to improve efficiency when looking up runs of glyphs in the same font (flutter#45191)
The atlas will now store a separate map of glyph positions for each font/scale pair. This avoids the cost of repeated hashing and copying of font objects for each glyph in a text run. See flutter#133201
1 parent 31d5662 commit b63eee2

15 files changed

+266
-166
lines changed

impeller/entity/contents/text_contents.cc

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -166,13 +166,17 @@ bool TextContents::Render(const ContentContext& renderer,
166166
const Font& font = run.GetFont();
167167
Scalar rounded_scale = TextFrame::RoundScaledFontSize(
168168
scale_, font.GetMetrics().point_size);
169+
const FontGlyphAtlas* font_atlas =
170+
atlas->GetFontGlyphAtlas(font, rounded_scale);
171+
if (!font_atlas) {
172+
VALIDATION_LOG << "Could not find font in the atlas.";
173+
continue;
174+
}
169175

170176
for (const TextRun::GlyphPosition& glyph_position :
171177
run.GetGlyphPositions()) {
172-
FontGlyphPair font_glyph_pair{font, glyph_position.glyph,
173-
rounded_scale};
174178
std::optional<Rect> maybe_atlas_glyph_bounds =
175-
atlas->FindFontGlyphBounds(font_glyph_pair);
179+
font_atlas->FindGlyphBounds(glyph_position.glyph);
176180
if (!maybe_atlas_glyph_bounds.has_value()) {
177181
VALIDATION_LOG << "Could not find glyph position in the atlas.";
178182
continue;

impeller/typographer/backends/skia/typographer_context_skia.cc

Lines changed: 55 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#include "impeller/typographer/backends/skia/typographer_context_skia.h"
66

7+
#include <numeric>
78
#include <utility>
89

910
#include "flutter/fml/logging.h"
@@ -21,9 +22,6 @@
2122

2223
namespace impeller {
2324

24-
using FontGlyphPairRefVector =
25-
std::vector<std::reference_wrapper<const FontGlyphPair>>;
26-
2725
// TODO(bdero): We might be able to remove this per-glyph padding if we fix
2826
// the underlying causes of the overlap.
2927
// https://github.com/flutter/flutter/issues/114563
@@ -43,7 +41,7 @@ TypographerContextSkia::CreateGlyphAtlasContext() const {
4341
}
4442

4543
static size_t PairsFitInAtlasOfSize(
46-
const FontGlyphPair::Set& pairs,
44+
const std::vector<FontGlyphPair>& pairs,
4745
const ISize& atlas_size,
4846
std::vector<Rect>& glyph_positions,
4947
const std::shared_ptr<RectanglePacker>& rect_packer) {
@@ -58,7 +56,8 @@ static size_t PairsFitInAtlasOfSize(
5856
for (auto it = pairs.begin(); it != pairs.end(); ++i, ++it) {
5957
const auto& pair = *it;
6058

61-
const auto glyph_size = ISize::Ceil(pair.glyph.bounds.size * pair.scale);
59+
const auto glyph_size =
60+
ISize::Ceil(pair.glyph.bounds.size * pair.scaled_font.scale);
6261
IPoint16 location_in_atlas;
6362
if (!rect_packer->addRect(glyph_size.width + kPadding, //
6463
glyph_size.height + kPadding, //
@@ -78,7 +77,7 @@ static size_t PairsFitInAtlasOfSize(
7877

7978
static bool CanAppendToExistingAtlas(
8079
const std::shared_ptr<GlyphAtlas>& atlas,
81-
const FontGlyphPairRefVector& extra_pairs,
80+
const std::vector<FontGlyphPair>& extra_pairs,
8281
std::vector<Rect>& glyph_positions,
8382
ISize atlas_size,
8483
const std::shared_ptr<RectanglePacker>& rect_packer) {
@@ -95,7 +94,8 @@ static bool CanAppendToExistingAtlas(
9594
for (size_t i = 0; i < extra_pairs.size(); i++) {
9695
const FontGlyphPair& pair = extra_pairs[i];
9796

98-
const auto glyph_size = ISize::Ceil(pair.glyph.bounds.size * pair.scale);
97+
const auto glyph_size =
98+
ISize::Ceil(pair.glyph.bounds.size * pair.scaled_font.scale);
9999
IPoint16 location_in_atlas;
100100
if (!rect_packer->addRect(glyph_size.width + kPadding, //
101101
glyph_size.height + kPadding, //
@@ -114,7 +114,7 @@ static bool CanAppendToExistingAtlas(
114114
}
115115

116116
static ISize OptimumAtlasSizeForFontGlyphPairs(
117-
const FontGlyphPair::Set& pairs,
117+
const std::vector<FontGlyphPair>& pairs,
118118
std::vector<Rect>& glyph_positions,
119119
const std::shared_ptr<GlyphAtlasContext>& atlas_context,
120120
GlyphAtlas::Type type) {
@@ -153,16 +153,17 @@ static ISize OptimumAtlasSizeForFontGlyphPairs(
153153
}
154154

155155
static void DrawGlyph(SkCanvas* canvas,
156-
const FontGlyphPair& font_glyph,
156+
const ScaledFont& scaled_font,
157+
const Glyph& glyph,
157158
const Rect& location,
158159
bool has_color) {
159-
const auto& metrics = font_glyph.font.GetMetrics();
160-
const auto position = SkPoint::Make(location.origin.x / font_glyph.scale,
161-
location.origin.y / font_glyph.scale);
162-
SkGlyphID glyph_id = font_glyph.glyph.index;
160+
const auto& metrics = scaled_font.font.GetMetrics();
161+
const auto position = SkPoint::Make(location.origin.x / scaled_font.scale,
162+
location.origin.y / scaled_font.scale);
163+
SkGlyphID glyph_id = glyph.index;
163164

164165
SkFont sk_font(
165-
TypefaceSkia::Cast(*font_glyph.font.GetTypeface()).GetSkiaTypeface(),
166+
TypefaceSkia::Cast(*scaled_font.font.GetTypeface()).GetSkiaTypeface(),
166167
metrics.point_size, metrics.scaleX, metrics.skewX);
167168
sk_font.setEdging(SkFont::Edging::kAntiAlias);
168169
sk_font.setHinting(SkFontHinting::kSlight);
@@ -173,21 +174,20 @@ static void DrawGlyph(SkCanvas* canvas,
173174
SkPaint glyph_paint;
174175
glyph_paint.setColor(glyph_color);
175176
canvas->resetMatrix();
176-
canvas->scale(font_glyph.scale, font_glyph.scale);
177-
canvas->drawGlyphs(
178-
1u, // count
179-
&glyph_id, // glyphs
180-
&position, // positions
181-
SkPoint::Make(-font_glyph.glyph.bounds.GetLeft(),
182-
-font_glyph.glyph.bounds.GetTop()), // origin
183-
sk_font, // font
184-
glyph_paint // paint
177+
canvas->scale(scaled_font.scale, scaled_font.scale);
178+
canvas->drawGlyphs(1u, // count
179+
&glyph_id, // glyphs
180+
&position, // positions
181+
SkPoint::Make(-glyph.bounds.GetLeft(),
182+
-glyph.bounds.GetTop()), // origin
183+
sk_font, // font
184+
glyph_paint // paint
185185
);
186186
}
187187

188188
static bool UpdateAtlasBitmap(const GlyphAtlas& atlas,
189189
const std::shared_ptr<SkBitmap>& bitmap,
190-
const FontGlyphPairRefVector& new_pairs) {
190+
const std::vector<FontGlyphPair>& new_pairs) {
191191
TRACE_EVENT0("impeller", __FUNCTION__);
192192
FML_DCHECK(bitmap != nullptr);
193193

@@ -207,7 +207,7 @@ static bool UpdateAtlasBitmap(const GlyphAtlas& atlas,
207207
if (!pos.has_value()) {
208208
continue;
209209
}
210-
DrawGlyph(canvas, pair, pos.value(), has_color);
210+
DrawGlyph(canvas, pair.scaled_font, pair.glyph, pos.value(), has_color);
211211
}
212212
return true;
213213
}
@@ -243,9 +243,10 @@ static std::shared_ptr<SkBitmap> CreateAtlasBitmap(const GlyphAtlas& atlas,
243243

244244
bool has_color = atlas.GetType() == GlyphAtlas::Type::kColorBitmap;
245245

246-
atlas.IterateGlyphs([canvas, has_color](const FontGlyphPair& font_glyph,
246+
atlas.IterateGlyphs([canvas, has_color](const ScaledFont& scaled_font,
247+
const Glyph& glyph,
247248
const Rect& location) -> bool {
248-
DrawGlyph(canvas, font_glyph, location, has_color);
249+
DrawGlyph(canvas, scaled_font, glyph, location, has_color);
249250
return true;
250251
});
251252

@@ -313,26 +314,37 @@ std::shared_ptr<GlyphAtlas> TypographerContextSkia::CreateGlyphAtlas(
313314
Context& context,
314315
GlyphAtlas::Type type,
315316
std::shared_ptr<GlyphAtlasContext> atlas_context,
316-
const FontGlyphPair::Set& font_glyph_pairs) const {
317+
const FontGlyphMap& font_glyph_map) const {
317318
TRACE_EVENT0("impeller", __FUNCTION__);
318319
if (!IsValid()) {
319320
return nullptr;
320321
}
321322
auto& atlas_context_skia = GlyphAtlasContextSkia::Cast(*atlas_context);
322323
std::shared_ptr<GlyphAtlas> last_atlas = atlas_context->GetGlyphAtlas();
323324

324-
if (font_glyph_pairs.empty()) {
325+
if (font_glyph_map.empty()) {
325326
return last_atlas;
326327
}
327328

328329
// ---------------------------------------------------------------------------
329330
// Step 1: Determine if the atlas type and font glyph pairs are compatible
330331
// with the current atlas and reuse if possible.
331332
// ---------------------------------------------------------------------------
332-
FontGlyphPairRefVector new_glyphs;
333-
for (const FontGlyphPair& pair : font_glyph_pairs) {
334-
if (!last_atlas->FindFontGlyphBounds(pair).has_value()) {
335-
new_glyphs.push_back(pair);
333+
std::vector<FontGlyphPair> new_glyphs;
334+
for (const auto& font_value : font_glyph_map) {
335+
const ScaledFont& scaled_font = font_value.first;
336+
const FontGlyphAtlas* font_glyph_atlas =
337+
last_atlas->GetFontGlyphAtlas(scaled_font.font, scaled_font.scale);
338+
if (font_glyph_atlas) {
339+
for (const Glyph& glyph : font_value.second) {
340+
if (!font_glyph_atlas->FindGlyphBounds(glyph)) {
341+
new_glyphs.emplace_back(scaled_font, glyph);
342+
}
343+
}
344+
} else {
345+
for (const Glyph& glyph : font_value.second) {
346+
new_glyphs.emplace_back(scaled_font, glyph);
347+
}
336348
}
337349
}
338350
if (last_atlas->GetType() == type && new_glyphs.size() == 0) {
@@ -381,6 +393,16 @@ std::shared_ptr<GlyphAtlas> TypographerContextSkia::CreateGlyphAtlas(
381393
// ---------------------------------------------------------------------------
382394
// Step 3b: Get the optimum size of the texture atlas.
383395
// ---------------------------------------------------------------------------
396+
std::vector<FontGlyphPair> font_glyph_pairs;
397+
font_glyph_pairs.reserve(std::accumulate(
398+
font_glyph_map.begin(), font_glyph_map.end(), 0,
399+
[](const int a, const auto& b) { return a + b.second.size(); }));
400+
for (const auto& font_value : font_glyph_map) {
401+
const ScaledFont& scaled_font = font_value.first;
402+
for (const Glyph& glyph : font_value.second) {
403+
font_glyph_pairs.push_back({scaled_font, glyph});
404+
}
405+
}
384406
auto glyph_atlas = std::make_shared<GlyphAtlas>(type);
385407
auto atlas_size = OptimumAtlasSizeForFontGlyphPairs(
386408
font_glyph_pairs, glyph_positions, atlas_context, type);

impeller/typographer/backends/skia/typographer_context_skia.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class TypographerContextSkia : public TypographerContext {
2525
Context& context,
2626
GlyphAtlas::Type type,
2727
std::shared_ptr<GlyphAtlasContext> atlas_context,
28-
const FontGlyphPair::Set& font_glyph_pairs) const override;
28+
const FontGlyphMap& font_glyph_map) const override;
2929

3030
private:
3131
FML_DISALLOW_COPY_AND_ASSIGN(TypographerContextSkia);

0 commit comments

Comments
 (0)