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

Commit 089071a

Browse files
authored
Moved font glyph atlas to flat_hash_map (#56847)
In #56844 we saw a 15% increase in performance by switching to absl::flat_hash_map. I suspect we say see even better results ([source](https://martin.ankerl.com/2022/08/27/hashmap-bench-01)). The absl guidance is to default to using this. test exempt: no intentional functional change, just performance [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
1 parent 349ad27 commit 089071a

File tree

4 files changed

+87
-36
lines changed

4 files changed

+87
-36
lines changed

impeller/typographer/BUILD.gn

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ impeller_component("typographer") {
3434
"../renderer",
3535
]
3636

37+
if (!is_fuchsia) {
38+
public_deps +=
39+
[ "//flutter/third_party/abseil-cpp/absl/container:flat_hash_map" ]
40+
}
41+
3742
deps = [ "//flutter/fml" ]
3843
}
3944

impeller/typographer/font_glyph_pair.h

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,10 @@ struct ScaledFont {
4141
Font font;
4242
Scalar scale;
4343

44-
struct Hash {
45-
constexpr std::size_t operator()(const impeller::ScaledFont& sf) const {
46-
return fml::HashCombine(sf.font.GetHash(), sf.scale);
47-
}
48-
};
44+
template <typename H>
45+
friend H AbslHashValue(H h, const ScaledFont& sf) {
46+
return H::combine(std::move(h), sf.font.GetHash(), sf.scale);
47+
}
4948

5049
struct Equal {
5150
constexpr bool operator()(const impeller::ScaledFont& lhs,
@@ -70,19 +69,18 @@ struct SubpixelGlyph {
7069
subpixel_offset(p_subpixel_offset),
7170
properties(p_properties) {}
7271

73-
struct Hash {
74-
constexpr std::size_t operator()(const impeller::SubpixelGlyph& sg) const {
75-
if (!sg.properties.has_value()) {
76-
return fml::HashCombine(sg.glyph.index, sg.subpixel_offset.x,
77-
sg.subpixel_offset.y);
78-
}
79-
return fml::HashCombine(
80-
sg.glyph.index, sg.subpixel_offset.x, sg.subpixel_offset.y,
81-
sg.properties->color.ToARGB(), sg.properties->stroke,
82-
sg.properties->stroke_cap, sg.properties->stroke_join,
83-
sg.properties->stroke_miter, sg.properties->stroke_width);
72+
template <typename H>
73+
friend H AbslHashValue(H h, const SubpixelGlyph& sg) {
74+
if (!sg.properties.has_value()) {
75+
return H::combine(std::move(h), sg.glyph.index, sg.subpixel_offset.x,
76+
sg.subpixel_offset.y);
8477
}
85-
};
78+
return H::combine(std::move(h), sg.glyph.index, sg.subpixel_offset.x,
79+
sg.subpixel_offset.y, sg.properties->color.ToARGB(),
80+
sg.properties->stroke, sg.properties->stroke_cap,
81+
sg.properties->stroke_join, sg.properties->stroke_miter,
82+
sg.properties->stroke_width);
83+
}
8684

8785
struct Equal {
8886
constexpr bool operator()(const impeller::SubpixelGlyph& lhs,

impeller/typographer/glyph_atlas.cc

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,9 @@ void GlyphAtlas::SetAtlasGeneration(size_t generation) {
7878
void GlyphAtlas::AddTypefaceGlyphPositionAndBounds(const FontGlyphPair& pair,
7979
Rect position,
8080
Rect bounds) {
81-
font_atlas_map_[pair.scaled_font].positions_[pair.glyph] =
81+
FontAtlasMap::iterator it = font_atlas_map_.find(pair.scaled_font);
82+
FML_DCHECK(it != font_atlas_map_.end());
83+
it->second.positions_[pair.glyph] =
8284
FrameBounds{position, bounds, /*is_placeholder=*/false};
8385
}
8486

@@ -93,12 +95,9 @@ std::optional<FrameBounds> GlyphAtlas::FindFontGlyphBounds(
9395

9496
FontGlyphAtlas* GlyphAtlas::GetOrCreateFontGlyphAtlas(
9597
const ScaledFont& scaled_font) {
96-
const auto& found = font_atlas_map_.find(scaled_font);
97-
if (found != font_atlas_map_.end()) {
98-
return &found->second;
99-
}
100-
font_atlas_map_[scaled_font] = FontGlyphAtlas();
101-
return &font_atlas_map_[scaled_font];
98+
auto [iter, inserted] =
99+
font_atlas_map_.try_emplace(scaled_font, FontGlyphAtlas());
100+
return &iter->second;
102101
}
103102

104103
size_t GlyphAtlas::GetGlyphCount() const {

impeller/typographer/glyph_atlas.h

Lines changed: 61 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,16 @@
88
#include <functional>
99
#include <memory>
1010
#include <optional>
11-
#include <unordered_map>
11+
12+
#include "flutter/fml/build_config.h"
13+
14+
#if defined(OS_FUCHSIA)
15+
// TODO(gaaclarke): Migrate to use absl. I couldn't get it working since absl
16+
// has special logic in its GN files for Fuchsia that I couldn't sort out.
17+
#define IMPELLER_TYPOGRAPHER_USE_STD_HASH
18+
#else
19+
#include "flutter/third_party/abseil-cpp/absl/container/flat_hash_map.h"
20+
#endif
1221

1322
#include "impeller/core/texture.h"
1423
#include "impeller/geometry/rect.h"
@@ -19,6 +28,30 @@ namespace impeller {
1928

2029
class FontGlyphAtlas;
2130

31+
/// Helper for AbslHashAdapter. Tallies a hash value with fml::HashCombine.
32+
template <typename T>
33+
struct AbslHashAdapterCombiner {
34+
std::size_t value = 0;
35+
36+
template <typename... Args>
37+
static AbslHashAdapterCombiner combine(AbslHashAdapterCombiner combiner,
38+
const Args&... args) {
39+
combiner.value = fml::HashCombine(combiner.value, args...);
40+
return combiner;
41+
}
42+
};
43+
44+
/// Adapts AbslHashValue functions to be used with std::unordered_map and the
45+
/// fml hash functions.
46+
template <typename T>
47+
struct AbslHashAdapter {
48+
constexpr std::size_t operator()(const T& element) const {
49+
AbslHashAdapterCombiner<T> combiner;
50+
combiner = AbslHashValue(std::move(combiner), element);
51+
return combiner.value;
52+
}
53+
};
54+
2255
struct FrameBounds {
2356
/// The bounds of the glyph within the glyph atlas.
2457
Rect atlas_bounds;
@@ -160,11 +193,19 @@ class GlyphAtlas {
160193
std::shared_ptr<Texture> texture_;
161194
size_t generation_ = 0;
162195

163-
std::unordered_map<ScaledFont,
164-
FontGlyphAtlas,
165-
ScaledFont::Hash,
166-
ScaledFont::Equal>
167-
font_atlas_map_;
196+
#if defined(IMPELLER_TYPOGRAPHER_USE_STD_HASH)
197+
using FontAtlasMap = std::unordered_map<ScaledFont,
198+
FontGlyphAtlas,
199+
AbslHashAdapter<ScaledFont>,
200+
ScaledFont::Equal>;
201+
#else
202+
using FontAtlasMap = absl::flat_hash_map<ScaledFont,
203+
FontGlyphAtlas,
204+
absl::Hash<ScaledFont>,
205+
ScaledFont::Equal>;
206+
#endif
207+
208+
FontAtlasMap font_atlas_map_;
168209

169210
GlyphAtlas(const GlyphAtlas&) = delete;
170211

@@ -228,6 +269,7 @@ class GlyphAtlasContext {
228269
class FontGlyphAtlas {
229270
public:
230271
FontGlyphAtlas() = default;
272+
FontGlyphAtlas(FontGlyphAtlas&&) = default;
231273

232274
//----------------------------------------------------------------------------
233275
/// @brief Find the location of a glyph in the atlas.
@@ -249,12 +291,19 @@ class FontGlyphAtlas {
249291
private:
250292
friend class GlyphAtlas;
251293

252-
std::unordered_map<SubpixelGlyph,
253-
FrameBounds,
254-
SubpixelGlyph::Hash,
255-
SubpixelGlyph::Equal>
256-
positions_;
257-
294+
#if defined(IMPELLER_TYPOGRAPHER_USE_STD_HASH)
295+
using PositionsMap = std::unordered_map<SubpixelGlyph,
296+
FrameBounds,
297+
AbslHashAdapter<SubpixelGlyph>,
298+
SubpixelGlyph::Equal>;
299+
#else
300+
using PositionsMap = absl::flat_hash_map<SubpixelGlyph,
301+
FrameBounds,
302+
absl::Hash<SubpixelGlyph>,
303+
SubpixelGlyph::Equal>;
304+
#endif
305+
306+
PositionsMap positions_;
258307
FontGlyphAtlas(const FontGlyphAtlas&) = delete;
259308
};
260309

0 commit comments

Comments
 (0)