Skip to main content

astrelis_render/
target.rs

1//! Render target abstraction for unified surface/framebuffer rendering.
2
3use crate::framebuffer::Framebuffer;
4
5/// Configuration for how to handle depth in a render pass.
6#[derive(Debug, Clone, Copy, Default)]
7pub enum DepthConfig<'a> {
8    /// No depth testing.
9    #[default]
10    None,
11    /// Use the provided depth view, loading existing values.
12    Load(&'a wgpu::TextureView),
13    /// Use the provided depth view, clearing to the specified value.
14    Clear(&'a wgpu::TextureView, f32),
15}
16
17/// A render target that can be either a window surface or an offscreen framebuffer.
18///
19/// This enum simplifies render pass setup by providing a unified interface
20/// for different rendering destinations.
21#[derive(Debug, Clone, Copy, Default)]
22pub enum RenderTarget<'a> {
23    /// Render to the window surface.
24    ///
25    /// The surface view is obtained from the [`Frame`] during render pass creation.
26    #[default]
27    Surface,
28
29    /// Render to the window surface with an attached depth buffer.
30    ///
31    /// This variant allows rendering to the surface while using a depth texture
32    /// for z-ordering, which is essential for UI systems and 3D overlays.
33    ///
34    /// # Example
35    ///
36    /// ```ignore
37    /// let depth_view = frame.window_depth_view().expect("Window has depth");
38    /// let mut pass = frame.render_pass()
39    ///     .target(RenderTarget::surface_with_depth_clear(&depth_view, 0.0))
40    ///     .clear_color(Color::BLACK)
41    ///     .build();
42    /// // ... rendering ...
43    /// ```
44    SurfaceWithDepth {
45        /// The depth texture view to attach.
46        depth_view: &'a wgpu::TextureView,
47        /// How to handle the depth buffer: None = load, Some(v) = clear to v.
48        clear_value: Option<f32>,
49    },
50
51    /// Render to an offscreen framebuffer.
52    ///
53    /// The framebuffer manages its own color, depth, and MSAA textures.
54    Framebuffer(&'a Framebuffer),
55}
56
57impl<'a> RenderTarget<'a> {
58    /// Create a surface target (no depth).
59    pub fn surface() -> Self {
60        RenderTarget::Surface
61    }
62
63    /// Create a surface target with a depth buffer that loads existing values.
64    pub fn surface_with_depth(depth: &'a wgpu::TextureView) -> Self {
65        RenderTarget::SurfaceWithDepth {
66            depth_view: depth,
67            clear_value: None,
68        }
69    }
70
71    /// Create a surface target with a depth buffer that clears to the specified value.
72    ///
73    /// For reverse-Z depth (recommended), use 0.0 as the clear value.
74    /// For standard depth, use 1.0 as the clear value.
75    pub fn surface_with_depth_clear(depth: &'a wgpu::TextureView, clear: f32) -> Self {
76        RenderTarget::SurfaceWithDepth {
77            depth_view: depth,
78            clear_value: Some(clear),
79        }
80    }
81
82    /// Create a framebuffer target.
83    pub fn framebuffer(fb: &'a Framebuffer) -> Self {
84        RenderTarget::Framebuffer(fb)
85    }
86
87    /// Check if this target is a surface (with or without depth).
88    pub fn is_surface(&self) -> bool {
89        matches!(
90            self,
91            RenderTarget::Surface | RenderTarget::SurfaceWithDepth { .. }
92        )
93    }
94
95    /// Check if this target is a framebuffer.
96    pub fn is_framebuffer(&self) -> bool {
97        matches!(self, RenderTarget::Framebuffer(_))
98    }
99
100    /// Get the framebuffer if this is a framebuffer target.
101    pub fn framebuffer_ref(&self) -> Option<&'a Framebuffer> {
102        match self {
103            RenderTarget::Framebuffer(fb) => Some(fb),
104            _ => None,
105        }
106    }
107
108    /// Get the texture format for this target.
109    ///
110    /// For framebuffers, returns the framebuffer's format.
111    /// For surfaces, returns None (format must be obtained from surface config).
112    pub fn format(&self) -> Option<wgpu::TextureFormat> {
113        match self {
114            RenderTarget::Surface | RenderTarget::SurfaceWithDepth { .. } => None,
115            RenderTarget::Framebuffer(fb) => Some(fb.format()),
116        }
117    }
118
119    /// Get the sample count for this target.
120    ///
121    /// For framebuffers, returns the framebuffer's sample count.
122    /// For surfaces, returns 1 (surfaces don't support MSAA directly).
123    pub fn sample_count(&self) -> u32 {
124        match self {
125            RenderTarget::Surface | RenderTarget::SurfaceWithDepth { .. } => 1,
126            RenderTarget::Framebuffer(fb) => fb.sample_count(),
127        }
128    }
129
130    /// Check if this target has MSAA enabled.
131    pub fn has_msaa(&self) -> bool {
132        self.sample_count() > 1
133    }
134
135    /// Check if this target has a depth buffer.
136    pub fn has_depth(&self) -> bool {
137        match self {
138            RenderTarget::Surface => false,
139            RenderTarget::SurfaceWithDepth { .. } => true,
140            RenderTarget::Framebuffer(fb) => fb.has_depth(),
141        }
142    }
143
144    /// Get the depth view if available.
145    pub fn depth_view(&self) -> Option<&'a wgpu::TextureView> {
146        match self {
147            RenderTarget::Surface => None,
148            RenderTarget::SurfaceWithDepth { depth_view, .. } => Some(depth_view),
149            RenderTarget::Framebuffer(fb) => fb.depth_view(),
150        }
151    }
152
153    /// Get the depth clear value if this target clears depth.
154    pub fn depth_clear_value(&self) -> Option<f32> {
155        match self {
156            RenderTarget::SurfaceWithDepth { clear_value, .. } => *clear_value,
157            _ => None,
158        }
159    }
160}
161
162impl<'a> From<&'a Framebuffer> for RenderTarget<'a> {
163    fn from(fb: &'a Framebuffer) -> Self {
164        RenderTarget::Framebuffer(fb)
165    }
166}