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}