use std::{collections::HashMap, fmt::Debug, sync::Arc};
use glam::Mat4;
use vulkano::{
DeviceAddress, Validated,
buffer::{AllocateBufferError, Buffer, BufferContents, BufferCreateInfo, BufferUsage},
command_buffer::DrawIndexedIndirectCommand,
memory::allocator::{AllocationCreateInfo, MemoryTypeFilter},
};
use crate::{
Context,
command_buffer::{StarlightCommandBuffer, StarlightIndexBuffer},
materials::MaterialHandle,
mesh_manager::{Mesh, MeshManager},
resource_manager::{ResourceHandle, ResourceManager},
resources::buffer::{Bda, StarlightSubBuffer},
};
#[derive(BufferContents, Clone, Copy, Debug)]
#[repr(C)]
struct ObjectMetadata {
material: DeviceAddress,
data: DeviceAddress,
global_transform: Option<Bda<'static, Mat4>>,
data_type: u8,
}
#[repr(u8)]
pub enum ExtDataType {
None = 0,
}
pub struct Object<Material: MaterialHandle> {
mesh: Arc<Mesh>,
material: Arc<Material>,
transform: ResourceHandle<Mat4>,
}
const TRANSFORM_BUFFER_SIZE: u64 = 512;
#[derive(Debug)]
pub struct ObjectManager {
transform_manager: Arc<ResourceManager<Mat4>>,
}
impl ObjectManager {
pub fn new() -> Self {
Self {
transform_manager: ResourceManager::new(TRANSFORM_BUFFER_SIZE),
}
}
pub async fn alloc<Material: MaterialHandle>(
&self,
ctx: &Context,
mesh: &Arc<Mesh>,
material: &Arc<Material>,
global_transform: Mat4,
) -> Result<Arc<Object<Material>>, Validated<AllocateBufferError>> {
let handle = self.transform_manager.alloc(ctx, global_transform).await?;
Ok(Arc::new(Object {
mesh: mesh.clone(),
material: material.clone(),
transform: handle,
}))
}
}
impl<M: MaterialHandle> Object<M> {
pub async fn update_global_transform(&self, global_transform: Mat4) {
self.transform
.new_wite_op()
.write(|mut v| *v = global_transform)
.await
.unwrap();
}
}
pub struct RenderInvocationData<Material: MaterialHandle> {
metadata: StarlightSubBuffer<[ObjectMetadata]>,
indirect_buf: StarlightSubBuffer<[DrawIndexedIndirectCommand]>,
indirect_obj_count: u32,
mesh_manager: Arc<MeshManager>,
objects: Vec<Arc<Object<Material>>>,
}
impl<Material: MaterialHandle> RenderInvocationData<Material> {
pub fn new<'a>(
ctx: &Context,
objects: Vec<Arc<Object<Material>>>,
) -> Result<Self, Validated<AllocateBufferError>> {
let metadata_buf = Buffer::new_slice(
ctx.alloc.clone(),
BufferCreateInfo {
usage: BufferUsage::SHADER_DEVICE_ADDRESS | BufferUsage::TRANSFER_DST,
..Default::default()
},
AllocationCreateInfo {
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
..Default::default()
},
objects.len() as u64,
)?;
let mut objs_map: HashMap<*const Mesh, Vec<&Object<Material>>> = HashMap::new();
for obj in &objects {
objs_map
.entry(Arc::as_ptr(&obj.mesh))
.or_default()
.push(obj);
}
let indirect_buf = Buffer::new_slice(
ctx.alloc.clone(),
BufferCreateInfo {
usage: BufferUsage::INDIRECT_BUFFER,
..Default::default()
},
AllocationCreateInfo {
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
..Default::default()
},
objs_map.len() as u64,
)?;
let mut indirect_writer = indirect_buf.write().unwrap();
let mut meta_writer = metadata_buf.write().unwrap();
let mut metadata_i = 0;
for (i, (mesh, objs)) in objs_map.into_iter().enumerate() {
let first_vertex = unsafe { mesh.as_ref_unchecked().vertex_alloc.offset };
let first_index = unsafe { mesh.as_ref_unchecked().index_alloc.offset };
let index_len = unsafe { mesh.as_ref_unchecked().index_len };
let first_instance = metadata_i;
for obj in &objs {
meta_writer[metadata_i] = ObjectMetadata {
material: unsafe { obj.material.get_device_ptr() },
data: 0,
global_transform: Some(unsafe {
obj.transform.device_address().unwrap().to_static()
}),
data_type: ExtDataType::None as u8,
};
metadata_i += 1;
}
indirect_writer[i] = DrawIndexedIndirectCommand {
index_count: index_len,
instance_count: dbg!(objs.len() as u32),
first_index,
vertex_offset: first_vertex,
first_instance: first_instance as u32,
};
}
drop(indirect_writer);
drop(meta_writer);
let indirect_obj_count = indirect_buf.len() as u32;
let metadata_buf = StarlightSubBuffer::from_subbuffer(ctx, metadata_buf);
let indirect_buf = StarlightSubBuffer::from_subbuffer(ctx, indirect_buf);
Ok(Self {
metadata: metadata_buf,
indirect_obj_count,
indirect_buf,
mesh_manager: objects[0].mesh.manager.clone(),
objects,
})
}
pub async fn bind_buffers(&self, cmds: &mut StarlightCommandBuffer) {
let vbuf = self.mesh_manager.vertex_buffer.read().await;
let ibuf = self.mesh_manager.index_buffer.read().await;
cmds.used_reads
.extend(self.objects.iter().flat_map(|v| v.material.get_read_ops()));
cmds.bind_vertex_buffers(0, &[vbuf.new_read_op()]).unwrap();
cmds.bind_index_buffer(&StarlightIndexBuffer::U32(ibuf.new_read_op()))
.unwrap();
}
pub fn metadata_bda(&self, cmds: &mut StarlightCommandBuffer) -> DeviceAddress {
cmds.used_reads.push(self.metadata.new_read_op());
unsafe {
self.metadata
.device_address()
.unwrap()
.to_static()
.as_raw()
.get()
}
}
pub fn draw(&self, cmds: &mut StarlightCommandBuffer) {
unsafe {
cmds.draw_indexed_indirect(
&(self.indirect_buf.new_read_op() as _),
self.indirect_obj_count,
size_of::<DrawIndexedIndirectCommand>() as u32,
)
.unwrap();
}
}
}