# Scratch ## Current Slice D3D12 migration foothold. D3D11 remains the visual reference renderer; the host now talks through `IAquariumRenderer`, and `--renderer d3d12` selects a D3D12 backend with device/queue/swapchain/RTV heap/per-frame command allocators/fence, plus a shader-visible descriptor arena, per-frame upload constant buffers, renderer-owned offscreen render target, fullscreen root signature, and smoke-test PSO that draws a diagnostic fullscreen triangle into the offscreen target before sampling it back to the swapchain. Render targets now support SRV/UAV descriptors, the smoke pass writes a diagnostic UAV through a per-frame transient descriptor without consuming a persistent static UAV slot, and resize rebuilds static shader/RTV descriptor arenas after a GPU wait instead of consuming fixed bring-up slots. Next renderer work can start porting real passes while keeping D3D11 as the visual reference. ## Hot Context - Root: `E:\Projects\Aquarium-Engine` - Upstream: `https://github.com/GameCult/Aquarium-Engine.git` - Live branch: `main` - Main verification: - `dotnet build Aquarium.Engine.slnx` - `.\scripts\dev-reload.ps1 -Headless -RetainSlots 4` - Prototype histories remain external references. Do not re-import their source trees into this engine repo. - Current cloud prototype is deliberately not the final volume pass. Next renderer steps: debug views, field instance contract, then explicit local volume/froxel pass. - First artifact found: near ellipsoid edges can occlude farther clouds if the SDF boundary emits a positive-distance absorbing veil. Keep density inside the sculpted container, eroded, feathered, and low-extinction. - Follow-up fix made the march structural: intersect each analytic cloud ellipsoid, jump empty space to the next true entry, and sample only inside cloud intervals so near containers cannot eat the budget before far clouds. - Method correction: stop iterating cloud features by agile half-step. Build the target renderer as a waterfall machine: field contract, broad phase, explicit local volume pass, composition, then acceleration. Current shader cloud code is temporary scaffolding only. - Current scene correction: analytic cloud scaffold is no longer composited. Bodies render as solids; the Grid traces separately and blends as a transparent schematic overlay so future volumetrics can own actual scene mass. - Grid transparency correction: do not use alpha blending over raymarched scene content. Use Aetheria-style stochastic coverage and only draw the Grid when it is nearer than the nearest solid body, so it does not overlay planets. - Pulled Aetheria's `Assets/Resources/LDR_LLL1_0.png` into Aquarium as source PNG plus runtime `R8_UNorm` bytes. The renderer binds it as `t2` with wrapping point sampling for stochastic Grid coverage. - Dev watcher fix: restart fingerprint now includes `src/Aquarium.Engine/Assets` so runtime content changes trigger apphost rebuild/restart instead of relying on shader or live assembly reload. - Dev watcher heartbeat fix: watcher now checks the recorded script-owned PID from the visible/headless dev-reload state and relaunches if that process is gone. It also fingerprints `scripts/*.ps1` so script changes are treated as restart-owned source changes. - Temporal pass: added clean-room TSR-inspired Gate 1. Scene renders into HDR/travel texture, resolve pass reprojects by current/previous camera basis, clamps history to current 3x3 neighborhood, rejects by travel delta, writes ping-pong history, and tonemaps after resolve. - Temporal Gate 2A: added scene/history metadata storing material id and normal. Resolve now rejects material mismatches and normal discontinuities. Grid stochastic surfaces record Grid travel/normal even when dither misses. - Temporal Gate 2B: field id replaces coarse material id for TAA identity. Each planet has a stable field id and analytic previous-center reprojection so orbiting body history follows the body instead of camera-only motion. - Temporal Gate 3A: scene pass now writes a temporal-control target separate from field/normal metadata. Current channels are reactive strength, stochastic coverage, reserved medium opacity, and reserved spare; the resolve uses the first two to reduce history for low-coverage reactive Grid samples. - Temporal Gate 3B: temporal control now has its own ping-pong history. Resolve samples previous control at the reprojected UV and reduces history across coverage or reserved medium-opacity discontinuities. - Temporal Gate 3C: temporal-control history `w` stores accepted history age. Validation grows age, failed validation resets it, and history authority ramps with age instead of fully trusting fresh samples immediately. - Temporal Gate 3D: travel validation compares previous history travel against the reprojected point's expected distance from the previous camera. Do not compare previous travel directly to current travel under camera motion. - Temporal Gate 3E: stochastic Grid history bypasses opaque-surface color clamp and color-delta rejection. A current dither miss is not evidence that the true Grid color is background. - Temporal debug keys: F1 cycles, 0 final, 1 raw current scene, 2 reprojected history, 3 history age, 4 history weight, 5 current temporal control, 6 current field identity. Use these to prove whether the presented frame is resolved and whether history is alive. - Temporal Gate 3F: Grid stochastic coverage now uses high-retention history without coverage/reactive authority penalties. Coverage remains a continuity signal, not a reason to throw away the accumulation low-alpha lines require. - Temporal Gate 3G: identity/control buffers are point-loaded and Grid history color uses nearest identity-aligned sampling. This prevents foreground glow from bilinearly bleeding into background Grid history at silhouettes. - Temporal Gate 3H: Grid temporal coverage is line support only, not broad overlay alpha. Current support gates Grid history so old stochastic hits do not survive as fine smoke across the surface. - Temporal Gate 3I: Grid resolve reconstructs analytic overlay color at the current world hit and uses premultiplied color as the current estimate. Raw scene debug mode may still be noisy; final no longer treats stochastic hit/miss color as ground truth. - Temporal Gate 3J: debug mode 1 is raw scene again. Final Grid pixels use the analytic current estimate directly instead of blending previous Grid color history; history debug surfaces remain diagnostics. - Temporal Gate 3M: final presentation now cuts temporal color history for all surfaces while jitter is disabled. History/color/control ping-pongs remain diagnostics and future volumetric scaffolding, not visible authority. - Temporal debug correction: mode 5 exposes current temporal control. The first look showed the entire Grid red because stale Grid logic marked low support as reactive. Grid reactive is now zero; support/coverage remains the green signal. Empty background coverage is now zero; opaque hits write coverage 1 and Grid hits write support coverage. - Temporal Gate 3L: projection jitter is scaled to zero because Self/planet surfaces visibly juddered. Keep jitter disabled until opaque and Grid resolve both hide the offset. Removed the abandoned unjittered Grid retrace; Grid analytic final now uses the existing current hit while jitter is zero. - HDR/PBR research pass: `research/rendering/hdr-pbr-presentation.md` says the next renderer work before the SDF field renderer is explicit exposure, pre-tonemap bloom/veil, and display-transform/luminance debug views. Avoid threshold glow; bloom spreads energy already present in scene-linear HDR. - HDR implementation pass: renderer now has fixed manual exposure, half/quarter/ eighth bloom targets, firefly-safe downsample, separable horizontal/vertical blur per level, pre-tonemap bloom/veil composite, mode 7 bloom contribution, and mode 8 exposed luminance. - Debug UI pass: Aquarium now has a native Direct2D/DirectWrite debug panel toggled with F2. It uses a CultLib-inspired code-first control API with retained bound sliders/toggles/buttons instead of Unity prefabs or ImGui chrome. First live controls are render debug mode, exposure, bloom intensity, and bloom veil; the HDR controls feed shader constants. Important UI lessons: Vortice `Rect` is x/y/width/height, so use `RectFromEdges` for edge math; rows/buttons/tooltips are flat Direct2D overlay UI; sliders need element-specific base/hover/active states where track/fill and handle brighten independently without geometry changes. To brighten saturated orange, lerp toward white instead of trying to increase HSV value. - Global graphics settings pass: render debug mode, exposure, bloom intensity, and bloom veil now live in the typed `epiphany.aquarium.graphics_settings` CultCache document. `GraphicsSettings` is a contracts DTO shared by host, live runtime, and renderer. The UI still binds to renderer properties, the host syncs changed renderer settings back to the live runtime, and the runtime flushes dirty settings on the normal state interval, shutdown, and before live assembly reload. - Fog-land Gate 1 foothold: renderer now packs a fixed field instance structured buffer for Self, planets, and local medium ellipsoids. HLSL consumes the buffer for registered medium density/transmittance diagnostics. Debug mode 9 shows medium density, mode 10 shows transmittance. Final scene composition is still untouched until the explicit low-res volume pass exists. - Fog-land Gate 3 foothold: registered medium integration moved out of the scene shader into a half-resolution medium pass. The texture stores density, transmittance, and source diagnostics. Mode 11 shows medium source. Final scene color remains unchanged; composition is still the next machine part. - Fog composition gate: the medium pass now writes separate diagnostic and transport targets. Resolve can composite `surface * transmittance + in-scattering`, controlled by persisted `MediumCompositeIntensity`, which defaults to zero so final remains unchanged until deliberately enabled/tuned. - Medium density correction: the medium pass now stores a 4x4 frustum slice atlas instead of a finished screen-space ray integral. Each slice texel reconstructs a world point and evaluates field-local density there; resolve integrates the stored cells front-to-back. Removed the time term from erosion noise so the texture itself is stationary under inspection. - Follow-up correction: mode 9 is no longer the frustum atlas mean or a single `z = 0` slice. It is a world-anchored density column over the Grid plane, so current above/below medium ellipsoids are actually visible. Modes 10/11 remain transport diagnostics; source debug is boosted separately from physical transport so black-on-black patches do not pass as information. - Debug UI control grammar now includes named option rows. Render Debug uses an option/dropdown selector instead of a numeric slider so the expanding debug mode list remains legible. - Dev watcher correction: live runtime reload now waits for the host stdout log to acknowledge the exact new live DLL path before declaring success. A pointer write alone is not proof. `dev-watch.ps1 -ReopenWhenClosed` reopens the last visible slot without rebuilding when the window has been closed. - Dev watcher failure found in practice: `Start-Process -Wait` around `dev-reload.ps1` could block after the app launched, leaving the watcher stuck at initial launch and never polling. The watcher now invokes dev-reload synchronously and leaves long-lived process ownership inside dev-reload. - CultCache startup failure found: dev cache `artifacts/dev-reload/cultcache/aquarium-client.msgpack` was zero bytes, so MessagePack threw `EndOfStreamException` before schema guardrails could run. Aquarium now quarantines unreadable snapshots with a `.corrupt-` suffix and boots fresh state; CultLib single-file stores now write through a temp file and atomic replace to avoid future zero-byte live snapshots. - Headless dev-reload correction: `-Headless` now creates a hidden Win32 window, waits for the two-frame run to exit, kills and fails on timeout, and logs renderer startup checkpoints to stdout. This exposed the real shader compile failure: the medium density-column debug loop must stay rolled because forcing an unroll expands the registered medium density stack beyond D3DCompiler's tolerance. - Medium debug correction: "Medium Source" was a misleading view. In volume rendering source should mean injected/in-scattered radiance from a real light field, but Aquarium does not have that injection stage yet. Debug modes now separate direct camera-ray density, direct camera-ray transmittance, and the low-resolution atlas density diagnostic. - Medium ray preview UI: debug controls can now be conditionally visible. `GraphicsSettings.MediumDebugStep` persists a ray sample index. The Ray Step slider appears only in Medium Ray Density / Medium Ray Transmittance modes; density mode shows density at that selected sample, and transmittance mode shows accumulated survival to that sample. - Medium ray preview binding fix: the resolve shader uses `registeredMediumDensity` for ray preview modes, so resolve must bind the field instance structured buffer at `t12`. The medium atlas pass already had the buffer, which is why atlas density could show blobs while ray density and ray transmittance were empty at every step. - Medium composite correction: final composition must not use the diagnostic 4x4 frustum atlas. The atlas intentionally reveals the current low-res pass artifact; composite now uses the same direct registered-medium ray integration as ray debug, clamped to the visible surface travel. Atlas density remains a diagnostic view only. - First real froxel volume pass: the failed 4x4 MAD path has been replaced by a packed 160x90x32-at-720p frustum froxel volume stored as an 8x4 D3D11 atlas. The medium pass writes per-froxel density/transmittance/in-scattering, resolve integrates the froxel transport for final composite, and direct ray debug remains the reference for checking shape and continuity. - Grid/fog composition correction: Grid travel is still written for temporal stability, but the Grid is not a solid medium terminator. Froxel composition stops at Self/planet solids only; Grid pixels integrate medium to the visible far distance so the schematic overlay cannot cut holes through fog or occlude itself. - D3D12 migration foothold: D3D11 is frozen as the reference backend, and the host now selects `d3d11` or `d3d12` through `--renderer` / `AQUARIUM_RENDERER`. The D3D12 backend currently proves lifecycle only: device, command queue, flip-discard swapchain, RTV heap, command allocator, graphics command list, fence wait, clear, and present. Do not build the transparent candidate pipe on D3D11 debt; port explicit D3D12 resource/pass ownership first. - D3D12 pass spine pass: the backend now uses one command allocator per swapchain buffer instead of stalling a single allocator each frame. It also compiles `D3D12Smoke.hlsl`, creates a fullscreen root signature and graphics PSO, binds viewport/scissor/RTV, and draws a fullscreen triangle. This proves shader, root signature, PSO, and draw submission before real Aquarium passes migrate. - D3D12 descriptor/upload foothold: the D3D12 path now owns a shader-visible CBV/SRV/UAV descriptor arena and per-frame upload constant buffers. The smoke pass binds a CBV descriptor table at b0 and uploads frame/debug/HDR-tinted constants before drawing. This is still diagnostic, but it proves descriptor heap visibility, root descriptor table binding, and per-frame CPU-to-GPU upload ownership. - D3D12 render-target foothold: the smoke pass now renders into a renderer-owned default-heap offscreen render target with explicit RTV descriptor ownership and tracked state transitions, then copies that texture into the swapchain backbuffer. This proves render-target resource lifetime and copy barriers before Aquarium scene/history/froxel targets are ported. - D3D12 research pass: `research/d3d12/synthesis.md` and `docs/d3d12-best-practices-audit.md` distill Microsoft/NVIDIA/AMD guidance. Current D3D12 path is valid bring-up scaffolding, but do not port real passes until descriptor allocation is split into static/transient ranges, upload data uses a fence-reclaimed ring, resource ownership is named/tracked, and debug names/PIX markers exist. - D3D12 full-ass foundation pass: D3D12 objects now have debug names, the frame and smoke/copy passes emit command-list events for PIX-style inspection, swapchain backbuffers are state-tracked resources, and per-frame constants are allocated from a persistent mapped upload ring after the frame fence wait. - D3D12 descriptor/registry pass: shader-visible descriptors are split into static and per-frame transient arenas. Transient descriptors reset after the frame fence wait, named resources live in `D3D12ResourceRegistry`, and one-second capacity diagnostics report upload ring, transient/static shader descriptor, and RTV usage. - D3D12 SRV/resize pass: render targets can now own SRV descriptors, the smoke target is sampled by a second fullscreen pass instead of copied as a raw resource, and the renderer interface passes current window dimensions so D3D12 can recreate swapchain buffers and dependent smoke resources on resize. Resize still consumes fixed bring-up descriptors and fails loudly before exhaustion; production descriptor reclamation/rebuild policy remains before serious pass migration. - D3D12 UAV/resize-rebuild pass: render targets can own UAV descriptors, the D3D12 smoke pass binds a transient per-frame UAV descriptor and writes a diagnostic target from the pixel shader. Pixel shader UAVs share output register namespace, so the diagnostic UAV binds at `u1` rather than `u0`. UAV resource capability is now independent from persistent UAV descriptor ownership, so transient diagnostic UAVs do not burn static descriptors. D3D12 ignores the D3D11 `--shader-source` override and compiles its own copied `D3D12Smoke.hlsl`. Resize now waits for the GPU, releases swapchain-dependent resources, rebuilds static shader/RTV descriptor arenas, and recreates backbuffer/smoke/diagnostic resources from a clean descriptor state. - D3D12 Grid height migration correction: the first version still did a fullscreen per-texel body loop. That was the wrong architecture. The D3D12 Grid height pass now behaves like Aetheria's gravity layer: a base Grid field draw, then one additive up-facing quad per body, driven by a small uploaded brush table. The target is scalar `R16_Float`: height is one value, and additive blending works on that format where `R32G32B32A32_Float` was the wrong bet. Render debug mode `11` displays the height target. Temporal previous camera/Grid/time state is renderer-level state, not swapchain-frame-resource state; swapchain image index is not frame history. Next D3D12 migration step is the froxel/field resource upload path. - D3D12 froxel/field upload pass: D3D12 now builds the froxel primitive id table and field instance table, uploads them through the per-frame upload ring, copies them into default-heap structured buffers, and binds transient SRVs to the diagnostic shader. This proves the real field data path without forcing shaders to read directly from upload heap memory. The table-building code is duplicated from D3D11 for this slice; extract it into one shared field ABI owner before porting the next field consumer.