# pool.ludic - what a frame is built of is kept and used again: ludic.ui builds a screen's tree anew # every frame, and Ludic frees nothing, so a node or an env made per element per frame was a leak # (the valley's HUD: ~100 MB/s). Each screen keeps two generations and alternates, so last frame's # tree - which an unmount's env, a hover or ui_box still reads - stays whole while this one is built. # So a host may read a tree until the same screen has been built twice more, and never keep it longer. export property UiPool { key: string = "" gen: int = 0 nodes0: []UiNode = null nodes1: []UiNode = null envs0: []UiEnv = null envs1: []UiEnv = null un: int = 0 ue: int = 0 ups0: []UiPass = null # a component root's passes (bd_pass_down) ups1: []UiPass = null uu: int = 0 vals0: []Val = null # what expressions work out while the screen is built (uv_new) vals1: []Val = null uv: int = 0 } # a build of `screen` begins: its other generation is handed out again from the start function pl_begin(ui_st: mut UiState, screen: string) -> void { var p: UiPool = null for i in 0 .. len(ui_st.pl_pools) { if ui_st.pl_pools[i].key == screen { p = ui_st.pl_pools[i] } } if p == null { p = new UiPool p.key = screen p.nodes0 = new []UiNode p.nodes1 = new []UiNode p.envs0 = new []UiEnv p.envs1 = new []UiEnv p.vals0 = new []Val p.vals1 = new []Val p.ups0 = new []UiPass p.ups1 = new []UiPass push(ui_st.pl_pools, p) } p.gen = 1 - p.gen p.un = 0 p.ue = 0 p.uv = 0 p.uu = 0 ui_st.pl_cur = p } # a node, blank, with its own lists (empty) - from the pool when a build is under way function nd_take(ui_st: mut UiState) -> UiNode { let p = ui_st.pl_cur if p == null { return nd_fresh() } var list = p.nodes0 if p.gen == 1 { list = p.nodes1 } if p.un < len(list) { let n = list[p.un] p.un += 1 nd_reset(n) return n } let n = nd_fresh() push(list, n) p.un += 1 return n } function nd_fresh() -> UiNode { let n = new UiNode n.children = new []UiNode n.classes = new []string n.akeys = new []string n.avals = new []string n.vals = new []Val return n } function env_take(ui_st: mut UiState) -> UiEnv { let p = ui_st.pl_cur if p == null { return env_fresh() } var list = p.envs0 if p.gen == 1 { list = p.envs1 } if p.ue < len(list) { let e = list[p.ue] p.ue += 1 env_reset(e) return e } let e = env_fresh() push(list, e) p.ue += 1 return e } function env_fresh() -> UiEnv { let e = new UiEnv e.names = new []string e.vals = new []Val e.on_envs = new []UiEnv return e } # a Value an expression works out while a screen is built: from the pool while the build runs (none # outlives the screen's next build but one, like its nodes), made as ever outside one - an action's # `set`, a 's start, what a handler is given are kept, so they are never pooled function uv_new(ui_st: mut UiState, tag: int) -> Val { let p = ui_st.pl_cur if not ui_st.uv_on or p == null { return Value.become(Value.null(), tag) } var list = p.vals0 if p.gen == 1 { list = p.vals1 } if p.uv < len(list) { let v = list[p.uv] p.uv += 1 return Value.become(v, tag) } let v = Value.become(Value.null(), tag) push(list, v) p.uv += 1 return v } function uv_int(ui_st: mut UiState, n: int) -> Val { if n >= 0 and n < 1024 { return mm_ival(ui_st, n) } return Value.be_int(uv_new(ui_st, 1), n) } function uv_float(ui_st: mut UiState, x: float) -> Val { return Value.be_float(uv_new(ui_st, 7), x) } function uv_str(ui_st: mut UiState, s: string) -> Val { return Value.be_str(uv_new(ui_st, 4), s) } function uv_list(ui_st: mut UiState) -> Val { return uv_new(ui_st, 5) } # true and false, one of each for good: read them, never change them function uv_bool(ui_st: mut UiState, b: bool) -> Val { if ui_st.uv_true == null { ui_st.uv_true = Value.bool(1) ui_st.uv_false = Value.bool(0) } if b { return ui_st.uv_true } return ui_st.uv_false } # a pooled record as it is, for a call to write its answer into (a list keeps its items); null outside # a build, where what is answered may be kept function uv_hold(ui_st: mut UiState) -> Val { let p = ui_st.pl_cur if not ui_st.uv_on or p == null { return null } var list = p.vals0 if p.gen == 1 { list = p.vals1 } if p.uv < len(list) { p.uv += 1 return list[p.uv - 1] } let v = Value.null() push(list, v) p.uv += 1 return v } function up_take(ui_st: mut UiState) -> UiPass { let p = ui_st.pl_cur if p == null { return new UiPass } var list = p.ups0 if p.gen == 1 { list = p.ups1 } if p.uu < len(list) { p.uu += 1 return list[p.uu - 1] } let u = new UiPass push(list, u) p.uu += 1 return u }