1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
use gl;
use gl::types::*;

use error::{GlResult, GlError};

macro_rules! get_info_log {
    ($get_attr:path, $get_log:path, $gl_id:expr) => {{
        let mut log_length_glint: GLint = 0;
        $get_attr($gl_id, gl::INFO_LOG_LENGTH, &mut log_length_glint);
        let log_length = log_length_glint as usize;
        if log_length == 0 {
            None
        } else {
            let mut raw_log = Vec::<u8>::with_capacity(log_length);
            $get_log($gl_id, log_length as GLsizei,
                0 as *mut GLsizei, raw_log.as_mut_ptr() as *mut GLchar);
            raw_log.set_len(log_length);
            let log = String::from_utf8(raw_log)
                .expect("OpenGL returned invalid utf8 in a program info log");
            Some(log)
        }
    }}
}

pub fn create_program() -> GlResult<GLuint> {
    let gl_id = unsafe { gl::CreateProgram() };
    if gl_id == 0 {
        Err(GlError::ProgramCreation)
    } else {
        Ok(gl_id)
    }
}

pub fn create_shader(kind: GLenum, source: &str) -> GlResult<GLuint> {
    unsafe {
        let gl_id = gl::CreateShader(kind as _);
        if gl_id == 0 {
            return Err(GlError::ShaderCreation);
        }
        gl::ShaderSource(
            gl_id,
            1,
            &(source.as_ptr() as *const _),
            &(source.len() as _)
        );
        gl::CompileShader(gl_id);

        let mut status = 0;
        gl::GetShaderiv(gl_id, gl::COMPILE_STATUS, &mut status);
        if status != 1 {
            Err(GlError::ShaderCompilation(
                get_info_log!(gl::GetShaderiv, gl::GetShaderInfoLog, gl_id)
            ))
        } else {
            Ok(gl_id)
        }
    }
}

pub fn get_link_status(program_id: GLuint) -> GlResult<()> {
    let mut link_status = gl::FALSE as i32;
    unsafe {
        gl::GetProgramiv(
            program_id,
            gl::LINK_STATUS,
            &mut link_status as *mut _
        );
        if link_status != gl::TRUE as i32 {
            Err(GlError::ProgramLinkage(
                get_info_log!(gl::GetProgramiv, gl::GetProgramInfoLog, program_id)
            ))
        } else {
            Ok(())
        }
    }
}

/// Create an OpenGL program with one function call.
///
/// Will report both shader compilation errors and program link errors.
///
/// Caller is responsible for deleting the program only. Shaders will be created and then freed
/// after the program is linked.
pub fn create_basic_program(vertex_source: &str, fragment_source: &str) -> GlResult<GLuint> {
    let vertex_shader = create_shader(gl::VERTEX_SHADER, vertex_source)?;
    let fragment_shader = create_shader(gl::FRAGMENT_SHADER, fragment_source)?;
    Ok(create_linked_program(
        &[
            vertex_shader,
            fragment_shader,
        ],
        true
    )?)
}

/// Create an OpenGL program given a slice of shader references.
///
/// Pass `true` for `delete_shaders` in order to automatically delete each shader after linking.
pub fn create_linked_program(shaders: &[GLuint], delete_shaders: bool) -> GlResult<GLuint> {
    let program = create_program()?;
    unsafe {
        for &shader in shaders {
            gl::AttachShader(program, shader);
        }
        gl::LinkProgram(program);
        get_link_status(program)?;
        // we have to detach the shaders before the shader objects will be freed
        for &shader in shaders {
            gl::DetachShader(program, shader);
            if delete_shaders {
                gl::DeleteShader(shader);
            }
        }
    }
    Ok(program)
}