glenda 0.2.0

An opengl utility and renderer library.
Documentation
use gl;
use gl::types::*;

type Error = Box<dyn std::error::Error>;

pub fn compile_shader(src: &str, ty: u32) -> Result<u32, Error> {
    let ty_str = detail::shader_type_as_str(ty)
        .ok_or(Error::from("Invalid shader type"))?;

    unsafe {
        let shader = gl::CreateShader(ty);
        detail::shader_source(shader, src);

        gl::CompileShader(shader);

        let mut success = 0;
        gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut success);
        if success == 0 {
            let mut len = 0;
            gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len);
            let mut buffer = vec![0; len as usize];
            gl::GetShaderInfoLog(shader, len, std::ptr::null_mut(), buffer.as_mut_ptr() as _);
            let log = std::str::from_utf8(&buffer).unwrap();
            let msg = format!("Failed to compile {} shader: {}", ty_str, log);

            gl::DeleteShader(shader);
            Err(msg.into())
        } else {
            Ok(shader)
        }
    }
}

pub fn link_shader_program(shaders: &[u32]) -> Result<u32, Error> {
    unsafe {
        let program = gl::CreateProgram();
        for &shader in shaders {
            gl::AttachShader(program, shader);
        }

        gl::LinkProgram(program);

        for &shader in shaders {
            gl::DetachShader(program, shader);
        }

        let mut success = 0;
        gl::GetProgramiv(program, gl::LINK_STATUS, &mut success);
        if success == 0 {
            let mut len = 0;
            gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut len);
            let mut buffer = vec![0; len as usize];
            gl::GetProgramInfoLog(program, len, std::ptr::null_mut(), buffer.as_mut_ptr() as _);
            let log = std::str::from_utf8(&buffer).unwrap();
            let msg = format!("Failed to link shader program: {}", log);

            gl::DeleteProgram(program);
            Err(msg.into())
        } else {
            Ok(program)
        }
    }
}

pub struct ShaderProgramBuilder {
    shaders: Vec<detail::CompiledShader>,
}

impl ShaderProgramBuilder {
    pub fn new() -> Self {
        Self {
            shaders: Vec::new(),
        }
    }

    fn add_shader(&mut self, src: &str, shader_type: GLenum) -> Result<(), Error> {
        let shader = detail::CompiledShader::new(src, shader_type)?;
        self.shaders.push(shader);
        Ok(())
    }

    pub fn add_vertex_shader(&mut self, src: &str) -> Result<(), Error> {
        self.add_shader(src, gl::VERTEX_SHADER)
    }

    pub fn add_fragment_shader(&mut self, src: &str) -> Result<(), Error> {
        self.add_shader(src, gl::FRAGMENT_SHADER)
    }

    pub fn add_geometry_shader(&mut self, src: &str) -> Result<(), Error> {
        self.add_shader(src, gl::GEOMETRY_SHADER)
    }

    pub fn add_tess_control_shader(&mut self, src: &str) -> Result<(), Error> {
        self.add_shader(src, gl::TESS_CONTROL_SHADER)
    }

    pub fn add_tess_evaluation_shader(&mut self, src: &str) -> Result<(), Error> {
        self.add_shader(src, gl::TESS_EVALUATION_SHADER)
    }

    pub fn add_compute_shader(&mut self, src: &str) -> Result<(), Error> {
        self.add_shader(src, gl::COMPUTE_SHADER)
    }

    pub fn build(self) -> Result<GLuint, Error> {
        let shader_ids: Vec<_> = self.shaders.iter().map(|s| s.0).collect();
        let program = link_shader_program(&shader_ids)?;
        Ok(program)
    }
}

mod detail {
    use super::*;

    pub fn shader_source(shader: u32, src: &str) {
        let src = std::ffi::CString::new(src).unwrap();
        let src_ptr = src.as_ptr();
    
        unsafe {
            gl::ShaderSource(shader, 1, &src_ptr, std::ptr::null());
        }
    }
    
    pub fn shader_type_as_str(ty: u32) -> Option<&'static str> {
        match ty {
            gl::VERTEX_SHADER => Some("vertex"),
            gl::TESS_CONTROL_SHADER => Some("tess control"),
            gl::TESS_EVALUATION_SHADER => Some("tess evaluation"),
            gl::GEOMETRY_SHADER => Some("geometry"),
            gl::FRAGMENT_SHADER => Some("fragment"),
            gl::COMPUTE_SHADER => Some("compute"),
            _ => None,
        }
    }
    pub struct CompiledShader(pub GLuint);

    impl CompiledShader {
        pub fn new(src: &str, shader_type: GLenum) -> Result<Self, Error> {
            let shader = compile_shader(src, shader_type)?;
            Ok(Self(shader))
        }
    }

    impl Drop for CompiledShader {
        fn drop(&mut self) {
            unsafe {
                gl::DeleteShader(self.0);
            }
        }
    }
}