use crate::context::Context;
use bytemuck::Pod;
use std::mem;
pub struct DynamicUniformBuffer<T: Pod> {
data: Vec<u8>,
buffer: wgpu::Buffer,
capacity: u64,
alignment: u64,
aligned_size: u64,
count: usize,
label: &'static str,
_marker: std::marker::PhantomData<T>,
}
impl<T: Pod> DynamicUniformBuffer<T> {
pub fn new(label: &'static str) -> Self {
Self::with_capacity(label, 256) }
pub fn with_capacity(label: &'static str, initial_capacity: usize) -> Self {
let ctxt = Context::get();
let alignment = ctxt.device.limits().min_uniform_buffer_offset_alignment as u64;
let unaligned_size = mem::size_of::<T>() as u64;
let aligned_size = unaligned_size.div_ceil(alignment) * alignment;
let capacity = aligned_size * initial_capacity as u64;
let buffer = ctxt.create_buffer(&wgpu::BufferDescriptor {
label: Some(label),
size: capacity,
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
Self {
data: Vec::with_capacity(capacity as usize),
buffer,
capacity,
alignment,
aligned_size,
count: 0,
label,
_marker: std::marker::PhantomData,
}
}
#[inline]
pub fn alignment(&self) -> u64 {
self.alignment
}
#[inline]
pub fn aligned_size(&self) -> u64 {
self.aligned_size
}
#[inline]
pub fn len(&self) -> usize {
self.count
}
#[inline]
pub fn is_empty(&self) -> bool {
self.count == 0
}
pub fn clear(&mut self) {
self.data.clear();
self.count = 0;
}
pub fn push(&mut self, value: &T) -> u32 {
let offset = (self.count as u64 * self.aligned_size) as u32;
let bytes = bytemuck::bytes_of(value);
self.data.extend_from_slice(bytes);
let padding = self.aligned_size as usize - bytes.len();
self.data.extend(std::iter::repeat_n(0u8, padding));
self.count += 1;
offset
}
pub fn flush(&mut self) -> bool {
if self.data.is_empty() {
return false;
}
let required_size = self.data.len() as u64;
let reallocated = if required_size > self.capacity {
self.grow(required_size);
true
} else {
false
};
let ctxt = Context::get();
ctxt.write_buffer(&self.buffer, 0, &self.data);
reallocated
}
fn grow(&mut self, required_size: u64) {
let ctxt = Context::get();
let mut new_capacity = self.capacity;
while new_capacity < required_size {
new_capacity *= 2;
}
self.buffer = ctxt.create_buffer(&wgpu::BufferDescriptor {
label: Some(self.label),
size: new_capacity,
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
self.capacity = new_capacity;
}
#[inline]
pub fn buffer(&self) -> &wgpu::Buffer {
&self.buffer
}
#[inline]
pub fn capacity(&self) -> u64 {
self.capacity
}
}