mirror of
https://github.com/noctalia-dev/noctalia-shell.git
synced 2026-05-11 17:08:27 +08:00
perf: avoid memory fragmentation on flex and glyph renderer
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user