feat(ecs): chunks as a grouping - IntIndex takes sparse values; ludic.things' chunk column and things_drop_chunk
A streamed world loads and lets go of a chunk's entities together, so a chunk is a first-class group: chunk_of(x, z, size) packs a chunk's cell into an int, tb_remove_all(tb, ix, v) removes every row an index files under one value (the observers told of each). IntIndex kept one list per value up to the largest, which a packed chunk number (hundreds of millions) turned into hundreds of millions of empty lists and a 12 GB test run; a value past 1024 now gets its slot through an IntMap, so a sparse value costs one list. ludic.things files every Thing under its 256 m chunk (TH_CHUNK), kept by every move; things_chunk_count and things_drop_chunk (the port and the facts told of each removal, as thing_remove tells them) are what the memory budget's streamed chunks ask. The moves are their own file (moves.ludic). Tests: ludic.base 35, ludic.things 8, all under a 2 GB cap (24 MB peak). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
7834b26ef8
commit
f3fec88888
11 changed files with 186 additions and 65 deletions
|
|
@ -152,6 +152,30 @@ program EcsTest {
|
|||
expect_eq(tb_changed_since(tb, seen, out), 2) # a removal is told by tb_on_remove
|
||||
}
|
||||
|
||||
# values far apart (a chunk's packed cell) each take one list, not every list up to them
|
||||
function sparse_case() -> void {
|
||||
let tb: Table<Toy> = table_new(2, 2)
|
||||
let ix = tb_index(tb, KIND, -1)
|
||||
let a = chunk_of(9000.0, -9000.0, 256.0)
|
||||
let b = chunk_of(-512.0, 700.0, 256.0)
|
||||
expect(a > 100000 and b > 100000)
|
||||
put(tb, "a", 0.0, 0.0, a)
|
||||
put(tb, "b", 0.0, 0.0, b)
|
||||
let c = put(tb, "c", 0.0, 0.0, a)
|
||||
put(tb, "d", 0.0, 0.0, 3)
|
||||
expect_eq(ix_count(ix, a), 2)
|
||||
expect_eq(ix_count(ix, b), 1)
|
||||
expect_eq(ix_count(ix, 3), 1)
|
||||
expect(len(ix.lists) < 1100)
|
||||
tb_set_i(tb, KIND, tb_row(tb, c), b)
|
||||
expect_eq(ix_count(ix, a), 1)
|
||||
expect_eq(ix_count(ix, b), 2)
|
||||
expect_eq(tb_remove_all(tb, ix, b), 2)
|
||||
expect_eq(ix_count(ix, b), 0)
|
||||
expect_eq(tb_len(tb), 2)
|
||||
}
|
||||
|
||||
test "a sparse value takes one list" () { sparse_case() }
|
||||
test "observers hear each add, and a remove once" (hook_st: HookState) { hooks_case(hook_st) }
|
||||
test "change detection skips the blocks nobody wrote" () { blocks_case() }
|
||||
test "a removed handle misses, rows stay dense, a reused slot is a new handle" () { handles_case() }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue