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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user