1use crate::texture::{TexParams, Texture};
2use enum_map::EnumMap;
3
4pub struct FrameBuffer<T: enum_map::EnumArray<Option<Texture>>> {
49 targets: EnumMap<T, Option<Texture>>, pub width: u32,
51 pub height: u32,
52 pub bind_group_layout: wgpu::BindGroupLayout,
53 pub bind_group: Option<wgpu::BindGroup>,
56 }
59impl<T: enum_map::EnumArray<Option<Texture>> + std::fmt::Debug> FrameBuffer<T> {
60 pub(self) fn new(device: &wgpu::Device, targets: EnumMap<T, Option<Texture>>, width: u32, height: u32, create_bind_group: bool) -> Self {
62 let mut layout_entries = Vec::new();
64 for (idx, tex) in targets.values().enumerate() {
65 if let Some(tex) = tex {
66 let mut sample_type = wgpu::TextureSampleType::Float { filterable: false };
68 if tex.texture.format().is_depth_stencil_format() {
69 sample_type = wgpu::TextureSampleType::Depth;
70 }
71
72 layout_entries.push(wgpu::BindGroupLayoutEntry {
73 binding: u32::try_from(idx).unwrap(),
74 visibility: wgpu::ShaderStages::FRAGMENT.union(wgpu::ShaderStages::COMPUTE),
75 ty: wgpu::BindingType::Texture {
76 multisampled: tex.texture.sample_count() > 1,
77 view_dimension: wgpu::TextureViewDimension::D2,
78 sample_type,
79 },
80 count: None,
81 });
82 }
83 }
84
85 let layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
86 label: Some("GBuffer Bind Group Layout"),
87 entries: layout_entries.as_slice(),
88 });
89
90 let bind_group = if create_bind_group {
93 Some(Self::create_bind_group(device, &targets, &layout))
94 } else {
95 None
96 };
97
98 Self {
99 targets,
100 width,
101 height,
102 bind_group_layout: layout,
104 bind_group,
105 }
106 }
107
108 pub fn get(&self, target_type: T) -> Option<&Texture> {
109 let tex = self.targets[target_type].as_ref();
110 tex
111 }
112
113 pub fn get_mut(&mut self, target_type: T) -> Option<&mut Texture> {
114 let tex = self.targets[target_type].as_mut();
115 tex
116 }
117
118 #[allow(clippy::missing_panics_doc)] pub fn resize(&mut self, device: &wgpu::Device, width: u32, height: u32) {
120 for tex in self.targets.values_mut() {
123 let tex = tex.as_mut().unwrap();
124
125 let scale_factor = tex.tex_params.scale_factor;
126 let scaled_width = (width / scale_factor).max(1);
127 let scaled_height = (height / scale_factor).max(1);
128 tex.resize(device, scaled_width, scaled_height);
129 }
130 self.width = width;
131 self.height = height;
132
133 if self.bind_group.is_some() {
134 self.bind_group = Some(Self::create_bind_group(device, &self.targets, &self.bind_group_layout));
135 }
136 }
137
138 fn create_bind_group(device: &wgpu::Device, targets: &EnumMap<T, Option<Texture>>, layout: &wgpu::BindGroupLayout) -> wgpu::BindGroup {
140 let mut bind_group_entries = Vec::new();
141 for (idx, tex) in targets.values().enumerate() {
142 bind_group_entries.push(wgpu::BindGroupEntry {
153 binding: u32::try_from(idx).unwrap(),
154 resource: wgpu::BindingResource::TextureView(&tex.as_ref().unwrap().view),
155 });
156 }
157
158 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
159 layout,
160 entries: bind_group_entries.as_slice(),
161 label: Some("gbuffer group"),
162 });
163 bind_group
164 }
165}
166
167pub struct FrameBufferBuilder<T: enum_map::EnumArray<Option<Texture>>> {
168 targets: EnumMap<T, Option<Texture>>, pub width: u32,
170 pub height: u32,
171 pub create_bind_group: bool,
172}
173impl<T: enum_map::EnumArray<Option<Texture>> + std::fmt::Debug> FrameBufferBuilder<T> {
174 pub fn new(width: u32, height: u32) -> Self {
175 let targets = EnumMap::default();
176 Self {
177 targets,
178 width,
179 height,
180 create_bind_group: false,
181 }
182 }
183
184 #[must_use]
187 pub fn add_render_target(
188 mut self,
189 device: &wgpu::Device,
190 target_type: T,
191 format: wgpu::TextureFormat,
192 usages: wgpu::TextureUsages,
193 tex_params: TexParams,
194 ) -> Self {
195 assert_ne!(usages, wgpu::TextureUsages::empty(), "Texture usage cannot be empty");
196
197 let scaled_width = self.width / tex_params.scale_factor;
198 let scaled_height = self.height / tex_params.scale_factor;
199
200 let tex = Texture::new(device, scaled_width, scaled_height, format, usages, tex_params);
201 self.targets[target_type] = Some(tex);
202 self
203 }
204
205 #[must_use]
206 pub fn create_bind_group(mut self) -> Self {
207 self.create_bind_group = true;
208 self
209 }
210
211 pub fn build(self, device: &wgpu::Device) -> FrameBuffer<T> {
214 assert!(
215 self.targets.len() != 0,
216 "You haven't assigned any render targets. You have to add render targets using add_render_target()"
217 );
218 FrameBuffer::new(device, self.targets, self.width, self.height, self.create_bind_group)
219 }
220}