Moved font glyph atlas to flat_hash_map (flutter/engine#56847)

In https://github.com/flutter/engine/pull/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
This commit is contained in:
gaaclarke 2024-12-02 19:04:17 -08:00 committed by GitHub
parent 52504d1d77
commit 6d390bfaed
4 changed files with 86 additions and 35 deletions

View File

@ -34,6 +34,11 @@ impeller_component("typographer") {
"../renderer",
]
if (!is_fuchsia) {
public_deps +=
[ "//flutter/third_party/abseil-cpp/absl/container:flat_hash_map" ]
}
deps = [ "//flutter/fml" ]
}

View File

@ -41,11 +41,10 @@ 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);
}
};
template <typename H>
friend H AbslHashValue(H h, const ScaledFont& sf) {
return H::combine(std::move(h), sf.font.GetHash(), sf.scale);
}
struct Equal {
constexpr bool operator()(const impeller::ScaledFont& lhs,
@ -70,19 +69,18 @@ struct SubpixelGlyph {
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);
template <typename H>
friend H AbslHashValue(H h, const SubpixelGlyph& sg) {
if (!sg.properties.has_value()) {
return H::combine(std::move(h), sg.glyph.index, sg.subpixel_offset.x,
sg.subpixel_offset.y);
}
};
return H::combine(std::move(h), 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,

View File

@ -78,7 +78,9 @@ void GlyphAtlas::SetAtlasGeneration(size_t generation) {
void GlyphAtlas::AddTypefaceGlyphPositionAndBounds(const FontGlyphPair& pair,
Rect position,
Rect bounds) {
font_atlas_map_[pair.scaled_font].positions_[pair.glyph] =
FontAtlasMap::iterator it = font_atlas_map_.find(pair.scaled_font);
FML_DCHECK(it != font_atlas_map_.end());
it->second.positions_[pair.glyph] =
FrameBounds{position, bounds, /*is_placeholder=*/false};
}
@ -93,12 +95,9 @@ std::optional<FrameBounds> GlyphAtlas::FindFontGlyphBounds(
FontGlyphAtlas* GlyphAtlas::GetOrCreateFontGlyphAtlas(
const ScaledFont& scaled_font) {
const auto& found = font_atlas_map_.find(scaled_font);
if (found != font_atlas_map_.end()) {
return &found->second;
}
font_atlas_map_[scaled_font] = FontGlyphAtlas();
return &font_atlas_map_[scaled_font];
auto [iter, inserted] =
font_atlas_map_.try_emplace(scaled_font, FontGlyphAtlas());
return &iter->second;
}
size_t GlyphAtlas::GetGlyphCount() const {

View File

@ -8,7 +8,16 @@
#include <functional>
#include <memory>
#include <optional>
#include <unordered_map>
#include "flutter/fml/build_config.h"
#if defined(OS_FUCHSIA)
// TODO(gaaclarke): Migrate to use absl. I couldn't get it working since absl
// has special logic in its GN files for Fuchsia that I couldn't sort out.
#define IMPELLER_TYPOGRAPHER_USE_STD_HASH
#else
#include "flutter/third_party/abseil-cpp/absl/container/flat_hash_map.h"
#endif
#include "impeller/core/texture.h"
#include "impeller/geometry/rect.h"
@ -19,6 +28,30 @@ namespace impeller {
class FontGlyphAtlas;
/// Helper for AbslHashAdapter. Tallies a hash value with fml::HashCombine.
template <typename T>
struct AbslHashAdapterCombiner {
std::size_t value = 0;
template <typename... Args>
static AbslHashAdapterCombiner combine(AbslHashAdapterCombiner combiner,
const Args&... args) {
combiner.value = fml::HashCombine(combiner.value, args...);
return combiner;
}
};
/// Adapts AbslHashValue functions to be used with std::unordered_map and the
/// fml hash functions.
template <typename T>
struct AbslHashAdapter {
constexpr std::size_t operator()(const T& element) const {
AbslHashAdapterCombiner<T> combiner;
combiner = AbslHashValue(std::move(combiner), element);
return combiner.value;
}
};
struct FrameBounds {
/// The bounds of the glyph within the glyph atlas.
Rect atlas_bounds;
@ -160,11 +193,19 @@ class GlyphAtlas {
std::shared_ptr<Texture> texture_;
size_t generation_ = 0;
std::unordered_map<ScaledFont,
FontGlyphAtlas,
ScaledFont::Hash,
ScaledFont::Equal>
font_atlas_map_;
#if defined(IMPELLER_TYPOGRAPHER_USE_STD_HASH)
using FontAtlasMap = std::unordered_map<ScaledFont,
FontGlyphAtlas,
AbslHashAdapter<ScaledFont>,
ScaledFont::Equal>;
#else
using FontAtlasMap = absl::flat_hash_map<ScaledFont,
FontGlyphAtlas,
absl::Hash<ScaledFont>,
ScaledFont::Equal>;
#endif
FontAtlasMap font_atlas_map_;
GlyphAtlas(const GlyphAtlas&) = delete;
@ -228,6 +269,7 @@ class GlyphAtlasContext {
class FontGlyphAtlas {
public:
FontGlyphAtlas() = default;
FontGlyphAtlas(FontGlyphAtlas&&) = default;
//----------------------------------------------------------------------------
/// @brief Find the location of a glyph in the atlas.
@ -249,12 +291,19 @@ class FontGlyphAtlas {
private:
friend class GlyphAtlas;
std::unordered_map<SubpixelGlyph,
FrameBounds,
SubpixelGlyph::Hash,
SubpixelGlyph::Equal>
positions_;
#if defined(IMPELLER_TYPOGRAPHER_USE_STD_HASH)
using PositionsMap = std::unordered_map<SubpixelGlyph,
FrameBounds,
AbslHashAdapter<SubpixelGlyph>,
SubpixelGlyph::Equal>;
#else
using PositionsMap = absl::flat_hash_map<SubpixelGlyph,
FrameBounds,
absl::Hash<SubpixelGlyph>,
SubpixelGlyph::Equal>;
#endif
PositionsMap positions_;
FontGlyphAtlas(const FontGlyphAtlas&) = delete;
};