diff options
Diffstat (limited to 'opengl-bindings/src/shader.rs')
-rw-r--r-- | opengl-bindings/src/shader.rs | 366 |
1 files changed, 366 insertions, 0 deletions
diff --git a/opengl-bindings/src/shader.rs b/opengl-bindings/src/shader.rs new file mode 100644 index 0000000..5ed66a2 --- /dev/null +++ b/opengl-bindings/src/shader.rs @@ -0,0 +1,366 @@ +use std::ffi::CStr; +use std::ptr::null_mut; + +use safer_ffi::layout::ReprC; + +use crate::data_types::{Matrix, Vec3}; +use crate::CurrentContextWithFns; + +#[derive(Debug)] +pub struct Shader +{ + shader: crate::sys::types::GLuint, +} + +impl Shader +{ + #[must_use] + pub fn new(current_context: &CurrentContextWithFns<'_>, kind: Kind) -> Self + { + let shader = unsafe { + current_context + .fns() + .CreateShader(kind as crate::sys::types::GLenum) + }; + + Self { shader } + } + + /// Sets the source code of this shader. + /// + /// # Errors + /// Returns `Err` if `source` is not ASCII. + pub fn set_source( + &self, + current_context: &CurrentContextWithFns<'_>, + source: &str, + ) -> Result<(), Error> + { + if !source.is_ascii() { + return Err(Error::SourceNotAscii); + } + + let length: crate::sys::types::GLint = + source.len().try_into().map_err(|_| Error::SourceTooLarge { + length: source.len(), + max_length: crate::sys::types::GLint::MAX as usize, + })?; + + unsafe { + current_context.fns().ShaderSource( + self.shader, + 1, + &source.as_ptr().cast(), + &raw const length, + ); + } + + Ok(()) + } + + /// Compiles this shader. + /// + /// # Errors + /// Returns `Err` if compiling fails. + pub fn compile( + &self, + current_context: &CurrentContextWithFns<'_>, + ) -> Result<(), Error> + { + unsafe { + current_context.fns().CompileShader(self.shader); + } + + let mut compile_success = crate::sys::types::GLint::default(); + + unsafe { + current_context.fns().GetShaderiv( + self.shader, + crate::sys::COMPILE_STATUS, + &raw mut compile_success, + ); + } + + if compile_success == 0 { + let info_log = self.get_info_log(current_context); + + return Err(Error::CompileFailed { log: info_log }); + } + + Ok(()) + } + + pub fn delete(self, current_context: &CurrentContextWithFns<'_>) + { + unsafe { + current_context.fns().DeleteShader(self.shader); + } + } + + fn get_info_log(&self, current_context: &CurrentContextWithFns<'_>) -> String + { + const BUF_SIZE: crate::sys::types::GLsizei = 512; + + let mut buf = vec![crate::sys::types::GLchar::default(); BUF_SIZE as usize]; + + unsafe { + current_context.fns().GetShaderInfoLog( + self.shader, + BUF_SIZE, + null_mut(), + buf.as_mut_ptr(), + ); + } + + let info_log = unsafe { CStr::from_ptr(buf.as_ptr()) }; + + unsafe { String::from_utf8_unchecked(info_log.to_bytes().to_vec()) } + } +} + +/// Shader kind. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[repr(u32)] +pub enum Kind +{ + Vertex = crate::sys::VERTEX_SHADER, + Fragment = crate::sys::FRAGMENT_SHADER, +} + +/// Shader program +#[derive(Debug, PartialEq, Eq, Hash)] +pub struct Program +{ + program: crate::sys::types::GLuint, +} + +impl Program +{ + #[must_use] + pub fn new(current_context: &CurrentContextWithFns<'_>) -> Self + { + let program = unsafe { current_context.fns().CreateProgram() }; + + Self { program } + } + + pub fn attach(&self, current_context: &CurrentContextWithFns<'_>, shader: &Shader) + { + unsafe { + current_context + .fns() + .AttachShader(self.program, shader.shader); + } + } + + /// Links this program. + /// + /// # Errors + /// Returns `Err` if linking fails. + pub fn link(&self, current_context: &CurrentContextWithFns<'_>) -> Result<(), Error> + { + unsafe { + current_context.fns().LinkProgram(self.program); + } + + let mut link_success = crate::sys::types::GLint::default(); + + unsafe { + current_context.fns().GetProgramiv( + self.program, + crate::sys::LINK_STATUS, + &raw mut link_success, + ); + } + + if link_success == 0 { + let info_log = self.get_info_log(current_context); + + return Err(Error::LinkFailed { log: info_log }); + } + + Ok(()) + } + + pub fn activate(&self, current_context: &CurrentContextWithFns<'_>) + { + unsafe { + current_context.fns().UseProgram(self.program); + } + } + + pub fn set_uniform( + &self, + current_context: &CurrentContextWithFns<'_>, + name: &CStr, + var: &impl UniformVariable, + ) + { + let location = UniformLocation(unsafe { + current_context + .fns() + .GetUniformLocation(self.program, name.as_ptr().cast()) + }); + + var.set(current_context, self, location); + } + + pub fn delete(self, current_context: &CurrentContextWithFns<'_>) + { + unsafe { + current_context.fns().DeleteProgram(self.program); + } + } + + fn get_info_log(&self, current_context: &CurrentContextWithFns<'_>) -> String + { + const BUF_SIZE: crate::sys::types::GLsizei = 512; + + let mut buf = vec![crate::sys::types::GLchar::default(); BUF_SIZE as usize]; + + unsafe { + current_context.fns().GetProgramInfoLog( + self.program, + BUF_SIZE, + null_mut(), + buf.as_mut_ptr(), + ); + } + + let info_log = unsafe { CStr::from_ptr(buf.as_ptr()) }; + + unsafe { String::from_utf8_unchecked(info_log.to_bytes().to_vec()) } + } +} + +pub trait UniformVariable: ReprC + sealed::Sealed +{ + fn set( + &self, + current_context: &CurrentContextWithFns<'_>, + program: &Program, + uniform_location: UniformLocation, + ); +} + +impl UniformVariable for f32 +{ + fn set( + &self, + current_context: &CurrentContextWithFns<'_>, + program: &Program, + uniform_location: UniformLocation, + ) + { + unsafe { + current_context.fns().ProgramUniform1f( + program.program, + uniform_location.0, + *self, + ); + } + } +} + +impl sealed::Sealed for f32 {} + +impl UniformVariable for i32 +{ + fn set( + &self, + current_context: &CurrentContextWithFns<'_>, + program: &Program, + uniform_location: UniformLocation, + ) + { + unsafe { + current_context.fns().ProgramUniform1i( + program.program, + uniform_location.0, + *self, + ); + } + } +} + +impl sealed::Sealed for i32 {} + +impl UniformVariable for Vec3<f32> +{ + fn set( + &self, + current_context: &CurrentContextWithFns<'_>, + program: &Program, + uniform_location: UniformLocation, + ) + { + unsafe { + current_context.fns().ProgramUniform3f( + program.program, + uniform_location.0, + self.x, + self.y, + self.z, + ); + } + } +} + +impl sealed::Sealed for Vec3<f32> {} + +impl UniformVariable for Matrix<f32, 4, 4> +{ + fn set( + &self, + current_context: &CurrentContextWithFns<'_>, + program: &Program, + uniform_location: UniformLocation, + ) + { + unsafe { + current_context.fns().ProgramUniformMatrix4fv( + program.program, + uniform_location.0, + 1, + crate::sys::FALSE, + self.items.as_ptr().cast::<f32>(), + ); + } + } +} + +impl sealed::Sealed for Matrix<f32, 4, 4> {} + +#[derive(Debug)] +pub struct UniformLocation(crate::sys::types::GLint); + +/// Shader error. +#[derive(Debug, thiserror::Error)] +pub enum Error +{ + #[error("All characters in source are not within the ASCII range")] + SourceNotAscii, + + #[error("Source is too large. Length ({length}) must be < {max_length}")] + SourceTooLarge + { + length: usize, max_length: usize + }, + + #[error("Failed to compile shader")] + CompileFailed + { + log: String + }, + + #[error("Failed to link shader program")] + LinkFailed + { + log: String + }, +} + +mod sealed +{ + pub trait Sealed {} +} |