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