From 6e0e621d19e1c2ecefe1f0041ef776ccc0290fb1 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Sun, 30 Dec 2018 09:38:57 -0800 Subject: [PATCH] WIP: get demo compiling again --- demo2/cover.vs.glsl | 6 +- demo2/opaque.vs.glsl | 7 +- demo2/pathfinder.ts | 135 +++++++++++++++++++++++++------------ utils/tile-svg/src/main.rs | 77 +++++++++++++++------ 4 files changed, 159 insertions(+), 66 deletions(-) diff --git a/demo2/cover.vs.glsl b/demo2/cover.vs.glsl index 5fd9db6f..8da2a824 100644 --- a/demo2/cover.vs.glsl +++ b/demo2/cover.vs.glsl @@ -15,12 +15,14 @@ precision highp float; uniform vec2 uFramebufferSize; uniform vec2 uTileSize; uniform vec2 uStencilTextureSize; +uniform sampler2D uFillColorsTexture; +uniform vec2 uFillColorsTextureSize; uniform vec2 uViewBoxOrigin; in vec2 aTessCoord; in vec2 aTileOrigin; in float aBackdrop; -in vec4 aColor; +in int aObject; out vec2 vTexCoord; out float vBackdrop; @@ -38,6 +40,6 @@ void main() { vec2 texCoord = computeTileOffset(tileIndex, uStencilTextureSize.x) + aTessCoord * uTileSize; vTexCoord = texCoord / uStencilTextureSize; vBackdrop = aBackdrop; - vColor = aColor; + vColor = texture(uFillColorsTexture, vec2(float(aObject) / uFillColorsTextureSize.x, 0.0)); gl_Position = vec4((position / uFramebufferSize * 2.0 - 1.0) * vec2(1.0, -1.0), 0.0, 1.0); } diff --git a/demo2/opaque.vs.glsl b/demo2/opaque.vs.glsl index a7227b0c..695a6b93 100644 --- a/demo2/opaque.vs.glsl +++ b/demo2/opaque.vs.glsl @@ -14,17 +14,18 @@ precision highp float; uniform vec2 uFramebufferSize; uniform vec2 uTileSize; +uniform sampler2D uFillColorsTexture; +uniform vec2 uFillColorsTextureSize; uniform vec2 uViewBoxOrigin; in vec2 aTessCoord; in vec2 aTileOrigin; -in vec4 aColor; +in int aObject; out vec4 vColor; void main() { vec2 position = (aTileOrigin + aTessCoord) * uTileSize + uViewBoxOrigin; - vColor = aColor; + vColor = texture(uFillColorsTexture, vec2(float(aObject) / uFillColorsTextureSize.x, 0.0)); gl_Position = vec4((position / uFramebufferSize * 2.0 - 1.0) * vec2(1.0, -1.0), 0.0, 1.0); } - diff --git a/demo2/pathfinder.ts b/demo2/pathfinder.ts index d919aefc..5aaad8c9 100644 --- a/demo2/pathfinder.ts +++ b/demo2/pathfinder.ts @@ -54,16 +54,23 @@ class App { private gl: WebGL2RenderingContext; private disjointTimerQueryExt: any; private areaLUTTexture: WebGLTexture; + private fillColorsTexture: WebGLTexture; private stencilTexture: WebGLTexture; private stencilFramebuffer: WebGLFramebuffer; private fillProgram: Program<'FramebufferSize' | 'TileSize' | 'AreaLUT', 'TessCoord' | 'From' | 'To' | 'TileIndex'>; - private solidTileProgram: Program<'FramebufferSize' | 'TileSize' | 'ViewBoxOrigin', - 'TessCoord' | 'TileOrigin' | 'Color'>; + private solidTileProgram: Program<'FramebufferSize' | + 'TileSize' | + 'FillColorsTexture' | 'FillColorsTextureSize' | + 'ViewBoxOrigin', + 'TessCoord' | 'TileOrigin' | 'Object'>; private maskTileProgram: - Program<'FramebufferSize' | 'TileSize' | 'StencilTexture' | 'StencilTextureSize' | + Program<'FramebufferSize' | + 'TileSize' | + 'StencilTexture' | 'StencilTextureSize' | + 'FillColorsTexture' | 'FillColorsTextureSize' | 'ViewBoxOrigin', - 'TessCoord' | 'TileOrigin' | 'Backdrop' | 'Color'>; + 'TessCoord' | 'TileOrigin' | 'Backdrop' | 'Object'>; private quadVertexBuffer: WebGLBuffer; private fillVertexBuffer: WebGLBuffer; private fillVertexArray: WebGLVertexArrayObject; @@ -77,6 +84,7 @@ class App { private fillPrimitiveCount: number; private solidTileCount: number; private maskTileCount: number; + private objectCount: number; constructor(areaLUT: HTMLImageElement) { const canvas = staticCast(document.getElementById('canvas'), HTMLCanvasElement); @@ -106,6 +114,8 @@ class App { gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + this.fillColorsTexture = unwrapNull(gl.createTexture()); + this.stencilTexture = unwrapNull(gl.createTexture()); gl.bindTexture(gl.TEXTURE_2D, this.stencilTexture); gl.texImage2D(gl.TEXTURE_2D, @@ -140,6 +150,8 @@ class App { 'TileSize', 'StencilTexture', 'StencilTextureSize', + 'FillColorsTexture', + 'FillColorsTextureSize', 'ViewBoxOrigin', ], [ @@ -147,15 +159,21 @@ class App { 'TileOrigin', 'TileIndex', 'Backdrop', - 'Color', + 'Object', ]); this.maskTileProgram = maskTileProgram; const solidTileProgram = new Program(gl, OPAQUE_VERTEX_SHADER_SOURCE, OPAQUE_FRAGMENT_SHADER_SOURCE, - ['FramebufferSize', 'TileSize', 'ViewBoxOrigin'], - ['TessCoord', 'TileOrigin', 'Color']); + [ + 'FramebufferSize', + 'TileSize', + 'FillColorsTexture', + 'FillColorsTextureSize', + 'ViewBoxOrigin', + ], + ['TessCoord', 'TileOrigin', 'Object']); this.solidTileProgram = solidTileProgram; const fillProgram = new Program(gl, @@ -233,16 +251,15 @@ class App { SOLID_TILE_INSTANCE_SIZE, 0); gl.vertexAttribDivisor(solidTileProgram.attributes.TileOrigin, 1); - gl.vertexAttribPointer(solidTileProgram.attributes.Color, - 4, - gl.UNSIGNED_BYTE, - true, - SOLID_TILE_INSTANCE_SIZE, - 4); - gl.vertexAttribDivisor(solidTileProgram.attributes.Color, 1); + gl.vertexAttribIPointer(solidTileProgram.attributes.Object, + 1, + gl.INT, + SOLID_TILE_INSTANCE_SIZE, + 4); + gl.vertexAttribDivisor(solidTileProgram.attributes.Object, 1); gl.enableVertexAttribArray(solidTileProgram.attributes.TessCoord); gl.enableVertexAttribArray(solidTileProgram.attributes.TileOrigin); - gl.enableVertexAttribArray(solidTileProgram.attributes.Color); + gl.enableVertexAttribArray(solidTileProgram.attributes.Object); // Initialize mask tile VAO. this.maskVertexArray = unwrapNull(gl.createVertexArray()); @@ -270,17 +287,16 @@ class App { MASK_TILE_INSTANCE_SIZE, 4); gl.vertexAttribDivisor(maskTileProgram.attributes.Backdrop, 1); - gl.vertexAttribPointer(maskTileProgram.attributes.Color, - 4, - gl.UNSIGNED_BYTE, - true, - MASK_TILE_INSTANCE_SIZE, - 8); - gl.vertexAttribDivisor(maskTileProgram.attributes.Color, 1); + gl.vertexAttribIPointer(maskTileProgram.attributes.Object, + 1, + gl.INT, + MASK_TILE_INSTANCE_SIZE, + 8); + gl.vertexAttribDivisor(maskTileProgram.attributes.Object, 1); gl.enableVertexAttribArray(maskTileProgram.attributes.TessCoord); gl.enableVertexAttribArray(maskTileProgram.attributes.TileOrigin); gl.enableVertexAttribArray(maskTileProgram.attributes.Backdrop); - gl.enableVertexAttribArray(maskTileProgram.attributes.Color); + gl.enableVertexAttribArray(maskTileProgram.attributes.Object); this.viewBox = new Rect(new Point2D(0.0, 0.0), new Size2D(0.0, 0.0)); @@ -290,6 +306,7 @@ class App { this.fillPrimitiveCount = 0; this.solidTileCount = 0; this.maskTileCount = 0; + this.objectCount = 0; } redraw(): void { @@ -341,6 +358,13 @@ class App { framebufferSize.width, framebufferSize.height); gl.uniform2f(this.solidTileProgram.uniforms.TileSize, TILE_SIZE.width, TILE_SIZE.height); + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, this.fillColorsTexture); + gl.uniform1i(this.solidTileProgram.uniforms.FillColorsTexture, 0); + // FIXME(pcwalton): Maybe this should be an ivec2 or uvec2? + gl.uniform2f(this.solidTileProgram.uniforms.FillColorsTextureSize, + this.objectCount, + 1.0); gl.uniform2f(this.solidTileProgram.uniforms.ViewBoxOrigin, this.viewBox.origin.x, this.viewBox.origin.y); @@ -360,6 +384,13 @@ class App { gl.uniform2f(this.maskTileProgram.uniforms.StencilTextureSize, STENCIL_FRAMEBUFFER_SIZE.width, STENCIL_FRAMEBUFFER_SIZE.height); + gl.activeTexture(gl.TEXTURE1); + gl.bindTexture(gl.TEXTURE_2D, this.fillColorsTexture); + gl.uniform1i(this.maskTileProgram.uniforms.FillColorsTexture, 1); + // FIXME(pcwalton): Maybe this should be an ivec2 or uvec2? + gl.uniform2f(this.maskTileProgram.uniforms.FillColorsTextureSize, + this.objectCount, + 1.0); gl.uniform2f(this.maskTileProgram.uniforms.ViewBoxOrigin, this.viewBox.origin.x, this.viewBox.origin.y); @@ -416,6 +447,8 @@ class App { const arrayBuffer = staticCast(reader.result, ArrayBuffer); const root = new RIFFChunk(new DataView(arrayBuffer)); for (const subchunk of root.subchunks()) { + const self = this; + const id = subchunk.stringID(); if (id === 'head') { const headerData = subchunk.contents(); @@ -426,36 +459,54 @@ class App { continue; } - let bindPoint, buffer; - let countFieldName: 'fillPrimitiveCount' | 'totalTileCount' | 'solidTileCount' | - 'maskTileCount'; - let instanceSize; switch (id) { case 'fill': - bindPoint = gl.ARRAY_BUFFER; - buffer = this.fillVertexBuffer; - countFieldName = 'fillPrimitiveCount'; - instanceSize = FILL_INSTANCE_SIZE; + uploadArrayBuffer(this.fillVertexBuffer, + 'fillPrimitiveCount', + FILL_INSTANCE_SIZE); break; case 'soli': - bindPoint = gl.ARRAY_BUFFER; - buffer = this.solidTileVertexBuffer; - countFieldName = 'solidTileCount'; - instanceSize = SOLID_TILE_INSTANCE_SIZE; + uploadArrayBuffer(this.solidTileVertexBuffer, + 'solidTileCount', + SOLID_TILE_INSTANCE_SIZE); break; case 'mask': - bindPoint = gl.ARRAY_BUFFER; - buffer = this.maskTileVertexBuffer; - countFieldName = 'maskTileCount'; - instanceSize = MASK_TILE_INSTANCE_SIZE; + uploadArrayBuffer(this.maskTileVertexBuffer, + 'maskTileCount', + MASK_TILE_INSTANCE_SIZE); + break; + case 'shad': + this.objectCount = subchunk.length() / 4; + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, this.fillColorsTexture); + gl.texImage2D(gl.TEXTURE_2D, + 0, + gl.RGBA, + this.objectCount, + 1, + 0, + gl.RGBA, + gl.UNSIGNED_BYTE, + subchunk.contents()); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); break; default: throw new Error("Unexpected subchunk ID: " + id); } - gl.bindBuffer(bindPoint, buffer); - gl.bufferData(bindPoint, subchunk.contents(), gl.DYNAMIC_DRAW); - this[countFieldName] = subchunk.length() / instanceSize; + type CountFieldName = 'fillPrimitiveCount' | 'solidTileCount' | 'maskTileCount'; + + function uploadArrayBuffer(buffer: WebGLBuffer, + countFieldName: CountFieldName, + instanceSize: number): + void { + gl.bindBuffer(gl.ARRAY_BUFFER, buffer); + gl.bufferData(gl.ARRAY_BUFFER, subchunk.contents(), gl.DYNAMIC_DRAW); + self[countFieldName] = subchunk.length() / instanceSize; + } } this.redraw(); diff --git a/utils/tile-svg/src/main.rs b/utils/tile-svg/src/main.rs index 035bc2e0..28534103 100644 --- a/utils/tile-svg/src/main.rs +++ b/utils/tile-svg/src/main.rs @@ -28,6 +28,7 @@ use lyon_path::iterator::PathIter; use pathfinder_path_utils::stroke::{StrokeStyle, StrokeToFillIter}; use quick_xml::Reader; use quick_xml::events::{BytesStart, Event}; +use rayon::ThreadPoolBuilder; use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator}; use std::cmp::Ordering; use std::fmt::{self, Debug, Formatter}; @@ -58,9 +59,11 @@ fn main() { .value_name("COUNT") .takes_value(true) .help("Run a benchmark with COUNT runs")) - .arg(Arg::with_name("sequential").short("s") - .long("sequential") - .help("Use only one thread")) + .arg(Arg::with_name("jobs").short("j") + .long("jobs") + .value_name("THREADS") + .takes_value(true) + .help("Number of threads to use")) .arg(Arg::with_name("INPUT").help("Path to the SVG file to render") .required(true) .index(1)) @@ -72,20 +75,26 @@ fn main() { Some(runs) => runs.parse().unwrap(), None => 1, }; - let sequential = matches.is_present("sequential"); + let jobs: Option = matches.value_of("jobs").map(|string| string.parse().unwrap()); let input_path = PathBuf::from(matches.value_of("INPUT").unwrap()); let output_path = matches.value_of("OUTPUT").map(PathBuf::from); + // Set up Rayon. + let mut thread_pool_builder = ThreadPoolBuilder::new(); + if let Some(jobs) = jobs { + thread_pool_builder = thread_pool_builder.num_threads(jobs); + } + thread_pool_builder.build_global().unwrap(); + let scene = Scene::from_path(&input_path); println!("Scene bounds: {:?}", scene.bounds); let start_time = Instant::now(); let mut built_scene = BuiltScene::new(&scene.view_box, scene.objects.len() as u32); for _ in 0..runs { - let built_objects = if sequential { - scene.build_objects_sequentially() - } else { - scene.build_objects_in_parallel() + let built_objects = match jobs { + Some(1) => scene.build_objects_sequentially(), + _ => scene.build_objects(), }; built_scene = BuiltScene::from_objects(&scene.view_box, &built_objects); } @@ -321,17 +330,30 @@ impl Scene { &self.styles[style.0 as usize] } + fn build_shader(&self, object_index: u32) -> ObjectShader { + ObjectShader { + fill_color: self.objects[object_index as usize].color, + } + } + + // This function exists to make profiling easier. fn build_objects_sequentially(&self) -> Vec { self.objects.iter().enumerate().map(|(object_index, object)| { - let mut tiler = Tiler::new(&object.outline, object_index as u32, &self.view_box); + let mut tiler = Tiler::new(&object.outline, + object_index as u32, + &self.view_box, + &self.build_shader(object_index as u32)); tiler.generate_tiles(); tiler.built_object }).collect() } - fn build_objects_in_parallel(&self) -> Vec { + fn build_objects(&self) -> Vec { self.objects.par_iter().enumerate().map(|(object_index, object)| { - let mut tiler = Tiler::new(&object.outline, object_index as u32, &self.view_box); + let mut tiler = Tiler::new(&object.outline, + object_index as u32, + &self.view_box, + &self.build_shader(object_index as u32)); tiler.generate_tiles(); tiler.built_object }).collect() @@ -955,9 +977,10 @@ struct Tiler<'o> { } impl<'o> Tiler<'o> { - fn new(outline: &'o Outline, object_index: u32, view_box: &Rect) -> Tiler<'o> { + fn new(outline: &'o Outline, object_index: u32, view_box: &Rect, shader: &ObjectShader) + -> Tiler<'o> { let bounds = outline.bounds.intersection(&view_box).unwrap_or(Rect::zero()); - let built_object = BuiltObject::new(&bounds); + let built_object = BuiltObject::new(&bounds, shader); Tiler { outline, @@ -1023,7 +1046,7 @@ impl<'o> Tiler<'o> { }; // Move over to the correct tile, filling in as we go. - let mut segment_tile_x = f32::floor(segment_x / TILE_WIDTH) as i16; + let segment_tile_x = f32::floor(segment_x / TILE_WIDTH) as i16; while current_tile_x < segment_tile_x { //println!("... filling!"); self.built_object.get_tile_mut(current_tile_x, tile_y).backdrop = current_winding; @@ -1153,10 +1176,10 @@ impl BuiltScene { fn new(view_box: &Rect, object_count: u32) -> BuiltScene { BuiltScene { view_box: *view_box, - object_count, fills: vec![], solid_tiles: vec![], mask_tiles: vec![], + shaders: vec![ObjectShader::default(); object_count as usize], tile_rect: round_rect_out_to_tile_bounds(view_box), } @@ -1237,6 +1260,7 @@ struct BuiltObject { tiles: Vec, fills: Vec, mask_tiles: FixedBitSet, + shader: ObjectShader, } #[derive(Debug)] @@ -1245,7 +1269,7 @@ struct BuiltScene { fills: Vec, solid_tiles: Vec, mask_tiles: Vec, - object_count: u32, + shaders: Vec, tile_rect: Rect, } @@ -1285,7 +1309,12 @@ struct MaskTileScenePrimitive { object_index: u32, } -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, Default)] +struct ObjectShader { + fill_color: ColorU, +} + +#[derive(Clone, Copy, Debug, Default)] struct ColorU { r: u8, g: u8, @@ -1296,7 +1325,7 @@ struct ColorU { // Utilities for built objects impl BuiltObject { - fn new(bounds: &Rect) -> BuiltObject { + fn new(bounds: &Rect, shader: &ObjectShader) -> BuiltObject { // Compute the tile rect. let tile_rect = round_rect_out_to_tile_bounds(&bounds); @@ -1315,6 +1344,7 @@ impl BuiltObject { tiles, fills: vec![], mask_tiles: FixedBitSet::with_capacity(tile_count), + shader: *shader, } } @@ -1350,11 +1380,13 @@ impl BuiltScene { let fill_size = self.fills.len() * mem::size_of::(); let solid_tiles_size = self.solid_tiles.len() * mem::size_of::(); let mask_tiles_size = self.mask_tiles.len() * mem::size_of::(); + let shaders_size = self.shaders.len() * mem::size_of::(); writer.write_u32::((4 + 8 + header_size + 8 + fill_size + 8 + solid_tiles_size + - 8 + mask_tiles_size) as u32)?; + 8 + mask_tiles_size + + 8 + shaders_size) as u32)?; writer.write_all(b"PF3S")?; @@ -1390,6 +1422,13 @@ impl BuiltScene { writer.write_u32::(tile_primitive.object_index)?; } + writer.write_all(b"shad")?; + writer.write_u32::(shaders_size as u32)?; + for &shader in &self.shaders { + let fill_color = shader.fill_color; + writer.write_all(&[fill_color.r, fill_color.g, fill_color.b, fill_color.a])?; + } + return Ok(()); fn write_point(writer: &mut W, point: &Point2D) -> io::Result<()> where W: Write {