2019-01-30 22:31:29 -05:00
|
|
|
// pathfinder/gl/src/lib.rs
|
|
|
|
//
|
|
|
|
// Copyright © 2019 The Pathfinder Project Developers.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
2019-03-05 17:51:29 -05:00
|
|
|
//! An OpenGL implementation of the device abstraction.
|
|
|
|
|
2019-04-29 19:43:24 -04:00
|
|
|
#[macro_use]
|
|
|
|
extern crate log;
|
|
|
|
|
2020-01-07 16:03:15 -05:00
|
|
|
use gl::types::{GLboolean, GLchar, GLenum, GLfloat, GLint, GLsizei, GLsizeiptr, GLsync};
|
|
|
|
use gl::types::{GLuint, GLvoid};
|
2019-12-20 14:48:29 -05:00
|
|
|
use half::f16;
|
2019-06-21 13:06:19 -04:00
|
|
|
use pathfinder_geometry::rect::RectI;
|
|
|
|
use pathfinder_geometry::vector::Vector2I;
|
2019-05-26 16:02:52 -04:00
|
|
|
use pathfinder_gpu::resources::ResourceLoader;
|
2020-01-07 16:03:15 -05:00
|
|
|
use pathfinder_gpu::{BlendFunc, BlendOp, BufferData, BufferTarget, BufferUploadMode, ClearOps};
|
|
|
|
use pathfinder_gpu::{DepthFunc, Device, Primitive, RenderOptions, RenderState, RenderTarget};
|
2019-12-19 11:58:02 -05:00
|
|
|
use pathfinder_gpu::{ShaderKind, StencilFunc, TextureData, TextureDataRef, TextureFormat};
|
|
|
|
use pathfinder_gpu::{UniformData, VertexAttrClass, VertexAttrDescriptor, VertexAttrType};
|
2019-03-05 17:51:29 -05:00
|
|
|
use pathfinder_simd::default::F32x4;
|
|
|
|
use std::ffi::CString;
|
|
|
|
use std::mem;
|
|
|
|
use std::ptr;
|
2019-03-08 19:52:47 -05:00
|
|
|
use std::str;
|
2019-03-05 17:51:29 -05:00
|
|
|
use std::time::Duration;
|
2019-04-29 19:43:24 -04:00
|
|
|
|
2019-03-08 19:52:47 -05:00
|
|
|
pub struct GLDevice {
|
|
|
|
version: GLVersion,
|
2019-03-21 14:34:59 -04:00
|
|
|
default_framebuffer: GLuint,
|
2019-03-08 19:52:47 -05:00
|
|
|
}
|
2019-03-05 17:51:29 -05:00
|
|
|
|
|
|
|
impl GLDevice {
|
|
|
|
#[inline]
|
2019-03-21 14:34:59 -04:00
|
|
|
pub fn new(version: GLVersion, default_framebuffer: GLuint) -> GLDevice {
|
|
|
|
GLDevice {
|
|
|
|
version,
|
|
|
|
default_framebuffer,
|
|
|
|
}
|
2019-03-08 19:52:47 -05:00
|
|
|
}
|
2019-03-05 17:51:29 -05:00
|
|
|
|
2019-05-27 07:56:36 -04:00
|
|
|
pub fn set_default_framebuffer(&mut self, framebuffer: GLuint) {
|
|
|
|
self.default_framebuffer = framebuffer;
|
|
|
|
}
|
|
|
|
|
2019-03-05 17:51:29 -05:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-05 17:35:46 -04:00
|
|
|
fn set_render_state(&self, render_state: &RenderState<GLDevice>) {
|
|
|
|
self.bind_render_target(render_state.target);
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
let (origin, size) = (render_state.viewport.origin(), render_state.viewport.size());
|
|
|
|
gl::Viewport(origin.x(), origin.y(), size.x(), size.y());
|
|
|
|
}
|
|
|
|
|
|
|
|
if render_state.options.clear_ops.has_ops() {
|
|
|
|
self.clear(&render_state.options.clear_ops);
|
|
|
|
}
|
|
|
|
|
|
|
|
self.use_program(render_state.program);
|
|
|
|
self.bind_vertex_array(render_state.vertex_array);
|
|
|
|
for (texture_unit, texture) in render_state.textures.iter().enumerate() {
|
|
|
|
self.bind_texture(texture, texture_unit as u32);
|
|
|
|
}
|
|
|
|
|
|
|
|
render_state.uniforms.iter().for_each(|(uniform, data)| self.set_uniform(uniform, data));
|
|
|
|
self.set_render_options(&render_state.options);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set_render_options(&self, render_options: &RenderOptions) {
|
2019-03-05 17:51:29 -05:00
|
|
|
unsafe {
|
|
|
|
// Set blend.
|
2019-06-05 17:35:46 -04:00
|
|
|
match render_options.blend {
|
2019-12-19 11:58:02 -05:00
|
|
|
None => {
|
2019-03-05 17:51:29 -05:00
|
|
|
gl::Disable(gl::BLEND); ck();
|
|
|
|
}
|
2019-12-19 11:58:02 -05:00
|
|
|
Some(ref blend) => {
|
|
|
|
match blend.func {
|
|
|
|
BlendFunc::RGBOneAlphaOne => {
|
|
|
|
gl::BlendFunc(gl::ONE, gl::ONE); ck();
|
|
|
|
}
|
|
|
|
BlendFunc::RGBOneAlphaOneMinusSrcAlpha => {
|
|
|
|
gl::BlendFuncSeparate(gl::ONE,
|
|
|
|
gl::ONE_MINUS_SRC_ALPHA,
|
|
|
|
gl::ONE,
|
|
|
|
gl::ONE); ck();
|
|
|
|
}
|
|
|
|
BlendFunc::RGBSrcAlphaAlphaOneMinusSrcAlpha => {
|
|
|
|
gl::BlendFuncSeparate(gl::SRC_ALPHA,
|
|
|
|
gl::ONE_MINUS_SRC_ALPHA,
|
|
|
|
gl::ONE,
|
|
|
|
gl::ONE); ck();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
match blend.op {
|
|
|
|
BlendOp::Add => {
|
|
|
|
gl::BlendEquation(gl::FUNC_ADD); ck();
|
|
|
|
}
|
|
|
|
BlendOp::Subtract => {
|
|
|
|
gl::BlendEquation(gl::FUNC_SUBTRACT); ck();
|
|
|
|
}
|
|
|
|
}
|
2019-03-05 17:51:29 -05:00
|
|
|
gl::Enable(gl::BLEND); ck();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set depth.
|
2019-06-05 17:35:46 -04:00
|
|
|
match render_options.depth {
|
2019-03-05 17:51:29 -05:00
|
|
|
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.
|
2019-06-05 17:35:46 -04:00
|
|
|
match render_options.stencil {
|
2019-03-05 17:51:29 -05:00
|
|
|
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.
|
2019-06-05 17:35:46 -04:00
|
|
|
let color_mask = render_options.color_mask as GLboolean;
|
2019-03-05 17:51:29 -05:00
|
|
|
gl::ColorMask(color_mask, color_mask, color_mask, color_mask); ck();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-05 17:35:46 -04:00
|
|
|
fn set_uniform(&self, uniform: &GLUniform, data: &UniformData) {
|
|
|
|
unsafe {
|
|
|
|
match *data {
|
2019-11-16 00:17:50 -05:00
|
|
|
UniformData::Float(value) => {
|
|
|
|
gl::Uniform1f(uniform.location, value); ck();
|
|
|
|
}
|
2019-06-05 17:35:46 -04:00
|
|
|
UniformData::Int(value) => {
|
|
|
|
gl::Uniform1i(uniform.location, value); ck();
|
|
|
|
}
|
2019-11-10 13:28:20 -05:00
|
|
|
UniformData::Mat2(data) => {
|
|
|
|
assert_eq!(mem::size_of::<F32x4>(), 4 * 4);
|
|
|
|
gl::UniformMatrix2fv(uniform.location,
|
|
|
|
1,
|
|
|
|
gl::FALSE,
|
|
|
|
&data as *const F32x4 as *const GLfloat);
|
|
|
|
}
|
2019-06-05 17:35:46 -04:00
|
|
|
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 reset_render_state(&self, render_state: &RenderState<GLDevice>) {
|
|
|
|
self.reset_render_options(&render_state.options);
|
|
|
|
for texture_unit in 0..(render_state.textures.len() as u32) {
|
|
|
|
self.unbind_texture(texture_unit);
|
|
|
|
}
|
|
|
|
self.unuse_program();
|
|
|
|
self.unbind_vertex_array();
|
|
|
|
}
|
|
|
|
|
|
|
|
fn reset_render_options(&self, render_options: &RenderOptions) {
|
2019-03-05 17:51:29 -05:00
|
|
|
unsafe {
|
2019-12-19 11:58:02 -05:00
|
|
|
if render_options.blend.is_some() {
|
|
|
|
gl::Disable(gl::BLEND); ck();
|
2019-03-05 17:51:29 -05:00
|
|
|
}
|
|
|
|
|
2019-06-05 17:35:46 -04:00
|
|
|
if render_options.depth.is_some() {
|
2019-03-05 17:51:29 -05:00
|
|
|
gl::Disable(gl::DEPTH_TEST); ck();
|
|
|
|
}
|
|
|
|
|
2019-06-05 17:35:46 -04:00
|
|
|
if render_options.stencil.is_some() {
|
2019-03-05 17:51:29 -05:00
|
|
|
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;
|
2020-01-07 16:03:15 -05:00
|
|
|
type TextureDataReceiver = GLTextureDataReceiver;
|
2019-03-05 17:51:29 -05:00
|
|
|
type TimerQuery = GLTimerQuery;
|
|
|
|
type Uniform = GLUniform;
|
|
|
|
type VertexArray = GLVertexArray;
|
|
|
|
type VertexAttr = GLVertexAttr;
|
|
|
|
|
2019-06-03 15:39:29 -04:00
|
|
|
fn create_texture(&self, format: TextureFormat, size: Vector2I) -> GLTexture {
|
2019-06-05 17:35:46 -04:00
|
|
|
let mut texture = GLTexture { gl_texture: 0, size, format };
|
2019-03-05 17:51:29 -05:00
|
|
|
unsafe {
|
|
|
|
gl::GenTextures(1, &mut texture.gl_texture); ck();
|
|
|
|
self.bind_texture(&texture, 0);
|
|
|
|
gl::TexImage2D(gl::TEXTURE_2D,
|
|
|
|
0,
|
2019-06-05 17:35:46 -04:00
|
|
|
format.gl_internal_format(),
|
2019-03-05 17:51:29 -05:00
|
|
|
size.x() as GLsizei,
|
|
|
|
size.y() as GLsizei,
|
|
|
|
0,
|
2019-06-05 17:35:46 -04:00
|
|
|
format.gl_format(),
|
|
|
|
format.gl_type(),
|
2019-03-05 17:51:29 -05:00
|
|
|
ptr::null()); ck();
|
|
|
|
}
|
|
|
|
|
|
|
|
self.set_texture_parameters(&texture);
|
|
|
|
texture
|
|
|
|
}
|
|
|
|
|
2019-12-19 11:58:02 -05:00
|
|
|
fn create_texture_from_data(&self, format: TextureFormat, size: Vector2I, data: TextureDataRef)
|
|
|
|
-> GLTexture {
|
2019-12-20 14:48:29 -05:00
|
|
|
let data_ptr = data.check_and_extract_data_ptr(size, format);
|
2019-06-05 17:35:46 -04:00
|
|
|
let mut texture = GLTexture { gl_texture: 0, size, format: TextureFormat::R8 };
|
2019-03-05 17:51:29 -05:00
|
|
|
unsafe {
|
|
|
|
gl::GenTextures(1, &mut texture.gl_texture); ck();
|
|
|
|
self.bind_texture(&texture, 0);
|
|
|
|
gl::TexImage2D(gl::TEXTURE_2D,
|
|
|
|
0,
|
2019-12-19 11:58:02 -05:00
|
|
|
format.gl_internal_format(),
|
2019-03-05 17:51:29 -05:00
|
|
|
size.x() as GLsizei,
|
|
|
|
size.y() as GLsizei,
|
|
|
|
0,
|
2019-12-19 11:58:02 -05:00
|
|
|
format.gl_format(),
|
|
|
|
format.gl_type(),
|
|
|
|
data_ptr)
|
2019-03-05 17:51:29 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
self.set_texture_parameters(&texture);
|
|
|
|
texture
|
|
|
|
}
|
|
|
|
|
2019-06-05 20:20:12 -04:00
|
|
|
fn create_shader_from_source(&self, name: &str, source: &[u8], kind: ShaderKind) -> GLShader {
|
2019-03-19 00:40:10 -04:00
|
|
|
// FIXME(pcwalton): Do this once and cache it.
|
2019-03-08 19:52:47 -05:00
|
|
|
let glsl_version_spec = self.version.to_glsl_version_spec();
|
2019-03-19 00:40:10 -04:00
|
|
|
|
2019-05-26 16:02:52 -04:00
|
|
|
let mut output = vec![];
|
2019-06-05 20:20:12 -04:00
|
|
|
self.preprocess(&mut output, source, glsl_version_spec);
|
2019-05-26 16:02:52 -04:00
|
|
|
let source = output;
|
2019-03-08 19:52:47 -05:00
|
|
|
|
2019-03-05 17:51:29 -05:00
|
|
|
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();
|
2019-04-29 19:43:24 -04:00
|
|
|
error!("Shader info log:\n{}", String::from_utf8_lossy(&info_log));
|
2019-03-05 17:51:29 -05:00
|
|
|
panic!("{:?} shader '{}' compilation failed", kind, name);
|
|
|
|
}
|
|
|
|
|
|
|
|
GLShader { gl_shader }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn create_program_from_shaders(&self,
|
2019-05-27 15:22:22 -04:00
|
|
|
_resources: &dyn ResourceLoader,
|
2019-03-05 17:51:29 -05:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-12 13:46:00 -04:00
|
|
|
fn get_vertex_attr(&self, program: &Self::Program, name: &str) -> Option<GLVertexAttr> {
|
2019-03-05 17:51:29 -05:00
|
|
|
let name = CString::new(format!("a{}", name)).unwrap();
|
|
|
|
let attr = unsafe {
|
2019-06-12 13:46:00 -04:00
|
|
|
gl::GetAttribLocation(program.gl_program, name.as_ptr() as *const GLchar)
|
2019-03-05 17:51:29 -05:00
|
|
|
}; ck();
|
2019-06-12 13:46:00 -04:00
|
|
|
if attr < 0 {
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
Some(GLVertexAttr { attr: attr as GLuint })
|
|
|
|
}
|
2019-03-05 17:51:29 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
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 }
|
|
|
|
}
|
|
|
|
|
2019-06-05 17:35:46 -04:00
|
|
|
fn configure_vertex_attr(&self,
|
|
|
|
vertex_array: &GLVertexArray,
|
|
|
|
attr: &GLVertexAttr,
|
|
|
|
descriptor: &VertexAttrDescriptor) {
|
|
|
|
debug_assert_ne!(descriptor.stride, 0);
|
|
|
|
|
|
|
|
self.bind_vertex_array(vertex_array);
|
2019-03-05 17:51:29 -05:00
|
|
|
|
|
|
|
unsafe {
|
2019-05-14 17:28:21 -04:00
|
|
|
let attr_type = descriptor.attr_type.to_gl_type();
|
|
|
|
match descriptor.class {
|
|
|
|
VertexAttrClass::Float | VertexAttrClass::FloatNorm => {
|
|
|
|
let normalized = if descriptor.class == VertexAttrClass::FloatNorm {
|
|
|
|
gl::TRUE
|
|
|
|
} else {
|
|
|
|
gl::FALSE
|
|
|
|
};
|
|
|
|
gl::VertexAttribPointer(attr.attr,
|
|
|
|
descriptor.size as GLint,
|
|
|
|
attr_type,
|
|
|
|
normalized,
|
|
|
|
descriptor.stride as GLint,
|
|
|
|
descriptor.offset as *const GLvoid); ck();
|
|
|
|
}
|
|
|
|
VertexAttrClass::Int => {
|
|
|
|
gl::VertexAttribIPointer(attr.attr,
|
|
|
|
descriptor.size as GLint,
|
|
|
|
attr_type,
|
|
|
|
descriptor.stride as GLint,
|
|
|
|
descriptor.offset as *const GLvoid); ck();
|
|
|
|
}
|
|
|
|
}
|
2019-03-05 17:51:29 -05:00
|
|
|
|
2019-05-14 17:28:21 -04:00
|
|
|
gl::VertexAttribDivisor(attr.attr, descriptor.divisor); ck();
|
2019-03-05 17:51:29 -05:00
|
|
|
gl::EnableVertexAttribArray(attr.attr); ck();
|
|
|
|
}
|
|
|
|
|
2019-06-05 17:35:46 -04:00
|
|
|
self.unbind_vertex_array();
|
2019-03-05 17:51:29 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
fn create_framebuffer(&self, texture: GLTexture) -> GLFramebuffer {
|
|
|
|
let mut gl_framebuffer = 0;
|
|
|
|
unsafe {
|
|
|
|
gl::GenFramebuffers(1, &mut gl_framebuffer); ck();
|
|
|
|
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 }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-15 16:21:24 -04:00
|
|
|
fn allocate_buffer<T>(&self,
|
|
|
|
buffer: &GLBuffer,
|
|
|
|
data: BufferData<T>,
|
|
|
|
target: BufferTarget,
|
|
|
|
mode: BufferUploadMode) {
|
2019-03-05 17:51:29 -05:00
|
|
|
let target = match target {
|
|
|
|
BufferTarget::Vertex => gl::ARRAY_BUFFER,
|
|
|
|
BufferTarget::Index => gl::ELEMENT_ARRAY_BUFFER,
|
|
|
|
};
|
2019-04-15 16:21:24 -04:00
|
|
|
let (ptr, len) = match data {
|
|
|
|
BufferData::Uninitialized(len) => (ptr::null(), len),
|
|
|
|
BufferData::Memory(buffer) => (buffer.as_ptr() as *const GLvoid, buffer.len()),
|
2019-03-05 17:51:29 -05:00
|
|
|
};
|
2019-04-15 16:21:24 -04:00
|
|
|
let len = (len * mem::size_of::<T>()) as GLsizeiptr;
|
|
|
|
let usage = mode.to_gl_usage();
|
2019-03-05 17:51:29 -05:00
|
|
|
unsafe {
|
|
|
|
gl::BindBuffer(target, buffer.gl_buffer); ck();
|
2019-04-15 16:21:24 -04:00
|
|
|
gl::BufferData(target, len, ptr, usage); ck();
|
2019-03-05 17:51:29 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn framebuffer_texture<'f>(&self, framebuffer: &'f Self::Framebuffer) -> &'f Self::Texture {
|
|
|
|
&framebuffer.texture
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2019-06-03 15:39:29 -04:00
|
|
|
fn texture_size(&self, texture: &Self::Texture) -> Vector2I {
|
2019-03-05 17:51:29 -05:00
|
|
|
texture.size
|
|
|
|
}
|
|
|
|
|
2019-12-20 14:48:29 -05:00
|
|
|
fn upload_to_texture(&self, texture: &Self::Texture, rect: RectI, data: TextureDataRef) {
|
|
|
|
let data_ptr = data.check_and_extract_data_ptr(rect.size(), texture.format);
|
|
|
|
|
|
|
|
assert!(rect.size().x() >= 0);
|
|
|
|
assert!(rect.size().y() >= 0);
|
|
|
|
assert!(rect.max_x() <= texture.size.x());
|
|
|
|
assert!(rect.max_y() <= texture.size.y());
|
|
|
|
|
2019-03-05 17:51:29 -05:00
|
|
|
unsafe {
|
|
|
|
self.bind_texture(texture, 0);
|
2019-12-20 14:48:29 -05:00
|
|
|
if rect.origin() == Vector2I::default() && rect.size() == texture.size {
|
|
|
|
gl::TexImage2D(gl::TEXTURE_2D,
|
|
|
|
0,
|
|
|
|
texture.format.gl_internal_format(),
|
|
|
|
texture.size.x() as GLsizei,
|
|
|
|
texture.size.y() as GLsizei,
|
|
|
|
0,
|
|
|
|
texture.format.gl_format(),
|
|
|
|
texture.format.gl_type(),
|
|
|
|
data_ptr); ck();
|
|
|
|
} else {
|
|
|
|
gl::TexSubImage2D(gl::TEXTURE_2D,
|
|
|
|
0,
|
|
|
|
rect.origin().x(),
|
|
|
|
rect.origin().y(),
|
|
|
|
rect.size().x() as GLsizei,
|
|
|
|
rect.size().y() as GLsizei,
|
|
|
|
texture.format.gl_format(),
|
|
|
|
texture.format.gl_type(),
|
|
|
|
data_ptr); ck();
|
|
|
|
}
|
2019-03-05 17:51:29 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
self.set_texture_parameters(texture);
|
|
|
|
}
|
|
|
|
|
2020-01-07 16:03:15 -05:00
|
|
|
fn read_pixels(&self, render_target: &RenderTarget<GLDevice>, viewport: RectI)
|
|
|
|
-> GLTextureDataReceiver {
|
2019-06-05 17:35:46 -04:00
|
|
|
let (origin, size) = (viewport.origin(), viewport.size());
|
|
|
|
let format = self.render_target_format(render_target);
|
|
|
|
self.bind_render_target(render_target);
|
2020-01-07 16:03:15 -05:00
|
|
|
let byte_size = size.x() as usize * size.y() as usize * format.bytes_per_pixel() as usize;
|
2019-03-05 17:51:29 -05:00
|
|
|
|
2020-01-07 16:03:15 -05:00
|
|
|
unsafe {
|
|
|
|
let mut gl_pixel_buffer = 0;
|
|
|
|
gl::GenBuffers(1, &mut gl_pixel_buffer); ck();
|
|
|
|
gl::BindBuffer(gl::PIXEL_PACK_BUFFER, gl_pixel_buffer); ck();
|
|
|
|
gl::BufferData(gl::PIXEL_PACK_BUFFER,
|
|
|
|
byte_size as GLsizeiptr,
|
|
|
|
ptr::null(),
|
|
|
|
gl::STATIC_READ); ck();
|
|
|
|
|
|
|
|
gl::ReadPixels(origin.x(),
|
|
|
|
origin.y(),
|
|
|
|
size.x() as GLsizei,
|
|
|
|
size.y() as GLsizei,
|
|
|
|
format.gl_format(),
|
|
|
|
format.gl_type(),
|
|
|
|
0 as *mut GLvoid); ck();
|
|
|
|
|
|
|
|
let gl_sync = gl::FenceSync(gl::SYNC_GPU_COMMANDS_COMPLETE, 0);
|
|
|
|
|
|
|
|
GLTextureDataReceiver { gl_pixel_buffer, gl_sync, size, format }
|
2019-03-05 17:51:29 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-05 17:35:46 -04:00
|
|
|
fn begin_commands(&self) {
|
|
|
|
// TODO(pcwalton): Add some checks in debug mode to make sure render commands are bracketed
|
|
|
|
// by these?
|
|
|
|
}
|
2019-04-22 16:50:38 -04:00
|
|
|
|
2019-06-05 17:35:46 -04:00
|
|
|
fn end_commands(&self) {
|
|
|
|
unsafe { gl::Flush(); }
|
2019-03-05 17:51:29 -05:00
|
|
|
}
|
|
|
|
|
2019-06-05 17:35:46 -04:00
|
|
|
fn draw_arrays(&self, index_count: u32, render_state: &RenderState<Self>) {
|
2019-03-05 17:51:29 -05:00
|
|
|
self.set_render_state(render_state);
|
|
|
|
unsafe {
|
2019-06-05 17:35:46 -04:00
|
|
|
gl::DrawArrays(render_state.primitive.to_gl_primitive(),
|
|
|
|
0,
|
|
|
|
index_count as GLsizei); ck();
|
2019-03-05 17:51:29 -05:00
|
|
|
}
|
|
|
|
self.reset_render_state(render_state);
|
|
|
|
}
|
|
|
|
|
2019-06-05 17:35:46 -04:00
|
|
|
fn draw_elements(&self, index_count: u32, render_state: &RenderState<Self>) {
|
2019-03-05 17:51:29 -05:00
|
|
|
self.set_render_state(render_state);
|
|
|
|
unsafe {
|
2019-06-05 17:35:46 -04:00
|
|
|
gl::DrawElements(render_state.primitive.to_gl_primitive(),
|
2019-03-05 17:51:29 -05:00
|
|
|
index_count as GLsizei,
|
|
|
|
gl::UNSIGNED_INT,
|
|
|
|
ptr::null()); ck();
|
|
|
|
}
|
|
|
|
self.reset_render_state(render_state);
|
|
|
|
}
|
|
|
|
|
2019-05-29 19:04:33 -04:00
|
|
|
fn draw_elements_instanced(&self,
|
|
|
|
index_count: u32,
|
|
|
|
instance_count: u32,
|
2019-06-05 17:35:46 -04:00
|
|
|
render_state: &RenderState<Self>) {
|
2019-03-05 17:51:29 -05:00
|
|
|
self.set_render_state(render_state);
|
|
|
|
unsafe {
|
2019-06-05 17:35:46 -04:00
|
|
|
gl::DrawElementsInstanced(render_state.primitive.to_gl_primitive(),
|
2019-05-29 19:04:33 -04:00
|
|
|
index_count as GLsizei,
|
|
|
|
gl::UNSIGNED_INT,
|
|
|
|
ptr::null(),
|
|
|
|
instance_count as GLsizei); ck();
|
2019-03-05 17:51:29 -05:00
|
|
|
}
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-07 16:03:15 -05:00
|
|
|
fn try_recv_timer_query(&self, query: &Self::TimerQuery) -> Option<Duration> {
|
2019-03-05 17:51:29 -05:00
|
|
|
unsafe {
|
|
|
|
let mut result = 0;
|
|
|
|
gl::GetQueryObjectiv(query.gl_query, gl::QUERY_RESULT_AVAILABLE, &mut result); ck();
|
2019-06-05 17:35:46 -04:00
|
|
|
if result == gl::FALSE as GLint {
|
2020-01-07 16:03:15 -05:00
|
|
|
None
|
|
|
|
} else {
|
|
|
|
Some(self.recv_timer_query(query))
|
2019-06-05 17:35:46 -04:00
|
|
|
}
|
2020-01-07 16:03:15 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn recv_timer_query(&self, query: &Self::TimerQuery) -> Duration {
|
|
|
|
unsafe {
|
2019-06-05 17:35:46 -04:00
|
|
|
let mut result = 0;
|
|
|
|
gl::GetQueryObjectui64v(query.gl_query, gl::QUERY_RESULT, &mut result); ck();
|
2020-01-07 16:03:15 -05:00
|
|
|
Duration::from_nanos(result)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn try_recv_texture_data(&self, receiver: &Self::TextureDataReceiver) -> Option<TextureData> {
|
|
|
|
unsafe {
|
|
|
|
let result = gl::ClientWaitSync(receiver.gl_sync,
|
|
|
|
gl::SYNC_FLUSH_COMMANDS_BIT,
|
|
|
|
0); ck();
|
|
|
|
if result == gl::TIMEOUT_EXPIRED || result == gl::WAIT_FAILED {
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
Some(self.get_texture_data(receiver))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn recv_texture_data(&self, receiver: &Self::TextureDataReceiver) -> TextureData {
|
|
|
|
unsafe {
|
|
|
|
let result = gl::ClientWaitSync(receiver.gl_sync,
|
|
|
|
gl::SYNC_FLUSH_COMMANDS_BIT,
|
|
|
|
!0); ck();
|
|
|
|
debug_assert!(result != gl::TIMEOUT_EXPIRED && result != gl::WAIT_FAILED);
|
|
|
|
self.get_texture_data(receiver)
|
2019-03-05 17:51:29 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2019-06-05 17:35:46 -04:00
|
|
|
fn bind_buffer(&self, vertex_array: &GLVertexArray, buffer: &GLBuffer, target: BufferTarget) {
|
|
|
|
self.bind_vertex_array(vertex_array);
|
2019-03-05 17:51:29 -05:00
|
|
|
unsafe {
|
2019-06-05 17:35:46 -04:00
|
|
|
gl::BindBuffer(target.to_gl_target(), buffer.gl_buffer); ck();
|
2019-03-05 17:51:29 -05:00
|
|
|
}
|
2019-06-05 17:35:46 -04:00
|
|
|
self.unbind_vertex_array();
|
2019-03-05 17:51:29 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2019-06-05 17:35:46 -04:00
|
|
|
fn create_shader(
|
|
|
|
&self,
|
|
|
|
resources: &dyn ResourceLoader,
|
|
|
|
name: &str,
|
|
|
|
kind: ShaderKind,
|
|
|
|
) -> Self::Shader {
|
|
|
|
let suffix = match kind {
|
|
|
|
ShaderKind::Vertex => 'v',
|
|
|
|
ShaderKind::Fragment => 'f',
|
|
|
|
};
|
|
|
|
let path = format!("shaders/gl3/{}.{}s.glsl", name, suffix);
|
|
|
|
self.create_shader_from_source(name, &resources.slurp(&path).unwrap(), kind)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl GLDevice {
|
|
|
|
fn bind_render_target(&self, attachment: &RenderTarget<GLDevice>) {
|
|
|
|
match *attachment {
|
|
|
|
RenderTarget::Default => self.bind_default_framebuffer(),
|
|
|
|
RenderTarget::Framebuffer(framebuffer) => self.bind_framebuffer(framebuffer),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-05 17:51:29 -05:00
|
|
|
fn bind_vertex_array(&self, vertex_array: &GLVertexArray) {
|
|
|
|
unsafe {
|
|
|
|
gl::BindVertexArray(vertex_array.gl_vertex_array); ck();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-05 17:35:46 -04:00
|
|
|
fn unbind_vertex_array(&self) {
|
2019-03-05 17:51:29 -05:00
|
|
|
unsafe {
|
2019-06-05 17:35:46 -04:00
|
|
|
gl::BindVertexArray(0); ck();
|
2019-03-05 17:51:29 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-05 17:35:46 -04:00
|
|
|
fn bind_texture(&self, texture: &GLTexture, unit: u32) {
|
2019-03-05 17:51:29 -05:00
|
|
|
unsafe {
|
2019-06-05 17:35:46 -04:00
|
|
|
gl::ActiveTexture(gl::TEXTURE0 + unit); ck();
|
|
|
|
gl::BindTexture(gl::TEXTURE_2D, texture.gl_texture); ck();
|
2019-03-05 17:51:29 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-05 17:35:46 -04:00
|
|
|
fn unbind_texture(&self, unit: u32) {
|
2019-03-05 17:51:29 -05:00
|
|
|
unsafe {
|
2019-06-05 17:35:46 -04:00
|
|
|
gl::ActiveTexture(gl::TEXTURE0 + unit); ck();
|
|
|
|
gl::BindTexture(gl::TEXTURE_2D, 0); ck();
|
2019-03-05 17:51:29 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-05 17:35:46 -04:00
|
|
|
fn use_program(&self, program: &GLProgram) {
|
2019-03-05 17:51:29 -05:00
|
|
|
unsafe {
|
2019-06-05 17:35:46 -04:00
|
|
|
gl::UseProgram(program.gl_program); ck();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn unuse_program(&self) {
|
|
|
|
unsafe {
|
|
|
|
gl::UseProgram(0); ck();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn bind_default_framebuffer(&self) {
|
|
|
|
unsafe {
|
|
|
|
gl::BindFramebuffer(gl::FRAMEBUFFER, self.default_framebuffer); ck();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn bind_framebuffer(&self, framebuffer: &GLFramebuffer) {
|
|
|
|
unsafe {
|
|
|
|
gl::BindFramebuffer(gl::FRAMEBUFFER, framebuffer.gl_framebuffer); ck();
|
2019-03-05 17:51:29 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-05 20:20:12 -04:00
|
|
|
fn preprocess(&self, output: &mut Vec<u8>, source: &[u8], version: &str) {
|
2019-05-26 16:02:52 -04:00
|
|
|
let mut index = 0;
|
|
|
|
while index < source.len() {
|
|
|
|
if source[index..].starts_with(b"{{") {
|
|
|
|
let end_index = source[index..].iter()
|
|
|
|
.position(|character| *character == b'}')
|
|
|
|
.expect("Expected `}`!") + index;
|
|
|
|
assert_eq!(source[end_index + 1], b'}');
|
|
|
|
let ident = String::from_utf8_lossy(&source[(index + 2)..end_index]);
|
|
|
|
if ident == "version" {
|
|
|
|
output.extend_from_slice(version.as_bytes());
|
|
|
|
} else {
|
|
|
|
panic!("unknown template variable: `{}`", ident);
|
|
|
|
}
|
|
|
|
index = end_index + 2;
|
|
|
|
} else {
|
|
|
|
output.push(source[index]);
|
|
|
|
index += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-06-05 17:35:46 -04:00
|
|
|
|
|
|
|
fn clear(&self, ops: &ClearOps) {
|
|
|
|
unsafe {
|
|
|
|
let mut flags = 0;
|
|
|
|
if let Some(color) = ops.color {
|
|
|
|
gl::ColorMask(gl::TRUE, gl::TRUE, gl::TRUE, gl::TRUE); ck();
|
|
|
|
gl::ClearColor(color.r(), color.g(), color.b(), color.a()); ck();
|
|
|
|
flags |= gl::COLOR_BUFFER_BIT;
|
|
|
|
}
|
|
|
|
if let Some(depth) = ops.depth {
|
|
|
|
gl::DepthMask(gl::TRUE); ck();
|
|
|
|
gl::ClearDepthf(depth as _); ck(); // FIXME(pcwalton): GLES
|
|
|
|
flags |= gl::DEPTH_BUFFER_BIT;
|
|
|
|
}
|
|
|
|
if let Some(stencil) = ops.stencil {
|
|
|
|
gl::StencilMask(!0); ck();
|
|
|
|
gl::ClearStencil(stencil as GLint); ck();
|
|
|
|
flags |= gl::STENCIL_BUFFER_BIT;
|
|
|
|
}
|
|
|
|
if flags != 0 {
|
|
|
|
gl::Clear(flags); ck();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn render_target_format(&self, render_target: &RenderTarget<GLDevice>) -> TextureFormat {
|
|
|
|
match *render_target {
|
|
|
|
RenderTarget::Default => TextureFormat::RGBA8,
|
|
|
|
RenderTarget::Framebuffer(ref framebuffer) => {
|
|
|
|
self.framebuffer_texture(framebuffer).format
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-01-07 16:03:15 -05:00
|
|
|
|
|
|
|
fn get_texture_data(&self, receiver: &GLTextureDataReceiver) -> TextureData {
|
|
|
|
unsafe {
|
|
|
|
let (format, size) = (receiver.format, receiver.size);
|
|
|
|
let channels = format.channels();
|
|
|
|
let (mut texture_data, texture_data_ptr, texture_data_len);
|
|
|
|
match format {
|
|
|
|
TextureFormat::R8 | TextureFormat::RGBA8 => {
|
|
|
|
let mut pixels: Vec<u8> =
|
|
|
|
vec![0; size.x() as usize * size.y() as usize * channels];
|
|
|
|
texture_data_ptr = pixels.as_mut_ptr();
|
|
|
|
texture_data_len = pixels.len() * mem::size_of::<u8>();
|
|
|
|
texture_data = TextureData::U8(pixels);
|
|
|
|
}
|
|
|
|
TextureFormat::R16F | TextureFormat::RGBA16F => {
|
|
|
|
let mut pixels: Vec<f16> =
|
|
|
|
vec![f16::default(); size.x() as usize * size.y() as usize * channels];
|
|
|
|
texture_data_ptr = pixels.as_mut_ptr() as *mut u8;
|
|
|
|
texture_data_len = pixels.len() * mem::size_of::<f16>();
|
|
|
|
texture_data = TextureData::F16(pixels);
|
|
|
|
}
|
|
|
|
TextureFormat::RGBA32F => {
|
|
|
|
let mut pixels = vec![0.0; size.x() as usize * size.y() as usize * channels];
|
|
|
|
texture_data_ptr = pixels.as_mut_ptr() as *mut u8;
|
|
|
|
texture_data_len = pixels.len() * mem::size_of::<f32>();
|
|
|
|
texture_data = TextureData::F32(pixels);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
gl::BindBuffer(gl::PIXEL_PACK_BUFFER, receiver.gl_pixel_buffer); ck();
|
|
|
|
gl::GetBufferSubData(gl::PIXEL_PACK_BUFFER,
|
|
|
|
0,
|
|
|
|
texture_data_len as GLsizeiptr,
|
|
|
|
texture_data_ptr as *mut GLvoid); ck();
|
|
|
|
gl::BindBuffer(gl::PIXEL_PACK_BUFFER, 0); ck();
|
|
|
|
|
|
|
|
match texture_data {
|
|
|
|
TextureData::U8(ref mut pixels) => flip_y(pixels, size, channels),
|
|
|
|
TextureData::U16(ref mut pixels) => flip_y(pixels, size, channels),
|
|
|
|
TextureData::F16(ref mut pixels) => flip_y(pixels, size, channels),
|
|
|
|
TextureData::F32(ref mut pixels) => flip_y(pixels, size, channels),
|
|
|
|
}
|
|
|
|
|
|
|
|
texture_data
|
|
|
|
}
|
|
|
|
}
|
2019-05-26 16:02:52 -04:00
|
|
|
}
|
|
|
|
|
2019-03-05 17:51:29 -05:00
|
|
|
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 {
|
2019-06-05 17:35:46 -04:00
|
|
|
location: GLint,
|
2019-03-05 17:51:29 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
2019-06-03 15:39:29 -04:00
|
|
|
pub size: Vector2I,
|
2019-06-05 17:35:46 -04:00
|
|
|
pub format: TextureFormat,
|
2019-03-05 17:51:29 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-15 16:21:24 -04:00
|
|
|
trait BufferUploadModeExt {
|
|
|
|
fn to_gl_usage(self) -> GLuint;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl BufferUploadModeExt for BufferUploadMode {
|
|
|
|
fn to_gl_usage(self) -> GLuint {
|
|
|
|
match self {
|
|
|
|
BufferUploadMode::Static => gl::STATIC_DRAW,
|
|
|
|
BufferUploadMode::Dynamic => gl::DYNAMIC_DRAW,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-05 17:51:29 -05:00
|
|
|
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::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,
|
2019-06-05 17:35:46 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
trait TextureFormatExt {
|
|
|
|
fn gl_internal_format(self) -> GLint;
|
|
|
|
fn gl_format(self) -> GLuint;
|
|
|
|
fn gl_type(self) -> GLuint;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TextureFormatExt for TextureFormat {
|
|
|
|
fn gl_internal_format(self) -> GLint {
|
|
|
|
match self {
|
|
|
|
TextureFormat::R8 => gl::R8 as GLint,
|
|
|
|
TextureFormat::R16F => gl::R16F as GLint,
|
|
|
|
TextureFormat::RGBA8 => gl::RGBA as GLint,
|
2019-12-20 14:48:29 -05:00
|
|
|
TextureFormat::RGBA16F => gl::RGBA16F as GLint,
|
2019-12-14 16:57:55 -05:00
|
|
|
TextureFormat::RGBA32F => gl::RGBA32F as GLint,
|
2019-06-05 17:35:46 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn gl_format(self) -> GLuint {
|
|
|
|
match self {
|
|
|
|
TextureFormat::R8 | TextureFormat::R16F => gl::RED,
|
2019-12-20 14:48:29 -05:00
|
|
|
TextureFormat::RGBA8 | TextureFormat::RGBA16F | TextureFormat::RGBA32F => gl::RGBA,
|
2019-06-05 17:35:46 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn gl_type(self) -> GLuint {
|
|
|
|
match self {
|
|
|
|
TextureFormat::R8 | TextureFormat::RGBA8 => gl::UNSIGNED_BYTE,
|
2019-12-20 14:48:29 -05:00
|
|
|
TextureFormat::R16F | TextureFormat::RGBA16F => gl::HALF_FLOAT,
|
2019-12-14 16:57:55 -05:00
|
|
|
TextureFormat::RGBA32F => gl::FLOAT,
|
2019-03-05 17:51:29 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
2019-04-15 16:21:24 -04:00
|
|
|
VertexAttrType::I8 => gl::BYTE,
|
2019-03-05 17:51:29 -05:00
|
|
|
VertexAttrType::U16 => gl::UNSIGNED_SHORT,
|
|
|
|
VertexAttrType::U8 => gl::UNSIGNED_BYTE,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-07 16:03:15 -05:00
|
|
|
pub struct GLTextureDataReceiver {
|
|
|
|
gl_pixel_buffer: GLuint,
|
|
|
|
gl_sync: GLsync,
|
|
|
|
size: Vector2I,
|
|
|
|
format: TextureFormat,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Drop for GLTextureDataReceiver {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
unsafe {
|
|
|
|
gl::DeleteBuffers(1, &mut self.gl_pixel_buffer); ck();
|
|
|
|
gl::DeleteSync(self.gl_sync); ck();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-08 19:52:47 -05:00
|
|
|
/// The version/dialect of OpenGL we should render with.
|
2019-05-24 20:45:19 -04:00
|
|
|
#[derive(Clone, Copy)]
|
|
|
|
#[repr(u32)]
|
2019-03-08 19:52:47 -05:00
|
|
|
pub enum GLVersion {
|
|
|
|
/// OpenGL 3.0+, core profile.
|
2019-05-24 20:45:19 -04:00
|
|
|
GL3 = 0,
|
2019-03-08 19:52:47 -05:00
|
|
|
/// OpenGL ES 3.0+.
|
2019-05-24 20:45:19 -04:00
|
|
|
GLES3 = 1,
|
2019-03-08 19:52:47 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
impl GLVersion {
|
|
|
|
fn to_glsl_version_spec(&self) -> &'static str {
|
|
|
|
match *self {
|
|
|
|
GLVersion::GL3 => "330",
|
|
|
|
GLVersion::GLES3 => "300 es",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-05 17:51:29 -05:00
|
|
|
// Error checking
|
|
|
|
|
2019-06-05 07:45:52 -04:00
|
|
|
#[cfg(debug_assertions)]
|
2019-03-05 17:51:29 -05:00
|
|
|
fn ck() {
|
|
|
|
unsafe {
|
2019-06-05 08:15:21 -04:00
|
|
|
// Note that ideally we should be calling gl::GetError() in a loop until it
|
|
|
|
// returns gl::NO_ERROR, but for now we'll just report the first one we find.
|
2019-03-05 17:51:29 -05:00
|
|
|
let err = gl::GetError();
|
2019-06-05 08:15:21 -04:00
|
|
|
if err != gl::NO_ERROR {
|
|
|
|
panic!("GL error: 0x{:x} ({})", err, match err {
|
|
|
|
gl::INVALID_ENUM => "INVALID_ENUM",
|
|
|
|
gl::INVALID_VALUE => "INVALID_VALUE",
|
|
|
|
gl::INVALID_OPERATION => "INVALID_OPERATION",
|
|
|
|
gl::INVALID_FRAMEBUFFER_OPERATION => "INVALID_FRAMEBUFFER_OPERATION",
|
|
|
|
gl::OUT_OF_MEMORY => "OUT_OF_MEMORY",
|
|
|
|
gl::STACK_UNDERFLOW => "STACK_UNDERFLOW",
|
|
|
|
gl::STACK_OVERFLOW => "STACK_OVERFLOW",
|
|
|
|
_ => "Unknown"
|
|
|
|
});
|
2019-03-05 17:51:29 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-05 07:45:52 -04:00
|
|
|
#[cfg(not(debug_assertions))]
|
2019-03-05 17:51:29 -05:00
|
|
|
fn ck() {}
|
2019-05-26 16:02:52 -04:00
|
|
|
|
2019-06-05 17:35:46 -04:00
|
|
|
// Utilities
|
|
|
|
|
|
|
|
// Flips a buffer of image data upside-down.
|
|
|
|
fn flip_y<T>(pixels: &mut [T], size: Vector2I, channels: usize) {
|
|
|
|
let stride = size.x() as usize * channels;
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|