use std::{
any::TypeId,
collections::hash_map::Entry,
ops::{Deref, DerefMut, Range},
};
use rustc_hash::FxHashMap;
use wgpu;
pub trait BufferItem: bytemuck::NoUninit + bytemuck::Pod + wgpu::WasmNotSend {}
impl<T: bytemuck::NoUninit + bytemuck::Pod + wgpu::WasmNotSend> BufferItem for T {}
pub type TypeIdMap<T> = FxHashMap<TypeId, T>;
#[derive(Default)]
pub struct Buffers {
buffers: TypeIdMap<DynamicBuffer>,
}
impl Buffers {
pub fn contains_key<T: 'static>(&self) -> bool {
self.buffers.contains_key(&TypeId::of::<T>())
}
pub fn get<T: 'static>(&self) -> Option<&DynamicBuffer> {
self.buffers.get(&TypeId::of::<T>())
}
pub fn get_mut<T: 'static>(&mut self) -> Option<&mut DynamicBuffer> {
self.buffers.get_mut(&TypeId::of::<T>())
}
pub fn entry<T: 'static>(&mut self) -> Entry<TypeId, DynamicBuffer> {
self.buffers.entry(TypeId::of::<T>())
}
pub fn insert(&mut self, buffer: DynamicBuffer) {
self.buffers.insert(buffer.type_id, buffer);
}
pub fn write_all(&mut self, device: &wgpu::Device, queue: &wgpu::Queue) {
for buffer in self.buffers.values_mut() {
buffer.write(device, queue);
}
}
pub fn clear_temporary_all(&mut self) {
for buffer in self.buffers.values_mut() {
buffer.clear_temporary();
}
}
}
pub struct DynamicBuffer {
pub buffer: wgpu::Buffer,
pub usage: wgpu::BufferUsages,
data: Vec<u8>,
data_temporary: Vec<u8>,
len_persist_written: usize,
type_id: TypeId,
type_size: usize,
label: String,
}
impl DynamicBuffer {
pub fn new<T: BufferItem>(device: &wgpu::Device, usage: wgpu::BufferUsages) -> Self {
Self::with_capacity::<T>(device, usage, 1)
}
pub fn with_capacity<T: BufferItem>(
device: &wgpu::Device,
usage: wgpu::BufferUsages,
capacity: usize,
) -> Self {
if size_of::<T>() % 4 != 0 {
panic!("size of item must be a multiple of 4");
}
let label = format!("dynamic buffer for {}", std::any::type_name::<T>());
let buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some(&label),
size: (size_of::<T>() * capacity) as u64,
usage,
mapped_at_creation: false,
});
DynamicBuffer {
buffer,
usage,
data: Vec::new(),
data_temporary: Vec::new(),
len_persist_written: 0,
type_id: TypeId::of::<T>(),
type_size: size_of::<T>(),
label,
}
}
pub fn append<T: BufferItem>(&mut self, values: Vec<T>) -> Range<u32> {
debug_assert_eq!(TypeId::of::<T>(), self.type_id);
let start = self.data.len() / self.type_size;
let end = start + values.len();
let mut values = bytemuck::cast_slice(&values).to_vec();
self.data.append(&mut values);
start as u32..end as u32
}
pub fn append_temporary<T: BufferItem>(&mut self, values: Vec<T>) -> Range<u32> {
debug_assert_eq!(TypeId::of::<T>(), self.type_id);
let start = self.data_temporary.len() / self.type_size;
let end = start + values.len();
let mut values = bytemuck::cast_slice(&values).to_vec();
self.data_temporary.append(&mut values);
start as u32..end as u32
}
pub fn clear(&mut self) {
self.data.clear();
self.len_persist_written = 0;
}
pub fn clear_temporary(&mut self) {
self.data_temporary.clear();
}
pub fn write(&mut self, device: &wgpu::Device, queue: &wgpu::Queue) -> bool {
let len_persist = self.data.len() / self.type_size;
let len_temporary = self.data_temporary.len() / self.type_size;
let len_total = len_persist + len_temporary;
let mut cap = self.buffer.size() as usize / self.type_size;
self.data.append(&mut self.data_temporary);
let mut realloced = false;
if len_total > self.len_persist_written {
if len_total > cap {
cap = (cap * 2).max(len_total);
self.buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some(&self.label),
size: (self.type_size * cap) as u64,
usage: self.usage,
mapped_at_creation: true,
});
self.buffer
.slice(..self.data.len() as u64)
.get_mapped_range_mut()
.copy_from_slice(&self.data);
self.buffer.unmap();
realloced = true;
} else {
queue.write_buffer(
&self.buffer,
(self.type_size * self.len_persist_written) as u64,
bytemuck::cast_slice(
&self.data
[self.type_size * self.len_persist_written..self.type_size * len_total],
),
);
}
}
self.len_persist_written = len_persist;
self.data.truncate(len_persist);
realloced
}
pub fn offset_temporary(&self) -> u32 {
self.len_persist_written as u32
}
pub fn offset_temporary_range(&self, bounds: Range<u32>) -> Range<u32> {
let len = self.len_persist_written as u32;
bounds.start + len..bounds.end + len
}
}
impl Deref for DynamicBuffer {
type Target = wgpu::Buffer;
fn deref(&self) -> &Self::Target {
&self.buffer
}
}
impl DerefMut for DynamicBuffer {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.buffer
}
}