From dcab5508f95537e92ce87a372e5e58b6a0d97643 Mon Sep 17 00:00:00 2001 From: Ian Chamberlain Date: Thu, 5 Oct 2023 21:53:52 -0400 Subject: [PATCH] Use a simple trait for binding uniforms It's not much but it should be extensible enough to apply for other uniform types. We might want a generic impl for &[u8] or something as well to support custom uniform types, but that gets trickier. --- citro3d/examples/triangle.rs | 4 ++-- citro3d/src/lib.rs | 20 +++++++++----------- citro3d/src/shader.rs | 30 +++++++++++++++++++++++------- citro3d/src/uniform.rs | 33 +++++++++++++++++++++++++++++++++ 4 files changed, 67 insertions(+), 20 deletions(-) create mode 100644 citro3d/src/uniform.rs diff --git a/citro3d/examples/triangle.rs b/citro3d/examples/triangle.rs index 61d4620..4009f5b 100644 --- a/citro3d/examples/triangle.rs +++ b/citro3d/examples/triangle.rs @@ -91,7 +91,7 @@ fn main() { scene_init(&mut program); - let projection_uniform_idx = program.get_uniform_location("projection").unwrap(); + let projection_uniform_idx = program.get_uniform("projection").unwrap(); while apt.main_loop() { hid.scan_input(); @@ -109,7 +109,7 @@ fn main() { let clear_color: u32 = 0x7F_7F_7F_FF; target.clear(ClearFlags::ALL, clear_color, 0); - instance.update_vertex_uniform_mat4x4(projection_uniform_idx, projection); + instance.bind_vertex_uniform(projection_uniform_idx, projection); instance.set_attr_info(&attr_info); diff --git a/citro3d/src/lib.rs b/citro3d/src/lib.rs index 68a25ae..53c1350 100644 --- a/citro3d/src/lib.rs +++ b/citro3d/src/lib.rs @@ -8,10 +8,13 @@ pub mod error; pub mod math; pub mod render; pub mod shader; +pub mod uniform; pub use error::{Error, Result}; pub use math::Matrix; +use self::uniform::Uniform; + pub mod macros { //! Helper macros for working with shaders. pub use citro3d_macros::*; @@ -123,17 +126,12 @@ impl Instance { } } - // TODO: need separate versions for vertex/geometry and different dimensions? - // Maybe we could do something nicer with const generics, or something, although - // it will probably be tricker - pub fn update_vertex_uniform_mat4x4(&mut self, index: i8, matrix: &Matrix) { - unsafe { - citro3d_sys::C3D_FVUnifMtx4x4( - ctru_sys::GPU_VERTEX_SHADER, - index.into(), - matrix.as_raw(), - ) - } + pub fn bind_vertex_uniform(&mut self, index: uniform::Index, uniform: &impl Uniform) { + uniform.bind(self, shader::Type::Vertex, index); + } + + pub fn bind_geometry_uniform(&mut self, index: uniform::Index, uniform: &impl Uniform) { + uniform.bind(self, shader::Type::Geometry, index); } } diff --git a/citro3d/src/shader.rs b/citro3d/src/shader.rs index 158fbb5..9d775f8 100644 --- a/citro3d/src/shader.rs +++ b/citro3d/src/shader.rs @@ -8,10 +8,12 @@ use std::error::Error; use std::ffi::CString; use std::mem::MaybeUninit; +use crate::uniform; + /// A PICA200 shader program. It may have one or both of: /// -/// * A vertex shader [`Library`] -/// * A geometry shader [`Library`] +/// * A [vertex](Type::Vertex) shader [`Library`] +/// * A [geometry](Type::Geometry) shader [`Library`] /// /// The PICA200 does not support user-programmable fragment shaders. pub struct Program { @@ -73,7 +75,7 @@ impl Program { /// /// * If the given `name` contains a null byte /// * If a uniform with the given `name` could not be found - pub fn get_uniform_location(&self, name: &str) -> crate::Result { + pub fn get_uniform(&self, name: &str) -> crate::Result { let vertex_instance = unsafe { (*self.as_raw()).vertexShader }; assert!( !vertex_instance.is_null(), @@ -88,14 +90,15 @@ impl Program { if idx < 0 { Err(crate::Error::NotFound) } else { - Ok(idx) + Ok(idx.into()) } } - // TODO: pub(crate) - pub fn as_raw(&self) -> *const ctru_sys::shaderProgram_s { + + pub(crate) fn as_raw(&self) -> *const ctru_sys::shaderProgram_s { &self.program } + // TODO: pub(crate) pub fn as_raw_mut(&mut self) -> *mut ctru_sys::shaderProgram_s { &mut self.program } @@ -109,6 +112,19 @@ impl Drop for Program { } } +/// The type of a shader. +#[repr(u32)] +pub enum Type { + Vertex = ctru_sys::GPU_VERTEX_SHADER, + Geometry = ctru_sys::GPU_GEOMETRY_SHADER, +} + +impl From for u32 { + fn from(value: Type) -> Self { + value as u32 + } +} + /// A PICA200 Shader Library (commonly called DVLB). This can be comprised of /// one or more [`Entrypoint`]s, but most commonly has one vertex shader and an /// optional geometry shader. @@ -131,7 +147,7 @@ impl Library { // SAFETY: we're trusting the parse implementation doesn't mutate // the contents of the data. From a quick read it looks like that's // correct and it should just take a const arg in the API. - aligned.as_ptr() as *mut _, + aligned.as_ptr().cast_mut(), aligned.len().try_into()?, ) })) diff --git a/citro3d/src/uniform.rs b/citro3d/src/uniform.rs new file mode 100644 index 0000000..7033a22 --- /dev/null +++ b/citro3d/src/uniform.rs @@ -0,0 +1,33 @@ +use crate::{shader, Instance, Matrix}; + +#[derive(Copy, Clone, Debug)] +pub struct Index(i8); + +impl From for Index { + fn from(value: i8) -> Self { + Self(value) + } +} + +impl From for i32 { + fn from(value: Index) -> Self { + value.0.into() + } +} + +mod private { + use crate::Matrix; + + pub trait Sealed {} + impl Sealed for Matrix {} +} + +pub trait Uniform: private::Sealed { + fn bind(&self, instance: &mut Instance, shader_type: shader::Type, index: Index); +} + +impl Uniform for Matrix { + fn bind(&self, _instance: &mut Instance, type_: shader::Type, index: Index) { + unsafe { citro3d_sys::C3D_FVUnifMtx4x4(type_.into(), index.into(), self.as_raw()) } + } +}