diff --git a/docs/plans/2026-07-27-vulkan-campaign.md b/docs/plans/2026-07-27-vulkan-campaign.md index fe1fb7f7..3259f0c3 100644 --- a/docs/plans/2026-07-27-vulkan-campaign.md +++ b/docs/plans/2026-07-27-vulkan-campaign.md @@ -1195,6 +1195,96 @@ instrument to a gate. world arm needs one descriptor set per renderer, or per-renderer sub-ranges of one buffer, and it needs to know that before it is written.** +#### 5.5.9 The Vulkan composition host: the seam, measured (V6g) + +§5.5.7's step 1 asked for "a Vulkan composition host — a slice the plan has never +scoped." V6g scoped it and did not build it. What follows is the specification, so +the slice that does build it starts from a file list rather than a survey. The +short version: **it is most of V4h, it is roughly 1,200–2,000 changed lines across +~20 files including `GameWindow.cs`, and it is a load-bearing slice in its own +right** — not a preface to one. + +**The fork seam is already there, in three places, and only one of them is +missing.** + +1. **Platform acquisition is already backend-neutral.** + `GameWindowPlatformResult` and + `IGameWindowPlatformPublication` + (`Composition/GameWindowPlatformAcquisition.cs:3,11`) are fully generic. Only + the *call sites* pin `TGraphics = GL`. Nothing in the acquisition machinery + needs changing. +2. **The host phase already has a factory interface built for exactly this + substitution.** `IHostInputCameraCompositionFactory` + (`Composition/HostInputCameraComposition.cs:49`) declares + `CreateViewportTarget`, `CreateGpuFrameFlights`, `CreateGpuDevice` and + `CreateWorldRenderDiagnostics`, each taking a `GL`, with one implementation + (`RetailHostInputCameraCompositionFactory`). **A `VulkanHostInputCameraCompositionFactory` + returning `VulkanGpuDevice` is the fork, and it is a new file, not a + modification.** The four signatures lose their `GL` parameter and take the + platform result instead. +3. **The frame root is the seam that does not exist yet**, and it is the real + work. `FrameRootComposition` builds `RuntimeRenderFrameClearPhase`, + `RetailPViewPassExecutor`, `WorldScenePassExecutor` and + `FrameProfilerGpuMeasurement` from `d.Gl` plus six raw-GL world renderers + (`live.DrawDispatcher`, `live.EnvCellRenderer`, `foundation.Terrain`, + `live.SkyRenderer`, `live.ParticleRenderer`, `live.PortalDepthMask`). On Vulkan + none of those exist, so the Vulkan arm is a **second frame-root assembly** whose + render graph is the retained UI and debug lines through the RHI and nothing + else. That is the slice's centre of gravity. + +**What the retained UI actually needs — better news than §5.5.7 implied.** V6f +wrote that "the retail widget tree is built from LayoutDesc and DAT chrome by +`TextureCache`, which is still a GL type until slice V4t." That is true of the type +but **not of the UI's texture path**, which V4a and V6d already moved onto +`IGpuDevice`: `TextureCache.UploadUiTexture` (`Rendering/TextureCache.cs:311`) goes +through `CreateTexture`/`CreateSampler`/`RegisterTexture`, and the public +`UploadRgba8` that `IconComposer` composes retail icons with routes into it. The +raw-`_gl` uploads that remain are the world's `Texture2D`/`Texture2DArray` paths, +which no UI draw reaches. **Three things, all small, stand between the real +`UiHost` and a Vulkan frame:** + +- `TextureCache`'s constructor requires a non-null `GL` (`:21`). +- `UploadUiTexture` hard-casts to `GlGpuTexture` for VRAM accounting + (`Rendering/TextureCache.cs:337`: `uint glName = ((GlGpuTexture)texture).GlName;`). +- `InteractionRetainedUiComposition:533` builds the UI-probe + `FrameScreenshotController` from `d.Gl`, reachable only when + `ACDREAM_UI_PROBE` is on. + +So **the retained UI is not V4t-blocked; the world is.** That is the one place +§5.5.7's sequencing should be read more precisely: V4t is a hard prerequisite for +step 4 (the world arm), not for step 1. + +**The mechanical remainder**, for estimation: `GL` appears concretely in five +dependency records (`FrameRootDependencies.Gl`, `LivePresentationDependencies.Gl`, +`InteractionRetainedUiDependencies.Gl`, plus the host and settings phases), each +guarded by a `ReferenceEquals(_dependencies.Gl, platform.Graphics)` consistency +check that has to become backend-aware. `SettingsDevToolsComposition` is ImGui, +which is not ported and is deleted at V11, so the Vulkan arm simply omits DevTools. +`GameWindow.Run` opens a `ContextAPI.OpenGL` window with MSAA and stencil +attributes; the Vulkan arm needs `WindowOptions.DefaultVulkan` and the +instance/surface/device/swapchain sequence `VulkanBringUpHost.CreateWindow` +through `CreateFrameResources` already performs — that code is reusable, which is +the argument for reducing `VulkanBringUpHost` to a capability-probe harness rather +than deleting it outright. + +**Corrected sequence**, superseding §5.5.7's four steps: + +1. ~~The three validation defects~~ — done at V6g (§5.5.8). They came first + because every one is on any world frame's path, and because a host built on a + frame that fails validation cannot be debugged. +2. **The Vulkan composition host**, as specified above, delivering DAT load, + streaming, camera, entity table, session and the real retained UI on Vulkan, + with no world renderers. Its gate is an offline Vulkan launch reaching the real + composition with a captured UI frame, a converging ownership ledger at shutdown, + and the strict GL offline gate unmoved. +3. **V4t**, the texture stack, which the world arm cannot be written without. +4. **The world arm** — V4c/V4d's content behind the construction-time backend + selection at the frame-root seam. + +Step 2's own acceptance criterion — "the real UI renders" — is what makes it worth +doing before V4t rather than after: it is the first frame acdream draws on Vulkan +that is the *client's* frame rather than a scene written to prove the backend. + ### 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