Display timing info in the demo

This commit is contained in:
Patrick Walton 2019-01-30 14:42:06 -08:00
parent 6b8848bb35
commit e105bdb7c9
13 changed files with 1044 additions and 386 deletions

View File

@ -11,6 +11,9 @@ 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]

View File

@ -0,0 +1,105 @@
{
"name": "D-DIN",
"size": 28,
"bold": false,
"italic": false,
"width": 512,
"height": 128,
"characters": {
"0":{"x":338,"y":0,"width":18,"height":30,"originX":0,"originY":28,"advance":18},
"1":{"x":30,"y":64,"width":11,"height":29,"originX":0,"originY":28,"advance":12},
"2":{"x":490,"y":35,"width":17,"height":29,"originX":0,"originY":28,"advance":17},
"3":{"x":356,"y":0,"width":18,"height":30,"originX":0,"originY":28,"advance":18},
"4":{"x":398,"y":35,"width":19,"height":29,"originX":0,"originY":28,"advance":19},
"5":{"x":374,"y":0,"width":18,"height":30,"originX":0,"originY":28,"advance":18},
"6":{"x":392,"y":0,"width":18,"height":30,"originX":0,"originY":28,"advance":18},
"7":{"x":436,"y":35,"width":18,"height":29,"originX":1,"originY":28,"advance":16},
"8":{"x":319,"y":0,"width":19,"height":30,"originX":0,"originY":28,"advance":19},
"9":{"x":454,"y":35,"width":18,"height":29,"originX":0,"originY":28,"advance":18},
" ":{"x":147,"y":93,"width":3,"height":3,"originX":1,"originY":1,"advance":8},
"!":{"x":62,"y":64,"width":7,"height":29,"originX":-1,"originY":28,"advance":9},
"\"":{"x":52,"y":93,"width":12,"height":12,"originX":0,"originY":28,"advance":11},
"#":{"x":233,"y":35,"width":21,"height":29,"originX":1,"originY":28,"advance":19},
"$":{"x":138,"y":0,"width":18,"height":34,"originX":1,"originY":30,"advance":17},
"%":{"x":156,"y":0,"width":31,"height":30,"originX":0,"originY":28,"advance":31},
"&":{"x":187,"y":0,"width":22,"height":30,"originX":0,"originY":28,"advance":22},
"'":{"x":64,"y":93,"width":6,"height":12,"originX":0,"originY":28,"advance":6},
"(":{"x":59,"y":0,"width":10,"height":35,"originX":0,"originY":30,"advance":11},
")":{"x":48,"y":0,"width":11,"height":35,"originX":0,"originY":30,"advance":11},
"*":{"x":19,"y":93,"width":14,"height":14,"originX":0,"originY":28,"advance":14},
"+":{"x":432,"y":64,"width":19,"height":19,"originX":0,"originY":20,"advance":19},
",":{"x":70,"y":93,"width":6,"height":11,"originX":0,"originY":5,"advance":6},
"-":{"x":105,"y":93,"width":14,"height":6,"originX":0,"originY":16,"advance":15},
".":{"x":119,"y":93,"width":7,"height":6,"originX":0,"originY":5,"advance":7},
"/":{"x":446,"y":0,"width":14,"height":30,"originX":0,"originY":28,"advance":15},
":":{"x":470,"y":64,"width":7,"height":19,"originX":0,"originY":18,"advance":7},
";":{"x":171,"y":64,"width":7,"height":24,"originX":0,"originY":18,"advance":7},
"<":{"x":477,"y":64,"width":19,"height":17,"originX":0,"originY":19,"advance":19},
"=":{"x":33,"y":93,"width":19,"height":13,"originX":0,"originY":17,"advance":19},
">":{"x":0,"y":93,"width":19,"height":17,"originX":0,"originY":19,"advance":19},
"?":{"x":0,"y":64,"width":17,"height":29,"originX":1,"originY":28,"advance":16},
"@":{"x":106,"y":0,"width":32,"height":34,"originX":0,"originY":28,"advance":32},
"A":{"x":26,"y":35,"width":25,"height":29,"originX":2,"originY":28,"advance":22},
"B":{"x":254,"y":35,"width":21,"height":29,"originX":-1,"originY":28,"advance":23},
"C":{"x":209,"y":0,"width":22,"height":30,"originX":0,"originY":28,"advance":23},
"D":{"x":145,"y":35,"width":22,"height":29,"originX":-1,"originY":28,"advance":23},
"E":{"x":338,"y":35,"width":20,"height":29,"originX":-1,"originY":28,"advance":20},
"F":{"x":358,"y":35,"width":20,"height":29,"originX":-1,"originY":28,"advance":20},
"G":{"x":231,"y":0,"width":22,"height":30,"originX":0,"originY":28,"advance":23},
"H":{"x":275,"y":35,"width":21,"height":29,"originX":-1,"originY":28,"advance":23},
"I":{"x":69,"y":64,"width":7,"height":29,"originX":-1,"originY":28,"advance":9},
"J":{"x":41,"y":64,"width":11,"height":29,"originX":1,"originY":28,"advance":12},
"K":{"x":76,"y":35,"width":23,"height":29,"originX":-1,"originY":28,"advance":22},
"L":{"x":378,"y":35,"width":20,"height":29,"originX":-1,"originY":28,"advance":20},
"M":{"x":0,"y":35,"width":26,"height":29,"originX":-1,"originY":28,"advance":28},
"N":{"x":99,"y":35,"width":23,"height":29,"originX":-1,"originY":28,"advance":25},
"O":{"x":253,"y":0,"width":22,"height":30,"originX":0,"originY":28,"advance":23},
"P":{"x":296,"y":35,"width":21,"height":29,"originX":-1,"originY":28,"advance":22},
"Q":{"x":0,"y":0,"width":22,"height":35,"originX":0,"originY":28,"advance":23},
"R":{"x":167,"y":35,"width":22,"height":29,"originX":-1,"originY":28,"advance":22},
"S":{"x":275,"y":0,"width":22,"height":30,"originX":1,"originY":28,"advance":20},
"T":{"x":189,"y":35,"width":22,"height":29,"originX":2,"originY":28,"advance":18},
"U":{"x":297,"y":0,"width":22,"height":30,"originX":-1,"originY":28,"advance":24},
"V":{"x":122,"y":35,"width":23,"height":29,"originX":2,"originY":28,"advance":19},
"W":{"x":474,"y":0,"width":33,"height":29,"originX":2,"originY":28,"advance":30},
"X":{"x":51,"y":35,"width":25,"height":29,"originX":2,"originY":28,"advance":21},
"Y":{"x":211,"y":35,"width":22,"height":29,"originX":2,"originY":28,"advance":19},
"Z":{"x":317,"y":35,"width":21,"height":29,"originX":1,"originY":28,"advance":19},
"[":{"x":69,"y":0,"width":10,"height":35,"originX":-1,"originY":30,"advance":11},
"\\":{"x":460,"y":0,"width":14,"height":30,"originX":0,"originY":28,"advance":15},
"]":{"x":79,"y":0,"width":10,"height":35,"originX":0,"originY":30,"advance":11},
"^":{"x":451,"y":64,"width":19,"height":19,"originX":0,"originY":28,"advance":19},
"_":{"x":126,"y":93,"width":21,"height":5,"originX":2,"originY":0,"advance":17},
"`":{"x":95,"y":93,"width":10,"height":8,"originX":-4,"originY":28,"advance":19},
"a":{"x":235,"y":64,"width":18,"height":23,"originX":0,"originY":21,"advance":19},
"b":{"x":410,"y":0,"width":18,"height":30,"originX":-1,"originY":28,"advance":19},
"c":{"x":178,"y":64,"width":19,"height":23,"originX":0,"originY":21,"advance":19},
"d":{"x":428,"y":0,"width":18,"height":30,"originX":0,"originY":28,"advance":19},
"e":{"x":197,"y":64,"width":19,"height":23,"originX":0,"originY":21,"advance":19},
"f":{"x":17,"y":64,"width":13,"height":29,"originX":0,"originY":28,"advance":12},
"g":{"x":96,"y":64,"width":18,"height":28,"originX":0,"originY":21,"advance":19},
"h":{"x":472,"y":35,"width":18,"height":29,"originX":-1,"originY":28,"advance":19},
"i":{"x":150,"y":64,"width":7,"height":28,"originX":-1,"originY":27,"advance":9},
"j":{"x":89,"y":0,"width":10,"height":35,"originX":3,"originY":28,"advance":8},
"k":{"x":417,"y":35,"width":19,"height":29,"originX":-1,"originY":28,"advance":18},
"l":{"x":52,"y":64,"width":10,"height":29,"originX":-1,"originY":28,"advance":10},
"m":{"x":289,"y":64,"width":29,"height":22,"originX":-1,"originY":21,"advance":31},
"n":{"x":366,"y":64,"width":18,"height":22,"originX":-1,"originY":21,"advance":19},
"o":{"x":216,"y":64,"width":19,"height":23,"originX":0,"originY":21,"advance":19},
"p":{"x":114,"y":64,"width":18,"height":28,"originX":-1,"originY":21,"advance":19},
"q":{"x":132,"y":64,"width":18,"height":28,"originX":0,"originY":21,"advance":19},
"r":{"x":419,"y":64,"width":13,"height":22,"originX":-1,"originY":21,"advance":13},
"s":{"x":253,"y":64,"width":18,"height":23,"originX":0,"originY":21,"advance":17},
"t":{"x":157,"y":64,"width":14,"height":27,"originX":1,"originY":26,"advance":13},
"u":{"x":271,"y":64,"width":18,"height":23,"originX":-1,"originY":21,"advance":19},
"v":{"x":347,"y":64,"width":19,"height":22,"originX":1,"originY":21,"advance":16},
"w":{"x":318,"y":64,"width":29,"height":22,"originX":1,"originY":21,"advance":27},
"x":{"x":384,"y":64,"width":18,"height":22,"originX":1,"originY":21,"advance":16},
"y":{"x":76,"y":64,"width":20,"height":28,"originX":2,"originY":21,"advance":16},
"z":{"x":402,"y":64,"width":17,"height":22,"originX":0,"originY":21,"advance":17},
"{":{"x":22,"y":0,"width":13,"height":35,"originX":0,"originY":30,"advance":13},
"|":{"x":99,"y":0,"width":7,"height":35,"originX":-2,"originY":30,"advance":11},
"}":{"x":35,"y":0,"width":13,"height":35,"originX":0,"originY":30,"advance":13},
"~":{"x":76,"y":93,"width":19,"height":9,"originX":0,"originY":15,"advance":19}
}
}

View File

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -0,0 +1,24 @@
#version 330
// pathfinder/demo3/shaders/debug_font.fs.glsl
//
// 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.
precision highp float;
uniform sampler2D uFontTexture;
in vec2 vTexCoord;
out vec4 oFragColor;
void main() {
float coverage = texture(uFontTexture, vTexCoord).r;
oFragColor = vec4(coverage);
}

View File

@ -0,0 +1,27 @@
#version 330
// pathfinder/demo3/shaders/debug_font.vs.glsl
//
// 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.
precision highp float;
uniform vec2 uFramebufferSize;
uniform vec2 uFontTextureSize;
in vec2 aPosition;
in vec2 aTexCoord;
out vec2 vTexCoord;
void main() {
vTexCoord = aTexCoord / uFontTextureSize;
vec2 position = aPosition / uFramebufferSize * 2.0 - 1.0;
gl_Position = vec4(position.x, -position.y, 0.0, 1.0);
}

View File

@ -0,0 +1,21 @@
#version 330
// pathfinder/demo3/shaders/debug_solid.fs.glsl
//
// 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.
precision highp float;
uniform vec4 uColor;
out vec4 oFragColor;
void main() {
oFragColor = uColor;
}

View File

@ -0,0 +1,22 @@
#version 330
// pathfinder/demo3/shaders/debug_solid.vs.glsl
//
// 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.
precision highp float;
uniform vec2 uFramebufferSize;
in vec2 aPosition;
void main() {
vec2 position = aPosition / uFramebufferSize * 2.0 - 1.0;
gl_Position = vec4(position.x, -position.y, 0.0, 1.0);
}

View File

@ -1,6 +1,6 @@
#version 330
// pathfinder/demo3/resources/shaders/mask_tile.vs.glsl
// pathfinder/demo3/shaders/mask_tile.vs.glsl
//
// Copyright © 2018 The Pathfinder Project Developers.
//

374
demo3/src/debug_text.rs Normal file
View File

@ -0,0 +1,374 @@
// pathfinder/demo3/src/debug_text.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.
//! Minimal text rendering.
//!
//! 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.
//!
//! The debug font atlas was generated using: https://evanw.github.io/font-texture-generator/
use crate::device::{Buffer, BufferTarget, BufferUploadMode, Program, Texture, Uniform, VertexAttr};
use euclid::{Point2D, Rect, Size2D};
use gl::types::{GLfloat, GLint, GLsizei, GLuint};
use gl;
use pathfinder_renderer::paint::ColorU;
use serde_json;
use std::collections::HashMap;
use std::fs::File;
use std::io::BufReader;
use std::ptr;
use std::time::Duration;
const DEBUG_FONT_VERTEX_SIZE: GLint = 8;
const DEBUG_SOLID_VERTEX_SIZE: GLint = 4;
const WINDOW_WIDTH: i16 = 400;
const WINDOW_HEIGHT: i16 = LINE_HEIGHT * 2 + PADDING + 3;
const PADDING: i16 = 12;
const FONT_ASCENT: i16 = 28;
const LINE_HEIGHT: i16 = 42;
static WINDOW_COLOR: ColorU = ColorU { r: 30, g: 30, b: 30, a: 255 - 30 };
static JSON_PATH: &'static str = "resources/debug-font.json";
static PNG_NAME: &'static str = "debug-font";
static QUAD_INDICES: [u32; 6] = [0, 1, 3, 1, 2, 3];
#[derive(Deserialize)]
#[allow(dead_code)]
pub struct DebugFont {
name: String,
size: i32,
bold: bool,
italic: bool,
width: u32,
height: u32,
characters: HashMap<char, DebugCharacter>,
}
#[derive(Deserialize)]
struct DebugCharacter {
x: u16,
y: u16,
width: u16,
height: u16,
#[serde(rename = "originX")]
origin_x: i16,
#[serde(rename = "originY")]
origin_y: i16,
advance: i16,
}
impl DebugFont {
fn load() -> DebugFont {
serde_json::from_reader(BufReader::new(File::open(JSON_PATH).unwrap())).unwrap()
}
}
pub struct DebugRenderer {
framebuffer_size: Size2D<u32>,
font_program: DebugFontProgram,
font_vertex_array: DebugFontVertexArray,
font_texture: Texture,
font: DebugFont,
solid_program: DebugSolidProgram,
solid_vertex_array: DebugSolidVertexArray,
}
impl DebugRenderer {
pub fn new(framebuffer_size: &Size2D<u32>) -> DebugRenderer {
let font_program = DebugFontProgram::new();
let font_vertex_array = DebugFontVertexArray::new(&font_program);
let font_texture = Texture::from_png(PNG_NAME);
let font = DebugFont::load();
let solid_program = DebugSolidProgram::new();
let solid_vertex_array = DebugSolidVertexArray::new(&solid_program);
solid_vertex_array.index_buffer.upload(&QUAD_INDICES,
BufferTarget::Index,
BufferUploadMode::Static);
DebugRenderer {
framebuffer_size: *framebuffer_size,
font_program,
font_vertex_array,
font_texture,
font,
solid_program,
solid_vertex_array,
}
}
pub fn set_framebuffer_size(&mut self, window_size: &Size2D<u32>) {
self.framebuffer_size = *window_size;
}
pub fn draw(&self, prepare_time: Duration, tile_time: Duration) {
let window_rect =
Rect::new(Point2D::new(self.framebuffer_size.width as i16 - PADDING - WINDOW_WIDTH,
self.framebuffer_size.height as i16 - PADDING - WINDOW_HEIGHT),
Size2D::new(WINDOW_WIDTH, WINDOW_HEIGHT));
self.draw_solid_rect(&window_rect, WINDOW_COLOR);
self.draw_text(&format!("Preparation: {:.3} ms", duration_ms(prepare_time)),
&Point2D::new(window_rect.origin.x + PADDING,
window_rect.origin.y + PADDING + FONT_ASCENT));
self.draw_text(&format!("Tiling: {:.3} ms", duration_ms(tile_time)),
&Point2D::new(window_rect.origin.x + PADDING,
window_rect.origin.y + PADDING + FONT_ASCENT + LINE_HEIGHT));
}
fn draw_solid_rect(&self, rect: &Rect<i16>, color: ColorU) {
let vertex_data = [
DebugSolidVertex::new(rect.origin),
DebugSolidVertex::new(rect.top_right()),
DebugSolidVertex::new(rect.bottom_right()),
DebugSolidVertex::new(rect.bottom_left()),
];
self.solid_vertex_array
.vertex_buffer
.upload(&vertex_data, BufferTarget::Vertex, BufferUploadMode::Dynamic);
unsafe {
gl::BindVertexArray(self.solid_vertex_array.gl_vertex_array);
gl::UseProgram(self.solid_program.program.gl_program);
gl::Uniform2f(self.solid_program.framebuffer_size_uniform.location,
self.framebuffer_size.width as GLfloat,
self.framebuffer_size.height as GLfloat);
gl::Uniform4f(self.solid_program.color_uniform.location,
color.r as f32 * (1.0 / 255.0),
color.g as f32 * (1.0 / 255.0),
color.b as f32 * (1.0 / 255.0),
color.a as f32 * (1.0 / 255.0));
gl::BlendEquation(gl::FUNC_ADD);
gl::BlendFunc(gl::ONE, gl::ONE_MINUS_SRC_ALPHA);
gl::Enable(gl::BLEND);
gl::DrawElements(gl::TRIANGLES, 6, gl::UNSIGNED_INT, ptr::null());
gl::Disable(gl::BLEND);
}
}
fn draw_text(&self, string: &str, origin: &Point2D<i16>) {
let mut next = *origin;
let char_count = string.chars().count();
let mut vertex_data = Vec::with_capacity(char_count * 4);
let mut index_data = Vec::with_capacity(char_count * 6);
for mut character in string.chars() {
if !self.font.characters.contains_key(&character) {
character = '?';
}
let info = &self.font.characters[&character];
let position_rect =
Rect::new(Point2D::new(next.x - info.origin_x, next.y - info.origin_y),
Size2D::new(info.width as i16, info.height as i16));
let tex_coord_rect = Rect::new(Point2D::new(info.x, info.y),
Size2D::new(info.width, info.height));
let first_vertex_index = vertex_data.len();
vertex_data.extend_from_slice(&[
DebugFontVertex::new(position_rect.origin, tex_coord_rect.origin),
DebugFontVertex::new(position_rect.top_right(), tex_coord_rect.top_right()),
DebugFontVertex::new(position_rect.bottom_right(), tex_coord_rect.bottom_right()),
DebugFontVertex::new(position_rect.bottom_left(), tex_coord_rect.bottom_left()),
]);
index_data.extend(QUAD_INDICES.iter().map(|&i| i + first_vertex_index as u32));
next.x += info.advance;
}
self.font_vertex_array
.vertex_buffer
.upload(&vertex_data, BufferTarget::Vertex, BufferUploadMode::Dynamic);
self.font_vertex_array
.index_buffer
.upload(&index_data, BufferTarget::Index, BufferUploadMode::Dynamic);
unsafe {
gl::BindVertexArray(self.font_vertex_array.gl_vertex_array);
gl::UseProgram(self.font_program.program.gl_program);
gl::Uniform2f(self.font_program.framebuffer_size_uniform.location,
self.framebuffer_size.width as GLfloat,
self.framebuffer_size.height as GLfloat);
gl::Uniform2f(self.font_program.font_texture_size_uniform.location,
self.font_texture.size.width as GLfloat,
self.font_texture.size.height as GLfloat);
self.font_texture.bind(0);
gl::Uniform1i(self.font_program.font_texture_uniform.location, 0);
gl::BlendEquation(gl::FUNC_ADD);
gl::BlendFunc(gl::ONE, gl::ONE_MINUS_SRC_ALPHA);
gl::Enable(gl::BLEND);
gl::DrawElements(gl::TRIANGLES,
index_data.len() as GLsizei,
gl::UNSIGNED_INT,
ptr::null());
gl::Disable(gl::BLEND);
}
}
}
struct DebugFontVertexArray {
gl_vertex_array: GLuint,
vertex_buffer: Buffer,
index_buffer: Buffer,
}
impl DebugFontVertexArray {
fn new(debug_font_program: &DebugFontProgram) -> DebugFontVertexArray {
let vertex_buffer = Buffer::new();
let index_buffer = Buffer::new();
let mut gl_vertex_array = 0;
unsafe {
let position_attr = VertexAttr::new(&debug_font_program.program, "Position");
let tex_coord_attr = VertexAttr::new(&debug_font_program.program, "TexCoord");
gl::GenVertexArrays(1, &mut gl_vertex_array);
gl::BindVertexArray(gl_vertex_array);
gl::UseProgram(debug_font_program.program.gl_program);
gl::BindBuffer(gl::ARRAY_BUFFER, vertex_buffer.gl_buffer);
position_attr.configure_float(2,
gl::UNSIGNED_SHORT,
false,
DEBUG_FONT_VERTEX_SIZE,
0,
0);
tex_coord_attr.configure_float(2,
gl::UNSIGNED_SHORT,
false,
DEBUG_FONT_VERTEX_SIZE,
4,
0);
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, index_buffer.gl_buffer);
}
DebugFontVertexArray { gl_vertex_array, vertex_buffer, index_buffer }
}
}
impl Drop for DebugFontVertexArray {
#[inline]
fn drop(&mut self) {
unsafe {
gl::DeleteVertexArrays(1, &mut self.gl_vertex_array);
}
}
}
struct DebugSolidVertexArray {
gl_vertex_array: GLuint,
vertex_buffer: Buffer,
index_buffer: Buffer,
}
impl DebugSolidVertexArray {
fn new(debug_solid_program: &DebugSolidProgram) -> DebugSolidVertexArray {
let vertex_buffer = Buffer::new();
let index_buffer = Buffer::new();
let mut gl_vertex_array = 0;
unsafe {
let position_attr = VertexAttr::new(&debug_solid_program.program, "Position");
gl::GenVertexArrays(1, &mut gl_vertex_array);
gl::BindVertexArray(gl_vertex_array);
gl::UseProgram(debug_solid_program.program.gl_program);
gl::BindBuffer(gl::ARRAY_BUFFER, vertex_buffer.gl_buffer);
position_attr.configure_float(2,
gl::UNSIGNED_SHORT,
false,
DEBUG_SOLID_VERTEX_SIZE,
0,
0);
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, index_buffer.gl_buffer);
}
DebugSolidVertexArray { gl_vertex_array, vertex_buffer, index_buffer }
}
}
impl Drop for DebugSolidVertexArray {
#[inline]
fn drop(&mut self) {
unsafe {
gl::DeleteVertexArrays(1, &mut self.gl_vertex_array);
}
}
}
struct DebugFontProgram {
program: Program,
framebuffer_size_uniform: Uniform,
font_texture_size_uniform: Uniform,
font_texture_uniform: Uniform,
}
impl DebugFontProgram {
fn new() -> DebugFontProgram {
let program = Program::new("debug_font");
let framebuffer_size_uniform = Uniform::new(&program, "FramebufferSize");
let font_texture_size_uniform = Uniform::new(&program, "FontTextureSize");
let font_texture_uniform = Uniform::new(&program, "FontTexture");
DebugFontProgram {
program,
framebuffer_size_uniform,
font_texture_size_uniform,
font_texture_uniform,
}
}
}
struct DebugSolidProgram {
program: Program,
framebuffer_size_uniform: Uniform,
color_uniform: Uniform,
}
impl DebugSolidProgram {
fn new() -> DebugSolidProgram {
let program = Program::new("debug_solid");
let framebuffer_size_uniform = Uniform::new(&program, "FramebufferSize");
let color_uniform = Uniform::new(&program, "Color");
DebugSolidProgram { program, framebuffer_size_uniform, color_uniform }
}
}
#[derive(Clone, Copy)]
#[allow(dead_code)]
struct DebugFontVertex {
position_x: i16,
position_y: i16,
tex_coord_x: u16,
tex_coord_y: u16,
}
impl DebugFontVertex {
fn new(position: Point2D<i16>, tex_coord: Point2D<u16>) -> DebugFontVertex {
DebugFontVertex {
position_x: position.x,
position_y: position.y,
tex_coord_x: tex_coord.x,
tex_coord_y: tex_coord.y,
}
}
}
#[derive(Clone, Copy)]
#[allow(dead_code)]
struct DebugSolidVertex {
position_x: i16,
position_y: i16,
}
impl DebugSolidVertex {
fn new(position: Point2D<i16>) -> DebugSolidVertex {
DebugSolidVertex { position_x: position.x, position_y: position.y }
}
}
fn duration_ms(time: Duration) -> f64 {
time.as_secs() as f64 * 1000.0 + time.subsec_nanos() as f64 / 1000000.0
}

375
demo3/src/device.rs Normal file
View File

@ -0,0 +1,375 @@
// pathfinder/demo3/src/device.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.
//! Minimal abstractions over GPU device capabilities.
use euclid::Size2D;
use gl::types::{GLchar, GLint, GLsizei, GLsizeiptr, GLuint, GLvoid};
use std::ffi::CString;
use std::fs::File;
use std::io::Read;
use std::mem;
use std::ptr;
pub struct VertexAttr {
attr: GLuint,
}
impl VertexAttr {
pub fn new(program: &Program, name: &str) -> VertexAttr {
let name = CString::new(format!("a{}", name)).unwrap();
let attr = unsafe {
gl::GetAttribLocation(program.gl_program, name.as_ptr() as *const GLchar) as GLuint
};
VertexAttr { attr }
}
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);
gl::VertexAttribDivisor(self.attr, divisor);
gl::EnableVertexAttribArray(self.attr);
}
}
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);
gl::VertexAttribDivisor(self.attr, divisor);
gl::EnableVertexAttribArray(self.attr);
}
}
}
pub struct Framebuffer {
pub gl_framebuffer: GLuint,
pub texture: Texture,
}
impl Framebuffer {
pub fn new(size: &Size2D<u32>) -> Framebuffer {
let texture = Texture::new_r16f(size);
let mut gl_framebuffer = 0;
unsafe {
gl::GenFramebuffers(1, &mut gl_framebuffer);
assert_eq!(gl::GetError(), gl::NO_ERROR);
gl::BindFramebuffer(gl::FRAMEBUFFER, gl_framebuffer);
texture.bind(0);
gl::FramebufferTexture2D(gl::FRAMEBUFFER,
gl::COLOR_ATTACHMENT0,
gl::TEXTURE_2D,
texture.gl_texture,
0);
assert_eq!(gl::CheckFramebufferStatus(gl::FRAMEBUFFER), gl::FRAMEBUFFER_COMPLETE);
}
Framebuffer { gl_framebuffer, texture }
}
}
impl Drop for Framebuffer {
fn drop(&mut self) {
unsafe {
gl::DeleteFramebuffers(1, &mut self.gl_framebuffer)
}
}
}
pub struct Buffer {
pub gl_buffer: GLuint,
}
impl Buffer {
pub fn new() -> Buffer {
unsafe {
let mut gl_buffer = 0;
gl::GenBuffers(1, &mut gl_buffer);
Buffer { gl_buffer }
}
}
pub fn upload<T>(&self, 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, self.gl_buffer);
gl::BufferData(target,
(data.len() * mem::size_of::<T>()) as GLsizeiptr,
data.as_ptr() as *const GLvoid,
mode);
}
}
}
impl Drop for Buffer {
fn drop(&mut self) {
unsafe {
gl::DeleteBuffers(1, &mut self.gl_buffer)
}
}
}
pub enum BufferTarget {
Vertex,
Index,
}
pub enum BufferUploadMode {
Static,
Dynamic,
}
#[derive(Debug)]
pub struct Uniform {
pub location: GLint,
}
impl Uniform {
pub fn new(program: &Program, name: &str) -> Uniform {
let name = CString::new(format!("u{}", name)).unwrap();
let location = unsafe {
gl::GetUniformLocation(program.gl_program, name.as_ptr() as *const GLchar)
};
Uniform { location }
}
}
pub struct Program {
pub gl_program: GLuint,
#[allow(dead_code)]
vertex_shader: Shader,
#[allow(dead_code)]
fragment_shader: Shader,
}
impl Program {
pub fn new(name: &'static str) -> Program {
let vertex_shader = Shader::new(name, ShaderKind::Vertex);
let fragment_shader = Shader::new(name, ShaderKind::Fragment);
let gl_program;
unsafe {
gl_program = gl::CreateProgram();
gl::AttachShader(gl_program, vertex_shader.gl_shader);
gl::AttachShader(gl_program, fragment_shader.gl_shader);
gl::LinkProgram(gl_program);
let mut link_status = 0;
gl::GetProgramiv(gl_program, gl::LINK_STATUS, &mut link_status);
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);
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);
eprintln!("Program info log:\n{}", String::from_utf8_lossy(&info_log));
panic!("Program '{}' linking failed", name);
}
}
Program { gl_program, vertex_shader, fragment_shader }
}
}
impl Drop for Program {
fn drop(&mut self) {
unsafe {
gl::DeleteProgram(self.gl_program)
}
}
}
struct Shader {
gl_shader: GLuint,
}
impl Shader {
fn new(name: &str, kind: ShaderKind) -> Shader {
let suffix = match kind { ShaderKind::Vertex => 'v', ShaderKind::Fragment => 'f' };
// FIXME(pcwalton): Put the shaders somewhere else. Maybe compile them in?
let path = format!("shaders/{}.{}s.glsl", name, suffix);
let mut source = vec![];
File::open(&path).unwrap().read_to_end(&mut source).unwrap();
unsafe {
let gl_shader_kind = match kind {
ShaderKind::Vertex => gl::VERTEX_SHADER,
ShaderKind::Fragment => gl::FRAGMENT_SHADER,
};
let gl_shader = gl::CreateShader(gl_shader_kind);
gl::ShaderSource(gl_shader,
1,
[source.as_ptr() as *const GLchar].as_ptr(),
[source.len() as GLint].as_ptr());
gl::CompileShader(gl_shader);
let mut compile_status = 0;
gl::GetShaderiv(gl_shader, gl::COMPILE_STATUS, &mut compile_status);
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);
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);
eprintln!("Shader info log:\n{}", String::from_utf8_lossy(&info_log));
panic!("{:?} shader '{}' compilation failed", kind, name);
}
Shader { gl_shader }
}
}
}
impl Drop for Shader {
fn drop(&mut self) {
unsafe {
gl::DeleteShader(self.gl_shader)
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
enum ShaderKind {
Vertex,
Fragment,
}
pub struct Texture {
gl_texture: GLuint,
pub size: Size2D<u32>,
}
impl Texture {
fn new_r16f(size: &Size2D<u32>) -> Texture {
let mut texture = Texture { gl_texture: 0, size: *size };
unsafe {
gl::GenTextures(1, &mut texture.gl_texture);
texture.bind(0);
gl::TexImage2D(gl::TEXTURE_2D,
0,
gl::R16F as GLint,
size.width as GLsizei,
size.height as GLsizei,
0,
gl::RED,
gl::HALF_FLOAT,
ptr::null());
}
texture.set_parameters();
texture
}
pub fn new_rgba(size: &Size2D<u32>) -> Texture {
let mut texture = Texture { gl_texture: 0, size: *size };
unsafe {
gl::GenTextures(1, &mut texture.gl_texture);
texture.bind(0);
gl::TexImage2D(gl::TEXTURE_2D,
0,
gl::RGBA as GLint,
size.width as GLsizei,
size.height as GLsizei,
0,
gl::RGBA,
gl::UNSIGNED_BYTE,
ptr::null());
}
texture.set_parameters();
texture
}
pub fn from_png(name: &str) -> Texture {
let path = format!("resources/textures/{}.png", name);
let image = image::open(&path).unwrap().to_luma();
let mut texture = Texture {
gl_texture: 0,
size: Size2D::new(image.width(), image.height()),
};
unsafe {
gl::GenTextures(1, &mut texture.gl_texture);
texture.bind(0);
gl::TexImage2D(gl::TEXTURE_2D,
0,
gl::RED as GLint,
image.width() as GLsizei,
image.height() as GLsizei,
0,
gl::RED,
gl::UNSIGNED_BYTE,
image.as_ptr() as *const GLvoid);
}
texture.set_parameters();
texture
}
pub fn bind(&self, unit: u32) {
unsafe {
gl::ActiveTexture(gl::TEXTURE0 + unit);
gl::BindTexture(gl::TEXTURE_2D, self.gl_texture);
}
}
pub fn upload_rgba(&self, size: &Size2D<u32>, data: &[u8]) {
assert!(data.len() >= size.width as usize * size.height as usize * 4);
unsafe {
self.bind(0);
gl::TexImage2D(gl::TEXTURE_2D,
0,
gl::RGBA as GLint,
size.width as GLsizei,
size.height as GLsizei,
0,
gl::RGBA,
gl::UNSIGNED_BYTE,
data.as_ptr() as *const GLvoid);
}
self.set_parameters();
}
fn set_parameters(&self) {
self.bind(0);
unsafe {
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as GLint);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as GLint);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as GLint);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as GLint);
}
}
}

View File

@ -8,12 +8,17 @@
// 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::{Uniform, VertexAttr};
use clap::{App, Arg};
use euclid::{Point2D, Rect, Size2D};
use gl::types::{GLchar, GLfloat, GLint, GLsizei, GLsizeiptr, GLuint, GLvoid};
use gl::types::{GLfloat, GLint, GLuint};
use jemallocator;
use pathfinder_geometry::point::{Point2DF32, Point4DF32};
use pathfinder_geometry::transform::Transform2DF32;
use pathfinder_geometry::point::Point4DF32;
use pathfinder_geometry::transform3d::{Perspective, Transform3DF32};
use pathfinder_renderer::builder::SceneBuilder;
use pathfinder_renderer::gpu_data::{Batch, BuiltScene, SolidTileScenePrimitive};
@ -27,14 +32,13 @@ use sdl2::event::Event;
use sdl2::keyboard::Keycode;
use sdl2::video::GLProfile;
use std::f32::consts::FRAC_PI_4;
use std::ffi::CString;
use std::fs::File;
use std::io::Read;
use std::mem;
use std::time::Instant;
use std::path::PathBuf;
use std::ptr;
use usvg::{Options as UsvgOptions, Tree};
mod debug_text;
mod device;
#[global_allocator]
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
@ -88,8 +92,9 @@ fn main() {
let (mut camera_yaw, mut camera_pitch) = (0.0, 0.0);
let window_size = Size2D::new(drawable_width, drawable_height);
renderer.debug_renderer.set_framebuffer_size(&window_size);
let mut base_scene = load_scene(&options, &window_size);
let base_scene = load_scene(&options, &window_size);
let mut dump_transformed_scene = false;
let mut events = vec![];
@ -97,6 +102,8 @@ fn main() {
while !exit {
let mut scene = base_scene.clone();
let mut start_time = Instant::now();
if options.run_in_3d {
let rotation = Transform3DF32::from_rotation(-camera_yaw, -camera_pitch, 0.0);
camera_position = camera_position + rotation.transform_point(camera_velocity);
@ -114,22 +121,38 @@ fn main() {
transform.post_mul(&Transform3DF32::from_scale(1.0 / 800.0, 1.0 / 800.0, 1.0));
let perspective = Perspective::new(&transform, &window_size);
scene.apply_perspective(&perspective);
match options.jobs {
Some(1) => scene.apply_perspective_sequentially(&perspective),
_ => scene.apply_perspective(&perspective),
}
} else {
scene.prepare();
}
let elapsed_prepare_time = Instant::now() - start_time;
if dump_transformed_scene {
println!("{:?}", scene);
dump_transformed_scene = false;
}
// Tile the scene.
start_time = Instant::now();
let built_scene = build_scene(&scene, &options);
let elapsed_tile_time = Instant::now() - start_time;
// Draw the scene.
unsafe {
gl::ClearColor(0.7, 0.7, 0.7, 1.0);
gl::Clear(gl::COLOR_BUFFER_BIT);
renderer.render_scene(&built_scene);
renderer.debug_renderer.draw(elapsed_prepare_time, elapsed_tile_time);
}
window.gl_swap_window();
@ -288,6 +311,8 @@ struct Renderer {
mask_framebuffer: Framebuffer,
fill_colors_texture: Texture,
debug_renderer: DebugRenderer,
main_framebuffer_size: Size2D<u32>,
}
@ -300,7 +325,9 @@ impl Renderer {
let area_lut_texture = Texture::from_png("area-lut");
let quad_vertex_positions_buffer = Buffer::new();
quad_vertex_positions_buffer.upload(&QUAD_VERTEX_POSITIONS, BufferUploadMode::Static);
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,
@ -314,6 +341,8 @@ impl Renderer {
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,
@ -326,6 +355,8 @@ impl Renderer {
mask_framebuffer,
fill_colors_texture,
debug_renderer,
main_framebuffer_size: *main_framebuffer_size,
}
}
@ -356,13 +387,18 @@ impl Renderer {
}
fn upload_solid_tiles(&mut self, solid_tiles: &[SolidTileScenePrimitive]) {
self.solid_tile_vertex_array.vertex_buffer.upload(solid_tiles, BufferUploadMode::Dynamic);
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, BufferUploadMode::Dynamic);
self.mask_tile_vertex_array.vertex_buffer.upload(&batch.mask_tiles,
BufferUploadMode::Dynamic);
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) {
@ -577,126 +613,6 @@ impl Drop for SolidTileVertexArray {
}
}
struct VertexAttr {
attr: GLuint,
}
impl VertexAttr {
fn new(program: &Program, name: &str) -> VertexAttr {
let name = CString::new(format!("a{}", name)).unwrap();
let attr = unsafe {
gl::GetAttribLocation(program.gl_program, name.as_ptr() as *const GLchar) as GLuint
};
VertexAttr { attr }
}
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);
gl::VertexAttribDivisor(self.attr, divisor);
gl::EnableVertexAttribArray(self.attr);
}
}
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);
gl::VertexAttribDivisor(self.attr, divisor);
gl::EnableVertexAttribArray(self.attr);
}
}
}
struct Framebuffer {
gl_framebuffer: GLuint,
texture: Texture,
}
impl Framebuffer {
fn new(size: &Size2D<u32>) -> Framebuffer {
let texture = Texture::new_r16f(size);
let mut gl_framebuffer = 0;
unsafe {
gl::GenFramebuffers(1, &mut gl_framebuffer);
assert_eq!(gl::GetError(), gl::NO_ERROR);
gl::BindFramebuffer(gl::FRAMEBUFFER, gl_framebuffer);
texture.bind(0);
gl::FramebufferTexture2D(gl::FRAMEBUFFER,
gl::COLOR_ATTACHMENT0,
gl::TEXTURE_2D,
texture.gl_texture,
0);
assert_eq!(gl::CheckFramebufferStatus(gl::FRAMEBUFFER), gl::FRAMEBUFFER_COMPLETE);
}
Framebuffer { gl_framebuffer, texture }
}
}
impl Drop for Framebuffer {
fn drop(&mut self) {
unsafe {
gl::DeleteFramebuffers(1, &mut self.gl_framebuffer)
}
}
}
struct Buffer {
gl_buffer: GLuint,
}
impl Buffer {
fn new() -> Buffer {
unsafe {
let mut gl_buffer = 0;
gl::GenBuffers(1, &mut gl_buffer);
Buffer { gl_buffer }
}
}
fn upload<T>(&self, data: &[T], mode: BufferUploadMode) {
let mode = match mode {
BufferUploadMode::Static => gl::STATIC_DRAW,
BufferUploadMode::Dynamic => gl::DYNAMIC_DRAW,
};
unsafe {
gl::BindBuffer(gl::ARRAY_BUFFER, self.gl_buffer);
gl::BufferData(gl::ARRAY_BUFFER,
(data.len() * mem::size_of::<T>()) as GLsizeiptr,
data.as_ptr() as *const GLvoid,
mode);
}
}
}
impl Drop for Buffer {
fn drop(&mut self) {
unsafe {
gl::DeleteBuffers(1, &mut self.gl_buffer)
}
}
}
enum BufferUploadMode {
Static,
Dynamic,
}
struct FillProgram {
program: Program,
framebuffer_size_uniform: Uniform,
@ -775,225 +691,3 @@ impl MaskTileProgram {
}
}
}
#[derive(Debug)]
struct Uniform {
location: GLint,
}
impl Uniform {
fn new(program: &Program, name: &str) -> Uniform {
let name = CString::new(format!("u{}", name)).unwrap();
let location = unsafe {
gl::GetUniformLocation(program.gl_program, name.as_ptr() as *const GLchar)
};
Uniform { location }
}
}
struct Program {
gl_program: GLuint,
#[allow(dead_code)]
vertex_shader: Shader,
#[allow(dead_code)]
fragment_shader: Shader,
}
impl Program {
fn new(name: &'static str) -> Program {
let vertex_shader = Shader::new(name, ShaderKind::Vertex);
let fragment_shader = Shader::new(name, ShaderKind::Fragment);
let gl_program;
unsafe {
gl_program = gl::CreateProgram();
gl::AttachShader(gl_program, vertex_shader.gl_shader);
gl::AttachShader(gl_program, fragment_shader.gl_shader);
gl::LinkProgram(gl_program);
let mut link_status = 0;
gl::GetProgramiv(gl_program, gl::LINK_STATUS, &mut link_status);
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);
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);
eprintln!("Program info log:\n{}", String::from_utf8_lossy(&info_log));
panic!("Program '{}' linking failed", name);
}
}
Program { gl_program, vertex_shader, fragment_shader }
}
}
impl Drop for Program {
fn drop(&mut self) {
unsafe {
gl::DeleteProgram(self.gl_program)
}
}
}
struct Shader {
gl_shader: GLuint,
}
impl Shader {
fn new(name: &str, kind: ShaderKind) -> Shader {
let suffix = match kind { ShaderKind::Vertex => 'v', ShaderKind::Fragment => 'f' };
// FIXME(pcwalton): Put the shaders somewhere else. Maybe compile them in?
let path = format!("shaders/{}.{}s.glsl", name, suffix);
let mut source = vec![];
File::open(&path).unwrap().read_to_end(&mut source).unwrap();
unsafe {
let gl_shader_kind = match kind {
ShaderKind::Vertex => gl::VERTEX_SHADER,
ShaderKind::Fragment => gl::FRAGMENT_SHADER,
};
let gl_shader = gl::CreateShader(gl_shader_kind);
gl::ShaderSource(gl_shader,
1,
[source.as_ptr() as *const GLchar].as_ptr(),
[source.len() as GLint].as_ptr());
gl::CompileShader(gl_shader);
let mut compile_status = 0;
gl::GetShaderiv(gl_shader, gl::COMPILE_STATUS, &mut compile_status);
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);
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);
eprintln!("Shader info log:\n{}", String::from_utf8_lossy(&info_log));
panic!("{:?} shader '{}' compilation failed", kind, name);
}
Shader { gl_shader }
}
}
}
impl Drop for Shader {
fn drop(&mut self) {
unsafe {
gl::DeleteShader(self.gl_shader)
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
enum ShaderKind {
Vertex,
Fragment,
}
struct Texture {
gl_texture: GLuint,
}
impl Texture {
fn new_r16f(size: &Size2D<u32>) -> Texture {
let mut texture = Texture { gl_texture: 0 };
unsafe {
gl::GenTextures(1, &mut texture.gl_texture);
texture.bind(0);
gl::TexImage2D(gl::TEXTURE_2D,
0,
gl::R16F as GLint,
size.width as GLsizei,
size.height as GLsizei,
0,
gl::RED,
gl::HALF_FLOAT,
ptr::null());
}
texture.set_parameters();
texture
}
fn new_rgba(size: &Size2D<u32>) -> Texture {
let mut texture = Texture { gl_texture: 0 };
unsafe {
gl::GenTextures(1, &mut texture.gl_texture);
texture.bind(0);
gl::TexImage2D(gl::TEXTURE_2D,
0,
gl::RGBA as GLint,
size.width as GLsizei,
size.height as GLsizei,
0,
gl::RGBA,
gl::UNSIGNED_BYTE,
ptr::null());
}
texture.set_parameters();
texture
}
fn from_png(name: &str) -> Texture {
let path = format!("textures/{}.png", name);
let image = image::open(&path).unwrap().to_luma();
let mut texture = Texture { gl_texture: 0 };
unsafe {
gl::GenTextures(1, &mut texture.gl_texture);
texture.bind(0);
gl::TexImage2D(gl::TEXTURE_2D,
0,
gl::RED as GLint,
image.width() as GLsizei,
image.height() as GLsizei,
0,
gl::RED,
gl::UNSIGNED_BYTE,
image.as_ptr() as *const GLvoid);
}
texture.set_parameters();
texture
}
fn bind(&self, unit: u32) {
unsafe {
gl::ActiveTexture(gl::TEXTURE0 + unit);
gl::BindTexture(gl::TEXTURE_2D, self.gl_texture);
}
}
fn upload_rgba(&self, size: &Size2D<u32>, data: &[u8]) {
assert!(data.len() >= size.width as usize * size.height as usize * 4);
unsafe {
self.bind(0);
gl::TexImage2D(gl::TEXTURE_2D,
0,
gl::RGBA as GLint,
size.width as GLsizei,
size.height as GLsizei,
0,
gl::RGBA,
gl::UNSIGNED_BYTE,
data.as_ptr() as *const GLvoid);
}
self.set_parameters();
}
fn set_parameters(&self) {
self.bind(0);
unsafe {
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as GLint);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as GLint);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as GLint);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as GLint);
}
}
}

View File

@ -18,10 +18,11 @@ use euclid::Rect;
use hashbrown::HashMap;
use pathfinder_geometry::clip::PolygonClipper3D;
use pathfinder_geometry::outline::Outline;
use pathfinder_geometry::point::Point3DF32;
use pathfinder_geometry::point::{Point2DF32, Point3DF32};
use pathfinder_geometry::transform3d::Perspective;
use pathfinder_geometry::transform::Transform2DF32;
use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator};
use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator};
use std::fmt::{self, Debug, Formatter};
#[derive(Clone)]
@ -102,6 +103,19 @@ impl Scene {
.collect()
}
fn update_bounds(&mut self) {
let mut bounds = Rect::zero();
for (object_index, object) in self.objects.iter_mut().enumerate() {
if object_index == 0 {
bounds = *object.outline.bounds();
} else {
bounds = bounds.union(object.outline.bounds());
}
}
self.bounds = bounds;
}
pub fn prepare(&mut self) {
for object in &mut self.objects {
object.outline.make_monotonic();
@ -109,46 +123,45 @@ impl Scene {
}
pub fn transform(&mut self, transform: &Transform2DF32) {
let mut bounds = Rect::zero();
for (object_index, object) in self.objects.iter_mut().enumerate() {
for object in &mut self.objects {
object.outline.transform(transform);
object.outline.clip_against_rect(&self.view_box);
if object_index == 0 {
bounds = *object.outline.bounds();
} else {
bounds = bounds.union(object.outline.bounds());
}
}
//println!("new bounds={:?}", bounds);
self.bounds = bounds;
self.update_bounds();
}
pub fn apply_perspective(&mut self, perspective: &Perspective) {
fn apply_perspective_to_quad(&self, perspective: &Perspective) -> Vec<Point2DF32> {
let quad = self.clip_bounding_quad_with_perspective(perspective);
//println!("bounds={:?} PRE-transform quad={:?}", self.bounds, quad);
let inverse_transform = perspective.transform.inverse();
let quad: Vec<_> = quad.into_iter().map(|point| {
inverse_transform.transform_point_3d(point).to_2d()
}).collect();
//println!("bounds={:?} POST-transform quad={:?}", self.bounds, quad);
quad.into_iter()
.map(|point| inverse_transform.transform_point_3d(point).to_2d())
.collect()
}
let mut bounds = Rect::zero();
for (object_index, object) in self.objects.iter_mut().enumerate() {
pub fn apply_perspective_sequentially(&mut self, perspective: &Perspective) {
let quad = self.apply_perspective_to_quad(perspective);
for object in &mut self.objects {
object.outline.clip_against_polygon(&quad);
object.outline.apply_perspective(perspective);
object.outline.clip_against_rect(&self.view_box);
if object_index == 0 {
bounds = *object.outline.bounds();
} else {
bounds = bounds.union(object.outline.bounds());
}
}
//println!("new bounds={:?}", bounds);
self.bounds = bounds;
self.update_bounds();
}
pub fn apply_perspective(&mut self, perspective: &Perspective) {
let quad = self.apply_perspective_to_quad(perspective);
let view_box = &self.view_box;
self.objects.par_iter_mut().for_each(|object| {
object.outline.clip_against_polygon(&quad);
object.outline.apply_perspective(perspective);
object.outline.clip_against_rect(&view_box);
});
self.update_bounds();
}
fn clip_bounding_quad_with_perspective(&self, perspective: &Perspective) -> Vec<Point3DF32> {