opengles_graphics/
shader_utils.rs

1//! Helper functions for dealing with shaders.
2
3// External crates.
4use crate::gl;
5use crate::gl::types::{GLboolean, GLchar, GLenum, GLint, GLsizeiptr, GLuint};
6use std::ffi::CString;
7use std::{ptr, mem};
8
9/// Describes a shader attribute.
10pub struct DynamicAttribute {
11    /// The vertex buffer object.
12    vbo: GLuint,
13    /// The number of components.
14    size: i32,
15    /// The location of the attribute in shader.
16    location: GLuint,
17    /// Whether to normalize when sending to GPU.
18    normalize: GLboolean,
19    /// The type, for example gl::FLOAT.
20    ty: GLenum,
21}
22
23impl Drop for DynamicAttribute {
24    fn drop(&mut self) {
25        unsafe {
26            gl::DeleteBuffers(1, &self.vbo);
27        }
28    }
29}
30
31impl DynamicAttribute {
32    /// Binds to a vertex array object.
33    ///
34    /// The vertex array object remembers the format for later.
35    pub fn bind_vao(&self, vao: GLuint) {
36        let stride = 0;
37        unsafe {
38            gl::BindVertexArray(vao);
39            gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo);
40            gl::VertexAttribPointer(self.location,
41                                    self.size,
42                                    self.ty,
43                                    self.normalize,
44                                    stride,
45                                    ptr::null());
46        }
47    }
48
49    fn new(program: GLuint,
50           name: &str,
51           size: i32,
52           normalize: GLboolean,
53           ty: GLenum)
54           -> Result<Self, String> {
55        let location = attribute_location(program, name)?;
56        let mut vbo = 0;
57        unsafe {
58            gl::GenBuffers(1, &mut vbo);
59        }
60        let res = DynamicAttribute {
61            vbo: vbo,
62            size: size,
63            location: location,
64            normalize: normalize,
65            ty: ty,
66        };
67        Ok(res)
68    }
69
70    /// Create XYZ vertex attribute.
71    pub fn xyz(program: GLuint, name: &str) -> Result<DynamicAttribute, String> {
72        DynamicAttribute::new(program, name, 3, gl::FALSE, gl::FLOAT)
73    }
74
75    /// Create XY vertex attribute.
76    pub fn xy(program: GLuint, name: &str) -> Result<DynamicAttribute, String> {
77        DynamicAttribute::new(program, name, 2, gl::FALSE, gl::FLOAT)
78    }
79
80    /// Create RGB color attribute.
81    pub fn rgb(program: GLuint, name: &str) -> Result<DynamicAttribute, String> {
82        DynamicAttribute::new(program, name, 3, gl::FALSE, gl::FLOAT)
83    }
84
85    /// Create RGBA color attribute.
86    pub fn rgba(program: GLuint, name: &str) -> Result<DynamicAttribute, String> {
87        DynamicAttribute::new(program, name, 4, gl::FALSE, gl::FLOAT)
88    }
89
90    /// Create texture coordinate attribute.
91    pub fn uv(program: GLuint, name: &str) -> Result<DynamicAttribute, String> {
92        DynamicAttribute::new(program, name, 2, gl::FALSE, gl::FLOAT)
93    }
94
95    /// Sets attribute data.
96    pub unsafe fn set<T>(&self, data: &[T]) {
97        gl::EnableVertexAttribArray(self.location);
98        gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo);
99        gl::BufferData(gl::ARRAY_BUFFER,
100                       data.len() as GLsizeiptr * mem::size_of::<T>() as GLsizeiptr,
101                       mem::transmute(data.as_ptr()),
102                       gl::DYNAMIC_DRAW);
103    }
104}
105
106/// Compiles a shader.
107///
108/// Returns a shader or a message with the error.
109pub fn compile_shader(shader_type: GLenum, source: &str) -> Result<GLuint, String> {
110    unsafe {
111        let shader = gl::CreateShader(shader_type);
112        let c_source = match CString::new(source) {
113            Ok(x) => x,
114            Err(err) => return Err(format!("compile_shader: {}", err)),
115        };
116        gl::ShaderSource(shader, 1, &c_source.as_ptr(), ptr::null());
117        drop(source);
118        gl::CompileShader(shader);
119        let mut status = gl::FALSE as GLint;
120        gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut status);
121        if status == (gl::TRUE as GLint) {
122            Ok(shader)
123        } else {
124            let mut len = 0;
125            gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len);
126
127            if len == 0 {
128                Err("Compilation failed with no log. \
129                     The OpenGL context might have been created on another thread, \
130                     or not have been created."
131                    .to_string())
132            } else {
133                // Subtract 1 to skip the trailing null character.
134                let mut buf = vec![0; len as usize - 1];
135                gl::GetShaderInfoLog(shader,
136                                     len,
137                                     ptr::null_mut(),
138                                     buf.as_mut_ptr() as *mut GLchar);
139
140                gl::DeleteShader(shader);
141
142                Err(String::from_utf8(buf).ok().expect("ShaderInfoLog not valid utf8"))
143            }
144        }
145    }
146}
147
148/// Finds attribute location from a program.
149///
150/// Returns `Err` if there is no attribute with such name.
151pub fn attribute_location(program: GLuint, name: &str) -> Result<GLuint, String> {
152    unsafe {
153        let c_name = match CString::new(name) {
154            Ok(x) => x,
155            Err(err) => return Err(format!("attribute_location: {}", err)),
156        };
157        let id = gl::GetAttribLocation(program, c_name.as_ptr());
158        drop(c_name);
159        if id < 0 {
160            Err(format!("Attribute '{}' does not exists in shader", name))
161        } else {
162            Ok(id as GLuint)
163        }
164    }
165}
166
167/// Finds uniform location from a program.
168///
169/// Returns `Err` if there is no uniform with such name.
170pub fn uniform_location(program: GLuint, name: &str) -> Result<GLuint, String> {
171    unsafe {
172        let c_name = match CString::new(name) {
173            Ok(x) => x,
174            Err(err) => return Err(format!("uniform_location: {}", err)),
175        };
176        let id = gl::GetUniformLocation(program, c_name.as_ptr());
177        drop(c_name);
178        if id < 0 {
179            Err(format!("Uniform '{}' does not exists in shader", name))
180        } else {
181            Ok(id as GLuint)
182        }
183    }
184}