This is an automated email from the git hooks/post-receive script.
git pushed a commit to branch edje-vector-intergration
in repository efl.
View the commit online.
commit 55d3e3e1881aab25d5734f7ac1326f46766c6974
Author: [email protected] <[email protected]>
AuthorDate: Wed Apr 29 10:34:45 2026 -0600
edje: hybrid lerp design for smooth VG state transitions (Phase 5.3)
Implement Task 5.3: make _edje_vector_recalc_apply honor ep->param2 during
transitions so that tree-based state changes interpolate instead of step.
The implementation uses a three-tree hybrid approach:
1. Working tree A: dup base_a + apply chosen_desc overrides (if any)
2. Working tree B: dup base_b + apply param2 overrides (if any)
3. Lerp tree: in-memory blend of A/B non-path properties via _edje_vg_tree_lerp
All three are materialized to Efl_VG hierarchies. The lerp tree becomes root
(which holds the interpolated viewbox + blended properties); efl_a and efl_b
are short-lived references passed to efl_gfx_path_interpolate(root, efl_a,
efl_b, pos), which walks the Efl_VG tree and recursively updates path command
sequences. If topology mismatches, the call returns false silently and root
retains B's paths (step semantics per spec ยง5.2).
Fall-through to src_a render when:
- param2 == NULL (no transition active)
- Next description's tree_id < 0 (Mode C โ static SVG bridging deferred)
- Materialization of any tree fails
Memory ordering is type-correct throughout: efl_a and efl_b are unref'd
after efl_gfx_path_interpolate consumes them; root is unref'd after adoption
by efl_canvas_vg_object_root_node_set; lerp_tree freed before the !root error
path; working_a and working_b freed at branch end.
Performance note: three Efl_VG instantiations per frame at 60fps โ 180/sec for
moderate trees. Caching (by description-pointer + override-list-hash +
classes_dirty flag) deferred to Phase 5+.
62/62 tests pass; valgrind reports no new leaks.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
---
src/lib/edje/edje_calc.c | 123 ++++++++++++++++++++++++++++++++++-------------
1 file changed, 90 insertions(+), 33 deletions(-)
diff --git a/src/lib/edje/edje_calc.c b/src/lib/edje/edje_calc.c
index 3fb4b751be..3965d49703 100644
--- a/src/lib/edje/edje_calc.c
+++ b/src/lib/edje/edje_calc.c
@@ -3283,10 +3283,14 @@ _edje_vector_recalc_apply(Edje *ed, Edje_Real_Part *ep, Edje_Calc_Params *p3 EIN
if (chosen_desc->vg.tree_id >= 0)
{
Edje_Real_Part_Vector *rpv = ep->typedata.vector;
- const Edje_Vg_Tree *base;
- Edje_Vg_Tree *working = NULL;
- const Edje_Vg_Tree *materialize_src;
+ const Edje_Vg_Tree *base_a;
+ Edje_Vg_Tree *working_a = NULL;
+ Edje_Vg_Tree *working_b = NULL;
+ Edje_Vg_Tree *lerp_tree = NULL;
+ Efl_VG *efl_a = NULL, *efl_b = NULL;
Eina_Rect viewbox;
+ Eina_List *l;
+ Edje_Vg_Override *ovr;
if (!ed->file->vector_dir ||
(unsigned int)chosen_desc->vg.tree_id >= ed->file->vector_dir->trees_count)
@@ -3297,48 +3301,108 @@ _edje_vector_recalc_apply(Edje *ed, Edje_Real_Part *ep, Edje_Calc_Params *p3 EIN
return;
}
- base = &ed->file->vector_dir->trees[chosen_desc->vg.tree_id];
+ base_a = &ed->file->vector_dir->trees[chosen_desc->vg.tree_id];
/* Cache the tree pointer; useful for class-dirty invalidation in
Phase 6. */
- rpv->cached_base_tree = base;
+ rpv->cached_base_tree = base_a;
rpv->cached_tree_id = chosen_desc->vg.tree_id;
- viewbox = EINA_RECT(base->vbx, base->vby, base->vbw, base->vbh);
-
/* Phase 4: when per-description overrides are present, dup the base
- tree, apply each override in order, then materialise the working
- copy. When there are no overrides, skip the dup and materialise the
- immutable base tree directly (fast path, unchanged from Phase 3). */
+ tree, apply each override in order. When there are no overrides,
+ skip the dup and use the immutable base tree directly (fast path). */
if (chosen_desc->vg.overrides)
{
- Eina_List *l;
- Edje_Vg_Override *ovr;
-
- working = _edje_vg_tree_dup(base);
- if (!working)
+ working_a = _edje_vg_tree_dup(base_a);
+ if (!working_a)
{
ERR("Failed to dup base tree for override application (tree_id=%d)",
chosen_desc->vg.tree_id);
return;
}
EINA_LIST_FOREACH(chosen_desc->vg.overrides, l, ovr)
- _edje_vg_tree_apply_override(working, ovr);
- materialize_src = working;
+ _edje_vg_tree_apply_override(working_a, ovr);
}
- else
+ const Edje_Vg_Tree *src_a = working_a ? working_a : base_a;
+
+ /* Phase 5: when a transition is active (param2 non-NULL and pos > 0),
+ try to build a lerp tree across both descriptions. If the next
+ description is not tree-based (tree_id < 0, e.g. Mode-C SVG), fall
+ through to the direct-materialise path instead; Mode-C bridging is
+ deferred. */
+ root = NULL;
+ if (ep->param2 && NEQ(pos, ZERO))
{
- materialize_src = base;
+ Edje_Part_Description_Vector *next_desc =
+ (Edje_Part_Description_Vector *)ep->param2->description;
+
+ if (next_desc->vg.tree_id >= 0 &&
+ (unsigned int)next_desc->vg.tree_id < ed->file->vector_dir->trees_count)
+ {
+ const Edje_Vg_Tree *base_b =
+ &ed->file->vector_dir->trees[next_desc->vg.tree_id];
+
+ /* Build B's working tree (dup + apply overrides if any). */
+ if (next_desc->vg.overrides)
+ {
+ working_b = _edje_vg_tree_dup(base_b);
+ if (working_b)
+ {
+ EINA_LIST_FOREACH(next_desc->vg.overrides, l, ovr)
+ _edje_vg_tree_apply_override(working_b, ovr);
+ }
+ }
+ const Edje_Vg_Tree *src_b = working_b ? working_b : base_b;
+
+ /* Build the lerp tree: structure mirrors B with non-path
+ properties lerped from A. Paths stay at B's value here;
+ efl_gfx_path_interpolate handles them below. */
+ lerp_tree = _edje_vg_tree_lerp(src_a, src_b, TO_DOUBLE(pos));
+
+ /* Materialise all three trees. efl_a and efl_b are
+ short-lived references used only for path interpolation. */
+ efl_a = _edje_vg_tree_to_efl_vg(src_a);
+ efl_b = _edje_vg_tree_to_efl_vg(src_b);
+ if (lerp_tree) root = _edje_vg_tree_to_efl_vg(lerp_tree);
+
+ /* Path-only interpolation: walks the Efl_VG hierarchy and
+ updates each shape's path in `root` via the EFL-native
+ interpolator. Returns EINA_FALSE on topology mismatch โ
+ root already holds B's paths (step semantics), so failure
+ is safe to ignore. */
+ if (root && efl_a && efl_b)
+ efl_gfx_path_interpolate(root, efl_a, efl_b, TO_DOUBLE(pos));
+
+ /* Release auxiliary A/B VG trees; root is independent. */
+ if (efl_a) efl_unref(efl_a);
+ if (efl_b) efl_unref(efl_b);
+ }
}
- /* TODO Phase 5: cache the materialised Efl_VG to avoid the
- dup-and-walk cost on every recalc. Cache key:
- (tree_id, override-list-hash, classes_dirty). Phase 4 added the
- dup-and-apply-overrides flow; Phase 5 needs the materialised cache
- for transition perf at 60fps. */
- root = _edje_vg_tree_to_efl_vg(materialize_src);
+ /* No transition active, transition fell through (Mode-C next desc),
+ or lerp_tree materialise failed: fall back to direct src_a render. */
+ if (!root)
+ root = _edje_vg_tree_to_efl_vg(src_a);
- if (working) _edje_vg_tree_free(working);
+ /* Viewbox: use lerped viewbox when transition is active (the lerp tree
+ already has the interpolated vbx/vby/vbw/vbh); else use base_a. */
+ if (lerp_tree)
+ viewbox = EINA_RECT(lerp_tree->vbx, lerp_tree->vby,
+ lerp_tree->vbw, lerp_tree->vbh);
+ else
+ viewbox = EINA_RECT(base_a->vbx, base_a->vby,
+ base_a->vbw, base_a->vbh);
+
+ /* TODO Phase 5+: cache materialised Efl_VG hierarchies to avoid the
+ dup-and-walk cost on every recalc. During a transition we currently
+ build three Efl_VG instances per frame (A, B, and OUT). At 60fps
+ this is 180 instantiations/sec for moderately-sized trees. Cache
+ key: (description pointer, override-list-hash, classes_dirty). */
+
+ /* Free working trees and the lerp tree; root is the only survivor. */
+ if (lerp_tree) _edje_vg_tree_free(lerp_tree);
+ if (working_a) _edje_vg_tree_free(working_a);
+ if (working_b) _edje_vg_tree_free(working_b);
if (!root)
{
@@ -3352,13 +3416,6 @@ _edje_vector_recalc_apply(Edje *ed, Edje_Real_Part *ep, Edje_Calc_Params *p3 EIN
caller-side ref from efl_add_ref so the tree is owned exclusively
by the VG object from this point forward. */
efl_unref(root);
- root = NULL;
-
- /* Phase 3 deferral: when ep->param2 is non-NULL (transition active), we
- skip the legacy interpolation flow. Mode A/B descriptions with the
- same tree_id on both sides will naturally converge on the same root;
- different tree_ids produce a step-at-pos=1 cut. Phase 5 owns
- smooth interpolation across tree-based states. */
return;
}
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.