[COLR] Reduce malloc pressure

By caching paint-extents.
This commit is contained in:
Behdad Esfahbod 2025-03-03 02:04:34 -07:00
parent 9efd17e225
commit e9134c8ce3
3 changed files with 79 additions and 14 deletions

View file

@ -47,6 +47,11 @@ namespace OT {
struct hb_paint_context_t;
}
struct hb_colr_scratch_t
{
hb_paint_extents_context_t paint_extents;
};
namespace OT {
struct COLR;
@ -2079,6 +2084,8 @@ struct COLR
{
static constexpr hb_tag_t tableTag = HB_OT_TAG_COLR;
bool has_data () const { return has_v0_data () || version; }
bool has_v0_data () const { return numBaseGlyphs; }
bool has_v1_data () const
{
@ -2115,13 +2122,21 @@ struct COLR
~accelerator_t () { this->colr.destroy (); }
bool has_data () const { return colr->has_data (); }
#ifndef HB_NO_PAINT
bool
get_extents (hb_font_t *font,
hb_codepoint_t glyph,
hb_glyph_extents_t *extents) const
{
return colr->get_extents (font, glyph, extents);
if (unlikely (!has_data ())) return false;
hb_colr_scratch_t *scratch = acquire_scratch ();
if (unlikely (!scratch)) return true;
bool ret = colr->get_extents (font, glyph, extents, *scratch);
release_scratch (scratch);
return ret;
}
bool paint_glyph (hb_font_t *font,
@ -2131,7 +2146,13 @@ struct COLR
hb_color_t foreground,
bool clip = true) const
{
return colr->paint_glyph (font, glyph, funcs, data, palette_index, foreground, clip);
if (unlikely (!has_data ())) return false;
hb_colr_scratch_t *scratch = acquire_scratch ();
if (unlikely (!scratch)) return true;
bool ret = colr->paint_glyph (font, glyph, funcs, data, palette_index, foreground, clip, *scratch);
release_scratch (scratch);
return ret;
}
#endif
@ -2168,8 +2189,34 @@ struct COLR
const DeltaSetIndexMap *get_delta_set_index_map_ptr () const
{ return colr->get_delta_set_index_map_ptr (); }
private:
hb_colr_scratch_t *acquire_scratch () const
{
hb_colr_scratch_t *scratch = cached_scratch.get_acquire ();
if (!scratch || unlikely (!cached_scratch.cmpexch (scratch, nullptr)))
{
scratch = (hb_colr_scratch_t *) hb_calloc (1, sizeof (hb_colr_scratch_t));
if (unlikely (!scratch))
return nullptr;
}
return scratch;
}
void release_scratch (hb_colr_scratch_t *scratch) const
{
if (!cached_scratch.cmpexch (nullptr, scratch))
{
scratch->~hb_colr_scratch_t ();
hb_free (scratch);
}
}
public:
hb_blob_ptr_t<COLR> colr;
private:
hb_atomic_ptr_t<hb_colr_scratch_t> cached_scratch;
};
void closure_glyphs (hb_codepoint_t glyph,
@ -2541,7 +2588,10 @@ struct COLR
#ifndef HB_NO_PAINT
bool
get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const
get_extents (hb_font_t *font,
hb_codepoint_t glyph,
hb_glyph_extents_t *extents,
hb_colr_scratch_t &scratch) const
{
ItemVarStoreInstancer instancer (get_var_store_ptr (),
@ -2555,10 +2605,10 @@ struct COLR
}
auto *extents_funcs = hb_paint_extents_get_funcs ();
hb_paint_extents_context_t extents_data;
bool ret = paint_glyph (font, glyph, extents_funcs, &extents_data, 0, HB_COLOR(0,0,0,0));
scratch.paint_extents.clear ();
bool ret = paint_glyph (font, glyph, extents_funcs, &scratch.paint_extents, 0, HB_COLOR(0,0,0,0), true, scratch);
hb_extents_t e = extents_data.get_extents ();
auto e = scratch.paint_extents.get_extents ();
if (e.is_void ())
{
extents->x_bearing = 0;
@ -2604,7 +2654,12 @@ struct COLR
#ifndef HB_NO_PAINT
bool
paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, unsigned int palette_index, hb_color_t foreground, bool clip = true) const
paint_glyph (hb_font_t *font,
hb_codepoint_t glyph,
hb_paint_funcs_t *funcs, void *data,
unsigned int palette_index, hb_color_t foreground,
bool clip,
hb_colr_scratch_t &scratch) const
{
ItemVarStoreInstancer instancer (get_var_store_ptr (),
get_delta_set_index_map_ptr (),
@ -2639,15 +2694,16 @@ struct COLR
else
{
auto *extents_funcs = hb_paint_extents_get_funcs ();
hb_paint_extents_context_t extents_data;
scratch.paint_extents.clear ();
paint_glyph (font, glyph,
extents_funcs, &extents_data,
extents_funcs, &scratch.paint_extents,
palette_index, foreground,
false);
false,
scratch);
hb_extents_t extents = extents_data.get_extents ();
is_bounded = extents_data.is_bounded ();
auto extents = scratch.paint_extents.get_extents ();
is_bounded = scratch.paint_extents.is_bounded ();
c.funcs->push_clip_rectangle (c.data,
extents.xmin,

View file

@ -243,7 +243,7 @@ struct hb_bounds_t
EMPTY,
};
hb_bounds_t (status_t status) : status (status) {}
hb_bounds_t (status_t status = UNBOUNDED) : status (status) {}
hb_bounds_t (const hb_extents_t &extents) :
status (extents.is_empty () ? EMPTY : BOUNDED), extents (extents) {}

View file

@ -35,13 +35,22 @@ typedef struct hb_paint_extents_context_t hb_paint_extents_context_t;
struct hb_paint_extents_context_t
{
hb_paint_extents_context_t ()
void clear ()
{
transforms.clear ();
clips.clear ();
groups.clear ();
transforms.push (hb_transform_t{});
clips.push (hb_bounds_t{hb_bounds_t::UNBOUNDED});
groups.push (hb_bounds_t{hb_bounds_t::EMPTY});
}
hb_paint_extents_context_t ()
{
clear ();
}
hb_extents_t get_extents ()
{
return groups.tail().extents;