You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
145 lines
3.9 KiB
Rust
145 lines
3.9 KiB
Rust
use crate::err::Error;
|
|
use crate::GlContext;
|
|
use std::ffi::CString;
|
|
use std::ops::{Deref, DerefMut};
|
|
use std::os::raw::c_char;
|
|
use std::ptr::null;
|
|
use std::str::from_utf8;
|
|
|
|
pub trait ShaderData {
|
|
fn init(&mut self, gl: &mut GlContext, program_id: u32);
|
|
fn apply(&self, gl: &GlContext, program_id: u32);
|
|
}
|
|
|
|
impl ShaderData for () {
|
|
fn init(&mut self, _gl: &mut GlContext, _program_id: u32) {}
|
|
fn apply(&self, _gl: &GlContext, _program_id: u32) {}
|
|
}
|
|
|
|
pub struct ShaderProgram<D: ShaderData> {
|
|
pub program_id: u32,
|
|
data: D,
|
|
}
|
|
|
|
impl<D: ShaderData> ShaderProgram<D> {
|
|
pub fn init(&mut self, gl: &mut GlContext) {
|
|
self.data.init(gl, self.program_id);
|
|
}
|
|
|
|
pub fn apply(&self, gl: &GlContext) {
|
|
self.data.apply(gl, self.program_id);
|
|
}
|
|
}
|
|
|
|
impl<D: ShaderData> Deref for ShaderProgram<D> {
|
|
type Target = D;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.data
|
|
}
|
|
}
|
|
|
|
impl<D: ShaderData> DerefMut for ShaderProgram<D> {
|
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
&mut self.data
|
|
}
|
|
}
|
|
|
|
pub struct Shader(pub u32);
|
|
|
|
impl Drop for Shader {
|
|
fn drop(&mut self) {
|
|
unsafe { gl::DeleteShader(self.0) }
|
|
}
|
|
}
|
|
|
|
#[repr(u32)]
|
|
pub enum ShaderType {
|
|
Vertex = gl::VERTEX_SHADER,
|
|
Fragment = gl::FRAGMENT_SHADER,
|
|
}
|
|
|
|
impl Shader {
|
|
pub fn compile(shader_type: ShaderType, source: &str) -> Result<Shader, Error> {
|
|
let shader_id = unsafe { gl::CreateShader(shader_type as u32) };
|
|
let source = CString::new(source.as_bytes())?;
|
|
unsafe {
|
|
gl::ShaderSource(shader_id, 1, &source.as_ptr(), null());
|
|
gl::CompileShader(shader_id);
|
|
let mut success = 0;
|
|
gl::GetShaderiv(shader_id, gl::COMPILE_STATUS, &mut success);
|
|
|
|
if success != 1 {
|
|
let mut log_length = 0;
|
|
gl::GetShaderiv(shader_id, gl::INFO_LOG_LENGTH, &mut log_length);
|
|
let mut log = Vec::with_capacity(log_length as usize);
|
|
let mut len = 0;
|
|
gl::GetShaderInfoLog(
|
|
shader_id,
|
|
log_length,
|
|
&mut len,
|
|
log.as_mut_ptr() as *mut c_char,
|
|
);
|
|
log.set_len(len as usize);
|
|
|
|
return Err(Error::ShaderCompilationFailure(
|
|
from_utf8(&log)?.to_string(),
|
|
));
|
|
}
|
|
}
|
|
|
|
Ok(Shader(shader_id))
|
|
}
|
|
}
|
|
|
|
impl ShaderProgram<()> {
|
|
pub fn create(shader0: Shader, shader1: Shader) -> Result<ShaderProgram<()>, Error> {
|
|
ShaderProgram::create_with_data((), shader0, shader1)
|
|
}
|
|
}
|
|
|
|
impl<D: ShaderData> ShaderProgram<D> {
|
|
pub fn enable(&self) {
|
|
unsafe { gl::UseProgram(self.program_id) }
|
|
}
|
|
|
|
pub fn create_with_data(
|
|
data: D,
|
|
shader0: Shader,
|
|
shader1: Shader,
|
|
) -> Result<ShaderProgram<D>, Error> {
|
|
unsafe {
|
|
let program_id = gl::CreateProgram();
|
|
gl::AttachShader(program_id, shader0.0);
|
|
gl::AttachShader(program_id, shader1.0);
|
|
gl::LinkProgram(program_id);
|
|
|
|
let mut status = 0;
|
|
gl::GetProgramiv(program_id, gl::LINK_STATUS, &mut status);
|
|
if status != 1 {
|
|
let mut log_length = 0;
|
|
gl::GetProgramiv(program_id, gl::INFO_LOG_LENGTH, &mut log_length);
|
|
let mut log = Vec::with_capacity(log_length as usize);
|
|
let mut len = 0;
|
|
gl::GetProgramInfoLog(
|
|
program_id,
|
|
log_length,
|
|
&mut len,
|
|
log.as_mut_ptr() as *mut c_char,
|
|
);
|
|
|
|
log.set_len(len as usize);
|
|
|
|
return Err(Error::ShaderProgramLinkFailure(
|
|
from_utf8(&log)?.to_string(),
|
|
));
|
|
}
|
|
|
|
gl::DeleteShader(shader0.0);
|
|
gl::DeleteShader(shader1.0);
|
|
|
|
Ok(ShaderProgram { data, program_id })
|
|
}
|
|
}
|
|
}
|