use super::*;
use runtime::gl::types::*;
use std::collections::HashMap;
pub static VERTICES: [GLfloat; 24] = [
-1.0, 1.0, 0.0, 1.0, 1.0, -1.0, 0.0, 1.0, -1.0, -1.0, 0.0, 1.0, -1.0, 1.0, 0.0, 1.0, 1.0, -1.0,
0.0, 1.0, 1.0, 1.0, 0.0, 1.0,
];
pub struct Display {
fragment_shader: u32,
shader_program: u32,
texture: u32,
vao: u32,
vbo: u32,
vertex_shader: u32,
vertex_shader_cache: HashMap<String, u32>,
fragment_shader_cache: HashMap<String, u32>,
shader_program_cache: HashMap<(u32, u32), u32>,
}
impl Display {
pub fn new() -> Result<Display, Error> {
let mut texture = 0;
unsafe {
gl::GenTextures(1, &mut texture);
gl::BindTexture(gl::TEXTURE_2D, texture);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST as i32);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::NEAREST as i32);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::REPEAT as i32);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::REPEAT as i32);
gl::ActiveTexture(gl::TEXTURE0);
}
let mut vao = 0;
let mut vbo = 0;
unsafe {
gl::GenVertexArrays(1, &mut vao);
gl::BindVertexArray(vao);
gl::GenBuffers(1, &mut vbo);
gl::BindBuffer(gl::ARRAY_BUFFER, vbo);
gl::BufferData(
gl::ARRAY_BUFFER,
(VERTICES.len() * mem::size_of::<GLfloat>()) as GLsizeiptr,
mem::transmute(&VERTICES[0]),
gl::STATIC_DRAW,
);
}
Ok(Display {
vertex_shader_cache: HashMap::new(),
fragment_shader_cache: HashMap::new(),
shader_program_cache: HashMap::new(),
fragment_shader: 0,
shader_program: 0,
vertex_shader: 0,
texture,
vao,
vbo,
})
}
pub fn set_shaders(
&mut self,
vertex_shader_source: &str,
fragment_shader_source: &str,
) -> Result<(), Error> {
let vertex_shader = Self::compile_shader(
vertex_shader_source,
gl::VERTEX_SHADER,
&mut self.vertex_shader_cache,
).map_err(|info_log| Error::VertexShaderCompilation { info_log })?;
let fragment_shader = Self::compile_shader(
fragment_shader_source,
gl::FRAGMENT_SHADER,
&mut self.fragment_shader_cache,
).map_err(|info_log| Error::FragmentShaderCompilation { info_log })?;
let shader_program = Self::link_program(
vertex_shader,
fragment_shader,
&mut self.shader_program_cache,
).map_err(|info_log| Error::ShaderProgramLinking { info_log })?;
if self.shader_program != shader_program {
unsafe {
gl::UseProgram(shader_program);
gl::BindFragDataLocation(shader_program, 0, CString::new("color").unwrap().as_ptr());
let pixel_uniform =
gl::GetUniformLocation(shader_program, CString::new("pixels").unwrap().as_ptr());
gl::Uniform1i(pixel_uniform, 0);
let pos_attr =
gl::GetAttribLocation(shader_program, CString::new("position").unwrap().as_ptr());
gl::EnableVertexAttribArray(pos_attr as GLuint);
gl::VertexAttribPointer(
pos_attr as GLuint,
4,
gl::FLOAT,
gl::FALSE as GLboolean,
0,
ptr::null(),
);
}
self.shader_program = shader_program;
}
Ok(())
}
pub fn present(&self, pixels: &[Pixel], dimensions: (usize, usize)) {
let pixels = pixels.as_ptr();
let bytes = pixels as *const std::os::raw::c_void;
unsafe {
gl::ClearColor(0.0, 0.0, 0.0, 1.0);
gl::Clear(gl::COLOR_BUFFER_BIT);
gl::TexImage2D(
gl::TEXTURE_2D,
0,
gl::RGBA32F as i32,
dimensions.0 as i32,
dimensions.1 as i32,
0,
gl::RGBA,
gl::FLOAT,
bytes,
);
gl::DrawArrays(gl::TRIANGLES, 0, 6);
#[cfg(debug_assertions)]
assert_eq!(gl::GetError(), gl::NO_ERROR);
}
}
fn compile_shader(
source: &str,
ty: GLenum,
shader_cache: &mut HashMap<String, GLuint>,
) -> Result<GLuint, String> {
if let Some(shader) = shader_cache.get(source).cloned() {
return Ok(shader);
}
unsafe {
let shader = gl::CreateShader(ty);
let c_str = CString::new(source.as_bytes()).unwrap();
gl::ShaderSource(shader, 1, &c_str.as_ptr(), ptr::null());
gl::CompileShader(shader);
let mut status = gl::FALSE as GLint;
gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut status);
if status != (gl::TRUE as GLint) {
let mut len = 0;
gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len);
let mut buf = Vec::with_capacity(len as usize);
buf.set_len((len as usize) - 1); gl::GetShaderInfoLog(
shader,
len,
ptr::null_mut(),
buf.as_mut_ptr() as *mut GLchar,
);
return Err(String::from_utf8_lossy(&buf).to_string());
}
shader_cache.insert(source.to_string(), shader);
Ok(shader)
}
}
fn link_program(
vs: GLuint,
fs: GLuint,
program_cache: &mut HashMap<(GLuint, GLuint), GLuint>,
) -> Result<GLuint, String> {
let cache_key = (vs, fs);
if let Some(program) = program_cache.get(&cache_key).cloned() {
return Ok(program);
}
unsafe {
let program = gl::CreateProgram();
gl::AttachShader(program, vs);
gl::AttachShader(program, fs);
gl::LinkProgram(program);
let mut status = gl::FALSE as GLint;
gl::GetProgramiv(program, gl::LINK_STATUS, &mut status);
if status != (gl::TRUE as GLint) {
let mut len: GLint = 0;
gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut len);
let mut buf = Vec::with_capacity(len as usize);
buf.set_len((len as usize) - 1); gl::GetProgramInfoLog(
program,
len,
ptr::null_mut(),
buf.as_mut_ptr() as *mut GLchar,
);
return Err(String::from_utf8_lossy(&buf).to_string());
}
program_cache.insert(cache_key, program);
Ok(program)
}
}
}
impl Drop for Display {
fn drop(&mut self) {
unsafe {
gl::DeleteProgram(self.shader_program);
gl::DeleteShader(self.fragment_shader);
gl::DeleteShader(self.vertex_shader);
gl::DeleteTextures(1, &self.texture);
gl::DeleteBuffers(1, &self.vbo);
gl::DeleteVertexArrays(1, &self.vao);
}
}
}