use std::mem::MaybeUninit; #[derive(Debug)] pub struct Info(pub(crate) citro3d_sys::C3D_AttrInfo); #[derive(Debug)] pub struct Register(libc::c_int); impl Register { pub fn new(n: u16) -> crate::Result { // TODO proper validation for attributes? Or maybe just a next() function // that gets atomically increasing indices or something? Or look at // and define some consts // or lookup functions Ok(Self(n.into())) } } #[must_use] pub struct Index(u8); #[repr(u32)] pub enum Format { Byte = ctru_sys::GPU_BYTE, UnsignedByte = ctru_sys::GPU_UNSIGNED_BYTE, Float = ctru_sys::GPU_FLOAT, Short = ctru_sys::GPU_SHORT, } // SAFETY: the RWLock ensures unique access when mutating the global struct, and // we trust citro3d to Do The Right Thing™ and not mutate it otherwise. unsafe impl Sync for Info {} unsafe impl Send for Info {} impl Default for Info { fn default() -> Self { let mut raw = MaybeUninit::zeroed(); let raw = unsafe { citro3d_sys::AttrInfo_Init(raw.as_mut_ptr()); raw.assume_init() }; Self(raw) } } impl Info { pub fn new() -> Self { Self::default() } pub(crate) fn copy_from(raw: *const citro3d_sys::C3D_AttrInfo) -> Option { if raw.is_null() { None } else { // This is less efficient than returning a pointer or something, but it's // safer since we don't know the lifetime of the pointee Some(Self(unsafe { *raw })) } } /// Add an attribute loader to the attribute info. By default, the resulting /// attribute index will be appended to the permutation pub fn add_loader( &mut self, register: Register, format: Format, count: usize, ) -> crate::Result { let count = count.try_into()?; // SAFETY: the &mut self.0 reference is only used to access fields in // the attribute info, not stored somewhere for later use let ret = unsafe { citro3d_sys::AttrInfo_AddLoader(&mut self.0, register.0, format as u32, count) }; let Ok(idx) = ret.try_into() else { return Err(crate::Error::FailedToInitialize) }; Ok(Index(idx)) } pub fn set_permutation(&mut self, indices: &[Index]) -> crate::Result<()> { if indices.len() > 16 { return Err(crate::Error::TooManyAttributes); } let attr_count: libc::c_int = indices.len().try_into().unwrap(); let mut bytes: Vec = indices .windows(2) .map(|window| { let [lo, hi] = match *window { [Index(lo), Index(hi)] => [lo, hi], [Index(lo)] => [lo, 0], // high nibble is just padding _ => unreachable!(), // window size of 2 == always 1 or 2 elements }; // each value is a nibble, combine them into a byte lo | (hi << 4) }) .collect(); // pad the remainder with zeros bytes.extend(std::iter::repeat(0).take(8 - bytes.len())); let permutation = bytemuck::cast(<[u8; 8]>::try_from(bytes).unwrap()); self.0.permutation = permutation; self.0.attrCount = attr_count; Ok(()) } /// Get the current permutation of input register to vertex attributes mapping. /// See [GPU/Internal Registers] for an explanation of how the bits are laid out /// in the resulting value. /// /// [GPU/Internal Registers]: https://3dbrew.org/wiki/GPU/Internal_Registers#GPUREG_SH_ATTRIBUTES_PERMUTATION_LOW pub fn permutation(&self) -> u64 { self.0.permutation } /// Get the number of attributes in the current permutation. pub fn count(&self) -> libc::c_int { self.0.attrCount } }