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", "../renderer",
] ]
if (!is_fuchsia) {
public_deps +=
[ "//flutter/third_party/abseil-cpp/absl/container:flat_hash_map" ]
}
deps = [ "//flutter/fml" ] deps = [ "//flutter/fml" ]
} }

View File

@ -41,11 +41,10 @@ struct ScaledFont {
Font font; Font font;
Scalar scale; Scalar scale;
struct Hash { template <typename H>
constexpr std::size_t operator()(const impeller::ScaledFont& sf) const { friend H AbslHashValue(H h, const ScaledFont& sf) {
return fml::HashCombine(sf.font.GetHash(), sf.scale); return H::combine(std::move(h), sf.font.GetHash(), sf.scale);
} }
};
struct Equal { struct Equal {
constexpr bool operator()(const impeller::ScaledFont& lhs, constexpr bool operator()(const impeller::ScaledFont& lhs,
@ -70,19 +69,18 @@ struct SubpixelGlyph {
subpixel_offset(p_subpixel_offset), subpixel_offset(p_subpixel_offset),
properties(p_properties) {} properties(p_properties) {}
struct Hash { template <typename H>
constexpr std::size_t operator()(const impeller::SubpixelGlyph& sg) const { friend H AbslHashValue(H h, const SubpixelGlyph& sg) {
if (!sg.properties.has_value()) { if (!sg.properties.has_value()) {
return fml::HashCombine(sg.glyph.index, sg.subpixel_offset.x, return H::combine(std::move(h), sg.glyph.index, sg.subpixel_offset.x,
sg.subpixel_offset.y); sg.subpixel_offset.y);
} }
return fml::HashCombine( return H::combine(std::move(h), sg.glyph.index, sg.subpixel_offset.x,
sg.glyph.index, sg.subpixel_offset.x, sg.subpixel_offset.y, sg.subpixel_offset.y, sg.properties->color.ToARGB(),
sg.properties->color.ToARGB(), sg.properties->stroke, sg.properties->stroke, sg.properties->stroke_cap,
sg.properties->stroke_cap, sg.properties->stroke_join, sg.properties->stroke_join, sg.properties->stroke_miter,
sg.properties->stroke_miter, sg.properties->stroke_width); sg.properties->stroke_width);
} }
};
struct Equal { struct Equal {
constexpr bool operator()(const impeller::SubpixelGlyph& lhs, 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, void GlyphAtlas::AddTypefaceGlyphPositionAndBounds(const FontGlyphPair& pair,
Rect position, Rect position,
Rect bounds) { 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}; FrameBounds{position, bounds, /*is_placeholder=*/false};
} }
@ -93,12 +95,9 @@ std::optional<FrameBounds> GlyphAtlas::FindFontGlyphBounds(
FontGlyphAtlas* GlyphAtlas::GetOrCreateFontGlyphAtlas( FontGlyphAtlas* GlyphAtlas::GetOrCreateFontGlyphAtlas(
const ScaledFont& scaled_font) { const ScaledFont& scaled_font) {
const auto& found = font_atlas_map_.find(scaled_font); auto [iter, inserted] =
if (found != font_atlas_map_.end()) { font_atlas_map_.try_emplace(scaled_font, FontGlyphAtlas());
return &found->second; return &iter->second;
}
font_atlas_map_[scaled_font] = FontGlyphAtlas();
return &font_atlas_map_[scaled_font];
} }
size_t GlyphAtlas::GetGlyphCount() const { size_t GlyphAtlas::GetGlyphCount() const {

View File

@ -8,7 +8,16 @@
#include <functional> #include <functional>
#include <memory> #include <memory>
#include <optional> #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/core/texture.h"
#include "impeller/geometry/rect.h" #include "impeller/geometry/rect.h"
@ -19,6 +28,30 @@ namespace impeller {
class FontGlyphAtlas; 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 { struct FrameBounds {
/// The bounds of the glyph within the glyph atlas. /// The bounds of the glyph within the glyph atlas.
Rect atlas_bounds; Rect atlas_bounds;
@ -160,11 +193,19 @@ class GlyphAtlas {
std::shared_ptr<Texture> texture_; std::shared_ptr<Texture> texture_;
size_t generation_ = 0; size_t generation_ = 0;
std::unordered_map<ScaledFont, #if defined(IMPELLER_TYPOGRAPHER_USE_STD_HASH)
using FontAtlasMap = std::unordered_map<ScaledFont,
FontGlyphAtlas, FontGlyphAtlas,
ScaledFont::Hash, AbslHashAdapter<ScaledFont>,
ScaledFont::Equal> ScaledFont::Equal>;
font_atlas_map_; #else
using FontAtlasMap = absl::flat_hash_map<ScaledFont,
FontGlyphAtlas,
absl::Hash<ScaledFont>,
ScaledFont::Equal>;
#endif
FontAtlasMap font_atlas_map_;
GlyphAtlas(const GlyphAtlas&) = delete; GlyphAtlas(const GlyphAtlas&) = delete;
@ -228,6 +269,7 @@ class GlyphAtlasContext {
class FontGlyphAtlas { class FontGlyphAtlas {
public: public:
FontGlyphAtlas() = default; FontGlyphAtlas() = default;
FontGlyphAtlas(FontGlyphAtlas&&) = default;
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
/// @brief Find the location of a glyph in the atlas. /// @brief Find the location of a glyph in the atlas.
@ -249,12 +291,19 @@ class FontGlyphAtlas {
private: private:
friend class GlyphAtlas; friend class GlyphAtlas;
std::unordered_map<SubpixelGlyph, #if defined(IMPELLER_TYPOGRAPHER_USE_STD_HASH)
using PositionsMap = std::unordered_map<SubpixelGlyph,
FrameBounds, FrameBounds,
SubpixelGlyph::Hash, AbslHashAdapter<SubpixelGlyph>,
SubpixelGlyph::Equal> SubpixelGlyph::Equal>;
positions_; #else
using PositionsMap = absl::flat_hash_map<SubpixelGlyph,
FrameBounds,
absl::Hash<SubpixelGlyph>,
SubpixelGlyph::Equal>;
#endif
PositionsMap positions_;
FontGlyphAtlas(const FontGlyphAtlas&) = delete; FontGlyphAtlas(const FontGlyphAtlas&) = delete;
}; };