use anyhow::{Result, anyhow};
use aligned_box::AlignedBox;
use core::panic;
use std::{collections::HashMap, sync::{RwLock, RwLockReadGuard, RwLockWriteGuard}, unimplemented, usize};
use crate::{runtime::{ParticleID, MemberID, MemberIndexEntry, ParticleIndexEntry}, utils::FipsValue};
const ALIGNMENT: usize = 32;
#[derive(Clone)]
pub struct UniformMembers {
single_allocations: HashMap<String, FipsValue>
}
impl UniformMembers {
pub fn new() -> Self {
Self {
single_allocations: HashMap::new()
}
}
pub fn set_uniform_member<S: Into<String>, T: Into<FipsValue>>(&mut self, name: S, value: T) {
self.single_allocations.insert(name.into(), value.into());
}
fn get_member(&self, name: &str) -> Option<&FipsValue> {
self.single_allocations.get(name)
}
}
pub struct ParticleStore {
particles: HashMap<ParticleID, RwLock<ParticleData>>
}
impl ParticleStore {
pub(crate) fn new() -> Self {
Self {
particles: HashMap::new()
}
}
pub(crate) fn create_particles<'a>(&'a mut self, particle: (ParticleID, &ParticleIndexEntry),
count: usize, uniform_members: &UniformMembers) -> Result<RwLockWriteGuard<'a, ParticleData>>
{
let (particle_id, particle_definition) = particle;
if self.particles.contains_key(&particle_id) {
return Err(anyhow!("Particle with name {} has already been allocated",
particle_definition.get_name()));
};
let particle_data = ParticleData::new(particle_definition, count, uniform_members)?;
self.particles.insert(particle_id, RwLock::new(particle_data));
Ok(self.particles.get(&particle_id).unwrap().write().unwrap())
}
pub(crate) fn get_memory_usage(&self) -> usize {
let mut memory = 0;
for (_, particle_data) in &self.particles {
memory += particle_data.read().unwrap().get_memory_usage();
}
memory
}
pub(crate) fn get_particles(&self) -> impl Iterator<Item = (ParticleID, &RwLock<ParticleData>)> {
self.particles.iter().map(|(k,v)| (*k,v))
}
pub(crate) fn get_particle<'a>(&self, particle_id: ParticleID) -> Option<RwLockReadGuard<ParticleData>> {
self.particles.get(&particle_id).map(|x| x.read().unwrap())
}
pub(crate) fn get_particle_mut<'a>(&self, particle_id: ParticleID) -> Option<RwLockWriteGuard<ParticleData>> {
self.particles.get(&particle_id).map(|x| x.write().unwrap())
}
}
pub struct ParticleData {
count: usize,
members: HashMap<MemberID, RwLock<MemberData>>
}
impl ParticleData {
pub(crate) fn new(particle: &ParticleIndexEntry, count: usize, uniform_members: &UniformMembers) -> Result<Self>
{
let mut members = HashMap::new();
for (member_id, member) in particle.get_members() {
members.insert(member_id, RwLock::new(MemberData::new(member, count, uniform_members)?));
}
Ok(Self {
count,
members
})
}
pub(crate) fn get_particle_count(&self) -> usize {
self.count
}
pub(crate) fn get_memory_usage(&self) -> usize {
let mut memory = 0;
for (_, member_data) in &self.members {
memory += member_data.read().unwrap().get_memory_usage();
}
memory
}
pub(crate) fn borrow_member(&self, member_id: &MemberID) -> Option<RwLockReadGuard<MemberData>> {
match self.members.get(member_id) {
None => None,
Some(member) => {
Some(member.read().unwrap())
}
}
}
pub(crate) fn borrow_member_mut(&self, member_id: &MemberID) -> Option<RwLockWriteGuard<MemberData>> {
match self.members.get(member_id) {
None => None,
Some(member) => {
Some(member.write().unwrap())
}
}
}
}
pub enum MemberData {
PerParticle {
data: AlignedBox<[u8]>,
mutable: bool
},
Uniform(FipsValue)
}
impl MemberData {
pub(crate) fn new(member: &MemberIndexEntry, count: usize, uniform_members: &UniformMembers) -> Result<Self> {
match uniform_members.get_member(member.get_name()) {
Some(value) => {
Ok(Self::Uniform(value.clone()))
}
None => {
let data = AlignedBox::slice_from_value(ALIGNMENT, count*member.get_member_size()?, 0u8)
.map_err(|e| anyhow!("Cannot allocate particle member data: {}", e))?;
let mutable = member.is_mutable();
Ok(Self::PerParticle {
data, mutable
})
}
}
}
pub(crate) fn get_memory_usage(&self) -> usize {
match self {
MemberData::PerParticle { data, .. } => { data.len() }
MemberData::Uniform(value) => { value.get_size() }
}
}
pub(crate) fn is_uniform(&self) -> bool {
match self {
MemberData::PerParticle{..} => false,
MemberData::Uniform(_) => true
}
}
pub(crate) fn as_i64(&self) -> i64 {
match self {
Self::PerParticle {data, ..} => *bytemuck::from_bytes::<i64>(data),
Self::Uniform(value) => match value {
FipsValue::Int64(value) => *value,
_ => panic!("Cannot access member data as i64 (type is {})", value.get_type())
}
}
}
pub(crate) fn as_f64(&self) -> f64 {
match self {
Self::PerParticle {data, ..} => *bytemuck::from_bytes::<f64>(data),
Self::Uniform(value) => match value {
FipsValue::Double(value) => *value,
_ => panic!("Cannot access member data as f64 (type is {})", value.get_type())
}
}
}
pub(crate) fn as_i64_slice(&self) -> &[i64] {
match self {
Self::PerParticle {data, ..} => bytemuck::cast_slice::<u8,i64>(data),
Self::Uniform(_) => unimplemented!()
}
}
pub(crate) fn as_f64_slice(&self) -> &[f64] {
match self {
Self::PerParticle {data, ..} => bytemuck::cast_slice::<u8,f64>(data),
Self::Uniform(_) => unimplemented!()
}
}
pub(crate) fn as_i64_slice_mut(&mut self) -> &mut [i64] {
match self {
Self::PerParticle {data, ..} => bytemuck::cast_slice_mut::<u8,i64>(data),
Self::Uniform(_) => unimplemented!()
}
}
pub(crate) fn as_f64_slice_mut(&mut self) -> &mut [f64] {
match self {
Self::PerParticle {data, ..} => bytemuck::cast_slice_mut::<u8,f64>(data),
Self::Uniform(_) => unimplemented!()
}
}
}