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/// A shader input register, usually corresponding to a single vertex attribute
11/// (e.g. position or color). These are called `v0`, `v1`, ... `v15` in the
12/// [picasso](https://github.com/devkitPro/picasso/blob/master/Manual.md)
13/// shader language.
14#[repr(u8)]
15#[derive(Copy, Clone, Debug)]
16pub enum Register {
17    V0 = 0,
18    V1 = 1,
19    V2 = 2,
20    V3 = 3,
21    V4 = 4,
22    V5 = 5,
23    V6 = 6,
24    V7 = 7,
25    V8 = 8,
26    V9 = 9,
27    V10 = 10,
28    V11 = 11,
29    V12 = 12,
30    V13 = 13,
31    V14 = 14,
32    V15 = 15,
33}
34
35/// The permutation of a buffer containing vertex attribute data.
36/// The Permutation maps the layout of an input buffer's fields to the
37/// input registers used in the picasso shader.
38#[derive(Debug, Copy, Clone)]
39pub struct Permutation {
40    pub(crate) permutation: u64,
41    pub(crate) attrib_count: u8,
42}
43
44impl Permutation {
45    /// Construct the permutation for a buffer whos fields (in order) correspond to the
46    /// provided list of input registers (as used in the picasso shader).
47    ///
48    /// # Example
49    /// ## Picasso
50    /// ```
51    /// ; Inputs (defined as aliases for convenience)
52    /// .alias inpos         v0 ; fvec3
53    /// .alias innorm        v1 ; fvec3
54    /// .alias intex         v2 ; fvec2
55    /// ```
56    ///
57    /// ## Rust
58    ///
59    /// ```
60    /// struct Vertex {
61    ///     pos: [f32; 3],
62    ///     tex: [f32; 2],
63    /// }
64    ///
65    /// impl Vertex {
66    ///     pub fn permutation() -> Permutation {
67    ///         Permutation::from_layout(&[Register::V0, Register::V2]).unwrap()
68    ///     }
69    /// }
70    ///
71    /// struct Normal {
72    ///     norm: [f32; 3],
73    /// }
74    ///
75    /// impl Normal {
76    ///     pub fn permutation() -> Permutation {
77    ///         Permutation::from_layout(&[Register::V1]).unwrap()
78    ///     }
79    /// }
80    /// ```
81    ///
82    /// # Errors
83    ///
84    /// * If more than 16 attribute registers are provided (i.e. `layout.len() > 16`)
85    pub fn from_layout(layout: &[Register]) -> Result<Self, crate::Error> {
86        if layout.len() > 16 {
87            return Err(crate::Error::IndexOutOfBounds {
88                idx: (layout.len() - 1) as i32,
89                len: 16,
90            });
91        }
92
93        let mut perm: u64 = 0;
94
95        for (i, l) in layout.iter().enumerate() {
96            perm |= (*l as u64) << (i * 4);
97        }
98
99        Ok(Self {
100            permutation: perm,
101            attrib_count: layout.len() as u8,
102        })
103    }
104}
105
106/// Vertex attribute info. This struct describes how vertex buffers are
107/// layed out and used (i.e. the shape of the vertex data).
108#[derive(Debug)]
109#[doc(alias = "C3D_AttrInfo")]
110pub struct Info(pub(crate) citro3d_sys::C3D_AttrInfo);
111
112/// An attribute index. This is the attribute's actual index in the input buffer,
113/// and may correspond to any [`Register`] (or multiple) as input in the shader
114/// program.
115#[allow(dead_code)]
116#[derive(Debug, Clone, Copy)]
117pub struct Index(u8);
118
119/// The data format of an attribute.
120#[repr(u8)]
121#[derive(Debug, Clone, Copy)]
122#[doc(alias = "GPU_FORMATS")]
123pub enum Format {
124    /// A signed byte, i.e. [`i8`].
125    Byte = ctru_sys::GPU_BYTE,
126    /// An unsigned byte, i.e. [`u8`].
127    UnsignedByte = ctru_sys::GPU_UNSIGNED_BYTE,
128    /// A float, i.e. [`f32`].
129    Float = ctru_sys::GPU_FLOAT,
130    /// A short integer, i.e. [`i16`].
131    Short = ctru_sys::GPU_SHORT,
132}
133
134impl From<Format> for u8 {
135    fn from(value: Format) -> Self {
136        value as u8
137    }
138}
139
140// SAFETY: the RWLock ensures unique access when mutating the global struct, and
141// we trust citro3d to Do The Right Thing™ and not mutate it otherwise.
142unsafe impl Sync for Info {}
143unsafe impl Send for Info {}
144
145impl Default for Info {
146    #[doc(alias = "AttrInfo_Init")]
147    fn default() -> Self {
148        let mut raw = MaybeUninit::zeroed();
149        let raw = unsafe {
150            citro3d_sys::AttrInfo_Init(raw.as_mut_ptr());
151            raw.assume_init()
152        };
153        Self(raw)
154    }
155}
156
157impl Info {
158    /// Construct a new attribute info structure with no attributes.
159    pub fn new() -> Self {
160        Self::default()
161    }
162
163    pub(crate) fn copy_from(raw: *const citro3d_sys::C3D_AttrInfo) -> Option<Self> {
164        if raw.is_null() {
165            None
166        } else {
167            // This is less efficient than returning a pointer or something, but it's
168            // safer since we don't know the lifetime of the pointee
169            Some(Self(unsafe { *raw }))
170        }
171    }
172
173    /// Add an attribute loader to the attribute info. The resulting attribute index
174    /// indicates the registration order of the attributes.
175    ///
176    /// # Parameters
177    ///
178    /// * `register`: the shader program input register for this attribute.
179    /// * `format`: the data format of this attribute.
180    /// * `count`: the number of elements in each attribute (up to 4, corresponding
181    ///   to `xyzw` / `rgba` / `stpq`).
182    ///
183    /// # Errors
184    ///
185    /// * If `count > 4`
186    /// * If this attribute info already has the maximum number of attributes.
187    #[doc(alias = "AttrInfo_AddLoader")]
188    pub fn add_loader(
189        &mut self,
190        register: Register,
191        format: Format,
192        count: u8,
193    ) -> crate::Result<Index> {
194        if count > 4 {
195            return Err(crate::Error::InvalidSize);
196        }
197
198        // SAFETY: the &mut self.0 reference is only used to access fields in
199        // the attribute info, not stored somewhere for later use
200        let ret = unsafe {
201            citro3d_sys::AttrInfo_AddLoader(&mut self.0, register as _, format.into(), count.into())
202        };
203
204        let Ok(idx) = ret.try_into() else {
205            return Err(crate::Error::TooManyAttributes);
206        };
207
208        Ok(Index(idx))
209    }
210
211    /// Get the [`Permutation`] for a buffer with elements containing all the fields
212    /// added to this `Info`. If the buffer elements do not contain all the fields
213    /// or contain them in a different order than they were added to this `Info`,
214    /// construct the [`Permutation`] yourself using [`Permutation::from_layout`].
215    pub fn permutation(&self) -> Permutation {
216        Permutation {
217            permutation: self.0.permutation,
218            attrib_count: self.attr_count() as u8,
219        }
220    }
221
222    /// Get the number of registered attributes.
223    pub fn attr_count(&self) -> libc::c_int {
224        self.0.attrCount
225    }
226}