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 f68ef4dfb7ca3257493c178261c3d6e0e22b42a5
Author: [email protected] <[email protected]>
AuthorDate: Mon Aug 10 21:01:27 2026 -0600
fix(evas_ector_gl): resume LRU after atlas flush, strengthen tests
Review found two issues in the prior gradient-atlas fix:
1. After draining pending draws in the all-pinned case, _alloc_row kept
every row's last_used stamped with current_frame and returned row 0
without resetting pin state. The very next distinct gradient in the
same pass found every row pinned again and forced another flush - one
heavy-gradient pass turned into a flush per gradient. Age the pinned
rows by one after a successful flush so ordinary LRU resumes; document
why the no-callback fallback still returns row 0 unconditionally
(nothing was drained, so no row can honestly be called safe, and there
is no other way to make progress).
2. grad_atlas_flush_cb_optional asserted only r >= 0, which also passed
under the pre-fix code and did not distinguish old buggy behaviour from
the new correct behaviour. Rewrote it to assert the deterministic
fallback (row 0 reused every time with no callback registered), and
extended grad_atlas_no_eviction_of_rows_used_this_frame to assert
flush_calls stays at 1 across several more distinct gradients in the
same pass, guarding directly against the flush-storm regression.
Verified each test can fail: reverting the post-flush aging reproduces
_flush_calls == 6 instead of 1; reverting the no-callback fallback to
return a different row reproduces r == 1 instead of 0.
---
.../engines/gl_generic/evas_ector_gl_grad_atlas.c | 27 +++++++++++++++++--
src/tests/ector/suite/ector_test_grad_atlas.c | 31 +++++++++++++++++++---
2 files changed, 53 insertions(+), 5 deletions(-)
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 fbb11bc01a..0cf1f8382a 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
@@ -180,8 +180,31 @@ _alloc_row(Span_Grad_Atlas *a)
}
if (best >= 0) return best;
- /* Every row is pinned by this pass. Drain, then any row may be reused. */
- if (a->flush_cb) a->flush_cb(a->flush_data);
+ /* Every row is pinned by this pass. Drain the pending draws, then no
+ * unflushed draw references any row any more - age every pinned row by
+ * one so ordinary LRU resumes for the rest of this pass instead of
+ * flushing again on the very next miss (which would turn a heavy-gradient
+ * pass into one pipe flush per gradient). */
+ if (a->flush_cb)
+ {
+ a->flush_cb(a->flush_data);
+ for (int i = 0; i < SPAN_GRAD_ATLAS_H; i++)
+ if (a->rows[i].last_used == a->current_frame)
+ a->rows[i].last_used = a->current_frame - 1;
+
+ best = 0;
+ best_age = a->rows[0].last_used;
+ for (int i = 1; i < SPAN_GRAD_ATLAS_H; i++)
+ if (a->rows[i].last_used < best_age)
+ { best = i; best_age = a->rows[i].last_used; }
+ return best;
+ }
+
+ /* No callback registered: nothing was drained, so no row is actually
+ * safe to reuse. There is no way to make progress otherwise (the
+ * gradient path has no failure mode for "atlas full mid-pass"), so
+ * return row 0 anyway - this is the same degenerate-but-non-crashing
+ * fallback the atlas has always had for an unrecoverable situation. */
return 0;
}
diff --git a/src/tests/ector/suite/ector_test_grad_atlas.c b/src/tests/ector/suite/ector_test_grad_atlas.c
index 48eb2fd5c2..536904edfa 100644
--- a/src/tests/ector/suite/ector_test_grad_atlas.c
+++ b/src/tests/ector/suite/ector_test_grad_atlas.c
@@ -230,27 +230,52 @@ EFL_START_TEST(grad_atlas_no_eviction_of_rows_used_this_frame)
ck_assert_int_ge(new_row, 0);
ck_assert_int_eq(_flush_calls, 1);
+ /* After the drain, no unflushed draw references any row any more, so the
+ * next several distinct gradients in the SAME pass must resume ordinary
+ * LRU reuse rather than forcing a flush each time - one flush must not
+ * turn into a flush storm for every further gradient. */
+ for (int i = 0; i < 5; i++)
+ {
+ _fill_ramp(ramp, (uint32_t)(20000 + i));
+ int r = span_grad_atlas_lookup(a, (void *)(uintptr_t)(0xD000 + i),
+ (uint32_t)(20000 + i), ramp);
+ ck_assert_int_ge(r, 0);
+ }
+ ck_assert_int_eq(_flush_calls, 1);
+
span_grad_atlas_free(a);
}
EFL_END_TEST
EFL_START_TEST(grad_atlas_flush_cb_optional)
{
- /* With no callback registered the atlas must still make progress rather
- * than fail the lookup. */
+ /* With no callback registered, nothing is ever drained, so the atlas
+ * cannot claim any row is safe to reuse once all are pinned. It falls
+ * back to always returning row 0 for further misses in the same pass -
+ * this is deterministic and must not crash across many repeated
+ * overwrites of that row. */
Span_Grad_Atlas *a = span_grad_atlas_new();
ck_assert_ptr_nonnull(a);
span_grad_atlas_test_enable(a);
uint8_t ramp[SPAN_GRAD_ATLAS_ROW_BYTES];
span_grad_atlas_frame_begin(a);
- for (int i = 0; i < SPAN_GRAD_ATLAS_H + 1; i++)
+ for (int i = 0; i < SPAN_GRAD_ATLAS_H; i++)
{
_fill_ramp(ramp, (uint32_t)(i + 1));
int r = span_grad_atlas_lookup(a, (void *)(uintptr_t)(0x3000 + i),
(uint32_t)(i + 1), ramp);
ck_assert_int_ge(r, 0);
}
+
+ /* Every subsequent distinct gradient in this pass must land on row 0. */
+ for (int i = 0; i < 5; i++)
+ {
+ _fill_ramp(ramp, (uint32_t)(40000 + i));
+ int r = span_grad_atlas_lookup(a, (void *)(uintptr_t)(0x4000 + i),
+ (uint32_t)(40000 + i), ramp);
+ ck_assert_int_eq(r, 0);
+ }
span_grad_atlas_free(a);
}
EFL_END_TEST
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.