use crate::mpm_shaders::IVector;
use crate::mpm_shaders::grid::grid::{ActiveBlockHeader, Grid, Node};
use crate::mpm_shaders::solver::particle::{Kinematics, ParticleProperties, Position};
use crate::pipeline::MpmState;
use khal::backend::{Backend, GpuBackend};
use nexus_rbd::math::Vector;
use vortx::tensor::Tensor;
#[cfg(feature = "dim2")]
pub const BLOCK_SIZE: u32 = 8;
#[cfg(feature = "dim3")]
pub const BLOCK_SIZE: u32 = 4;
const NUM_CELL_PER_BLOCK: u32 = BLOCK_SIZE.pow(crate::mpm_shaders::DIM);
#[derive(Copy, Clone, Debug, Default, PartialEq)]
pub struct DebugParticle {
pub position: Vector,
pub velocity: Vector,
pub mass: f32,
pub radius: f32,
pub group_id: u32,
pub enabled: bool,
pub fixed: bool,
pub cdf_normal: Vector,
pub cdf_distance: f32,
pub cdf_affinity: u32,
}
#[derive(Copy, Clone, Debug, Default, PartialEq)]
pub struct DebugGridBlock {
pub virtual_id: IVector,
pub mins: Vector,
pub maxs: Vector,
pub num_particles: u32,
pub num_particles_with_extras: u32,
}
#[derive(Copy, Clone, Debug, Default, PartialEq)]
pub struct DebugGridNode {
pub position: Vector,
pub velocity: Vector,
pub mass: f32,
pub cdf_distance: f32,
pub cdf_affinity: u32,
}
#[derive(Clone, Debug, Default)]
pub struct DebugGrid {
pub cell_width: f32,
pub blocks: Vec<DebugGridBlock>,
pub nodes: Vec<DebugGridNode>,
}
impl MpmState {
pub async fn debug_particles(&self, backend: &GpuBackend) -> Vec<DebugParticle> {
let len = self.particles.len();
let positions = read_prefix::<Position>(backend, &self.particles.positions, len).await;
let kinematics = read_prefix::<Kinematics>(backend, &self.particles.kinematics, len).await;
let properties =
read_prefix::<ParticleProperties>(backend, &self.particles.properties, len).await;
if positions.len() < len || kinematics.len() < len || properties.len() < len {
return Vec::new();
}
(0..len)
.map(|i| {
let kin = &kinematics[i];
let props = &properties[i];
DebugParticle {
position: positions[i].pt,
velocity: kin.velocity,
mass: kin.mass,
radius: props.init_radius,
group_id: props.group_id,
enabled: kin.enabled != 0,
fixed: props.fixed != 0,
cdf_normal: kin.cdf.normal,
cdf_distance: kin.cdf.signed_distance,
cdf_affinity: kin.cdf.affinity.0,
}
})
.collect()
}
pub async fn debug_grid(&self, backend: &GpuBackend) -> DebugGrid {
let Ok(meta) = backend.slow_read_vec::<Grid>(self.grid.meta.buffer()).await else {
return DebugGrid::default();
};
let Some(meta) = meta.first().copied() else {
return DebugGrid::default();
};
let cell_width = meta.cell_width;
let num_blocks = meta.num_active_blocks.min(meta.capacity) as usize;
let headers =
read_prefix::<ActiveBlockHeader>(backend, &self.grid.active_blocks, num_blocks).await;
let nodes = read_prefix::<Node>(
backend,
&self.grid.nodes,
num_blocks * NUM_CELL_PER_BLOCK as usize,
)
.await;
let block_extent = Vector::splat(BLOCK_SIZE as f32 * cell_width);
let mut blocks = Vec::with_capacity(headers.len());
let mut debug_nodes = Vec::new();
for (bid, header) in headers.iter().enumerate() {
let mins = block_origin(header.virtual_id.id, cell_width);
blocks.push(DebugGridBlock {
virtual_id: header.virtual_id.id,
mins,
maxs: mins + block_extent,
num_particles: header.num_particles,
num_particles_with_extras: header.num_particles_with_extras,
});
let first_node = bid * NUM_CELL_PER_BLOCK as usize;
for shift in 0..NUM_CELL_PER_BLOCK as usize {
let Some(node) = nodes.get(first_node + shift) else {
continue;
};
if node.mass <= 0.0 {
continue;
}
debug_nodes.push(DebugGridNode {
position: mins + node_shift(shift as u32) * cell_width,
velocity: node.momentum_velocity,
mass: node.mass,
cdf_distance: node.cdf.distance,
cdf_affinity: node.cdf.affinities.0,
});
}
}
DebugGrid {
cell_width,
blocks,
nodes: debug_nodes,
}
}
}
fn block_origin(virtual_id: IVector, cell_width: f32) -> Vector {
#[cfg(feature = "dim2")]
let origin = Vector::new(virtual_id.x as f32, virtual_id.y as f32);
#[cfg(feature = "dim3")]
let origin = Vector::new(
virtual_id.x as f32,
virtual_id.y as f32,
virtual_id.z as f32,
);
origin * (BLOCK_SIZE as f32 * cell_width)
}
fn node_shift(shift_in_block: u32) -> Vector {
#[cfg(feature = "dim2")]
{
Vector::new(
(shift_in_block % BLOCK_SIZE) as f32,
(shift_in_block / BLOCK_SIZE) as f32,
)
}
#[cfg(feature = "dim3")]
{
Vector::new(
(shift_in_block % BLOCK_SIZE) as f32,
((shift_in_block / BLOCK_SIZE) % BLOCK_SIZE) as f32,
(shift_in_block / (BLOCK_SIZE * BLOCK_SIZE)) as f32,
)
}
}
async fn read_prefix<T: bytemuck::Pod + Default + Send + Sync>(
backend: &GpuBackend,
tensor: &Tensor<T>,
len: usize,
) -> Vec<T> {
let mut out = vec![T::default(); len.min(tensor.len() as usize)];
if out.is_empty() {
return out;
}
match backend.slow_read_buffer(tensor.buffer(), &mut out).await {
Ok(()) => out,
Err(_) => Vec::new(),
}
}