astrelis_render/
framebuffer.rs1use crate::context::GraphicsContext;
4
5pub const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float;
7
8#[derive(Debug)]
10pub struct Framebuffer {
11 color_texture: wgpu::Texture,
12 color_view: wgpu::TextureView,
13 depth_texture: Option<wgpu::Texture>,
14 depth_view: Option<wgpu::TextureView>,
15 msaa_texture: Option<wgpu::Texture>,
16 msaa_view: Option<wgpu::TextureView>,
17 width: u32,
18 height: u32,
19 format: wgpu::TextureFormat,
20 sample_count: u32,
21}
22
23impl Framebuffer {
24 pub fn builder(width: u32, height: u32) -> FramebufferBuilder {
26 FramebufferBuilder::new(width, height)
27 }
28
29 pub fn color_texture(&self) -> &wgpu::Texture {
31 &self.color_texture
32 }
33
34 pub fn color_view(&self) -> &wgpu::TextureView {
36 &self.color_view
37 }
38
39 pub fn depth_texture(&self) -> Option<&wgpu::Texture> {
41 self.depth_texture.as_ref()
42 }
43
44 pub fn depth_view(&self) -> Option<&wgpu::TextureView> {
46 self.depth_view.as_ref()
47 }
48
49 pub fn msaa_texture(&self) -> Option<&wgpu::Texture> {
51 self.msaa_texture.as_ref()
52 }
53
54 pub fn msaa_view(&self) -> Option<&wgpu::TextureView> {
56 self.msaa_view.as_ref()
57 }
58
59 pub fn render_view(&self) -> &wgpu::TextureView {
61 self.msaa_view.as_ref().unwrap_or(&self.color_view)
62 }
63
64 pub fn resolve_target(&self) -> Option<&wgpu::TextureView> {
66 if self.msaa_view.is_some() {
67 Some(&self.color_view)
68 } else {
69 None
70 }
71 }
72
73 pub fn width(&self) -> u32 {
75 self.width
76 }
77
78 pub fn height(&self) -> u32 {
80 self.height
81 }
82
83 pub fn size(&self) -> (u32, u32) {
85 (self.width, self.height)
86 }
87
88 pub fn format(&self) -> wgpu::TextureFormat {
90 self.format
91 }
92
93 pub fn sample_count(&self) -> u32 {
95 self.sample_count
96 }
97
98 pub fn has_msaa(&self) -> bool {
100 self.sample_count > 1
101 }
102
103 pub fn has_depth(&self) -> bool {
105 self.depth_texture.is_some()
106 }
107
108 pub fn resize(&mut self, context: &GraphicsContext, width: u32, height: u32) {
110 if self.width == width && self.height == height {
111 return;
112 }
113
114 let new_fb = FramebufferBuilder::new(width, height)
115 .format(self.format)
116 .sample_count_if(self.sample_count > 1, self.sample_count)
117 .depth_if(self.depth_texture.is_some())
118 .build(context);
119
120 *self = new_fb;
121 }
122}
123
124pub struct FramebufferBuilder {
126 width: u32,
127 height: u32,
128 format: wgpu::TextureFormat,
129 sample_count: u32,
130 with_depth: bool,
131 label: Option<&'static str>,
132}
133
134impl FramebufferBuilder {
135 pub fn new(width: u32, height: u32) -> Self {
137 Self {
138 width,
139 height,
140 format: wgpu::TextureFormat::Rgba8UnormSrgb,
141 sample_count: 1,
142 with_depth: false,
143 label: None,
144 }
145 }
146
147 pub fn format(mut self, format: wgpu::TextureFormat) -> Self {
149 self.format = format;
150 self
151 }
152
153 pub fn with_msaa(mut self, sample_count: u32) -> Self {
155 self.sample_count = sample_count;
156 self
157 }
158
159 pub fn sample_count_if(mut self, condition: bool, sample_count: u32) -> Self {
161 if condition {
162 self.sample_count = sample_count;
163 }
164 self
165 }
166
167 pub fn with_depth(mut self) -> Self {
169 self.with_depth = true;
170 self
171 }
172
173 pub fn depth_if(mut self, condition: bool) -> Self {
175 self.with_depth = condition;
176 self
177 }
178
179 pub fn label(mut self, label: &'static str) -> Self {
181 self.label = Some(label);
182 self
183 }
184
185 pub fn build(self, context: &GraphicsContext) -> Framebuffer {
187 let label_prefix = self.label.unwrap_or("Framebuffer");
188
189 let size = wgpu::Extent3d {
190 width: self.width,
191 height: self.height,
192 depth_or_array_layers: 1,
193 };
194
195 let color_texture = context.device.create_texture(&wgpu::TextureDescriptor {
197 label: Some(&format!("{} Color", label_prefix)),
198 size,
199 mip_level_count: 1,
200 sample_count: 1,
201 dimension: wgpu::TextureDimension::D2,
202 format: self.format,
203 usage: wgpu::TextureUsages::RENDER_ATTACHMENT
204 | wgpu::TextureUsages::TEXTURE_BINDING
205 | wgpu::TextureUsages::COPY_SRC,
206 view_formats: &[],
207 });
208
209 let color_view = color_texture.create_view(&wgpu::TextureViewDescriptor::default());
210
211 let (msaa_texture, msaa_view) = if self.sample_count > 1 {
213 let texture = context.device.create_texture(&wgpu::TextureDescriptor {
214 label: Some(&format!("{} MSAA", label_prefix)),
215 size,
216 mip_level_count: 1,
217 sample_count: self.sample_count,
218 dimension: wgpu::TextureDimension::D2,
219 format: self.format,
220 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
221 view_formats: &[],
222 });
223 let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
224 (Some(texture), Some(view))
225 } else {
226 (None, None)
227 };
228
229 let (depth_texture, depth_view) = if self.with_depth {
231 let depth_sample_count = if self.sample_count > 1 {
232 self.sample_count
233 } else {
234 1
235 };
236
237 let texture = context.device.create_texture(&wgpu::TextureDescriptor {
238 label: Some(&format!("{} Depth", label_prefix)),
239 size,
240 mip_level_count: 1,
241 sample_count: depth_sample_count,
242 dimension: wgpu::TextureDimension::D2,
243 format: DEPTH_FORMAT,
244 usage: wgpu::TextureUsages::RENDER_ATTACHMENT
245 | wgpu::TextureUsages::TEXTURE_BINDING,
246 view_formats: &[],
247 });
248 let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
249 (Some(texture), Some(view))
250 } else {
251 (None, None)
252 };
253
254 Framebuffer {
255 color_texture,
256 color_view,
257 depth_texture,
258 depth_view,
259 msaa_texture,
260 msaa_view,
261 width: self.width,
262 height: self.height,
263 format: self.format,
264 sample_count: self.sample_count,
265 }
266 }
267}