code wiki / _hdl_build / nx_craft_erosion_shot.nx
nx_craft_erosion_shot.nx
buildroot/runtime/_hdl_build/nx_craft_erosion_shot.nx
about
nx_craft_erosion_shot.nx -- THE BEFORE/AFTER PICTURE OF EROSION, TAKEN FROM THE WASM A VISITOR LOADS.
WHY THIS EXISTS (operator standing rule, 2026-08-25): "if it's not publishing on a live page then
it's not truly deployed, as I can't validate." Talus erosion ships in /craft and is proven by
NUMBERS -- nx_wasm_craft_vm_gate measures talus 154/256, 509 of 576 lattice heights moved, rugosity
88 -> 75 permil -- and not one of those is a PICTURE. A number the operator cannot see is a claim he
has to take on trust, which is the one thing this estate exists to stop doing.
* WHY NOT nx_world_snap, THE INCUMBENT CAPTURE INSTRUMENT. Two reasons, and either alone is
decisive. (1) IT RENDERS NATIVELY: it imports nx_wasm_craft as a LIB and calls init_impl_v /
render_impl in this process's own memory. That is precisely the "a gate that tests a different
build than the one that ships is not testing the product" defect nx_wasm_craft_vm_gate exists to
correct -- /craft shipped BLACK TWICE while the native gate read 59/59 GREEN both times, because
the native build never meets the wasm memory bound at all. A before/after picture drawn by the
native twin would prove the twin erodes, not the product. (2) IT CANNOT REACH THE COLUMN ANYWAY:
its style poke is bounded 7..58 and its gen-row allowlist is 0..6, 59, 60, 61 -- so spec idx 64,
THE EROSION BUDGET, is unreachable from nx_world_snap in BOTH of its lanes. Measured, not assumed.
So this organ drives the emitted .wasm through the estate's own nx_wasm_vm, in a linear memory
sized to exactly what the module declares -- the same subject, the same bound and the same VM the
vm-gate uses. It opts in through the GENP block byte-for-byte the way craft.html's NXGP loop does,
so the erosion it photographs arrives by the SHIPPED door and not through a private back channel.
The PNG encoder is COMPOSED, never rewritten: nx_png_write_rgb is the estate's one encoder.
usage: nx_craft_erosion_shot <outbase> <variant> <seed> [q] [budgetA] [budgetB] [flat]
writes <outbase>_off.png (budgetA, default 0) and <outbase>_on.png (budgetB, default 1),
same variant + same seed + same camera; the erosion budget is the only difference.
q omitted or 0 -> DERIVED (see es_q_mem / es_q_rays). q given -> explicit, still refused
if it breaks the module's declared memory.
flat=1 -> ALSO zero P_TAMP1 and P_TAMP2 through the same GENP door, i.e. ask for a world
with NO RELIEF. THIS IS THE CONTROL THAT MATTERS, and it was earned the hard way: the
obvious control (render /beach, which never opts in) was run first and CAME BACK 680 PERMIL
DIFFERENT -- so it proved nothing about the pipeline and could not be published. beach's
sand is NEAR-flat (P_TAMP1=2, P_TAMP2=1), not flat, and on a two-block relief a one-block
shed moves most columns. A flat world has no slope steeper than its own average at all, so
the measured talus is zero and the operator turns itself off. Because budgetA and budgetB
then describe the SAME world, one such run proves BOTH that the capture is deterministic
AND that erosion does nothing without slopes: an image pipeline that painted noise, drifted
with wall-clock time, or added an offset would fail it.
dependencies 5 imports · 0 importers
imports: nx_syscalls.nxnx_wasm_vm.nxnx_wasm_craft.nxnx_png_write.nxnx_lineconf_lib.nx
imported by: nobody (leaf or entry point)
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 50 | const ES_WASM: *u8 = "buildroot/_build/nx_wasm_craft_emit.wasm" |
| 51 | const ES_CONF: *u8 = "knowledge/craft_erosion_shot.conf" |
| 52 | const ES_KEY_RAYS: *u8 = "shot-ray-budget" |
| 53 | const ES_W64: i64 = 8 // bytes in an i64 -- every plane below is made of these |
| 54 | const ES_RGB: i64 = 3 // bytes per pixel handed to nx_png_write_rgb |
| 55 | const ES_BYTE: i64 = 256 // one colour channel's modulus |
| 56 | const ES_PATHCAP: i64 = 512 |
| 57 | const ES_COLCAP: i64 = 4096 // distinct-colour census ceiling. A frame that reaches it is |
| 60 | const ES_MS_PER_S: i64 = 1000 |
| 61 | const ES_NS_PER_MS: i64 = 1000000 |
| 62 | const ES_PERMIL: i64 = 1000 |
| 63 | const ES_SCRATCH: i64 = 32 |
| 64 | const ES_BOX: i64 = 16 |
| 67 | const ES_GP_MAGIC: i64 = 0 |
| 68 | const ES_GP_N: i64 = 1 |
| 69 | const ES_GP_IDX0: i64 = 2 |
| 70 | const ES_GP_VAL0: i64 = 3 |
| 71 | const ES_GP_STRIDE: i64 = 2 // words per (idx,val) pair |
| 72 | const ES_GENP_N: i64 = 1 // the erosion pair (P_ERODE, budget) is always written |
| 73 | const ES_GENP_FLAT: i64 = 3 // ...plus P_TAMP1=0 and P_TAMP2=0 when a FLAT world is asked for |
| 75 | const ES_O_RW: i64 = 0 |
| 76 | const ES_O_RH: i64 = 1 |
| 77 | const ES_O_INITMS: i64 = 2 |
| 78 | const ES_O_RENDMS: i64 = 3 |
| 79 | const ES_O_FBOFF: i64 = 4 |
| 80 | const ES_O_DISTINCT: i64 = 5 |
| 81 | const ES_O_GENP: i64 = 6 // S_GENP: how many overrides the ENGINE says it applied. World |
| 87 | const ES_O_CX: i64 = 7 |
| 88 | const ES_O_CY: i64 = 8 |
| 89 | const ES_O_CZ: i64 = 9 |
| 90 | const ES_O_YAW: i64 = 10 |
| 91 | const ES_O_PITCH: i64 = 11 |
| 92 | const ES_OBOX: i64 = 16 |
| 93 | const ES_EXIT_USAGE: i64 = 2 |
| 94 | const ES_EXIT_WASM: i64 = 4 |
| 95 | const ES_EXIT_PNG: i64 = 5 |
| 96 | const ES_EXIT_NOFIT: i64 = 6 |
functions
| 98 | func es_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } called by 1: es_w |
| 99 | func es_w(s: *u8) -> i64 { sys_write(1, s, es_slen(s)); return 0 } |
| 100 | func es_n(v: i64) -> i64 |
| 115 | func es_int(s: *u8) -> i64 called by 1: main |
| 125 | func es_ms(ts: *i64) -> i64 |
| 130 | func es_path(dst: *u8, base: *u8, sfx: *u8) -> *u8 called by 1: main |
| 150 | func es_q_mem(fboff: i64) -> i64 called by 1: main |
| 158 | func es_q_rays(budget: i64) -> i64 called by 1: main |
| 168 | func es_genp(mem: *u8, k: i64, idx: i64, val: i64) -> i64 called by 1: es_shoot |
| 188 | func es_shoot(mod: *WasmMod, v: i64, seed: i64, erode: i64, flat: i64, q: i64, rgb: *u8, ob: *i64, ts: *i64, pin: *i64) -> i64 |
| 267 | func es_report(tag: *u8, ob: *i64) -> i64 |
| 279 | func main(argc: i64, argv: *i64) -> i64 |