From d1c9359e9602eeefebd21514d03b545e31a4c1cc Mon Sep 17 00:00:00 2001 From: Orkuncakilkaya Date: Sun, 27 Sep 2026 23:33:48 +0300 Subject: [PATCH] ludic.ui: a screen's nodes and envs are pooled and used again - two generations a screen, reset in place, their own lists emptied not replaced; a tree is good until its screen is built twice more (benchmark 256 -> 110 KB a frame) Co-Authored-By: Claude Opus 5.5 --- examples/library/ui_pointer.ludic | 4 +- packages/ludic.ui/backend.ludic | 2 + packages/ludic.ui/build.ludic | 5 +- packages/ludic.ui/build_class.ludic | 3 +- packages/ludic.ui/build_more.ludic | 11 ++- packages/ludic.ui/cascade.ludic | 9 ++- packages/ludic.ui/controls_build.ludic | 47 ++++++------ packages/ludic.ui/controls_parts.ludic | 10 +-- packages/ludic.ui/elem.ludic | 14 ++-- packages/ludic.ui/env.ludic | 6 +- packages/ludic.ui/frame.ludic | 10 +-- packages/ludic.ui/index.ludic | 3 + packages/ludic.ui/native.ludic | 2 +- packages/ludic.ui/pool.ludic | 84 ++++++++++++++++++++++ packages/ludic.ui/pool_reset.ludic | 61 ++++++++++++++++ packages/ludic.ui/pool_reset_more.ludic | 96 +++++++++++++++++++++++++ packages/ludic.ui/screen_class.ludic | 14 ++-- packages/ludic.ui/scroll_bar.ludic | 4 +- packages/ludic.ui/tooltip.ludic | 5 +- 19 files changed, 307 insertions(+), 83 deletions(-) create mode 100644 packages/ludic.ui/pool.ludic create mode 100644 packages/ludic.ui/pool_reset.ludic create mode 100644 packages/ludic.ui/pool_reset_more.ludic diff --git a/examples/library/ui_pointer.ludic b/examples/library/ui_pointer.ludic index 9433e22e..3a6e615d 100644 --- a/examples/library/ui_pointer.ludic +++ b/examples/library/ui_pointer.ludic @@ -53,10 +53,10 @@ program UiPointer { let active = down.children[2].bg let mid = said(down) frame(ui_st, hold.x + 5.0, hold.y + 90.0, false, 0.0, false) - let off = frame(ui_st, -1.0, -1.0, false, 0.0, false) + let off = said(frame(ui_st, -1.0, -1.0, false, 0.0, false)) frame(ui_st, -1.0, -1.0, false, 0.0, true) let keyed = said(frame(ui_st, -1.0, -1.0, false, 0.0, true)) let up = frame(ui_st, -1.0, -1.0, false, 0.0, false) - print(`{ui_pointer_st.log}dragged {int(ui_pointer_st.dragged)} | {mid} active {active} | {said(off)} | {keyed} | {said(up)} {up.children[2].bg}`) + print(`{ui_pointer_st.log}dragged {int(ui_pointer_st.dragged)} | {mid} active {active} | {off} | {keyed} | {said(up)} {up.children[2].bg}`) } } diff --git a/packages/ludic.ui/backend.ludic b/packages/ludic.ui/backend.ludic index 3b6d3558..dbbba037 100644 --- a/packages/ludic.ui/backend.ludic +++ b/packages/ludic.ui/backend.ludic @@ -32,6 +32,8 @@ function ui_default_backend() -> UiBackend { return UiRenderers[0].make() } export state UiState { + pl_pools: []UiPool = new []UiPool # pool.ludic: each screen's nodes and envs, used again + pl_cur: UiPool = null an_frames: []UiKeyframes = new []UiKeyframes an_keys: []string = new []string an_start: []float = new []float diff --git a/packages/ludic.ui/build.ludic b/packages/ludic.ui/build.ludic index 74b12d07..0e76d38b 100644 --- a/packages/ludic.ui/build.ludic +++ b/packages/ludic.ui/build.ludic @@ -47,7 +47,7 @@ function bd_each(ui_st: mut UiState, k: UiTpl, e: UiEnv, key: string, out: []UiN let at_name = bd_name(k, "index", "index") let by = tpl_attr(k, "key") for j in 0 .. Value.count(list) { - let le = env_new(e) + let le = env_new(ui_st, e) env_bind(le, as_name, Value.at(list, j)) env_bind(le, at_name, Value.int(j)) var id = string(j) @@ -77,7 +77,7 @@ function bd_comp(ui_st: mut UiState, k: UiTpl, e: UiEnv, key: string, out: []UiN } if k.comp == null { return false } let c = k.comp - let ce = env_new(null) + let ce = env_new(ui_st, null) ce.model = e.model ce.ctx = e.ctx for i in 0 .. len(c.keys) { @@ -86,7 +86,6 @@ function bd_comp(ui_st: mut UiState, k: UiTpl, e: UiEnv, key: string, out: []UiN for i in 0 .. len(k.keys) { env_bind(ce, k.keys[i], ui_eval(ui_st, k.vals[i], e)) } ce.on_names = k.on_keys ce.on_acts = k.on_acts - ce.on_envs = new []UiEnv for i in 0 .. len(k.on_keys) { push(ce.on_envs, e) } ce.slot = k ce.slot_env = e diff --git a/packages/ludic.ui/build_class.ludic b/packages/ludic.ui/build_class.ludic index 62e11c71..d2b2ed76 100644 --- a/packages/ludic.ui/build_class.ludic +++ b/packages/ludic.ui/build_class.ludic @@ -11,7 +11,7 @@ function bd_class(ui_st: mut UiState, k: UiTpl, e: UiEnv, key: string, out: []Ui if a != "class" and a != "style" and a != "id" { Value.put(props, a, ui_eval(ui_st, k.vals[i], e)) } } cls.props(it.ptr, props) - let ce = env_new(null) + let ce = env_new(ui_st, null) ce.model = cls.model(it.ptr) ce.inst = it ce.ctx = e.ctx @@ -19,7 +19,6 @@ function bd_class(ui_st: mut UiState, k: UiTpl, e: UiEnv, key: string, out: []Ui ce.slot_env = e ce.on_names = k.on_keys ce.on_acts = k.on_acts - ce.on_envs = new []UiEnv for i in 0 .. len(k.on_keys) { push(ce.on_envs, e) } lc_note(ui_st, key, k.on_keys, k.on_acts, e) let at = len(out) diff --git a/packages/ludic.ui/build_more.ludic b/packages/ludic.ui/build_more.ludic index 0e58a44c..e0a2386b 100644 --- a/packages/ludic.ui/build_more.ludic +++ b/packages/ludic.ui/build_more.ludic @@ -1,14 +1,14 @@ # build_more.ludic - names a value for the siblings after it, and # gives it to everything inside, components included (React's context) function bd_let(ui_st: mut UiState, k: UiTpl, e: UiEnv) -> UiEnv { - let le = env_new(e) + let le = env_new(ui_st, e) for i in 0 .. len(k.keys) { env_bind(le, k.keys[i], ui_eval(ui_st, k.vals[i], e)) } return le } function bd_provide(ui_st: mut UiState, k: UiTpl, e: UiEnv, key: string, out: []UiNode) -> void { - let cx = env_new(e.ctx) + let cx = env_new(ui_st, e.ctx) for i in 0 .. len(k.keys) { env_bind(cx, k.keys[i], ui_eval(ui_st, k.vals[i], e)) } - let pe = env_new(e) + let pe = env_new(ui_st, e) pe.ctx = cx bd_kids(ui_st, k.kids, pe, key, out) } @@ -18,12 +18,11 @@ function bd_group(ui_st: mut UiState, k: UiTpl, e: UiEnv, key: string, out: []Ui bd_kids(ui_st, k.kids, e, key, out) return } - let t = new UiNode + let t = nd_take(ui_st) t.kind = UI_TEXT t.key = key t.text = ui_tr(ui_st, ui_eval_text(ui_st, k.text, e)) t.env = e - t.children = new []UiNode push(out, t) } # text inside text -

Hi there

,

- reads as one line of it @@ -37,5 +36,5 @@ function el_inline(n: UiNode) -> void { s = s + c.text } n.text = s - n.children = new []UiNode + List.clear(n.children) } diff --git a/packages/ludic.ui/cascade.ludic b/packages/ludic.ui/cascade.ludic index 4cbc3530..6aad6834 100644 --- a/packages/ludic.ui/cascade.ludic +++ b/packages/ludic.ui/cascade.ludic @@ -9,11 +9,14 @@ function cs_tree(ui_st: mut UiState, n: UiNode) -> void { } if n.tag != "" { cs_node(ui_st, n) } for i in 0 .. len(n.children) { cs_tree(ui_st, n.children[i]) } - var shown = new []UiNode + var kept = 0 for i in 0 .. len(n.children) { - if not n.children[i].hidden { push(shown, n.children[i]) } + if not n.children[i].hidden { + n.children[kept] = n.children[i] + kept += 1 + } } - n.children = shown + while len(n.children) > kept { List.pop(n.children) } cs_order(n) } # twice: the font size (and zoom) first, from every rule, so an em anywhere reads the size the diff --git a/packages/ludic.ui/controls_build.ludic b/packages/ludic.ui/controls_build.ludic index 450d8350..631a61d0 100644 --- a/packages/ludic.ui/controls_build.ludic +++ b/packages/ludic.ui/controls_build.ludic @@ -4,18 +4,13 @@ # input[type=text|number|key] .ui-label .ui-field > .ui-value (+ .ui-caret while focused) # select .ui-label .ui-prev .ui-value .ui-next (its