diff --git a/docs/plans/2026-07-27-vulkan-campaign.md b/docs/plans/2026-07-27-vulkan-campaign.md index e7066614..36fa9dc9 100644 --- a/docs/plans/2026-07-27-vulkan-campaign.md +++ b/docs/plans/2026-07-27-vulkan-campaign.md @@ -435,7 +435,7 @@ because sample positions are not specified across implementations. |---|---|---| | **V0** ✅ | Pinned RHI contract, `RecordingGpuDevice`, contract tests, this document, roadmap entry. | build + tests + contract tests | | **V1** ✅ | GL backend: `GlGpuDevice` (no Chorizite inheritance), buffers (`BufferSubData`, behaviour-preserving), ring over the existing fence-bounded pattern, textures + the binding-9 handle table, samplers, pipelines, timers, backbuffer capture. Constructed in composition (`HostInputCameraCompositionPhase`, right after the frame-flight controller); no consumers yet. | build + tests + GL unit tests + pixel gate (trivially identical — a tripwire) | -| **V2** | Shader dialect + texture-index migration **on GL**: `uvec2 textureHandle` → `uint textureIndex`, binding-9 table, `common.glsl` preamble, CPU batch-struct change, caches registering into the device table. Sub-commits: V2a mesh, V2b terrain, V2c particles. | pixel gate per sub-commit | +| **V2** | Shader dialect + texture-index migration **on GL**: `uvec2 textureHandle` → `uint textureIndex`, a binding-9 handle table, `common.glsl` preamble, CPU batch-struct change. Sub-commits: V2a mesh, V2b terrain, V2c particles. **The table is owned by the existing texture caches, NOT by `GlGpuDevice`** — see the note below. | pixel gate per sub-commit | | **V3** | Clip-space and sRGB audit: verify every projection producer is [0,1] convention, confirm clip-plane derivation, record the sRGB swapchain decision and the depth-precision divergence class here. | pixel gate + connected lifecycle | | **V4a** | `TextRenderer` (three fence-buffered VBO sets → ring allocations), `BitmapFont`, `DebugLineRenderer`, the UI RenderSurface upload path, `UiViewport`'s texture handoff. | pixel gate (UI-heavy checkpoints) | | **V4b** | `GlobalMeshBuffer` + `ObjectMeshManager` onto `IGpuBuffer`; arena, LRU and ledger logic untouched. | pixel gate | @@ -453,6 +453,27 @@ because sample positions are not specified across implementations. | **V10** | Cutover: Vulkan default, GL reachable by env var for one slice, gate scripts default to VK. | complete Release suite + retail expected PNGs **on VK** (baselines not regenerated) + both connected routes + **user visual sign-off** | | **V11** | GL deletion and closeout: delete `Gpu/Gl`, `OpenGLGraphicsDevice`, `ManagedGL*`, `GLSLShader`, `GLHelpers`, `GLStateScope`, `RenderStateCache`, `BindlessSupport`, `GraphicalGlFunctionProbe`, the GL branch in `GameWindow`, the ImGui project and Studio; drop the GL and (if the audit is clean) Chorizite packages; file the retained-UI dev-panels follow-up; swap CI assertions to VK; update the divergence register, architecture doc, code-structure doc, and rendering memory crib; re-measure memory. | complete Release suite + both connected routes + working-set re-measure | +### 5.2 Why V2's handle table is not the device's table + +The obvious reading of V2 — "have the texture caches call +`IGpuDevice.RegisterTexture`" — does not work at V2, and the reason is worth +recording so nobody re-derives it later. + +`GlGpuDevice` flushes its dirty texture table immediately before each draw it +records. At V2 the draws still go through raw GL inside `WbDrawDispatcher`, which +the device knows nothing about, so the device would never flush — the table would +be stale on the GPU. Making it work would need a manual `FlushTextureTable()` +escape hatch plus a way to bind the device's buffer from raw GL code, which leaks +the backend straight back through the seam we are building. + +So V2 keeps the indirection entirely inside the existing GL world: the texture +caches own a small handle-table storage buffer at binding 9 and flush it on their +existing schedule. **V4c then deletes that interim table** when `WbDrawDispatcher` +moves onto the encoder and the device's table — with its retirement-gated slot +recycling — becomes reachable. Two small, separately pixel-gated changes beat one +entangled one; separating the data-model change from the RHI plumbing change is +precisely what de-risks V4c, the largest slice in the campaign. + **Sequencing invariants.** The app ships on GL until V10. V0→V1→V2→V3→V4a…V4h are strictly sequential. The only permitted parallelism is V5 alongside V4d and/or V4f (fully disjoint files), and optionally V9's `.github`/`tools`-only