docs(render): record V6j - the world arm, the winding measurement, and the V7 list
Section 5.5.15 and the slice-table row. Three things worth having written down rather than rediscovered. The winding inversion V6c wrote was wrong and had never been asked a question: every Vulkan consumer through V6i sets Cull = None, so the mapping had not decided a fragment until the world arm arrived. That makes three defects this campaign has found in a path that compiled, validated clean, and had a test - the descriptor layouts and the TerrainClip set were the first two - and all three share the shape of a test that asserts the behaviour rather than the requirement. The A8 CullMode.Landblock override, which sections 5.5.13 and 5.5.14 both flagged as due for an answer the moment world materials drew on a second backend, has one: it carried over verbatim and is still load-bearing, so it is now a divergence with two consumers rather than one. The dungeon pass in section 5.1's checklist is what settles it, not this slice. And the V7 defect list, so the differential is not run against a target that cannot pass it: section 5.4's null-target divergence is still live on GL, EnvCellRenderer's Vulkan arm and the deferred-alpha and doorway-scissor paths are unexercised by the offline scene, and MSAA must come off. The pixel-gate figures are recorded as a distribution rather than a number, because 31 differing pixels sits at the documented band's top and a single value there is not evidence either way. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
f84eef3256
commit
7ae796a110
1 changed files with 129 additions and 0 deletions
|
|
@ -576,6 +576,7 @@ tenth pair with no consumer at all; see the V6e report.
|
||||||
| **V6h** ✅ | **The Vulkan composition host**, specified by §5.5.9 and reported in §5.5.10. `ACDREAM_RENDER_BACKEND=vulkan` runs the real `GameWindow` composition — DAT load, streaming, camera, entity table, session, and the real retained `UiHost` through the RHI — with no world renderers. Three seams: the already-generic platform acquisition now publishes a `GameWindowGraphics`; `VulkanHostInputCameraCompositionFactory` is the host-phase fork; the frame root gains a Vulkan arm. `VulkanBringUpHost` is reduced to the capability-probe harness over the extracted `VulkanGraphicsContext`. | offline Vulkan launch reaching the real composition with the client's own UI captured, one validation-layer run at 0 errors / 0 warnings, converging ownership ledger, App tests 4,075/3, complete Release suite 9,138/5, GL offline pixel gate 1.78e-05 |
|
| **V6h** ✅ | **The Vulkan composition host**, specified by §5.5.9 and reported in §5.5.10. `ACDREAM_RENDER_BACKEND=vulkan` runs the real `GameWindow` composition — DAT load, streaming, camera, entity table, session, and the real retained `UiHost` through the RHI — with no world renderers. Three seams: the already-generic platform acquisition now publishes a `GameWindowGraphics`; `VulkanHostInputCameraCompositionFactory` is the host-phase fork; the frame root gains a Vulkan arm. `VulkanBringUpHost` is reduced to the capability-probe harness over the extracted `VulkanGraphicsContext`. | offline Vulkan launch reaching the real composition with the client's own UI captured, one validation-layer run at 0 errors / 0 warnings, converging ownership ledger, App tests 4,075/3, complete Release suite 9,138/5, GL offline pixel gate 1.78e-05 |
|
||||||
| **V6i** ✅ | **The world arm's prerequisites**, in two parts. **V6i-1** (`df6e2a79`, reported in §5.5.12) closed §5.5.8's one-binding-two-buffers hazard with one descriptor-set pair per renderer scope, derived from the descriptor state rather than declared, and measured the ordered remainder list the world arm still needed. **V6i-2** (reported in §5.5.13) took items 1, 2 and 6 of that list in three gated commits: **1** the `TerrainClip` descriptor-set fix plus set 1's missing bindings 2 and 4, proven by `spirv-dis` and now gated by a SPIR-V-reading contract test (`f7344758`); **2** world texture CREATION crosses to `IGpuTexture` — `IWorldTextureArray` over `TextureAtlasManager`/`ManagedGLTextureArray`, `TerrainAtlas`'s second construction path, and `ICompositeTextureArrayBackend`'s RHI arm — with the Vulkan arm exercised at startup (`c8d0f70b`); **3** `IMeshPipelineDevice` decouples `ObjectMeshManager`/`WbMeshAdapter` from `OpenGLGraphicsDevice`. **Items 3–5 — the submission arms, `RetailPViewPassExecutor`, and the pass-structure merge — are the next slice's.** | pixel gate per commit (3.02e-05, 3.20e-05, 1.60e-05 vs `0ca802cd`), App tests 4,109/3 and complete Release suite 9,172/5, 3/3 desktop-witness connected run at commits 2 and 3, one validation-layer Vulkan run per commit |
|
| **V6i** ✅ | **The world arm's prerequisites**, in two parts. **V6i-1** (`df6e2a79`, reported in §5.5.12) closed §5.5.8's one-binding-two-buffers hazard with one descriptor-set pair per renderer scope, derived from the descriptor state rather than declared, and measured the ordered remainder list the world arm still needed. **V6i-2** (reported in §5.5.13) took items 1, 2 and 6 of that list in three gated commits: **1** the `TerrainClip` descriptor-set fix plus set 1's missing bindings 2 and 4, proven by `spirv-dis` and now gated by a SPIR-V-reading contract test (`f7344758`); **2** world texture CREATION crosses to `IGpuTexture` — `IWorldTextureArray` over `TextureAtlasManager`/`ManagedGLTextureArray`, `TerrainAtlas`'s second construction path, and `ICompositeTextureArrayBackend`'s RHI arm — with the Vulkan arm exercised at startup (`c8d0f70b`); **3** `IMeshPipelineDevice` decouples `ObjectMeshManager`/`WbMeshAdapter` from `OpenGLGraphicsDevice`. **Items 3–5 — the submission arms, `RetailPViewPassExecutor`, and the pass-structure merge — are the next slice's.** | pixel gate per commit (3.02e-05, 3.20e-05, 1.60e-05 vs `0ca802cd`), App tests 4,109/3 and complete Release suite 9,172/5, 3/3 desktop-witness connected run at commits 2 and 3, one validation-layer Vulkan run per commit |
|
||||||
| **V6i-3** | **The mesh pipeline runs on both arms, and the Vulkan frame gets a world pass**, reported in §5.5.14. Two commits: **1** `GlobalMeshBuffer` takes `GL?` and publishes `VertexStore`/`IndexStore`, `ObjectMeshManager`'s `RequireGl` narrows to the unreachable legacy upload, `VulkanMeshPipelineDevice` is `IMeshPipelineDevice`'s second implementation, and `NullWbMeshAdapter` is deleted (`fe8abacf`); **2** the clear merges into the world pass with `Store = Resolve` and descriptor sets bind at draw time (`887de4ae`). **The world renderers' submission arms and `RetailPViewPassExecutor` did NOT land** — §5.5.14 enumerates what they still need. | pixel gate per commit (4.44e-05, 3.73e-05 vs `579e0b7f`), App tests 4,112/3, one 3/3 desktop-witness connected run at HEAD, one validation-layer Vulkan run per commit, and a bit-identical (0/921,600) Vulkan capture across the pass merge |
|
| **V6i-3** | **The mesh pipeline runs on both arms, and the Vulkan frame gets a world pass**, reported in §5.5.14. Two commits: **1** `GlobalMeshBuffer` takes `GL?` and publishes `VertexStore`/`IndexStore`, `ObjectMeshManager`'s `RequireGl` narrows to the unreachable legacy upload, `VulkanMeshPipelineDevice` is `IMeshPipelineDevice`'s second implementation, and `NullWbMeshAdapter` is deleted (`fe8abacf`); **2** the clear merges into the world pass with `Store = Resolve` and descriptor sets bind at draw time (`887de4ae`). **The world renderers' submission arms and `RetailPViewPassExecutor` did NOT land** — §5.5.14 enumerates what they still need. | pixel gate per commit (4.44e-05, 3.73e-05 vs `579e0b7f`), App tests 4,112/3, one 3/3 desktop-witness connected run at HEAD, one validation-layer Vulkan run per commit, and a bit-identical (0/921,600) Vulkan capture across the pass merge |
|
||||||
|
| **V6j** ✅ | **The world arm, whole**, reported in §5.5.15. Two commits: **1** `VulkanViewportMapping` stops inverting the front face — the world arm is the mapping's first culling consumer and measured that the inversion culls terrain outright and turns every closed shell inside-out (`81fe5e1b`); **2** the three renderers' RHI submission arms as a SECOND arm per §5.5.6, both pass executors made backend-neutral behind `IWorldPassSurface`, `VulkanWorldPassScope` publishing the frame's one pass, `WorldFrameSections` carrying the three frame-global sections, and the composition that reaches them (`f84eef32`). **`ACDREAM_RENDER_BACKEND=vulkan` renders Dereth**: terrain, blending, roads, water, statics, scenery and the retained UI. Sky stays fog until V4f. | GL pixel gate 5.50e-05 vs `847f14ae` with a characterised 12–31 px noise distribution, App tests 4,112/3, complete Release suite 9,175/5, GL connected `-Runs 3` at 3/3 on both columns, one validation-layer Vulkan run at 0 errors / 0 warnings, and the world PNG inspected in §5.5.15 |
|
||||||
| **V7** | GL-versus-Vulkan differential: `tools/run-backend-differential-gate.ps1`, strict paired-PNG compare, divergences fixed in the Vulkan backend only, then lifecycle + R6 soak natively on Vulkan, one validation-layer-clean run, one RenderDoc capture. **Milestone: parity.** | every differential checkpoint passes; both connected routes green on VK |
|
| **V7** | GL-versus-Vulkan differential: `tools/run-backend-differential-gate.ps1`, strict paired-PNG compare, divergences fixed in the Vulkan backend only, then lifecycle + R6 soak natively on Vulkan, one validation-layer-clean run, one RenderDoc capture. **Milestone: parity.** | every differential checkpoint passes; both connected routes green on VK |
|
||||||
| **V8** | Perf gate on the RX 9070 XT, uncapped, both backends, same route. | §2 acceptance table; parity is the floor |
|
| **V8** | Perf gate on the RX 9070 XT, uncapped, both backends, same route. | §2 acceptance table; parity is the floor |
|
||||||
| **V9** | Linux + CI: X11/Wayland surfaces; a `linux-vulkan` job on lavapipe (probe accepts on a real 1.3 software device, a short real render under xvfb, forced-unsupported → exit 4, `.spv` freshness). Physical Linux GPU row deferred post-cutover, as for Slice L. | CI green including the new job |
|
| **V9** | Linux + CI: X11/Wayland surfaces; a `linux-vulkan` job on lavapipe (probe accepts on a real 1.3 software device, a short real render under xvfb, forced-unsupported → exit 4, `.spv` freshness). Physical Linux GPU row deferred post-cutover, as for Slice L. | CI green including the new job |
|
||||||
|
|
@ -1857,6 +1858,134 @@ submission arms and `RetailPViewPassExecutor`, which §5.5.12 item 3 established
|
||||||
is on the critical path because the offline gate's scene has buildings and
|
is on the critical path because the offline gate's scene has buildings and
|
||||||
therefore takes the PView route.
|
therefore takes the PView route.
|
||||||
|
|
||||||
|
#### 5.5.15 V6j (2026-07-28): the world arm lands, and Dereth draws on Vulkan
|
||||||
|
|
||||||
|
§5.5.14's remainder — the three renderers' submission arms,
|
||||||
|
`RetailPViewPassExecutor`, and the frame-global sections — is done.
|
||||||
|
**`ACDREAM_RENDER_BACKEND=vulkan` renders the offline scene: terrain with blended
|
||||||
|
textures and road overlays, the water edge, static world meshes, procedural
|
||||||
|
scenery, and the complete retained UI**, from the same camera as the GL capture
|
||||||
|
(`artifacts/v6j-vk2` against `artifacts/v6j-head2`). Sky is still the atmosphere
|
||||||
|
fog clear, because `SkyRenderer` is raw GL until V4f.
|
||||||
|
|
||||||
|
Three predecessors stopped at this unit under §7.1 rule 3. It landed as one slice
|
||||||
|
in two commits, because the rule is satisfied at SLICE granularity: the first
|
||||||
|
commit's piece is unreachable on its own and the second wires it and exercises it.
|
||||||
|
|
||||||
|
**1. The winding inversion was wrong, and nothing had ever asked** (`81fe5e1b`).
|
||||||
|
`VulkanViewportMapping` has inverted the front face since V6c, on the standard
|
||||||
|
negative-viewport argument. Every Vulkan consumer through V6i declares
|
||||||
|
`Cull = GpuCullMode.None`, so the mapping had never decided a fragment. The world
|
||||||
|
arm is its first culling consumer and falsified it twice on one frame: terrain —
|
||||||
|
the one single-sided surface acdream draws, `FrontFace(Ccw)` + `Cull(Back)` per
|
||||||
|
`ACRender::landPolysDraw`'s eye-side predicate — vanished entirely while issuing
|
||||||
|
190 multi-draw commands against 625 loaded landblocks, and every closed building
|
||||||
|
shell rendered inside-out with its front wall culled and its interior beams
|
||||||
|
visible through the gap. The identity mapping restores both at once. Two
|
||||||
|
independent surfaces, one change. The viewport flip itself is untouched and still
|
||||||
|
correct; what goes is the claim that a winding inversion travels with it.
|
||||||
|
|
||||||
|
**Worth generalising**: this is the third defect this campaign has found in a path
|
||||||
|
that compiled, validated clean, and had a test — because the test asserted the
|
||||||
|
behaviour rather than the requirement, and no consumer exercised it. §5.5.12's
|
||||||
|
descriptor layouts and §5.5.13's `TerrainClip` set were the first two.
|
||||||
|
|
||||||
|
**2. The world arm** (`f84eef32`). V4c's and V4d-2's content returns as a SECOND
|
||||||
|
arm, per §5.5.6's option (B): the GL arm issues the same statements in the same
|
||||||
|
order, and the encoder arm lives in three `.Rhi.cs` partials entered by one branch
|
||||||
|
per submission site. Three differences from V4c, each because the tree moved —
|
||||||
|
no binding-9 table (V4t put the slot on the device; Vulkan binds set 2), pipelines
|
||||||
|
carry the device's sample count, and **no renderer opens a pass**.
|
||||||
|
|
||||||
|
`VulkanWorldScenePhase` opens the frame's one backbuffer pass, publishes the
|
||||||
|
encoder on `VulkanWorldPassScope` for exactly the span of the inner
|
||||||
|
`WorldSceneRenderer`, and every renderer borrows it — §5.5.14 item 1's shape,
|
||||||
|
built as specified. The three frame-global sections are PUBLISHED on
|
||||||
|
`WorldFrameSections` and each renderer binds them inside the pass after its own
|
||||||
|
binds (item 2). The interior depth clear is `vkCmdClearAttachments` through the
|
||||||
|
scope (item 3); clip distances are no-ops (item 4); the collision-wireframe
|
||||||
|
`DebugLineRenderer` is composed null (item 7).
|
||||||
|
|
||||||
|
**Both pass executors became backend-neutral rather than gaining twins.** That is
|
||||||
|
the one place this slice diverged from §5.5.14's sketch, and it is why the slice
|
||||||
|
was tractable. Everything those two classes do is delegation to a renderer except
|
||||||
|
four concerns — the clip-frame publication, the doorway scissor,
|
||||||
|
`gl_ClipDistance` enablement and the interior depth clear — so those moved behind
|
||||||
|
`IWorldPassSurface` and retail's ordering, which is what the classes are actually
|
||||||
|
for, is written once for both backends. `RetailPViewPassExecutor` was on the
|
||||||
|
critical path precisely because the gate's scene has buildings (§5.5.12 item 3);
|
||||||
|
it now serves both arms unchanged.
|
||||||
|
|
||||||
|
**The A8 question is answered.** §5.5.13 and §5.5.14 asked what happens to the
|
||||||
|
Phase A8 `CullMode.Landblock → None` override the moment world materials draw on
|
||||||
|
a second backend. It carried over verbatim and is still load-bearing:
|
||||||
|
`EnvCellRenderer`'s Vulkan arm applies the same rewrite, for the same reason, and
|
||||||
|
the offline scene has no interior to disprove it with. The override is therefore
|
||||||
|
neither vindicated nor retired by this slice — it is now a divergence with two
|
||||||
|
consumers instead of one, and the dungeon pass in §5.1's checklist is what will
|
||||||
|
settle it.
|
||||||
|
|
||||||
|
**Gates.** Release build green. App tests **4,112 / 3 skips** — the unchanged
|
||||||
|
baseline; complete Release suite **9,175 / 5**. Strict GL offline pixel gate
|
||||||
|
against `847f14ae`: **5.50e-05** (31 differing pixels of 563,200), inside the
|
||||||
|
documented 9–31 band and 18x under the threshold. Characterised rather than
|
||||||
|
accepted, because 31 is the band's top:
|
||||||
|
|
||||||
|
```
|
||||||
|
cross-commit (847f14ae vs V6j): 21, 29, 31
|
||||||
|
same-commit (V6j vs V6j): 12, 20
|
||||||
|
maximumChannelDelta, all pairs: 46-52
|
||||||
|
```
|
||||||
|
|
||||||
|
The max delta is 46–52 in every comparison INCLUDING the pure same-commit
|
||||||
|
controls, so the few large-delta pixels are a property of the capture. A
|
||||||
|
cross-commit pair at 21 against a same-commit pair at 20 is not what a systematic
|
||||||
|
shift looks like — a real one floors every cross-commit comparison above every
|
||||||
|
same-commit one. GL connected `tools/run-repeat-connected-gate.ps1 -Runs 3`:
|
||||||
|
**3/3 RENDERED** on the desktop witness and 3/3 on the client capture. One offline
|
||||||
|
Vulkan run with `VK_LAYER_KHRONOS_validation` **proven inserted by the loader**:
|
||||||
|
**zero validation errors, zero warnings**, a captured world frame, graceful close.
|
||||||
|
`tools/run-offline-vulkan-capture.ps1` is the new script that produces it.
|
||||||
|
|
||||||
|
**The Dereth PNG, inspected honestly.** Right: terrain geometry, terrain blending
|
||||||
|
and its T-code transitions, road overlays, the water edge and its shoreline,
|
||||||
|
static world meshes with correct silhouettes and closed shells, procedural
|
||||||
|
scenery, per-instance lighting, the fog colour, and the complete retained UI
|
||||||
|
including the radar. The frame matches the GL capture's framing and content
|
||||||
|
element for element below the horizon.
|
||||||
|
|
||||||
|
Wrong, and each already owned by a later slice: **the sky is flat fog** (V4f —
|
||||||
|
`SkyRenderer` is raw GL, so there is no dome, no sun, no cloud sheet, no horizon
|
||||||
|
band); **no particles** (V4e); **no paperdoll or appraisal viewport and no portal
|
||||||
|
depth mask** (V4g); **no GPU frame timings** (`NullRenderFrameGpuMeasurement`
|
||||||
|
until V4h). None of these is a defect in this slice — they are the renderers it
|
||||||
|
deliberately does not compose.
|
||||||
|
|
||||||
|
**The V7 defect list — what a differential must not be run against yet.**
|
||||||
|
|
||||||
|
1. **Sky, particles, viewports, portal mask** — absent, not wrong. V4e/V4f/V4g.
|
||||||
|
2. **§5.4's `Target: null` divergence is still live on GL.** V7 must not run until
|
||||||
|
V4g/V4h remove it, or it surfaces as an entire viewport on the wrong surface —
|
||||||
|
unchanged from §5.4's own warning, and now more binding because both backends
|
||||||
|
draw a world.
|
||||||
|
3. **`EnvCellRenderer`'s Vulkan arm is unproven by anything automated.** The
|
||||||
|
offline scene has no interior. This is V4c's exact debt, restated on the new
|
||||||
|
backend.
|
||||||
|
4. **The deferred-alpha path and the doorway scissor are unexercised** in the
|
||||||
|
offline scene: no alpha queue collects and no doorway slice is assembled, so
|
||||||
|
`DrawPreparedAlphaBatch`'s Vulkan arm and `RhiWorldPassSurface.BeginScissor`
|
||||||
|
have run zero times.
|
||||||
|
5. **MSAA must be forced off for the differential**, as §5.1 already states —
|
||||||
|
sample positions are not specified across implementations, and the world
|
||||||
|
pipelines now genuinely differ in sample count from the UI's.
|
||||||
|
6. **Bindings 4, 6, 7 and 8 cost a descriptor write per draw** (§5.5.14 item 5).
|
||||||
|
Correct and bounded, and a V8 perf item rather than a correctness one, but the
|
||||||
|
arena's `ScopeCount` is the number to watch.
|
||||||
|
|
||||||
|
**What this slice does NOT claim.** It has not run connected on Vulkan, it has not
|
||||||
|
been seen by the user, and no dungeon, portal, paperdoll or spell effect has been
|
||||||
|
drawn on it. The milestone "full game frame on Vulkan" needs V4e–V4g behind it.
|
||||||
|
|
||||||
### 5.4 The null-target `BeginPass` divergence (V4c) — must be undone at V6
|
### 5.4 The null-target `BeginPass` divergence (V4c) — must be undone at V6
|
||||||
|
|
||||||
V4c had to stop GL's `BeginPass` from binding framebuffer 0 when a pass declares
|
V4c had to stop GL's `BeginPass` from binding framebuffer 0 when a pass declares
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue