1use std::collections::HashMap;
2
3use crate::pipelines::{DEPTH_FORMAT, SAMPLE_COUNT};
4
5pub struct TargetPool {
15 device: wgpu::Device,
16 frame: u64,
17 layers: Vec<Pooled<LayerTarget>>,
19 snapshots: Vec<Pooled<Snapshot>>,
20 filters: Vec<Pooled<FilterTarget>>,
21 raster_attachments: Vec<Pooled<RasterAttachments>>,
22 taken_layers: Vec<Pooled<LayerTarget>>,
24 taken_snapshots: Vec<Pooled<Snapshot>>,
25 taken_filters: Vec<Pooled<FilterTarget>>,
26 taken_raster_attachments: Vec<Pooled<RasterAttachments>>,
27 main_scratch: HashMap<(u32, u32, wgpu::TextureFormat, bool), MainScratch>,
28}
29
30pub const FILTER_SIZE_BUCKET: u32 = 32;
35
36const EVICT_AFTER_FRAMES: u64 = 3;
37
38struct Pooled<T> {
39 size: [u32; 2],
40 format: wgpu::TextureFormat,
41 transient: bool,
44 last_used: u64,
45 value: T,
46}
47
48#[derive(Clone)]
54pub struct LayerTarget {
55 pub msaa: wgpu::TextureView,
56 pub resolve_texture: wgpu::Texture,
57 pub resolve: wgpu::TextureView,
58 pub depth: wgpu::TextureView,
59}
60
61#[derive(Clone)]
65pub struct Snapshot {
66 pub texture: wgpu::Texture,
67 pub view: wgpu::TextureView,
68}
69
70#[derive(Clone)]
76pub struct RasterAttachments {
77 pub msaa: wgpu::TextureView,
78 pub depth: wgpu::TextureView,
79}
80
81#[derive(Clone)]
86pub struct FilterTarget {
87 pub view: wgpu::TextureView,
88}
89
90#[derive(Clone)]
95pub struct MainScratch {
96 pub msaa: wgpu::TextureView,
97 pub depth: wgpu::TextureView,
98}
99
100impl TargetPool {
101 pub fn new(device: &wgpu::Device) -> Self {
103 Self {
104 device: device.clone(),
105 frame: 0,
106 layers: Vec::new(),
107 snapshots: Vec::new(),
108 filters: Vec::new(),
109 raster_attachments: Vec::new(),
110 taken_layers: Vec::new(),
111 taken_snapshots: Vec::new(),
112 taken_filters: Vec::new(),
113 taken_raster_attachments: Vec::new(),
114 main_scratch: HashMap::new(),
115 }
116 }
117
118 pub fn take_layer(
123 &mut self,
124 size: [u32; 2],
125 format: wgpu::TextureFormat,
126 transient: bool,
127 ) -> LayerTarget {
128 let entry = take_matching(&mut self.layers, size, format, transient)
129 .unwrap_or_else(|| self.create_layer(size, format, transient));
130 let value = entry.value.clone();
131 self.taken_layers.push(refreshed(entry, self.frame));
132 value
133 }
134
135 pub fn take_raster_attachments(
142 &mut self,
143 size: [u32; 2],
144 format: wgpu::TextureFormat,
145 ) -> RasterAttachments {
146 let entry = take_matching(&mut self.raster_attachments, size, format, true)
147 .unwrap_or_else(|| self.create_raster_attachments(size, format));
148 let value = entry.value.clone();
149 self.taken_raster_attachments
150 .push(refreshed(entry, self.frame));
151 value
152 }
153
154 pub fn take_snapshot(&mut self, size: [u32; 2], format: wgpu::TextureFormat) -> Snapshot {
158 let entry = take_matching(&mut self.snapshots, size, format, false)
159 .unwrap_or_else(|| self.create_snapshot(size, format));
160 let value = entry.value.clone();
161 self.taken_snapshots.push(refreshed(entry, self.frame));
162 value
163 }
164
165 pub fn take_filter(&mut self, size: [u32; 2], format: wgpu::TextureFormat) -> FilterTarget {
170 let entry = take_matching(&mut self.filters, size, format, false)
171 .unwrap_or_else(|| self.create_filter(size, format));
172 let value = entry.value.clone();
173 self.taken_filters.push(refreshed(entry, self.frame));
174 value
175 }
176
177 pub fn main_scratch(
183 &mut self,
184 size: [u32; 2],
185 format: wgpu::TextureFormat,
186 transient: bool,
187 ) -> MainScratch {
188 if self.main_scratch.len() > 8 {
189 self.main_scratch.clear(); }
191 let device = self.device.clone();
192 self.main_scratch
193 .entry((size[0], size[1], format, transient))
194 .or_insert_with(|| MainScratch {
195 msaa: attachment_texture(&device, size, format, SAMPLE_COUNT, false, transient)
196 .create_view(&Default::default()),
197 depth: attachment_texture(
198 &device,
199 size,
200 DEPTH_FORMAT,
201 SAMPLE_COUNT,
202 false,
203 transient,
204 )
205 .create_view(&Default::default()),
206 })
207 .clone()
208 }
209
210 pub fn end_frame(&mut self) {
212 self.frame += 1;
213 let cutoff = self.frame.saturating_sub(EVICT_AFTER_FRAMES);
214 self.layers.append(&mut self.taken_layers);
215 self.snapshots.append(&mut self.taken_snapshots);
216 self.filters.append(&mut self.taken_filters);
217 self.raster_attachments
218 .append(&mut self.taken_raster_attachments);
219 self.layers.retain(|e| e.last_used >= cutoff);
220 self.snapshots.retain(|e| e.last_used >= cutoff);
221 self.filters.retain(|e| e.last_used >= cutoff);
222 self.raster_attachments.retain(|e| e.last_used >= cutoff);
223 }
224
225 fn create_layer(
226 &self,
227 size: [u32; 2],
228 format: wgpu::TextureFormat,
229 transient: bool,
230 ) -> Pooled<LayerTarget> {
231 let msaa = attachment_texture(&self.device, size, format, SAMPLE_COUNT, false, transient);
232 let resolve = attachment_texture(&self.device, size, format, 1, true, false);
233 let depth = attachment_texture(
234 &self.device,
235 size,
236 DEPTH_FORMAT,
237 SAMPLE_COUNT,
238 false,
239 transient,
240 );
241 Pooled {
242 size,
243 format,
244 transient,
245 last_used: self.frame,
246 value: LayerTarget {
247 msaa: msaa.create_view(&Default::default()),
248 resolve: resolve.create_view(&Default::default()),
249 resolve_texture: resolve,
250 depth: depth.create_view(&Default::default()),
251 },
252 }
253 }
254
255 fn create_raster_attachments(
256 &self,
257 size: [u32; 2],
258 format: wgpu::TextureFormat,
259 ) -> Pooled<RasterAttachments> {
260 let msaa = attachment_texture(&self.device, size, format, SAMPLE_COUNT, false, true);
261 let depth = attachment_texture(&self.device, size, DEPTH_FORMAT, SAMPLE_COUNT, false, true);
262 Pooled {
263 size,
264 format,
265 transient: true,
266 last_used: self.frame,
267 value: RasterAttachments {
268 msaa: msaa.create_view(&Default::default()),
269 depth: depth.create_view(&Default::default()),
270 },
271 }
272 }
273
274 fn create_filter(&self, size: [u32; 2], format: wgpu::TextureFormat) -> Pooled<FilterTarget> {
275 let texture = attachment_texture(&self.device, size, format, 1, true, false);
276 Pooled {
277 size,
278 format,
279 transient: false,
280 last_used: self.frame,
281 value: FilterTarget {
282 view: texture.create_view(&Default::default()),
283 },
284 }
285 }
286
287 fn create_snapshot(&self, size: [u32; 2], format: wgpu::TextureFormat) -> Pooled<Snapshot> {
288 let texture = self.device.create_texture(&wgpu::TextureDescriptor {
289 label: Some("valo.snapshot"),
290 size: wgpu::Extent3d {
291 width: size[0],
292 height: size[1],
293 depth_or_array_layers: 1,
294 },
295 mip_level_count: 1,
296 sample_count: 1,
297 dimension: wgpu::TextureDimension::D2,
298 format,
299 usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
300 view_formats: &[],
301 });
302 Pooled {
303 size,
304 format,
305 transient: false,
306 last_used: self.frame,
307 value: Snapshot {
308 view: texture.create_view(&Default::default()),
309 texture,
310 },
311 }
312 }
313}
314
315fn take_matching<T>(
316 pool: &mut Vec<Pooled<T>>,
317 size: [u32; 2],
318 format: wgpu::TextureFormat,
319 transient: bool,
320) -> Option<Pooled<T>> {
321 let idx = pool
322 .iter()
323 .position(|e| e.size == size && e.format == format && e.transient == transient)?;
324 Some(pool.swap_remove(idx))
325}
326
327fn refreshed<T>(mut entry: Pooled<T>, frame: u64) -> Pooled<T> {
328 entry.last_used = frame;
329 entry
330}
331
332fn attachment_texture(
335 device: &wgpu::Device,
336 size: [u32; 2],
337 format: wgpu::TextureFormat,
338 samples: u32,
339 sampleable: bool,
340 transient: bool,
341) -> wgpu::Texture {
342 let mut usage = wgpu::TextureUsages::RENDER_ATTACHMENT;
343 if sampleable {
344 usage |= wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_SRC;
345 }
346 if transient {
347 debug_assert!(!sampleable, "transient attachments cannot be sampled");
352 usage |= wgpu::TextureUsages::TRANSIENT_ATTACHMENT;
353 }
354 device.create_texture(&wgpu::TextureDescriptor {
355 label: Some("valo.pooled"),
356 size: wgpu::Extent3d {
357 width: size[0],
358 height: size[1],
359 depth_or_array_layers: 1,
360 },
361 mip_level_count: 1,
362 sample_count: samples,
363 dimension: wgpu::TextureDimension::D2,
364 format,
365 usage,
366 view_formats: &[],
367 })
368}
369
370impl TargetPool {
371 pub(crate) fn report(&self) -> crate::PoolReport {
374 const LAYER_BPP: u64 = 36;
377 const LAYER_TRANSIENT_BPP: u64 = 4;
378 const SCRATCH_BPP: u64 = 32; const FLAT_BPP: u64 = 4; let mut count = 0u32;
381 let mut bytes = 0u64;
382 let mut add = |size: [u32; 2], bpp: u64| {
383 count += 1;
384 bytes += size[0] as u64 * size[1] as u64 * bpp;
385 };
386 for t in self.layers.iter().chain(&self.taken_layers) {
387 add(
388 t.size,
389 if t.transient {
390 LAYER_TRANSIENT_BPP
391 } else {
392 LAYER_BPP
393 },
394 );
395 }
396 for t in self.snapshots.iter().chain(&self.taken_snapshots) {
397 add(t.size, FLAT_BPP);
398 }
399 for t in self.filters.iter().chain(&self.taken_filters) {
400 add(t.size, FLAT_BPP);
401 }
402 for &(w, h, _, transient) in self.main_scratch.keys() {
403 add([w, h], if transient { 0 } else { SCRATCH_BPP });
405 }
406 crate::PoolReport { count, bytes }
407 }
408}