citro3d/
attrib.rs

1//! Configure vertex attributes.
2//!
3//! This module has types and helpers for describing the shape/structure of vertex
4//! data to be sent to the GPU.
5//!
6//! See the [`buffer`](crate::buffer) module to use the vertex data itself.
7
8use std::mem::MaybeUninit;
9
10/// Vertex attribute info. This struct describes how vertex buffers are
11/// layed out and used (i.e. the shape of the vertex data).
12#[derive(Debug)]
13#[doc(alias = "C3D_AttrInfo")]
14pub struct Info(pub(crate) citro3d_sys::C3D_AttrInfo);
15
16/// A shader input register, usually corresponding to a single vertex attribute
17/// (e.g. position or color). These are called `v0`, `v1`, ... `v15` in the
18/// [picasso](https://github.com/devkitPro/picasso/blob/master/Manual.md)
19/// shader language.
20#[derive(Debug, Clone, Copy)]
21pub struct Register(libc::c_int);
22
23impl Register {
24    /// Get a register corresponding to the given index.
25    ///
26    /// # Errors
27    ///
28    /// Returns an error for `n >= 16`.
29    pub fn new(n: u16) -> crate::Result<Self> {
30        if n < 16 {
31            Ok(Self(n.into()))
32        } else {
33            Err(crate::Error::TooManyAttributes)
34        }
35    }
36}
37
38/// An attribute index. This is the attribute's actual index in the input buffer,
39/// and may correspond to any [`Register`] (or multiple) as input in the shader
40/// program.
41#[allow(dead_code)]
42#[derive(Debug, Clone, Copy)]
43pub struct Index(u8);
44
45/// The data format of an attribute.
46#[repr(u8)]
47#[derive(Debug, Clone, Copy)]
48#[doc(alias = "GPU_FORMATS")]
49pub enum Format {
50    /// A signed byte, i.e. [`i8`].
51    Byte = ctru_sys::GPU_BYTE,
52    /// An unsigned byte, i.e. [`u8`].
53    UnsignedByte = ctru_sys::GPU_UNSIGNED_BYTE,
54    /// A float, i.e. [`f32`].
55    Float = ctru_sys::GPU_FLOAT,
56    /// A short integer, i.e. [`i16`].
57    Short = ctru_sys::GPU_SHORT,
58}
59
60impl From<Format> for u8 {
61    fn from(value: Format) -> Self {
62        value as u8
63    }
64}
65
66// SAFETY: the RWLock ensures unique access when mutating the global struct, and
67// we trust citro3d to Do The Right Thing™ and not mutate it otherwise.
68unsafe impl Sync for Info {}
69unsafe impl Send for Info {}
70
71impl Default for Info {
72    #[doc(alias = "AttrInfo_Init")]
73    fn default() -> Self {
74        let mut raw = MaybeUninit::zeroed();
75        let raw = unsafe {
76            citro3d_sys::AttrInfo_Init(raw.as_mut_ptr());
77            raw.assume_init()
78        };
79        Self(raw)
80    }
81}
82
83impl Info {
84    /// Construct a new attribute info structure with no attributes.
85    pub fn new() -> Self {
86        Self::default()
87    }
88
89    pub(crate) fn copy_from(raw: *const citro3d_sys::C3D_AttrInfo) -> Option<Self> {
90        if raw.is_null() {
91            None
92        } else {
93            // This is less efficient than returning a pointer or something, but it's
94            // safer since we don't know the lifetime of the pointee
95            Some(Self(unsafe { *raw }))
96        }
97    }
98
99    /// Add an attribute loader to the attribute info. The resulting attribute index
100    /// indicates the registration order of the attributes.
101    ///
102    /// # Parameters
103    ///
104    /// * `register`: the shader program input register for this attribute.
105    /// * `format`: the data format of this attribute.
106    /// * `count`: the number of elements in each attribute (up to 4, corresponding
107    ///   to `xyzw` / `rgba` / `stpq`).
108    ///
109    /// # Errors
110    ///
111    /// * If `count > 4`
112    /// * If this attribute info already has the maximum number of attributes.
113    #[doc(alias = "AttrInfo_AddLoader")]
114    pub fn add_loader(
115        &mut self,
116        register: Register,
117        format: Format,
118        count: u8,
119    ) -> crate::Result<Index> {
120        if count > 4 {
121            return Err(crate::Error::InvalidSize);
122        }
123
124        // SAFETY: the &mut self.0 reference is only used to access fields in
125        // the attribute info, not stored somewhere for later use
126        let ret = unsafe {
127            citro3d_sys::AttrInfo_AddLoader(&mut self.0, register.0, format.into(), count.into())
128        };
129
130        let Ok(idx) = ret.try_into() else {
131            return Err(crate::Error::TooManyAttributes);
132        };
133
134        Ok(Index(idx))
135    }
136
137    pub(crate) fn permutation(&self) -> u64 {
138        self.0.permutation
139    }
140
141    /// Get the number of registered attributes.
142    pub fn attr_count(&self) -> libc::c_int {
143        self.0.attrCount
144    }
145}