feat(ecs): #80 entity-pool stats (Pool.live/free/reserved/capacity)
Ludic's ECS is already pool-based — the allocator recycles freed entity slots through a freelist (L_alloc pops @L_freen before growing @L_entc), and component storage is fixed per-entity arrays, so spawn/despawn churn (bullet-hell/horde) does no per-spawn heap allocation and cannot fragment. Expose that with a Pool.* namespace so a game can watch reuse: Pool.live (alive now), Pool.free (recycled slots waiting), Pool.reserved (high-water — stays flat across a steady spawn/despawn loop, proving reuse not reallocation), Pool.capacity (the fixed cap). Zero-cost inline reads of the existing counters. Example pool.ludic proves the key property: after despawn+respawn, Pool.reserved() stays 3 (freed slot reused) — prints 0 0 3 3 0 2 1 3 0 3 1. 4 docs pages. Full suite 118/0, goldens byte-identical, fixpoint holds. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
f2cb3cd7e8
commit
347352cc4c
10 changed files with 25580 additions and 25382 deletions
|
|
@ -148,6 +148,21 @@ function emit_ns_call(ns: pointer, meth: pointer, e: Node) -> Val {
|
|||
if is_clock_ns(meth) { return emit_clock_ns(meth, e) }
|
||||
perr(`unknown builtin Clock.{meth}`)
|
||||
}
|
||||
# #80 — entity pool stats. The ECS allocator already recycles freed entity slots
|
||||
# through a freelist (L_alloc pops @L_freen before growing @L_entc), and component
|
||||
# storage is fixed per-entity arrays — so there is no per-spawn heap allocation or
|
||||
# fragmentation. Pool.* just reads those counters so a game can watch reuse.
|
||||
if (ns == "Pool") {
|
||||
if (meth == "capacity") { return val(itoa(MAX_ENT), "int") } # max entities
|
||||
if (meth == "reserved") { return val(emit_bind("load i32, ptr @L_entc"), "int") } # slots ever allocated (high-water)
|
||||
if (meth == "free") { return val(emit_bind("load i32, ptr @L_freen"), "int") } # recycled slots ready for reuse
|
||||
if (meth == "live") { # currently alive = reserved - free
|
||||
let ec = emit_bind("load i32, ptr @L_entc")
|
||||
let fr = emit_bind("load i32, ptr @L_freen")
|
||||
return val(emit_bind(`sub i32 {ec}, {fr}`), "int")
|
||||
}
|
||||
perr(`unknown builtin Pool.{meth}`)
|
||||
}
|
||||
var bare: pointer = null
|
||||
let labels = new []pointer
|
||||
if (ns == "Screen") {
|
||||
|
|
|
|||
50841
selfhost/ludicc.seed.ll
50841
selfhost/ludicc.seed.ll
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue