docs(render): Campaign V slice V3 - clip-space and sRGB audit
V3 exists to verify the assumptions the whole Vulkan design rests on, before V6 builds on them. The central claim held. One plan assumption did not, and catching it here is the slice paying for itself. Confirmed: every projection reaching a shader is built by Matrix4x4.CreatePerspectiveFieldOfView, so NDC z is already [0,1] - Vulkan's own convention - and no projection rework is needed. There are no orthographic projections in production at all; the retained UI converts pixel coordinates straight to NDC with a constant z, so V4a has no matrix to convert. Phase U.3's clip planes are derived and compared entirely in clip space with plane.z always zero, making them insensitive to both the depth convention and the viewport Y flip. SkyProjection.WithDepthRange is the only hand-written matrix edit and it re-derives the same D3D-convention mapping rather than a GL-style depth scale. Corrected: the plan specified a B8G8R8A8_SRGB swapchain "matching the GL FramebufferSrgb contract." That contract does not exist. FramebufferSrgb is enabled only inside the throwaway capability probe and disabled immediately, never on the real backbuffer; no texture uses an sRGB internal format; no shader converts gamma. The renderer is UNORM end to end, so the correct swapchain format is B8G8R8A8_UNORM. Shipping _SRGB would have applied an encode to already-display-space values - a global brightening on every frame that nothing before V7 would have caught. Two acceptance items carried forward to V6/V7: the Vulkan encoder must flip scissor rectangles itself, because vkCmdSetScissor is top-left-origin and the negative viewport height does not affect it; and the V7 differential must launch both backends with ACDREAM_MSAA_SAMPLES=0, since MSAA is fixed at window creation and cannot be toggled mid-session. Filed #248 for FrustumCuller's near-plane extraction, which uses the GL [-1,1] Gribb-Hartmann formula against [0,1] matrices. It is provably over-inclusive rather than over-culling, and it is pure CPU math untouched by the backend swap, so it is tracked rather than fixed inside this campaign. No code changed, so the pixel gate is trivially satisfied. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
22b5abba57
commit
ec414d60cd
2 changed files with 108 additions and 6 deletions
|
|
@ -240,8 +240,9 @@ straight into `GpuMemoryTracker` for exact accounting, which VMA would obscure.
|
|||
write-to-array-then-`BufferSubData` (driver validation, copy, rename tracking)
|
||||
simply stops existing.
|
||||
- **Textures** — device-local pool. Formats stay UNORM (`BC1/2/3_UNORM`,
|
||||
`R8G8B8A8_UNORM`, `R8_UNORM`); sRGB correctness lives at the framebuffer, as
|
||||
it does on GL today. 2D arrays are allocated full-size and filled
|
||||
`R8G8B8A8_UNORM`, `R8_UNORM`), matching GL exactly. There is no sRGB anywhere
|
||||
in the pipeline — not on upload, not in the shaders, not at the framebuffer
|
||||
(V3 audit, §4.10). 2D arrays are allocated full-size and filled
|
||||
incrementally, mirroring `ManagedGLTextureArray`.
|
||||
- **Mip generation** — DAT surfaces ship no mips. Uncompressed formats get a
|
||||
`vkCmdBlitImage` chain at upload. **BC formats cannot be blit targets**, so
|
||||
|
|
@ -355,16 +356,76 @@ Budget: roughly 4–6 batched `vkCmdPipelineBarrier2` calls per frame.
|
|||
|
||||
Surface through Silk windowing (`GraphicsAPI.DefaultVulkan`, `IWindow.VkSurface`)
|
||||
so the existing GLFW platform selection, `ACDREAM_DISPLAY_PROTOCOL`, and window
|
||||
lifecycle are unchanged. Format `B8G8R8A8_SRGB` preferred (matching the GL
|
||||
`FramebufferSrgb` contract: shaders write linear, the attachment encodes);
|
||||
screenshots swizzle BGRA→RGBA on the CPU to preserve
|
||||
lifecycle are unchanged. Format **`B8G8R8A8_UNORM`** — see §4.10, this was
|
||||
corrected at V3 and is the single highest-severity finding of the audit.
|
||||
Screenshots swizzle BGRA→RGBA on the CPU to preserve
|
||||
`FrameScreenshotController`'s RGBA byte contract. Present modes: `FIFO` when
|
||||
VSync is on; `IMMEDIATE` preferred then `MAILBOX` when off, with
|
||||
`FramePacingController` and its platform waiters continuing to drive the software
|
||||
cap. `OUT_OF_DATE` recreates immediately, `SUBOPTIMAL` at the next frame
|
||||
boundary, both through `FramebufferResizeController`.
|
||||
|
||||
### 4.10 Capability gate
|
||||
### 4.10 V3 audit findings (2026-07-27)
|
||||
|
||||
Slice V3 audited every projection producer, every depth-range assumption, the
|
||||
clip-plane derivation, the sRGB path, and MSAA control. The central claim held —
|
||||
**but one plan assumption was wrong, and catching it is why V3 exists.**
|
||||
|
||||
**Confirmed.** Every projection that reaches a shader is built by
|
||||
`Matrix4x4.CreatePerspectiveFieldOfView` (world, portal tunnel, paperdoll,
|
||||
appraisal cameras; terrain, mesh, particles, debug lines and sky all consume the
|
||||
same matrices). There are **no orthographic projections in production code at
|
||||
all** — the retained UI's `ui_text.vert` converts pixel coordinates straight to
|
||||
NDC with a constant `z = 0`, so V4a has no matrix to convert, only a Y-sign to
|
||||
check. So: no projection rework, exactly as designed.
|
||||
|
||||
`SkyProjection.WithDepthRange` is the only hand-written matrix edit, assigning
|
||||
`M33`/`M43` directly. It re-derives the *same* D3D-convention near/far mapping
|
||||
(it even throws on an orthographic input) rather than a GL-style `2/(f-n)`
|
||||
scale — correct, but the sharpest edge in the codebase and a required
|
||||
cross-check at V6.
|
||||
|
||||
Phase U.3's clip planes are derived and consumed entirely in clip space with
|
||||
`plane.z` always 0, so they are insensitive to both the depth convention and the
|
||||
viewport Y flip. No change needed.
|
||||
|
||||
**Corrected — sRGB.** The plan previously specified a `B8G8R8A8_SRGB` swapchain
|
||||
"matching the GL `FramebufferSrgb` contract." That contract does not exist.
|
||||
`EnableCap.FramebufferSrgb` is enabled only inside the throwaway 2×2 capability
|
||||
probe (`GraphicalGlFunctionProbe.cs:419-429`) and disabled immediately; it is
|
||||
never enabled on the real backbuffer. No texture is uploaded in an sRGB internal
|
||||
format (`TextureFormatExtensions` has none), and no shader performs any gamma
|
||||
conversion. The renderer is plain UNORM end to end. **The correct Vulkan
|
||||
swapchain format is `B8G8R8A8_UNORM`**; shipping `_SRGB` would have applied an
|
||||
unwanted encode to already-display-space values — a global brightening across
|
||||
every frame, and precisely the failure mode §6 lists as "cannot pass silently."
|
||||
It would have passed silently right up to V7.
|
||||
|
||||
Separately: the capability gate *requires* sRGB-framebuffer support that the
|
||||
renderer never uses. Harmless today, but the Vulkan gate must not carry the
|
||||
stale requirement forward.
|
||||
|
||||
**MSAA.** `ACDREAM_MSAA_SAMPLES` overrides the quality preset
|
||||
(`QualityPreset.cs:43-59`) and `0` forces MSAA off, but it is read at window
|
||||
creation and cannot change mid-session. The V7 differential script must therefore
|
||||
*launch* both backends with `ACDREAM_MSAA_SAMPLES=0` rather than toggling a
|
||||
setting.
|
||||
|
||||
**Two concrete acceptance items carried to V6/V7.**
|
||||
|
||||
1. **Scissor Y convention.** `NdcScissorRect.ToPixels` emits GL bottom-left-origin
|
||||
pixel rectangles. Vulkan's `vkCmdSetScissor` is always top-left-origin — the
|
||||
negative viewport height does *not* flip the scissor. The contract already says
|
||||
callers keep GL convention and the backend converts
|
||||
(`IGpuPassEncoder.SetScissor`), so the Vulkan encoder must do that flip. A
|
||||
scissored aperture — a doorway — is the right differential-gate target.
|
||||
2. **`FrustumCuller` near plane** extracts `col4 + col3`, the GL `[-1,1]` Gribb-
|
||||
Hartmann formula, against `[0,1]`-convention matrices; the correct extraction
|
||||
is `col3` alone. Proven over-inclusive rather than over-culling, so it is not a
|
||||
visibility bug, and it is pure CPU math untouched by the backend swap. Filed as
|
||||
a tracked issue rather than fixed here — it is not Campaign V's scope.
|
||||
|
||||
### 4.11 Capability gate
|
||||
|
||||
Mirrors the GL three-layer shape exactly — passive record, **active** probes, an
|
||||
`Evaluate` that throws `NotSupportedException` → `Program.cs` exit code 4 → an
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue