use ahash::RandomState;
use arrayvec::ArrayVec;
use bytemuck::NoUninit;
use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard};
use std::fmt::{Debug, Formatter};
use std::marker::PhantomData;
use std::sync::Arc;
use vulkano::buffer::{AllocateBufferError, Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer};
use vulkano::descriptor_set::layout::DescriptorSetLayoutBinding;
use vulkano::descriptor_set::{DescriptorBufferInfo, WriteDescriptorSet};
use vulkano::memory::allocator::{AllocationCreateInfo, MemoryTypeFilter};
use vulkano::{DeviceSize, Validated};
use crate::render::descriptor_set::{DescriptorOffsetIter, VKDescriptorSource};
use crate::render::frame::{FrameManager, FrameSet, FrameSetUpdateStyle};
use crate::render::EngineDevice;
pub trait UniformValue<T: BufferContents>: Debug + Send + Sync + 'static {
fn buffer_contents(&self) -> T;
}
impl<T: Debug + Clone + BufferContents> UniformValue<T> for T {
#[inline]
fn buffer_contents(&self) -> T {
self.clone()
}
}
struct UniformBuffer<T: ?Sized> {
buffer: Subbuffer<T>,
stale: bool,
}
impl<T: ?Sized> Debug for UniformBuffer<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("UniformBuffer")
.field("buffer", &"...")
.field("stale", &self.stale)
.finish()
}
}
pub type UniformReadGuard<'a, T> = RwLockReadGuard<'a, T>;
pub type UniformWriteGuard<'a, T> = RwLockWriteGuard<'a, T>;
pub struct Uniform<T: BufferContents, U: UniformValue<T> = T> {
value: Arc<RwLock<U>>,
buffers: Arc<FrameSet<UniformBuffer<T>, Validated<AllocateBufferError>>>,
random_state: RandomState,
}
impl<T: BufferContents, U: UniformValue<T>> Debug for Uniform<T, U> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Uniform")
.field("value", &self.value)
.field("buffers", &self.buffers)
.finish_non_exhaustive()
}
}
impl<T: BufferContents, U: UniformValue<T>> Uniform<T, U> {
pub fn new(
device: Arc<EngineDevice>,
frame_manager: Arc<dyn FrameManager>,
value: U,
) -> Result<Self, Validated<AllocateBufferError>> {
let value = Arc::new(RwLock::new(value));
let buffer_device = device.clone();
let buffer_value = value.clone();
let buffers = FrameSet::with_update_style(
frame_manager,
FrameSetUpdateStyle::KeepExisting,
move |_| {
let buffer = Buffer::from_data(
buffer_device.memory_allocator().clone(),
BufferCreateInfo {
usage: BufferUsage::UNIFORM_BUFFER,
..Default::default()
},
AllocationCreateInfo {
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
..Default::default()
},
buffer_value.read().buffer_contents(),
)?;
Ok(UniformBuffer {
buffer,
stale: false,
})
},
)?;
Ok(Self {
value,
buffers,
random_state: RandomState::new(),
})
}
#[inline]
pub fn read(&self) -> UniformReadGuard<'_, U> {
self.value.read()
}
#[inline]
pub fn write(&self) -> UniformWriteGuard<'_, U> {
let lock = self.value.write();
self.buffers.for_each(|buffer| buffer.stale = true);
lock
}
}
impl<T: BufferContents, U: UniformValue<T>> VKDescriptorSource for Uniform<T, U> {
fn write_descriptor(
&self,
frame_idx: usize,
binding: u32,
_layout: &DescriptorSetLayoutBinding,
) -> (WriteDescriptorSet, u64) {
let buffer = self.buffers.get(frame_idx).unwrap();
(
WriteDescriptorSet::buffer(binding, buffer.buffer.clone()),
self.random_state.hash_one(&buffer.buffer),
)
}
fn update_descriptor_source(&self, frame_idx: usize) -> u64 {
let value = self.value.read();
let mut buffer = self.buffers.get(frame_idx).unwrap();
if buffer.stale {
*buffer.buffer.write().unwrap() = value.buffer_contents();
buffer.stale = false;
}
self.random_state.hash_one(&buffer.buffer)
}
}
#[derive(Debug)]
struct UniformSetBuffer {
buffer: Subbuffer<[u8]>,
len: usize,
stale: bool,
}
pub struct UniformSet<T: BufferContents, const CAP: usize, U: UniformValue<T> = T> {
uniforms: Arc<RwLock<ArrayVec<U, CAP>>>,
buffers: Arc<FrameSet<UniformSetBuffer, Validated<AllocateBufferError>>>,
align: usize,
random_state: RandomState,
_phantom: PhantomData<T>,
}
impl<T: BufferContents, const CAP: usize, U: UniformValue<T>> Debug for UniformSet<T, CAP, U> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("UniformSet")
.field("uniforms", &self.uniforms)
.field("buffers", &self.buffers)
.field("align", &self.align)
.finish_non_exhaustive()
}
}
impl<T: BufferContents + NoUninit, const CAP: usize, U: UniformValue<T>> UniformSet<T, CAP, U> {
fn fill_buffer(buffer: &mut UniformSetBuffer, uniforms: &ArrayVec<U, CAP>, align: usize) {
let mut write_guard = buffer.buffer.write().unwrap();
for (val_idx, value) in uniforms.iter().enumerate() {
let buffer_value = value.buffer_contents();
let bytes = bytemuck::bytes_of(&buffer_value);
for (idx, byte) in bytes.iter().enumerate() {
write_guard[(val_idx * align) + idx] = *byte;
}
for idx in bytes.len()..align {
write_guard[(val_idx * align) + idx] = 0;
}
}
for idx in (uniforms.len() * align)..write_guard.len() {
write_guard[idx] = 0;
}
buffer.len = uniforms.len();
}
pub fn new(
device: Arc<EngineDevice>,
frame_manager: Arc<dyn FrameManager>,
values: impl IntoIterator<Item=U>,
) -> Result<Self, Validated<AllocateBufferError>> {
let uniforms = Arc::new(RwLock::new(
values.into_iter().collect::<ArrayVec<_, CAP>>()
));
let min_dynamic_align = device.vk_device()
.physical_device()
.properties().min_uniform_buffer_offset_alignment
.as_devicesize() as usize;
let align = (size_of::<T>() + min_dynamic_align - 1) & !(min_dynamic_align - 1);
let buffer_device = device.clone();
let buffer_uniforms = uniforms.clone();
let buffers = FrameSet::with_update_style(
frame_manager,
FrameSetUpdateStyle::KeepExisting,
move |_| {
let buffer = Buffer::new_slice::<u8>(
buffer_device.memory_allocator().clone(),
BufferCreateInfo {
usage: BufferUsage::UNIFORM_BUFFER,
..Default::default()
},
AllocationCreateInfo {
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
..Default::default()
},
(align * CAP).try_into().unwrap(),
)?;
let mut uniform_buffer = UniformSetBuffer {
buffer,
len: 0,
stale: false,
} ;
Self::fill_buffer(
&mut uniform_buffer,
&buffer_uniforms.read(),
align,
);
Ok(uniform_buffer)
},
)?;
Ok(Self {
uniforms,
buffers,
align,
random_state: RandomState::new(),
_phantom: PhantomData,
})
}
}
impl<T: BufferContents, const CAP: usize, U: UniformValue<T>> UniformSet<T, CAP, U> {
#[inline]
pub fn read(&self) -> UniformReadGuard<'_, ArrayVec<U, CAP>> {
self.uniforms.read()
}
#[inline]
pub fn write(&self) -> UniformWriteGuard<'_, ArrayVec<U, CAP>> {
let lock = self.uniforms.write();
self.buffers.for_each(|buffer| buffer.stale = true);
lock
}
}
impl<T: BufferContents + NoUninit, const CAP: usize, U: UniformValue<T>> VKDescriptorSource for UniformSet<T, CAP, U> {
fn write_descriptor(
&self,
frame_idx: usize,
binding: u32,
_layout: &DescriptorSetLayoutBinding,
) -> (WriteDescriptorSet, u64) {
let buffer = self.buffers.get(frame_idx).unwrap();
(
WriteDescriptorSet::buffer_with_range(
binding,
DescriptorBufferInfo {
buffer: buffer.buffer.clone(),
range: 0..size_of::<T>() as DeviceSize,
},
),
self.random_state.hash_one(&buffer.buffer),
)
}
fn update_descriptor_source(&self, frame_idx: usize) -> u64 {
let values = self.uniforms.read();
let mut buffer = self.buffers.get(frame_idx).unwrap();
if buffer.stale {
Self::fill_buffer(
&mut buffer,
&values,
self.align,
);
buffer.stale = false;
}
self.random_state.hash_one(&buffer.buffer)
}
fn descriptor_offsets(&self, frame_idx: usize) -> Option<DescriptorOffsetIter> {
let buffer = self.buffers.get(frame_idx).unwrap();
Some(DescriptorOffsetIter::new(self.align as u32, (buffer.len * self.align) as u32))
}
}