use std::{ffi::CString, ptr};
use ogl33::{
consts::{GL_FALSE, GL_FRAGMENT_SHADER, GL_INFO_LOG_LENGTH, GL_LINK_STATUS, GL_VERTEX_SHADER},
functions::{
glAttachShader, glCreateProgram, glDeleteProgram, glDetachShader, glGetProgramInfoLog, glGetProgramiv,
glLinkProgram, glUseProgram,
},
types::{GLchar, GLint, GLuint},
};
use crate::Error;
use super::{util, Shader};
pub struct Program {
id: GLuint,
}
impl Program {
pub(crate) fn link(shaders: &[Shader]) -> crate::Result<Self> {
let id: GLuint = unsafe { glCreateProgram() };
for shader in shaders {
unsafe {
glAttachShader(id, shader.id());
}
}
unsafe {
glLinkProgram(id);
}
for shader in shaders {
unsafe {
glDetachShader(id, shader.id());
}
}
let mut status: GLint = GL_FALSE as GLint;
unsafe {
glGetProgramiv(id, GL_LINK_STATUS, &mut status);
}
if status == GL_FALSE as GLint {
let mut len: GLint = 0;
unsafe {
glGetProgramiv(id, GL_INFO_LOG_LENGTH, &mut len);
}
let info = unsafe {
let info: *mut GLchar = util::make_gl_str(len);
glGetProgramInfoLog(id, len, ptr::null_mut(), info);
CString::from_raw(info)
};
unsafe {
glDeleteProgram(id);
}
Err(Error::LinkProgram(info.to_string_lossy().into_owned()))
} else {
unsafe {
glUseProgram(id);
}
Ok(Self { id })
}
}
}
impl Default for Program {
fn default() -> Self {
let vs_src = CString::new(include_str!("src/default.vs"))
.expect("Default vertex shader source code is not a valid C string");
let vs_bin = Shader::compile(&vs_src, GL_VERTEX_SHADER).expect("Could not compile default vertex shader");
let fs_src = CString::new(include_str!("src/default.fs"))
.expect("Default fragment shader source code is not a valid C string");
let fs_bin = Shader::compile(&fs_src, GL_FRAGMENT_SHADER).expect("Could not compile default fragment shader");
Self::link(&[vs_bin, fs_bin]).expect("Could not link default program")
}
}
impl Drop for Program {
fn drop(&mut self) {
unsafe {
glDeleteProgram(self.id);
}
}
}