From 8c1414e75d4529e6b223539127659e579b49f659 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Fri, 16 Nov 2018 14:14:16 -0800 Subject: [PATCH] Enlarge --- demo2/pathfinder.css | 2 + demo2/pathfinder.ts | 183 +++++++++++++++++++++++++++++-------------- 2 files changed, 127 insertions(+), 58 deletions(-) diff --git a/demo2/pathfinder.css b/demo2/pathfinder.css index 0baae6b5..6f8b720c 100644 --- a/demo2/pathfinder.css +++ b/demo2/pathfinder.css @@ -1,5 +1,7 @@ html, body { background: #e0e0e0; + margin: 0; + overflow: hidden; } .tile { diff --git a/demo2/pathfinder.ts b/demo2/pathfinder.ts index 2653884a..4101da64 100644 --- a/demo2/pathfinder.ts +++ b/demo2/pathfinder.ts @@ -19,10 +19,8 @@ const parseColor: (color: string) => any = require('parse-color'); const SVG_NS: string = "http://www.w3.org/2000/svg"; -const TILE_SIZE: number = 16.0; -const STENCIL_FRAMEBUFFER_SIZE: number = TILE_SIZE * 64; - -const GLOBAL_OFFSET: Point2D = {x: 200.0, y: 150.0}; +const TILE_SIZE: number = 32.0; +const STENCIL_FRAMEBUFFER_SIZE: number = TILE_SIZE * 128; const QUAD_VERTEX_POSITIONS: Uint8Array = new Uint8Array([ 0, 0, @@ -34,6 +32,7 @@ const QUAD_VERTEX_POSITIONS: Uint8Array = new Uint8Array([ interface SVGPath { abs(): SVGPath; translate(x: number, y: number): SVGPath; + matrix(m: number[]): SVGPath; iterate(f: (segment: string[], index: number, x: number, y: number) => string[][] | void): SVGPath; } @@ -59,6 +58,20 @@ interface Vector3D { z: number; } +class Matrix2D { + a: number; b: number; + c: number; d: number; + tx: number; ty: number; + + constructor(a: number, b: number, c: number, d: number, tx: number, ty: number) { + this.a = a; this.b = b; + this.c = c; this.d = d; + this.tx = tx; this.ty = ty; + } +} + +const GLOBAL_TRANSFORM: Matrix2D = new Matrix2D(3.0, 0.0, 0.0, 3.0, 800.0, 550.0); + interface Color { r: number; g: number; @@ -73,6 +86,7 @@ class App { private svg: XMLDocument; private gl: WebGL2RenderingContext; + private disjointTimerQueryExt: any; private stencilTexture: WebGLTexture; private stencilFramebuffer: WebGLFramebuffer; private coverProgram: @@ -87,13 +101,24 @@ class App { private coverVertexBuffer: WebGLBuffer; private coverVertexArray: WebGLVertexArrayObject; + private scene: Scene | null; + private primitiveCount: number | null; + constructor(svg: XMLDocument) { - this.canvas = staticCast(document.getElementById('canvas'), HTMLCanvasElement); + const canvas = staticCast(document.getElementById('canvas'), HTMLCanvasElement); + this.canvas = canvas; this.svg = svg; + const devicePixelRatio = window.devicePixelRatio; + canvas.width = window.innerWidth * devicePixelRatio; + canvas.height = window.innerHeight * devicePixelRatio; + canvas.style.width = window.innerWidth + "px"; + canvas.style.height = window.innerHeight + "px"; + const gl = unwrapNull(this.canvas.getContext('webgl2', {antialias: false})); this.gl = gl; gl.getExtension('EXT_color_buffer_float'); + this.disjointTimerQueryExt = gl.getExtension('EXT_disjoint_timer_query'); this.stencilTexture = unwrapNull(gl.createTexture()); gl.bindTexture(gl.TEXTURE_2D, this.stencilTexture); @@ -203,17 +228,80 @@ class App { gl.enableVertexAttribArray(coverProgram.attributes.TileIndex); gl.enableVertexAttribArray(coverProgram.attributes.Color); - // TODO(pcwalton) + this.scene = null; + this.primitiveCount = 0; } - run(): void { - const gl = this.gl, canvas = this.canvas; + redraw(): void { + const gl = this.gl, canvas = this.canvas, scene = unwrapNull(this.scene); - const scene = new Scene(this.svg); - console.log(scene.tiles.length, "tiles"); + // Start timer. + let timerQuery = null; + if (this.disjointTimerQueryExt != null) { + timerQuery = unwrapNull(gl.createQuery()); + gl.beginQuery(this.disjointTimerQueryExt.TIME_ELAPSED_EXT, timerQuery); + } + + // Stencil. + gl.bindFramebuffer(gl.FRAMEBUFFER, this.stencilFramebuffer); + gl.viewport(0, 0, STENCIL_FRAMEBUFFER_SIZE, STENCIL_FRAMEBUFFER_SIZE); + gl.clearColor(0.0, 0.0, 0.0, 0.0); + gl.clear(gl.COLOR_BUFFER_BIT); + + gl.bindVertexArray(this.stencilVertexArray); + gl.useProgram(this.stencilProgram.program); + gl.uniform2f(this.stencilProgram.uniforms.FramebufferSize, + STENCIL_FRAMEBUFFER_SIZE, + STENCIL_FRAMEBUFFER_SIZE); + gl.uniform2f(this.stencilProgram.uniforms.TileSize, TILE_SIZE, TILE_SIZE); + gl.blendEquation(gl.FUNC_ADD); + gl.blendFunc(gl.ONE, gl.ONE); + gl.enable(gl.BLEND); + gl.drawArraysInstanced(gl.TRIANGLE_FAN, 0, 4, unwrapNull(this.primitiveCount)); + gl.disable(gl.BLEND); + + // Cover. + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + const framebufferSize = {width: canvas.width, height: canvas.height}; + gl.viewport(0, 0, framebufferSize.width, framebufferSize.height); + gl.clearColor(1.0, 1.0, 1.0, 1.0); + gl.clear(gl.COLOR_BUFFER_BIT); + + gl.bindVertexArray(this.coverVertexArray); + gl.useProgram(this.coverProgram.program); + gl.uniform2f(this.coverProgram.uniforms.FramebufferSize, + framebufferSize.width, + framebufferSize.height); + gl.uniform2f(this.coverProgram.uniforms.TileSize, TILE_SIZE, TILE_SIZE); + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, this.stencilTexture); + gl.uniform1i(this.coverProgram.uniforms.StencilTexture, 0); + gl.uniform2f(this.coverProgram.uniforms.StencilTextureSize, + STENCIL_FRAMEBUFFER_SIZE, + STENCIL_FRAMEBUFFER_SIZE); + gl.blendEquation(gl.FUNC_ADD); + gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); + gl.enable(gl.BLEND); + gl.drawArraysInstanced(gl.TRIANGLE_FAN, 0, 4, scene.tiles.length); + gl.disable(gl.BLEND); + + // End timer. + if (timerQuery != null) { + gl.endQuery(this.disjointTimerQueryExt.TIME_ELAPSED_EXT); + waitForQuery(gl, this.disjointTimerQueryExt, timerQuery); + } + } + + buildScene(): void { + this.scene = new Scene(this.svg); + console.log(this.scene.tiles.length, "tiles"); + } + + prepare(): void { + const gl = this.gl, scene = unwrapNull(this.scene); // Construct stencil VBOs. - let primitives = 0; + let primitiveCount = 0; const stencilVertexPositions: number[] = [], stencilVertexTileIndices: number[] = []; for (let tileIndex = 0; tileIndex < scene.tiles.length; tileIndex++) { const tile = scene.tiles[tileIndex]; @@ -243,7 +331,7 @@ class App { } else { stencilVertexPositions.push(lastPoint.x, lastPoint.y, point.x, point.y); stencilVertexTileIndices.push(tileIndex); - primitives++; + primitiveCount++; } lastPoint = point; }); @@ -256,24 +344,6 @@ class App { gl.bindBuffer(gl.ARRAY_BUFFER, this.stencilVertexTileIndicesBuffer); gl.bufferData(gl.ARRAY_BUFFER, new Uint16Array(stencilVertexTileIndices), gl.STATIC_DRAW); - // Stencil. - gl.bindFramebuffer(gl.FRAMEBUFFER, this.stencilFramebuffer); - gl.viewport(0, 0, STENCIL_FRAMEBUFFER_SIZE, STENCIL_FRAMEBUFFER_SIZE); - gl.clearColor(0.0, 0.0, 0.0, 0.0); - gl.clear(gl.COLOR_BUFFER_BIT); - - gl.bindVertexArray(this.stencilVertexArray); - gl.useProgram(this.stencilProgram.program); - gl.uniform2f(this.stencilProgram.uniforms.FramebufferSize, - STENCIL_FRAMEBUFFER_SIZE, - STENCIL_FRAMEBUFFER_SIZE); - gl.uniform2f(this.stencilProgram.uniforms.TileSize, TILE_SIZE, TILE_SIZE); - gl.blendEquation(gl.FUNC_ADD); - gl.blendFunc(gl.ONE, gl.ONE); - gl.enable(gl.BLEND); - gl.drawArraysInstanced(gl.TRIANGLE_FAN, 0, 4, primitives); - gl.disable(gl.BLEND); - // Populate the cover VBO. const coverVertexBufferData = new Int16Array(scene.tiles.length * 5); for (let tileIndex = 0; tileIndex < scene.tiles.length; tileIndex++) { @@ -289,30 +359,8 @@ class App { gl.bufferData(gl.ARRAY_BUFFER, coverVertexBufferData, gl.DYNAMIC_DRAW); console.log(coverVertexBufferData); - // Cover. - gl.bindFramebuffer(gl.FRAMEBUFFER, null); - const framebufferSize = {width: canvas.width, height: canvas.height}; - gl.viewport(0, 0, framebufferSize.width, framebufferSize.height); - gl.clearColor(1.0, 1.0, 1.0, 1.0); - gl.clear(gl.COLOR_BUFFER_BIT); - - gl.bindVertexArray(this.coverVertexArray); - gl.useProgram(this.coverProgram.program); - gl.uniform2f(this.coverProgram.uniforms.FramebufferSize, - framebufferSize.width, - framebufferSize.height); - gl.uniform2f(this.coverProgram.uniforms.TileSize, TILE_SIZE, TILE_SIZE); - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, this.stencilTexture); - gl.uniform1i(this.coverProgram.uniforms.StencilTexture, 0); - gl.uniform2f(this.coverProgram.uniforms.StencilTextureSize, - STENCIL_FRAMEBUFFER_SIZE, - STENCIL_FRAMEBUFFER_SIZE); - gl.blendEquation(gl.FUNC_ADD); - gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); - gl.enable(gl.BLEND); - gl.drawArraysInstanced(gl.TRIANGLE_FAN, 0, 4, scene.tiles.length); - gl.disable(gl.BLEND); + this.primitiveCount = primitiveCount; + console.log(primitiveCount + " primitives"); } } @@ -350,9 +398,13 @@ class Scene { a: Math.round(color[3] * 255.), }); - let path = - SVGPath(unwrapNull(pathElement.getAttribute('d'))).translate(GLOBAL_OFFSET.x, - GLOBAL_OFFSET.y); + let path = SVGPath(unwrapNull(pathElement.getAttribute('d'))); + path = path.matrix([ + GLOBAL_TRANSFORM.a, GLOBAL_TRANSFORM.b, + GLOBAL_TRANSFORM.c, GLOBAL_TRANSFORM.d, + GLOBAL_TRANSFORM.tx, GLOBAL_TRANSFORM.ty, + ]); + path = canonicalizePath(path); const boundingRect = this.boundingRectOfPath(path); @@ -623,13 +675,28 @@ function cross(a: Vector3D, b: Vector3D): Vector3D { }; } +function waitForQuery(gl: WebGL2RenderingContext, disjointTimerQueryExt: any, query: WebGLQuery): + void { + const queryResultAvailable = disjointTimerQueryExt.QUERY_RESULT_AVAILABLE_EXT; + const queryResult = disjointTimerQueryExt.QUERY_RESULT_EXT; + if (!disjointTimerQueryExt.getQueryObjectEXT(query, queryResultAvailable)) { + setTimeout(() => waitForQuery(gl, disjointTimerQueryExt, query), 10); + return; + } + const elapsed = disjointTimerQueryExt.getQueryObjectEXT(query, queryResult) / 1000000.0; + console.log(elapsed + "ms elapsed"); +} + function main(): void { window.fetch(SVG).then(svg => { svg.text().then(svgText => { const svg = staticCast((new DOMParser).parseFromString(svgText, 'image/svg+xml'), XMLDocument); try { - new App(svg).run(); + const app = new App(svg); + app.buildScene(); + app.prepare(); + app.redraw(); } catch (e) { console.error("error", e, e.stack); }