This is an automated email from the git hooks/post-receive script.
git pushed a commit to branch span-gl-clean
in repository efl.
View the commit online.
commit a7a36afcf45d4e94f2dff7a213aa95345409f7ad
Author: [email protected] <[email protected]>
AuthorDate: Wed May 6 11:03:09 2026 -0600
perf(evas_ector_gl): cache ramp CRC on gradient data, drop atlas re-hash
Callgrind profiling of the regressed VG tests (120, 123-126) on expedite
revealed that _eina_crc was consuming 16-22% of frame Ir, with a telltale
pattern: fixed cost per push regardless of content, and exactly 2× the
pre-refactor baseline. This pinpointed two distinct inefficiencies in
the Tier-3 cache added in d97ad6c374.
## Fix 1: eliminate redundant hash in span_grad_atlas_lookup
span_grad_atlas_lookup already receives the CRC via the 'version' parameter,
computed by the caller as span_grad_atlas_hash(bytes). On identity miss,
the function was re-hashing those same bytes to drive _find_by_content.
By contract, version == h, so the second hash was redundant.
Replace uint32_t h = span_grad_atlas_hash(bytes) with uint32_t h = version.
Saves one 4 KB CRC per identity miss, which occurs every frame for any
gradient not explicitly hashed in the current frame's collect pass.
## Fix 2: relocate gradient-ramp CRC cache from Span_Collector to gd
The cache lived on Span_Collector, which is a per-frame slot pool managed
by a high-water mark allocator: slot[0] may serve shape A in frame N,
shape B in frame N+1. The cache fields, keyed implicitly on the slot,
thus invalidated every frame and never hit.
Move cached_ctable_crc, cached_ctable_status, and cached_ctable_crc_valid
onto Ector_Renderer_Software_Gradient_Data (the gradient's Eo private data,
stable for the object's lifetime). This gives the cache a stable home.
Staleness is invalidated via the existing early-return path: when
_compute_gradient_coeffs early-returns because ctable_status isn't READY,
it now sets gd->cached_ctable_crc_valid = EINA_FALSE. The framework
cycles status through CTABLE_NOT_READY whenever stops change, so any
content change forces the next READY frame to recompute the CRC.
Cross-module placement note: putting GL-engine cache fields on a struct
in lib/ector/software/ crosses a layer boundary, but is acceptable because
the fields are opaque cache state with no API surface and the struct lives
in a private header only by the gradient renderer subtree.
Verification:
- ector suite 19/0/0
- evas suite 120/0/0
- Expedite VG: 10/10 PNGs byte-identical with baseline
- Build clean
- Expected: _eina_crc Ir on regressed tests drops to cache-miss cost only
(first frame per gradient + upload memcmp), recovering remaining FPS
regression on those tests
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
---
src/lib/ector/software/ector_software_private.h | 16 +++++++++++++
.../engines/gl_generic/evas_ector_gl_grad_atlas.c | 7 ++++--
.../evas/engines/gl_generic/evas_ector_gl_span.h | 11 ---------
src/modules/evas/engines/gl_generic/evas_engine.c | 27 ++++++++++++++--------
4 files changed, 39 insertions(+), 22 deletions(-)
diff --git a/src/lib/ector/software/ector_software_private.h b/src/lib/ector/software/ector_software_private.h
index fe74bca515..283394549e 100644
--- a/src/lib/ector/software/ector_software_private.h
+++ b/src/lib/ector/software/ector_software_private.h
@@ -49,6 +49,22 @@ typedef struct _Ector_Renderer_Software_Gradient_Data
Eina_Bool alpha;
int ctable_status; //Ready for color table?
+
+ /* GL atlas CRC cache: avoids re-hashing 4 KB of color_table on every
+ * span push when the ramp content has not changed since the previous
+ * compute. Invalidated when the framework regenerates color_table
+ * (status cycles through CTABLE_NOT_READY before becoming READY again).
+ *
+ * Lives here rather than on Span_Collector so the cache survives the
+ * collector pool's high-water-mark slot reuse — collectors are
+ * recycled across shapes between frames, but gradient_data is stable
+ * per-gradient-renderer-object.
+ *
+ * Zero-initialised automatically: Eo private data is calloc'd by the
+ * Eo framework, so cached_ctable_crc_valid starts as EINA_FALSE. */
+ uint32_t cached_ctable_crc;
+ int cached_ctable_status; /* ctable_status when crc was computed */
+ Eina_Bool cached_ctable_crc_valid;
} Ector_Renderer_Software_Gradient_Data;
typedef struct _Shape_Rle_Data
diff --git a/src/modules/evas/engines/gl_generic/evas_ector_gl_grad_atlas.c b/src/modules/evas/engines/gl_generic/evas_ector_gl_grad_atlas.c
index be134a7d54..9db85de342 100644
--- a/src/modules/evas/engines/gl_generic/evas_ector_gl_grad_atlas.c
+++ b/src/modules/evas/engines/gl_generic/evas_ector_gl_grad_atlas.c
@@ -209,8 +209,11 @@ span_grad_atlas_lookup(Span_Grad_Atlas *a, void *grad_id,
return row;
}
- /* Hash + byte-compare path. */
- uint32_t h = span_grad_atlas_hash(bytes);
+ /* Hash + byte-compare path.
+ * version == span_grad_atlas_hash(bytes) by contract: callers compute it
+ * via span_grad_atlas_hash() before calling lookup, so recomputing here
+ * is redundant. Use version directly. */
+ uint32_t h = version;
row = _find_by_content(a, h, bytes);
if (row >= 0)
{
diff --git a/src/modules/evas/engines/gl_generic/evas_ector_gl_span.h b/src/modules/evas/engines/gl_generic/evas_ector_gl_span.h
index 36f3947948..d3b5288816 100644
--- a/src/modules/evas/engines/gl_generic/evas_ector_gl_span.h
+++ b/src/modules/evas/engines/gl_generic/evas_ector_gl_span.h
@@ -231,17 +231,6 @@ struct _Span_Collector
void *mask_surface; /* Evas_GL_Image* for the mask FBO (NULL = no mask) */
int comp_method; /* Efl_Gfx_Vg_Composite_Method (0 = NONE) */
- /* Cached gradient ramp CRC for the gl_generic atlas lookup. Avoids
- * re-hashing 4 KB of color_table content every frame when the gradient
- * has not changed. Invalidated when color_table pointer differs (the
- * framework typically reallocates on stop changes) or when ctable_status
- * was last seen non-READY (signalling content dirty/regen in flight).
- * Zero-initialised by calloc inside span_collector_new — first lookup
- * always misses (cached_ctable_ptr == NULL). */
- const uint32_t *cached_ctable_ptr; /* pointer observed at last READY hash */
- uint32_t cached_ctable_crc; /* CRC32 computed at last hash */
- int cached_ctable_status;/* ctable_status observed at last hash */
-
};
/* ------------------------------------------------------------------ */
diff --git a/src/modules/evas/engines/gl_generic/evas_engine.c b/src/modules/evas/engines/gl_generic/evas_engine.c
index 57fe62e6c5..08c2412856 100644
--- a/src/modules/evas/engines/gl_generic/evas_engine.c
+++ b/src/modules/evas/engines/gl_generic/evas_engine.c
@@ -2966,27 +2966,36 @@ _compute_gradient_coeffs(Span_Collector *sc,
* regen in flight, or pointer realloc). */
if (!(atlas && gd->color_table && gd->ctable_status == CTABLE_READY_DONE))
{
- sc->cached_ctable_ptr = NULL; /* invalidate — content may regen */
+ /* Ramp not ready or atlas unavailable — invalidate gd-side cache so
+ * that when status returns to READY we recompute the CRC against
+ * potentially new content. */
+ gd->cached_ctable_crc_valid = EINA_FALSE;
*out_atlas_skip = EINA_TRUE;
return;
}
/* Pass gd->color_table directly — no staging rearrangement needed.
- * _upload_row in the atlas does the ARGB→RGBA swap at GL upload time. */
+ * _upload_row in the atlas does the ARGB→RGBA swap at GL upload time.
+ *
+ * Tier-3 cache: skip the 4 KB CRC recompute on frames where color_table
+ * content has not changed. Staleness signal: the framework cycles
+ * ctable_status through CTABLE_NOT_READY when stops change; we check
+ * the status at the time the CRC was last computed rather than the
+ * pointer (pointer may survive in-place regen). Cache lives on gd so
+ * it survives Span_Collector slot reuse across shapes between frames. */
const uint8_t *ramp_bytes = (const uint8_t *)gd->color_table;
uint32_t version;
- if (sc->cached_ctable_ptr == gd->color_table &&
- sc->cached_ctable_status == CTABLE_READY_DONE &&
- gd->ctable_status == CTABLE_READY_DONE)
+ if (gd->cached_ctable_crc_valid &&
+ gd->cached_ctable_status == CTABLE_READY_DONE)
{
- version = sc->cached_ctable_crc;
+ version = gd->cached_ctable_crc;
}
else
{
version = span_grad_atlas_hash(ramp_bytes);
- sc->cached_ctable_ptr = gd->color_table;
- sc->cached_ctable_crc = version;
- sc->cached_ctable_status = gd->ctable_status;
+ gd->cached_ctable_crc = version;
+ gd->cached_ctable_status = gd->ctable_status;
+ gd->cached_ctable_crc_valid = EINA_TRUE;
}
int row = span_grad_atlas_lookup(atlas, sc->gradient_data,
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.