Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 2ddbff9

Browse files
[Impeller] Append to existing atlas if room exists, reuse texture (#38253)
* [impeller][wip] append to existing atlas if room exists * cleanups and reuse texture * ++ * ++ * ++ * ++ * dnfield review * ++ * ++ * ++
1 parent e1b265b commit 2ddbff9

File tree

4 files changed

+277
-67
lines changed

4 files changed

+277
-67
lines changed

impeller/typographer/backends/skia/text_render_context_skia.cc

Lines changed: 184 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@
2222

2323
namespace impeller {
2424

25+
// TODO(bdero): We might be able to remove this per-glyph padding if we fix
26+
// the underlying causes of the overlap.
27+
// https://github.com/flutter/flutter/issues/114563
28+
constexpr auto kPadding = 2;
29+
2530
TextRenderContextSkia::TextRenderContextSkia(std::shared_ptr<Context> context)
2631
: TextRenderContext(std::move(context)) {}
2732

@@ -58,33 +63,27 @@ static FontGlyphPair::Vector CollectUniqueFontGlyphPairs(
5863
return vector;
5964
}
6065

61-
static size_t PairsFitInAtlasOfSize(const FontGlyphPair::Vector& pairs,
62-
const ISize& atlas_size,
63-
std::vector<Rect>& glyph_positions) {
66+
static size_t PairsFitInAtlasOfSize(
67+
const FontGlyphPair::Vector& pairs,
68+
const ISize& atlas_size,
69+
std::vector<Rect>& glyph_positions,
70+
const std::shared_ptr<GrRectanizer>& rect_packer) {
6471
if (atlas_size.IsEmpty()) {
6572
return false;
6673
}
6774

68-
auto rect_packer = std::unique_ptr<GrRectanizer>(
69-
GrRectanizer::Factory(atlas_size.width, atlas_size.height));
70-
7175
glyph_positions.clear();
7276
glyph_positions.reserve(pairs.size());
7377

74-
// TODO(bdero): We might be able to remove this per-glyph padding if we fix
75-
// the underlying causes of the overlap.
76-
// https://github.com/flutter/flutter/issues/114563
77-
constexpr auto padding = 2;
78-
7978
for (size_t i = 0; i < pairs.size(); i++) {
8079
const auto& pair = pairs[i];
8180

8281
const auto glyph_size =
8382
ISize::Ceil((pair.glyph.bounds * pair.font.GetMetrics().scale).size);
8483
SkIPoint16 location_in_atlas;
85-
if (!rect_packer->addRect(glyph_size.width + padding, //
86-
glyph_size.height + padding, //
87-
&location_in_atlas //
84+
if (!rect_packer->addRect(glyph_size.width + kPadding, //
85+
glyph_size.height + kPadding, //
86+
&location_in_atlas //
8887
)) {
8988
return pairs.size() - i;
9089
}
@@ -98,9 +97,48 @@ static size_t PairsFitInAtlasOfSize(const FontGlyphPair::Vector& pairs,
9897
return 0;
9998
}
10099

100+
static bool CanAppendToExistingAtlas(
101+
const std::shared_ptr<GlyphAtlas>& atlas,
102+
const FontGlyphPair::Vector& extra_pairs,
103+
std::vector<Rect>& glyph_positions,
104+
ISize atlas_size,
105+
const std::shared_ptr<GrRectanizer>& rect_packer) {
106+
TRACE_EVENT0("impeller", __FUNCTION__);
107+
if (!rect_packer || atlas_size.IsEmpty()) {
108+
return false;
109+
}
110+
111+
// We assume that all existing glyphs will fit. After all, they fit before.
112+
// The glyph_positions only contains the values for the additional glyphs
113+
// from extra_pairs.
114+
FML_DCHECK(glyph_positions.size() == 0);
115+
glyph_positions.reserve(extra_pairs.size());
116+
for (size_t i = 0; i < extra_pairs.size(); i++) {
117+
const auto& pair = extra_pairs[i];
118+
119+
const auto glyph_size =
120+
ISize::Ceil((pair.glyph.bounds * pair.font.GetMetrics().scale).size);
121+
SkIPoint16 location_in_atlas;
122+
if (!rect_packer->addRect(glyph_size.width + kPadding, //
123+
glyph_size.height + kPadding, //
124+
&location_in_atlas //
125+
)) {
126+
return false;
127+
}
128+
glyph_positions.emplace_back(Rect::MakeXYWH(location_in_atlas.x(), //
129+
location_in_atlas.y(), //
130+
glyph_size.width, //
131+
glyph_size.height //
132+
));
133+
}
134+
135+
return true;
136+
}
137+
101138
static ISize OptimumAtlasSizeForFontGlyphPairs(
102139
const FontGlyphPair::Vector& pairs,
103-
std::vector<Rect>& glyph_positions) {
140+
std::vector<Rect>& glyph_positions,
141+
const std::shared_ptr<GlyphAtlasContext>& atlas_context) {
104142
static constexpr auto kMinAtlasSize = 8u;
105143
static constexpr auto kMaxAtlasSize = 4096u;
106144

@@ -109,9 +147,13 @@ static ISize OptimumAtlasSizeForFontGlyphPairs(
109147
ISize current_size(kMinAtlasSize, kMinAtlasSize);
110148
size_t total_pairs = pairs.size() + 1;
111149
do {
112-
auto remaining_pairs =
113-
PairsFitInAtlasOfSize(pairs, current_size, glyph_positions);
150+
auto rect_packer = std::shared_ptr<GrRectanizer>(
151+
GrRectanizer::Factory(current_size.width, current_size.height));
152+
153+
auto remaining_pairs = PairsFitInAtlasOfSize(pairs, current_size,
154+
glyph_positions, rect_packer);
114155
if (remaining_pairs == 0) {
156+
atlas_context->UpdateRectPacker(rect_packer);
115157
return current_size;
116158
} else if (remaining_pairs < std::ceil(total_pairs / 2)) {
117159
current_size = ISize::MakeWH(
@@ -243,6 +285,59 @@ static void ConvertBitmapToSignedDistanceField(uint8_t* pixels,
243285
#undef nearestpt
244286
}
245287

288+
static void DrawGlyph(SkCanvas* canvas,
289+
const FontGlyphPair& font_glyph,
290+
const Rect& location) {
291+
const auto& metrics = font_glyph.font.GetMetrics();
292+
const auto position = SkPoint::Make(location.origin.x / metrics.scale,
293+
location.origin.y / metrics.scale);
294+
SkGlyphID glyph_id = font_glyph.glyph.index;
295+
296+
SkFont sk_font(
297+
TypefaceSkia::Cast(*font_glyph.font.GetTypeface()).GetSkiaTypeface(),
298+
metrics.point_size);
299+
auto glyph_color = SK_ColorWHITE;
300+
301+
SkPaint glyph_paint;
302+
glyph_paint.setColor(glyph_color);
303+
canvas->resetMatrix();
304+
canvas->scale(metrics.scale, metrics.scale);
305+
canvas->drawGlyphs(
306+
1u, // count
307+
&glyph_id, // glyphs
308+
&position, // positions
309+
SkPoint::Make(-font_glyph.glyph.bounds.GetLeft(),
310+
-font_glyph.glyph.bounds.GetTop()), // origin
311+
sk_font, // font
312+
glyph_paint // paint
313+
);
314+
}
315+
316+
static bool UpdateAtlasBitmap(const GlyphAtlas& atlas,
317+
const std::shared_ptr<SkBitmap>& bitmap,
318+
const FontGlyphPair::Vector& new_pairs) {
319+
TRACE_EVENT0("impeller", __FUNCTION__);
320+
FML_DCHECK(bitmap != nullptr);
321+
322+
auto surface = SkSurface::MakeRasterDirect(bitmap->pixmap());
323+
if (!surface) {
324+
return false;
325+
}
326+
auto canvas = surface->getCanvas();
327+
if (!canvas) {
328+
return false;
329+
}
330+
331+
for (const auto& pair : new_pairs) {
332+
auto pos = atlas.FindFontGlyphPosition(pair);
333+
if (!pos.has_value()) {
334+
continue;
335+
}
336+
DrawGlyph(canvas, pair, pos.value());
337+
}
338+
return true;
339+
}
340+
246341
static std::shared_ptr<SkBitmap> CreateAtlasBitmap(const GlyphAtlas& atlas,
247342
const ISize& atlas_size) {
248343
TRACE_EVENT0("impeller", __FUNCTION__);
@@ -263,6 +358,7 @@ static std::shared_ptr<SkBitmap> CreateAtlasBitmap(const GlyphAtlas& atlas,
263358
if (!bitmap->tryAllocPixels(image_info)) {
264359
return nullptr;
265360
}
361+
266362
auto surface = SkSurface::MakeRasterDirect(bitmap->pixmap());
267363
if (!surface) {
268364
return nullptr;
@@ -272,37 +368,31 @@ static std::shared_ptr<SkBitmap> CreateAtlasBitmap(const GlyphAtlas& atlas,
272368
return nullptr;
273369
}
274370

275-
atlas.IterateGlyphs([canvas](const FontGlyphPair& font_glyph,
276-
const Rect& location) -> bool {
277-
const auto& metrics = font_glyph.font.GetMetrics();
278-
const auto position = SkPoint::Make(location.origin.x / metrics.scale,
279-
location.origin.y / metrics.scale);
280-
SkGlyphID glyph_id = font_glyph.glyph.index;
281-
282-
SkFont sk_font(
283-
TypefaceSkia::Cast(*font_glyph.font.GetTypeface()).GetSkiaTypeface(),
284-
metrics.point_size);
285-
auto glyph_color = SK_ColorWHITE;
286-
287-
SkPaint glyph_paint;
288-
glyph_paint.setColor(glyph_color);
289-
canvas->resetMatrix();
290-
canvas->scale(metrics.scale, metrics.scale);
291-
canvas->drawGlyphs(
292-
1u, // count
293-
&glyph_id, // glyphs
294-
&position, // positions
295-
SkPoint::Make(-font_glyph.glyph.bounds.GetLeft(),
296-
-font_glyph.glyph.bounds.GetTop()), // origin
297-
sk_font, // font
298-
glyph_paint // paint
299-
);
300-
return true;
301-
});
371+
atlas.IterateGlyphs(
372+
[canvas](const FontGlyphPair& font_glyph, const Rect& location) -> bool {
373+
DrawGlyph(canvas, font_glyph, location);
374+
return true;
375+
});
302376

303377
return bitmap;
304378
}
305379

380+
static bool UpdateGlyphTextureAtlas(std::shared_ptr<SkBitmap> bitmap,
381+
const std::shared_ptr<Texture>& texture) {
382+
TRACE_EVENT0("impeller", __FUNCTION__);
383+
384+
FML_DCHECK(bitmap != nullptr);
385+
auto texture_descriptor = texture->GetTextureDescriptor();
386+
387+
auto mapping = std::make_shared<fml::NonOwnedMapping>(
388+
reinterpret_cast<const uint8_t*>(bitmap->getAddr(0, 0)), // data
389+
texture_descriptor.GetByteSizeOfBaseMipLevel(), // size
390+
[bitmap](auto, auto) mutable { bitmap.reset(); } // proc
391+
);
392+
393+
return texture->SetContents(mapping);
394+
}
395+
306396
static std::shared_ptr<Texture> UploadGlyphTextureAtlas(
307397
const std::shared_ptr<Allocator>& allocator,
308398
std::shared_ptr<SkBitmap> bitmap,
@@ -367,26 +457,61 @@ std::shared_ptr<GlyphAtlas> TextRenderContextSkia::CreateGlyphAtlas(
367457
// Step 2: Determine if the atlas type and font glyph pairs are compatible
368458
// with the current atlas and reuse if possible.
369459
// ---------------------------------------------------------------------------
370-
if (last_atlas->GetType() == type &&
371-
last_atlas->HasSamePairs(font_glyph_pairs)) {
460+
auto new_glyphs = last_atlas->HasSamePairs(font_glyph_pairs);
461+
if (last_atlas->GetType() == type && new_glyphs.size() == 0) {
372462
return last_atlas;
373463
}
374464

375-
auto glyph_atlas = std::make_shared<GlyphAtlas>(type);
376-
atlas_context->UpdateGlyphAtlas(glyph_atlas);
377-
378465
// ---------------------------------------------------------------------------
379-
// Step 3: Get the optimum size of the texture atlas.
466+
// Step 3: Determine if the additional missing glyphs can be appended to the
467+
// existing bitmap without recreating the atlas.
380468
// ---------------------------------------------------------------------------
381469
std::vector<Rect> glyph_positions;
382-
const auto atlas_size =
383-
OptimumAtlasSizeForFontGlyphPairs(font_glyph_pairs, glyph_positions);
470+
if (CanAppendToExistingAtlas(last_atlas, new_glyphs, glyph_positions,
471+
atlas_context->GetAtlasSize(),
472+
atlas_context->GetRectPacker())) {
473+
// The old bitmap will be reused and only the additional glyphs will be
474+
// added.
475+
476+
// ---------------------------------------------------------------------------
477+
// Step 4: Record the positions in the glyph atlas of the newly added
478+
// glyphs.
479+
// ---------------------------------------------------------------------------
480+
for (size_t i = 0, count = glyph_positions.size(); i < count; i++) {
481+
last_atlas->AddTypefaceGlyphPosition(new_glyphs[i], glyph_positions[i]);
482+
}
483+
484+
// ---------------------------------------------------------------------------
485+
// Step 5: Draw new font-glyph pairs into the existing bitmap.
486+
// ---------------------------------------------------------------------------
487+
auto bitmap = atlas_context->GetBitmap();
488+
if (!UpdateAtlasBitmap(*last_atlas, bitmap, new_glyphs)) {
489+
return nullptr;
490+
}
491+
492+
// ---------------------------------------------------------------------------
493+
// Step 6: Update the existing texture with the updated bitmap.
494+
// ---------------------------------------------------------------------------
495+
if (!UpdateGlyphTextureAtlas(bitmap, last_atlas->GetTexture())) {
496+
return nullptr;
497+
}
498+
return last_atlas;
499+
}
500+
// A new glyph atlas must be created.
501+
502+
// ---------------------------------------------------------------------------
503+
// Step 4: Get the optimum size of the texture atlas.
504+
// ---------------------------------------------------------------------------
505+
auto glyph_atlas = std::make_shared<GlyphAtlas>(type);
506+
auto atlas_size = OptimumAtlasSizeForFontGlyphPairs(
507+
font_glyph_pairs, glyph_positions, atlas_context);
508+
509+
atlas_context->UpdateGlyphAtlas(glyph_atlas, atlas_size);
384510
if (atlas_size.IsEmpty()) {
385511
return nullptr;
386512
}
387-
388513
// ---------------------------------------------------------------------------
389-
// Step 4: Find location of font-glyph pairs in the atlas. We have this from
514+
// Step 5: Find location of font-glyph pairs in the atlas. We have this from
390515
// the last step. So no need to do create another rect packer. But just do a
391516
// sanity check of counts. This could also be just an assertion as only a
392517
// construction issue would cause such a failure.
@@ -396,23 +521,24 @@ std::shared_ptr<GlyphAtlas> TextRenderContextSkia::CreateGlyphAtlas(
396521
}
397522

398523
// ---------------------------------------------------------------------------
399-
// Step 5: Record the positions in the glyph atlas.
524+
// Step 6: Record the positions in the glyph atlas.
400525
// ---------------------------------------------------------------------------
401526
for (size_t i = 0, count = glyph_positions.size(); i < count; i++) {
402527
glyph_atlas->AddTypefaceGlyphPosition(font_glyph_pairs[i],
403528
glyph_positions[i]);
404529
}
405530

406531
// ---------------------------------------------------------------------------
407-
// Step 6: Draw font-glyph pairs in the correct spot in the atlas.
532+
// Step 7: Draw font-glyph pairs in the correct spot in the atlas.
408533
// ---------------------------------------------------------------------------
409534
auto bitmap = CreateAtlasBitmap(*glyph_atlas, atlas_size);
410535
if (!bitmap) {
411536
return nullptr;
412537
}
538+
atlas_context->UpdateBitmap(bitmap);
413539

414540
// ---------------------------------------------------------------------------
415-
// Step 7: Upload the atlas as a texture.
541+
// Step 8: Upload the atlas as a texture.
416542
// ---------------------------------------------------------------------------
417543
PixelFormat format;
418544
switch (type) {
@@ -434,7 +560,7 @@ std::shared_ptr<GlyphAtlas> TextRenderContextSkia::CreateGlyphAtlas(
434560
}
435561

436562
// ---------------------------------------------------------------------------
437-
// Step 8: Record the texture in the glyph atlas.
563+
// Step 9: Record the texture in the glyph atlas.
438564
// ---------------------------------------------------------------------------
439565
glyph_atlas->SetTexture(std::move(texture));
440566

0 commit comments

Comments
 (0)