WIP: get demo compiling again

This commit is contained in:
Patrick Walton 2018-12-30 09:38:57 -08:00
parent 28d948a36e
commit 6e0e621d19
4 changed files with 159 additions and 66 deletions

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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 maskTileProgram:
Program<'FramebufferSize' | 'TileSize' | 'StencilTexture' | 'StencilTextureSize' |
private solidTileProgram: Program<'FramebufferSize' |
'TileSize' |
'FillColorsTexture' | 'FillColorsTextureSize' |
'ViewBoxOrigin',
'TessCoord' | 'TileOrigin' | 'Backdrop' | 'Color'>;
'TessCoord' | 'TileOrigin' | 'Object'>;
private maskTileProgram:
Program<'FramebufferSize' |
'TileSize' |
'StencilTexture' | 'StencilTextureSize' |
'FillColorsTexture' | 'FillColorsTextureSize' |
'ViewBoxOrigin',
'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,
gl.vertexAttribIPointer(solidTileProgram.attributes.Object,
1,
gl.INT,
SOLID_TILE_INSTANCE_SIZE,
4);
gl.vertexAttribDivisor(solidTileProgram.attributes.Color, 1);
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,
gl.vertexAttribIPointer(maskTileProgram.attributes.Object,
1,
gl.INT,
MASK_TILE_INSTANCE_SIZE,
8);
gl.vertexAttribDivisor(maskTileProgram.attributes.Color, 1);
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();

View File

@ -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<usize> = 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<BuiltObject> {
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<BuiltObject> {
fn build_objects(&self) -> Vec<BuiltObject> {
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<f32>) -> Tiler<'o> {
fn new(outline: &'o Outline, object_index: u32, view_box: &Rect<f32>, 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<f32>, 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<TileObjectPrimitive>,
fills: Vec<FillObjectPrimitive>,
mask_tiles: FixedBitSet,
shader: ObjectShader,
}
#[derive(Debug)]
@ -1245,7 +1269,7 @@ struct BuiltScene {
fills: Vec<FillScenePrimitive>,
solid_tiles: Vec<SolidTileScenePrimitive>,
mask_tiles: Vec<MaskTileScenePrimitive>,
object_count: u32,
shaders: Vec<ObjectShader>,
tile_rect: Rect<i16>,
}
@ -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<f32>) -> BuiltObject {
fn new(bounds: &Rect<f32>, 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::<FillScenePrimitive>();
let solid_tiles_size = self.solid_tiles.len() * mem::size_of::<SolidTileScenePrimitive>();
let mask_tiles_size = self.mask_tiles.len() * mem::size_of::<MaskTileScenePrimitive>();
let shaders_size = self.shaders.len() * mem::size_of::<ObjectShader>();
writer.write_u32::<LittleEndian>((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::<LittleEndian>(tile_primitive.object_index)?;
}
writer.write_all(b"shad")?;
writer.write_u32::<LittleEndian>(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<W>(writer: &mut W, point: &Point2D<f32>) -> io::Result<()> where W: Write {