|
|
|
@@ -0,0 +1,237 @@ |
|
|
|
use std::fs::read_to_string; |
|
|
|
use std::iter::FromIterator; |
|
|
|
use std::path::Path; |
|
|
|
use std::ptr::{null, null_mut}; |
|
|
|
use std::str::from_utf8; |
|
|
|
|
|
|
|
use crate::error::Error; |
|
|
|
|
|
|
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)] |
|
|
|
pub enum Type { |
|
|
|
Vertex, |
|
|
|
Compute, |
|
|
|
Fragment, |
|
|
|
Geometry, |
|
|
|
TesslationControl, |
|
|
|
TesslationEvaluation, |
|
|
|
} |
|
|
|
|
|
|
|
/* Shader */ |
|
|
|
|
|
|
|
pub struct Shader { |
|
|
|
id: Option<gl::GLuint>, |
|
|
|
type_: Type, |
|
|
|
source: String, |
|
|
|
compiled: bool, |
|
|
|
} |
|
|
|
|
|
|
|
impl Shader { |
|
|
|
pub fn from_string(type_: Type, source: String) -> Self { |
|
|
|
Self { |
|
|
|
id: None, |
|
|
|
type_, |
|
|
|
source, |
|
|
|
compiled: false, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
pub fn from_file<P>(type_: Type, path: P) -> Result<Self, Error> |
|
|
|
where |
|
|
|
P: AsRef<Path>, |
|
|
|
{ |
|
|
|
let source = read_to_string(path)?; |
|
|
|
|
|
|
|
Ok(Self::from_string(type_, source)) |
|
|
|
} |
|
|
|
|
|
|
|
pub fn id(&mut self) -> Result<gl::GLuint, Error> { |
|
|
|
if self.id.is_none() { |
|
|
|
let type_ = match self.type_ { |
|
|
|
Type::Vertex => gl::VERTEX_SHADER, |
|
|
|
Type::Compute => gl::COMPUTE_SHADER, |
|
|
|
Type::Fragment => gl::FRAGMENT_SHADER, |
|
|
|
Type::Geometry => gl::GEOMETRY_SHADER, |
|
|
|
Type::TesslationControl => gl::TESS_CONTROL_SHADER, |
|
|
|
Type::TesslationEvaluation => gl::TESS_EVALUATION_SHADER, |
|
|
|
}; |
|
|
|
|
|
|
|
let id = gl::create_shader(type_); |
|
|
|
let id = Error::err_if(&0, id)?; |
|
|
|
self.id = Some(id) |
|
|
|
} |
|
|
|
|
|
|
|
Ok(self.id.unwrap()) |
|
|
|
} |
|
|
|
|
|
|
|
pub fn id_opt(&self) -> Option<gl::GLuint> { |
|
|
|
self.id |
|
|
|
} |
|
|
|
|
|
|
|
pub fn type_(&self) -> Type { |
|
|
|
self.type_ |
|
|
|
} |
|
|
|
|
|
|
|
pub fn compile(&mut self) -> Result<(), Error> { |
|
|
|
if self.compiled { |
|
|
|
return Ok(()); |
|
|
|
} |
|
|
|
|
|
|
|
let id = self.id()?; |
|
|
|
let source = self.source.as_ptr() as *const i8; |
|
|
|
let source: *const *const i8 = &source; |
|
|
|
|
|
|
|
gl::shader_source(id, 1, source, null()); |
|
|
|
gl::compile_shader(id); |
|
|
|
|
|
|
|
let mut success = 1; |
|
|
|
gl::get_shader_iv(id, gl::COMPILE_STATUS, &mut success); |
|
|
|
|
|
|
|
if success != 1 { |
|
|
|
let mut len = 0; |
|
|
|
gl::get_shader_iv(id, gl::INFO_LOG_LENGTH, &mut len); |
|
|
|
|
|
|
|
let mut buffer = Vec::<u8>::with_capacity(len as usize + 1); |
|
|
|
buffer.resize(len as usize, 0); |
|
|
|
|
|
|
|
gl::get_shader_info_log( |
|
|
|
id, |
|
|
|
len, |
|
|
|
null_mut(), |
|
|
|
buffer.as_mut_ptr() as *mut gl::types::GLchar, |
|
|
|
); |
|
|
|
|
|
|
|
let msg = from_utf8(&buffer)?; |
|
|
|
|
|
|
|
return Err(Error::ShaderCompile(msg.into())); |
|
|
|
} |
|
|
|
|
|
|
|
self.compiled = true; |
|
|
|
|
|
|
|
Ok(()) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
impl Drop for Shader { |
|
|
|
fn drop(&mut self) { |
|
|
|
if let Some(id) = &self.id { |
|
|
|
gl::delete_shader(*id); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/* Programm */ |
|
|
|
|
|
|
|
#[derive(Default)] |
|
|
|
pub struct Program { |
|
|
|
id: Option<gl::GLuint>, |
|
|
|
shaders: Vec<Shader>, |
|
|
|
compiled: bool, |
|
|
|
} |
|
|
|
|
|
|
|
impl Program { |
|
|
|
pub fn id(&mut self) -> Result<gl::GLuint, Error> { |
|
|
|
if self.id.is_none() { |
|
|
|
let id = gl::create_program(); |
|
|
|
let id = Error::err_if(&0, id)?; |
|
|
|
self.id = Some(id) |
|
|
|
} |
|
|
|
|
|
|
|
Ok(self.id.unwrap()) |
|
|
|
} |
|
|
|
|
|
|
|
pub fn id_opt(&self) -> Option<gl::GLuint> { |
|
|
|
self.id |
|
|
|
} |
|
|
|
|
|
|
|
pub fn bind(&self) { |
|
|
|
if let Some(id) = &self.id { |
|
|
|
gl::use_program(*id); |
|
|
|
} else { |
|
|
|
gl::use_program(0); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
pub fn unbind(&self) { |
|
|
|
gl::use_program(0); |
|
|
|
} |
|
|
|
|
|
|
|
pub fn link(&mut self, keep_shaders: bool) -> Result<(), Error> { |
|
|
|
if self.compiled { |
|
|
|
return Ok(()); |
|
|
|
} |
|
|
|
|
|
|
|
let id = self.id()?; |
|
|
|
|
|
|
|
for shader in &mut self.shaders { |
|
|
|
shader.compile()?; |
|
|
|
} |
|
|
|
|
|
|
|
for shader in &mut self.shaders { |
|
|
|
gl::attach_shader(id, shader.id()?); |
|
|
|
} |
|
|
|
|
|
|
|
let mut success = 1; |
|
|
|
gl::get_program_iv(id, gl::LINK_STATUS, &mut success); |
|
|
|
|
|
|
|
if success != 1 { |
|
|
|
let mut len = 0; |
|
|
|
gl::get_program_iv(id, gl::INFO_LOG_LENGTH, &mut len); |
|
|
|
|
|
|
|
let mut buffer = Vec::<u8>::with_capacity(len as usize + 1); |
|
|
|
buffer.resize(len as usize, 0); |
|
|
|
|
|
|
|
gl::get_program_info_log( |
|
|
|
id, |
|
|
|
len, |
|
|
|
null_mut(), |
|
|
|
buffer.as_mut_ptr() as *mut gl::types::GLchar, |
|
|
|
); |
|
|
|
|
|
|
|
let msg = from_utf8(&buffer)?; |
|
|
|
|
|
|
|
return Err(Error::ShaderLink(msg.into())); |
|
|
|
} |
|
|
|
|
|
|
|
for shader in &mut self.shaders { |
|
|
|
gl::detach_shader(id, shader.id()?); |
|
|
|
} |
|
|
|
|
|
|
|
if !keep_shaders { |
|
|
|
self.shaders.clear(); |
|
|
|
} |
|
|
|
|
|
|
|
self.compiled = true; |
|
|
|
|
|
|
|
Ok(()) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
impl Extend<Shader> for Program { |
|
|
|
fn extend<I>(&mut self, iter: I) |
|
|
|
where |
|
|
|
I: IntoIterator<Item = Shader>, |
|
|
|
{ |
|
|
|
self.shaders.extend(iter); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
impl FromIterator<Shader> for Program { |
|
|
|
fn from_iter<I>(iter: I) -> Self |
|
|
|
where |
|
|
|
I: IntoIterator<Item = Shader>, |
|
|
|
{ |
|
|
|
Self { |
|
|
|
id: None, |
|
|
|
shaders: iter.into_iter().collect(), |
|
|
|
compiled: false, |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
impl Drop for Program { |
|
|
|
fn drop(&mut self) { |
|
|
|
if let Some(id) = &self.id { |
|
|
|
gl::delete_program(*id); |
|
|
|
} |
|
|
|
} |
|
|
|
} |