diff --git a/citro3d/examples/triangle.rs b/citro3d/examples/triangle.rs index 6a1e0cc..7e51ace 100644 --- a/citro3d/examples/triangle.rs +++ b/citro3d/examples/triangle.rs @@ -1,7 +1,10 @@ +//! This example demonstrates the most basic usage of `citro3d`: rendering a simple +//! RGB triangle (sometimes called a "Hello triangle") to the 3DS screen. + #![feature(allocator_api)] -use citro3d::attrib::{self, AttrInfo}; -use citro3d::buffers::{self, BufInfo}; +use citro3d::attrib::{self}; +use citro3d::buffer::{self}; use citro3d::render::{ClearFlags, Target}; use citro3d::{include_aligned_bytes, shader}; use citro3d_sys::C3D_Mtx; @@ -110,7 +113,7 @@ fn main() { ); } - instance.draw_arrays(buffers::Primitive::Triangles, vbo_idx); + instance.draw_arrays(buffer::Primitive::Triangles, vbo_idx); }); }; @@ -119,9 +122,9 @@ fn main() { } } -fn prepare_vbos(vbo_data: &[Vertex]) -> buffers::Index { +fn prepare_vbos(vbo_data: &[Vertex]) -> buffer::Index { // Configure attributes for use with the vertex shader - let mut attr_info = AttrInfo::get_mut().expect("failed to get global attr info"); + let mut attr_info = attrib::Info::get_mut().expect("failed to get global attr info"); let reg0 = attrib::Register::new(0).unwrap(); let reg1 = attrib::Register::new(1).unwrap(); @@ -143,7 +146,7 @@ fn prepare_vbos(vbo_data: &[Vertex]) -> buffers::Index { .unwrap(); // Configure buffers - let mut buf_info = BufInfo::get_mut().unwrap(); + let mut buf_info = buffer::Info::get_mut().unwrap(); let buf_idx = buf_info.add(vbo_data, &attr_info).unwrap(); buf_idx diff --git a/citro3d/src/attrib.rs b/citro3d/src/attrib.rs index d4ee14b..5b7d354 100644 --- a/citro3d/src/attrib.rs +++ b/citro3d/src/attrib.rs @@ -1,7 +1,7 @@ use std::ops::{Deref, DerefMut}; use std::sync::{LazyLock, RwLock}; -static ATTR_INFO: LazyLock> = LazyLock::new(|| { +static INFO: LazyLock> = LazyLock::new(|| { let raw = unsafe { // TODO: should we check is_null() here? let info = citro3d_sys::C3D_GetAttrInfo(); @@ -9,13 +9,14 @@ static ATTR_INFO: LazyLock> = LazyLock::new(|| { info }; - RwLock::new(AttrInfo { raw }) + RwLock::new(Info { raw }) }); -pub struct AttrInfo { +pub struct Info { raw: *mut citro3d_sys::C3D_AttrInfo, } +#[derive(Debug, Clone, Copy)] pub struct Register(libc::c_int); impl Register { @@ -24,12 +25,12 @@ impl Register { // that gets atomically increasing indices or something? Or look at // and define some consts // or lookup functions - Ok(Self(n as _)) + Ok(Self(n.into())) } } #[must_use] -pub struct Index(libc::c_int); +pub struct Index(u8); #[repr(u32)] pub enum Format { @@ -41,18 +42,18 @@ pub enum Format { // 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 AttrInfo {} -unsafe impl Send for AttrInfo {} +unsafe impl Sync for Info {} +unsafe impl Send for Info {} -impl AttrInfo { +impl Info { /// Get a reference to the global attribute info. pub fn get() -> crate::Result> { - Ok(ATTR_INFO.try_read()?) + Ok(INFO.try_read()?) } /// Get a mutable reference to the global attribute info. pub fn get_mut() -> crate::Result> { - Ok(ATTR_INFO.try_write()?) + Ok(INFO.try_write()?) } /// Add an attribute loader to the attribute info. By default, the resulting @@ -65,9 +66,13 @@ impl AttrInfo { ) -> crate::Result { let count = count.try_into()?; - let idx = + let ret = unsafe { citro3d_sys::AttrInfo_AddLoader(self.raw, register.0, format as u32, count) }; + let Ok(idx) = ret.try_into() else { + return Err(crate::Error::FailedToInitialize) + }; + Ok(Index(idx)) } @@ -75,6 +80,7 @@ impl AttrInfo { 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) @@ -85,7 +91,7 @@ impl AttrInfo { _ => unreachable!(), // window size of 2 == always 1 or 2 elements }; // each value is a nibble, combine them into a byte - lo as u8 | (hi as u8) << 4 + lo | (hi << 4) }) .collect(); @@ -96,7 +102,7 @@ impl AttrInfo { unsafe { (*self.raw).permutation = permutation; - (*self.raw).attrCount = indices.len() as _; + (*self.raw).attrCount = attr_count; } Ok(()) } diff --git a/citro3d/src/buffers.rs b/citro3d/src/buffer.rs similarity index 86% rename from citro3d/src/buffers.rs rename to citro3d/src/buffer.rs index 9459c9f..a41527a 100644 --- a/citro3d/src/buffers.rs +++ b/citro3d/src/buffer.rs @@ -1,27 +1,29 @@ use std::marker::PhantomData; +use std::mem::MaybeUninit; use std::ops::{Deref, DerefMut}; use std::sync::{LazyLock, RwLock}; use crate::attrib; -static BUF_INFO: LazyLock> = LazyLock::new(|| { +static BUF_INFO: LazyLock> = LazyLock::new(|| { let raw = unsafe { let info = citro3d_sys::C3D_GetBufInfo(); citro3d_sys::BufInfo_Init(info); info }; - RwLock::new(BufInfo { raw }) + RwLock::new(Info { raw }) }); -pub struct BufInfo { +/// Vertex attribute info. This struct can be used to +pub struct Info { raw: *mut citro3d_sys::C3D_BufInfo, } // 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 BufInfo {} -unsafe impl Send for BufInfo {} +unsafe impl Sync for Info {} +unsafe impl Send for Info {} // TODO: is this a good name? It's more like a "handle" to the VBO data, or a slice. #[derive(Debug, Clone, Copy)] @@ -50,7 +52,7 @@ pub enum Primitive { GeometryPrim = ctru_sys::GPU_GEOMETRY_PRIM, } -impl BufInfo { +impl Info { /// Get a reference to the global buffer info. pub fn get() -> crate::Result> { Ok(BUF_INFO.try_read()?) @@ -64,7 +66,7 @@ impl BufInfo { pub fn add<'vbo, T>( &mut self, vbo_data: &'vbo [T], - attrib_info: &attrib::AttrInfo, + attrib_info: &attrib::Info, ) -> crate::Result> { let stride = std::mem::size_of::().try_into()?; let attrib_count = attrib_info.count(); @@ -73,8 +75,6 @@ impl BufInfo { let res = unsafe { citro3d_sys::BufInfo_Add( self.raw, - // TODO: figure out how the hell to encode the lifetime of this - // data so that we don't try to use it after it's destroyed... vbo_data.as_ptr().cast(), stride, attrib_count, diff --git a/citro3d/src/shader.rs b/citro3d/src/shader.rs index 91142ee..e962532 100644 --- a/citro3d/src/shader.rs +++ b/citro3d/src/shader.rs @@ -11,8 +11,8 @@ pub mod macros; /// A PICA200 shader program. It may have one or both of: /// -/// * A vertex [shader instance](Instance) -/// * A geometry [shader instance](Instance) +/// * A vertex shader [`Library`] +/// * A geometry shader [`Library`] /// /// The PICA200 does not support user-programmable fragment shaders. pub struct Program { @@ -86,7 +86,7 @@ impl Drop for Program { /// one or more [`Entrypoint`]s, but most commonly has one vertex shader and an /// optional geometry shader. /// -/// This is the result of parsing a shader binary (shbin), and the resulting +/// This is the result of parsing a shader binary (`.shbin`), and the resulting /// [`Entrypoint`]s can be used as part of a [`Program`]. pub struct Library(*mut ctru_sys::DVLB_s);