use std::collections::HashMap;
use crate::dispatcher::BoundResource;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct TextureKey {
pub width: u32,
pub height: u32,
pub format: wgpu::TextureFormat,
pub usage: wgpu::TextureUsages,
}
impl TextureKey {
#[must_use]
pub fn offscreen(width: u32, height: u32, format: wgpu::TextureFormat) -> Self {
Self {
width: width.max(1),
height: height.max(1),
format,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT
| wgpu::TextureUsages::TEXTURE_BINDING,
}
}
}
#[derive(Debug)]
pub struct TextureLease {
key: TextureKey,
texture: wgpu::Texture,
view: wgpu::TextureView,
}
impl TextureLease {
#[must_use]
pub fn key(&self) -> TextureKey {
self.key
}
#[must_use]
pub fn texture(&self) -> &wgpu::Texture {
&self.texture
}
#[must_use]
pub fn view(&self) -> &wgpu::TextureView {
&self.view
}
#[must_use]
pub fn bound_resource(&self) -> BoundResource {
BoundResource::Texture {
view: self.view.clone(),
format: self.key.format,
}
}
}
#[derive(Debug, Default)]
pub struct TexturePool {
free: HashMap<TextureKey, Vec<(wgpu::Texture, wgpu::TextureView)>>,
}
impl TexturePool {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn lease(&mut self, device: &wgpu::Device, key: TextureKey) -> TextureLease {
if let Some((texture, view)) =
self.free.get_mut(&key).and_then(Vec::pop)
{
return TextureLease { key, texture, view };
}
let texture = device.create_texture(&wgpu::TextureDescriptor {
label: Some("engawa-wgpu pooled texture"),
size: wgpu::Extent3d {
width: key.width,
height: key.height,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: key.format,
usage: key.usage,
view_formats: &[],
});
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
TextureLease { key, texture, view }
}
pub fn release(&mut self, lease: TextureLease) {
self.free
.entry(lease.key)
.or_default()
.push((lease.texture, lease.view));
}
#[must_use]
pub fn free_count(&self) -> usize {
self.free.values().map(Vec::len).sum()
}
pub fn clear(&mut self) {
self.free.clear();
}
pub fn retain(&mut self, mut keep: impl FnMut(&TextureKey) -> bool) {
self.free.retain(|key, _| keep(key));
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn offscreen_key_clamps_zero_dimensions_and_sets_postprocess_usage() {
let key = TextureKey::offscreen(0, 0, wgpu::TextureFormat::Rgba8UnormSrgb);
assert_eq!(key.width, 1);
assert_eq!(key.height, 1);
assert!(key.usage.contains(wgpu::TextureUsages::RENDER_ATTACHMENT));
assert!(key.usage.contains(wgpu::TextureUsages::TEXTURE_BINDING));
}
#[test]
fn keys_differ_by_any_axis() {
let base = TextureKey::offscreen(64, 64, wgpu::TextureFormat::Rgba8UnormSrgb);
let wider = TextureKey::offscreen(128, 64, wgpu::TextureFormat::Rgba8UnormSrgb);
let other_format =
TextureKey::offscreen(64, 64, wgpu::TextureFormat::Bgra8UnormSrgb);
assert_ne!(base, wider);
assert_ne!(base, other_format);
assert_eq!(
base,
TextureKey::offscreen(64, 64, wgpu::TextureFormat::Rgba8UnormSrgb)
);
}
}