From e09b447d0d0da8edb1806de48cdc199fcb0b5f98 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Tue, 5 Mar 2019 14:51:29 -0800 Subject: [PATCH] Move the contents of `gl::device` into `gl` --- demo/common/src/lib.rs | 2 +- gl/src/device.rs | 813 ----------------------------------------- gl/src/lib.rs | 809 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 803 insertions(+), 821 deletions(-) delete mode 100644 gl/src/device.rs diff --git a/demo/common/src/lib.rs b/demo/common/src/lib.rs index 7a63e1b5..f4ad68a4 100644 --- a/demo/common/src/lib.rs +++ b/demo/common/src/lib.rs @@ -19,7 +19,7 @@ use pathfinder_geometry::basic::point::{Point2DF32, Point2DI32, Point3DF32}; use pathfinder_geometry::basic::rect::RectF32; use pathfinder_geometry::basic::transform2d::Transform2DF32; use pathfinder_geometry::basic::transform3d::{Perspective, Transform3DF32}; -use pathfinder_gl::device::GLDevice; +use pathfinder_gl::GLDevice; use pathfinder_gpu::{DepthFunc, DepthState, Device, Primitive, RenderState, Resources}; use pathfinder_gpu::{StencilFunc, StencilState, UniformData}; use pathfinder_renderer::builder::{RenderOptions, RenderTransform, SceneBuilder}; diff --git a/gl/src/device.rs b/gl/src/device.rs deleted file mode 100644 index bc246156..00000000 --- a/gl/src/device.rs +++ /dev/null @@ -1,813 +0,0 @@ -// pathfinder/gl/src/device.rs -// -// Copyright © 2019 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! An OpenGL implementation of the device abstraction. - -use gl::types::{GLboolean, GLchar, GLdouble, GLenum, GLfloat, GLint, GLsizei, GLsizeiptr}; -use gl::types::{GLuint, GLvoid}; -use pathfinder_geometry::basic::point::Point2DI32; -use pathfinder_gpu::{BlendState, BufferTarget, BufferUploadMode, DepthFunc, Device, Primitive}; -use pathfinder_gpu::{RenderState, ShaderKind, StencilFunc, TextureFormat}; -use pathfinder_gpu::{UniformData, VertexAttrType}; -use pathfinder_simd::default::F32x4; -use std::ffi::CString; -use std::mem; -use std::ptr; -use std::time::Duration; - -pub struct GLDevice; - -impl GLDevice { - #[inline] - pub fn new() -> GLDevice { GLDevice } - - fn set_texture_parameters(&self, texture: &GLTexture) { - self.bind_texture(texture, 0); - unsafe { - gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as GLint); ck(); - gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as GLint); ck(); - gl::TexParameteri(gl::TEXTURE_2D, - gl::TEXTURE_WRAP_S, - gl::CLAMP_TO_EDGE as GLint); ck(); - gl::TexParameteri(gl::TEXTURE_2D, - gl::TEXTURE_WRAP_T, - gl::CLAMP_TO_EDGE as GLint); ck(); - } - } - - fn set_render_state(&self, render_state: &RenderState) { - unsafe { - // Set blend. - match render_state.blend { - BlendState::Off => { - gl::Disable(gl::BLEND); ck(); - } - BlendState::RGBOneAlphaOne => { - gl::BlendEquation(gl::FUNC_ADD); ck(); - gl::BlendFunc(gl::ONE, gl::ONE); ck(); - gl::Enable(gl::BLEND); ck(); - } - BlendState::RGBOneAlphaOneMinusSrcAlpha => { - gl::BlendEquation(gl::FUNC_ADD); ck(); - gl::BlendFuncSeparate(gl::ONE, - gl::ONE_MINUS_SRC_ALPHA, - gl::ONE, - gl::ONE); ck(); - gl::Enable(gl::BLEND); ck(); - } - BlendState::RGBSrcAlphaAlphaOneMinusSrcAlpha => { - gl::BlendEquation(gl::FUNC_ADD); ck(); - gl::BlendFuncSeparate(gl::SRC_ALPHA, - gl::ONE_MINUS_SRC_ALPHA, - gl::ONE, - gl::ONE); ck(); - gl::Enable(gl::BLEND); ck(); - } - } - - // Set depth. - match render_state.depth { - None => { - gl::Disable(gl::DEPTH_TEST); ck(); - } - Some(ref state) => { - gl::DepthFunc(state.func.to_gl_depth_func()); ck(); - gl::DepthMask(state.write as GLboolean); ck(); - gl::Enable(gl::DEPTH_TEST); ck(); - } - } - - // Set stencil. - match render_state.stencil { - None => { - gl::Disable(gl::STENCIL_TEST); ck(); - } - Some(ref state) => { - gl::StencilFunc(state.func.to_gl_stencil_func(), - state.reference as GLint, - state.mask); ck(); - let (pass_action, write_mask) = if state.write { - (gl::REPLACE, state.mask) - } else { - (gl::KEEP, 0) - }; - gl::StencilOp(gl::KEEP, gl::KEEP, pass_action); ck(); - gl::StencilMask(write_mask); - gl::Enable(gl::STENCIL_TEST); ck(); - } - } - - // Set color mask. - let color_mask = render_state.color_mask as GLboolean; - gl::ColorMask(color_mask, color_mask, color_mask, color_mask); ck(); - } - } - - fn reset_render_state(&self, render_state: &RenderState) { - unsafe { - match render_state.blend { - BlendState::Off => {} - BlendState::RGBOneAlphaOneMinusSrcAlpha | - BlendState::RGBOneAlphaOne | - BlendState::RGBSrcAlphaAlphaOneMinusSrcAlpha => { - gl::Disable(gl::BLEND); ck(); - } - } - - if render_state.depth.is_some() { - gl::Disable(gl::DEPTH_TEST); ck(); - } - - if render_state.stencil.is_some() { - gl::StencilMask(!0); ck(); - gl::Disable(gl::STENCIL_TEST); ck(); - } - - gl::ColorMask(gl::TRUE, gl::TRUE, gl::TRUE, gl::TRUE); ck(); - } - } -} - -impl Device for GLDevice { - type Buffer = GLBuffer; - type Framebuffer = GLFramebuffer; - type Program = GLProgram; - type Shader = GLShader; - type Texture = GLTexture; - type TimerQuery = GLTimerQuery; - type Uniform = GLUniform; - type VertexArray = GLVertexArray; - type VertexAttr = GLVertexAttr; - - fn create_texture(&self, format: TextureFormat, size: Point2DI32) -> GLTexture { - let (gl_internal_format, gl_format, gl_type); - match format { - TextureFormat::R16F => { - gl_internal_format = gl::R16F as GLint; - gl_format = gl::RED; - gl_type = gl::HALF_FLOAT; - } - TextureFormat::RGBA8 => { - gl_internal_format = gl::RGBA as GLint; - gl_format = gl::RGBA; - gl_type = gl::UNSIGNED_BYTE; - } - } - - let mut texture = GLTexture { gl_texture: 0, size }; - unsafe { - gl::GenTextures(1, &mut texture.gl_texture); ck(); - self.bind_texture(&texture, 0); - gl::TexImage2D(gl::TEXTURE_2D, - 0, - gl_internal_format, - size.x() as GLsizei, - size.y() as GLsizei, - 0, - gl_format, - gl_type, - ptr::null()); ck(); - } - - self.set_texture_parameters(&texture); - texture - } - - fn create_texture_from_data(&self, size: Point2DI32, data: &[u8]) -> GLTexture { - assert!(data.len() >= size.x() as usize * size.y() as usize); - - let mut texture = GLTexture { gl_texture: 0, size }; - unsafe { - gl::GenTextures(1, &mut texture.gl_texture); ck(); - self.bind_texture(&texture, 0); - gl::TexImage2D(gl::TEXTURE_2D, - 0, - gl::RED as GLint, - size.x() as GLsizei, - size.y() as GLsizei, - 0, - gl::RED, - gl::UNSIGNED_BYTE, - data.as_ptr() as *const GLvoid); ck(); - } - - self.set_texture_parameters(&texture); - texture - } - - fn create_shader_from_source(&self, name: &str, source: &[u8], kind: ShaderKind) -> GLShader { - let gl_shader_kind = match kind { - ShaderKind::Vertex => gl::VERTEX_SHADER, - ShaderKind::Fragment => gl::FRAGMENT_SHADER, - }; - - unsafe { - let gl_shader = gl::CreateShader(gl_shader_kind); ck(); - gl::ShaderSource(gl_shader, - 1, - [source.as_ptr() as *const GLchar].as_ptr(), - [source.len() as GLint].as_ptr()); ck(); - gl::CompileShader(gl_shader); ck(); - - let mut compile_status = 0; - gl::GetShaderiv(gl_shader, gl::COMPILE_STATUS, &mut compile_status); ck(); - if compile_status != gl::TRUE as GLint { - let mut info_log_length = 0; - gl::GetShaderiv(gl_shader, gl::INFO_LOG_LENGTH, &mut info_log_length); ck(); - let mut info_log = vec![0; info_log_length as usize]; - gl::GetShaderInfoLog(gl_shader, - info_log.len() as GLint, - ptr::null_mut(), - info_log.as_mut_ptr() as *mut GLchar); ck(); - eprintln!("Shader info log:\n{}", String::from_utf8_lossy(&info_log)); - panic!("{:?} shader '{}' compilation failed", kind, name); - } - - GLShader { gl_shader } - } - } - - fn create_program_from_shaders(&self, - name: &str, - vertex_shader: GLShader, - fragment_shader: GLShader) - -> GLProgram { - let gl_program; - unsafe { - gl_program = gl::CreateProgram(); ck(); - gl::AttachShader(gl_program, vertex_shader.gl_shader); ck(); - gl::AttachShader(gl_program, fragment_shader.gl_shader); ck(); - gl::LinkProgram(gl_program); ck(); - - let mut link_status = 0; - gl::GetProgramiv(gl_program, gl::LINK_STATUS, &mut link_status); ck(); - if link_status != gl::TRUE as GLint { - let mut info_log_length = 0; - gl::GetProgramiv(gl_program, gl::INFO_LOG_LENGTH, &mut info_log_length); ck(); - let mut info_log = vec![0; info_log_length as usize]; - gl::GetProgramInfoLog(gl_program, - info_log.len() as GLint, - ptr::null_mut(), - info_log.as_mut_ptr() as *mut GLchar); ck(); - eprintln!("Program info log:\n{}", String::from_utf8_lossy(&info_log)); - panic!("Program '{}' linking failed", name); - } - } - - GLProgram { gl_program, vertex_shader, fragment_shader } - } - - #[inline] - fn create_vertex_array(&self) -> GLVertexArray { - unsafe { - let mut array = GLVertexArray { gl_vertex_array: 0 }; - gl::GenVertexArrays(1, &mut array.gl_vertex_array); ck(); - array - } - } - - fn get_vertex_attr(&self, program: &Self::Program, name: &str) -> GLVertexAttr { - let name = CString::new(format!("a{}", name)).unwrap(); - let attr = unsafe { - gl::GetAttribLocation(program.gl_program, name.as_ptr() as *const GLchar) as GLuint - }; ck(); - GLVertexAttr { attr } - } - - fn get_uniform(&self, program: &GLProgram, name: &str) -> GLUniform { - let name = CString::new(format!("u{}", name)).unwrap(); - let location = unsafe { - gl::GetUniformLocation(program.gl_program, name.as_ptr() as *const GLchar) - }; ck(); - GLUniform { location } - } - - fn use_program(&self, program: &Self::Program) { - unsafe { - gl::UseProgram(program.gl_program); ck(); - } - } - - fn configure_float_vertex_attr(&self, - attr: &GLVertexAttr, - size: usize, - attr_type: VertexAttrType, - normalized: bool, - stride: usize, - offset: usize, - divisor: u32) { - unsafe { - gl::VertexAttribPointer(attr.attr, - size as GLint, - attr_type.to_gl_type(), - if normalized { gl::TRUE } else { gl::FALSE }, - stride as GLint, - offset as *const GLvoid); ck(); - gl::VertexAttribDivisor(attr.attr, divisor); ck(); - gl::EnableVertexAttribArray(attr.attr); ck(); - } - } - - fn configure_int_vertex_attr(&self, - attr: &GLVertexAttr, - size: usize, - attr_type: VertexAttrType, - stride: usize, - offset: usize, - divisor: u32) { - unsafe { - gl::VertexAttribIPointer(attr.attr, - size as GLint, - attr_type.to_gl_type(), - stride as GLint, - offset as *const GLvoid); ck(); - gl::VertexAttribDivisor(attr.attr, divisor); ck(); - gl::EnableVertexAttribArray(attr.attr); ck(); - } - } - - fn set_uniform(&self, uniform: &Self::Uniform, data: UniformData) { - unsafe { - match data { - UniformData::Mat4(data) => { - assert_eq!(mem::size_of::<[F32x4; 4]>(), 4 * 4 * 4); - let data_ptr: *const F32x4 = data.as_ptr(); - gl::UniformMatrix4fv(uniform.location, - 1, - gl::FALSE, - data_ptr as *const GLfloat); - } - UniformData::Vec2(data) => { - gl::Uniform2f(uniform.location, data.x(), data.y()); ck(); - } - UniformData::Vec4(data) => { - gl::Uniform4f(uniform.location, data.x(), data.y(), data.z(), data.w()); ck(); - } - UniformData::TextureUnit(unit) => { - gl::Uniform1i(uniform.location, unit as GLint); ck(); - } - } - } - } - - fn create_framebuffer(&self, texture: GLTexture) -> GLFramebuffer { - let mut gl_framebuffer = 0; - unsafe { - gl::GenFramebuffers(1, &mut gl_framebuffer); ck(); - assert_eq!(gl::GetError(), gl::NO_ERROR); - gl::BindFramebuffer(gl::FRAMEBUFFER, gl_framebuffer); ck(); - self.bind_texture(&texture, 0); - gl::FramebufferTexture2D(gl::FRAMEBUFFER, - gl::COLOR_ATTACHMENT0, - gl::TEXTURE_2D, - texture.gl_texture, - 0); ck(); - assert_eq!(gl::CheckFramebufferStatus(gl::FRAMEBUFFER), gl::FRAMEBUFFER_COMPLETE); - } - - GLFramebuffer { gl_framebuffer, texture } - } - - fn create_buffer(&self) -> GLBuffer { - unsafe { - let mut gl_buffer = 0; - gl::GenBuffers(1, &mut gl_buffer); ck(); - GLBuffer { gl_buffer } - } - } - - fn upload_to_buffer(&self, - buffer: &GLBuffer, - data: &[T], - target: BufferTarget, - mode: BufferUploadMode) { - let target = match target { - BufferTarget::Vertex => gl::ARRAY_BUFFER, - BufferTarget::Index => gl::ELEMENT_ARRAY_BUFFER, - }; - let mode = match mode { - BufferUploadMode::Static => gl::STATIC_DRAW, - BufferUploadMode::Dynamic => gl::DYNAMIC_DRAW, - }; - unsafe { - gl::BindBuffer(target, buffer.gl_buffer); ck(); - gl::BufferData(target, - (data.len() * mem::size_of::()) as GLsizeiptr, - data.as_ptr() as *const GLvoid, - mode); ck(); - } - } - - #[inline] - fn framebuffer_texture<'f>(&self, framebuffer: &'f Self::Framebuffer) -> &'f Self::Texture { - &framebuffer.texture - } - - #[inline] - fn texture_size(&self, texture: &Self::Texture) -> Point2DI32 { - texture.size - } - - fn upload_to_texture(&self, texture: &Self::Texture, size: Point2DI32, data: &[u8]) { - assert!(data.len() >= size.x() as usize * size.y() as usize * 4); - unsafe { - self.bind_texture(texture, 0); - gl::TexImage2D(gl::TEXTURE_2D, - 0, - gl::RGBA as GLint, - size.x() as GLsizei, - size.y() as GLsizei, - 0, - gl::RGBA, - gl::UNSIGNED_BYTE, - data.as_ptr() as *const GLvoid); ck(); - } - - self.set_texture_parameters(texture); - } - - fn read_pixels_from_default_framebuffer(&self, size: Point2DI32) -> Vec { - let mut pixels = vec![0; size.x() as usize * size.y() as usize * 4]; - unsafe { - gl::BindFramebuffer(gl::FRAMEBUFFER, 0); ck(); - gl::ReadPixels(0, - 0, - size.x() as GLsizei, - size.y() as GLsizei, - gl::RGBA, - gl::UNSIGNED_BYTE, - pixels.as_mut_ptr() as *mut GLvoid); ck(); - } - - // Flip right-side-up. - let stride = size.x() as usize * 4; - for y in 0..(size.y() as usize / 2) { - let (index_a, index_b) = (y * stride, (size.y() as usize - y - 1) * stride); - for offset in 0..stride { - pixels.swap(index_a + offset, index_b + offset); - } - } - - pixels - } - - // TODO(pcwalton): Switch to `ColorF`! - fn clear(&self, color: Option, depth: Option, stencil: Option) { - unsafe { - let mut flags = 0; - if let Some(color) = color { - gl::ColorMask(gl::TRUE, gl::TRUE, gl::TRUE, gl::TRUE); ck(); - gl::ClearColor(color.x(), color.y(), color.z(), color.w()); ck(); - flags |= gl::COLOR_BUFFER_BIT; - } - if let Some(depth) = depth { - gl::DepthMask(gl::TRUE); ck(); - gl::ClearDepth(depth as GLdouble); ck(); - flags |= gl::DEPTH_BUFFER_BIT; - } - if let Some(stencil) = stencil { - gl::StencilMask(!0); ck(); - gl::ClearStencil(stencil as GLint); ck(); - flags |= gl::STENCIL_BUFFER_BIT; - } - if flags != 0 { - gl::Clear(flags); ck(); - } - } - } - - fn draw_arrays(&self, primitive: Primitive, index_count: u32, render_state: &RenderState) { - self.set_render_state(render_state); - unsafe { - gl::DrawArrays(primitive.to_gl_primitive(), 0, index_count as GLsizei); ck(); - } - self.reset_render_state(render_state); - } - - fn draw_elements(&self, primitive: Primitive, index_count: u32, render_state: &RenderState) { - self.set_render_state(render_state); - unsafe { - gl::DrawElements(primitive.to_gl_primitive(), - index_count as GLsizei, - gl::UNSIGNED_INT, - ptr::null()); ck(); - } - self.reset_render_state(render_state); - } - - fn draw_arrays_instanced(&self, - primitive: Primitive, - index_count: u32, - instance_count: u32, - render_state: &RenderState) { - self.set_render_state(render_state); - unsafe { - gl::DrawArraysInstanced(primitive.to_gl_primitive(), - 0, - index_count as GLsizei, - instance_count as GLsizei); ck(); - } - self.reset_render_state(render_state); - } - - #[inline] - fn create_timer_query(&self) -> GLTimerQuery { - let mut query = GLTimerQuery { gl_query: 0 }; - unsafe { - gl::GenQueries(1, &mut query.gl_query); ck(); - } - query - } - - #[inline] - fn begin_timer_query(&self, query: &Self::TimerQuery) { - unsafe { - gl::BeginQuery(gl::TIME_ELAPSED, query.gl_query); ck(); - } - } - - #[inline] - fn end_timer_query(&self, _: &Self::TimerQuery) { - unsafe { - gl::EndQuery(gl::TIME_ELAPSED); ck(); - } - } - - #[inline] - fn timer_query_is_available(&self, query: &Self::TimerQuery) -> bool { - unsafe { - let mut result = 0; - gl::GetQueryObjectiv(query.gl_query, gl::QUERY_RESULT_AVAILABLE, &mut result); ck(); - result != gl::FALSE as GLint - } - } - - #[inline] - fn get_timer_query(&self, query: &Self::TimerQuery) -> Duration { - unsafe { - let mut result = 0; - gl::GetQueryObjectui64v(query.gl_query, gl::QUERY_RESULT, &mut result); ck(); - Duration::from_nanos(result) - } - } - - #[inline] - fn bind_vertex_array(&self, vertex_array: &GLVertexArray) { - unsafe { - gl::BindVertexArray(vertex_array.gl_vertex_array); ck(); - } - } - - #[inline] - fn bind_buffer(&self, buffer: &GLBuffer, target: BufferTarget) { - unsafe { - gl::BindBuffer(target.to_gl_target(), buffer.gl_buffer); ck(); - } - } - - #[inline] - fn bind_default_framebuffer(&self, size: Point2DI32) { - unsafe { - gl::BindFramebuffer(gl::FRAMEBUFFER, 0); ck(); - gl::Viewport(0, 0, size.x(), size.y()); ck(); - } - } - - #[inline] - fn bind_framebuffer(&self, framebuffer: &GLFramebuffer) { - unsafe { - gl::BindFramebuffer(gl::FRAMEBUFFER, framebuffer.gl_framebuffer); ck(); - gl::Viewport(0, 0, framebuffer.texture.size.x(), framebuffer.texture.size.y()); ck(); - } - } - - #[inline] - fn bind_texture(&self, texture: &GLTexture, unit: u32) { - unsafe { - gl::ActiveTexture(gl::TEXTURE0 + unit); ck(); - gl::BindTexture(gl::TEXTURE_2D, texture.gl_texture); ck(); - } - } -} - -pub struct GLVertexArray { - pub gl_vertex_array: GLuint, -} - -impl Drop for GLVertexArray { - #[inline] - fn drop(&mut self) { - unsafe { - gl::DeleteVertexArrays(1, &mut self.gl_vertex_array); ck(); - } - } -} - -pub struct GLVertexAttr { - attr: GLuint, -} - -impl GLVertexAttr { - pub fn configure_float(&self, - size: GLint, - gl_type: GLuint, - normalized: bool, - stride: GLsizei, - offset: usize, - divisor: GLuint) { - unsafe { - gl::VertexAttribPointer(self.attr, - size, - gl_type, - if normalized { gl::TRUE } else { gl::FALSE }, - stride, - offset as *const GLvoid); ck(); - gl::VertexAttribDivisor(self.attr, divisor); ck(); - gl::EnableVertexAttribArray(self.attr); ck(); - } - } - - pub fn configure_int(&self, - size: GLint, - gl_type: GLuint, - stride: GLsizei, - offset: usize, - divisor: GLuint) { - unsafe { - gl::VertexAttribIPointer(self.attr, - size, - gl_type, - stride, - offset as *const GLvoid); ck(); - gl::VertexAttribDivisor(self.attr, divisor); ck(); - gl::EnableVertexAttribArray(self.attr); ck(); - } - } -} - -pub struct GLFramebuffer { - pub gl_framebuffer: GLuint, - pub texture: GLTexture, -} - -impl Drop for GLFramebuffer { - fn drop(&mut self) { - unsafe { - gl::DeleteFramebuffers(1, &mut self.gl_framebuffer); ck(); - } - } -} - -pub struct GLBuffer { - pub gl_buffer: GLuint, -} - -impl Drop for GLBuffer { - fn drop(&mut self) { - unsafe { - gl::DeleteBuffers(1, &mut self.gl_buffer); ck(); - } - } -} - -#[derive(Debug)] -pub struct GLUniform { - pub location: GLint, -} - -pub struct GLProgram { - pub gl_program: GLuint, - #[allow(dead_code)] - vertex_shader: GLShader, - #[allow(dead_code)] - fragment_shader: GLShader, -} - -impl Drop for GLProgram { - fn drop(&mut self) { - unsafe { - gl::DeleteProgram(self.gl_program); ck(); - } - } -} - -pub struct GLShader { - gl_shader: GLuint, -} - -impl Drop for GLShader { - fn drop(&mut self) { - unsafe { - gl::DeleteShader(self.gl_shader); ck(); - } - } -} - -pub struct GLTexture { - gl_texture: GLuint, - pub size: Point2DI32, -} - -pub struct GLTimerQuery { - gl_query: GLuint, -} - -impl Drop for GLTimerQuery { - #[inline] - fn drop(&mut self) { - unsafe { - gl::DeleteQueries(1, &mut self.gl_query); ck(); - } - } -} - -trait BufferTargetExt { - fn to_gl_target(self) -> GLuint; -} - -impl BufferTargetExt for BufferTarget { - fn to_gl_target(self) -> GLuint { - match self { - BufferTarget::Vertex => gl::ARRAY_BUFFER, - BufferTarget::Index => gl::ELEMENT_ARRAY_BUFFER, - } - } -} - -trait DepthFuncExt { - fn to_gl_depth_func(self) -> GLenum; -} - -impl DepthFuncExt for DepthFunc { - fn to_gl_depth_func(self) -> GLenum { - match self { - DepthFunc::Less => gl::LESS, - DepthFunc::Always => gl::ALWAYS, - } - } -} - -trait PrimitiveExt { - fn to_gl_primitive(self) -> GLuint; -} - -impl PrimitiveExt for Primitive { - fn to_gl_primitive(self) -> GLuint { - match self { - Primitive::Triangles => gl::TRIANGLES, - Primitive::TriangleFan => gl::TRIANGLE_FAN, - Primitive::Lines => gl::LINES, - } - } -} - -trait StencilFuncExt { - fn to_gl_stencil_func(self) -> GLenum; -} - -impl StencilFuncExt for StencilFunc { - fn to_gl_stencil_func(self) -> GLenum { - match self { - StencilFunc::Always => gl::ALWAYS, - StencilFunc::Equal => gl::EQUAL, - StencilFunc::NotEqual => gl::NOTEQUAL, - } - } -} - -trait VertexAttrTypeExt { - fn to_gl_type(self) -> GLuint; -} - -impl VertexAttrTypeExt for VertexAttrType { - fn to_gl_type(self) -> GLuint { - match self { - VertexAttrType::F32 => gl::FLOAT, - VertexAttrType::I16 => gl::SHORT, - VertexAttrType::U16 => gl::UNSIGNED_SHORT, - VertexAttrType::U8 => gl::UNSIGNED_BYTE, - } - } -} - -// Error checking - -#[cfg(debug)] -fn ck() { - unsafe { - let err = gl::GetError(); - if err != 0 { - panic!("GL error: 0x{:x}", err); - } - } -} - -#[cfg(not(debug))] -fn ck() {} diff --git a/gl/src/lib.rs b/gl/src/lib.rs index 2adbe251..d284c683 100644 --- a/gl/src/lib.rs +++ b/gl/src/lib.rs @@ -8,11 +8,806 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! An OpenGL backend for Pathfinder. -//! -//! It's not necessary to use this crate to render vector graphics with -//! Pathfinder; you can use the `pathfinder_renderer` crate and do the GPU -//! rendering yourself using the API or engine of your choice. This crate can -//! be useful for simple use cases, however. +//! An OpenGL implementation of the device abstraction. -pub mod device; +use gl::types::{GLboolean, GLchar, GLdouble, GLenum, GLfloat, GLint, GLsizei, GLsizeiptr}; +use gl::types::{GLuint, GLvoid}; +use pathfinder_geometry::basic::point::Point2DI32; +use pathfinder_gpu::{BlendState, BufferTarget, BufferUploadMode, DepthFunc, Device, Primitive}; +use pathfinder_gpu::{RenderState, ShaderKind, StencilFunc, TextureFormat}; +use pathfinder_gpu::{UniformData, VertexAttrType}; +use pathfinder_simd::default::F32x4; +use std::ffi::CString; +use std::mem; +use std::ptr; +use std::time::Duration; + +pub struct GLDevice; + +impl GLDevice { + #[inline] + pub fn new() -> GLDevice { GLDevice } + + fn set_texture_parameters(&self, texture: &GLTexture) { + self.bind_texture(texture, 0); + unsafe { + gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as GLint); ck(); + gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as GLint); ck(); + gl::TexParameteri(gl::TEXTURE_2D, + gl::TEXTURE_WRAP_S, + gl::CLAMP_TO_EDGE as GLint); ck(); + gl::TexParameteri(gl::TEXTURE_2D, + gl::TEXTURE_WRAP_T, + gl::CLAMP_TO_EDGE as GLint); ck(); + } + } + + fn set_render_state(&self, render_state: &RenderState) { + unsafe { + // Set blend. + match render_state.blend { + BlendState::Off => { + gl::Disable(gl::BLEND); ck(); + } + BlendState::RGBOneAlphaOne => { + gl::BlendEquation(gl::FUNC_ADD); ck(); + gl::BlendFunc(gl::ONE, gl::ONE); ck(); + gl::Enable(gl::BLEND); ck(); + } + BlendState::RGBOneAlphaOneMinusSrcAlpha => { + gl::BlendEquation(gl::FUNC_ADD); ck(); + gl::BlendFuncSeparate(gl::ONE, + gl::ONE_MINUS_SRC_ALPHA, + gl::ONE, + gl::ONE); ck(); + gl::Enable(gl::BLEND); ck(); + } + BlendState::RGBSrcAlphaAlphaOneMinusSrcAlpha => { + gl::BlendEquation(gl::FUNC_ADD); ck(); + gl::BlendFuncSeparate(gl::SRC_ALPHA, + gl::ONE_MINUS_SRC_ALPHA, + gl::ONE, + gl::ONE); ck(); + gl::Enable(gl::BLEND); ck(); + } + } + + // Set depth. + match render_state.depth { + None => { + gl::Disable(gl::DEPTH_TEST); ck(); + } + Some(ref state) => { + gl::DepthFunc(state.func.to_gl_depth_func()); ck(); + gl::DepthMask(state.write as GLboolean); ck(); + gl::Enable(gl::DEPTH_TEST); ck(); + } + } + + // Set stencil. + match render_state.stencil { + None => { + gl::Disable(gl::STENCIL_TEST); ck(); + } + Some(ref state) => { + gl::StencilFunc(state.func.to_gl_stencil_func(), + state.reference as GLint, + state.mask); ck(); + let (pass_action, write_mask) = if state.write { + (gl::REPLACE, state.mask) + } else { + (gl::KEEP, 0) + }; + gl::StencilOp(gl::KEEP, gl::KEEP, pass_action); ck(); + gl::StencilMask(write_mask); + gl::Enable(gl::STENCIL_TEST); ck(); + } + } + + // Set color mask. + let color_mask = render_state.color_mask as GLboolean; + gl::ColorMask(color_mask, color_mask, color_mask, color_mask); ck(); + } + } + + fn reset_render_state(&self, render_state: &RenderState) { + unsafe { + match render_state.blend { + BlendState::Off => {} + BlendState::RGBOneAlphaOneMinusSrcAlpha | + BlendState::RGBOneAlphaOne | + BlendState::RGBSrcAlphaAlphaOneMinusSrcAlpha => { + gl::Disable(gl::BLEND); ck(); + } + } + + if render_state.depth.is_some() { + gl::Disable(gl::DEPTH_TEST); ck(); + } + + if render_state.stencil.is_some() { + gl::StencilMask(!0); ck(); + gl::Disable(gl::STENCIL_TEST); ck(); + } + + gl::ColorMask(gl::TRUE, gl::TRUE, gl::TRUE, gl::TRUE); ck(); + } + } +} + +impl Device for GLDevice { + type Buffer = GLBuffer; + type Framebuffer = GLFramebuffer; + type Program = GLProgram; + type Shader = GLShader; + type Texture = GLTexture; + type TimerQuery = GLTimerQuery; + type Uniform = GLUniform; + type VertexArray = GLVertexArray; + type VertexAttr = GLVertexAttr; + + fn create_texture(&self, format: TextureFormat, size: Point2DI32) -> GLTexture { + let (gl_internal_format, gl_format, gl_type); + match format { + TextureFormat::R16F => { + gl_internal_format = gl::R16F as GLint; + gl_format = gl::RED; + gl_type = gl::HALF_FLOAT; + } + TextureFormat::RGBA8 => { + gl_internal_format = gl::RGBA as GLint; + gl_format = gl::RGBA; + gl_type = gl::UNSIGNED_BYTE; + } + } + + let mut texture = GLTexture { gl_texture: 0, size }; + unsafe { + gl::GenTextures(1, &mut texture.gl_texture); ck(); + self.bind_texture(&texture, 0); + gl::TexImage2D(gl::TEXTURE_2D, + 0, + gl_internal_format, + size.x() as GLsizei, + size.y() as GLsizei, + 0, + gl_format, + gl_type, + ptr::null()); ck(); + } + + self.set_texture_parameters(&texture); + texture + } + + fn create_texture_from_data(&self, size: Point2DI32, data: &[u8]) -> GLTexture { + assert!(data.len() >= size.x() as usize * size.y() as usize); + + let mut texture = GLTexture { gl_texture: 0, size }; + unsafe { + gl::GenTextures(1, &mut texture.gl_texture); ck(); + self.bind_texture(&texture, 0); + gl::TexImage2D(gl::TEXTURE_2D, + 0, + gl::RED as GLint, + size.x() as GLsizei, + size.y() as GLsizei, + 0, + gl::RED, + gl::UNSIGNED_BYTE, + data.as_ptr() as *const GLvoid); ck(); + } + + self.set_texture_parameters(&texture); + texture + } + + fn create_shader_from_source(&self, name: &str, source: &[u8], kind: ShaderKind) -> GLShader { + let gl_shader_kind = match kind { + ShaderKind::Vertex => gl::VERTEX_SHADER, + ShaderKind::Fragment => gl::FRAGMENT_SHADER, + }; + + unsafe { + let gl_shader = gl::CreateShader(gl_shader_kind); ck(); + gl::ShaderSource(gl_shader, + 1, + [source.as_ptr() as *const GLchar].as_ptr(), + [source.len() as GLint].as_ptr()); ck(); + gl::CompileShader(gl_shader); ck(); + + let mut compile_status = 0; + gl::GetShaderiv(gl_shader, gl::COMPILE_STATUS, &mut compile_status); ck(); + if compile_status != gl::TRUE as GLint { + let mut info_log_length = 0; + gl::GetShaderiv(gl_shader, gl::INFO_LOG_LENGTH, &mut info_log_length); ck(); + let mut info_log = vec![0; info_log_length as usize]; + gl::GetShaderInfoLog(gl_shader, + info_log.len() as GLint, + ptr::null_mut(), + info_log.as_mut_ptr() as *mut GLchar); ck(); + eprintln!("Shader info log:\n{}", String::from_utf8_lossy(&info_log)); + panic!("{:?} shader '{}' compilation failed", kind, name); + } + + GLShader { gl_shader } + } + } + + fn create_program_from_shaders(&self, + name: &str, + vertex_shader: GLShader, + fragment_shader: GLShader) + -> GLProgram { + let gl_program; + unsafe { + gl_program = gl::CreateProgram(); ck(); + gl::AttachShader(gl_program, vertex_shader.gl_shader); ck(); + gl::AttachShader(gl_program, fragment_shader.gl_shader); ck(); + gl::LinkProgram(gl_program); ck(); + + let mut link_status = 0; + gl::GetProgramiv(gl_program, gl::LINK_STATUS, &mut link_status); ck(); + if link_status != gl::TRUE as GLint { + let mut info_log_length = 0; + gl::GetProgramiv(gl_program, gl::INFO_LOG_LENGTH, &mut info_log_length); ck(); + let mut info_log = vec![0; info_log_length as usize]; + gl::GetProgramInfoLog(gl_program, + info_log.len() as GLint, + ptr::null_mut(), + info_log.as_mut_ptr() as *mut GLchar); ck(); + eprintln!("Program info log:\n{}", String::from_utf8_lossy(&info_log)); + panic!("Program '{}' linking failed", name); + } + } + + GLProgram { gl_program, vertex_shader, fragment_shader } + } + + #[inline] + fn create_vertex_array(&self) -> GLVertexArray { + unsafe { + let mut array = GLVertexArray { gl_vertex_array: 0 }; + gl::GenVertexArrays(1, &mut array.gl_vertex_array); ck(); + array + } + } + + fn get_vertex_attr(&self, program: &Self::Program, name: &str) -> GLVertexAttr { + let name = CString::new(format!("a{}", name)).unwrap(); + let attr = unsafe { + gl::GetAttribLocation(program.gl_program, name.as_ptr() as *const GLchar) as GLuint + }; ck(); + GLVertexAttr { attr } + } + + fn get_uniform(&self, program: &GLProgram, name: &str) -> GLUniform { + let name = CString::new(format!("u{}", name)).unwrap(); + let location = unsafe { + gl::GetUniformLocation(program.gl_program, name.as_ptr() as *const GLchar) + }; ck(); + GLUniform { location } + } + + fn use_program(&self, program: &Self::Program) { + unsafe { + gl::UseProgram(program.gl_program); ck(); + } + } + + fn configure_float_vertex_attr(&self, + attr: &GLVertexAttr, + size: usize, + attr_type: VertexAttrType, + normalized: bool, + stride: usize, + offset: usize, + divisor: u32) { + unsafe { + gl::VertexAttribPointer(attr.attr, + size as GLint, + attr_type.to_gl_type(), + if normalized { gl::TRUE } else { gl::FALSE }, + stride as GLint, + offset as *const GLvoid); ck(); + gl::VertexAttribDivisor(attr.attr, divisor); ck(); + gl::EnableVertexAttribArray(attr.attr); ck(); + } + } + + fn configure_int_vertex_attr(&self, + attr: &GLVertexAttr, + size: usize, + attr_type: VertexAttrType, + stride: usize, + offset: usize, + divisor: u32) { + unsafe { + gl::VertexAttribIPointer(attr.attr, + size as GLint, + attr_type.to_gl_type(), + stride as GLint, + offset as *const GLvoid); ck(); + gl::VertexAttribDivisor(attr.attr, divisor); ck(); + gl::EnableVertexAttribArray(attr.attr); ck(); + } + } + + fn set_uniform(&self, uniform: &Self::Uniform, data: UniformData) { + unsafe { + match data { + UniformData::Mat4(data) => { + assert_eq!(mem::size_of::<[F32x4; 4]>(), 4 * 4 * 4); + let data_ptr: *const F32x4 = data.as_ptr(); + gl::UniformMatrix4fv(uniform.location, + 1, + gl::FALSE, + data_ptr as *const GLfloat); + } + UniformData::Vec2(data) => { + gl::Uniform2f(uniform.location, data.x(), data.y()); ck(); + } + UniformData::Vec4(data) => { + gl::Uniform4f(uniform.location, data.x(), data.y(), data.z(), data.w()); ck(); + } + UniformData::TextureUnit(unit) => { + gl::Uniform1i(uniform.location, unit as GLint); ck(); + } + } + } + } + + fn create_framebuffer(&self, texture: GLTexture) -> GLFramebuffer { + let mut gl_framebuffer = 0; + unsafe { + gl::GenFramebuffers(1, &mut gl_framebuffer); ck(); + assert_eq!(gl::GetError(), gl::NO_ERROR); + gl::BindFramebuffer(gl::FRAMEBUFFER, gl_framebuffer); ck(); + self.bind_texture(&texture, 0); + gl::FramebufferTexture2D(gl::FRAMEBUFFER, + gl::COLOR_ATTACHMENT0, + gl::TEXTURE_2D, + texture.gl_texture, + 0); ck(); + assert_eq!(gl::CheckFramebufferStatus(gl::FRAMEBUFFER), gl::FRAMEBUFFER_COMPLETE); + } + + GLFramebuffer { gl_framebuffer, texture } + } + + fn create_buffer(&self) -> GLBuffer { + unsafe { + let mut gl_buffer = 0; + gl::GenBuffers(1, &mut gl_buffer); ck(); + GLBuffer { gl_buffer } + } + } + + fn upload_to_buffer(&self, + buffer: &GLBuffer, + data: &[T], + target: BufferTarget, + mode: BufferUploadMode) { + let target = match target { + BufferTarget::Vertex => gl::ARRAY_BUFFER, + BufferTarget::Index => gl::ELEMENT_ARRAY_BUFFER, + }; + let mode = match mode { + BufferUploadMode::Static => gl::STATIC_DRAW, + BufferUploadMode::Dynamic => gl::DYNAMIC_DRAW, + }; + unsafe { + gl::BindBuffer(target, buffer.gl_buffer); ck(); + gl::BufferData(target, + (data.len() * mem::size_of::()) as GLsizeiptr, + data.as_ptr() as *const GLvoid, + mode); ck(); + } + } + + #[inline] + fn framebuffer_texture<'f>(&self, framebuffer: &'f Self::Framebuffer) -> &'f Self::Texture { + &framebuffer.texture + } + + #[inline] + fn texture_size(&self, texture: &Self::Texture) -> Point2DI32 { + texture.size + } + + fn upload_to_texture(&self, texture: &Self::Texture, size: Point2DI32, data: &[u8]) { + assert!(data.len() >= size.x() as usize * size.y() as usize * 4); + unsafe { + self.bind_texture(texture, 0); + gl::TexImage2D(gl::TEXTURE_2D, + 0, + gl::RGBA as GLint, + size.x() as GLsizei, + size.y() as GLsizei, + 0, + gl::RGBA, + gl::UNSIGNED_BYTE, + data.as_ptr() as *const GLvoid); ck(); + } + + self.set_texture_parameters(texture); + } + + fn read_pixels_from_default_framebuffer(&self, size: Point2DI32) -> Vec { + let mut pixels = vec![0; size.x() as usize * size.y() as usize * 4]; + unsafe { + gl::BindFramebuffer(gl::FRAMEBUFFER, 0); ck(); + gl::ReadPixels(0, + 0, + size.x() as GLsizei, + size.y() as GLsizei, + gl::RGBA, + gl::UNSIGNED_BYTE, + pixels.as_mut_ptr() as *mut GLvoid); ck(); + } + + // Flip right-side-up. + let stride = size.x() as usize * 4; + for y in 0..(size.y() as usize / 2) { + let (index_a, index_b) = (y * stride, (size.y() as usize - y - 1) * stride); + for offset in 0..stride { + pixels.swap(index_a + offset, index_b + offset); + } + } + + pixels + } + + // TODO(pcwalton): Switch to `ColorF`! + fn clear(&self, color: Option, depth: Option, stencil: Option) { + unsafe { + let mut flags = 0; + if let Some(color) = color { + gl::ColorMask(gl::TRUE, gl::TRUE, gl::TRUE, gl::TRUE); ck(); + gl::ClearColor(color.x(), color.y(), color.z(), color.w()); ck(); + flags |= gl::COLOR_BUFFER_BIT; + } + if let Some(depth) = depth { + gl::DepthMask(gl::TRUE); ck(); + gl::ClearDepth(depth as GLdouble); ck(); + flags |= gl::DEPTH_BUFFER_BIT; + } + if let Some(stencil) = stencil { + gl::StencilMask(!0); ck(); + gl::ClearStencil(stencil as GLint); ck(); + flags |= gl::STENCIL_BUFFER_BIT; + } + if flags != 0 { + gl::Clear(flags); ck(); + } + } + } + + fn draw_arrays(&self, primitive: Primitive, index_count: u32, render_state: &RenderState) { + self.set_render_state(render_state); + unsafe { + gl::DrawArrays(primitive.to_gl_primitive(), 0, index_count as GLsizei); ck(); + } + self.reset_render_state(render_state); + } + + fn draw_elements(&self, primitive: Primitive, index_count: u32, render_state: &RenderState) { + self.set_render_state(render_state); + unsafe { + gl::DrawElements(primitive.to_gl_primitive(), + index_count as GLsizei, + gl::UNSIGNED_INT, + ptr::null()); ck(); + } + self.reset_render_state(render_state); + } + + fn draw_arrays_instanced(&self, + primitive: Primitive, + index_count: u32, + instance_count: u32, + render_state: &RenderState) { + self.set_render_state(render_state); + unsafe { + gl::DrawArraysInstanced(primitive.to_gl_primitive(), + 0, + index_count as GLsizei, + instance_count as GLsizei); ck(); + } + self.reset_render_state(render_state); + } + + #[inline] + fn create_timer_query(&self) -> GLTimerQuery { + let mut query = GLTimerQuery { gl_query: 0 }; + unsafe { + gl::GenQueries(1, &mut query.gl_query); ck(); + } + query + } + + #[inline] + fn begin_timer_query(&self, query: &Self::TimerQuery) { + unsafe { + gl::BeginQuery(gl::TIME_ELAPSED, query.gl_query); ck(); + } + } + + #[inline] + fn end_timer_query(&self, _: &Self::TimerQuery) { + unsafe { + gl::EndQuery(gl::TIME_ELAPSED); ck(); + } + } + + #[inline] + fn timer_query_is_available(&self, query: &Self::TimerQuery) -> bool { + unsafe { + let mut result = 0; + gl::GetQueryObjectiv(query.gl_query, gl::QUERY_RESULT_AVAILABLE, &mut result); ck(); + result != gl::FALSE as GLint + } + } + + #[inline] + fn get_timer_query(&self, query: &Self::TimerQuery) -> Duration { + unsafe { + let mut result = 0; + gl::GetQueryObjectui64v(query.gl_query, gl::QUERY_RESULT, &mut result); ck(); + Duration::from_nanos(result) + } + } + + #[inline] + fn bind_vertex_array(&self, vertex_array: &GLVertexArray) { + unsafe { + gl::BindVertexArray(vertex_array.gl_vertex_array); ck(); + } + } + + #[inline] + fn bind_buffer(&self, buffer: &GLBuffer, target: BufferTarget) { + unsafe { + gl::BindBuffer(target.to_gl_target(), buffer.gl_buffer); ck(); + } + } + + #[inline] + fn bind_default_framebuffer(&self, size: Point2DI32) { + unsafe { + gl::BindFramebuffer(gl::FRAMEBUFFER, 0); ck(); + gl::Viewport(0, 0, size.x(), size.y()); ck(); + } + } + + #[inline] + fn bind_framebuffer(&self, framebuffer: &GLFramebuffer) { + unsafe { + gl::BindFramebuffer(gl::FRAMEBUFFER, framebuffer.gl_framebuffer); ck(); + gl::Viewport(0, 0, framebuffer.texture.size.x(), framebuffer.texture.size.y()); ck(); + } + } + + #[inline] + fn bind_texture(&self, texture: &GLTexture, unit: u32) { + unsafe { + gl::ActiveTexture(gl::TEXTURE0 + unit); ck(); + gl::BindTexture(gl::TEXTURE_2D, texture.gl_texture); ck(); + } + } +} + +pub struct GLVertexArray { + pub gl_vertex_array: GLuint, +} + +impl Drop for GLVertexArray { + #[inline] + fn drop(&mut self) { + unsafe { + gl::DeleteVertexArrays(1, &mut self.gl_vertex_array); ck(); + } + } +} + +pub struct GLVertexAttr { + attr: GLuint, +} + +impl GLVertexAttr { + pub fn configure_float(&self, + size: GLint, + gl_type: GLuint, + normalized: bool, + stride: GLsizei, + offset: usize, + divisor: GLuint) { + unsafe { + gl::VertexAttribPointer(self.attr, + size, + gl_type, + if normalized { gl::TRUE } else { gl::FALSE }, + stride, + offset as *const GLvoid); ck(); + gl::VertexAttribDivisor(self.attr, divisor); ck(); + gl::EnableVertexAttribArray(self.attr); ck(); + } + } + + pub fn configure_int(&self, + size: GLint, + gl_type: GLuint, + stride: GLsizei, + offset: usize, + divisor: GLuint) { + unsafe { + gl::VertexAttribIPointer(self.attr, + size, + gl_type, + stride, + offset as *const GLvoid); ck(); + gl::VertexAttribDivisor(self.attr, divisor); ck(); + gl::EnableVertexAttribArray(self.attr); ck(); + } + } +} + +pub struct GLFramebuffer { + pub gl_framebuffer: GLuint, + pub texture: GLTexture, +} + +impl Drop for GLFramebuffer { + fn drop(&mut self) { + unsafe { + gl::DeleteFramebuffers(1, &mut self.gl_framebuffer); ck(); + } + } +} + +pub struct GLBuffer { + pub gl_buffer: GLuint, +} + +impl Drop for GLBuffer { + fn drop(&mut self) { + unsafe { + gl::DeleteBuffers(1, &mut self.gl_buffer); ck(); + } + } +} + +#[derive(Debug)] +pub struct GLUniform { + pub location: GLint, +} + +pub struct GLProgram { + pub gl_program: GLuint, + #[allow(dead_code)] + vertex_shader: GLShader, + #[allow(dead_code)] + fragment_shader: GLShader, +} + +impl Drop for GLProgram { + fn drop(&mut self) { + unsafe { + gl::DeleteProgram(self.gl_program); ck(); + } + } +} + +pub struct GLShader { + gl_shader: GLuint, +} + +impl Drop for GLShader { + fn drop(&mut self) { + unsafe { + gl::DeleteShader(self.gl_shader); ck(); + } + } +} + +pub struct GLTexture { + gl_texture: GLuint, + pub size: Point2DI32, +} + +pub struct GLTimerQuery { + gl_query: GLuint, +} + +impl Drop for GLTimerQuery { + #[inline] + fn drop(&mut self) { + unsafe { + gl::DeleteQueries(1, &mut self.gl_query); ck(); + } + } +} + +trait BufferTargetExt { + fn to_gl_target(self) -> GLuint; +} + +impl BufferTargetExt for BufferTarget { + fn to_gl_target(self) -> GLuint { + match self { + BufferTarget::Vertex => gl::ARRAY_BUFFER, + BufferTarget::Index => gl::ELEMENT_ARRAY_BUFFER, + } + } +} + +trait DepthFuncExt { + fn to_gl_depth_func(self) -> GLenum; +} + +impl DepthFuncExt for DepthFunc { + fn to_gl_depth_func(self) -> GLenum { + match self { + DepthFunc::Less => gl::LESS, + DepthFunc::Always => gl::ALWAYS, + } + } +} + +trait PrimitiveExt { + fn to_gl_primitive(self) -> GLuint; +} + +impl PrimitiveExt for Primitive { + fn to_gl_primitive(self) -> GLuint { + match self { + Primitive::Triangles => gl::TRIANGLES, + Primitive::TriangleFan => gl::TRIANGLE_FAN, + Primitive::Lines => gl::LINES, + } + } +} + +trait StencilFuncExt { + fn to_gl_stencil_func(self) -> GLenum; +} + +impl StencilFuncExt for StencilFunc { + fn to_gl_stencil_func(self) -> GLenum { + match self { + StencilFunc::Always => gl::ALWAYS, + StencilFunc::Equal => gl::EQUAL, + StencilFunc::NotEqual => gl::NOTEQUAL, + } + } +} + +trait VertexAttrTypeExt { + fn to_gl_type(self) -> GLuint; +} + +impl VertexAttrTypeExt for VertexAttrType { + fn to_gl_type(self) -> GLuint { + match self { + VertexAttrType::F32 => gl::FLOAT, + VertexAttrType::I16 => gl::SHORT, + VertexAttrType::U16 => gl::UNSIGNED_SHORT, + VertexAttrType::U8 => gl::UNSIGNED_BYTE, + } + } +} + +// Error checking + +#[cfg(debug)] +fn ck() { + unsafe { + let err = gl::GetError(); + if err != 0 { + panic!("GL error: 0x{:x}", err); + } + } +} + +#[cfg(not(debug))] +fn ck() {}