use {
crate::{
graphics::vulkan::{
raii, Frame, FramesInFlight, OwnedBlock, VulkanContext,
},
trace,
},
anyhow::{bail, Result},
ash::vk,
std::marker::PhantomData,
};
#[derive(Debug)]
pub struct UniformBuffer<DataT: Sized + Copy> {
buffer: raii::Buffer,
block: OwnedBlock,
aligned_unit_size: usize,
count: usize,
_phantom_data: PhantomData<DataT>,
}
impl<DataT> UniformBuffer<DataT>
where
DataT: Sized + Copy,
{
pub fn allocate(cxt: &VulkanContext, count: usize) -> Result<Self> {
let properties = unsafe {
cxt.instance
.get_physical_device_properties(cxt.physical_device)
};
let aligned_unit_size: u64 = {
let count = size_of::<DataT>() as u64
/ properties.limits.min_uniform_buffer_offset_alignment;
(count + 1) * properties.limits.min_uniform_buffer_offset_alignment
};
let buffer_size_in_bytes = aligned_unit_size * count as u64;
let (block, buffer) = OwnedBlock::allocate_buffer(
cxt.allocator.clone(),
&vk::BufferCreateInfo {
size: buffer_size_in_bytes,
usage: vk::BufferUsageFlags::UNIFORM_BUFFER,
sharing_mode: vk::SharingMode::EXCLUSIVE,
queue_family_index_count: 1,
p_queue_family_indices: &cxt.graphics_queue_family_index,
..Default::default()
},
vk::MemoryPropertyFlags::HOST_VISIBLE
| vk::MemoryPropertyFlags::HOST_COHERENT,
)?;
Ok(Self {
buffer,
block,
count,
aligned_unit_size: aligned_unit_size as usize,
_phantom_data: PhantomData,
})
}
pub fn allocate_per_frame(
cxt: &VulkanContext,
frames_in_flight: &FramesInFlight,
) -> Result<Self> {
Self::allocate(cxt, frames_in_flight.frame_count())
}
pub fn buffer(&self) -> vk::Buffer {
self.buffer.raw
}
pub fn update_frame_data(
&mut self,
frame: &Frame,
data: DataT,
) -> Result<()> {
unsafe { self.write_indexed(frame.frame_index(), data) }
}
pub fn offset_for_index(&self, frame_index: usize) -> u64 {
(frame_index * self.aligned_unit_size) as u64
}
pub unsafe fn write_indexed(
&mut self,
index: usize,
data: DataT,
) -> Result<()> {
if index >= self.count {
bail!(
trace!("Attempt to write to index {}/{}", index, self.count)()
);
}
let offset = self.offset_for_index(index) as isize;
std::ptr::copy_nonoverlapping(
&data,
self.block.mapped_ptr().byte_offset(offset) as *mut DataT,
1,
);
Ok(())
}
}