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.as_ref().map(|m| m.view()).unwrap_or(self.color.view())
78 }
79
80 pub fn resolve_target(&self) -> Option<&wgpu::TextureView> {
82 if self.msaa.is_some() {
83 Some(self.color.view())
84 } else {
85 None
86 }
87 }
88
89 pub fn width(&self) -> u32 {
91 self.color.width()
92 }
93
94 pub fn height(&self) -> u32 {
96 self.color.height()
97 }
98
99 pub fn size(&self) -> (u32, u32) {
101 (self.color.width(), self.color.height())
102 }
103
104 pub fn format(&self) -> wgpu::TextureFormat {
106 self.color.format()
107 }
108
109 pub fn sample_count(&self) -> u32 {
111 self.sample_count
112 }
113
114 pub fn has_msaa(&self) -> bool {
116 self.sample_count > 1
117 }
118
119 pub fn has_depth(&self) -> bool {
121 self.depth.is_some()
122 }
123
124 pub fn resize(&mut self, context: &GraphicsContext, width: u32, height: u32) {
126 if self.color.width() == width && self.color.height() == height {
127 return;
128 }
129
130 let new_fb = FramebufferBuilder::new(width, height)
131 .format(self.color.format())
132 .sample_count_if(self.sample_count > 1, self.sample_count)
133 .depth_if(self.depth.is_some())
134 .build(context);
135
136 *self = new_fb;
137 }
138}
139
140pub struct FramebufferBuilder {
142 width: u32,
143 height: u32,
144 format: wgpu::TextureFormat,
145 sample_count: u32,
146 with_depth: bool,
147 label: Option<&'static str>,
148}
149
150impl FramebufferBuilder {
151 pub fn new(width: u32, height: u32) -> Self {
153 Self {
154 width,
155 height,
156 format: wgpu::TextureFormat::Rgba8UnormSrgb,
157 sample_count: 1,
158 with_depth: false,
159 label: None,
160 }
161 }
162
163 pub fn format(mut self, format: wgpu::TextureFormat) -> Self {
165 self.format = format;
166 self
167 }
168
169 pub fn with_msaa(mut self, sample_count: u32) -> Self {
171 self.sample_count = sample_count;
172 self
173 }
174
175 pub fn sample_count_if(mut self, condition: bool, sample_count: u32) -> Self {
177 if condition {
178 self.sample_count = sample_count;
179 }
180 self
181 }
182
183 pub fn with_depth(mut self) -> Self {
185 self.with_depth = true;
186 self
187 }
188
189 pub fn depth_if(mut self, condition: bool) -> Self {
191 self.with_depth = condition;
192 self
193 }
194
195 pub fn label(mut self, label: &'static str) -> Self {
197 self.label = Some(label);
198 self
199 }
200
201 pub fn build(self, context: &GraphicsContext) -> Framebuffer {
203 profile_function!();
204 let label_prefix = self.label.unwrap_or("Framebuffer");
205
206 let size = wgpu::Extent3d {
207 width: self.width,
208 height: self.height,
209 depth_or_array_layers: 1,
210 };
211
212 let color = GpuTexture::new(
214 context.device(),
215 &wgpu::TextureDescriptor {
216 label: Some(&format!("{} Color", label_prefix)),
217 size,
218 mip_level_count: 1,
219 sample_count: 1,
220 dimension: wgpu::TextureDimension::D2,
221 format: self.format,
222 usage: wgpu::TextureUsages::RENDER_ATTACHMENT
223 | wgpu::TextureUsages::TEXTURE_BINDING
224 | wgpu::TextureUsages::COPY_SRC,
225 view_formats: &[],
226 },
227 );
228
229 let msaa = if self.sample_count > 1 {
231 Some(GpuTexture::new(
232 context.device(),
233 &wgpu::TextureDescriptor {
234 label: Some(&format!("{} MSAA", label_prefix)),
235 size,
236 mip_level_count: 1,
237 sample_count: self.sample_count,
238 dimension: wgpu::TextureDimension::D2,
239 format: self.format,
240 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
241 view_formats: &[],
242 },
243 ))
244 } else {
245 None
246 };
247
248 let depth = if self.with_depth {
250 let depth_sample_count = if self.sample_count > 1 {
251 self.sample_count
252 } else {
253 1
254 };
255
256 Some(GpuTexture::new(
257 context.device(),
258 &wgpu::TextureDescriptor {
259 label: Some(&format!("{} Depth", label_prefix)),
260 size,
261 mip_level_count: 1,
262 sample_count: depth_sample_count,
263 dimension: wgpu::TextureDimension::D2,
264 format: DEPTH_FORMAT,
265 usage: wgpu::TextureUsages::RENDER_ATTACHMENT
266 | wgpu::TextureUsages::TEXTURE_BINDING,
267 view_formats: &[],
268 },
269 ))
270 } else {
271 None
272 };
273
274 Framebuffer {
275 color,
276 depth,
277 msaa,
278 sample_count: self.sample_count,
279 }
280 }
281}