Two independent implementations now measure the same thing: rejecting a cliff at
the apply stage beats rejecting it at the crossing stage, and is worse on
nothing.
The measurement
Scored on ORIENTATION against the game's own LuaEntity.cliff_orientation,
across the three regions of
oracle-vulcanus-cliff-entities.seed123456.json:
| model |
matched |
wrong |
surplus |
missing |
rejectAtCrossingStage (ships) |
1504 |
21 |
22 |
6 |
applyCliffs, lava + ore |
1508 |
18 |
22 |
5 |
applyCliffs, no cascade |
1500 |
25 |
22 |
6 |
Better on three counts and worse on none. The no-cascade row is what makes the
middle row mean something: without it, "the apply stage is better" would not
separate the cascade from the re-staging.
test/cliffConnections.spec.ts has held these numbers on the TypeScript side.
crates/fmw-noise/src/fixtures.rs now reaches all twelve of them independently,
first run, from a separate implementation of Cliff::updateConnections and
onDestroy (#225, PR #306).
Why it is not already wired in
rejectAtCrossingStage zeroes a rejected cell's four edges. The real stage
destroys the entity and lets Cliff::onDestroy take the facing end of each
CONNECTED neighbour - one or two sides, not four, and by rewriting the
orientation rather than clearing a crossing.
The renderer paints POSITIONS and ignores orientation, and on positions alone
the two models are a wash: 1526 against 1525 of 1531, one cell. So the gain
is almost entirely in a quantity the preview does not draw.
What adopting it costs
The connection pass needs a HALO. A cell on the query's outer chunk ring reads
its neighbour across the boundary, and the onDestroy cascade can reach further
still, so the renderer would have to run the pass over a padded query and filter
afterwards.
That changes the geometry two gates currently pin byte-identical between a whole
render and its tiles:
It would also need doing on both ports in the same change, or tier 3's
byte-identity assertion goes red - which is the mechanism working, not a
problem.
Suggested shape
- Decide whether one position and three orientations justify a padded query.
The honest answer may be no; recording that decision is worth as much as
making the change.
- If yes: change both ports together, re-score the table above on both sides,
and re-measure the tiled-vs-whole cost - the cliff pass quantises its
enumeration to 32-tile chunks, so a wider query can pull in a whole extra
chunk per axis. A symmetric-vs-exact halo was already measured at 1.40x for
zero pixels of difference.
- Keep the no-cascade arm in whatever grades it. It is the control.
Related: #84 (the remaining 2.4% residual, open against both ports), #83.
Two independent implementations now measure the same thing: rejecting a cliff at
the apply stage beats rejecting it at the crossing stage, and is worse on
nothing.
The measurement
Scored on ORIENTATION against the game's own
LuaEntity.cliff_orientation,across the three regions of
oracle-vulcanus-cliff-entities.seed123456.json:rejectAtCrossingStage(ships)applyCliffs, lava + oreapplyCliffs, no cascadeBetter on three counts and worse on none. The no-cascade row is what makes the
middle row mean something: without it, "the apply stage is better" would not
separate the cascade from the re-staging.
test/cliffConnections.spec.tshas held these numbers on the TypeScript side.crates/fmw-noise/src/fixtures.rsnow reaches all twelve of them independently,first run, from a separate implementation of
Cliff::updateConnectionsandonDestroy(#225, PR #306).Why it is not already wired in
rejectAtCrossingStagezeroes a rejected cell's four edges. The real stagedestroys the entity and lets
Cliff::onDestroytake the facing end of eachCONNECTED neighbour - one or two sides, not four, and by rewriting the
orientation rather than clearing a crossing.
The renderer paints POSITIONS and ignores orientation, and on positions alone
the two models are a wash: 1526 against 1525 of 1531, one cell. So the gain
is almost entirely in a quantity the preview does not draw.
What adopting it costs
The connection pass needs a HALO. A cell on the query's outer chunk ring reads
its neighbour across the boundary, and the
onDestroycascade can reach furtherstill, so the renderer would have to run the pass over a padded query and filter
afterwards.
That changes the geometry two gates currently pin byte-identical between a whole
render and its tiles:
test/tiledEquality.spec.ts(TypeScript);test/wasmVulcanusRenderParity.spec.ts, addedin Port the Vulcanus cliff stack to Rust and render cliffs through the engine (#225) #306, which runs through the engine.
It would also need doing on both ports in the same change, or tier 3's
byte-identity assertion goes red - which is the mechanism working, not a
problem.
Suggested shape
The honest answer may be no; recording that decision is worth as much as
making the change.
and re-measure the tiled-vs-whole cost - the cliff pass quantises its
enumeration to 32-tile chunks, so a wider query can pull in a whole extra
chunk per axis. A symmetric-vs-exact halo was already measured at 1.40x for
zero pixels of difference.
Related: #84 (the remaining 2.4% residual, open against both ports), #83.