1use std::any::type_name;
7use std::ffi::c_void;
8use std::mem::MaybeUninit;
9use std::rc::Rc;
10
11use ctru::linear::LinearAllocator;
12
13use crate::Error;
14use crate::attrib;
15
16pub trait BufferData: 'static {
18 fn buf_ptr(&self) -> *mut c_void;
20 fn stride(&self) -> usize;
22 fn buf_len(&self) -> usize;
24}
25
26impl<T: Sized + 'static> BufferData for Vec<T, LinearAllocator> {
27 fn buf_ptr(&self) -> *mut c_void {
28 self.as_ptr() as _
29 }
30
31 fn stride(&self) -> usize {
32 std::mem::size_of::<T>()
33 }
34
35 fn buf_len(&self) -> usize {
36 self.len()
37 }
38}
39
40impl<T: Sized + 'static> BufferData for Box<[T], LinearAllocator> {
41 fn buf_ptr(&self) -> *mut c_void {
42 self.as_ref() as *const _ as _
43 }
44
45 fn stride(&self) -> usize {
46 std::mem::size_of::<T>()
47 }
48
49 fn buf_len(&self) -> usize {
50 self.len()
51 }
52}
53
54impl<T: Sized + 'static> BufferData for Rc<[T], LinearAllocator> {
55 fn buf_ptr(&self) -> *mut c_void {
56 self.as_ref() as *const _ as _
57 }
58
59 fn stride(&self) -> usize {
60 std::mem::size_of::<T>()
61 }
62
63 fn buf_len(&self) -> usize {
64 self.len()
65 }
66}
67
68#[derive(Clone)]
72pub struct Buffer {
73 _data: Rc<dyn BufferData>,
74
75 data_ptr: *const c_void,
79 stride: isize,
80 len: usize,
81}
82
83impl Buffer {
84 pub fn new<T: Sized + Copy + 'static>(data: &[T]) -> Buffer {
91 let mut linear_data = Vec::with_capacity_in(data.len(), LinearAllocator);
92 linear_data.extend_from_slice(data);
93
94 Buffer {
95 data_ptr: linear_data.as_ptr() as _,
96 len: linear_data.len(),
97 stride: linear_data
98 .stride()
99 .try_into()
100 .map_err(|_| format!("{} is too large to be used in a buffer.", type_name::<T>()))
101 .unwrap(),
102 _data: Rc::new(linear_data),
103 }
104 }
105
106 pub fn new_with_stride(data: &[u8], stride: usize) -> Option<Buffer> {
117 if !data.len().is_multiple_of(stride) {
118 return None;
119 }
120
121 let mut linear_data = Vec::with_capacity_in(data.len(), LinearAllocator);
122 linear_data.extend_from_slice(data);
123
124 Some(Buffer {
125 data_ptr: linear_data.as_ptr() as _,
126 len: linear_data.len() / stride,
127 stride: stride as isize,
128 _data: Rc::new(linear_data),
129 })
130 }
131
132 pub fn new_in_linear<B: BufferData>(data: B) -> Buffer {
136 Buffer {
137 data_ptr: data.buf_ptr() as _,
138 stride: data
139 .stride()
140 .try_into()
141 .map_err(|_| format!("{}'s buffer elements are too large.", type_name::<B>()))
142 .unwrap(),
143 len: data.buf_len(),
144 _data: Rc::new(data),
145 }
146 }
147
148 pub fn new_in_linear_with_stride(data: impl BufferData, stride: usize) -> Option<Buffer> {
155 if !data.buf_len().is_multiple_of(stride) {
156 return None;
157 }
158
159 Some(Buffer {
160 data_ptr: data.buf_ptr() as _,
161 stride: stride
162 .try_into()
163 .map_err(|_| format!("{stride} is too large for a buffer element."))
164 .unwrap(),
165 len: data.buf_len() / stride,
166 _data: Rc::new(data),
167 })
168 }
169
170 pub fn as_ptr(&self) -> *const c_void {
171 self.data_ptr
172 }
173
174 pub fn stride(&self) -> isize {
175 self.stride
176 }
177
178 pub fn len(&self) -> usize {
179 self.len
180 }
181
182 pub fn is_empty(&self) -> bool {
183 self.len == 0
184 }
185}
186
187#[doc(alias = "C3D_BufInfo")]
190#[derive(Clone)]
191pub struct Info {
192 info: citro3d_sys::C3D_BufInfo,
193 buffers: Vec<Buffer>,
194}
195
196pub trait Index {
198 const TYPE: libc::c_int;
200}
201
202impl Index for u8 {
203 const TYPE: libc::c_int = citro3d_sys::C3D_UNSIGNED_BYTE as _;
204}
205
206impl Index for u16 {
207 const TYPE: libc::c_int = citro3d_sys::C3D_UNSIGNED_SHORT as _;
208}
209
210#[repr(u16)]
212#[derive(Debug, Clone, Copy)]
213#[doc(alias = "GPU_Primitive_t")]
214pub enum Primitive {
215 Triangles = ctru_sys::GPU_TRIANGLES,
217 TriangleStrip = ctru_sys::GPU_TRIANGLE_STRIP,
219 TriangleFan = ctru_sys::GPU_TRIANGLE_FAN,
221 GeometryPrim = ctru_sys::GPU_GEOMETRY_PRIM,
224}
225
226impl Default for Info {
227 #[doc(alias = "BufInfo_Init")]
228 fn default() -> Self {
229 let mut info = MaybeUninit::zeroed();
230 let info = unsafe {
231 citro3d_sys::BufInfo_Init(info.as_mut_ptr());
232 info.assume_init()
233 };
234 Self {
235 info,
236 buffers: Vec::new(),
237 }
238 }
239}
240
241impl Info {
242 pub fn as_raw(&self) -> *mut citro3d_sys::C3D_BufInfo {
243 &self.info as *const _ as _
244 }
245
246 pub fn new() -> Self {
248 Self::default()
249 }
250
251 pub fn len(&self) -> u16 {
252 self.buffers.first().map(|b| b.len() as _).unwrap_or(0)
253 }
254
255 pub fn is_empty(&self) -> bool {
256 self.len() == 0
257 }
258
259 #[doc(alias = "BufInfo_Add")]
270 pub fn add<'this, 'idx>(
271 &'this mut self,
272 vbo_buffer: Buffer,
273 permutation: attrib::Permutation,
274 ) -> Result<(), Error>
275 where
276 'this: 'idx,
277 {
278 let res = unsafe {
284 citro3d_sys::BufInfo_Add(
285 &mut self.info,
286 vbo_buffer.as_ptr().cast(),
287 vbo_buffer.stride(),
288 permutation.attrib_count as _,
289 permutation.permutation,
290 )
291 };
292
293 match res {
295 ..=-3 => Err(crate::Error::System(res)),
296 -2 => Err(crate::Error::InvalidMemoryLocation),
297 -1 => Err(crate::Error::TooManyBuffers),
298 _ => {
299 self.buffers.push(vbo_buffer);
300 Ok(())
301 }
302 }
303 }
304}