use ahash::HashMap;
use super::pipeline;
use super::types::TextureSlot2D;
pub struct TextureArray {
bind_group_cache: HashMap<u64, wgpu::BindGroup>,
standard_layout: wgpu::BindGroupLayout,
fallback_bind_group: wgpu::BindGroup,
_fallback_texture: wgpu::Texture,
_fallback_view: wgpu::TextureView,
_fallback_sampler: wgpu::Sampler,
}
impl TextureArray {
pub fn new(device: &wgpu::Device, queue: &wgpu::Queue) -> Self {
let standard_layout = pipeline::create_standard_texture_bind_group_layout(device);
let (fallback_texture, fallback_view, fallback_sampler) =
pipeline::create_fallback_texture(device, queue);
let fallback_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("batched_fallback_bg"),
layout: &standard_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(&fallback_view),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Sampler(&fallback_sampler),
},
],
});
Self {
bind_group_cache: HashMap::default(),
standard_layout,
fallback_bind_group,
_fallback_texture: fallback_texture,
_fallback_view: fallback_view,
_fallback_sampler: fallback_sampler,
}
}
pub fn standard_layout(&self) -> &wgpu::BindGroupLayout {
&self.standard_layout
}
pub fn fallback_bind_group(&self) -> &wgpu::BindGroup {
&self.fallback_bind_group
}
pub fn update_standard(&mut self, device: &wgpu::Device, textures: &[TextureSlot2D]) {
for slot in textures {
self.bind_group_cache.entry(slot.id).or_insert_with(|| {
device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("batched_texture_bg"),
layout: &self.standard_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(&slot.view),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Sampler(&slot.sampler),
},
],
})
});
}
}
pub fn get_standard_bind_group(&self, texture_id: u64) -> Option<&wgpu::BindGroup> {
self.bind_group_cache.get(&texture_id)
}
pub fn evict(&mut self, active_ids: &[u64]) {
self.bind_group_cache
.retain(|id, _| active_ids.contains(id));
}
}
pub struct BindlessTextureArray {
layout: wgpu::BindGroupLayout,
bind_group: Option<wgpu::BindGroup>,
current_ids: Vec<u64>,
sampler: wgpu::Sampler,
_fallback_texture: wgpu::Texture,
fallback_view: wgpu::TextureView,
max_textures: u32,
}
impl BindlessTextureArray {
pub fn new(device: &wgpu::Device, queue: &wgpu::Queue, max_textures: u32) -> Self {
let layout = pipeline::create_bindless_texture_bind_group_layout(device, max_textures);
let (fallback_texture, fallback_view, _) = pipeline::create_fallback_texture(device, queue);
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
label: Some("batched_bindless_sampler"),
mag_filter: wgpu::FilterMode::Linear,
min_filter: wgpu::FilterMode::Linear,
..Default::default()
});
Self {
layout,
bind_group: None,
current_ids: Vec::new(),
sampler,
_fallback_texture: fallback_texture,
fallback_view,
max_textures,
}
}
pub fn layout(&self) -> &wgpu::BindGroupLayout {
&self.layout
}
pub fn update(&mut self, device: &wgpu::Device, textures: &[TextureSlot2D]) {
let new_ids: Vec<u64> = textures.iter().map(|s| s.id).collect();
if new_ids == self.current_ids && self.bind_group.is_some() {
return;
}
let mut views: Vec<&wgpu::TextureView> = Vec::with_capacity(self.max_textures as usize);
for slot in textures {
views.push(&slot.view);
}
while views.len() < self.max_textures as usize {
views.push(&self.fallback_view);
}
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("batched_bindless_bg"),
layout: &self.layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureViewArray(&views),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Sampler(&self.sampler),
},
],
});
self.bind_group = Some(bind_group);
self.current_ids = new_ids;
}
pub fn bind_group(&self) -> Option<&wgpu::BindGroup> {
self.bind_group.as_ref()
}
}