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 uTileSize;
uniform vec2 uStencilTextureSize;
uniform vec2 uViewBoxOrigin;
in vec2 aTessCoord;
in vec2 aTileOrigin;
@ -33,7 +34,7 @@ vec2 computeTileOffset(uint tileIndex, float stencilTextureWidth) {
void main() {
uint tileIndex = uint(gl_InstanceID);
vec2 position = aTileOrigin + uTileSize * aTessCoord;
vec2 position = (aTileOrigin + aTessCoord) * uTileSize + uViewBoxOrigin;
vec2 texCoord = computeTileOffset(tileIndex, uStencilTextureSize.x) + aTessCoord * uTileSize;
vTexCoord = texCoord / uStencilTextureSize;
vBackdrop = aBackdrop;

View File

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

View File

@ -14,6 +14,7 @@ precision highp float;
uniform vec2 uFramebufferSize;
uniform vec2 uTileSize;
uniform vec2 uViewBoxOrigin;
in vec2 aTessCoord;
in vec2 aTileOrigin;
@ -22,7 +23,7 @@ in vec4 aColor;
out vec4 vColor;
void main() {
vec2 position = aTileOrigin + uTileSize * aTessCoord;
vec2 position = (aTileOrigin + aTessCoord) * uTileSize + uViewBoxOrigin;
vColor = aColor;
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_FRAGMENT_SHADER_SOURCE from "./stencil.fs.glsl";
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 {staticCast, unwrapNull} from "./util";
@ -34,8 +34,8 @@ const QUAD_VERTEX_POSITIONS: Uint8Array = new Uint8Array([
]);
const FILL_INSTANCE_SIZE: number = 20;
const SOLID_TILE_INSTANCE_SIZE: number = 12;
const MASK_TILE_INSTANCE_SIZE: number = 16;
const SOLID_TILE_INSTANCE_SIZE: number = 8;
const MASK_TILE_INSTANCE_SIZE: number = 12;
interface Color {
r: number;
@ -58,10 +58,11 @@ class App {
private stencilFramebuffer: WebGLFramebuffer;
private fillProgram: Program<'FramebufferSize' | 'TileSize' | 'AreaLUT',
'TessCoord' | 'From' | 'To' | 'TileIndex'>;
private solidTileProgram: Program<'FramebufferSize' | 'TileSize',
private solidTileProgram: Program<'FramebufferSize' | 'TileSize' | 'ViewBoxOrigin',
'TessCoord' | 'TileOrigin' | 'Color'>;
private maskTileProgram:
Program<'FramebufferSize' | 'TileSize' | 'StencilTexture' | 'StencilTextureSize',
Program<'FramebufferSize' | 'TileSize' | 'StencilTexture' | 'StencilTextureSize' |
'ViewBoxOrigin',
'TessCoord' | 'TileOrigin' | 'Backdrop' | 'Color'>;
private quadVertexBuffer: WebGLBuffer;
private fillVertexBuffer: WebGLBuffer;
@ -71,6 +72,8 @@ class App {
private maskTileVertexBuffer: WebGLBuffer;
private maskVertexArray: WebGLVertexArrayObject;
private viewBox: Rect;
private fillPrimitiveCount: number;
private solidTileCount: number;
private maskTileCount: number;
@ -136,7 +139,8 @@ class App {
'FramebufferSize',
'TileSize',
'StencilTexture',
'StencilTextureSize'
'StencilTextureSize',
'ViewBoxOrigin',
],
[
'TessCoord',
@ -150,7 +154,7 @@ class App {
const solidTileProgram = new Program(gl,
OPAQUE_VERTEX_SHADER_SOURCE,
OPAQUE_FRAGMENT_SHADER_SOURCE,
['FramebufferSize', 'TileSize'],
['FramebufferSize', 'TileSize', 'ViewBoxOrigin'],
['TessCoord', 'TileOrigin', 'Color']);
this.solidTileProgram = solidTileProgram;
@ -224,7 +228,7 @@ class App {
gl.bindBuffer(gl.ARRAY_BUFFER, this.solidTileVertexBuffer);
gl.vertexAttribPointer(solidTileProgram.attributes.TileOrigin,
2,
gl.FLOAT,
gl.SHORT,
false,
SOLID_TILE_INSTANCE_SIZE,
0);
@ -234,7 +238,7 @@ class App {
gl.UNSIGNED_BYTE,
true,
SOLID_TILE_INSTANCE_SIZE,
8);
4);
gl.vertexAttribDivisor(solidTileProgram.attributes.Color, 1);
gl.enableVertexAttribArray(solidTileProgram.attributes.TessCoord);
gl.enableVertexAttribArray(solidTileProgram.attributes.TileOrigin);
@ -254,7 +258,7 @@ class App {
gl.bindBuffer(gl.ARRAY_BUFFER, this.maskTileVertexBuffer);
gl.vertexAttribPointer(maskTileProgram.attributes.TileOrigin,
2,
gl.FLOAT,
gl.SHORT,
false,
MASK_TILE_INSTANCE_SIZE,
0);
@ -264,20 +268,22 @@ class App {
gl.FLOAT,
false,
MASK_TILE_INSTANCE_SIZE,
8);
4);
gl.vertexAttribDivisor(maskTileProgram.attributes.Backdrop, 1);
gl.vertexAttribPointer(maskTileProgram.attributes.Color,
4,
gl.UNSIGNED_BYTE,
true,
MASK_TILE_INSTANCE_SIZE,
12);
8);
gl.vertexAttribDivisor(maskTileProgram.attributes.Color, 1);
gl.enableVertexAttribArray(maskTileProgram.attributes.TessCoord);
gl.enableVertexAttribArray(maskTileProgram.attributes.TileOrigin);
gl.enableVertexAttribArray(maskTileProgram.attributes.Backdrop);
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.
this.canvas.addEventListener('click', event => this.onClick(event), false);
@ -289,6 +295,8 @@ class App {
redraw(): void {
const gl = this.gl, canvas = this.canvas;
console.log("viewBox", this.viewBox);
// Start timer.
let timerQuery = null;
if (this.disjointTimerQueryExt != null) {
@ -333,6 +341,9 @@ class App {
framebufferSize.width,
framebufferSize.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.drawArraysInstanced(gl.TRIANGLE_FAN, 0, 4, this.solidTileCount);
@ -349,6 +360,9 @@ class App {
gl.uniform2f(this.maskTileProgram.uniforms.StencilTextureSize,
STENCIL_FRAMEBUFFER_SIZE.width,
STENCIL_FRAMEBUFFER_SIZE.height);
gl.uniform2f(this.maskTileProgram.uniforms.ViewBoxOrigin,
this.viewBox.origin.x,
this.viewBox.origin.y);
gl.blendEquation(gl.FUNC_ADD);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
gl.enable(gl.BLEND);
@ -402,11 +416,20 @@ class App {
const arrayBuffer = staticCast(reader.result, ArrayBuffer);
const root = new RIFFChunk(new DataView(arrayBuffer));
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 countFieldName: 'fillPrimitiveCount' | 'totalTileCount' | 'solidTileCount' |
'maskTileCount';
let instanceSize;
const id = subchunk.stringID();
switch (id) {
case 'fill':
bindPoint = gl.ARRAY_BUFFER;

View File

@ -74,7 +74,7 @@ fn main() {
println!("Scene bounds: {:?}", scene.bounds);
let start_time = Instant::now();
let mut built_scene = BuiltScene::new();
let mut built_scene = BuiltScene::new(&scene.view_box);
for _ in 0..runs {
built_scene = scene.build();
}
@ -111,7 +111,7 @@ struct Scene {
objects: Vec<PathObject>,
styles: Vec<ComputedStyle>,
bounds: Rect<f32>,
view_box: Option<Rect<f32>>,
view_box: Rect<f32>,
}
#[derive(Debug)]
@ -154,7 +154,7 @@ struct StyleId(u32);
impl 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 {
@ -213,7 +213,7 @@ impl Scene {
elements.next().unwrap()),
Size2D::new(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 {
let mut built_scene = BuiltScene::new();
let mut built_scene = BuiltScene::new(&self.view_box);
for object in &self.objects {
let mut tiler = Tiler::from_outline(&object.outline,
object.color,
@ -935,7 +935,7 @@ struct Tiler<'o, 'p> {
fill_color: ColorU,
built_scene: &'p mut BuiltScene,
view_box: Option<Rect<f32>>,
view_box: Rect<f32>,
point_queue: SortedVector<QueuedEndpoint>,
active_edges: SortedVector<ActiveEdge>,
@ -948,7 +948,7 @@ struct Tiler<'o, 'p> {
impl<'o, 'p> Tiler<'o, 'p> {
fn from_outline(outline: &'o Outline,
fill_color: ColorU,
view_box: &Option<Rect<f32>>,
view_box: &Rect<f32>,
built_scene: &'p mut BuiltScene)
-> Tiler<'o, 'p> {
Tiler {
@ -974,13 +974,11 @@ impl<'o, 'p> Tiler<'o, 'p> {
// Clip to the view box.
let mut bounds = self.outline.bounds;
if let Some(view_box) = self.view_box {
let max_x = f32::min(view_box.max_x(), bounds.max_x());
let max_y = f32::min(view_box.max_y(), bounds.max_y());
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.height = f32::max(0.0, max_y - bounds.origin.y);
}
let max_x = f32::min(self.view_box.max_x(), bounds.max_x());
let max_y = f32::min(self.view_box.max_y(), bounds.max_y());
bounds.origin.x = f32::max(self.view_box.origin.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);
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 view_box_origin_y = match self.view_box {
Some(view_box) => view_box.origin.y,
None => 0.0,
};
let mut tile_index_y = (f32::floor(view_box_origin_y / TILE_HEIGHT) * TILE_HEIGHT) as i16;
let mut tile_index_y = (f32::floor(self.view_box.origin.y / TILE_HEIGHT) * TILE_HEIGHT)
as i16;
self.strip_tiles.clear();
self.strip_tiles.reserve(tiles_across);
@ -1124,6 +1119,7 @@ impl<'o, 'p> Tiler<'o, 'p> {
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 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) {
@ -1166,7 +1162,6 @@ impl<'o, 'p> Tiler<'o, 'p> {
#[inline(never)]
fn flush_tiles(&mut self, tile_index_y: i16) {
// 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() {
if self.used_strip_tiles.contains(tile_index_x) {
self.built_scene.mask_tiles.push(*tile);
@ -1286,6 +1281,7 @@ fn process_active_edge(active_edge: &mut Segment,
#[derive(Debug)]
struct BuiltScene {
view_box: Rect<f32>,
fills: Vec<FillPrimitive>,
solid_tiles: Vec<SolidTilePrimitive>,
mask_tiles: Vec<MaskTilePrimitive>,
@ -1322,23 +1318,32 @@ struct ColorU {
}
impl BuiltScene {
fn new() -> BuiltScene {
BuiltScene { fills: vec![], solid_tiles: vec![], mask_tiles: vec![] }
fn new(view_box: &Rect<f32>) -> BuiltScene {
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 {
writer.write_all(b"RIFF")?;
let header_size = 4 * 4;
let fill_size = self.fills.len() * mem::size_of::<FillPrimitive>();
let solid_tiles_size = self.solid_tiles.len() * mem::size_of::<SolidTilePrimitive>();
let mask_tiles_size = self.mask_tiles.len() * mem::size_of::<MaskTilePrimitive>();
writer.write_u32::<LittleEndian>((4 +
8 + header_size +
8 + fill_size +
8 + solid_tiles_size +
8 + mask_tiles_size) as u32)?;
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_u32::<LittleEndian>(fill_size as u32)?;
for fill_primitive in &self.fills {