Browse Source

Add attrib docstrings and update some names

pull/16/head
Ian Chamberlain 2 years ago
parent
commit
f65da50b68
No known key found for this signature in database
GPG Key ID: AE5484D09405AA60
  1. 14
      citro3d/examples/triangle.rs
  2. 100
      citro3d/src/attrib.rs
  3. 15
      citro3d/src/buffer.rs
  4. 2
      citro3d/src/error.rs
  5. 2
      citro3d/src/lib.rs

14
citro3d/examples/triangle.rs

@ -143,7 +143,7 @@ fn main() {
fn prepare_vbos<'buf, 'info, 'vbo>( fn prepare_vbos<'buf, 'info, 'vbo>(
buf_info: &'info mut buffer::Info, buf_info: &'info mut buffer::Info,
vbo_data: &'vbo [Vertex], vbo_data: &'vbo [Vertex],
) -> (attrib::Info, buffer::Index<'buf>) ) -> (attrib::Info, buffer::Slice<'buf>)
where where
'info: 'buf, 'info: 'buf,
'vbo: 'buf, 'vbo: 'buf,
@ -154,20 +154,12 @@ where
let reg0 = attrib::Register::new(0).unwrap(); let reg0 = attrib::Register::new(0).unwrap();
let reg1 = attrib::Register::new(1).unwrap(); let reg1 = attrib::Register::new(1).unwrap();
// The default permutation would actually already be what we want if we attr_info
// inserted position, then color, but just show that it's customizable
// by swapping the order then using `set_permutation`.
let color_attr = attr_info
.add_loader(reg0, attrib::Format::Float, 3) .add_loader(reg0, attrib::Format::Float, 3)
.unwrap(); .unwrap();
let position_attr = attr_info
.add_loader(reg1, attrib::Format::Float, 3)
.unwrap();
attr_info attr_info
.set_permutation(&[position_attr, color_attr]) .add_loader(reg1, attrib::Format::Float, 5)
.unwrap(); .unwrap();
let buf_idx = buf_info.add(vbo_data, &attr_info).unwrap(); let buf_idx = buf_info.add(vbo_data, &attr_info).unwrap();

100
citro3d/src/attrib.rs

@ -1,29 +1,49 @@
use std::mem::MaybeUninit; use std::mem::MaybeUninit;
/// Vertex attribute info. This struct is used to describe how vertex buffers are
/// used (i.e. the shape of the vertex data).
#[derive(Debug)] #[derive(Debug)]
pub struct Info(pub(crate) citro3d_sys::C3D_AttrInfo); pub struct Info(pub(crate) citro3d_sys::C3D_AttrInfo);
#[derive(Debug)] /// A shader input register, usually corresponding to a single vertex attribute
/// (e.g. position or color). These are called `v0`, `v1`, ... `v15` in the
/// [picasso](https://github.com/devkitPro/picasso/blob/master/Manual.md)
/// shader language.
#[derive(Debug, Clone, Copy)]
pub struct Register(libc::c_int); pub struct Register(libc::c_int);
impl Register { impl Register {
/// Get a register corresponding to the given index.
///
/// # Errors
///
/// Returns an error for `n >= 16`.
pub fn new(n: u16) -> crate::Result<Self> { pub fn new(n: u16) -> crate::Result<Self> {
// TODO proper validation for attributes? Or maybe just a next() function if n < 16 {
// that gets atomically increasing indices or something? Or look at
// <https://3dbrew.org/wiki/GPU/Internal_Registers> and define some consts
// or lookup functions
Ok(Self(n.into())) Ok(Self(n.into()))
} else {
Err(crate::Error::TooManyAttributes)
}
} }
} }
#[must_use] /// An attribute index. This is the attribute's actual index in the input buffer,
/// and may correspond to any [`Register`] (or multiple) as input in the shader
/// program.
#[derive(Debug, Clone, Copy)]
pub struct Index(u8); pub struct Index(u8);
/// The data format of an attribute.
#[repr(u32)] #[repr(u32)]
#[derive(Debug, Clone, Copy)]
pub enum Format { pub enum Format {
/// A signed byte, i.e. [`i8`].
Byte = ctru_sys::GPU_BYTE, Byte = ctru_sys::GPU_BYTE,
/// An unsigned byte, i.e. [`u8`].
UnsignedByte = ctru_sys::GPU_UNSIGNED_BYTE, UnsignedByte = ctru_sys::GPU_UNSIGNED_BYTE,
/// A float, i.e. [`f32`].
Float = ctru_sys::GPU_FLOAT, Float = ctru_sys::GPU_FLOAT,
/// A short integer, i.e. [`i16`].
Short = ctru_sys::GPU_SHORT, Short = ctru_sys::GPU_SHORT,
} }
@ -44,6 +64,7 @@ impl Default for Info {
} }
impl Info { impl Info {
/// Construct a new attribute info structure with no attributes.
pub fn new() -> Self { pub fn new() -> Self {
Self::default() Self::default()
} }
@ -58,70 +79,49 @@ impl Info {
} }
} }
/// Add an attribute loader to the attribute info. By default, the resulting /// Add an attribute loader to the attribute info. The resulting attribute index
/// attribute index will be appended to the permutation /// indicates the registration order of the attributes.
///
/// ## Parameters
///
/// * `register`: the shader program input register for this attribute.
/// * `format`: the data format of this attribute.
/// * `count`: the number of elements in each attribute (up to 4, corresponding
/// to `xyzw` / `rgba` / `stpq`).
///
/// ## Errors
///
/// * If `count > 4`
/// * If this attribute info already has the maximum number of attributes.
pub fn add_loader( pub fn add_loader(
&mut self, &mut self,
register: Register, register: Register,
format: Format, format: Format,
count: usize, count: u8,
) -> crate::Result<Index> { ) -> crate::Result<Index> {
let count = count.try_into()?; if count > 4 {
return Err(crate::Error::InvalidSize);
}
// SAFETY: the &mut self.0 reference is only used to access fields in // SAFETY: the &mut self.0 reference is only used to access fields in
// the attribute info, not stored somewhere for later use // the attribute info, not stored somewhere for later use
let ret = unsafe { let ret = unsafe {
citro3d_sys::AttrInfo_AddLoader(&mut self.0, register.0, format as u32, count) citro3d_sys::AttrInfo_AddLoader(&mut self.0, register.0, format as u32, count.into())
}; };
let Ok(idx) = ret.try_into() else { let Ok(idx) = ret.try_into() else {
return Err(crate::Error::FailedToInitialize) return Err(crate::Error::TooManyAttributes)
}; };
Ok(Index(idx)) Ok(Index(idx))
} }
pub fn set_permutation(&mut self, indices: &[Index]) -> crate::Result<()> { pub(crate) fn permutation(&self) -> u64 {
if indices.len() > 16 {
return Err(crate::Error::TooManyAttributes);
}
let attr_count: libc::c_int = indices.len().try_into().unwrap();
let mut bytes: Vec<u8> = 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 self.0.permutation
} }
/// Get the number of attributes in the current permutation. /// Get the number of registered attributes.
pub fn count(&self) -> libc::c_int { pub fn attr_count(&self) -> libc::c_int {
self.0.attrCount self.0.attrCount
} }
} }

15
citro3d/src/buffer.rs

@ -6,16 +6,15 @@ use crate::attrib;
#[derive(Debug)] #[derive(Debug)]
pub struct Info(pub(crate) citro3d_sys::C3D_BufInfo); pub struct Info(pub(crate) citro3d_sys::C3D_BufInfo);
// TODO: is this a good name? It's more like a "handle" to the VBO data, or a slice.
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct Index<'buf> { pub struct Slice<'buf> {
index: libc::c_int, index: libc::c_int,
size: libc::c_int, size: libc::c_int,
_vbo_data: PhantomData<&'buf ()>, _vbo_data: PhantomData<&'buf ()>,
buf_info: &'buf Info, buf_info: &'buf Info,
} }
impl Index<'_> { impl Slice<'_> {
pub fn as_raw(&self) -> libc::c_int { pub fn as_raw(&self) -> libc::c_int {
self.index self.index
} }
@ -68,14 +67,12 @@ impl Info {
&'this mut self, &'this mut self,
vbo_data: &'vbo [T], vbo_data: &'vbo [T],
attrib_info: &attrib::Info, attrib_info: &attrib::Info,
) -> crate::Result<Index<'idx>> ) -> crate::Result<Slice<'idx>>
where where
'this: 'idx, 'this: 'idx,
'vbo: 'idx, 'vbo: 'idx,
{ {
let stride = std::mem::size_of::<T>().try_into()?; let stride = std::mem::size_of::<T>().try_into()?;
let attrib_count = attrib_info.count();
let permutation = attrib_info.permutation();
// SAFETY: the lifetime of the VBO data is encapsulated in the return value's // 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 // 'vbo lifetime, and the pointer to &mut self.0 is used to access values
@ -85,8 +82,8 @@ impl Info {
&mut self.0, &mut self.0,
vbo_data.as_ptr().cast(), vbo_data.as_ptr().cast(),
stride, stride,
attrib_count, attrib_info.attr_count(),
permutation, attrib_info.permutation(),
) )
}; };
@ -99,7 +96,7 @@ impl Info {
// <https://github.com/devkitPro/citro3d/blob/master/source/buffers.c#L13-L17> // <https://github.com/devkitPro/citro3d/blob/master/source/buffers.c#L13-L17>
Err(crate::Error::System(res)) Err(crate::Error::System(res))
} else { } else {
Ok(Index { Ok(Slice {
index: res, index: res,
size: vbo_data.len().try_into()?, size: vbo_data.len().try_into()?,
_vbo_data: PhantomData, _vbo_data: PhantomData,

2
citro3d/src/error.rs

@ -24,7 +24,7 @@ pub enum Error {
/// Indicates that a reference could not be obtained because a lock is already /// Indicates that a reference could not be obtained because a lock is already
/// held on the requested object. /// held on the requested object.
LockHeld, LockHeld,
/// Indicates that too many vertex attributes were specified (max 16 supported). /// Indicates that too many vertex attributes were specified (max 12 supported).
TooManyAttributes, TooManyAttributes,
} }

2
citro3d/src/lib.rs

@ -102,7 +102,7 @@ impl Instance {
} }
/// Draw the specified primitivearrays. The /// Draw the specified primitivearrays. The
pub fn draw_arrays(&mut self, primitive: buffer::Primitive, index: buffer::Index) { pub fn draw_arrays(&mut self, primitive: buffer::Primitive, index: buffer::Slice) {
self.set_buffer_info(index.info()); self.set_buffer_info(index.info());
// TODO: should we also require the attrib info directly here? // TODO: should we also require the attrib info directly here?

Loading…
Cancel
Save