// Single-resample warp-direct compositing — M4 step 2. // // Collapses the two-pass path (desktop -> eye texture -> warp -> panel, // two resamples, each one softens) into one: for each panel pixel, the // per-channel POLY3 warp gives a source UV in the eye's q-space window, // the UV maps linearly to a tangent-space ray in the eye (lens) frame // (the same grow-expanded intrinsics window the canted projection spans), // the ray is rotated to world and intersected with the screen plane // (z = quadZ), the hit point is tested against each screen rectangle, and // the desktop texture is sampled ONCE (trilinear, explicit gradients — // SampleGrad is legal in the divergent per-screen branch; mips matter, a // 4K monitor at 2 m is heavily minified). Distortion + lateral CA + scene // placement in one resample. CANTED cameras only (the locked render path). // // The two-pass path stays compiled behind the same frame loop for live // A/B ('w' key) until the step-2 gate passes. #pragma once #include #include #include "calib/hmd_config.h" namespace sauna { constexpr int kWarpDirectMaxScreens = 8; // HLSL cbuffer mirror (float4-aligned, 256-byte CB slot). The app fills // the pose-dependent fields per eye per frame; fillEyeConsts() fills the // calibration-derived ones once. struct WarpDirectCB { float centerScale[4]; // cx, cy, scale (0.5/(1+grow)), r2 cutoff float kr[4], kg[4], kb[4]; // POLY3 k1,k2,k3 per channel float colorMult[4]; // rgb, unused float tanLRTB[4]; // tangents at uv x=0, x=1, y=0(top), y=1(bottom) float origin[4]; // eye position in world (xyz), w unused float rwe[3][4]; // rows of eye->world rotation (R_head * e2h) float misc[4]; // screenCount, colorMultOn, r2Clamp, unused // Per-screen oriented plane basis (curved arrangement: each screen has // its own plane; the flat layout is just every normal = +Z). The // screen's unit normal is packed into the three w components. // p0n: top-left corner world position, n.x // un: u axis (unit right, scaled 1/width m) — dot(hit-p0, un) = u, n.y // vn: v axis (unit down, scaled 1/height m) — dot(hit-p0, vn) = v, n.z float p0n[kWarpDirectMaxScreens][4]; float un[kWarpDirectMaxScreens][4]; float vn[kWarpDirectMaxScreens][4]; }; // CB slot stride: root-CBV addresses must be 256-aligned; the struct is // 560 B, so slots are 768 apart. constexpr uint32_t kWarpDirectCbStride = 768; static_assert(sizeof(WarpDirectCB) <= kWarpDirectCbStride, "fits one CB slot"); class WarpDirect { public: bool init(ID3D12Device* dev, DXGI_FORMAT rtFormat, const HmdConfig& cfg); // Calibration-derived constants for one eye: warp polys, color mult, // q-space window tangents (the same unfolded grow-expanded intrinsics // window the canted projection uses). Pose fields left untouched. void fillEyeConsts(int eye, WarpDirectCB* cb) const; // Records the direct composite for one eye into its half of the panel // RT. cb = GPU VA of a filled WarpDirectCB (upload heap, app-owned // ring). screens = shader-visible table of screenCount SRVs (t0..), // order matching cb->rects. void record(ID3D12GraphicsCommandList* list, int eye, D3D12_GPU_VIRTUAL_ADDRESS cb, D3D12_GPU_DESCRIPTOR_HANDLE screens, uint32_t panelW, uint32_t panelH); private: Microsoft::WRL::ComPtr rootSig_; Microsoft::WRL::ComPtr pso_; float eyeConsts_[2][24]; // cx,cy,scale,cutoff, kr,kg,kb, colorMult, tans }; } // namespace sauna