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