perf: avoid memory fragmentation on flex and glyph renderer

This commit is contained in:
Lemmy
2026-05-08 08:23:39 -04:00
parent 9475eb521e
commit b2d62c02a1
4 changed files with 62 additions and 17 deletions
+18 -15
View File
@@ -37,23 +37,17 @@ namespace {
void hashCombine(std::size_t& seed, std::size_t v) { seed ^= v + 0x9E3779B97F4A7C15ULL + (seed << 12) + (seed >> 4); }
cairo_scaled_font_t* create_scaled_font(cairo_font_face_t* face, float rasterSize) {
// Hinting is disabled for icons: tabler glyphs are monoline strokes with
// fractional widths by design. Autohinter snaps each stroke to the nearest
// integer pixel, which visibly thins the icons. Grayscale AA without
// hinting preserves the intended stroke thickness.
cairo_scaled_font_t* create_scaled_font(cairo_font_face_t* face, cairo_font_options_t* fontOptions,
float rasterSize) {
cairo_matrix_t fontMatrix;
cairo_matrix_init_scale(&fontMatrix, rasterSize, rasterSize);
cairo_matrix_t ctm;
cairo_matrix_init_identity(&ctm);
// Disable hinting for icons: tabler glyphs are monoline strokes with
// fractional widths by design. Autohinter snaps each stroke to the nearest
// integer pixel, which visibly thins the icons. Grayscale AA without
// hinting preserves the intended stroke thickness.
cairo_font_options_t* fontOptions = cairo_font_options_create();
cairo_font_options_set_antialias(fontOptions, CAIRO_ANTIALIAS_GRAY);
cairo_font_options_set_hint_style(fontOptions, CAIRO_HINT_STYLE_NONE);
cairo_font_options_set_hint_metrics(fontOptions, CAIRO_HINT_METRICS_OFF);
cairo_scaled_font_t* scaledFont = cairo_scaled_font_create(face, &fontMatrix, &ctm, fontOptions);
cairo_font_options_destroy(fontOptions);
return scaledFont;
return cairo_scaled_font_create(face, &fontMatrix, &ctm, fontOptions);
}
CairoGlyphRenderer::TextMetrics metrics_from_extents(const cairo_text_extents_t& extents, float invScale) {
@@ -100,6 +94,11 @@ void CairoGlyphRenderer::initialize(const std::string& fontPath, RenderBackend*
throw std::runtime_error("CairoGlyphRenderer: cairo_ft_font_face_create_for_ft_face failed");
}
m_fontOptions = cairo_font_options_create();
cairo_font_options_set_antialias(m_fontOptions, CAIRO_ANTIALIAS_GRAY);
cairo_font_options_set_hint_style(m_fontOptions, CAIRO_HINT_STYLE_NONE);
cairo_font_options_set_hint_metrics(m_fontOptions, CAIRO_HINT_METRICS_OFF);
m_cache.max_load_factor(1.0f);
m_cache.reserve(kMaxCacheEntries + 16);
}
@@ -114,6 +113,10 @@ void CairoGlyphRenderer::cleanup() {
m_lru.clear();
m_cacheBytes = 0;
if (m_fontOptions != nullptr) {
cairo_font_options_destroy(m_fontOptions);
m_fontOptions = nullptr;
}
if (m_cairoFace != nullptr) {
cairo_font_face_destroy(m_cairoFace);
m_cairoFace = nullptr;
@@ -172,7 +175,7 @@ CairoGlyphRenderer::TextMetrics CairoGlyphRenderer::measureGlyph(char32_t codepo
return {};
}
cairo_scaled_font_t* scaledFont = create_scaled_font(m_cairoFace, rasterSize);
cairo_scaled_font_t* scaledFont = create_scaled_font(m_cairoFace, m_fontOptions, rasterSize);
if (scaledFont == nullptr || cairo_scaled_font_status(scaledFont) != CAIRO_STATUS_SUCCESS) {
if (scaledFont != nullptr) {
cairo_scaled_font_destroy(scaledFont);
@@ -212,7 +215,7 @@ CairoGlyphRenderer::CacheEntry* CairoGlyphRenderer::lookupOrRasterize(char32_t c
return nullptr;
}
cairo_scaled_font_t* scaledFont = create_scaled_font(m_cairoFace, rasterSize);
cairo_scaled_font_t* scaledFont = create_scaled_font(m_cairoFace, m_fontOptions, rasterSize);
if (scaledFont == nullptr || cairo_scaled_font_status(scaledFont) != CAIRO_STATUS_SUCCESS) {
if (scaledFont != nullptr) {
cairo_scaled_font_destroy(scaledFont);
+2
View File
@@ -15,6 +15,7 @@ typedef struct FT_FaceRec_* FT_Face;
// Cairo forwards
typedef struct _cairo_font_face cairo_font_face_t;
typedef struct _cairo_font_options cairo_font_options_t;
class RenderBackend;
class TextureManager;
@@ -87,6 +88,7 @@ private:
FT_Library m_ftLibrary = nullptr;
FT_Face m_face = nullptr;
cairo_font_face_t* m_cairoFace = nullptr;
cairo_font_options_t* m_fontOptions = nullptr;
RenderBackend* m_backend = nullptr;
TextureManager* m_textureManager = nullptr;
+40 -1
View File
@@ -9,6 +9,7 @@
#include "ui/style.h"
#include <algorithm>
#include <deque>
#include <memory>
#include <vector>
@@ -78,6 +79,43 @@ struct Flex::ChildLayout {
float cross = 0.0f;
};
namespace {
// Pool of scratch buffers for Flex::runLayout. Layout can recurse (a Flex
// child triggers another runLayout while the parent's items vector is still
// live), so we keep a stack indexed by nesting depth. Each call grabs the
// slot for its depth and clears it on entry; capacity is preserved across
// calls so steady-state layout does no heap allocation for these vectors.
// std::deque is used so growing the stack during a recursive call does not
// invalidate references held by parent frames.
thread_local std::deque<std::vector<Flex::ChildLayout>> tlScratchStack;
thread_local std::size_t tlScratchDepth = 0;
class FlexScratchGuard {
public:
FlexScratchGuard() {
if (tlScratchStack.size() <= tlScratchDepth) {
tlScratchStack.emplace_back();
}
m_slot = &tlScratchStack[tlScratchDepth++];
m_slot->clear();
}
~FlexScratchGuard() {
m_slot->clear();
--tlScratchDepth;
}
FlexScratchGuard(const FlexScratchGuard&) = delete;
FlexScratchGuard& operator=(const FlexScratchGuard&) = delete;
[[nodiscard]] std::vector<Flex::ChildLayout>& items() noexcept { return *m_slot; }
private:
std::vector<Flex::ChildLayout>* m_slot;
};
} // namespace
Flex::Flex() {
m_paletteConn = paletteChanged().connect([this] { applyPalette(); });
}
@@ -378,7 +416,8 @@ LayoutSize Flex::runLayout(Renderer& renderer, const LayoutConstraints& constrai
? std::max(0.0f, containerCross - crossPaddingStart(*this, horizontal) - crossPaddingEnd(*this, horizontal))
: 0.0f;
std::vector<ChildLayout> items;
FlexScratchGuard scratch;
auto& items = scratch.items();
items.reserve(children().size());
float totalGrow = 0.0f;
for (auto& child : children()) {
+2 -1
View File
@@ -92,9 +92,10 @@ protected:
LayoutSize measureByLayout(Renderer& renderer, const LayoutConstraints& constraints);
void arrangeByLayout(Renderer& renderer, const LayoutRect& rect);
private:
public:
struct ChildLayout;
private:
void ensureBackground();
void applyPalette();
LayoutSize runLayout(Renderer& renderer, const LayoutConstraints& constraints, bool arrangeChildren);