use std::marker::PhantomData; use std::mem::MaybeUninit; use crate::attrib; #[derive(Debug)] pub struct Info(pub(crate) citro3d_sys::C3D_BufInfo); #[derive(Debug, Clone, Copy)] pub struct Slice<'buf> { index: libc::c_int, size: libc::c_int, _vbo_data: PhantomData<&'buf ()>, buf_info: &'buf Info, } impl Slice<'_> { pub fn as_raw(&self) -> libc::c_int { self.index } pub fn size(&self) -> libc::c_int { self.size } pub fn info(&self) -> &Info { self.buf_info } } #[repr(u32)] #[derive(Debug, Clone, Copy)] pub enum Primitive { Triangles = ctru_sys::GPU_TRIANGLES, TriangleStrip = ctru_sys::GPU_TRIANGLE_STRIP, TriangleFan = ctru_sys::GPU_TRIANGLE_FAN, GeometryPrim = ctru_sys::GPU_GEOMETRY_PRIM, } impl Default for Info { fn default() -> Self { let mut info = MaybeUninit::zeroed(); let info = unsafe { citro3d_sys::BufInfo_Init(info.as_mut_ptr()); info.assume_init() }; Self(info) } } impl Info { pub fn new() -> Self { Self::default() } pub(crate) fn copy_from(raw: *const citro3d_sys::C3D_BufInfo) -> 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 })) } } pub fn add<'this, 'vbo, 'idx, T>( &'this mut self, vbo_data: &'vbo [T], attrib_info: &attrib::Info, ) -> crate::Result> where 'this: 'idx, 'vbo: 'idx, { let stride = std::mem::size_of::().try_into()?; // SAFETY: the lifetime of the VBO data is encapsulated in the return value's // 'vbo lifetime, and the pointer to &mut self.0 is used to access values // in the BufInfo, not copied to be used later. let res = unsafe { citro3d_sys::BufInfo_Add( &mut self.0, vbo_data.as_ptr().cast(), stride, attrib_info.attr_count(), attrib_info.permutation(), ) }; if res < 0 { // TODO: should we convert to a more specific error if this fails? // It looks like the common cases are // - too many buffers already added (max 12) // - physical memory address in the wrong place (this can be seen by // using default allocator instead of LinearAllocator) // Err(crate::Error::System(res)) } else { Ok(Slice { index: res, size: vbo_data.len().try_into()?, _vbo_data: PhantomData, buf_info: self, }) } } }