Metabolism: decision-cadence (think slower than you move) + empty-block skip; flat typed-array bytecode; WebGPU decision-layer slice (experimental) + heavy-world benchmark

This commit is contained in:
Dejvino
2026-10-28 13:51:58 +01:00
parent 476fa1833a
commit 3a72525ccb
7 changed files with 608 additions and 43 deletions
+47
View File
@@ -249,6 +249,53 @@ and wander); rock has a budget that survives a whole film (calm songs keep
sparse stone, percussive ones accumulate walls); agriculture consumes stone
instead of minting it. 44 tests green; realtime holds at 640×360.
### The decision layer on the GPU (analysis, first slice landed)
The profile of a heavy frame (field 128, ~40 000 organisms planted) says:
| layer | share |
|---|---|
| the strain VMs (`runProgram`) | ~35% |
| senses/gradients/action plumbing (`runStrains`) | ~20% |
| field passes (block sums, reaction, `substep`) | ~16% |
| advection (+allegiance, burden) | ~7% |
| camera + render | ~10% |
Three moves, in the order they pay:
1. **Think slower than you move (landed).** The programs ran at 150 Hz —
every substep — though a mind's decisions barely change in 1/30 s. The
world now thinks every `vmEvery`-th substep (default 2: ~15 thoughts/s)
and coasts on its last decision between thoughts; blocks with no
population think not at all. Measured on the heavy world
(41 k planted, sim only): 25.0 → 20.3 ms/frame at ×2, 17.7 at ×3 — with
statistically identical ecologies (38.6 k vs 38.7 k final). The tests
pinned the rest.
2. **The GPU (first slice landed, experimental).** The decision layer is
ideal GPU work: m uniform programs over ~4 000 independent blocks —
lockstep, only the data differs. `src/sim/gpu.js` holds a WebGPU compute
path for exactly that piece: senses/grads packed on the CPU, the flat
bytecode uploaded per strain, registers resident on the device, one
dispatch per strain, the action buffer read back per substep.
`tools/gpu-check.js` (browser-side) validates the GPU result against the
CPU interpreter and times both. Status: written against the WebGPU API,
awaiting a device to run on; NOT yet wired into `World.step` — that
wiring needs an async stepper (the readback is asynchronous), which the
demo can adopt and the exporter need not.
3. **The rest of the field** (block sums, advection, reaction) is the next
and larger GPU slice — element-wise and stencil passes over the fields,
with the population/politics staying on the CPU. That is what unlocks
field 256 and fatter casts.
**Determinism policy.** GPU float ordering differs from the CPU's, so a
GPU-accelerated preview may drift slightly from a CPU-exported film. The
policy: the prepared-artifact/export pipeline stays CPU and remains the
canonical pixel source; the GPU path is a preview accelerator whose world
is the same world to within float noise. If preview/export bit-identity is
ever demanded, the exporter must run on the same backend as the preview —
a node WebGPU binding (e.g. a wgpu native addon) is the door for that, and
it is explicitly out of scope until the film needs it.
### M3 — the lens: travel, don't cut
Replace the cut-to-a-point grammar with a **move grammar**: *track* (follow a
moving front), *reveal* (pan along a coastline), *establish* (hold wide and