1use std::borrow::Cow;
8use std::error::Error;
9use std::ffi::CString;
10use std::mem::MaybeUninit;
11use std::rc::Rc;
12
13use crate::uniform;
14
15#[doc(alias = "shaderProgram_s")]
22#[must_use]
23pub struct Program {
24 program: ctru_sys::shaderProgram_s,
25 _vsh: Entrypoint,
26 gsh: Option<Entrypoint>,
27}
28
29impl Program {
30 #[doc(alias = "shaderProgramInit")]
38 #[doc(alias = "shaderProgramSetVsh")]
39 pub fn new(mut vertex_shader: Entrypoint) -> Result<Self, ctru::Error> {
40 let mut program = unsafe {
41 let mut program = MaybeUninit::uninit();
42 let result = ctru_sys::shaderProgramInit(program.as_mut_ptr());
43 if result != 0 {
44 return Err(ctru::Error::from(result));
45 }
46 program.assume_init()
47 };
48
49 let ret = unsafe { ctru_sys::shaderProgramSetVsh(&mut program, vertex_shader.as_raw()) };
50
51 if ret == 0 {
52 Ok(Self {
53 program,
54 _vsh: vertex_shader,
55 gsh: None,
56 })
57 } else {
58 Err(ctru::Error::from(ret))
59 }
60 }
61
62 #[doc(alias = "shaderProgramSetGsh")]
69 pub fn set_geometry_shader(
70 &mut self,
71 mut geometry_shader: Entrypoint,
72 stride: u8,
73 ) -> Result<(), ctru::Error> {
74 let ret = unsafe {
75 ctru_sys::shaderProgramSetGsh(&mut self.program, geometry_shader.as_raw(), stride)
76 };
77
78 if ret == 0 {
79 self.gsh = Some(geometry_shader);
80 Ok(())
81 } else {
82 Err(ctru::Error::from(ret))
83 }
84 }
85
86 #[doc(alias = "shaderInstanceGetUniformLocation")]
93 pub fn get_vertex_uniform(&self, name: &str) -> crate::Result<uniform::Index> {
94 let vertex_instance = unsafe { (*self.as_raw()).vertexShader };
95 assert!(
96 !vertex_instance.is_null(),
97 "vertex shader should never be null!"
98 );
99
100 let name = CString::new(name)?;
101
102 let idx =
103 unsafe { ctru_sys::shaderInstanceGetUniformLocation(vertex_instance, name.as_ptr()) };
104
105 if idx < 0 {
106 Err(crate::Error::NotFound)
107 } else {
108 Ok((idx as u8).into())
109 }
110 }
111
112 #[doc(alias = "shaderInstanceGetUniformLocation")]
120 pub fn get_geometry_uniform(&self, name: &str) -> crate::Result<uniform::Index> {
121 if self.gsh.is_none() {
122 return Err(crate::Error::MissingProgram);
123 }
124
125 let geometry_instance = unsafe { (*self.as_raw()).geometryShader };
126
127 let name = CString::new(name)?;
128
129 let idx =
130 unsafe { ctru_sys::shaderInstanceGetUniformLocation(geometry_instance, name.as_ptr()) };
131
132 if idx < 0 {
133 Err(crate::Error::NotFound)
134 } else {
135 Ok((idx as u8).into())
136 }
137 }
138
139 pub(crate) fn as_raw(&self) -> *const ctru_sys::shaderProgram_s {
140 &self.program
141 }
142}
143
144impl Drop for Program {
145 #[doc(alias = "shaderProgramFree")]
146 fn drop(&mut self) {
147 unsafe {
148 let _ = ctru_sys::shaderProgramFree(self.as_raw().cast_mut());
149 }
150 }
151}
152
153#[repr(u8)]
155#[derive(Clone, Copy)]
156pub enum Type {
157 Vertex = ctru_sys::GPU_VERTEX_SHADER,
159 Geometry = ctru_sys::GPU_GEOMETRY_SHADER,
161}
162
163impl From<Type> for u8 {
164 fn from(value: Type) -> Self {
165 value as u8
166 }
167}
168
169#[doc(alias = "DVLB_s")]
176pub struct Library {
177 dvlb: *mut ctru_sys::DVLB_s,
178 _bytes: Cow<'static, [u8]>,
179}
180
181impl Library {
182 #[doc(alias = "DVLB_ParseFile")]
189 pub fn from_bytes<B: Into<Cow<'static, [u8]>>>(bytes: B) -> Result<Self, Box<dyn Error>> {
190 let bytes = bytes.into();
191
192 let aligned: &[u32] = bytemuck::try_cast_slice(&bytes)?;
193 let dvlb = unsafe {
194 ctru_sys::DVLB_ParseFile(
195 aligned.as_ptr().cast_mut(),
199 aligned.len().try_into()?,
200 )
201 };
202
203 Ok(Self {
204 dvlb,
205 _bytes: bytes,
206 })
207 }
208
209 #[must_use]
211 #[doc(alias = "numDVLE")]
212 pub fn len(&self) -> usize {
213 unsafe { (*self.dvlb).numDVLE as usize }
214 }
215
216 #[must_use]
218 pub fn is_empty(&self) -> bool {
219 self.len() == 0
220 }
221
222 #[must_use]
224 pub fn get(self, index: usize) -> Option<Entrypoint> {
225 if index < self.len() {
226 Some(Entrypoint {
227 ptr: unsafe { (*self.dvlb).DVLE.add(index) },
228 _library: MaybeRc::Owned(self),
229 })
230 } else {
231 None
232 }
233 }
234
235 #[must_use]
236 pub fn get_shared(self: Rc<Self>, index: usize) -> Option<Entrypoint> {
241 if index < self.len() {
242 Some(Entrypoint {
243 ptr: unsafe { (*self.dvlb).DVLE.add(index) },
244 _library: MaybeRc::Shared(self),
245 })
246 } else {
247 None
248 }
249 }
250
251 fn as_raw(&mut self) -> *mut ctru_sys::DVLB_s {
252 self.dvlb
253 }
254}
255
256impl Drop for Library {
257 #[doc(alias = "DVLB_Free")]
258 fn drop(&mut self) {
259 unsafe {
260 ctru_sys::DVLB_Free(self.as_raw());
261 }
262 }
263}
264
265#[allow(dead_code)]
266enum MaybeRc<T> {
267 Owned(T),
268 Shared(Rc<T>),
269}
270
271pub struct Entrypoint {
274 ptr: *mut ctru_sys::DVLE_s,
275 _library: MaybeRc<Library>,
276}
277
278impl Entrypoint {
279 fn as_raw(&mut self) -> *mut ctru_sys::DVLE_s {
280 self.ptr
281 }
282}