Use instanced rendering in the 3D demo.

This massively decreases the load time and memory usage in exchange for more draw calls.
This commit is contained in:
Patrick Walton 2017-10-03 15:24:56 -07:00
parent b631fec80f
commit 157292175b
9 changed files with 303 additions and 168 deletions

View File

@ -24,7 +24,7 @@ import SSAAStrategy from "./ssaa-strategy";
import {BUILTIN_FONT_URI, ExpandedMeshData} from "./text"; import {BUILTIN_FONT_URI, ExpandedMeshData} from "./text";
import {GlyphStore, Hint, PathfinderFont, TextFrame, TextRun} from "./text"; import {GlyphStore, Hint, PathfinderFont, TextFrame, TextRun} from "./text";
import {assert, panic, PathfinderError, unwrapNull} from "./utils"; import {assert, panic, PathfinderError, unwrapNull} from "./utils";
import {PathfinderDemoView, Timings} from "./view"; import {DemoView, Timings} from "./view";
const TEXT_AVAILABLE_WIDTH: number = 150000; const TEXT_AVAILABLE_WIDTH: number = 150000;
const TEXT_PADDING: number = 2000; const TEXT_PADDING: number = 2000;
@ -94,12 +94,19 @@ interface MonumentSide {
lines: TextLine[]; lines: TextLine[];
} }
interface MeshDescriptor {
glyphID: number;
textFrameIndex: number;
positions: glmatrix.vec2[];
}
class ThreeDController extends DemoAppController<ThreeDView> { class ThreeDController extends DemoAppController<ThreeDView> {
textFrames: TextFrame[]; textFrames: TextFrame[];
glyphStore: GlyphStore; glyphStore: GlyphStore;
meshDescriptors: MeshDescriptor[];
private baseMeshes: PathfinderMeshData; private baseMeshes: PathfinderMeshData;
private expandedMeshes: ExpandedMeshData[]; private expandedMeshes: PathfinderMeshData[];
private monumentPromise: Promise<MonumentSide[]>; private monumentPromise: Promise<MonumentSide[]>;
@ -196,20 +203,64 @@ class ThreeDController extends DemoAppController<ThreeDView> {
this.glyphStore = new GlyphStore(font, glyphsNeeded); this.glyphStore = new GlyphStore(font, glyphsNeeded);
this.glyphStore.partition().then(result => { this.glyphStore.partition().then(result => {
const hint = new Hint(this.glyphStore.font, PIXELS_PER_UNIT, false);
this.baseMeshes = result.meshes; this.baseMeshes = result.meshes;
this.expandedMeshes = this.textFrames.map(textFrame => {
return textFrame.expandMeshes(this.baseMeshes, glyphsNeeded); this.meshDescriptors = [];
for (let textFrameIndex = 0;
textFrameIndex < this.textFrames.length;
textFrameIndex++) {
const textFrame = this.textFrames[textFrameIndex];
let glyphDescriptors = [];
for (const run of textFrame.runs) {
for (let glyphIndex = 0; glyphIndex < run.glyphIDs.length; glyphIndex++) {
glyphDescriptors.push({
glyphID: run.glyphIDs[glyphIndex],
position: run.calculatePixelOriginForGlyphAt(glyphIndex,
PIXELS_PER_UNIT,
hint),
});
}
}
glyphDescriptors = _.sortBy(glyphDescriptors, descriptor => descriptor.glyphID);
let currentMeshDescriptor: (MeshDescriptor | null) = null;
for (const glyphDescriptor of glyphDescriptors) {
if (currentMeshDescriptor == null ||
glyphDescriptor.glyphID !== currentMeshDescriptor.glyphID) {
if (currentMeshDescriptor != null)
this.meshDescriptors.push(currentMeshDescriptor);
currentMeshDescriptor = {
glyphID: glyphDescriptor.glyphID,
positions: [],
textFrameIndex: textFrameIndex,
};
}
currentMeshDescriptor.positions.push(glyphDescriptor.position);
}
if (currentMeshDescriptor != null)
this.meshDescriptors.push(currentMeshDescriptor);
}
this.expandedMeshes = this.meshDescriptors.map(meshDescriptor => {
const glyphIndex = _.sortedIndexOf(glyphsNeeded, meshDescriptor.glyphID);
return this.baseMeshes.expand([glyphIndex + 1]);
}); });
this.view.then(view => { this.view.then(view => {
view.uploadPathColors(this.expandedMeshes.length); view.uploadPathColors(this.expandedMeshes.length);
view.uploadPathTransforms(this.expandedMeshes.length); view.uploadPathTransforms(this.expandedMeshes.length);
view.attachMeshes(this.expandedMeshes.map(meshes => meshes.meshes)); view.attachMeshes(this.expandedMeshes);
}); });
}); });
} }
} }
class ThreeDView extends PathfinderDemoView { class ThreeDView extends DemoView {
destFramebuffer: WebGLFramebuffer | null = null; destFramebuffer: WebGLFramebuffer | null = null;
camera: PerspectiveCamera; camera: PerspectiveCamera;
@ -221,6 +272,10 @@ class ThreeDView extends PathfinderDemoView {
protected depthFunction: number = this.gl.LESS; protected depthFunction: number = this.gl.LESS;
protected get pathIDsAreInstanced(): boolean {
return true;
}
private _scale: number; private _scale: number;
private appController: ThreeDController; private appController: ThreeDController;
@ -249,38 +304,18 @@ class ThreeDView extends PathfinderDemoView {
this.gl.bufferData(this.gl.ELEMENT_ARRAY_BUFFER, CUBE_INDICES, this.gl.STATIC_DRAW); this.gl.bufferData(this.gl.ELEMENT_ARRAY_BUFFER, CUBE_INDICES, this.gl.STATIC_DRAW);
} }
protected pathColorsForObject(textFrameIndex: number): Uint8Array { protected pathColorsForObject(objectIndex: number): Uint8Array {
const textFrame = this.appController.textFrames[textFrameIndex]; return TEXT_COLOR;
const pathCount = textFrame.totalGlyphCount;
const pathColors = new Uint8Array(4 * (pathCount + 1));
for (let pathIndex = 0; pathIndex < pathCount; pathIndex++)
pathColors.set(TEXT_COLOR, (pathIndex + 1) * 4);
return pathColors;
} }
protected pathTransformsForObject(textFrameIndex: number): Float32Array { protected pathTransformsForObject(objectIndex: number): Float32Array {
const textFrame = this.appController.textFrames[textFrameIndex]; const meshDescriptor = this.appController.meshDescriptors[objectIndex];
const pathCount = textFrame.totalGlyphCount; const pathCount = meshDescriptor.positions.length;
const hint = new Hint(this.appController.glyphStore.font, PIXELS_PER_UNIT, false);
const pathTransforms = new Float32Array(4 * (pathCount + 1)); const pathTransforms = new Float32Array(4 * (pathCount + 1));
for (let pathIndex = 0; pathIndex < pathCount; pathIndex++) {
let globalPathIndex = 0; const glyphOrigin = meshDescriptor.positions[pathIndex];
for (const run of textFrame.runs) { pathTransforms.set([1, 1, glyphOrigin[0], glyphOrigin[1]], (pathIndex + 1) * 4);
for (let pathIndex = 0;
pathIndex < run.glyphIDs.length;
pathIndex++, globalPathIndex++) {
const glyphOrigin = run.calculatePixelOriginForGlyphAt(pathIndex,
PIXELS_PER_UNIT,
hint);
pathTransforms.set([1, 1, glyphOrigin[0], glyphOrigin[1]],
(globalPathIndex + 1) * 4);
}
} }
return pathTransforms; return pathTransforms;
} }
@ -343,17 +378,19 @@ class ThreeDView extends PathfinderDemoView {
} }
protected getModelviewTransform(objectIndex: number): glmatrix.mat4 { protected getModelviewTransform(objectIndex: number): glmatrix.mat4 {
const textFrameIndex = this.appController.meshDescriptors[objectIndex].textFrameIndex;
const transform = glmatrix.mat4.create(); const transform = glmatrix.mat4.create();
glmatrix.mat4.rotateY(transform, transform, Math.PI / 2.0 * objectIndex); glmatrix.mat4.rotateY(transform, transform, Math.PI / 2.0 * textFrameIndex);
glmatrix.mat4.translate(transform, transform, TEXT_TRANSLATION); glmatrix.mat4.translate(transform, transform, TEXT_TRANSLATION);
return transform; return transform;
} }
// Cheap but effective backface culling. // Cheap but effective backface culling.
protected shouldRenderObject(objectIndex: number): boolean { protected shouldRenderObject(objectIndex: number): boolean {
const textFrameIndex = this.appController.meshDescriptors[objectIndex].textFrameIndex;
const translation = this.camera.translation; const translation = this.camera.translation;
const extent = TEXT_TRANSLATION[2] * TEXT_SCALE[2]; const extent = TEXT_TRANSLATION[2] * TEXT_SCALE[2];
switch (objectIndex) { switch (textFrameIndex) {
case 0: return translation[2] < -extent; case 0: return translation[2] < -extent;
case 1: return translation[0] < -extent; case 1: return translation[0] < -extent;
case 2: return translation[2] > extent; case 2: return translation[2] > extent;
@ -361,6 +398,10 @@ class ThreeDView extends PathfinderDemoView {
} }
} }
protected meshInstanceCountForObject(objectIndex: number): number {
return this.appController.meshDescriptors[objectIndex].positions.length;
}
get destAllocatedSize(): glmatrix.vec2 { get destAllocatedSize(): glmatrix.vec2 {
return glmatrix.vec2.fromValues(this.canvas.width, this.canvas.height); return glmatrix.vec2.fromValues(this.canvas.width, this.canvas.height);
} }

View File

@ -10,7 +10,7 @@
import * as glmatrix from 'gl-matrix'; import * as glmatrix from 'gl-matrix';
import {PathfinderDemoView} from './view'; import {DemoView} from './view';
export type AntialiasingStrategyName = 'none' | 'ssaa' | 'ecaa'; export type AntialiasingStrategyName = 'none' | 'ssaa' | 'ecaa';
@ -21,15 +21,15 @@ export abstract class AntialiasingStrategy {
shouldRenderDirect: boolean; shouldRenderDirect: boolean;
// Prepares any OpenGL data. This is only called on startup and canvas resize. // Prepares any OpenGL data. This is only called on startup and canvas resize.
init(view: PathfinderDemoView): void { init(view: DemoView): void {
this.setFramebufferSize(view); this.setFramebufferSize(view);
} }
// Uploads any mesh data. This is called whenever a new set of meshes is supplied. // Uploads any mesh data. This is called whenever a new set of meshes is supplied.
abstract attachMeshes(view: PathfinderDemoView): void; abstract attachMeshes(view: DemoView): void;
// This is called whenever the framebuffer has changed. // This is called whenever the framebuffer has changed.
abstract setFramebufferSize(view: PathfinderDemoView): void; abstract setFramebufferSize(view: DemoView): void;
// Returns the transformation matrix that should be applied when directly rendering. // Returns the transformation matrix that should be applied when directly rendering.
abstract get transform(): glmatrix.mat4; abstract get transform(): glmatrix.mat4;
@ -37,17 +37,17 @@ export abstract class AntialiasingStrategy {
// Called before direct rendering. // Called before direct rendering.
// //
// Typically, this redirects direct rendering to a framebuffer of some sort. // Typically, this redirects direct rendering to a framebuffer of some sort.
abstract prepare(view: PathfinderDemoView): void; abstract prepare(view: DemoView): void;
// Called after direct rendering. // Called after direct rendering.
// //
// This usually performs the actual antialiasing. // This usually performs the actual antialiasing.
abstract antialias(view: PathfinderDemoView): void; abstract antialias(view: DemoView): void;
// Called after antialiasing. // Called after antialiasing.
// //
// This usually blits to the real framebuffer. // This usually blits to the real framebuffer.
abstract resolve(view: PathfinderDemoView): void; abstract resolve(view: DemoView): void;
} }
export class NoAAStrategy extends AntialiasingStrategy { export class NoAAStrategy extends AntialiasingStrategy {
@ -58,9 +58,9 @@ export class NoAAStrategy extends AntialiasingStrategy {
this.framebufferSize = glmatrix.vec2.create(); this.framebufferSize = glmatrix.vec2.create();
} }
attachMeshes(view: PathfinderDemoView) {} attachMeshes(view: DemoView) {}
setFramebufferSize(view: PathfinderDemoView) { setFramebufferSize(view: DemoView) {
this.framebufferSize = view.destAllocatedSize; this.framebufferSize = view.destAllocatedSize;
} }
@ -68,15 +68,15 @@ export class NoAAStrategy extends AntialiasingStrategy {
return glmatrix.mat4.create(); return glmatrix.mat4.create();
} }
prepare(view: PathfinderDemoView) { prepare(view: DemoView) {
view.gl.bindFramebuffer(view.gl.FRAMEBUFFER, view.destFramebuffer); view.gl.bindFramebuffer(view.gl.FRAMEBUFFER, view.destFramebuffer);
view.gl.viewport(0, 0, this.framebufferSize[0], this.framebufferSize[1]); view.gl.viewport(0, 0, this.framebufferSize[0], this.framebufferSize[1]);
view.gl.disable(view.gl.SCISSOR_TEST); view.gl.disable(view.gl.SCISSOR_TEST);
} }
antialias(view: PathfinderDemoView) {} antialias(view: DemoView) {}
resolve(view: PathfinderDemoView) {} resolve(view: DemoView) {}
get shouldRenderDirect() { get shouldRenderDirect() {
return true; return true;

View File

@ -12,7 +12,7 @@ import { AntialiasingStrategyName, SubpixelAAType } from "./aa-strategy";
import {FilePickerView} from "./file-picker"; import {FilePickerView} from "./file-picker";
import {ShaderLoader, ShaderMap, ShaderProgramSource} from './shader-loader'; import {ShaderLoader, ShaderMap, ShaderProgramSource} from './shader-loader';
import {expectNotNull, unwrapNull, unwrapUndef} from './utils'; import {expectNotNull, unwrapNull, unwrapUndef} from './utils';
import {PathfinderDemoView, Timings, TIMINGS} from "./view"; import {DemoView, Timings, TIMINGS} from "./view";
export abstract class AppController { export abstract class AppController {
protected canvas: HTMLCanvasElement; protected canvas: HTMLCanvasElement;
@ -45,7 +45,7 @@ export abstract class AppController {
protected abstract get defaultFile(): string; protected abstract get defaultFile(): string;
} }
export abstract class DemoAppController<View extends PathfinderDemoView> extends AppController { export abstract class DemoAppController<View extends DemoView> extends AppController {
view: Promise<View>; view: Promise<View>;
protected abstract readonly builtinFileURI: string; protected abstract readonly builtinFileURI: string;

View File

@ -23,7 +23,7 @@ import SSAAStrategy from './ssaa-strategy';
import {BUILTIN_FONT_URI, ExpandedMeshData, GlyphStore, PathfinderFont, TextFrame} from "./text"; import {BUILTIN_FONT_URI, ExpandedMeshData, GlyphStore, PathfinderFont, TextFrame} from "./text";
import {TextRun} from "./text"; import {TextRun} from "./text";
import {assert, PathfinderError, unwrapNull} from "./utils"; import {assert, PathfinderError, unwrapNull} from "./utils";
import {MonochromePathfinderView, PathfinderDemoView, Timings } from "./view"; import {DemoView, MonochromeDemoView, Timings } from "./view";
const STRING: string = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; const STRING: string = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
@ -178,7 +178,7 @@ class BenchmarkAppController extends DemoAppController<BenchmarkTestView> {
} }
} }
class BenchmarkTestView extends MonochromePathfinderView { class BenchmarkTestView extends MonochromeDemoView {
destFramebuffer: WebGLFramebuffer | null = null; destFramebuffer: WebGLFramebuffer | null = null;
renderingPromiseCallback: ((time: number) => void) | null; renderingPromiseCallback: ((time: number) => void) | null;

View File

@ -18,7 +18,7 @@ import {WebGLVertexArrayObject} from './gl-utils';
import {B_QUAD_LOWER_INDICES_OFFSET, B_QUAD_SIZE, B_QUAD_UPPER_INDICES_OFFSET} from './meshes'; import {B_QUAD_LOWER_INDICES_OFFSET, B_QUAD_SIZE, B_QUAD_UPPER_INDICES_OFFSET} from './meshes';
import {PathfinderShaderProgram} from './shader-loader'; import {PathfinderShaderProgram} from './shader-loader';
import {UINT32_SIZE, unwrapNull} from './utils'; import {UINT32_SIZE, unwrapNull} from './utils';
import {MonochromePathfinderView} from './view'; import {MonochromeDemoView} from './view';
interface UpperAndLower<T> { interface UpperAndLower<T> {
upper: T; upper: T;
@ -56,14 +56,14 @@ export abstract class ECAAStrategy extends AntialiasingStrategy {
this.destFramebufferSize = glmatrix.vec2.create(); this.destFramebufferSize = glmatrix.vec2.create();
} }
init(view: MonochromePathfinderView) { init(view: MonochromeDemoView) {
super.init(view); super.init(view);
this.bVertexPositionBufferTexture = new PathfinderBufferTexture(view.gl, this.bVertexPositionBufferTexture = new PathfinderBufferTexture(view.gl,
'uBVertexPosition'); 'uBVertexPosition');
this.bVertexPathIDBufferTexture = new PathfinderBufferTexture(view.gl, 'uBVertexPathID'); this.bVertexPathIDBufferTexture = new PathfinderBufferTexture(view.gl, 'uBVertexPathID');
} }
attachMeshes(view: MonochromePathfinderView) { attachMeshes(view: MonochromeDemoView) {
const bVertexPositions = new Float32Array(view.meshData[0].bVertexPositions); const bVertexPositions = new Float32Array(view.meshData[0].bVertexPositions);
const bVertexPathIDs = new Uint8Array(view.meshData[0].bVertexPathIDs); const bVertexPathIDs = new Uint8Array(view.meshData[0].bVertexPathIDs);
this.bVertexPositionBufferTexture.upload(view.gl, bVertexPositions); this.bVertexPositionBufferTexture.upload(view.gl, bVertexPositions);
@ -76,7 +76,7 @@ export abstract class ECAAStrategy extends AntialiasingStrategy {
this.createResolveVAO(view); this.createResolveVAO(view);
} }
setFramebufferSize(view: MonochromePathfinderView) { setFramebufferSize(view: MonochromeDemoView) {
this.destFramebufferSize = glmatrix.vec2.clone(view.destAllocatedSize); this.destFramebufferSize = glmatrix.vec2.clone(view.destAllocatedSize);
glmatrix.vec2.mul(this.supersampledFramebufferSize, glmatrix.vec2.mul(this.supersampledFramebufferSize,
this.destFramebufferSize, this.destFramebufferSize,
@ -88,7 +88,7 @@ export abstract class ECAAStrategy extends AntialiasingStrategy {
view.gl.bindFramebuffer(view.gl.FRAMEBUFFER, null); view.gl.bindFramebuffer(view.gl.FRAMEBUFFER, null);
} }
prepare(view: MonochromePathfinderView) { prepare(view: MonochromeDemoView) {
const usedSize = this.supersampledUsedSize(view); const usedSize = this.supersampledUsedSize(view);
view.gl.bindFramebuffer(view.gl.FRAMEBUFFER, this.directFramebuffer); view.gl.bindFramebuffer(view.gl.FRAMEBUFFER, this.directFramebuffer);
view.gl.viewport(0, view.gl.viewport(0,
@ -123,7 +123,7 @@ export abstract class ECAAStrategy extends AntialiasingStrategy {
]); ]);
} }
antialias(view: MonochromePathfinderView) { antialias(view: MonochromeDemoView) {
// Detect edges if necessary. // Detect edges if necessary.
this.detectEdgesIfNecessary(view); this.detectEdgesIfNecessary(view);
@ -135,7 +135,7 @@ export abstract class ECAAStrategy extends AntialiasingStrategy {
this.antialiasCurves(view); this.antialiasCurves(view);
} }
resolve(view: MonochromePathfinderView) { resolve(view: MonochromeDemoView) {
// Resolve the antialiasing. // Resolve the antialiasing.
this.resolveAA(view); this.resolveAA(view);
} }
@ -144,7 +144,7 @@ export abstract class ECAAStrategy extends AntialiasingStrategy {
return glmatrix.mat4.create(); return glmatrix.mat4.create();
} }
protected initDirectFramebuffer(view: MonochromePathfinderView) { protected initDirectFramebuffer(view: MonochromeDemoView) {
this.directColorTexture = createFramebufferColorTexture(view.gl, this.destFramebufferSize); this.directColorTexture = createFramebufferColorTexture(view.gl, this.destFramebufferSize);
this.directPathIDTexture = createFramebufferColorTexture(view.gl, this.directPathIDTexture = createFramebufferColorTexture(view.gl,
this.destFramebufferSize); this.destFramebufferSize);
@ -155,31 +155,31 @@ export abstract class ECAAStrategy extends AntialiasingStrategy {
this.directDepthTexture); this.directDepthTexture);
} }
protected setCoverDepthState(view: MonochromePathfinderView): void { protected setCoverDepthState(view: MonochromeDemoView): void {
view.gl.disable(view.gl.DEPTH_TEST); view.gl.disable(view.gl.DEPTH_TEST);
} }
protected setResolveDepthState(view: MonochromePathfinderView): void { protected setResolveDepthState(view: MonochromeDemoView): void {
view.gl.disable(view.gl.DEPTH_TEST); view.gl.disable(view.gl.DEPTH_TEST);
} }
protected supersampledUsedSize(view: MonochromePathfinderView): glmatrix.vec2 { protected supersampledUsedSize(view: MonochromeDemoView): glmatrix.vec2 {
const usedSize = glmatrix.vec2.create(); const usedSize = glmatrix.vec2.create();
glmatrix.vec2.mul(usedSize, view.destUsedSize, this.supersampleScale); glmatrix.vec2.mul(usedSize, view.destUsedSize, this.supersampleScale);
return usedSize; return usedSize;
} }
protected abstract getResolveProgram(view: MonochromePathfinderView): PathfinderShaderProgram; protected abstract getResolveProgram(view: MonochromeDemoView): PathfinderShaderProgram;
protected abstract initEdgeDetectFramebuffer(view: MonochromePathfinderView): void; protected abstract initEdgeDetectFramebuffer(view: MonochromeDemoView): void;
protected abstract createEdgeDetectVAO(view: MonochromePathfinderView): void; protected abstract createEdgeDetectVAO(view: MonochromeDemoView): void;
protected abstract detectEdgesIfNecessary(view: MonochromePathfinderView): void; protected abstract detectEdgesIfNecessary(view: MonochromeDemoView): void;
protected abstract clearForCover(view: MonochromePathfinderView): void; protected abstract clearForCover(view: MonochromeDemoView): void;
protected abstract setAADepthState(view: MonochromePathfinderView): void; protected abstract setAADepthState(view: MonochromeDemoView): void;
protected abstract clearForResolve(view: MonochromePathfinderView): void; protected abstract clearForResolve(view: MonochromeDemoView): void;
protected abstract setResolveUniforms(view: MonochromePathfinderView, protected abstract setResolveUniforms(view: MonochromeDemoView,
program: PathfinderShaderProgram): void; program: PathfinderShaderProgram): void;
private initAAAlphaFramebuffer(view: MonochromePathfinderView) { private initAAAlphaFramebuffer(view: MonochromeDemoView) {
this.aaAlphaTexture = unwrapNull(view.gl.createTexture()); this.aaAlphaTexture = unwrapNull(view.gl.createTexture());
view.gl.activeTexture(view.gl.TEXTURE0); view.gl.activeTexture(view.gl.TEXTURE0);
view.gl.bindTexture(view.gl.TEXTURE_2D, this.aaAlphaTexture); view.gl.bindTexture(view.gl.TEXTURE_2D, this.aaAlphaTexture);
@ -203,7 +203,7 @@ export abstract class ECAAStrategy extends AntialiasingStrategy {
this.aaDepthTexture); this.aaDepthTexture);
} }
private createCoverVAO(view: MonochromePathfinderView) { private createCoverVAO(view: MonochromeDemoView) {
this.coverVAO = view.vertexArrayObjectExt.createVertexArrayOES(); this.coverVAO = view.vertexArrayObjectExt.createVertexArrayOES();
view.vertexArrayObjectExt.bindVertexArrayOES(this.coverVAO); view.vertexArrayObjectExt.bindVertexArrayOES(this.coverVAO);
@ -235,7 +235,7 @@ export abstract class ECAAStrategy extends AntialiasingStrategy {
view.vertexArrayObjectExt.bindVertexArrayOES(null); view.vertexArrayObjectExt.bindVertexArrayOES(null);
} }
private createLineVAOs(view: MonochromePathfinderView) { private createLineVAOs(view: MonochromeDemoView) {
const lineProgram = view.shaderPrograms.ecaaLine; const lineProgram = view.shaderPrograms.ecaaLine;
const attributes = lineProgram.attributes; const attributes = lineProgram.attributes;
@ -270,7 +270,7 @@ export abstract class ECAAStrategy extends AntialiasingStrategy {
this.lineVAOs = vaos as UpperAndLower<WebGLVertexArrayObject>; this.lineVAOs = vaos as UpperAndLower<WebGLVertexArrayObject>;
} }
private createCurveVAOs(view: MonochromePathfinderView) { private createCurveVAOs(view: MonochromeDemoView) {
const curveProgram = view.shaderPrograms.ecaaCurve; const curveProgram = view.shaderPrograms.ecaaCurve;
const attributes = curveProgram.attributes; const attributes = curveProgram.attributes;
@ -313,7 +313,7 @@ export abstract class ECAAStrategy extends AntialiasingStrategy {
this.curveVAOs = vaos as UpperAndLower<WebGLVertexArrayObject>; this.curveVAOs = vaos as UpperAndLower<WebGLVertexArrayObject>;
} }
private createResolveVAO(view: MonochromePathfinderView) { private createResolveVAO(view: MonochromeDemoView) {
this.resolveVAO = view.vertexArrayObjectExt.createVertexArrayOES(); this.resolveVAO = view.vertexArrayObjectExt.createVertexArrayOES();
view.vertexArrayObjectExt.bindVertexArrayOES(this.resolveVAO); view.vertexArrayObjectExt.bindVertexArrayOES(this.resolveVAO);
@ -324,7 +324,7 @@ export abstract class ECAAStrategy extends AntialiasingStrategy {
view.vertexArrayObjectExt.bindVertexArrayOES(null); view.vertexArrayObjectExt.bindVertexArrayOES(null);
} }
private cover(view: MonochromePathfinderView) { private cover(view: MonochromeDemoView) {
// Set state for conservative coverage. // Set state for conservative coverage.
const coverProgram = view.shaderPrograms.ecaaCover; const coverProgram = view.shaderPrograms.ecaaCover;
const usedSize = this.supersampledUsedSize(view); const usedSize = this.supersampledUsedSize(view);
@ -356,7 +356,7 @@ export abstract class ECAAStrategy extends AntialiasingStrategy {
view.vertexArrayObjectExt.bindVertexArrayOES(null); view.vertexArrayObjectExt.bindVertexArrayOES(null);
} }
private setAAState(view: MonochromePathfinderView) { private setAAState(view: MonochromeDemoView) {
const usedSize = this.supersampledUsedSize(view); const usedSize = this.supersampledUsedSize(view);
view.gl.bindFramebuffer(view.gl.FRAMEBUFFER, this.aaFramebuffer); view.gl.bindFramebuffer(view.gl.FRAMEBUFFER, this.aaFramebuffer);
view.gl.viewport(0, view.gl.viewport(0,
@ -373,7 +373,7 @@ export abstract class ECAAStrategy extends AntialiasingStrategy {
view.gl.enable(view.gl.BLEND); view.gl.enable(view.gl.BLEND);
} }
private setAAUniforms(view: MonochromePathfinderView, uniforms: UniformMap) { private setAAUniforms(view: MonochromeDemoView, uniforms: UniformMap) {
view.setTransformSTUniform(uniforms, 0); view.setTransformSTUniform(uniforms, 0);
view.setFramebufferSizeUniform(uniforms); view.setFramebufferSizeUniform(uniforms);
this.bVertexPositionBufferTexture.bind(view.gl, uniforms, 0); this.bVertexPositionBufferTexture.bind(view.gl, uniforms, 0);
@ -383,7 +383,7 @@ export abstract class ECAAStrategy extends AntialiasingStrategy {
view.pathHintsBufferTexture.bind(view.gl, uniforms, 3); view.pathHintsBufferTexture.bind(view.gl, uniforms, 3);
} }
private antialiasLines(view: MonochromePathfinderView) { private antialiasLines(view: MonochromeDemoView) {
this.setAAState(view); this.setAAState(view);
const lineProgram = view.shaderPrograms.ecaaLine; const lineProgram = view.shaderPrograms.ecaaLine;
@ -408,7 +408,7 @@ export abstract class ECAAStrategy extends AntialiasingStrategy {
view.vertexArrayObjectExt.bindVertexArrayOES(null); view.vertexArrayObjectExt.bindVertexArrayOES(null);
} }
private antialiasCurves(view: MonochromePathfinderView) { private antialiasCurves(view: MonochromeDemoView) {
this.setAAState(view); this.setAAState(view);
const curveProgram = view.shaderPrograms.ecaaCurve; const curveProgram = view.shaderPrograms.ecaaCurve;
@ -433,7 +433,7 @@ export abstract class ECAAStrategy extends AntialiasingStrategy {
view.vertexArrayObjectExt.bindVertexArrayOES(null); view.vertexArrayObjectExt.bindVertexArrayOES(null);
} }
private resolveAA(view: MonochromePathfinderView) { private resolveAA(view: MonochromeDemoView) {
// Set state for ECAA resolve. // Set state for ECAA resolve.
const usedSize = view.destUsedSize; const usedSize = view.destUsedSize;
view.gl.bindFramebuffer(view.gl.FRAMEBUFFER, view.destFramebuffer); view.gl.bindFramebuffer(view.gl.FRAMEBUFFER, view.destFramebuffer);
@ -477,34 +477,34 @@ export abstract class ECAAStrategy extends AntialiasingStrategy {
} }
export class ECAAMonochromeStrategy extends ECAAStrategy { export class ECAAMonochromeStrategy extends ECAAStrategy {
protected getResolveProgram(view: MonochromePathfinderView): PathfinderShaderProgram { protected getResolveProgram(view: MonochromeDemoView): PathfinderShaderProgram {
if (this.subpixelAA !== 'none') if (this.subpixelAA !== 'none')
return view.shaderPrograms.ecaaMonoSubpixelResolve; return view.shaderPrograms.ecaaMonoSubpixelResolve;
return view.shaderPrograms.ecaaMonoResolve; return view.shaderPrograms.ecaaMonoResolve;
} }
protected initEdgeDetectFramebuffer(view: MonochromePathfinderView) {} protected initEdgeDetectFramebuffer(view: MonochromeDemoView) {}
protected createEdgeDetectVAO(view: MonochromePathfinderView) {} protected createEdgeDetectVAO(view: MonochromeDemoView) {}
protected detectEdgesIfNecessary(view: MonochromePathfinderView) {} protected detectEdgesIfNecessary(view: MonochromeDemoView) {}
protected clearForCover(view: MonochromePathfinderView) { protected clearForCover(view: MonochromeDemoView) {
view.gl.clearColor(0.0, 0.0, 0.0, 0.0); view.gl.clearColor(0.0, 0.0, 0.0, 0.0);
view.gl.clearDepth(0.0); view.gl.clearDepth(0.0);
view.gl.clear(view.gl.COLOR_BUFFER_BIT | view.gl.DEPTH_BUFFER_BIT); view.gl.clear(view.gl.COLOR_BUFFER_BIT | view.gl.DEPTH_BUFFER_BIT);
} }
protected setAADepthState(view: MonochromePathfinderView) { protected setAADepthState(view: MonochromeDemoView) {
view.gl.disable(view.gl.DEPTH_TEST); view.gl.disable(view.gl.DEPTH_TEST);
} }
protected clearForResolve(view: MonochromePathfinderView) { protected clearForResolve(view: MonochromeDemoView) {
view.gl.clearColor(0.0, 0.0, 0.0, 0.0); view.gl.clearColor(0.0, 0.0, 0.0, 0.0);
view.gl.clear(view.gl.COLOR_BUFFER_BIT); view.gl.clear(view.gl.COLOR_BUFFER_BIT);
} }
protected setResolveUniforms(view: MonochromePathfinderView, program: PathfinderShaderProgram) { protected setResolveUniforms(view: MonochromeDemoView, program: PathfinderShaderProgram) {
view.gl.uniform4fv(program.uniforms.uBGColor, view.bgColor); view.gl.uniform4fv(program.uniforms.uBGColor, view.bgColor);
view.gl.uniform4fv(program.uniforms.uFGColor, view.fgColor); view.gl.uniform4fv(program.uniforms.uFGColor, view.fgColor);
} }
@ -522,17 +522,17 @@ export class ECAAMulticolorStrategy extends ECAAStrategy {
private bgColorTexture: WebGLTexture; private bgColorTexture: WebGLTexture;
private fgColorTexture: WebGLTexture; private fgColorTexture: WebGLTexture;
protected getResolveProgram(view: MonochromePathfinderView): PathfinderShaderProgram { protected getResolveProgram(view: MonochromeDemoView): PathfinderShaderProgram {
return view.shaderPrograms.ecaaMultiResolve; return view.shaderPrograms.ecaaMultiResolve;
} }
protected initDirectFramebuffer(view: MonochromePathfinderView) { protected initDirectFramebuffer(view: MonochromeDemoView) {
this._directDepthTexture = this._directDepthTexture =
createFramebufferDepthTexture(view.gl, this.supersampledFramebufferSize); createFramebufferDepthTexture(view.gl, this.supersampledFramebufferSize);
super.initDirectFramebuffer(view); super.initDirectFramebuffer(view);
} }
protected initEdgeDetectFramebuffer(view: MonochromePathfinderView) { protected initEdgeDetectFramebuffer(view: MonochromeDemoView) {
this.bgColorTexture = createFramebufferColorTexture(view.gl, this.bgColorTexture = createFramebufferColorTexture(view.gl,
this.supersampledFramebufferSize); this.supersampledFramebufferSize);
this.fgColorTexture = createFramebufferColorTexture(view.gl, this.fgColorTexture = createFramebufferColorTexture(view.gl,
@ -543,7 +543,7 @@ export class ECAAMulticolorStrategy extends ECAAStrategy {
this.aaDepthTexture); this.aaDepthTexture);
} }
protected createEdgeDetectVAO(view: MonochromePathfinderView) { protected createEdgeDetectVAO(view: MonochromeDemoView) {
this.edgeDetectVAO = view.vertexArrayObjectExt.createVertexArrayOES(); this.edgeDetectVAO = view.vertexArrayObjectExt.createVertexArrayOES();
view.vertexArrayObjectExt.bindVertexArrayOES(this.edgeDetectVAO); view.vertexArrayObjectExt.bindVertexArrayOES(this.edgeDetectVAO);
@ -554,7 +554,7 @@ export class ECAAMulticolorStrategy extends ECAAStrategy {
view.vertexArrayObjectExt.bindVertexArrayOES(null); view.vertexArrayObjectExt.bindVertexArrayOES(null);
} }
protected detectEdgesIfNecessary(view: MonochromePathfinderView) { protected detectEdgesIfNecessary(view: MonochromeDemoView) {
// Set state for edge detection. // Set state for edge detection.
const edgeDetectProgram = view.shaderPrograms.ecaaEdgeDetect; const edgeDetectProgram = view.shaderPrograms.ecaaEdgeDetect;
view.gl.bindFramebuffer(view.gl.FRAMEBUFFER, this.edgeDetectFramebuffer); view.gl.bindFramebuffer(view.gl.FRAMEBUFFER, this.edgeDetectFramebuffer);
@ -593,32 +593,32 @@ export class ECAAMulticolorStrategy extends ECAAStrategy {
view.vertexArrayObjectExt.bindVertexArrayOES(null); view.vertexArrayObjectExt.bindVertexArrayOES(null);
} }
protected setCoverDepthState(view: MonochromePathfinderView) { protected setCoverDepthState(view: MonochromeDemoView) {
view.gl.depthMask(false); view.gl.depthMask(false);
view.gl.depthFunc(view.gl.ALWAYS); view.gl.depthFunc(view.gl.ALWAYS);
view.gl.enable(view.gl.DEPTH_TEST); view.gl.enable(view.gl.DEPTH_TEST);
} }
protected clearForCover(view: MonochromePathfinderView) { protected clearForCover(view: MonochromeDemoView) {
view.gl.clearColor(0.0, 0.0, 0.0, 0.0); view.gl.clearColor(0.0, 0.0, 0.0, 0.0);
view.gl.clear(view.gl.COLOR_BUFFER_BIT); view.gl.clear(view.gl.COLOR_BUFFER_BIT);
} }
protected setAADepthState(view: MonochromePathfinderView) { protected setAADepthState(view: MonochromeDemoView) {
view.gl.depthMask(false); view.gl.depthMask(false);
view.gl.depthFunc(view.gl.EQUAL); view.gl.depthFunc(view.gl.EQUAL);
view.gl.enable(view.gl.DEPTH_TEST); view.gl.enable(view.gl.DEPTH_TEST);
} }
protected setResolveDepthState(view: MonochromePathfinderView) { protected setResolveDepthState(view: MonochromeDemoView) {
view.gl.depthMask(false); view.gl.depthMask(false);
view.gl.depthFunc(view.gl.NOTEQUAL); view.gl.depthFunc(view.gl.NOTEQUAL);
view.gl.enable(view.gl.DEPTH_TEST); view.gl.enable(view.gl.DEPTH_TEST);
} }
protected clearForResolve(view: MonochromePathfinderView) {} protected clearForResolve(view: MonochromeDemoView) {}
protected setResolveUniforms(view: MonochromePathfinderView, program: PathfinderShaderProgram) { protected setResolveUniforms(view: MonochromeDemoView, program: PathfinderShaderProgram) {
view.gl.activeTexture(view.gl.TEXTURE1); view.gl.activeTexture(view.gl.TEXTURE1);
view.gl.bindTexture(view.gl.TEXTURE_2D, this.bgColorTexture); view.gl.bindTexture(view.gl.TEXTURE_2D, this.bgColorTexture);
view.gl.uniform1i(program.uniforms.uBGColor, 1); view.gl.uniform1i(program.uniforms.uBGColor, 1);

View File

@ -13,7 +13,7 @@ import * as glmatrix from 'gl-matrix';
import {AntialiasingStrategy, SubpixelAAType} from './aa-strategy'; import {AntialiasingStrategy, SubpixelAAType} from './aa-strategy';
import {createFramebuffer, createFramebufferDepthTexture, setTextureParameters} from './gl-utils'; import {createFramebuffer, createFramebufferDepthTexture, setTextureParameters} from './gl-utils';
import {unwrapNull} from './utils'; import {unwrapNull} from './utils';
import {PathfinderDemoView} from './view'; import {DemoView} from './view';
export default class SSAAStrategy extends AntialiasingStrategy { export default class SSAAStrategy extends AntialiasingStrategy {
private level: number; private level: number;
@ -33,9 +33,9 @@ export default class SSAAStrategy extends AntialiasingStrategy {
this.supersampledFramebufferSize = glmatrix.vec2.create(); this.supersampledFramebufferSize = glmatrix.vec2.create();
} }
attachMeshes(view: PathfinderDemoView) {} attachMeshes(view: DemoView) {}
setFramebufferSize(view: PathfinderDemoView) { setFramebufferSize(view: DemoView) {
this.destFramebufferSize = glmatrix.vec2.clone(view.destAllocatedSize); this.destFramebufferSize = glmatrix.vec2.clone(view.destAllocatedSize);
this.supersampledFramebufferSize = glmatrix.vec2.create(); this.supersampledFramebufferSize = glmatrix.vec2.create();
@ -77,7 +77,7 @@ export default class SSAAStrategy extends AntialiasingStrategy {
return transform; return transform;
} }
prepare(view: PathfinderDemoView) { prepare(view: DemoView) {
const framebufferSize = this.supersampledFramebufferSize; const framebufferSize = this.supersampledFramebufferSize;
const usedSize = this.usedSupersampledFramebufferSize(view); const usedSize = this.usedSupersampledFramebufferSize(view);
view.gl.bindFramebuffer(view.gl.FRAMEBUFFER, this.supersampledFramebuffer); view.gl.bindFramebuffer(view.gl.FRAMEBUFFER, this.supersampledFramebuffer);
@ -86,9 +86,9 @@ export default class SSAAStrategy extends AntialiasingStrategy {
view.gl.enable(view.gl.SCISSOR_TEST); view.gl.enable(view.gl.SCISSOR_TEST);
} }
antialias(view: PathfinderDemoView) {} antialias(view: DemoView) {}
resolve(view: PathfinderDemoView) { resolve(view: DemoView) {
view.gl.bindFramebuffer(view.gl.FRAMEBUFFER, view.destFramebuffer); view.gl.bindFramebuffer(view.gl.FRAMEBUFFER, view.destFramebuffer);
view.gl.viewport(0, 0, view.destAllocatedSize[0], view.destAllocatedSize[1]); view.gl.viewport(0, 0, view.destAllocatedSize[0], view.destAllocatedSize[1]);
view.gl.disable(view.gl.DEPTH_TEST); view.gl.disable(view.gl.DEPTH_TEST);
@ -123,7 +123,7 @@ export default class SSAAStrategy extends AntialiasingStrategy {
return glmatrix.vec2.clone([this.subpixelAA !== 'none' ? 3 : 2, this.level === 2 ? 1 : 2]); return glmatrix.vec2.clone([this.subpixelAA !== 'none' ? 3 : 2, this.level === 2 ? 1 : 2]);
} }
private usedSupersampledFramebufferSize(view: PathfinderDemoView): glmatrix.vec2 { private usedSupersampledFramebufferSize(view: DemoView): glmatrix.vec2 {
const result = glmatrix.vec2.create(); const result = glmatrix.vec2.create();
glmatrix.vec2.mul(result, view.destUsedSize, this.supersampleScale); glmatrix.vec2.mul(result, view.destUsedSize, this.supersampleScale);
return result; return result;

View File

@ -22,7 +22,7 @@ import {ShaderMap, ShaderProgramSource} from './shader-loader';
import SSAAStrategy from "./ssaa-strategy"; import SSAAStrategy from "./ssaa-strategy";
import {BUILTIN_SVG_URI, SVGLoader} from './svg-loader'; import {BUILTIN_SVG_URI, SVGLoader} from './svg-loader';
import {panic, unwrapNull} from './utils'; import {panic, unwrapNull} from './utils';
import {PathfinderDemoView, Timings} from './view'; import {DemoView, Timings} from './view';
const parseColor = require('parse-color'); const parseColor = require('parse-color');
@ -87,7 +87,7 @@ class SVGDemoController extends DemoAppController<SVGDemoView> {
} }
} }
class SVGDemoView extends PathfinderDemoView { class SVGDemoView extends DemoView {
camera: OrthographicCamera; camera: OrthographicCamera;
protected depthFunction: number = this.gl.GREATER; protected depthFunction: number = this.gl.GREATER;

View File

@ -30,7 +30,7 @@ import {calculatePixelDescent, calculatePixelRectForGlyph, PathfinderFont} from
import {BUILTIN_FONT_URI, calculatePixelXMin, GlyphStore, Hint, SimpleTextLayout} from "./text"; import {BUILTIN_FONT_URI, calculatePixelXMin, GlyphStore, Hint, SimpleTextLayout} from "./text";
import {assert, expectNotNull, panic, PathfinderError, scaleRect, UINT32_SIZE} from './utils'; import {assert, expectNotNull, panic, PathfinderError, scaleRect, UINT32_SIZE} from './utils';
import {unwrapNull} from './utils'; import {unwrapNull} from './utils';
import {MonochromePathfinderView, Timings, TIMINGS} from './view'; import {MonochromeDemoView, Timings, TIMINGS} from './view';
const DEFAULT_TEXT: string = const DEFAULT_TEXT: string =
`Twas brillig, and the slithy toves `Twas brillig, and the slithy toves
@ -268,7 +268,7 @@ class TextDemoController extends DemoAppController<TextDemoView> {
} }
} }
class TextDemoView extends MonochromePathfinderView { class TextDemoView extends MonochromeDemoView {
atlasFramebuffer: WebGLFramebuffer; atlasFramebuffer: WebGLFramebuffer;
atlasDepthTexture: WebGLTexture; atlasDepthTexture: WebGLTexture;

View File

@ -8,9 +8,13 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
// FIXME(pcwalton): This is turning into a fragile inheritance hierarchy. See if we can refactor to
// use composition more.
import * as glmatrix from 'gl-matrix'; import * as glmatrix from 'gl-matrix';
import { AntialiasingStrategy, AntialiasingStrategyName, NoAAStrategy, SubpixelAAType } from "./aa-strategy"; import {AntialiasingStrategy, AntialiasingStrategyName, NoAAStrategy} from "./aa-strategy";
import {SubpixelAAType} from "./aa-strategy";
import PathfinderBufferTexture from './buffer-texture'; import PathfinderBufferTexture from './buffer-texture';
import {Camera} from "./camera"; import {Camera} from "./camera";
import {QUAD_ELEMENTS, UniformMap} from './gl-utils'; import {QUAD_ELEMENTS, UniformMap} from './gl-utils';
@ -19,6 +23,8 @@ import {PathfinderShaderProgram, SHADER_NAMES, ShaderMap} from './shader-loader'
import {ShaderProgramSource, UnlinkedShaderProgram} from './shader-loader'; import {ShaderProgramSource, UnlinkedShaderProgram} from './shader-loader';
import {expectNotNull, PathfinderError, UINT32_SIZE, unwrapNull} from './utils'; import {expectNotNull, PathfinderError, UINT32_SIZE, unwrapNull} from './utils';
const MAX_PATHS: number = 65535;
const TIME_INTERVAL_DELAY: number = 32; const TIME_INTERVAL_DELAY: number = 32;
const B_LOOP_BLINN_DATA_SIZE: number = 4; const B_LOOP_BLINN_DATA_SIZE: number = 4;
@ -115,7 +121,7 @@ export abstract class PathfinderView {
} }
} }
export abstract class PathfinderDemoView extends PathfinderView { export abstract class DemoView extends PathfinderView {
gl: WebGLRenderingContext; gl: WebGLRenderingContext;
shaderPrograms: ShaderMap<PathfinderShaderProgram>; shaderPrograms: ShaderMap<PathfinderShaderProgram>;
@ -143,6 +149,12 @@ export abstract class PathfinderDemoView extends PathfinderView {
protected lastTimings: Timings; protected lastTimings: Timings;
protected get pathIDsAreInstanced(): boolean {
return false;
}
private instancedPathIDVBO: WebGLBuffer | null;
private atlasRenderingTimerQuery: WebGLQuery; private atlasRenderingTimerQuery: WebGLQuery;
private compositingTimerQuery: WebGLQuery; private compositingTimerQuery: WebGLQuery;
private timerQueryPollInterval: number | null; private timerQueryPollInterval: number | null;
@ -162,6 +174,9 @@ export abstract class PathfinderDemoView extends PathfinderView {
this.pathTransformBufferTextures = []; this.pathTransformBufferTextures = [];
this.pathColorsBufferTextures = []; this.pathColorsBufferTextures = [];
if (this.pathIDsAreInstanced)
this.initInstancedPathIDVBO();
this.wantsScreenshot = false; this.wantsScreenshot = false;
this.antialiasingStrategy = new NoAAStrategy(0, 'none'); this.antialiasingStrategy = new NoAAStrategy(0, 'none');
@ -390,6 +405,10 @@ export abstract class PathfinderDemoView extends PathfinderView {
protected abstract compositeIfNecessary(): void; protected abstract compositeIfNecessary(): void;
protected meshInstanceCountForObject(objectIndex: number): number {
return 1;
}
private compileShaders(commonSource: string, shaderSources: ShaderMap<ShaderProgramSource>): private compileShaders(commonSource: string, shaderSources: ShaderMap<ShaderProgramSource>):
ShaderMap<UnlinkedShaderProgram> { ShaderMap<UnlinkedShaderProgram> {
const shaders: Partial<ShaderMap<Partial<UnlinkedShaderProgram>>> = {}; const shaders: Partial<ShaderMap<Partial<UnlinkedShaderProgram>>> = {};
@ -434,6 +453,17 @@ export abstract class PathfinderDemoView extends PathfinderView {
return shaderProgramMap as ShaderMap<PathfinderShaderProgram>; return shaderProgramMap as ShaderMap<PathfinderShaderProgram>;
} }
private initInstancedPathIDVBO(): void {
const pathIDs = new Uint16Array(MAX_PATHS);
for (let pathIndex = 0; pathIndex < MAX_PATHS; pathIndex++)
pathIDs[pathIndex] = pathIndex + 1;
this.instancedPathIDVBO = this.gl.createBuffer();
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.instancedPathIDVBO);
this.gl.bufferData(this.gl.ARRAY_BUFFER, pathIDs, this.gl.STATIC_DRAW);
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, null);
}
private setTransformUniform(uniforms: UniformMap, objectIndex: number) { private setTransformUniform(uniforms: UniformMap, objectIndex: number) {
const transform = glmatrix.mat4.clone(this.worldTransform); const transform = glmatrix.mat4.clone(this.worldTransform);
glmatrix.mat4.mul(transform, transform, this.getModelviewTransform(objectIndex)); glmatrix.mat4.mul(transform, transform, this.getModelviewTransform(objectIndex));
@ -447,6 +477,12 @@ export abstract class PathfinderDemoView extends PathfinderView {
const meshes = this.meshes[objectIndex]; const meshes = this.meshes[objectIndex];
let instanceCount: number | null;
if (!this.pathIDsAreInstanced)
instanceCount = null;
else
instanceCount = this.meshInstanceCountForObject(objectIndex);
// Set up implicit cover state. // Set up implicit cover state.
this.gl.depthFunc(this.depthFunction); this.gl.depthFunc(this.depthFunction);
this.gl.depthMask(true); this.gl.depthMask(true);
@ -454,25 +490,12 @@ export abstract class PathfinderDemoView extends PathfinderView {
this.gl.disable(this.gl.BLEND); this.gl.disable(this.gl.BLEND);
// Set up the implicit cover interior VAO. // Set up the implicit cover interior VAO.
//
// TODO(pcwalton): Cache these.
const directInteriorProgram = this.shaderPrograms[this.directInteriorProgramName]; const directInteriorProgram = this.shaderPrograms[this.directInteriorProgramName];
this.gl.useProgram(directInteriorProgram.program); const implicitCoverInteriorVAO = this.vertexArrayObjectExt.createVertexArrayOES();
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, meshes.bVertexPositions); this.vertexArrayObjectExt.bindVertexArrayOES(implicitCoverInteriorVAO);
this.gl.vertexAttribPointer(directInteriorProgram.attributes.aPosition, this.initImplicitCoverInteriorVAO(objectIndex);
2,
this.gl.FLOAT,
false,
0,
0);
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, meshes.bVertexPathIDs);
this.gl.vertexAttribPointer(directInteriorProgram.attributes.aPathID,
1,
this.gl.UNSIGNED_SHORT,
false,
0,
0);
this.gl.enableVertexAttribArray(directInteriorProgram.attributes.aPosition);
this.gl.enableVertexAttribArray(directInteriorProgram.attributes.aPathID);
this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, meshes.coverInteriorIndices);
// Draw direct interior parts. // Draw direct interior parts.
this.setTransformUniform(directInteriorProgram.uniforms, objectIndex); this.setTransformUniform(directInteriorProgram.uniforms, objectIndex);
@ -487,7 +510,15 @@ export abstract class PathfinderDemoView extends PathfinderView {
this.pathHintsBufferTexture.bind(this.gl, directInteriorProgram.uniforms, 2); this.pathHintsBufferTexture.bind(this.gl, directInteriorProgram.uniforms, 2);
let indexCount = this.gl.getBufferParameter(this.gl.ELEMENT_ARRAY_BUFFER, let indexCount = this.gl.getBufferParameter(this.gl.ELEMENT_ARRAY_BUFFER,
this.gl.BUFFER_SIZE) / UINT32_SIZE; this.gl.BUFFER_SIZE) / UINT32_SIZE;
this.gl.drawElements(this.gl.TRIANGLES, indexCount, this.gl.UNSIGNED_INT, 0); if (instanceCount == null) {
this.gl.drawElements(this.gl.TRIANGLES, indexCount, this.gl.UNSIGNED_INT, 0);
} else {
this.instancedArraysExt.drawElementsInstancedANGLE(this.gl.TRIANGLES,
indexCount,
this.gl.UNSIGNED_INT,
0,
instanceCount);
}
// Set up direct curve state. // Set up direct curve state.
this.gl.depthMask(false); this.gl.depthMask(false);
@ -497,40 +528,12 @@ export abstract class PathfinderDemoView extends PathfinderView {
this.gl.ONE, this.gl.ONE); this.gl.ONE, this.gl.ONE);
// Set up the direct curve VAO. // Set up the direct curve VAO.
//
// TODO(pcwalton): Cache these.
const directCurveProgram = this.shaderPrograms[this.directCurveProgramName]; const directCurveProgram = this.shaderPrograms[this.directCurveProgramName];
this.gl.useProgram(directCurveProgram.program); const implicitCoverCurveVAO = this.vertexArrayObjectExt.createVertexArrayOES();
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, meshes.bVertexPositions); this.vertexArrayObjectExt.bindVertexArrayOES(implicitCoverCurveVAO);
this.gl.vertexAttribPointer(directCurveProgram.attributes.aPosition, this.initImplicitCoverCurveVAO(objectIndex);
2,
this.gl.FLOAT,
false,
0,
0);
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, meshes.bVertexPathIDs);
this.gl.vertexAttribPointer(directCurveProgram.attributes.aPathID,
1,
this.gl.UNSIGNED_SHORT,
false,
0,
0);
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, meshes.bVertexLoopBlinnData);
this.gl.vertexAttribPointer(directCurveProgram.attributes.aTexCoord,
2,
this.gl.UNSIGNED_BYTE,
false,
B_LOOP_BLINN_DATA_SIZE,
B_LOOP_BLINN_DATA_TEX_COORD_OFFSET);
this.gl.vertexAttribPointer(directCurveProgram.attributes.aSign,
1,
this.gl.BYTE,
false,
B_LOOP_BLINN_DATA_SIZE,
B_LOOP_BLINN_DATA_SIGN_OFFSET);
this.gl.enableVertexAttribArray(directCurveProgram.attributes.aPosition);
this.gl.enableVertexAttribArray(directCurveProgram.attributes.aTexCoord);
this.gl.enableVertexAttribArray(directCurveProgram.attributes.aPathID);
this.gl.enableVertexAttribArray(directCurveProgram.attributes.aSign);
this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, meshes.coverCurveIndices);
// Draw direct curve parts. // Draw direct curve parts.
this.setTransformUniform(directCurveProgram.uniforms, objectIndex); this.setTransformUniform(directCurveProgram.uniforms, objectIndex);
@ -545,10 +548,101 @@ export abstract class PathfinderDemoView extends PathfinderView {
this.pathHintsBufferTexture.bind(this.gl, directCurveProgram.uniforms, 2); this.pathHintsBufferTexture.bind(this.gl, directCurveProgram.uniforms, 2);
indexCount = this.gl.getBufferParameter(this.gl.ELEMENT_ARRAY_BUFFER, indexCount = this.gl.getBufferParameter(this.gl.ELEMENT_ARRAY_BUFFER,
this.gl.BUFFER_SIZE) / UINT32_SIZE; this.gl.BUFFER_SIZE) / UINT32_SIZE;
this.gl.drawElements(this.gl.TRIANGLES, indexCount, this.gl.UNSIGNED_INT, 0); if (instanceCount == null) {
this.gl.drawElements(this.gl.TRIANGLES, indexCount, this.gl.UNSIGNED_INT, 0);
} else {
this.instancedArraysExt.drawElementsInstancedANGLE(this.gl.TRIANGLES,
indexCount,
this.gl.UNSIGNED_INT,
0,
instanceCount);
}
this.vertexArrayObjectExt.bindVertexArrayOES(null);
} }
} }
private initImplicitCoverInteriorVAO(objectIndex: number): void {
const meshes = this.meshes[objectIndex];
const directInteriorProgram = this.shaderPrograms[this.directInteriorProgramName];
this.gl.useProgram(directInteriorProgram.program);
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, meshes.bVertexPositions);
this.gl.vertexAttribPointer(directInteriorProgram.attributes.aPosition,
2,
this.gl.FLOAT,
false,
0,
0);
if (this.pathIDsAreInstanced)
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.instancedPathIDVBO);
else
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, meshes.bVertexPathIDs);
this.gl.vertexAttribPointer(directInteriorProgram.attributes.aPathID,
1,
this.gl.UNSIGNED_SHORT,
false,
0,
0);
if (this.pathIDsAreInstanced) {
this.instancedArraysExt
.vertexAttribDivisorANGLE(directInteriorProgram.attributes.aPathID, 1);
}
this.gl.enableVertexAttribArray(directInteriorProgram.attributes.aPosition);
this.gl.enableVertexAttribArray(directInteriorProgram.attributes.aPathID);
this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, meshes.coverInteriorIndices);
}
private initImplicitCoverCurveVAO(objectIndex: number): void {
const meshes = this.meshes[objectIndex];
const directCurveProgram = this.shaderPrograms[this.directCurveProgramName];
this.gl.useProgram(directCurveProgram.program);
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, meshes.bVertexPositions);
this.gl.vertexAttribPointer(directCurveProgram.attributes.aPosition,
2,
this.gl.FLOAT,
false,
0,
0);
if (this.pathIDsAreInstanced)
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.instancedPathIDVBO);
else
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, meshes.bVertexPathIDs);
this.gl.vertexAttribPointer(directCurveProgram.attributes.aPathID,
1,
this.gl.UNSIGNED_SHORT,
false,
0,
0);
if (this.pathIDsAreInstanced) {
this.instancedArraysExt
.vertexAttribDivisorANGLE(directCurveProgram.attributes.aPathID, 1);
}
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, meshes.bVertexLoopBlinnData);
this.gl.vertexAttribPointer(directCurveProgram.attributes.aTexCoord,
2,
this.gl.UNSIGNED_BYTE,
false,
B_LOOP_BLINN_DATA_SIZE,
B_LOOP_BLINN_DATA_TEX_COORD_OFFSET);
this.gl.vertexAttribPointer(directCurveProgram.attributes.aSign,
1,
this.gl.BYTE,
false,
B_LOOP_BLINN_DATA_SIZE,
B_LOOP_BLINN_DATA_SIGN_OFFSET);
this.gl.enableVertexAttribArray(directCurveProgram.attributes.aPosition);
this.gl.enableVertexAttribArray(directCurveProgram.attributes.aTexCoord);
this.gl.enableVertexAttribArray(directCurveProgram.attributes.aPathID);
this.gl.enableVertexAttribArray(directCurveProgram.attributes.aSign);
this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, meshes.coverCurveIndices);
}
private finishTiming() { private finishTiming() {
if (this.timerQueryPollInterval != null) if (this.timerQueryPollInterval != null)
return; return;
@ -613,7 +707,7 @@ export abstract class PathfinderDemoView extends PathfinderView {
protected abstract get directInteriorProgramName(): keyof ShaderMap<void>; protected abstract get directInteriorProgramName(): keyof ShaderMap<void>;
} }
export abstract class MonochromePathfinderView extends PathfinderDemoView { export abstract class MonochromeDemoView extends DemoView {
abstract get bgColor(): glmatrix.vec4; abstract get bgColor(): glmatrix.vec4;
abstract get fgColor(): glmatrix.vec4; abstract get fgColor(): glmatrix.vec4;
} }