docs(overhaul): contract ordered sidecar index repair

Bind S5 packet section 27 for issue 470. The contract preserves the shared absolute transform index while making every live per-submission sidecar local, requires a nonzero-prefix production recording and offscreen pixel witness, and keeps receiver selection plus issue 469 out of scope.
This commit is contained in:
Erik 2026-09-05 02:15:10 +02:00
parent d43f6809b4
commit 67a8bc21f6
3 changed files with 127 additions and 3 deletions

View file

@ -2316,3 +2316,126 @@ terrain bleed, or missing waterfall is visible. The separate known #469
lighting transition and #470 local-sidecar defect remain G4 blockers and are
not waived by this scope gate. #471 is closed by the ordered one-pass arm.
Nothing has merged to `main`.
## 27. S5-#470 contract — split shared transform and local sidecar indices
### 27.1 Lead-verified production boundary
This is a modern Vulkan ownership/indexing repair, not a new retail algorithm.
No AC-specific material, lighting, visibility, or ordering fact changes. The
named-retail facts already carried by the existing light/opacity/selection
ports remain untouched; the paired retail binary has no analogue for acdream's
shared Vulkan frame-ring layout.
The production defect is exact and bounded:
1. `PrepareOrderedStream` writes this stream's transforms through
`WriteWorldTransformSection`, which appends them to the shared
directional-shadow/world transform arena and returns a possibly nonzero
`transformBaseInstance`.
2. `WriteIndirectCommands` adds that prefix to every indirect command's
`BaseInstance`, and `DrawOrderedRange` publishes the same prefix in
`GpuPushConstants.TextureIndexB`.
3. The alpha, selection-lighting, per-instance light-set, indoor, detail, and
dormant clip-slot sections are separate ring sections whose descriptor
offsets already point at element zero for this ordered submission.
4. `mesh_atmospheric.vert` therefore correctly names two indices: absolute
`transformIndex = gl_BaseInstanceARB + gl_InstanceID`, and local
`instanceIndex = transformIndex - int(uTextureIndexB)`. It reads only the
transform with the former and every live per-instance sidecar with the
latter.
5. `mesh_modern.vert` instead uses the absolute value as `instanceIndex` for
both domains. Any nonzero transform prefix therefore reads beyond or into
unrelated local sidecar elements. This affects the ordinary ordered path;
S5-c4 already repaired and proved atmospheric receiver selection/binding.
The batch array is a third, already-correct domain indexed by
`uDrawIDOffset + gl_DrawIDARB`; the global-light array is indexed by values
stored in the local light-set sidecar. The clip-slot buffer is still bound and
written but no world shader reads it after S3; do not reintroduce a clip read
or use its dead payload to justify wider work.
### 27.2 Bounded production repair
Make `mesh_modern.vert` use the same explicit split as
`mesh_atmospheric.vert`:
- add/read the existing push member as `uniform uint uTextureIndexB`;
- compute absolute `transformIndex` from base-instance plus instance-id;
- compute local `instanceIndex` by subtracting `uTextureIndexB`;
- index `Instances[]` only with `transformIndex`; and
- index alpha, selection lighting, indoor, the eight-entry light-set, and
detail category only with `instanceIndex`.
Correct the two shaders' comments so they describe the shared-transform/local-
sidecar ownership rather than claiming that every buffer uses an identical
index. Recompile the committed shader set and update only the generated SPIR-V
and manifest/hash pins that the source change requires.
Do not change `PrepareOrderedStream`, ring-allocation ownership, descriptor
offsets, indirect command construction, push-constant ABI/size/offsets,
pipeline choice, atmospheric receiver binding, render-pack policy, SetSurface
state, detail equations, alpha queues/FIFO order, walk stages, batching, cull,
depth, blend, fog, transforms, sidecar contents, texture/batch indices,
geometry, DAT/package recipe, #469 lighting direction, or any unrelated
renderer. Do not add a second data owner, copy the local sidecars into the
shared transform arena, zero the transform prefix, special-case atmospheric
mode, or add a per-frame allocation. No divergence-register row is expected:
this restores intended production indexing without changing retail-visible
policy. If any wider production change proves necessary, stop and return the
exact dependency before expanding scope.
Allowed production scope is the ordinary vertex shader plus regenerated
shader outputs. Tests may add the smallest production-path recording and
offscreen proof needed below. The implementer changes no packet, plan, issue,
architecture, package, gate artifact, or campaign memory file.
### 27.3 Discriminating proof and review return
Add permanent proof that fails on the pre-fix shader and exercises the real
ordered preparation/draw seam:
1. deliberately consume enough shared frame-ring transform space before
`PrepareOrderedStream` that its transform prefix is nonzero;
2. record two ordered instances with deliberately distinct transform, alpha,
selection-lighting, light-set, indoor, and detail values;
3. assert the uploaded indirect commands carry the nonzero absolute
`BaseInstance`, the push block carries that exact prefix, and each local
sidecar descriptor section still begins at its own element zero;
4. compile/inspect the production ordinary SPIR-V and prove the source/ABI has
one absolute transform lookup and local lookup for every live sidecar;
5. add an offscreen pixel witness through the production ordinary shader at a
nonzero transform prefix. Its two instances must encode distinguishable
sidecar-driven output so using the absolute index for either domain changes
the readback; a source-string-only or parallel-policy oracle is not this
witness; and
6. prove active and inactive directional-shadow receiver states still select
and bind the same ordinary/atmospheric pipelines as before.
Apply and independently restore at least these mutations, recording the first
discriminating failure for each in the commit body:
1. make the transform lookup use local `instanceIndex`;
2. make at least one live local sidecar lookup use absolute `transformIndex`;
3. omit or zero the pushed transform prefix while keeping the shader split;
4. force the inactive receiver state onto the atmospheric pipeline, then force
the active receiver state onto the ordinary pipeline; and
5. point the offscreen witness at a copied/test shader instead of the committed
production ordinary SPIR-V.
Return one clean implementation commit atop this contract; exact changed-file
and line counts; all mutation first failures; both `git diff --check` forms;
Release solution build with zero warnings/errors; focused ordered-stream,
receiver, shader ABI/manifest/SPIR-V, offscreen, allocation, material, and
render-pack tests; official hermetic lane; and inclusive InstalledDat with all
accepted nonpassing identities named. Recipe 10 and prepared content are
unchanged, so no bake is required. No graphical client launch.
Review stays sequential. Retail/behavior pass 1/10 first verifies that no
retail-visible algorithm or state changed, the existing named-retail sidecar
meanings remain intact, and the proof actually distinguishes both index
domains. Only after PASS does production/gate-honesty pass 2/10 inspect the
complete data/descriptor/push/SPIR-V path, reproduce at least three mutations,
and rule on gate sufficiency. A failed pass receives a bounded fix contract;
the owner-authorized ceiling is ten review passes. Nothing lands or launches
graphically before both lenses pass.