Skip to main content

myth_render/graph/
render_state.rs

1//! Render State
2//!
3//! Manages per-frame render state (camera, time, and other global uniforms).
4
5use std::sync::atomic::{AtomicU32, Ordering};
6
7use myth_resources::buffer::CpuBuffer;
8use myth_resources::uniforms::RenderStateUniforms;
9use myth_scene::camera::RenderCamera;
10
11use crate::renderer::FrameTime;
12
13// ─── Debug View Target (compile-time gated) ─────────────────────────────────
14
15/// Semantic identifier for an intermediate render texture to visualise.
16///
17/// This enum lives in the **state layer** — it carries no frame-specific
18/// physical IDs (`TextureNodeId`).  The [`FrameComposer`] resolves it each
19/// frame into a concrete RDG resource, safely handling cases where the
20/// target texture was not produced (e.g. SSAO disabled).
21#[cfg(feature = "debug_view")]
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub enum DebugViewTarget {
24    /// No debug overlay — show the final tonemapped image.
25    None,
26    /// Main scene depth buffer (reverse-Z, linearised for display).
27    SceneDepth,
28    /// View-space normals from the geometry prepass.
29    SceneNormal,
30    /// Screen-space velocity buffer (TAA reprojection vectors).
31    Velocity,
32    /// Raw SSAO term before spatial blur.
33    SsaoRaw,
34    /// First mip level of the Bloom downsample chain.
35    BloomMip0,
36}
37
38#[cfg(feature = "debug_view")]
39impl Default for DebugViewTarget {
40    fn default() -> Self {
41        Self::None
42    }
43}
44
45#[cfg(feature = "debug_view")]
46impl DebugViewTarget {
47    /// Display label for the UI combo box.
48    pub const fn label(self) -> &'static str {
49        match self {
50            Self::None => "Final Image",
51            Self::SceneDepth => "Scene Depth",
52            Self::SceneNormal => "Scene Normal",
53            Self::Velocity => "Velocity Buffer",
54            Self::SsaoRaw => "SSAO Raw",
55            Self::BloomMip0 => "Bloom Mip 0",
56        }
57    }
58
59    /// WGSL `view_mode` uniform value for the debug shader.
60    ///
61    /// | Mode | Mapping |
62    /// |------|---------|
63    /// | 0    | RGB pass-through |
64    /// | 1    | Single-channel R → grayscale |
65    /// | 2    | Signed vector `[-1,1]` → `[0,1]` |
66    /// | 3    | Linear depth visualisation |
67    pub const fn view_mode(self) -> u32 {
68        match self {
69            Self::None => 0,
70            Self::SceneDepth => 3,
71            Self::SceneNormal => 2,
72            Self::Velocity => 2,
73            Self::SsaoRaw => 1,
74            Self::BloomMip0 => 0,
75        }
76    }
77}
78
79static NEXT_RENDER_STATE_ID: AtomicU32 = AtomicU32::new(0);
80
81pub struct RenderState {
82    pub id: u32,
83    uniforms: CpuBuffer<RenderStateUniforms>,
84    /// Previous frame's view-projection matrix (for TAA reprojection).
85    prev_view_projection: glam::Mat4,
86    /// Previous frame's jitter (for TAA de-jitter).
87    prev_jitter: glam::Vec2,
88    /// Previous frame's jitter-free VP matrix (for velocity calculation).
89    prev_unjittered_vp: glam::Mat4,
90    /// Active debug-view target (semantic intent, resolved per-frame).
91    #[cfg(feature = "debug_view")]
92    pub debug_view_target: DebugViewTarget,
93}
94
95impl Default for RenderState {
96    fn default() -> Self {
97        Self::new()
98    }
99}
100
101impl RenderState {
102    pub fn new() -> Self {
103        Self {
104            id: NEXT_RENDER_STATE_ID.fetch_add(1, Ordering::Relaxed),
105            uniforms: CpuBuffer::new(
106                RenderStateUniforms::default(),
107                wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
108                Some("RenderState Uniforms"),
109            ),
110            prev_view_projection: glam::Mat4::IDENTITY,
111            prev_jitter: glam::Vec2::ZERO,
112            prev_unjittered_vp: glam::Mat4::IDENTITY,
113            #[cfg(feature = "debug_view")]
114            debug_view_target: DebugViewTarget::None,
115        }
116    }
117
118    pub fn uniforms(&self) -> &CpuBuffer<RenderStateUniforms> {
119        &self.uniforms
120    }
121
122    pub fn uniforms_mut(&mut self) -> myth_resources::buffer::BufferGuard<'_, RenderStateUniforms> {
123        self.uniforms.write()
124    }
125
126    pub fn update(&mut self, camera: &RenderCamera, frame_time: FrameTime) {
127        let prev_vp = self.prev_view_projection;
128        let prev_j = self.prev_jitter;
129        let prev_unjittered_vp = self.prev_unjittered_vp;
130
131        let unjittered_vp = camera.unjittered_projection * camera.view_matrix;
132
133        let mut u = self.uniforms_mut();
134        u.view_projection = camera.view_projection_matrix;
135        u.view_projection_inverse = camera.view_projection_matrix.inverse();
136        u.projection_matrix = camera.projection_matrix;
137        u.projection_inverse = camera.projection_matrix.inverse();
138        u.view_matrix = camera.view_matrix;
139        u.prev_view_projection = prev_vp;
140        u.unjittered_view_projection = unjittered_vp;
141        u.prev_unjittered_view_projection = prev_unjittered_vp;
142        u.camera_position = camera.position.into();
143        u.time = frame_time.time % 7200.0; // Wrap time to avoid precision issues in shaders
144        u.time_cycle_2pi = frame_time.time % std::f32::consts::PI * 2.0;
145        u.delta_time = frame_time.delta_time;
146        u.jitter = camera.jitter;
147        u.prev_jitter = prev_j;
148        u.camera_near = camera.near;
149        u.camera_far = camera.far;
150        drop(u);
151
152        // Latch current values for next frame.
153        self.prev_view_projection = camera.view_projection_matrix;
154        self.prev_jitter = camera.jitter;
155        self.prev_unjittered_vp = unjittered_vp;
156    }
157}