diff --git a/Cargo.lock b/Cargo.lock index 2740953f..e8378dcb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -162,16 +162,13 @@ dependencies = [ "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.19.4 (registry+https://github.com/rust-lang/crates.io-index)", "gl 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "image 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)", "jemallocator 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "pathfinder_geometry 0.3.0", + "pathfinder_gl 0.1.0", "pathfinder_renderer 0.1.0", "pathfinder_svg 0.1.0", "rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "sdl2 0.32.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", "usvg 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -531,6 +528,20 @@ dependencies = [ "euclid 0.19.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "pathfinder_gl" +version = "0.1.0" +dependencies = [ + "euclid 0.19.4 (registry+https://github.com/rust-lang/crates.io-index)", + "gl 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "image 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pathfinder_geometry 0.3.0", + "pathfinder_renderer 0.1.0", + "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "pathfinder_renderer" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 63f517c6..69e96164 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ members = [ "demo3", "geometry", "gfx-utils", + "gl", "renderer", "svg", "utils/area-lut", diff --git a/demo3/Cargo.toml b/demo3/Cargo.toml index beafd288..72cc9e07 100644 --- a/demo3/Cargo.toml +++ b/demo3/Cargo.toml @@ -11,19 +11,14 @@ gl = "0.6" jemallocator = "0.1" rayon = "1.0" sdl2 = "0.32" -serde = "1.0" -serde_derive = "1.0" -serde_json = "1.0" usvg = "0.4" -[dependencies.image] -version = "0.21" -default-features = false -features = ["png_codec"] - [dependencies.pathfinder_geometry] path = "../geometry" +[dependencies.pathfinder_gl] +path = "../gl" + [dependencies.pathfinder_renderer] path = "../renderer" diff --git a/demo3/src/main.rs b/demo3/src/main.rs index 0c31811e..67be668c 100644 --- a/demo3/src/main.rs +++ b/demo3/src/main.rs @@ -8,57 +8,32 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#[macro_use] -extern crate serde_derive; - -use crate::debug_text::DebugRenderer; -use crate::device::{Buffer, BufferTarget, BufferUploadMode, Framebuffer, Program, Texture}; -use crate::device::{TimerQuery, Uniform, VertexAttr}; use clap::{App, Arg}; use euclid::{Point2D, Rect, Size2D}; -use gl::types::{GLfloat, GLint, GLuint}; use jemallocator; use pathfinder_geometry::point::Point4DF32; use pathfinder_geometry::transform3d::{Perspective, Transform3DF32}; +use pathfinder_gl::renderer::Renderer; use pathfinder_renderer::builder::SceneBuilder; -use pathfinder_renderer::gpu_data::{Batch, BuiltScene, SolidTileScenePrimitive}; -use pathfinder_renderer::paint::ObjectShader; +use pathfinder_renderer::gpu_data::BuiltScene; use pathfinder_renderer::scene::Scene; -use pathfinder_renderer::tiles::{TILE_HEIGHT, TILE_WIDTH}; use pathfinder_renderer::z_buffer::ZBuffer; use pathfinder_svg::SceneExt; use rayon::ThreadPoolBuilder; use sdl2::event::Event; use sdl2::keyboard::Keycode; use sdl2::video::GLProfile; -use std::collections::VecDeque; use std::f32::consts::FRAC_PI_4; -use std::time::{Duration, Instant}; +use std::time::Instant; use std::path::PathBuf; use usvg::{Options as UsvgOptions, Tree}; -mod debug_text; -mod device; - #[global_allocator] static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; -static QUAD_VERTEX_POSITIONS: [u8; 8] = [0, 0, 1, 0, 1, 1, 0, 1]; - -// TODO(pcwalton): Replace with `mem::size_of` calls? -const FILL_INSTANCE_SIZE: GLint = 8; -const SOLID_TILE_INSTANCE_SIZE: GLint = 6; -const MASK_TILE_INSTANCE_SIZE: GLint = 8; - -const MASK_FRAMEBUFFER_WIDTH: u32 = TILE_WIDTH * 256; -const MASK_FRAMEBUFFER_HEIGHT: u32 = TILE_HEIGHT * 256; - const MAIN_FRAMEBUFFER_WIDTH: u32 = 1067; const MAIN_FRAMEBUFFER_HEIGHT: u32 = 800; -const FILL_COLORS_TEXTURE_WIDTH: u32 = 256; -const FILL_COLORS_TEXTURE_HEIGHT: u32 = 256; - const MOUSELOOK_ROTATION_SPEED: f32 = 0.01; const CAMERA_VELOCITY: f32 = 0.03; @@ -299,420 +274,3 @@ fn build_scene(scene: &Scene, options: &Options) -> BuiltScene { } built_scene } - -struct Renderer { - fill_program: FillProgram, - solid_tile_program: SolidTileProgram, - mask_tile_program: MaskTileProgram, - area_lut_texture: Texture, - #[allow(dead_code)] - quad_vertex_positions_buffer: Buffer, - fill_vertex_array: FillVertexArray, - mask_tile_vertex_array: MaskTileVertexArray, - solid_tile_vertex_array: SolidTileVertexArray, - mask_framebuffer: Framebuffer, - fill_colors_texture: Texture, - - pending_timer_queries: VecDeque, - free_timer_queries: Vec, - - debug_renderer: DebugRenderer, - - main_framebuffer_size: Size2D, -} - -impl Renderer { - fn new(main_framebuffer_size: &Size2D) -> Renderer { - let fill_program = FillProgram::new(); - let solid_tile_program = SolidTileProgram::new(); - let mask_tile_program = MaskTileProgram::new(); - - let area_lut_texture = Texture::from_png("area-lut"); - - let quad_vertex_positions_buffer = Buffer::new(); - quad_vertex_positions_buffer.upload(&QUAD_VERTEX_POSITIONS, - BufferTarget::Vertex, - BufferUploadMode::Static); - - let fill_vertex_array = FillVertexArray::new(&fill_program, &quad_vertex_positions_buffer); - let mask_tile_vertex_array = MaskTileVertexArray::new(&mask_tile_program, - &quad_vertex_positions_buffer); - let solid_tile_vertex_array = SolidTileVertexArray::new(&solid_tile_program, - &quad_vertex_positions_buffer); - - let mask_framebuffer = Framebuffer::new(&Size2D::new(MASK_FRAMEBUFFER_WIDTH, - MASK_FRAMEBUFFER_HEIGHT)); - - let fill_colors_texture = Texture::new_rgba(&Size2D::new(FILL_COLORS_TEXTURE_WIDTH, - FILL_COLORS_TEXTURE_HEIGHT)); - - let debug_renderer = DebugRenderer::new(main_framebuffer_size); - - Renderer { - fill_program, - solid_tile_program, - mask_tile_program, - area_lut_texture, - quad_vertex_positions_buffer, - fill_vertex_array, - mask_tile_vertex_array, - solid_tile_vertex_array, - mask_framebuffer, - fill_colors_texture, - - pending_timer_queries: VecDeque::new(), - free_timer_queries: vec![], - - debug_renderer, - - main_framebuffer_size: *main_framebuffer_size, - } - } - - fn render_scene(&mut self, built_scene: &BuiltScene) { - let timer_query = self.free_timer_queries.pop().unwrap_or_else(|| TimerQuery::new()); - timer_query.begin(); - - self.upload_shaders(&built_scene.shaders); - - self.upload_solid_tiles(&built_scene.solid_tiles); - self.draw_solid_tiles(&built_scene.solid_tiles); - - for batch in &built_scene.batches { - self.upload_batch(batch); - self.draw_batch_fills(batch); - self.draw_batch_mask_tiles(batch); - } - - timer_query.end(); - self.pending_timer_queries.push_back(timer_query); - } - - fn shift_timer_query(&mut self) -> Option { - let query = self.pending_timer_queries.front()?; - if !query.is_available() { - return None - } - let query = self.pending_timer_queries.pop_front().unwrap(); - let result = Duration::from_nanos(query.get()); - self.free_timer_queries.push(query); - Some(result) - } - - fn upload_shaders(&mut self, shaders: &[ObjectShader]) { - let size = Size2D::new(FILL_COLORS_TEXTURE_WIDTH, FILL_COLORS_TEXTURE_HEIGHT); - let mut fill_colors = vec![0; size.width as usize * size.height as usize * 4]; - for (shader_index, shader) in shaders.iter().enumerate() { - fill_colors[shader_index * 4 + 0] = shader.fill_color.r; - fill_colors[shader_index * 4 + 1] = shader.fill_color.g; - fill_colors[shader_index * 4 + 2] = shader.fill_color.b; - fill_colors[shader_index * 4 + 3] = shader.fill_color.a; - } - self.fill_colors_texture.upload_rgba(&size, &fill_colors); - } - - fn upload_solid_tiles(&mut self, solid_tiles: &[SolidTileScenePrimitive]) { - self.solid_tile_vertex_array - .vertex_buffer - .upload(solid_tiles, BufferTarget::Vertex, BufferUploadMode::Dynamic); - } - - fn upload_batch(&mut self, batch: &Batch) { - self.fill_vertex_array - .vertex_buffer - .upload(&batch.fills, BufferTarget::Vertex, BufferUploadMode::Dynamic); - self.mask_tile_vertex_array - .vertex_buffer - .upload(&batch.mask_tiles, BufferTarget::Vertex, BufferUploadMode::Dynamic); - } - - fn draw_batch_fills(&mut self, batch: &Batch) { - unsafe { - gl::BindFramebuffer(gl::FRAMEBUFFER, self.mask_framebuffer.gl_framebuffer); - gl::Viewport(0, 0, MASK_FRAMEBUFFER_WIDTH as GLint, MASK_FRAMEBUFFER_HEIGHT as GLint); - // TODO(pcwalton): Only clear the appropriate portion? - gl::ClearColor(0.0, 0.0, 0.0, 0.0); - gl::Clear(gl::COLOR_BUFFER_BIT); - - gl::BindVertexArray(self.fill_vertex_array.gl_vertex_array); - gl::UseProgram(self.fill_program.program.gl_program); - gl::Uniform2f(self.fill_program.framebuffer_size_uniform.location, - MASK_FRAMEBUFFER_WIDTH as GLfloat, - MASK_FRAMEBUFFER_HEIGHT as GLfloat); - gl::Uniform2f(self.fill_program.tile_size_uniform.location, - TILE_WIDTH as GLfloat, - TILE_HEIGHT as GLfloat); - self.area_lut_texture.bind(0); - gl::Uniform1i(self.fill_program.area_lut_uniform.location, 0); - gl::BlendEquation(gl::FUNC_ADD); - gl::BlendFunc(gl::ONE, gl::ONE); - gl::Enable(gl::BLEND); - gl::DrawArraysInstanced(gl::TRIANGLE_FAN, 0, 4, batch.fills.len() as GLint); - gl::Disable(gl::BLEND); - } - } - - fn draw_batch_mask_tiles(&mut self, batch: &Batch) { - unsafe { - gl::BindFramebuffer(gl::FRAMEBUFFER, 0); - gl::Viewport(0, - 0, - self.main_framebuffer_size.width as GLint, - self.main_framebuffer_size.height as GLint); - - gl::BindVertexArray(self.mask_tile_vertex_array.gl_vertex_array); - gl::UseProgram(self.mask_tile_program.program.gl_program); - gl::Uniform2f(self.mask_tile_program.framebuffer_size_uniform.location, - self.main_framebuffer_size.width as GLfloat, - self.main_framebuffer_size.height as GLfloat); - gl::Uniform2f(self.mask_tile_program.tile_size_uniform.location, - TILE_WIDTH as GLfloat, - TILE_HEIGHT as GLfloat); - self.mask_framebuffer.texture.bind(0); - gl::Uniform1i(self.mask_tile_program.stencil_texture_uniform.location, 0); - gl::Uniform2f(self.mask_tile_program.stencil_texture_size_uniform.location, - MASK_FRAMEBUFFER_WIDTH as GLfloat, - MASK_FRAMEBUFFER_HEIGHT as GLfloat); - self.fill_colors_texture.bind(1); - gl::Uniform1i(self.mask_tile_program.fill_colors_texture_uniform.location, 1); - gl::Uniform2f(self.mask_tile_program.fill_colors_texture_size_uniform.location, - FILL_COLORS_TEXTURE_WIDTH as GLfloat, - FILL_COLORS_TEXTURE_HEIGHT as GLfloat); - // FIXME(pcwalton): Fill this in properly! - gl::Uniform2f(self.mask_tile_program.view_box_origin_uniform.location, 0.0, 0.0); - gl::BlendEquation(gl::FUNC_ADD); - gl::BlendFuncSeparate(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA, gl::ONE, gl::ONE); - gl::Enable(gl::BLEND); - gl::DrawArraysInstanced(gl::TRIANGLE_FAN, 0, 4, batch.mask_tiles.len() as GLint); - gl::Disable(gl::BLEND); - } - } - - fn draw_solid_tiles(&mut self, solid_tiles: &[SolidTileScenePrimitive]) { - unsafe { - gl::BindFramebuffer(gl::FRAMEBUFFER, 0); - gl::Viewport(0, - 0, - self.main_framebuffer_size.width as GLint, - self.main_framebuffer_size.height as GLint); - - gl::BindVertexArray(self.solid_tile_vertex_array.gl_vertex_array); - gl::UseProgram(self.solid_tile_program.program.gl_program); - gl::Uniform2f(self.solid_tile_program.framebuffer_size_uniform.location, - self.main_framebuffer_size.width as GLfloat, - self.main_framebuffer_size.height as GLfloat); - gl::Uniform2f(self.solid_tile_program.tile_size_uniform.location, - TILE_WIDTH as GLfloat, - TILE_HEIGHT as GLfloat); - self.fill_colors_texture.bind(0); - gl::Uniform1i(self.solid_tile_program.fill_colors_texture_uniform.location, 0); - gl::Uniform2f(self.solid_tile_program.fill_colors_texture_size_uniform.location, - FILL_COLORS_TEXTURE_WIDTH as GLfloat, - FILL_COLORS_TEXTURE_HEIGHT as GLfloat); - // FIXME(pcwalton): Fill this in properly! - gl::Uniform2f(self.solid_tile_program.view_box_origin_uniform.location, 0.0, 0.0); - gl::Disable(gl::BLEND); - gl::DrawArraysInstanced(gl::TRIANGLE_FAN, 0, 4, solid_tiles.len() as GLint); - } - } -} - -struct FillVertexArray { - gl_vertex_array: GLuint, - vertex_buffer: Buffer, -} - -impl FillVertexArray { - fn new(fill_program: &FillProgram, quad_vertex_positions_buffer: &Buffer) -> FillVertexArray { - let vertex_buffer = Buffer::new(); - let mut gl_vertex_array = 0; - unsafe { - let tess_coord_attr = VertexAttr::new(&fill_program.program, "TessCoord"); - let from_px_attr = VertexAttr::new(&fill_program.program, "FromPx"); - let to_px_attr = VertexAttr::new(&fill_program.program, "ToPx"); - let from_subpx_attr = VertexAttr::new(&fill_program.program, "FromSubpx"); - let to_subpx_attr = VertexAttr::new(&fill_program.program, "ToSubpx"); - let tile_index_attr = VertexAttr::new(&fill_program.program, "TileIndex"); - - gl::GenVertexArrays(1, &mut gl_vertex_array); - gl::BindVertexArray(gl_vertex_array); - gl::UseProgram(fill_program.program.gl_program); - gl::BindBuffer(gl::ARRAY_BUFFER, quad_vertex_positions_buffer.gl_buffer); - tess_coord_attr.configure_float(2, gl::UNSIGNED_BYTE, false, 0, 0, 0); - gl::BindBuffer(gl::ARRAY_BUFFER, vertex_buffer.gl_buffer); - from_px_attr.configure_int(1, gl::UNSIGNED_BYTE, FILL_INSTANCE_SIZE, 0, 1); - to_px_attr.configure_int(1, gl::UNSIGNED_BYTE, FILL_INSTANCE_SIZE, 1, 1); - from_subpx_attr.configure_float(2, gl::UNSIGNED_BYTE, true, FILL_INSTANCE_SIZE, 2, 1); - to_subpx_attr.configure_float(2, gl::UNSIGNED_BYTE, true, FILL_INSTANCE_SIZE, 4, 1); - tile_index_attr.configure_int(1, gl::UNSIGNED_SHORT, FILL_INSTANCE_SIZE, 6, 1); - } - - FillVertexArray { gl_vertex_array, vertex_buffer } - } -} - -impl Drop for FillVertexArray { - #[inline] - fn drop(&mut self) { - unsafe { - gl::DeleteVertexArrays(1, &mut self.gl_vertex_array); - } - } -} - -struct MaskTileVertexArray { - gl_vertex_array: GLuint, - vertex_buffer: Buffer, -} - -impl MaskTileVertexArray { - fn new(mask_tile_program: &MaskTileProgram, quad_vertex_positions_buffer: &Buffer) - -> MaskTileVertexArray { - let vertex_buffer = Buffer::new(); - let mut gl_vertex_array = 0; - unsafe { - let tess_coord_attr = VertexAttr::new(&mask_tile_program.program, "TessCoord"); - let tile_origin_attr = VertexAttr::new(&mask_tile_program.program, "TileOrigin"); - let backdrop_attr = VertexAttr::new(&mask_tile_program.program, "Backdrop"); - let object_attr = VertexAttr::new(&mask_tile_program.program, "Object"); - - gl::GenVertexArrays(1, &mut gl_vertex_array); - gl::BindVertexArray(gl_vertex_array); - gl::UseProgram(mask_tile_program.program.gl_program); - gl::BindBuffer(gl::ARRAY_BUFFER, quad_vertex_positions_buffer.gl_buffer); - tess_coord_attr.configure_float(2, gl::UNSIGNED_BYTE, false, 0, 0, 0); - gl::BindBuffer(gl::ARRAY_BUFFER, vertex_buffer.gl_buffer); - tile_origin_attr.configure_float(2, gl::SHORT, false, MASK_TILE_INSTANCE_SIZE, 0, 1); - backdrop_attr.configure_int(1, gl::SHORT, MASK_TILE_INSTANCE_SIZE, 4, 1); - object_attr.configure_int(2, gl::UNSIGNED_SHORT, MASK_TILE_INSTANCE_SIZE, 6, 1); - } - - MaskTileVertexArray { gl_vertex_array, vertex_buffer } - } -} - -impl Drop for MaskTileVertexArray { - #[inline] - fn drop(&mut self) { - unsafe { - gl::DeleteVertexArrays(1, &mut self.gl_vertex_array); - } - } -} - -struct SolidTileVertexArray { - gl_vertex_array: GLuint, - vertex_buffer: Buffer, -} - -impl SolidTileVertexArray { - fn new(solid_tile_program: &SolidTileProgram, quad_vertex_positions_buffer: &Buffer) - -> SolidTileVertexArray { - let vertex_buffer = Buffer::new(); - let mut gl_vertex_array = 0; - unsafe { - let tess_coord_attr = VertexAttr::new(&solid_tile_program.program, "TessCoord"); - let tile_origin_attr = VertexAttr::new(&solid_tile_program.program, "TileOrigin"); - let object_attr = VertexAttr::new(&solid_tile_program.program, "Object"); - - gl::GenVertexArrays(1, &mut gl_vertex_array); - gl::BindVertexArray(gl_vertex_array); - gl::UseProgram(solid_tile_program.program.gl_program); - gl::BindBuffer(gl::ARRAY_BUFFER, quad_vertex_positions_buffer.gl_buffer); - tess_coord_attr.configure_float(2, gl::UNSIGNED_BYTE, false, 0, 0, 0); - gl::BindBuffer(gl::ARRAY_BUFFER, vertex_buffer.gl_buffer); - tile_origin_attr.configure_float(2, gl::SHORT, false, SOLID_TILE_INSTANCE_SIZE, 0, 1); - object_attr.configure_int(1, gl::UNSIGNED_SHORT, SOLID_TILE_INSTANCE_SIZE, 4, 1); - } - - SolidTileVertexArray { gl_vertex_array, vertex_buffer } - } -} - -impl Drop for SolidTileVertexArray { - #[inline] - fn drop(&mut self) { - unsafe { - gl::DeleteVertexArrays(1, &mut self.gl_vertex_array); - } - } -} - -struct FillProgram { - program: Program, - framebuffer_size_uniform: Uniform, - tile_size_uniform: Uniform, - area_lut_uniform: Uniform, -} - -impl FillProgram { - fn new() -> FillProgram { - let program = Program::new("fill"); - let framebuffer_size_uniform = Uniform::new(&program, "FramebufferSize"); - let tile_size_uniform = Uniform::new(&program, "TileSize"); - let area_lut_uniform = Uniform::new(&program, "AreaLUT"); - FillProgram { program, framebuffer_size_uniform, tile_size_uniform, area_lut_uniform } - } -} - -struct SolidTileProgram { - program: Program, - framebuffer_size_uniform: Uniform, - tile_size_uniform: Uniform, - fill_colors_texture_uniform: Uniform, - fill_colors_texture_size_uniform: Uniform, - view_box_origin_uniform: Uniform, -} - -impl SolidTileProgram { - fn new() -> SolidTileProgram { - let program = Program::new("solid_tile"); - let framebuffer_size_uniform = Uniform::new(&program, "FramebufferSize"); - let tile_size_uniform = Uniform::new(&program, "TileSize"); - let fill_colors_texture_uniform = Uniform::new(&program, "FillColorsTexture"); - let fill_colors_texture_size_uniform = Uniform::new(&program, "FillColorsTextureSize"); - let view_box_origin_uniform = Uniform::new(&program, "ViewBoxOrigin"); - SolidTileProgram { - program, - framebuffer_size_uniform, - tile_size_uniform, - fill_colors_texture_uniform, - fill_colors_texture_size_uniform, - view_box_origin_uniform, - } - } -} - -struct MaskTileProgram { - program: Program, - framebuffer_size_uniform: Uniform, - tile_size_uniform: Uniform, - stencil_texture_uniform: Uniform, - stencil_texture_size_uniform: Uniform, - fill_colors_texture_uniform: Uniform, - fill_colors_texture_size_uniform: Uniform, - view_box_origin_uniform: Uniform, -} - -impl MaskTileProgram { - fn new() -> MaskTileProgram { - let program = Program::new("mask_tile"); - let framebuffer_size_uniform = Uniform::new(&program, "FramebufferSize"); - let tile_size_uniform = Uniform::new(&program, "TileSize"); - let stencil_texture_uniform = Uniform::new(&program, "StencilTexture"); - let stencil_texture_size_uniform = Uniform::new(&program, "StencilTextureSize"); - let fill_colors_texture_uniform = Uniform::new(&program, "FillColorsTexture"); - let fill_colors_texture_size_uniform = Uniform::new(&program, "FillColorsTextureSize"); - let view_box_origin_uniform = Uniform::new(&program, "ViewBoxOrigin"); - MaskTileProgram { - program, - framebuffer_size_uniform, - tile_size_uniform, - stencil_texture_uniform, - stencil_texture_size_uniform, - fill_colors_texture_uniform, - fill_colors_texture_size_uniform, - view_box_origin_uniform, - } - } -} diff --git a/gl/Cargo.toml b/gl/Cargo.toml new file mode 100644 index 00000000..5cccd0cf --- /dev/null +++ b/gl/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "pathfinder_gl" +version = "0.1.0" +edition = "2018" +authors = ["Patrick Walton "] + +[dependencies] +euclid = "0.19" +gl = "0.6" +serde = "1.0" +serde_derive = "1.0" +serde_json = "1.0" + +[dependencies.image] +version = "0.21" +default-features = false +features = ["png_codec"] + +[dependencies.pathfinder_geometry] +path = "../geometry" + +[dependencies.pathfinder_renderer] +path = "../renderer" diff --git a/demo3/src/debug_text.rs b/gl/src/debug.rs similarity index 99% rename from demo3/src/debug_text.rs rename to gl/src/debug.rs index 25a4b4a0..4ed731fd 100644 --- a/demo3/src/debug_text.rs +++ b/gl/src/debug.rs @@ -1,4 +1,4 @@ -// pathfinder/demo3/src/debug_text.rs +// pathfinder/gl/src/debug.rs // // Copyright © 2019 The Pathfinder Project Developers. // @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! Minimal text rendering. +//! A debug overlay. //! //! We don't render the demo UI text using Pathfinder itself so that we can use the debug UI to //! debug Pathfinder if it's totally busted. diff --git a/demo3/src/device.rs b/gl/src/device.rs similarity index 100% rename from demo3/src/device.rs rename to gl/src/device.rs diff --git a/gl/src/lib.rs b/gl/src/lib.rs new file mode 100644 index 00000000..928d84a2 --- /dev/null +++ b/gl/src/lib.rs @@ -0,0 +1,23 @@ +// pathfinder/gl/src/lib.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 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. + +#[macro_use] +extern crate serde_derive; + +pub mod debug; +pub mod device; +pub mod renderer; diff --git a/gl/src/renderer.rs b/gl/src/renderer.rs new file mode 100644 index 00000000..84d04892 --- /dev/null +++ b/gl/src/renderer.rs @@ -0,0 +1,450 @@ +// pathfinder/gl/src/renderer.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. + +use crate::debug::DebugRenderer; +use crate::device::{Buffer, BufferTarget, BufferUploadMode, Framebuffer, Program, Texture}; +use crate::device::{TimerQuery, Uniform, VertexAttr}; +use euclid::Size2D; +use gl::types::{GLfloat, GLint, GLuint}; +use pathfinder_renderer::gpu_data::{Batch, BuiltScene, SolidTileScenePrimitive}; +use pathfinder_renderer::paint::ObjectShader; +use pathfinder_renderer::tiles::{TILE_HEIGHT, TILE_WIDTH}; +use std::collections::VecDeque; +use std::time::Duration; + +static QUAD_VERTEX_POSITIONS: [u8; 8] = [0, 0, 1, 0, 1, 1, 0, 1]; + +const MASK_FRAMEBUFFER_WIDTH: u32 = TILE_WIDTH * 256; +const MASK_FRAMEBUFFER_HEIGHT: u32 = TILE_HEIGHT * 256; + +// TODO(pcwalton): Replace with `mem::size_of` calls? +const FILL_INSTANCE_SIZE: GLint = 8; +const SOLID_TILE_INSTANCE_SIZE: GLint = 6; +const MASK_TILE_INSTANCE_SIZE: GLint = 8; + +const FILL_COLORS_TEXTURE_WIDTH: u32 = 256; +const FILL_COLORS_TEXTURE_HEIGHT: u32 = 256; + +pub struct Renderer { + fill_program: FillProgram, + solid_tile_program: SolidTileProgram, + mask_tile_program: MaskTileProgram, + area_lut_texture: Texture, + #[allow(dead_code)] + quad_vertex_positions_buffer: Buffer, + fill_vertex_array: FillVertexArray, + mask_tile_vertex_array: MaskTileVertexArray, + solid_tile_vertex_array: SolidTileVertexArray, + mask_framebuffer: Framebuffer, + fill_colors_texture: Texture, + + pending_timer_queries: VecDeque, + free_timer_queries: Vec, + + pub debug_renderer: DebugRenderer, + + main_framebuffer_size: Size2D, +} + +impl Renderer { + pub fn new(main_framebuffer_size: &Size2D) -> Renderer { + let fill_program = FillProgram::new(); + let solid_tile_program = SolidTileProgram::new(); + let mask_tile_program = MaskTileProgram::new(); + + let area_lut_texture = Texture::from_png("area-lut"); + + let quad_vertex_positions_buffer = Buffer::new(); + quad_vertex_positions_buffer.upload(&QUAD_VERTEX_POSITIONS, + BufferTarget::Vertex, + BufferUploadMode::Static); + + let fill_vertex_array = FillVertexArray::new(&fill_program, &quad_vertex_positions_buffer); + let mask_tile_vertex_array = MaskTileVertexArray::new(&mask_tile_program, + &quad_vertex_positions_buffer); + let solid_tile_vertex_array = SolidTileVertexArray::new(&solid_tile_program, + &quad_vertex_positions_buffer); + + let mask_framebuffer = Framebuffer::new(&Size2D::new(MASK_FRAMEBUFFER_WIDTH, + MASK_FRAMEBUFFER_HEIGHT)); + + let fill_colors_texture = Texture::new_rgba(&Size2D::new(FILL_COLORS_TEXTURE_WIDTH, + FILL_COLORS_TEXTURE_HEIGHT)); + + let debug_renderer = DebugRenderer::new(main_framebuffer_size); + + Renderer { + fill_program, + solid_tile_program, + mask_tile_program, + area_lut_texture, + quad_vertex_positions_buffer, + fill_vertex_array, + mask_tile_vertex_array, + solid_tile_vertex_array, + mask_framebuffer, + fill_colors_texture, + + pending_timer_queries: VecDeque::new(), + free_timer_queries: vec![], + + debug_renderer, + + main_framebuffer_size: *main_framebuffer_size, + } + } + + pub fn render_scene(&mut self, built_scene: &BuiltScene) { + let timer_query = self.free_timer_queries.pop().unwrap_or_else(|| TimerQuery::new()); + timer_query.begin(); + + self.upload_shaders(&built_scene.shaders); + + self.upload_solid_tiles(&built_scene.solid_tiles); + self.draw_solid_tiles(&built_scene.solid_tiles); + + for batch in &built_scene.batches { + self.upload_batch(batch); + self.draw_batch_fills(batch); + self.draw_batch_mask_tiles(batch); + } + + timer_query.end(); + self.pending_timer_queries.push_back(timer_query); + } + + pub fn shift_timer_query(&mut self) -> Option { + let query = self.pending_timer_queries.front()?; + if !query.is_available() { + return None + } + let query = self.pending_timer_queries.pop_front().unwrap(); + let result = Duration::from_nanos(query.get()); + self.free_timer_queries.push(query); + Some(result) + } + + fn upload_shaders(&mut self, shaders: &[ObjectShader]) { + let size = Size2D::new(FILL_COLORS_TEXTURE_WIDTH, FILL_COLORS_TEXTURE_HEIGHT); + let mut fill_colors = vec![0; size.width as usize * size.height as usize * 4]; + for (shader_index, shader) in shaders.iter().enumerate() { + fill_colors[shader_index * 4 + 0] = shader.fill_color.r; + fill_colors[shader_index * 4 + 1] = shader.fill_color.g; + fill_colors[shader_index * 4 + 2] = shader.fill_color.b; + fill_colors[shader_index * 4 + 3] = shader.fill_color.a; + } + self.fill_colors_texture.upload_rgba(&size, &fill_colors); + } + + fn upload_solid_tiles(&mut self, solid_tiles: &[SolidTileScenePrimitive]) { + self.solid_tile_vertex_array + .vertex_buffer + .upload(solid_tiles, BufferTarget::Vertex, BufferUploadMode::Dynamic); + } + + fn upload_batch(&mut self, batch: &Batch) { + self.fill_vertex_array + .vertex_buffer + .upload(&batch.fills, BufferTarget::Vertex, BufferUploadMode::Dynamic); + self.mask_tile_vertex_array + .vertex_buffer + .upload(&batch.mask_tiles, BufferTarget::Vertex, BufferUploadMode::Dynamic); + } + + fn draw_batch_fills(&mut self, batch: &Batch) { + unsafe { + gl::BindFramebuffer(gl::FRAMEBUFFER, self.mask_framebuffer.gl_framebuffer); + gl::Viewport(0, 0, MASK_FRAMEBUFFER_WIDTH as GLint, MASK_FRAMEBUFFER_HEIGHT as GLint); + // TODO(pcwalton): Only clear the appropriate portion? + gl::ClearColor(0.0, 0.0, 0.0, 0.0); + gl::Clear(gl::COLOR_BUFFER_BIT); + + gl::BindVertexArray(self.fill_vertex_array.gl_vertex_array); + gl::UseProgram(self.fill_program.program.gl_program); + gl::Uniform2f(self.fill_program.framebuffer_size_uniform.location, + MASK_FRAMEBUFFER_WIDTH as GLfloat, + MASK_FRAMEBUFFER_HEIGHT as GLfloat); + gl::Uniform2f(self.fill_program.tile_size_uniform.location, + TILE_WIDTH as GLfloat, + TILE_HEIGHT as GLfloat); + self.area_lut_texture.bind(0); + gl::Uniform1i(self.fill_program.area_lut_uniform.location, 0); + gl::BlendEquation(gl::FUNC_ADD); + gl::BlendFunc(gl::ONE, gl::ONE); + gl::Enable(gl::BLEND); + gl::DrawArraysInstanced(gl::TRIANGLE_FAN, 0, 4, batch.fills.len() as GLint); + gl::Disable(gl::BLEND); + } + } + + fn draw_batch_mask_tiles(&mut self, batch: &Batch) { + unsafe { + gl::BindFramebuffer(gl::FRAMEBUFFER, 0); + gl::Viewport(0, + 0, + self.main_framebuffer_size.width as GLint, + self.main_framebuffer_size.height as GLint); + + gl::BindVertexArray(self.mask_tile_vertex_array.gl_vertex_array); + gl::UseProgram(self.mask_tile_program.program.gl_program); + gl::Uniform2f(self.mask_tile_program.framebuffer_size_uniform.location, + self.main_framebuffer_size.width as GLfloat, + self.main_framebuffer_size.height as GLfloat); + gl::Uniform2f(self.mask_tile_program.tile_size_uniform.location, + TILE_WIDTH as GLfloat, + TILE_HEIGHT as GLfloat); + self.mask_framebuffer.texture.bind(0); + gl::Uniform1i(self.mask_tile_program.stencil_texture_uniform.location, 0); + gl::Uniform2f(self.mask_tile_program.stencil_texture_size_uniform.location, + MASK_FRAMEBUFFER_WIDTH as GLfloat, + MASK_FRAMEBUFFER_HEIGHT as GLfloat); + self.fill_colors_texture.bind(1); + gl::Uniform1i(self.mask_tile_program.fill_colors_texture_uniform.location, 1); + gl::Uniform2f(self.mask_tile_program.fill_colors_texture_size_uniform.location, + FILL_COLORS_TEXTURE_WIDTH as GLfloat, + FILL_COLORS_TEXTURE_HEIGHT as GLfloat); + // FIXME(pcwalton): Fill this in properly! + gl::Uniform2f(self.mask_tile_program.view_box_origin_uniform.location, 0.0, 0.0); + gl::BlendEquation(gl::FUNC_ADD); + gl::BlendFuncSeparate(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA, gl::ONE, gl::ONE); + gl::Enable(gl::BLEND); + gl::DrawArraysInstanced(gl::TRIANGLE_FAN, 0, 4, batch.mask_tiles.len() as GLint); + gl::Disable(gl::BLEND); + } + } + + fn draw_solid_tiles(&mut self, solid_tiles: &[SolidTileScenePrimitive]) { + unsafe { + gl::BindFramebuffer(gl::FRAMEBUFFER, 0); + gl::Viewport(0, + 0, + self.main_framebuffer_size.width as GLint, + self.main_framebuffer_size.height as GLint); + + gl::BindVertexArray(self.solid_tile_vertex_array.gl_vertex_array); + gl::UseProgram(self.solid_tile_program.program.gl_program); + gl::Uniform2f(self.solid_tile_program.framebuffer_size_uniform.location, + self.main_framebuffer_size.width as GLfloat, + self.main_framebuffer_size.height as GLfloat); + gl::Uniform2f(self.solid_tile_program.tile_size_uniform.location, + TILE_WIDTH as GLfloat, + TILE_HEIGHT as GLfloat); + self.fill_colors_texture.bind(0); + gl::Uniform1i(self.solid_tile_program.fill_colors_texture_uniform.location, 0); + gl::Uniform2f(self.solid_tile_program.fill_colors_texture_size_uniform.location, + FILL_COLORS_TEXTURE_WIDTH as GLfloat, + FILL_COLORS_TEXTURE_HEIGHT as GLfloat); + // FIXME(pcwalton): Fill this in properly! + gl::Uniform2f(self.solid_tile_program.view_box_origin_uniform.location, 0.0, 0.0); + gl::Disable(gl::BLEND); + gl::DrawArraysInstanced(gl::TRIANGLE_FAN, 0, 4, solid_tiles.len() as GLint); + } + } +} + +struct FillVertexArray { + gl_vertex_array: GLuint, + vertex_buffer: Buffer, +} + +impl FillVertexArray { + fn new(fill_program: &FillProgram, quad_vertex_positions_buffer: &Buffer) -> FillVertexArray { + let vertex_buffer = Buffer::new(); + let mut gl_vertex_array = 0; + unsafe { + let tess_coord_attr = VertexAttr::new(&fill_program.program, "TessCoord"); + let from_px_attr = VertexAttr::new(&fill_program.program, "FromPx"); + let to_px_attr = VertexAttr::new(&fill_program.program, "ToPx"); + let from_subpx_attr = VertexAttr::new(&fill_program.program, "FromSubpx"); + let to_subpx_attr = VertexAttr::new(&fill_program.program, "ToSubpx"); + let tile_index_attr = VertexAttr::new(&fill_program.program, "TileIndex"); + + gl::GenVertexArrays(1, &mut gl_vertex_array); + gl::BindVertexArray(gl_vertex_array); + gl::UseProgram(fill_program.program.gl_program); + gl::BindBuffer(gl::ARRAY_BUFFER, quad_vertex_positions_buffer.gl_buffer); + tess_coord_attr.configure_float(2, gl::UNSIGNED_BYTE, false, 0, 0, 0); + gl::BindBuffer(gl::ARRAY_BUFFER, vertex_buffer.gl_buffer); + from_px_attr.configure_int(1, gl::UNSIGNED_BYTE, FILL_INSTANCE_SIZE, 0, 1); + to_px_attr.configure_int(1, gl::UNSIGNED_BYTE, FILL_INSTANCE_SIZE, 1, 1); + from_subpx_attr.configure_float(2, gl::UNSIGNED_BYTE, true, FILL_INSTANCE_SIZE, 2, 1); + to_subpx_attr.configure_float(2, gl::UNSIGNED_BYTE, true, FILL_INSTANCE_SIZE, 4, 1); + tile_index_attr.configure_int(1, gl::UNSIGNED_SHORT, FILL_INSTANCE_SIZE, 6, 1); + } + + FillVertexArray { gl_vertex_array, vertex_buffer } + } +} + +impl Drop for FillVertexArray { + #[inline] + fn drop(&mut self) { + unsafe { + gl::DeleteVertexArrays(1, &mut self.gl_vertex_array); + } + } +} + +struct MaskTileVertexArray { + gl_vertex_array: GLuint, + vertex_buffer: Buffer, +} + +impl MaskTileVertexArray { + fn new(mask_tile_program: &MaskTileProgram, quad_vertex_positions_buffer: &Buffer) + -> MaskTileVertexArray { + let vertex_buffer = Buffer::new(); + let mut gl_vertex_array = 0; + unsafe { + let tess_coord_attr = VertexAttr::new(&mask_tile_program.program, "TessCoord"); + let tile_origin_attr = VertexAttr::new(&mask_tile_program.program, "TileOrigin"); + let backdrop_attr = VertexAttr::new(&mask_tile_program.program, "Backdrop"); + let object_attr = VertexAttr::new(&mask_tile_program.program, "Object"); + + gl::GenVertexArrays(1, &mut gl_vertex_array); + gl::BindVertexArray(gl_vertex_array); + gl::UseProgram(mask_tile_program.program.gl_program); + gl::BindBuffer(gl::ARRAY_BUFFER, quad_vertex_positions_buffer.gl_buffer); + tess_coord_attr.configure_float(2, gl::UNSIGNED_BYTE, false, 0, 0, 0); + gl::BindBuffer(gl::ARRAY_BUFFER, vertex_buffer.gl_buffer); + tile_origin_attr.configure_float(2, gl::SHORT, false, MASK_TILE_INSTANCE_SIZE, 0, 1); + backdrop_attr.configure_int(1, gl::SHORT, MASK_TILE_INSTANCE_SIZE, 4, 1); + object_attr.configure_int(2, gl::UNSIGNED_SHORT, MASK_TILE_INSTANCE_SIZE, 6, 1); + } + + MaskTileVertexArray { gl_vertex_array, vertex_buffer } + } +} + +impl Drop for MaskTileVertexArray { + #[inline] + fn drop(&mut self) { + unsafe { + gl::DeleteVertexArrays(1, &mut self.gl_vertex_array); + } + } +} + +struct SolidTileVertexArray { + gl_vertex_array: GLuint, + vertex_buffer: Buffer, +} + +impl SolidTileVertexArray { + fn new(solid_tile_program: &SolidTileProgram, quad_vertex_positions_buffer: &Buffer) + -> SolidTileVertexArray { + let vertex_buffer = Buffer::new(); + let mut gl_vertex_array = 0; + unsafe { + let tess_coord_attr = VertexAttr::new(&solid_tile_program.program, "TessCoord"); + let tile_origin_attr = VertexAttr::new(&solid_tile_program.program, "TileOrigin"); + let object_attr = VertexAttr::new(&solid_tile_program.program, "Object"); + + gl::GenVertexArrays(1, &mut gl_vertex_array); + gl::BindVertexArray(gl_vertex_array); + gl::UseProgram(solid_tile_program.program.gl_program); + gl::BindBuffer(gl::ARRAY_BUFFER, quad_vertex_positions_buffer.gl_buffer); + tess_coord_attr.configure_float(2, gl::UNSIGNED_BYTE, false, 0, 0, 0); + gl::BindBuffer(gl::ARRAY_BUFFER, vertex_buffer.gl_buffer); + tile_origin_attr.configure_float(2, gl::SHORT, false, SOLID_TILE_INSTANCE_SIZE, 0, 1); + object_attr.configure_int(1, gl::UNSIGNED_SHORT, SOLID_TILE_INSTANCE_SIZE, 4, 1); + } + + SolidTileVertexArray { gl_vertex_array, vertex_buffer } + } +} + +impl Drop for SolidTileVertexArray { + #[inline] + fn drop(&mut self) { + unsafe { + gl::DeleteVertexArrays(1, &mut self.gl_vertex_array); + } + } +} + +struct FillProgram { + program: Program, + framebuffer_size_uniform: Uniform, + tile_size_uniform: Uniform, + area_lut_uniform: Uniform, +} + +impl FillProgram { + fn new() -> FillProgram { + let program = Program::new("fill"); + let framebuffer_size_uniform = Uniform::new(&program, "FramebufferSize"); + let tile_size_uniform = Uniform::new(&program, "TileSize"); + let area_lut_uniform = Uniform::new(&program, "AreaLUT"); + FillProgram { program, framebuffer_size_uniform, tile_size_uniform, area_lut_uniform } + } +} + +struct SolidTileProgram { + program: Program, + framebuffer_size_uniform: Uniform, + tile_size_uniform: Uniform, + fill_colors_texture_uniform: Uniform, + fill_colors_texture_size_uniform: Uniform, + view_box_origin_uniform: Uniform, +} + +impl SolidTileProgram { + fn new() -> SolidTileProgram { + let program = Program::new("solid_tile"); + let framebuffer_size_uniform = Uniform::new(&program, "FramebufferSize"); + let tile_size_uniform = Uniform::new(&program, "TileSize"); + let fill_colors_texture_uniform = Uniform::new(&program, "FillColorsTexture"); + let fill_colors_texture_size_uniform = Uniform::new(&program, "FillColorsTextureSize"); + let view_box_origin_uniform = Uniform::new(&program, "ViewBoxOrigin"); + SolidTileProgram { + program, + framebuffer_size_uniform, + tile_size_uniform, + fill_colors_texture_uniform, + fill_colors_texture_size_uniform, + view_box_origin_uniform, + } + } +} + +struct MaskTileProgram { + program: Program, + framebuffer_size_uniform: Uniform, + tile_size_uniform: Uniform, + stencil_texture_uniform: Uniform, + stencil_texture_size_uniform: Uniform, + fill_colors_texture_uniform: Uniform, + fill_colors_texture_size_uniform: Uniform, + view_box_origin_uniform: Uniform, +} + +impl MaskTileProgram { + fn new() -> MaskTileProgram { + let program = Program::new("mask_tile"); + let framebuffer_size_uniform = Uniform::new(&program, "FramebufferSize"); + let tile_size_uniform = Uniform::new(&program, "TileSize"); + let stencil_texture_uniform = Uniform::new(&program, "StencilTexture"); + let stencil_texture_size_uniform = Uniform::new(&program, "StencilTextureSize"); + let fill_colors_texture_uniform = Uniform::new(&program, "FillColorsTexture"); + let fill_colors_texture_size_uniform = Uniform::new(&program, "FillColorsTextureSize"); + let view_box_origin_uniform = Uniform::new(&program, "ViewBoxOrigin"); + MaskTileProgram { + program, + framebuffer_size_uniform, + tile_size_uniform, + stencil_texture_uniform, + stencil_texture_size_uniform, + fill_colors_texture_uniform, + fill_colors_texture_size_uniform, + view_box_origin_uniform, + } + } +}