use glutin::GlContext; use glutin; use program::ProgramId; use std::ffi::CStr; use std::mem; pub(crate) mod gl { #![allow(unknown_lints)] #![allow(clippy)] pub use self::Gles2 as Gl; include!(concat!(env!("OUT_DIR"), "/gles_bindings.rs")); } #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct TextureUnit(u32); impl TextureUnit { pub fn index(&self) -> u32 { self.0 } } #[derive(Clone)] pub struct Context { pub(crate) gl: gl::Gl, active_texture_unit: TextureUnit, next_free_texture_unit: TextureUnit, active_program: ProgramId, } impl ::std::fmt::Debug for Context { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { let version = unsafe { let data = CStr::from_ptr(self.gl.GetString(gl::VERSION) as *const _).to_bytes().to_vec(); String::from_utf8(data).unwrap_or_else(|_| "".into()) }; write!(f, "Context {{ version: {:?} }}", version) } } macro_rules! check_gl_errors { ($cx:expr) => ( $cx.check_errors(file!(), line!()); ) } impl Context { pub fn from_gl_window(window: &glutin::GlWindow) -> Context { let gl = gl::Gl::load_with(|ptr| window.get_proc_address(ptr) as *const _); let cx = Context { gl, /// Initial active texture unit is supposed to be GL_TEXTURE0 active_texture_unit: TextureUnit(0), next_free_texture_unit: TextureUnit(0), active_program: ProgramId::invalid(), }; // Initialize a vertex array object (VAO) if the current OpenGL context supports it. VAOs are // not OpenGL ES 2.0 compatible, but are required for rendering with a core context. if cx.gl.BindVertexArray.is_loaded() { unsafe { let mut vao = mem::uninitialized(); cx.gl.GenVertexArrays(1, &mut vao); cx.gl.BindVertexArray(vao); } } info!("OpenGL version: {}", cx.gl_version()); debug!("MAX_TEXTURE_SIZE: {}", cx.max_texture_size()); cx } pub fn gl_version(&self) -> String { unsafe { let data = CStr::from_ptr(self.gl.GetString(gl::VERSION) as *const _).to_bytes().to_vec(); String::from_utf8(data).unwrap_or_else(|_| "".into()) } } pub fn max_texture_size(&self) -> i32 { unsafe { let mut size = 0; self.gl.GetIntegerv(gl::MAX_TEXTURE_SIZE, &mut size as *mut _); size } } pub fn check_errors(&self, file: &str, line: u32) { let mut fail = false; loop { match unsafe { self.gl.GetError() } { gl::NO_ERROR => break, gl::INVALID_VALUE => { error!("{}:{}, invalid value error", file, line); fail = true; }, gl::INVALID_ENUM => { error!("{}:{}, invalid enum error", file, line); fail = true; }, gl::INVALID_OPERATION => { error!("{}:{}, invalid operation error", file, line); fail = true; }, gl::INVALID_FRAMEBUFFER_OPERATION => { error!("{}:{}, invalid framebuffer operation error", file, line); fail = true; }, gl::OUT_OF_MEMORY => { error!("{}:{}, out of memory error", file, line); fail = true; }, x => { error!("{}:{}, unknown error {}", file, line, x); fail = true; }, } } if fail { panic!("OpenGL error"); } } pub fn clear_color(&self, color: (f32, f32, f32, f32)) { unsafe { self.gl.ClearColor(color.0, color.1, color.2, color.3); self.gl.Clear(gl::COLOR_BUFFER_BIT); } } pub fn set_viewport(&self, x: i32, y: i32, width: u32, height: u32) { unsafe { self.gl.Viewport( x, y, width as gl::types::GLsizei, height as gl::types::GLsizei, ); } } pub fn set_active_texture_unit(&mut self, unit: TextureUnit) { if unit != self.active_texture_unit { unsafe { self.gl.ActiveTexture(gl::TEXTURE0 + unit.0); } self.active_texture_unit = unit; } } pub fn occupy_free_texture_unit(&mut self) -> TextureUnit { let tu = self.next_free_texture_unit; //TODO check against max number of texture units //TODO add a way to free texture units self.next_free_texture_unit = TextureUnit(self.next_free_texture_unit.0 + 1); tu } pub fn use_program(&mut self, prog: ProgramId) { if prog != self.active_program { unsafe { self.gl.UseProgram(prog.index()); } self.active_program = prog; } } }