refactor(render): Campaign V slice V6i-2 commit 3 — the mesh pipeline stops naming a backend
Plan §5.5.10 recorded the blocker as a fact about types: "WbMeshAdapter owns an
OpenGLGraphicsDevice, so it is not constructible on Vulkan until slice V4t" —
which is the entire reason NullWbMeshAdapter exists. §5.5.12 item 6 then measured
how wide that dependency really is, and the answer is seven members out of a
760-line class: a GL context, the retirement queue, the shared instance VBO, and
two capability flags.
IMeshPipelineDevice is exactly that surface. OpenGLGraphicsDevice declares it and
every member already existed under a GL-specific name, so the shipping backend
executes not one changed statement — these are aliases, not behaviour.
Two casts moved, and they are what actually blocked construction:
- ObjectMeshManager downcast IGpuDevice to GlGpuDevice in its CONSTRUCTOR, so a
Vulkan-composed pipeline threw before running a statement. V4t put it there
because the class registered bindless handles itself; commit 2 moved that into
the array, leaving the field a pass-through for the raw-GL renderers' handle
table. The cast now lives on that one property and names the backend it was
composed against instead of reporting a failed cast.
- The atlas array factory is selected by IWorldTextureArrayFactory.For, which is
the one place the texture stack branches on a backend.
MeshPipelineDeviceSeamTests proves the decoupling rather than describing it: it
builds ObjectMeshManager against a device whose Gl is null, asserts it constructs,
asserts construction built no GL object, asserts the handle table refuses by name,
and asserts the factory picks the RHI arm. A reflection test pins the seam's
member set so a later slice cannot quietly widen it back out — the whole value
here is that it is narrow.
What this does NOT claim: the mesh pipeline does not RUN on Vulkan. Its upload
bodies are still raw GL — GlobalMeshBuffer, the VAO/IBO construction, the layer
transfers — and they now fail through one RequireGl() accessor that names the
slice that owns porting them, instead of failing at construction. WbMeshAdapter
still creates an OpenGLGraphicsDevice in its GL constructor, because there is no
second implementation to create yet. Those bodies are items 3–5 of §5.5.12's
remainder list, along with RetailPViewPassExecutor and the three world renderers'
submission arms.
§5.5.13 reports the whole of V6i-2 and the slice table gains its V6i row.
Gates: Release build; App tests 4,109 / 3 skips (the 4,086 baseline plus 23 across
the three commits); complete Release suite 9,172 / 5; strict GL offline pixel gate
vs 0ca802cd 1.60e-05 (9 px of 563,200 — the low end of the documented 9–31 px
control band, and fewer than a same-commit control has measured); GL connected
tools/run-repeat-connected-gate.ps1 -Runs 3 at 3/3 RENDERED on the desktop witness
and 3/3 on the client capture; one Vulkan composition-host run with
VK_LAYER_KHRONOS_validation proven inserted by the loader at zero errors, zero
warnings, no [shutdown] diagnostic, and a captured frame.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
c8d0f70bbe
commit
5b3d72a90c
7 changed files with 459 additions and 18 deletions
|
|
@ -574,6 +574,7 @@ tenth pair with no consumer at all; see the V6e report.
|
|||
|
||||
| **V6g** | The four Vulkan validation defects §5.5.7 and its log left open: the dynamic-descriptor split (an architect decision, §5.5.8 item 1), per-pass depth-format pipeline variants, first-use backbuffer attachment layout transitions, and a backbuffer capture that no longer reads a presented swapchain image. Confined to `Gpu/Vk/`; the GL backend executes not one changed statement. | validation-clean bring-up run (0 errors / 0 warnings over 39,855 frames, against 7 VUIDs + 1 UNASSIGNED at the parent), App tests, GL offline pixel gate 4.08e-05 — its own same-commit control value |
|
||||
| **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 (1.60e-05, 3.20e-05, 3.02e-05 vs `0ca802cd`), App tests, 3/3 desktop-witness connected run at commits 2 and 3, one validation-layer Vulkan run per commit |
|
||||
| **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 |
|
||||
| **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 |
|
||||
|
|
@ -1609,6 +1610,134 @@ honest state and it is why the hazard was closed as its own commit: when the
|
|||
world arm lands and a frame first holds three renderers' buffers, a blank or
|
||||
corrupt result cannot be this defect.
|
||||
|
||||
#### 5.5.13 V6i-2 (2026-07-28): world texture creation reaches `IGpuTexture`, and the mesh pipeline stops naming a backend
|
||||
|
||||
§5.5.12's ordered remainder list had six items. This slice is items 1, 2 and 6 —
|
||||
the exercisable prerequisites the world arm cannot be written without — in three
|
||||
separately gated commits. Items 3–5 are the next slice's and are unchanged.
|
||||
|
||||
**1. The terrain clip block was in the wrong descriptor set, and so was the whole
|
||||
uniform layout** (`f7344758`). `terrain_modern.vert` declared `TerrainClip` with
|
||||
no `ACDREAM_UBO_SET`, so the Vulkan dialect put it at set 0 binding 2 — which set
|
||||
0 declares as a STORAGE buffer. `spirv-dis` on the committed `.spv`, before and
|
||||
after:
|
||||
|
||||
```
|
||||
before %372 = OpVariable %_ptr_Uniform__struct_370 Uniform
|
||||
OpDecorate %372 DescriptorSet 0 / Binding 2
|
||||
after OpDecorate %372 DescriptorSet 1 / Binding 2
|
||||
```
|
||||
|
||||
with `%_struct_370 = OpTypeStruct %int %_arr_v4float_uint_8` — the block's
|
||||
`{ int; vec4[8]; }` — unchanged in both. The same commit closed §5.5.8's recorded
|
||||
`UniformSkyParams` gap, because set 1's layout declared only bindings 1 and 3 and
|
||||
was therefore missing BOTH. All four are now declared and dynamic, which is half
|
||||
Vulkan's guaranteed `maxDescriptorSetUniformBuffersDynamic`.
|
||||
|
||||
Membership AND order now come from one predicate — `IsDeclaredUniformBinding` —
|
||||
that the layout, the descriptor writes and `vkCmdBindDescriptorSets`'s
|
||||
dynamic-offset array are all derived from, the shape V6g gave set 0. The three had
|
||||
been restated separately, which is how a fifth binding would have gone wrong the
|
||||
same way.
|
||||
|
||||
**Both gaps were found by hand, months apart, and neither could fail on the
|
||||
shipping backend.** `VulkanShaderDescriptorContractTests` now reads every
|
||||
committed `.spv` and asserts the partition instead: every uniform block at a
|
||||
declared set-1 binding, every storage block inside set 0's range, every sampled
|
||||
resource in the one texture table. Checked out against the pre-fix `.spv`, two of
|
||||
its four tests fail — so it is a gate, not a description.
|
||||
|
||||
**2. World texture CREATION crosses to `IGpuTexture`** (`c8d0f70b`), which is what
|
||||
V4t explicitly deferred and §5.5.12 item 1 handed forward. `IWorldTextureArray` is
|
||||
the seam, and **the slot is what crosses it**: `ObjectMeshManager` used to read
|
||||
`BindlessWrapHandle`/`BindlessClampHandle` off the concrete GL array and intern
|
||||
them itself, and a 64-bit `ARB_bindless_texture` handle has no Vulkan spelling. The
|
||||
array now answers `ResolveSlot(wrapping)` — the GL arm makes the same idempotent
|
||||
interning call one level down, the RHI arm returns a pair registered at
|
||||
construction — and `ReleaseTextureSlots` replaces the snapshot dictionary the
|
||||
manager kept, still running only once physical retirement completes.
|
||||
|
||||
Which implementation exists is decided ONCE, by the factory composition builds.
|
||||
Everything above the seam is written once: capacity policy, slot allocation, ref
|
||||
counting, layer retirement, empty-atlas eviction, and the whole of
|
||||
`ObjectMeshManager`'s atlas policy.
|
||||
|
||||
Three deliberate differences, each because the backends genuinely differ. BC mip
|
||||
chains are CPU-built through V6b's `BlockCompressionMipChain`, since Vulkan cannot
|
||||
blit into a compressed image, while RGBA8 uses the device's blit. Filtering lives
|
||||
in an immutable sampler rather than a texture parameter, so both address modes are
|
||||
registered up front — the same reason the GL array holds two resident handles. And
|
||||
**RGB8, A8 and Rgba32f are refused at creation with the reason named.** A8 is the
|
||||
one worth recording: the GL array serves it by swizzling R into A, and a Vulkan
|
||||
swizzle lives in the image VIEW, which the pinned `GpuTextureDescription` does not
|
||||
describe. A silent substitution would render wrong and look like a shader bug.
|
||||
Whoever draws world materials on Vulkan either meets a real A8 atlas and extends
|
||||
the contract, or proves none exists.
|
||||
|
||||
`TerrainAtlas` gained the second construction path V6i drafted and reverted, with
|
||||
the decode factored out and shared so both arms read the same DATs in the same
|
||||
order with the same resize-to-max policy. `ICompositeTextureArrayBackend` gained
|
||||
its RHI arm, which is four small methods because that seam was already a seam.
|
||||
|
||||
**The arm is EXERCISED, and that is the point.** The V6i draft was reverted
|
||||
precisely because nothing exercised it, and §5.5.12 measured the same failure twice
|
||||
over in the descriptor layouts. So the composition host now builds the real terrain
|
||||
atlas through `IGpuDevice.CreateTexture` on the arm with no GL context, and creates
|
||||
and releases one shared array of each format family plus one composite array at
|
||||
startup. Creation only; nothing draws them. Releasing them in the same statement
|
||||
covers what a retained bundle would not — that both slot pairs come back and the
|
||||
images route through the retirement queue.
|
||||
|
||||
**3. The mesh pipeline stops naming a backend** (§5.5.12 item 6). That item
|
||||
measured the dependency and found it seven members wide out of a 760-line class: a
|
||||
GL context, the retirement queue, the instance VBO, and two capability flags.
|
||||
`IMeshPipelineDevice` is exactly that surface, `OpenGLGraphicsDevice` declares it,
|
||||
and every member already existed — so the GL arm executes not one changed
|
||||
statement. Two casts moved: the `GlGpuDevice` downcast left the constructor for the
|
||||
one property that genuinely needs it (the raw-GL renderers' handle table), and the
|
||||
atlas factory is selected by `IWorldTextureArrayFactory.For`. **`ObjectMeshManager`
|
||||
now constructs against a device with no GL context at all**, which is what
|
||||
`MeshPipelineDeviceSeamTests` proves; before this commit the constructor threw a
|
||||
cast before running a statement, and that is why `NullWbMeshAdapter` exists.
|
||||
|
||||
**What this does NOT claim.** The mesh pipeline does not RUN on Vulkan. Its upload
|
||||
bodies are still raw GL — `GlobalMeshBuffer`, the VAO/IBO construction, the layer
|
||||
transfers — and they now fail at the site that needs GL, naming the slice that owns
|
||||
them, instead of failing at construction. `WbMeshAdapter` still creates an
|
||||
`OpenGLGraphicsDevice` in its GL constructor, because there is no second
|
||||
implementation to create yet. A reflection test pins the seam's member set so a
|
||||
later slice cannot quietly widen it back out.
|
||||
|
||||
**Gate results.** Strict GL offline pixel gate against `0ca802cd`: **1.60e-05**,
|
||||
**3.20e-05** and **3.02e-05** at the three commits (9, 18 and 17 differing pixels
|
||||
of 563,200), every one inside the documented 9–31 px control band and at least 31x
|
||||
under the 0.001 threshold. GL connected `tools/run-repeat-connected-gate.ps1
|
||||
-Runs 3` at commits 2 and 3: **3/3 RENDERED** on the desktop witness and 3/3 on the
|
||||
client capture, both times. One Vulkan composition-host run per commit with
|
||||
`VK_LAYER_KHRONOS_validation` **proven inserted by the loader**: zero validation
|
||||
errors, zero warnings, no `[shutdown]` diagnostic on either stream, captured frame.
|
||||
App tests 4,109 / 3 skips — the 4,086 baseline plus 23. The Vulkan run at commit 2
|
||||
built `terrain-atlas` 512x512x33 with 10 mip levels, `terrain-alpha-atlas`
|
||||
512x512x8 (4 corner, 1 side, 3 road), RGBA8 64x64x32 at slots 3/4 with 174,720 mip
|
||||
bytes blitted, BC1 64x64x32 at slots 5/6 with 696 mip bytes encoded, and a
|
||||
composite 32x32x8 at slot 7.
|
||||
|
||||
**Two intermittent test failures, filed rather than attributed.** A whole-suite run
|
||||
failed `Issue181WallPressEquilibriumTests` once; it passed alone and did not recur
|
||||
in five further runs. Seven test classes mutate the same process-global
|
||||
`CameraDiagnostics` switches with no xUnit collection isolation, and this slice
|
||||
touches no camera, visibility or physics code. Separately, a run of the UNCHANGED
|
||||
parent tree failed a zero-allocation test — `#250`'s documented class, which fired
|
||||
on three different tests across these runs.
|
||||
|
||||
**What the world arm still needs, and it is now exactly items 3–5.** §5.5.12 item
|
||||
3's flat-versus-PView question is unchanged and still decides how much of the world
|
||||
arm is one slice: the offline gate's scene has buildings, so
|
||||
`WorldRenderFrame.ClipRoot` takes the PView path, and `RetailPViewPassExecutor`
|
||||
(685 lines) is on the critical path to the first Vulkan Dereth PNG. Beyond that:
|
||||
the three world renderers' submission arms, and the mesh pipeline's raw-GL upload
|
||||
bodies this slice deliberately left in place.
|
||||
|
||||
### 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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue