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 {GlyphStore, Hint, PathfinderFont, TextFrame, TextRun} from "./text";
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_PADDING: number = 2000;
@ -94,12 +94,19 @@ interface MonumentSide {
lines: TextLine[];
}
interface MeshDescriptor {
glyphID: number;
textFrameIndex: number;
positions: glmatrix.vec2[];
}
class ThreeDController extends DemoAppController<ThreeDView> {
textFrames: TextFrame[];
glyphStore: GlyphStore;
meshDescriptors: MeshDescriptor[];
private baseMeshes: PathfinderMeshData;
private expandedMeshes: ExpandedMeshData[];
private expandedMeshes: PathfinderMeshData[];
private monumentPromise: Promise<MonumentSide[]>;
@ -196,20 +203,64 @@ class ThreeDController extends DemoAppController<ThreeDView> {
this.glyphStore = new GlyphStore(font, glyphsNeeded);
this.glyphStore.partition().then(result => {
const hint = new Hint(this.glyphStore.font, PIXELS_PER_UNIT, false);
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 => {
view.uploadPathColors(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;
camera: PerspectiveCamera;
@ -221,6 +272,10 @@ class ThreeDView extends PathfinderDemoView {
protected depthFunction: number = this.gl.LESS;
protected get pathIDsAreInstanced(): boolean {
return true;
}
private _scale: number;
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);
}
protected pathColorsForObject(textFrameIndex: number): Uint8Array {
const textFrame = this.appController.textFrames[textFrameIndex];
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 pathColorsForObject(objectIndex: number): Uint8Array {
return TEXT_COLOR;
}
protected pathTransformsForObject(textFrameIndex: number): Float32Array {
const textFrame = this.appController.textFrames[textFrameIndex];
const pathCount = textFrame.totalGlyphCount;
const hint = new Hint(this.appController.glyphStore.font, PIXELS_PER_UNIT, false);
protected pathTransformsForObject(objectIndex: number): Float32Array {
const meshDescriptor = this.appController.meshDescriptors[objectIndex];
const pathCount = meshDescriptor.positions.length;
const pathTransforms = new Float32Array(4 * (pathCount + 1));
let globalPathIndex = 0;
for (const run of textFrame.runs) {
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);
}
for (let pathIndex = 0; pathIndex < pathCount; pathIndex++) {
const glyphOrigin = meshDescriptor.positions[pathIndex];
pathTransforms.set([1, 1, glyphOrigin[0], glyphOrigin[1]], (pathIndex + 1) * 4);
}
return pathTransforms;
}
@ -343,17 +378,19 @@ class ThreeDView extends PathfinderDemoView {
}
protected getModelviewTransform(objectIndex: number): glmatrix.mat4 {
const textFrameIndex = this.appController.meshDescriptors[objectIndex].textFrameIndex;
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);
return transform;
}
// Cheap but effective backface culling.
protected shouldRenderObject(objectIndex: number): boolean {
const textFrameIndex = this.appController.meshDescriptors[objectIndex].textFrameIndex;
const translation = this.camera.translation;
const extent = TEXT_TRANSLATION[2] * TEXT_SCALE[2];
switch (objectIndex) {
switch (textFrameIndex) {
case 0: return translation[2] < -extent;
case 1: return translation[0] < -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 {
return glmatrix.vec2.fromValues(this.canvas.width, this.canvas.height);
}

View File

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

View File

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

View File

@ -22,7 +22,7 @@ import {ShaderMap, ShaderProgramSource} from './shader-loader';
import SSAAStrategy from "./ssaa-strategy";
import {BUILTIN_SVG_URI, SVGLoader} from './svg-loader';
import {panic, unwrapNull} from './utils';
import {PathfinderDemoView, Timings} from './view';
import {DemoView, Timings} from './view';
const parseColor = require('parse-color');
@ -87,7 +87,7 @@ class SVGDemoController extends DemoAppController<SVGDemoView> {
}
}
class SVGDemoView extends PathfinderDemoView {
class SVGDemoView extends DemoView {
camera: OrthographicCamera;
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 {assert, expectNotNull, panic, PathfinderError, scaleRect, UINT32_SIZE} from './utils';
import {unwrapNull} from './utils';
import {MonochromePathfinderView, Timings, TIMINGS} from './view';
import {MonochromeDemoView, Timings, TIMINGS} from './view';
const DEFAULT_TEXT: string =
`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;
atlasDepthTexture: WebGLTexture;

View File

@ -8,9 +8,13 @@
// option. This file may not be copied, modified, or distributed
// 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 { 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 {Camera} from "./camera";
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 {expectNotNull, PathfinderError, UINT32_SIZE, unwrapNull} from './utils';
const MAX_PATHS: number = 65535;
const TIME_INTERVAL_DELAY: number = 32;
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;
shaderPrograms: ShaderMap<PathfinderShaderProgram>;
@ -143,6 +149,12 @@ export abstract class PathfinderDemoView extends PathfinderView {
protected lastTimings: Timings;
protected get pathIDsAreInstanced(): boolean {
return false;
}
private instancedPathIDVBO: WebGLBuffer | null;
private atlasRenderingTimerQuery: WebGLQuery;
private compositingTimerQuery: WebGLQuery;
private timerQueryPollInterval: number | null;
@ -162,6 +174,9 @@ export abstract class PathfinderDemoView extends PathfinderView {
this.pathTransformBufferTextures = [];
this.pathColorsBufferTextures = [];
if (this.pathIDsAreInstanced)
this.initInstancedPathIDVBO();
this.wantsScreenshot = false;
this.antialiasingStrategy = new NoAAStrategy(0, 'none');
@ -390,6 +405,10 @@ export abstract class PathfinderDemoView extends PathfinderView {
protected abstract compositeIfNecessary(): void;
protected meshInstanceCountForObject(objectIndex: number): number {
return 1;
}
private compileShaders(commonSource: string, shaderSources: ShaderMap<ShaderProgramSource>):
ShaderMap<UnlinkedShaderProgram> {
const shaders: Partial<ShaderMap<Partial<UnlinkedShaderProgram>>> = {};
@ -434,6 +453,17 @@ export abstract class PathfinderDemoView extends PathfinderView {
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) {
const transform = glmatrix.mat4.clone(this.worldTransform);
glmatrix.mat4.mul(transform, transform, this.getModelviewTransform(objectIndex));
@ -447,6 +477,12 @@ export abstract class PathfinderDemoView extends PathfinderView {
const meshes = this.meshes[objectIndex];
let instanceCount: number | null;
if (!this.pathIDsAreInstanced)
instanceCount = null;
else
instanceCount = this.meshInstanceCountForObject(objectIndex);
// Set up implicit cover state.
this.gl.depthFunc(this.depthFunction);
this.gl.depthMask(true);
@ -454,25 +490,12 @@ export abstract class PathfinderDemoView extends PathfinderView {
this.gl.disable(this.gl.BLEND);
// Set up the implicit cover interior VAO.
//
// TODO(pcwalton): Cache these.
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);
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);
const implicitCoverInteriorVAO = this.vertexArrayObjectExt.createVertexArrayOES();
this.vertexArrayObjectExt.bindVertexArrayOES(implicitCoverInteriorVAO);
this.initImplicitCoverInteriorVAO(objectIndex);
// Draw direct interior parts.
this.setTransformUniform(directInteriorProgram.uniforms, objectIndex);
@ -487,7 +510,15 @@ export abstract class PathfinderDemoView extends PathfinderView {
this.pathHintsBufferTexture.bind(this.gl, directInteriorProgram.uniforms, 2);
let indexCount = this.gl.getBufferParameter(this.gl.ELEMENT_ARRAY_BUFFER,
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.
this.gl.depthMask(false);
@ -497,40 +528,12 @@ export abstract class PathfinderDemoView extends PathfinderView {
this.gl.ONE, this.gl.ONE);
// Set up the direct curve VAO.
//
// TODO(pcwalton): Cache these.
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);
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);
const implicitCoverCurveVAO = this.vertexArrayObjectExt.createVertexArrayOES();
this.vertexArrayObjectExt.bindVertexArrayOES(implicitCoverCurveVAO);
this.initImplicitCoverCurveVAO(objectIndex);
// Draw direct curve parts.
this.setTransformUniform(directCurveProgram.uniforms, objectIndex);
@ -545,10 +548,101 @@ export abstract class PathfinderDemoView extends PathfinderView {
this.pathHintsBufferTexture.bind(this.gl, directCurveProgram.uniforms, 2);
indexCount = this.gl.getBufferParameter(this.gl.ELEMENT_ARRAY_BUFFER,
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() {
if (this.timerQueryPollInterval != null)
return;
@ -613,7 +707,7 @@ export abstract class PathfinderDemoView extends PathfinderView {
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 fgColor(): glmatrix.vec4;
}