astrelis_render/
framebuffer.rs1use astrelis_core::profiling::profile_function;
4
5use crate::context::GraphicsContext;
6use crate::types::GpuTexture;
7
8pub const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float;
10
11pub struct Framebuffer {
13 color: GpuTexture,
15 depth: Option<GpuTexture>,
17 msaa: Option<GpuTexture>,
19 sample_count: u32,
21}
22
23impl std::fmt::Debug for Framebuffer {
24 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25 f.debug_struct("Framebuffer")
26 .field("width", &self.color.width())
27 .field("height", &self.color.height())
28 .field("format", &self.color.format())
29 .field("sample_count", &self.sample_count)
30 .field("has_depth", &self.depth.is_some())
31 .field("has_msaa", &self.msaa.is_some())
32 .finish()
33 }
34}
35
36impl Framebuffer {
37 pub fn builder(width: u32, height: u32) -> FramebufferBuilder {
39 FramebufferBuilder::new(width, height)
40 }
41
42 pub fn color_texture(&self) -> &wgpu::Texture {
44 use crate::extension::AsWgpu;
45 self.color.as_wgpu()
46 }
47
48 pub fn color_view(&self) -> &wgpu::TextureView {
50 self.color.view()
51 }
52
53 pub fn depth_texture(&self) -> Option<&wgpu::Texture> {
55 use crate::extension::AsWgpu;
56 self.depth.as_ref().map(|d| d.as_wgpu())
57 }
58
59 pub fn depth_view(&self) -> Option<&wgpu::TextureView> {
61 self.depth.as_ref().map(|d| d.view())
62 }
63
64 pub fn msaa_texture(&self) -> Option<&wgpu::Texture> {
66 use crate::extension::AsWgpu;
67 self.msaa.as_ref().map(|m| m.as_wgpu())
68 }
69
70 pub fn msaa_view(&self) -> Option<&wgpu::TextureView> {
72 self.msaa.as_ref().map(|m| m.view())
73 }
74
75 pub fn render_view(&self) -> &wgpu::TextureView {
77 self.msaa
78 .as_ref()
79 .map(|m| m.view())
80 .unwrap_or(self.color.view())
81 }
82
83 pub fn resolve_target(&self) -> Option<&wgpu::TextureView> {
85 if self.msaa.is_some() {
86 Some(self.color.view())
87 } else {
88 None
89 }
90 }
91
92 pub fn width(&self) -> u32 {
94 self.color.width()
95 }
96
97 pub fn height(&self) -> u32 {
99 self.color.height()
100 }
101
102 pub fn size(&self) -> (u32, u32) {
104 (self.color.width(), self.color.height())
105 }
106
107 pub fn format(&self) -> wgpu::TextureFormat {
109 self.color.format()
110 }
111
112 pub fn sample_count(&self) -> u32 {
114 self.sample_count
115 }
116
117 pub fn has_msaa(&self) -> bool {
119 self.sample_count > 1
120 }
121
122 pub fn has_depth(&self) -> bool {
124 self.depth.is_some()
125 }
126
127 pub fn resize(&mut self, context: &GraphicsContext, width: u32, height: u32) {
129 if self.color.width() == width && self.color.height() == height {
130 return;
131 }
132
133 let new_fb = FramebufferBuilder::new(width, height)
134 .format(self.color.format())
135 .sample_count_if(self.sample_count > 1, self.sample_count)
136 .depth_if(self.depth.is_some())
137 .build(context);
138
139 *self = new_fb;
140 }
141}
142
143pub struct FramebufferBuilder {
145 width: u32,
146 height: u32,
147 format: wgpu::TextureFormat,
148 sample_count: u32,
149 with_depth: bool,
150 label: Option<&'static str>,
151}
152
153impl FramebufferBuilder {
154 pub fn new(width: u32, height: u32) -> Self {
156 Self {
157 width,
158 height,
159 format: wgpu::TextureFormat::Rgba8UnormSrgb,
160 sample_count: 1,
161 with_depth: false,
162 label: None,
163 }
164 }
165
166 pub fn format(mut self, format: wgpu::TextureFormat) -> Self {
168 self.format = format;
169 self
170 }
171
172 pub fn with_msaa(mut self, sample_count: u32) -> Self {
174 self.sample_count = sample_count;
175 self
176 }
177
178 pub fn sample_count_if(mut self, condition: bool, sample_count: u32) -> Self {
180 if condition {
181 self.sample_count = sample_count;
182 }
183 self
184 }
185
186 pub fn with_depth(mut self) -> Self {
188 self.with_depth = true;
189 self
190 }
191
192 pub fn depth_if(mut self, condition: bool) -> Self {
194 self.with_depth = condition;
195 self
196 }
197
198 pub fn label(mut self, label: &'static str) -> Self {
200 self.label = Some(label);
201 self
202 }
203
204 pub fn build(self, context: &GraphicsContext) -> Framebuffer {
206 profile_function!();
207 let label_prefix = self.label.unwrap_or("Framebuffer");
208
209 let size = wgpu::Extent3d {
210 width: self.width,
211 height: self.height,
212 depth_or_array_layers: 1,
213 };
214
215 let color = GpuTexture::new(
217 context.device(),
218 &wgpu::TextureDescriptor {
219 label: Some(&format!("{} Color", label_prefix)),
220 size,
221 mip_level_count: 1,
222 sample_count: 1,
223 dimension: wgpu::TextureDimension::D2,
224 format: self.format,
225 usage: wgpu::TextureUsages::RENDER_ATTACHMENT
226 | wgpu::TextureUsages::TEXTURE_BINDING
227 | wgpu::TextureUsages::COPY_SRC,
228 view_formats: &[],
229 },
230 );
231
232 let msaa = if self.sample_count > 1 {
234 Some(GpuTexture::new(
235 context.device(),
236 &wgpu::TextureDescriptor {
237 label: Some(&format!("{} MSAA", label_prefix)),
238 size,
239 mip_level_count: 1,
240 sample_count: self.sample_count,
241 dimension: wgpu::TextureDimension::D2,
242 format: self.format,
243 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
244 view_formats: &[],
245 },
246 ))
247 } else {
248 None
249 };
250
251 let depth = if self.with_depth {
253 let depth_sample_count = if self.sample_count > 1 {
254 self.sample_count
255 } else {
256 1
257 };
258
259 Some(GpuTexture::new(
260 context.device(),
261 &wgpu::TextureDescriptor {
262 label: Some(&format!("{} Depth", label_prefix)),
263 size,
264 mip_level_count: 1,
265 sample_count: depth_sample_count,
266 dimension: wgpu::TextureDimension::D2,
267 format: DEPTH_FORMAT,
268 usage: wgpu::TextureUsages::RENDER_ATTACHMENT
269 | wgpu::TextureUsages::TEXTURE_BINDING,
270 view_formats: &[],
271 },
272 ))
273 } else {
274 None
275 };
276
277 Framebuffer {
278 color,
279 depth,
280 msaa,
281 sample_count: self.sample_count,
282 }
283 }
284}