use std::marker::PhantomData;
use std::ptr;
use gl;
use gl::types::*;
use context::{AContext, BaseContext};
use types::{GLObject, GLError};
pub struct Shader {
gl_id: GLuint,
_phantom: PhantomData<*mut ()>
}
impl Drop for Shader {
fn drop(&mut self) {
unsafe {
gl::DeleteShader(self.gl_id);
}
}
}
impl GLObject for Shader {
type Id = GLuint;
unsafe fn from_raw(id: Self::Id) -> Self {
Shader {
gl_id: id,
_phantom: PhantomData
}
}
fn id(&self) -> Self::Id {
self.gl_id
}
}
unsafe fn _get_shader_iv(shader: &Shader,
pname: GLenum,
params: *mut GLint)
{
gl::GetShaderiv(shader.id(), pname, params);
dbg_gl_sanity_check! {
GLError::InvalidEnum => "`pname` is not an accepted value",
GLError::InvalidValue => "`shader` is not a value generated by OpenGL",
GLError::InvalidOperation => "`shader` is not a shader object, or `pname` is GL_COMPILE_STATUS, GL_INFO_LOG_LENGTH or GL_SHADER_SOURCE_LENGTH but a shader compiler is not supported",
_ => "Unknown error"
}
}
pub struct ShaderBuilder<'a, C: 'a>
where C: AContext
{
gl: &'a C,
ty: ShaderType,
source: &'a str
}
impl<'a, C: 'a> ShaderBuilder<'a, C>
where C: AContext
{
fn new(gl: &'a C, ty: ShaderType, source: &'a str)
-> Self
{
ShaderBuilder { gl: gl, ty: ty, source: source }
}
pub fn try_unwrap(self) -> Result<Shader, GLError> {
unsafe {
let mut shader = try! {
self.gl.create_shader(self.ty).or_else(|_| {
let msg = "Error creating OpenGL shader";
Err(GLError::Message(msg.to_owned()))
})
};
self.gl.shader_source(&mut shader, self.source);
try!(self.gl.compile_shader(&mut shader));
Ok(shader)
}
}
pub fn unwrap(self) -> Shader {
self.try_unwrap().unwrap()
}
}
pub trait ContextShaderBuilderExt: AContext + Sized {
fn build_shader<'a>(&'a self, ty: ShaderType, source: &'a str)
-> ShaderBuilder<'a, Self>;
fn build_fragment_shader<'a>(&'a self, source: &'a str)
-> ShaderBuilder<'a, Self>
{
self.build_shader(ShaderType::FragmentShader, source)
}
fn build_vertex_shader<'a>(&'a self, source: &'a str)
-> ShaderBuilder<'a, Self>
{
self.build_shader(ShaderType::VertexShader, source)
}
}
impl<C: AContext> ContextShaderBuilderExt for C {
fn build_shader<'a>(&'a self, ty: ShaderType, source: &'a str)
-> ShaderBuilder<'a, C>
{
ShaderBuilder::new(self, ty, source)
}
}
pub trait ContextShaderExt: BaseContext {
unsafe fn create_shader(&self, shader_type: ShaderType)
-> Result<Shader, ()>
{
let id = gl::CreateShader(shader_type.gl_enum());
dbg_gl_sanity_check! {
GLError::InvalidEnum => "`shaderType` is not an accepted value",
_ => "Unknown error"
}
if id > 0 {
Ok(Shader::from_raw(id))
}
else {
Err(())
}
}
fn shader_source(&self, shader: &mut Shader, source: &str) {
unsafe {
let source_ptr = source.as_ptr() as *const GLchar;
let source_len = source.len() as GLint;
gl::ShaderSource(shader.id(), 1,
&source_ptr as *const *const GLchar,
&source_len as *const GLint);
dbg_gl_error! {
GLError::InvalidOperation => "`shader` is not a shader object or shader compiler is not supported",
GLError::InvalidValue => "`shader` is not a value generated by OpenGL or `count` < 0",
_ => "Unknown error"
}
}
}
fn compile_shader(&self, shader: &mut Shader) -> Result<(), GLError> {
let success = unsafe {
gl::CompileShader(shader.id());
dbg_gl_error! {
GLError::InvalidOperation => "`shader` is not a shader object or shader compiler is not supported",
GLError::InvalidValue => "`shader` is not a value generated by OpenGL",
_ => "Unknown error"
}
let mut compile_status : GLint = 0;
_get_shader_iv(shader,
gl::COMPILE_STATUS,
&mut compile_status as *mut GLint);
compile_status == gl::TRUE as GLint
};
if success {
Ok(())
}
else {
let msg = match self.get_shader_info_log(&shader) {
Some(s) => { s },
None => { String::from("[Unknown shader error]") }
};
Err(GLError::Message(msg))
}
}
fn get_shader_info_log(&self, shader: &Shader) -> Option<String> {
unsafe {
let mut info_length : GLint = 0;
_get_shader_iv(shader,
gl::INFO_LOG_LENGTH,
&mut info_length as *mut GLint);
if info_length > 0 {
let mut bytes = Vec::<u8>::with_capacity(info_length as usize);
gl::GetShaderInfoLog(shader.id(),
info_length,
ptr::null_mut(),
bytes.as_mut_ptr() as *mut GLchar);
dbg_gl_sanity_check! {
GLError::InvalidValue => "`shader` is not a value generated by OpenGL, or `maxLength` < 0",
GLError::InvalidOperation => "`shader` is not a shader object",
_ => "Unknown error"
}
bytes.set_len((info_length - 1) as usize);
String::from_utf8(bytes).ok()
}
else {
None
}
}
}
}
impl<C: BaseContext> ContextShaderExt for C {
}
gl_enum! {
pub gl_enum ShaderType {
pub const VertexShader as VERTEX_SHADER = gl::VERTEX_SHADER,
pub const FragmentShader as FRAGMENT_SHADER = gl::FRAGMENT_SHADER
}
}