Pack vertices a little tighter

This commit is contained in:
Patrick Walton 2018-12-21 14:10:26 -08:00
parent 3b92ed0798
commit 4471a74144
5 changed files with 74 additions and 38 deletions

View File

@ -15,6 +15,7 @@ precision highp float;
uniform vec2 uFramebufferSize; uniform vec2 uFramebufferSize;
uniform vec2 uTileSize; uniform vec2 uTileSize;
uniform vec2 uStencilTextureSize; uniform vec2 uStencilTextureSize;
uniform vec2 uViewBoxOrigin;
in vec2 aTessCoord; in vec2 aTessCoord;
in vec2 aTileOrigin; in vec2 aTileOrigin;
@ -33,7 +34,7 @@ vec2 computeTileOffset(uint tileIndex, float stencilTextureWidth) {
void main() { void main() {
uint tileIndex = uint(gl_InstanceID); uint tileIndex = uint(gl_InstanceID);
vec2 position = aTileOrigin + uTileSize * aTessCoord; vec2 position = (aTileOrigin + aTessCoord) * uTileSize + uViewBoxOrigin;
vec2 texCoord = computeTileOffset(tileIndex, uStencilTextureSize.x) + aTessCoord * uTileSize; vec2 texCoord = computeTileOffset(tileIndex, uStencilTextureSize.x) + aTessCoord * uTileSize;
vTexCoord = texCoord / uStencilTextureSize; vTexCoord = texCoord / uStencilTextureSize;
vBackdrop = aBackdrop; vBackdrop = aBackdrop;

View File

@ -54,9 +54,15 @@ export class Point2D {
} }
} }
export interface Size2D { export class Size2D {
width: number; width: number;
height: number; height: number;
constructor(width: number, height: number) {
this.width = width;
this.height = height;
Object.freeze(this);
}
} }
export class Rect { export class Rect {

View File

@ -14,6 +14,7 @@ precision highp float;
uniform vec2 uFramebufferSize; uniform vec2 uFramebufferSize;
uniform vec2 uTileSize; uniform vec2 uTileSize;
uniform vec2 uViewBoxOrigin;
in vec2 aTessCoord; in vec2 aTessCoord;
in vec2 aTileOrigin; in vec2 aTileOrigin;
@ -22,7 +23,7 @@ in vec4 aColor;
out vec4 vColor; out vec4 vColor;
void main() { void main() {
vec2 position = aTileOrigin + uTileSize * aTessCoord; vec2 position = (aTileOrigin + aTessCoord) * uTileSize + uViewBoxOrigin;
vColor = aColor; vColor = aColor;
gl_Position = vec4((position / uFramebufferSize * 2.0 - 1.0) * vec2(1.0, -1.0), 0.0, 1.0); gl_Position = vec4((position / uFramebufferSize * 2.0 - 1.0) * vec2(1.0, -1.0), 0.0, 1.0);
} }

View File

@ -15,7 +15,7 @@ import OPAQUE_FRAGMENT_SHADER_SOURCE from "./opaque.fs.glsl";
import STENCIL_VERTEX_SHADER_SOURCE from "./stencil.vs.glsl"; import STENCIL_VERTEX_SHADER_SOURCE from "./stencil.vs.glsl";
import STENCIL_FRAGMENT_SHADER_SOURCE from "./stencil.fs.glsl"; import STENCIL_FRAGMENT_SHADER_SOURCE from "./stencil.fs.glsl";
import AREA_LUT from "../resources/textures/area-lut.png"; import AREA_LUT from "../resources/textures/area-lut.png";
import {Matrix2D, Size2D} from "./geometry"; import {Matrix2D, Size2D, Rect, Point2D} from "./geometry";
import {SVGPath, TILE_SIZE} from "./tiling"; import {SVGPath, TILE_SIZE} from "./tiling";
import {staticCast, unwrapNull} from "./util"; import {staticCast, unwrapNull} from "./util";
@ -34,8 +34,8 @@ const QUAD_VERTEX_POSITIONS: Uint8Array = new Uint8Array([
]); ]);
const FILL_INSTANCE_SIZE: number = 20; const FILL_INSTANCE_SIZE: number = 20;
const SOLID_TILE_INSTANCE_SIZE: number = 12; const SOLID_TILE_INSTANCE_SIZE: number = 8;
const MASK_TILE_INSTANCE_SIZE: number = 16; const MASK_TILE_INSTANCE_SIZE: number = 12;
interface Color { interface Color {
r: number; r: number;
@ -58,10 +58,11 @@ class App {
private stencilFramebuffer: WebGLFramebuffer; private stencilFramebuffer: WebGLFramebuffer;
private fillProgram: Program<'FramebufferSize' | 'TileSize' | 'AreaLUT', private fillProgram: Program<'FramebufferSize' | 'TileSize' | 'AreaLUT',
'TessCoord' | 'From' | 'To' | 'TileIndex'>; 'TessCoord' | 'From' | 'To' | 'TileIndex'>;
private solidTileProgram: Program<'FramebufferSize' | 'TileSize', private solidTileProgram: Program<'FramebufferSize' | 'TileSize' | 'ViewBoxOrigin',
'TessCoord' | 'TileOrigin' | 'Color'>; 'TessCoord' | 'TileOrigin' | 'Color'>;
private maskTileProgram: private maskTileProgram:
Program<'FramebufferSize' | 'TileSize' | 'StencilTexture' | 'StencilTextureSize', Program<'FramebufferSize' | 'TileSize' | 'StencilTexture' | 'StencilTextureSize' |
'ViewBoxOrigin',
'TessCoord' | 'TileOrigin' | 'Backdrop' | 'Color'>; 'TessCoord' | 'TileOrigin' | 'Backdrop' | 'Color'>;
private quadVertexBuffer: WebGLBuffer; private quadVertexBuffer: WebGLBuffer;
private fillVertexBuffer: WebGLBuffer; private fillVertexBuffer: WebGLBuffer;
@ -71,6 +72,8 @@ class App {
private maskTileVertexBuffer: WebGLBuffer; private maskTileVertexBuffer: WebGLBuffer;
private maskVertexArray: WebGLVertexArrayObject; private maskVertexArray: WebGLVertexArrayObject;
private viewBox: Rect;
private fillPrimitiveCount: number; private fillPrimitiveCount: number;
private solidTileCount: number; private solidTileCount: number;
private maskTileCount: number; private maskTileCount: number;
@ -136,7 +139,8 @@ class App {
'FramebufferSize', 'FramebufferSize',
'TileSize', 'TileSize',
'StencilTexture', 'StencilTexture',
'StencilTextureSize' 'StencilTextureSize',
'ViewBoxOrigin',
], ],
[ [
'TessCoord', 'TessCoord',
@ -150,7 +154,7 @@ class App {
const solidTileProgram = new Program(gl, const solidTileProgram = new Program(gl,
OPAQUE_VERTEX_SHADER_SOURCE, OPAQUE_VERTEX_SHADER_SOURCE,
OPAQUE_FRAGMENT_SHADER_SOURCE, OPAQUE_FRAGMENT_SHADER_SOURCE,
['FramebufferSize', 'TileSize'], ['FramebufferSize', 'TileSize', 'ViewBoxOrigin'],
['TessCoord', 'TileOrigin', 'Color']); ['TessCoord', 'TileOrigin', 'Color']);
this.solidTileProgram = solidTileProgram; this.solidTileProgram = solidTileProgram;
@ -224,7 +228,7 @@ class App {
gl.bindBuffer(gl.ARRAY_BUFFER, this.solidTileVertexBuffer); gl.bindBuffer(gl.ARRAY_BUFFER, this.solidTileVertexBuffer);
gl.vertexAttribPointer(solidTileProgram.attributes.TileOrigin, gl.vertexAttribPointer(solidTileProgram.attributes.TileOrigin,
2, 2,
gl.FLOAT, gl.SHORT,
false, false,
SOLID_TILE_INSTANCE_SIZE, SOLID_TILE_INSTANCE_SIZE,
0); 0);
@ -234,7 +238,7 @@ class App {
gl.UNSIGNED_BYTE, gl.UNSIGNED_BYTE,
true, true,
SOLID_TILE_INSTANCE_SIZE, SOLID_TILE_INSTANCE_SIZE,
8); 4);
gl.vertexAttribDivisor(solidTileProgram.attributes.Color, 1); gl.vertexAttribDivisor(solidTileProgram.attributes.Color, 1);
gl.enableVertexAttribArray(solidTileProgram.attributes.TessCoord); gl.enableVertexAttribArray(solidTileProgram.attributes.TessCoord);
gl.enableVertexAttribArray(solidTileProgram.attributes.TileOrigin); gl.enableVertexAttribArray(solidTileProgram.attributes.TileOrigin);
@ -254,7 +258,7 @@ class App {
gl.bindBuffer(gl.ARRAY_BUFFER, this.maskTileVertexBuffer); gl.bindBuffer(gl.ARRAY_BUFFER, this.maskTileVertexBuffer);
gl.vertexAttribPointer(maskTileProgram.attributes.TileOrigin, gl.vertexAttribPointer(maskTileProgram.attributes.TileOrigin,
2, 2,
gl.FLOAT, gl.SHORT,
false, false,
MASK_TILE_INSTANCE_SIZE, MASK_TILE_INSTANCE_SIZE,
0); 0);
@ -264,20 +268,22 @@ class App {
gl.FLOAT, gl.FLOAT,
false, false,
MASK_TILE_INSTANCE_SIZE, MASK_TILE_INSTANCE_SIZE,
8); 4);
gl.vertexAttribDivisor(maskTileProgram.attributes.Backdrop, 1); gl.vertexAttribDivisor(maskTileProgram.attributes.Backdrop, 1);
gl.vertexAttribPointer(maskTileProgram.attributes.Color, gl.vertexAttribPointer(maskTileProgram.attributes.Color,
4, 4,
gl.UNSIGNED_BYTE, gl.UNSIGNED_BYTE,
true, true,
MASK_TILE_INSTANCE_SIZE, MASK_TILE_INSTANCE_SIZE,
12); 8);
gl.vertexAttribDivisor(maskTileProgram.attributes.Color, 1); gl.vertexAttribDivisor(maskTileProgram.attributes.Color, 1);
gl.enableVertexAttribArray(maskTileProgram.attributes.TessCoord); gl.enableVertexAttribArray(maskTileProgram.attributes.TessCoord);
gl.enableVertexAttribArray(maskTileProgram.attributes.TileOrigin); gl.enableVertexAttribArray(maskTileProgram.attributes.TileOrigin);
gl.enableVertexAttribArray(maskTileProgram.attributes.Backdrop); gl.enableVertexAttribArray(maskTileProgram.attributes.Backdrop);
gl.enableVertexAttribArray(maskTileProgram.attributes.Color); gl.enableVertexAttribArray(maskTileProgram.attributes.Color);
this.viewBox = new Rect(new Point2D(0.0, 0.0), new Size2D(0.0, 0.0));
// Set up event handlers. // Set up event handlers.
this.canvas.addEventListener('click', event => this.onClick(event), false); this.canvas.addEventListener('click', event => this.onClick(event), false);
@ -289,6 +295,8 @@ class App {
redraw(): void { redraw(): void {
const gl = this.gl, canvas = this.canvas; const gl = this.gl, canvas = this.canvas;
console.log("viewBox", this.viewBox);
// Start timer. // Start timer.
let timerQuery = null; let timerQuery = null;
if (this.disjointTimerQueryExt != null) { if (this.disjointTimerQueryExt != null) {
@ -333,6 +341,9 @@ class App {
framebufferSize.width, framebufferSize.width,
framebufferSize.height); framebufferSize.height);
gl.uniform2f(this.solidTileProgram.uniforms.TileSize, TILE_SIZE.width, TILE_SIZE.height); gl.uniform2f(this.solidTileProgram.uniforms.TileSize, TILE_SIZE.width, TILE_SIZE.height);
gl.uniform2f(this.solidTileProgram.uniforms.ViewBoxOrigin,
this.viewBox.origin.x,
this.viewBox.origin.y);
gl.disable(gl.BLEND); gl.disable(gl.BLEND);
gl.drawArraysInstanced(gl.TRIANGLE_FAN, 0, 4, this.solidTileCount); gl.drawArraysInstanced(gl.TRIANGLE_FAN, 0, 4, this.solidTileCount);
@ -349,6 +360,9 @@ class App {
gl.uniform2f(this.maskTileProgram.uniforms.StencilTextureSize, gl.uniform2f(this.maskTileProgram.uniforms.StencilTextureSize,
STENCIL_FRAMEBUFFER_SIZE.width, STENCIL_FRAMEBUFFER_SIZE.width,
STENCIL_FRAMEBUFFER_SIZE.height); STENCIL_FRAMEBUFFER_SIZE.height);
gl.uniform2f(this.maskTileProgram.uniforms.ViewBoxOrigin,
this.viewBox.origin.x,
this.viewBox.origin.y);
gl.blendEquation(gl.FUNC_ADD); gl.blendEquation(gl.FUNC_ADD);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
gl.enable(gl.BLEND); gl.enable(gl.BLEND);
@ -402,11 +416,20 @@ class App {
const arrayBuffer = staticCast(reader.result, ArrayBuffer); const arrayBuffer = staticCast(reader.result, ArrayBuffer);
const root = new RIFFChunk(new DataView(arrayBuffer)); const root = new RIFFChunk(new DataView(arrayBuffer));
for (const subchunk of root.subchunks()) { for (const subchunk of root.subchunks()) {
const id = subchunk.stringID();
if (id === 'head') {
const headerData = subchunk.contents();
this.viewBox = new Rect(new Point2D(headerData.getFloat32(0, true),
headerData.getFloat32(4, true)),
new Size2D(headerData.getFloat32(8, true),
headerData.getFloat32(12, true)));
continue;
}
let bindPoint, buffer; let bindPoint, buffer;
let countFieldName: 'fillPrimitiveCount' | 'totalTileCount' | 'solidTileCount' | let countFieldName: 'fillPrimitiveCount' | 'totalTileCount' | 'solidTileCount' |
'maskTileCount'; 'maskTileCount';
let instanceSize; let instanceSize;
const id = subchunk.stringID();
switch (id) { switch (id) {
case 'fill': case 'fill':
bindPoint = gl.ARRAY_BUFFER; bindPoint = gl.ARRAY_BUFFER;

View File

@ -74,7 +74,7 @@ fn main() {
println!("Scene bounds: {:?}", scene.bounds); println!("Scene bounds: {:?}", scene.bounds);
let start_time = Instant::now(); let start_time = Instant::now();
let mut built_scene = BuiltScene::new(); let mut built_scene = BuiltScene::new(&scene.view_box);
for _ in 0..runs { for _ in 0..runs {
built_scene = scene.build(); built_scene = scene.build();
} }
@ -111,7 +111,7 @@ struct Scene {
objects: Vec<PathObject>, objects: Vec<PathObject>,
styles: Vec<ComputedStyle>, styles: Vec<ComputedStyle>,
bounds: Rect<f32>, bounds: Rect<f32>,
view_box: Option<Rect<f32>>, view_box: Rect<f32>,
} }
#[derive(Debug)] #[derive(Debug)]
@ -154,7 +154,7 @@ struct StyleId(u32);
impl Scene { impl Scene {
fn new() -> Scene { fn new() -> Scene {
Scene { objects: vec![], styles: vec![], bounds: Rect::zero(), view_box: None } Scene { objects: vec![], styles: vec![], bounds: Rect::zero(), view_box: Rect::zero() }
} }
fn from_path(path: &Path) -> Scene { fn from_path(path: &Path) -> Scene {
@ -213,7 +213,7 @@ impl Scene {
elements.next().unwrap()), elements.next().unwrap()),
Size2D::new(elements.next().unwrap(), Size2D::new(elements.next().unwrap(),
elements.next().unwrap())); elements.next().unwrap()));
scene.view_box = Some(global_transform.transform_rect(&view_box)); scene.view_box = global_transform.transform_rect(&view_box);
} }
} }
} }
@ -311,7 +311,7 @@ impl Scene {
} }
fn build(&self) -> BuiltScene { fn build(&self) -> BuiltScene {
let mut built_scene = BuiltScene::new(); let mut built_scene = BuiltScene::new(&self.view_box);
for object in &self.objects { for object in &self.objects {
let mut tiler = Tiler::from_outline(&object.outline, let mut tiler = Tiler::from_outline(&object.outline,
object.color, object.color,
@ -935,7 +935,7 @@ struct Tiler<'o, 'p> {
fill_color: ColorU, fill_color: ColorU,
built_scene: &'p mut BuiltScene, built_scene: &'p mut BuiltScene,
view_box: Option<Rect<f32>>, view_box: Rect<f32>,
point_queue: SortedVector<QueuedEndpoint>, point_queue: SortedVector<QueuedEndpoint>,
active_edges: SortedVector<ActiveEdge>, active_edges: SortedVector<ActiveEdge>,
@ -948,7 +948,7 @@ struct Tiler<'o, 'p> {
impl<'o, 'p> Tiler<'o, 'p> { impl<'o, 'p> Tiler<'o, 'p> {
fn from_outline(outline: &'o Outline, fn from_outline(outline: &'o Outline,
fill_color: ColorU, fill_color: ColorU,
view_box: &Option<Rect<f32>>, view_box: &Rect<f32>,
built_scene: &'p mut BuiltScene) built_scene: &'p mut BuiltScene)
-> Tiler<'o, 'p> { -> Tiler<'o, 'p> {
Tiler { Tiler {
@ -974,13 +974,11 @@ impl<'o, 'p> Tiler<'o, 'p> {
// Clip to the view box. // Clip to the view box.
let mut bounds = self.outline.bounds; let mut bounds = self.outline.bounds;
if let Some(view_box) = self.view_box { let max_x = f32::min(self.view_box.max_x(), bounds.max_x());
let max_x = f32::min(view_box.max_x(), bounds.max_x()); let max_y = f32::min(self.view_box.max_y(), bounds.max_y());
let max_y = f32::min(view_box.max_y(), bounds.max_y()); bounds.origin.x = f32::max(self.view_box.origin.x, bounds.origin.x);
bounds.origin.x = f32::max(view_box.origin.x, bounds.origin.x); bounds.size.width = f32::max(0.0, max_x - bounds.origin.x);
bounds.size.width = f32::max(0.0, max_x - bounds.origin.x); bounds.size.height = f32::max(0.0, max_y - bounds.origin.y);
bounds.size.height = f32::max(0.0, max_y - bounds.origin.y);
}
self.active_edges.clear(); self.active_edges.clear();
@ -991,11 +989,8 @@ impl<'o, 'p> Tiler<'o, 'p> {
let tiles_across = ((strip_right_extent - strip_origin.x) / TILE_WIDTH) as usize; let tiles_across = ((strip_right_extent - strip_origin.x) / TILE_WIDTH) as usize;
let view_box_origin_y = match self.view_box { let mut tile_index_y = (f32::floor(self.view_box.origin.y / TILE_HEIGHT) * TILE_HEIGHT)
Some(view_box) => view_box.origin.y, as i16;
None => 0.0,
};
let mut tile_index_y = (f32::floor(view_box_origin_y / TILE_HEIGHT) * TILE_HEIGHT) as i16;
self.strip_tiles.clear(); self.strip_tiles.clear();
self.strip_tiles.reserve(tiles_across); self.strip_tiles.reserve(tiles_across);
@ -1124,6 +1119,7 @@ impl<'o, 'p> Tiler<'o, 'p> {
let contour = &outline.contours[point_index.contour_index]; let contour = &outline.contours[point_index.contour_index];
// TODO(pcwalton): Could use a bitset of processed edges…
let prev_endpoint_index = contour.prev_endpoint_index_of(point_index.point_index); let prev_endpoint_index = contour.prev_endpoint_index_of(point_index.point_index);
let next_endpoint_index = contour.next_endpoint_index_of(point_index.point_index); let next_endpoint_index = contour.next_endpoint_index_of(point_index.point_index);
if contour.point_is_logically_above(point_index.point_index, prev_endpoint_index) { if contour.point_is_logically_above(point_index.point_index, prev_endpoint_index) {
@ -1166,7 +1162,6 @@ impl<'o, 'p> Tiler<'o, 'p> {
#[inline(never)] #[inline(never)]
fn flush_tiles(&mut self, tile_index_y: i16) { fn flush_tiles(&mut self, tile_index_y: i16) {
// Flush tiles. // Flush tiles.
let first_tile_index = self.built_scene.mask_tiles.len() as u32;
for (tile_index_x, tile) in self.strip_tiles.iter().enumerate() { for (tile_index_x, tile) in self.strip_tiles.iter().enumerate() {
if self.used_strip_tiles.contains(tile_index_x) { if self.used_strip_tiles.contains(tile_index_x) {
self.built_scene.mask_tiles.push(*tile); self.built_scene.mask_tiles.push(*tile);
@ -1286,6 +1281,7 @@ fn process_active_edge(active_edge: &mut Segment,
#[derive(Debug)] #[derive(Debug)]
struct BuiltScene { struct BuiltScene {
view_box: Rect<f32>,
fills: Vec<FillPrimitive>, fills: Vec<FillPrimitive>,
solid_tiles: Vec<SolidTilePrimitive>, solid_tiles: Vec<SolidTilePrimitive>,
mask_tiles: Vec<MaskTilePrimitive>, mask_tiles: Vec<MaskTilePrimitive>,
@ -1322,23 +1318,32 @@ struct ColorU {
} }
impl BuiltScene { impl BuiltScene {
fn new() -> BuiltScene { fn new(view_box: &Rect<f32>) -> BuiltScene {
BuiltScene { fills: vec![], solid_tiles: vec![], mask_tiles: vec![] } BuiltScene { view_box: *view_box, fills: vec![], solid_tiles: vec![], mask_tiles: vec![] }
} }
fn write<W>(&self, writer: &mut W) -> io::Result<()> where W: Write { fn write<W>(&self, writer: &mut W) -> io::Result<()> where W: Write {
writer.write_all(b"RIFF")?; writer.write_all(b"RIFF")?;
let header_size = 4 * 4;
let fill_size = self.fills.len() * mem::size_of::<FillPrimitive>(); let fill_size = self.fills.len() * mem::size_of::<FillPrimitive>();
let solid_tiles_size = self.solid_tiles.len() * mem::size_of::<SolidTilePrimitive>(); let solid_tiles_size = self.solid_tiles.len() * mem::size_of::<SolidTilePrimitive>();
let mask_tiles_size = self.mask_tiles.len() * mem::size_of::<MaskTilePrimitive>(); let mask_tiles_size = self.mask_tiles.len() * mem::size_of::<MaskTilePrimitive>();
writer.write_u32::<LittleEndian>((4 + writer.write_u32::<LittleEndian>((4 +
8 + header_size +
8 + fill_size + 8 + fill_size +
8 + solid_tiles_size + 8 + solid_tiles_size +
8 + mask_tiles_size) as u32)?; 8 + mask_tiles_size) as u32)?;
writer.write_all(b"PF3S")?; writer.write_all(b"PF3S")?;
writer.write_all(b"head")?;
writer.write_u32::<LittleEndian>(header_size as u32)?;
writer.write_f32::<LittleEndian>(self.view_box.origin.x)?;
writer.write_f32::<LittleEndian>(self.view_box.origin.y)?;
writer.write_f32::<LittleEndian>(self.view_box.size.width)?;
writer.write_f32::<LittleEndian>(self.view_box.size.height)?;
writer.write_all(b"fill")?; writer.write_all(b"fill")?;
writer.write_u32::<LittleEndian>(fill_size as u32)?; writer.write_u32::<LittleEndian>(fill_size as u32)?;
for fill_primitive in &self.fills { for fill_primitive in &self.fills {