use vulkano::buffer::{Buffer, BufferCreateInfo, BufferUsage, Subbuffer};
use vulkano::memory::allocator::{
AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator,
};
use std::sync::Arc;
use super::scene_renderer::{ParticleVertex, SceneRenderError};
pub(super) mod shader {
vulkano_shaders::shader! {
ty: "compute",
include: ["src/shaders"],
path: "src/shaders/compute/gpu_particles.comp",
}
}
pub(super) struct GpuParticleState {
pub particles: Subbuffer<[[f32; 4]]>,
pub billboards: Subbuffer<[ParticleVertex]>,
pub spawned: u64,
pub slots: u32,
pub trail_points: u32,
}
impl GpuParticleState {
pub fn new(
allocator: &Arc<StandardMemoryAllocator>,
slots: u32,
trail_points: u32,
) -> Result<Self, SceneRenderError> {
let error =
|error: &dyn std::fmt::Display| SceneRenderError(error.to_string());
let device_buffer = |usage| {
(
BufferCreateInfo {
usage,
..Default::default()
},
AllocationCreateInfo {
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE,
..Default::default()
},
)
};
let (info, allocation) = device_buffer(
BufferUsage::STORAGE_BUFFER | BufferUsage::TRANSFER_DST,
);
let particles = Buffer::new_slice::<[f32; 4]>(
allocator.clone(),
info,
allocation,
u64::from(3 + trail_points) * u64::from(slots),
)
.map_err(|e| error(&e))?;
let (info, allocation) = device_buffer(
BufferUsage::STORAGE_BUFFER | BufferUsage::VERTEX_BUFFER,
);
let billboards = Buffer::new_slice::<ParticleVertex>(
allocator.clone(),
info,
allocation,
u64::from(1 + trail_points) * u64::from(slots),
)
.map_err(|e| error(&e))?;
Ok(Self {
particles,
billboards,
spawned: 0,
slots,
trail_points,
})
}
}
pub(super) fn new_spawns(placed: u64, spawned: u64, slots: u32) -> (u64, u32) {
let fresh = if spawned < placed {
spawned
} else {
spawned - placed
};
let count = fresh.min(u64::from(slots));
(spawned - count, count as u32)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_spawns_cap_at_one_ring_and_restart_from_zero() {
assert_eq!(new_spawns(0, 5, 100), (0, 5));
assert_eq!(new_spawns(5, 7, 100), (5, 2));
assert_eq!(new_spawns(5, 1_000, 100), (900, 100));
assert_eq!(new_spawns(50, 3, 100), (0, 3));
assert_eq!(new_spawns(9, 9, 100), (9, 0));
}
}