use crate::frame::ContextAttachments;
use crate::pipelines::{DEPTH_FORMAT, SAMPLE_COUNT};
pub struct TargetPool {
device: wgpu::Device,
frame: u64,
layers: Shelf<LayerTarget>,
copy_textures: Shelf<CopyTexture>,
filters: Shelf<FilterTarget>,
attachments: Shelf<Attachments>,
}
const EVICT_AFTER_FRAMES: u64 = 3;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
struct PoolKey {
size: [u32; 2],
format: wgpu::TextureFormat,
}
struct Shelf<T> {
free: Vec<Pooled<T>>,
taken: Vec<Pooled<T>>,
}
struct Pooled<T> {
key: PoolKey,
last_used: u64,
value: T,
}
impl<T: Clone> Shelf<T> {
fn new() -> Self {
Self {
free: Vec::new(),
taken: Vec::new(),
}
}
fn take(&mut self, key: PoolKey, frame: u64, create: impl FnOnce() -> T) -> T {
let mut entry = match self.free.iter().position(|entry| entry.key == key) {
Some(index) => self.free.swap_remove(index),
None => Pooled {
key,
last_used: frame,
value: create(),
},
};
entry.last_used = frame;
let value = entry.value.clone();
self.taken.push(entry);
value
}
fn end_frame(&mut self, cutoff: u64) {
self.free.append(&mut self.taken);
self.free.retain(|entry| entry.last_used >= cutoff);
}
fn keys(&self) -> impl Iterator<Item = &PoolKey> {
self.free.iter().chain(&self.taken).map(|entry| &entry.key)
}
}
#[derive(Clone)]
pub struct LayerTarget {
pub msaa: wgpu::TextureView,
pub resolve_texture: wgpu::Texture,
pub resolve: wgpu::TextureView,
pub depth: wgpu::TextureView,
}
#[derive(Clone)]
pub struct CopyTexture {
pub texture: wgpu::Texture,
pub view: wgpu::TextureView,
}
#[derive(Clone)]
pub struct Attachments {
pub msaa: wgpu::TextureView,
pub depth: wgpu::TextureView,
}
impl Attachments {
pub(crate) fn resolving_into(self, resolve: wgpu::TextureView) -> ContextAttachments {
ContextAttachments {
msaa: self.msaa,
depth: self.depth,
resolve,
}
}
}
#[derive(Clone)]
pub struct FilterTarget {
pub view: wgpu::TextureView,
}
impl TargetPool {
pub fn new(device: &wgpu::Device) -> Self {
Self {
device: device.clone(),
frame: 0,
layers: Shelf::new(),
copy_textures: Shelf::new(),
filters: Shelf::new(),
attachments: Shelf::new(),
}
}
pub fn take_layer(&mut self, size: [u32; 2], format: wgpu::TextureFormat) -> LayerTarget {
let key = PoolKey { size, format };
let device = &self.device;
self.layers
.take(key, self.frame, || new_layer(device, size, format))
}
pub fn take_attachments(&mut self, size: [u32; 2], format: wgpu::TextureFormat) -> Attachments {
let key = PoolKey { size, format };
let device = &self.device;
self.attachments
.take(key, self.frame, || new_attachments(device, size, format))
}
pub fn take_copy_texture(
&mut self,
size: [u32; 2],
format: wgpu::TextureFormat,
) -> CopyTexture {
let key = PoolKey { size, format };
let device = &self.device;
self.copy_textures
.take(key, self.frame, || new_copy_texture(device, size, format))
}
pub fn take_filter(&mut self, size: [u32; 2], format: wgpu::TextureFormat) -> FilterTarget {
let key = PoolKey { size, format };
let device = &self.device;
self.filters.take(key, self.frame, || FilterTarget {
view: attachment_texture(device, size, format, 1, true, false)
.create_view(&Default::default()),
})
}
pub fn end_frame(&mut self) {
self.frame += 1;
let cutoff = self.frame.saturating_sub(EVICT_AFTER_FRAMES);
self.layers.end_frame(cutoff);
self.copy_textures.end_frame(cutoff);
self.filters.end_frame(cutoff);
self.attachments.end_frame(cutoff);
}
}
fn new_layer(device: &wgpu::Device, size: [u32; 2], format: wgpu::TextureFormat) -> LayerTarget {
let msaa = attachment_texture(device, size, format, SAMPLE_COUNT, false, true);
let resolve = attachment_texture(device, size, format, 1, true, false);
let depth = attachment_texture(device, size, DEPTH_FORMAT, SAMPLE_COUNT, false, true);
LayerTarget {
msaa: msaa.create_view(&Default::default()),
resolve: resolve.create_view(&Default::default()),
resolve_texture: resolve,
depth: depth.create_view(&Default::default()),
}
}
fn new_attachments(
device: &wgpu::Device,
size: [u32; 2],
format: wgpu::TextureFormat,
) -> Attachments {
let msaa = attachment_texture(device, size, format, SAMPLE_COUNT, false, true);
let depth = attachment_texture(device, size, DEPTH_FORMAT, SAMPLE_COUNT, false, true);
Attachments {
msaa: msaa.create_view(&Default::default()),
depth: depth.create_view(&Default::default()),
}
}
fn new_copy_texture(
device: &wgpu::Device,
size: [u32; 2],
format: wgpu::TextureFormat,
) -> CopyTexture {
let texture = device.create_texture(&wgpu::TextureDescriptor {
label: Some("valo.copy"),
size: wgpu::Extent3d {
width: size[0],
height: size[1],
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format,
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
view_formats: &[],
});
CopyTexture {
view: texture.create_view(&Default::default()),
texture,
}
}
fn attachment_texture(
device: &wgpu::Device,
size: [u32; 2],
format: wgpu::TextureFormat,
samples: u32,
sampleable: bool,
transient: bool,
) -> wgpu::Texture {
let mut usage = wgpu::TextureUsages::RENDER_ATTACHMENT;
if sampleable {
usage |= wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_SRC;
}
if transient {
debug_assert!(!sampleable, "transient attachments cannot be sampled");
usage |= wgpu::TextureUsages::TRANSIENT_ATTACHMENT;
}
device.create_texture(&wgpu::TextureDescriptor {
label: Some("valo.pooled"),
size: wgpu::Extent3d {
width: size[0],
height: size[1],
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: samples,
dimension: wgpu::TextureDimension::D2,
format,
usage,
view_formats: &[],
})
}
impl TargetPool {
pub(crate) fn report(&self) -> crate::PoolReport {
const LAYER_BPP: u64 = 4;
const FLAT_BPP: u64 = 4; const ATTACHMENTS_BPP: u64 = 0;
let mut count = 0u32;
let mut bytes = 0u64;
let mut add = |key: &PoolKey, bpp: u64| {
count += 1;
bytes += key.size[0] as u64 * key.size[1] as u64 * bpp;
};
self.layers.keys().for_each(|key| add(key, LAYER_BPP));
self.copy_textures.keys().for_each(|key| add(key, FLAT_BPP));
self.filters.keys().for_each(|key| add(key, FLAT_BPP));
self.attachments
.keys()
.for_each(|key| add(key, ATTACHMENTS_BPP));
crate::PoolReport { count, bytes }
}
}