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}