Get a bare-minimum benchmark up and running

This commit is contained in:
Patrick Walton 2017-09-12 12:40:14 -07:00
parent a7d75f913c
commit 2c8c11b303
7 changed files with 347 additions and 111 deletions

View File

@ -8,6 +8,7 @@
</head> </head>
<body> <body>
{{>partials/navbar.html isTool=true}} {{>partials/navbar.html isTool=true}}
<canvas id="pf-canvas" class="pf-draggable" width="400" height="300"></canvas>
<div class="fixed-bottom mb-3 d-flex justify-content-end align-items-end pf-pointer-events-none"> <div class="fixed-bottom mb-3 d-flex justify-content-end align-items-end pf-pointer-events-none">
<div id="pf-toolbar"> <div id="pf-toolbar">
<button id="pf-run-benchmark-button" type="button" <button id="pf-run-benchmark-button" type="button"

View File

@ -168,7 +168,8 @@ class ThreeDController extends DemoAppController<ThreeDView> {
this.baseMeshes = baseMeshes; this.baseMeshes = baseMeshes;
this.expandedMeshes = this.glyphStorage.expandMeshes(baseMeshes); this.expandedMeshes = this.glyphStorage.expandMeshes(baseMeshes);
this.view.then(view => { this.view.then(view => {
view.uploadPathMetadata(); view.uploadPathColors(this.expandedMeshes.length);
view.uploadPathTransforms(this.expandedMeshes.length);
view.attachMeshes(this.expandedMeshes.map(meshes => meshes.meshes)); view.attachMeshes(this.expandedMeshes.map(meshes => meshes.meshes));
}); });
}); });
@ -216,41 +217,34 @@ 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);
} }
uploadPathMetadata() { protected pathColorsForObject(textFrameIndex: number): Uint8Array {
this.pathColorsBufferTextures = []; const textFrame = this.appController.glyphStorage.textFrames[textFrameIndex];
this.pathTransformBufferTextures = []; const textGlyphs = textFrame.allGlyphs;
const pathCount = textGlyphs.length;
const textFrameCount = this.appController.glyphStorage.textFrames.length; const pathColors = new Uint8Array(4 * (pathCount + 1));
for (let textFrameIndex = 0; for (let pathIndex = 0; pathIndex < pathCount; pathIndex++)
textFrameIndex < textFrameCount; pathColors.set(TEXT_COLOR, (pathIndex + 1) * 4);
textFrameIndex++) {
const textFrame = this.appController.glyphStorage.textFrames[textFrameIndex];
const textGlyphs = textFrame.allGlyphs;
const pathCount = textGlyphs.length;
const hint = new Hint(this.appController.glyphStorage.font, PIXELS_PER_UNIT, false); return pathColors;
}
const pathColors = new Uint8Array(4 * (pathCount + 1)); protected pathTransformsForObject(textFrameIndex: number): Float32Array {
const pathTransforms = new Float32Array(4 * (pathCount + 1)); const textFrame = this.appController.glyphStorage.textFrames[textFrameIndex];
const textGlyphs = textFrame.allGlyphs;
const pathCount = textGlyphs.length;
for (let pathIndex = 0; pathIndex < pathCount; pathIndex++) { const hint = new Hint(this.appController.glyphStorage.font, PIXELS_PER_UNIT, false);
const startOffset = (pathIndex + 1) * 4;
pathColors.set(TEXT_COLOR, startOffset); const pathTransforms = new Float32Array(4 * (pathCount + 1));
const textGlyph = textGlyphs[pathIndex]; for (let pathIndex = 0; pathIndex < pathCount; pathIndex++) {
const glyphOrigin = textGlyph.calculatePixelOrigin(hint, PIXELS_PER_UNIT); const textGlyph = textGlyphs[pathIndex];
pathTransforms.set([1, 1, glyphOrigin[0], glyphOrigin[1]], startOffset); const glyphOrigin = textGlyph.calculatePixelOrigin(hint, PIXELS_PER_UNIT);
} pathTransforms.set([1, 1, glyphOrigin[0], glyphOrigin[1]], (pathIndex + 1) * 4);
const pathColorsBufferTexture = new PathfinderBufferTexture(this.gl, 'uPathColors');
const pathTransformBufferTexture = new PathfinderBufferTexture(this.gl,
'uPathTransform');
pathColorsBufferTexture.upload(this.gl, pathColors);
pathTransformBufferTexture.upload(this.gl, pathTransforms);
this.pathColorsBufferTextures.push(pathColorsBufferTexture);
this.pathTransformBufferTextures.push(pathTransformBufferTexture);
} }
return pathTransforms;
} }
protected createAAStrategy(aaType: AntialiasingStrategyName, protected createAAStrategy(aaType: AntialiasingStrategyName,
@ -304,17 +298,13 @@ class ThreeDView extends PathfinderDemoView {
return glmatrix.vec2.fromValues(this.canvas.width, this.canvas.height); return glmatrix.vec2.fromValues(this.canvas.width, this.canvas.height);
} }
get destFramebuffer(): WebGLFramebuffer | null { destFramebuffer: WebGLFramebuffer | null = null;
return null;
}
get destUsedSize(): glmatrix.vec2 { get destUsedSize(): glmatrix.vec2 {
return this.destAllocatedSize; return this.destAllocatedSize;
} }
protected get usedSizeFactor(): glmatrix.vec2 { protected usedSizeFactor: glmatrix.vec2 = glmatrix.vec2.clone([1.0, 1.0]);
return glmatrix.vec2.fromValues(1.0, 1.0);
}
private calculateWorldTransform(modelviewTranslation: glmatrix.vec3, private calculateWorldTransform(modelviewTranslation: glmatrix.vec3,
modelviewScale: glmatrix.vec3): modelviewScale: glmatrix.vec3):
@ -355,13 +345,8 @@ class ThreeDView extends PathfinderDemoView {
return transform; return transform;
} }
protected get directCurveProgramName(): keyof ShaderMap<void> { protected directCurveProgramName: keyof ShaderMap<void> = 'direct3DCurve';
return 'direct3DCurve'; protected directInteriorProgramName: keyof ShaderMap<void> = 'direct3DInterior';
}
protected get directInteriorProgramName(): keyof ShaderMap<void> {
return 'direct3DInterior';
}
protected depthFunction: number = this.gl.LESS; protected depthFunction: number = this.gl.LESS;

View File

@ -58,22 +58,29 @@ export abstract class DemoAppController<View extends PathfinderDemoView> extends
start() { start() {
super.start(); super.start();
this.settingsCard = document.getElementById('pf-settings') as HTMLElement; const settingsCard = document.getElementById('pf-settings') as (HTMLElement | null);
this.settingsButton = document.getElementById('pf-settings-button') as HTMLButtonElement; const settingsButton = document.getElementById('pf-settings-button') as
this.settingsCloseButton = document.getElementById('pf-settings-close-button') as (HTMLButtonElement | null);
HTMLButtonElement; const settingsCloseButton = document.getElementById('pf-settings-close-button') as
(HTMLButtonElement | null);
this.settingsButton.addEventListener('click', event => { if (settingsButton != null) {
event.stopPropagation(); settingsButton.addEventListener('click', event => {
this.settingsCard.classList.toggle('pf-invisible'); event.stopPropagation();
}, false); unwrapNull(settingsCard).classList.toggle('pf-invisible');
this.settingsCloseButton.addEventListener('click', () => { }, false);
this.settingsCard.classList.add('pf-invisible'); }
}, false); if (settingsCloseButton != null) {
document.body.addEventListener('click', () => { settingsCloseButton.addEventListener('click', () => {
this.settingsCard.classList.add('pf-invisible'); unwrapNull(settingsCard).classList.add('pf-invisible');
}, false); }, false);
this.settingsCard.addEventListener('click', event => event.stopPropagation(), false); }
if (settingsCard != null) {
document.body.addEventListener('click', () => {
settingsCard.classList.add('pf-invisible');
}, false);
settingsCard.addEventListener('click', event => event.stopPropagation(), false);
}
const screenshotButton = document.getElementById('pf-screenshot-button') as const screenshotButton = document.getElementById('pf-screenshot-button') as
HTMLButtonElement | null; HTMLButtonElement | null;
@ -124,20 +131,31 @@ export abstract class DemoAppController<View extends PathfinderDemoView> extends
return this.createView(); return this.createView();
}); });
this.aaLevelSelect = document.getElementById('pf-aa-level-select') as HTMLSelectElement; this.aaLevelSelect = document.getElementById('pf-aa-level-select') as
(HTMLSelectElement | null);
if (this.aaLevelSelect != null)
this.aaLevelSelect.addEventListener('change', () => this.updateAALevel(), false);
this.subpixelAASwitch = this.subpixelAASwitch =
document.getElementById('pf-subpixel-aa') as HTMLInputElement | null; document.getElementById('pf-subpixel-aa') as HTMLInputElement | null;
this.aaLevelSelect.addEventListener('change', () => this.updateAALevel(), false);
if (this.subpixelAASwitch != null) if (this.subpixelAASwitch != null)
this.subpixelAASwitch.addEventListener('change', () => this.updateAALevel(), false); this.subpixelAASwitch.addEventListener('change', () => this.updateAALevel(), false);
this.updateAALevel(); this.updateAALevel();
} }
private updateAALevel() { private updateAALevel() {
const selectedOption = this.aaLevelSelect.selectedOptions[0]; let aaType: AntialiasingStrategyName, aaLevel: number;
const aaValues = unwrapNull(/^([a-z-]+)(?:-([0-9]+))?$/.exec(selectedOption.value)); if (this.aaLevelSelect != null) {
const aaType = aaValues[1] as AntialiasingStrategyName; const selectedOption = this.aaLevelSelect.selectedOptions[0];
const aaLevel = aaValues[2] === "" ? 1 : parseInt(aaValues[2]); const aaValues = unwrapNull(/^([a-z-]+)(?:-([0-9]+))?$/.exec(selectedOption.value));
aaType = aaValues[1] as AntialiasingStrategyName;
aaLevel = aaValues[2] === "" ? 1 : parseInt(aaValues[2]);
} else {
aaType = 'none';
aaLevel = 0;
}
const subpixelAA = this.subpixelAASwitch == null ? false : this.subpixelAASwitch.checked; const subpixelAA = this.subpixelAASwitch == null ? false : this.subpixelAASwitch.checked;
this.view.then(view => view.setAntialiasingOptions(aaType, aaLevel, subpixelAA)); this.view.then(view => view.setAntialiasingOptions(aaType, aaLevel, subpixelAA));
} }
@ -187,10 +205,6 @@ export abstract class DemoAppController<View extends PathfinderDemoView> extends
protected commonShaderSource: string | null; protected commonShaderSource: string | null;
protected shaderSources: ShaderMap<ShaderProgramSource> | null; protected shaderSources: ShaderMap<ShaderProgramSource> | null;
private aaLevelSelect: HTMLSelectElement; private aaLevelSelect: HTMLSelectElement | null;
private subpixelAASwitch: HTMLInputElement | null; private subpixelAASwitch: HTMLInputElement | null;
private settingsCard: HTMLElement;
private settingsButton: HTMLButtonElement;
private settingsCloseButton: HTMLButtonElement;
} }

View File

@ -8,18 +8,44 @@
// 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.
import * as glmatrix from 'gl-matrix';
import * as opentype from "opentype.js"; import * as opentype from "opentype.js";
import {AppController} from "./app-controller"; import { AppController, DemoAppController } from "./app-controller";
import {PathfinderMeshData} from "./meshes"; import {PathfinderMeshData} from "./meshes";
import {BUILTIN_FONT_URI, GlyphStorage, PathfinderGlyph, TextFrame, TextRun} from "./text"; import { BUILTIN_FONT_URI, GlyphStorage, PathfinderGlyph, TextFrame, TextRun, ExpandedMeshData } from "./text";
import {assert, unwrapNull} from "./utils"; import { assert, unwrapNull, PathfinderError } from "./utils";
import { PathfinderDemoView, Timings } from "./view";
import { ShaderMap, ShaderProgramSource } from "./shader-loader";
import { AntialiasingStrategy, AntialiasingStrategyName, NoAAStrategy } from "./aa-strategy";
import SSAAStrategy from './ssaa-strategy';
import { OrthographicCamera } from './camera';
const STRING: string = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; const STRING: string = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
const FONT: string = 'nimbus-sans'; const FONT: string = 'nimbus-sans';
class BenchmarkAppController extends AppController { const TEXT_COLOR: number[] = [0, 0, 0, 255];
const MIN_FONT_SIZE: number = 6;
const MAX_FONT_SIZE: number = 200;
const ANTIALIASING_STRATEGIES: AntialiasingStrategyTable = {
none: NoAAStrategy,
ssaa: SSAAStrategy,
};
interface ElapsedTime {
size: number;
time: number;
}
interface AntialiasingStrategyTable {
none: typeof NoAAStrategy;
ssaa: typeof SSAAStrategy;
}
class BenchmarkAppController extends DemoAppController<BenchmarkTestView> {
start() { start() {
super.start(); super.start();
@ -31,29 +57,176 @@ class BenchmarkAppController extends AppController {
protected fileLoaded(): void { protected fileLoaded(): void {
const font = opentype.parse(this.fileData); const font = opentype.parse(this.fileData);
assert(font.isSupported(), "The font type is unsupported!"); this.font = font;
assert(this.font.isSupported(), "The font type is unsupported!");
const createGlyph = (glyph: opentype.Glyph) => new BenchmarkGlyph(glyph); const createGlyph = (glyph: opentype.Glyph) => new BenchmarkGlyph(glyph);
const textRun = new TextRun<BenchmarkGlyph>(STRING, [0, 0], font, createGlyph); const textRun = new TextRun<BenchmarkGlyph>(STRING, [0, 0], font, createGlyph);
this.textRun = textRun;
const textFrame = new TextFrame([textRun], font); const textFrame = new TextFrame([textRun], font);
this.glyphStorage = new GlyphStorage(this.fileData, [textFrame], createGlyph, font); this.glyphStorage = new GlyphStorage(this.fileData, [textFrame], createGlyph, font);
this.glyphStorage.partition().then(meshes => { this.glyphStorage.partition().then(baseMeshes => {
this.meshes = meshes; this.baseMeshes = baseMeshes;
// TODO(pcwalton) const expandedMeshes = this.glyphStorage.expandMeshes(baseMeshes)[0];
// this.renderer.attachMeshes(); this.expandedMeshes = expandedMeshes;
this.view.then(view => {
view.uploadPathColors(1);
view.uploadPathTransforms(1);
view.attachMeshes([expandedMeshes.meshes]);
})
}) })
} }
protected createView(): BenchmarkTestView {
return new BenchmarkTestView(this,
unwrapNull(this.commonShaderSource),
unwrapNull(this.shaderSources));
}
private runBenchmark(): void { private runBenchmark(): void {
// TODO(pcwalton) this.pixelsPerEm = MIN_FONT_SIZE;
this.elapsedTimes = [];
this.view.then(view => this.runOneBenchmarkTest(view));
}
private runOneBenchmarkTest(view: BenchmarkTestView): void {
const renderedPromise = new Promise<number>((resolve, reject) => {
view.renderingPromiseCallback = resolve;
view.pixelsPerEm = this.pixelsPerEm;
});
renderedPromise.then(elapsedTime => {
this.elapsedTimes.push({ size: this.pixelsPerEm, time: elapsedTime });
if (this.pixelsPerEm == MAX_FONT_SIZE) {
console.info(this.elapsedTimes);
return;
}
this.pixelsPerEm++;
this.runOneBenchmarkTest(view);
});
} }
protected readonly defaultFile: string = FONT; protected readonly defaultFile: string = FONT;
protected readonly builtinFileURI: string = BUILTIN_FONT_URI; protected readonly builtinFileURI: string = BUILTIN_FONT_URI;
private glyphStorage: GlyphStorage<BenchmarkGlyph>; private glyphStorage: GlyphStorage<BenchmarkGlyph>;
private meshes: PathfinderMeshData; private baseMeshes: PathfinderMeshData;
private expandedMeshes: ExpandedMeshData;
private pixelsPerEm: number;
private elapsedTimes: ElapsedTime[];
font: opentype.Font | null;
textRun: TextRun<BenchmarkGlyph> | null;
}
class BenchmarkTestView extends PathfinderDemoView {
constructor(appController: BenchmarkAppController,
commonShaderSource: string,
shaderSources: ShaderMap<ShaderProgramSource>) {
super(commonShaderSource, shaderSources);
this.appController = appController;
this.camera = new OrthographicCamera(this.canvas);
this.camera.onPan = () => this.setDirty();
this.camera.onZoom = () => this.setDirty();
}
protected createAAStrategy(aaType: AntialiasingStrategyName,
aaLevel: number,
subpixelAA: boolean):
AntialiasingStrategy {
if (aaType !== 'ecaa')
return new (ANTIALIASING_STRATEGIES[aaType])(aaLevel, subpixelAA);
throw new PathfinderError("Unsupported antialiasing type!");
}
protected compositeIfNecessary(): void {}
protected updateTimings(timings: Timings): void {
// TODO(pcwalton)
}
protected pathColorsForObject(objectIndex: number): Uint8Array {
const pathColors = new Uint8Array(4 * (STRING.length + 1));
for (let pathIndex = 0; pathIndex < STRING.length; pathIndex++)
pathColors.set(TEXT_COLOR, (pathIndex + 1) * 4);
return pathColors;
}
protected pathTransformsForObject(objectIndex: number): Float32Array {
const pathTransforms = new Float32Array(4 * (STRING.length + 1));
let currentX = 0;
for (let glyphIndex = 0; glyphIndex < STRING.length; glyphIndex++) {
const glyph = unwrapNull(this.appController.textRun).glyphs[glyphIndex];
pathTransforms.set([1, 1, currentX, 0], (glyphIndex + 1) * 4);
currentX += glyph.advanceWidth;
}
return pathTransforms;
}
protected renderingFinished(): void {
if (this.renderingPromiseCallback != null)
this.renderingPromiseCallback(this.lastTimings.atlasRendering);
}
destFramebuffer: WebGLFramebuffer | null = null;
get destAllocatedSize(): glmatrix.vec2 {
return glmatrix.vec2.clone([this.canvas.width, this.canvas.height]);
}
get destUsedSize(): glmatrix.vec2 {
return this.destAllocatedSize;
}
private readonly appController: BenchmarkAppController;
protected usedSizeFactor: glmatrix.vec2 = glmatrix.vec2.clone([1.0, 1.0]);
protected get worldTransform() {
const transform = glmatrix.mat4.create();
const translation = this.camera.translation;
glmatrix.mat4.fromTranslation(transform, [translation[0], translation[1], 0]);
glmatrix.mat4.scale(transform, transform, [this.camera.scale, this.camera.scale, 1.0]);
const pixelsPerUnit = this._pixelsPerEm / unwrapNull(this.appController.font).unitsPerEm;
glmatrix.mat4.scale(transform, transform, [pixelsPerUnit, pixelsPerUnit, 1.0]);
return transform;
}
get pixelsPerEm(): number {
return this._pixelsPerEm;
}
set pixelsPerEm(newPixelsPerEm: number) {
this._pixelsPerEm = newPixelsPerEm;
this.setDirty();
}
renderingPromiseCallback: ((time: number) => void) | null;
private _pixelsPerEm: number = 32.0;
protected directCurveProgramName: keyof ShaderMap<void> = 'directCurve';
protected directInteriorProgramName: keyof ShaderMap<void> = 'directInterior';
protected depthFunction: number = this.gl.GREATER;
protected camera: OrthographicCamera;
} }
class BenchmarkGlyph extends PathfinderGlyph {} class BenchmarkGlyph extends PathfinderGlyph {}
function main() {
const controller = new BenchmarkAppController;
window.addEventListener('load', () => controller.start(), false);
}
main();

View File

@ -190,7 +190,8 @@ class SVGDemoController extends DemoAppController<SVGDemoView> {
private meshesReceived(bounds: glmatrix.vec4): void { private meshesReceived(bounds: glmatrix.vec4): void {
this.view.then(view => { this.view.then(view => {
view.uploadPathMetadata(this.pathInstances); view.uploadPathColors(1);
view.uploadPathTransforms(1);
view.attachMeshes([this.meshes]); view.attachMeshes([this.meshes]);
view.camera.bounds = bounds; view.camera.bounds = bounds;
@ -198,8 +199,9 @@ class SVGDemoController extends DemoAppController<SVGDemoView> {
}) })
} }
pathInstances: PathInstance[];
private svg: SVGSVGElement; private svg: SVGSVGElement;
private pathInstances: PathInstance[];
private meshes: PathfinderMeshData; private meshes: PathfinderMeshData;
} }
@ -228,9 +230,10 @@ class SVGDemoView extends PathfinderDemoView {
return this.destAllocatedSize; return this.destAllocatedSize;
} }
uploadPathMetadata(instances: PathInstance[]) { protected pathColorsForObject(objectIndex: number): Uint8Array {
const instances = this.appController.pathInstances;
const pathColors = new Uint8Array(4 * (instances.length + 1)); const pathColors = new Uint8Array(4 * (instances.length + 1));
const pathTransforms = new Float32Array(4 * (instances.length + 1));
for (let pathIndex = 0; pathIndex < instances.length; pathIndex++) { for (let pathIndex = 0; pathIndex < instances.length; pathIndex++) {
const startOffset = (pathIndex + 1) * 4; const startOffset = (pathIndex + 1) * 4;
@ -241,17 +244,22 @@ class SVGDemoView extends PathfinderDemoView {
style[property] === 'none' ? [0, 0, 0, 0] : parseColor(style[property]).rgba; style[property] === 'none' ? [0, 0, 0, 0] : parseColor(style[property]).rgba;
pathColors.set(color.slice(0, 3), startOffset); pathColors.set(color.slice(0, 3), startOffset);
pathColors[startOffset + 3] = color[3] * 255; pathColors[startOffset + 3] = color[3] * 255;
}
return pathColors;
}
protected pathTransformsForObject(objectIndex: number): Float32Array {
const instances = this.appController.pathInstances;
const pathTransforms = new Float32Array(4 * (instances.length + 1));
for (let pathIndex = 0; pathIndex < instances.length; pathIndex++) {
// TODO(pcwalton): Set transform. // TODO(pcwalton): Set transform.
const startOffset = (pathIndex + 1) * 4;
pathTransforms.set([1, 1, 0, 0], startOffset); pathTransforms.set([1, 1, 0, 0], startOffset);
} }
const pathColorsBufferTexture = new PathfinderBufferTexture(this.gl, 'uPathColors'); return pathTransforms;
const pathTransformBufferTexture = new PathfinderBufferTexture(this.gl, 'uPathTransform');
pathColorsBufferTexture.upload(this.gl, pathColors);
pathTransformBufferTexture.upload(this.gl, pathTransforms);
this.pathColorsBufferTextures = [pathColorsBufferTexture];
this.pathTransformBufferTextures = [pathTransformBufferTexture];
} }
protected createAAStrategy(aaType: AntialiasingStrategyName, protected createAAStrategy(aaType: AntialiasingStrategyName,

View File

@ -178,7 +178,7 @@ class TextDemoController extends DemoAppController<TextDemoView> {
this.meshes = meshes; this.meshes = meshes;
this.view.then(view => { this.view.then(view => {
view.attachText(); view.attachText();
view.uploadPathMetadata(this.layout.glyphStorage.uniqueGlyphs.length); view.uploadPathColors(1);
view.attachMeshes([this.meshes]); view.attachMeshes([this.meshes]);
}); });
}); });
@ -262,17 +262,19 @@ class TextDemoView extends MonochromePathfinderView {
super.initContext(); super.initContext();
} }
uploadPathMetadata(pathCount: number) { protected pathColorsForObject(objectIndex: number): Uint8Array {
const atlasGlyphs = this.appController.atlasGlyphs;
const pathCount = atlasGlyphs.length;
const pathColors = new Uint8Array(4 * (pathCount + 1)); const pathColors = new Uint8Array(4 * (pathCount + 1));
for (let pathIndex = 0; pathIndex < pathCount; pathIndex++) { for (let pathIndex = 0; pathIndex < pathCount; pathIndex++) {
for (let channel = 0; channel < 3; channel++) for (let channel = 0; channel < 3; channel++)
pathColors[(pathIndex + 1) * 4 + channel] = 0x00; // RGB pathColors[(pathIndex + 1) * 4 + channel] = 0x00; // RGB
pathColors[(pathIndex + 1) * 4 + 3] = 0xff; // alpha pathColors[(pathIndex + 1) * 4 + 3] = 0xff; // alpha
} }
const pathColorsBufferTexture = new PathfinderBufferTexture(this.gl, 'uPathColors'); return pathColors;
pathColorsBufferTexture.upload(this.gl, pathColors);
this.pathColorsBufferTextures = [pathColorsBufferTexture];
} }
/// Lays out glyphs on the canvas. /// Lays out glyphs on the canvas.
@ -342,10 +344,37 @@ class TextDemoView extends MonochromePathfinderView {
const uniqueGlyphIndices = uniqueGlyphs.map(glyph => glyph.index); const uniqueGlyphIndices = uniqueGlyphs.map(glyph => glyph.index);
uniqueGlyphIndices.sort((a, b) => a - b); uniqueGlyphIndices.sort((a, b) => a - b);
this.uploadPathTransforms(1);
// TODO(pcwalton): Regenerate the IBOs to include only the glyphs we care about. // TODO(pcwalton): Regenerate the IBOs to include only the glyphs we care about.
const transforms = new Float32Array((uniqueGlyphs.length + 1) * 4);
const pathHints = new Float32Array((uniqueGlyphs.length + 1) * 4); const pathHints = new Float32Array((uniqueGlyphs.length + 1) * 4);
for (let glyphIndex = 0; glyphIndex < atlasGlyphs.length; glyphIndex++) {
const glyph = atlasGlyphs[glyphIndex];
let pathID = _.sortedIndexOf(uniqueGlyphIndices, glyph.index);
assert(pathID >= 0, "No path ID!");
pathID++;
pathHints[pathID * 4 + 0] = hint.xHeight;
pathHints[pathID * 4 + 1] = hint.hintedXHeight;
}
const pathHintsBufferTexture = new PathfinderBufferTexture(this.gl, 'uPathHints');
pathHintsBufferTexture.upload(this.gl, pathHints);
this.pathHintsBufferTexture = pathHintsBufferTexture;
}
protected pathTransformsForObject(objectIndex: number): Float32Array {
const atlasGlyphs = this.appController.atlasGlyphs;
const pixelsPerUnit = this.appController.pixelsPerUnit;
const uniqueGlyphs = this.appController.layout.glyphStorage.uniqueGlyphs;
const uniqueGlyphIndices = uniqueGlyphs.map(glyph => glyph.index);
uniqueGlyphIndices.sort((a, b) => a - b);
const transforms = new Float32Array((uniqueGlyphs.length + 1) * 4);
for (let glyphIndex = 0; glyphIndex < atlasGlyphs.length; glyphIndex++) { for (let glyphIndex = 0; glyphIndex < atlasGlyphs.length; glyphIndex++) {
const glyph = atlasGlyphs[glyphIndex]; const glyph = atlasGlyphs[glyphIndex];
@ -359,18 +388,9 @@ class TextDemoView extends MonochromePathfinderView {
transforms[pathID * 4 + 1] = pixelsPerUnit; transforms[pathID * 4 + 1] = pixelsPerUnit;
transforms[pathID * 4 + 2] = atlasOrigin[0]; transforms[pathID * 4 + 2] = atlasOrigin[0];
transforms[pathID * 4 + 3] = atlasOrigin[1]; transforms[pathID * 4 + 3] = atlasOrigin[1];
pathHints[pathID * 4 + 0] = hint.xHeight;
pathHints[pathID * 4 + 1] = hint.hintedXHeight;
} }
const pathTransformBufferTexture = new PathfinderBufferTexture(this.gl, 'uPathTransform'); return transforms;
pathTransformBufferTexture.upload(this.gl, transforms);
this.pathTransformBufferTextures = [pathTransformBufferTexture];
const pathHintsBufferTexture = new PathfinderBufferTexture(this.gl, 'uPathHints');
pathHintsBufferTexture.upload(this.gl, pathHints);
this.pathHintsBufferTexture = pathHintsBufferTexture;
} }
private createAtlasFramebuffer() { private createAtlasFramebuffer() {

View File

@ -109,6 +109,8 @@ export abstract class PathfinderDemoView extends PathfinderView {
this.initContext(); this.initContext();
this.lastTimings = { atlasRendering: 0, compositing: 0 };
const shaderSource = this.compileShaders(commonShaderSource, shaderSources); const shaderSource = this.compileShaders(commonShaderSource, shaderSources);
this.shaderPrograms = this.linkShaders(shaderSource); this.shaderPrograms = this.linkShaders(shaderSource);
@ -281,6 +283,9 @@ export abstract class PathfinderDemoView extends PathfinderView {
// Finish timing. // Finish timing.
this.finishTiming(); this.finishTiming();
// Invoke the post-render hook.
this.renderingFinished();
// Take a screenshot if desired. // Take a screenshot if desired.
if (this.wantsScreenshot) { if (this.wantsScreenshot) {
this.wantsScreenshot = false; this.wantsScreenshot = false;
@ -288,6 +293,8 @@ export abstract class PathfinderDemoView extends PathfinderView {
} }
} }
protected renderingFinished(): void {}
private setTransformUniform(uniforms: UniformMap, objectIndex: number) { private setTransformUniform(uniforms: UniformMap, objectIndex: number) {
const transform = glmatrix.mat4.create(); const transform = glmatrix.mat4.create();
if (this.antialiasingStrategy != null) if (this.antialiasingStrategy != null)
@ -423,10 +430,10 @@ export abstract class PathfinderDemoView extends PathfinderView {
const compositingTime = const compositingTime =
this.timerQueryExt.getQueryObjectEXT(this.compositingTimerQuery, this.timerQueryExt.getQueryObjectEXT(this.compositingTimerQuery,
this.timerQueryExt.QUERY_RESULT_EXT); this.timerQueryExt.QUERY_RESULT_EXT);
this.updateTimings({ this.lastTimings = {
atlasRendering: atlasRenderingTime / 1000000.0, atlasRendering: atlasRenderingTime / 1000000.0,
compositing: compositingTime / 1000000.0, compositing: compositingTime / 1000000.0,
}); };
window.clearInterval(this.timerQueryPollInterval!); window.clearInterval(this.timerQueryPollInterval!);
this.timerQueryPollInterval = null; this.timerQueryPollInterval = null;
@ -485,6 +492,34 @@ export abstract class PathfinderDemoView extends PathfinderView {
this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT); this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);
} }
uploadPathColors(objectCount: number) {
this.pathColorsBufferTextures = [];
for (let objectIndex = 0; objectIndex < objectCount; objectIndex++) {
const pathColorsBufferTexture = new PathfinderBufferTexture(this.gl, 'uPathColors');
const pathColors = this.pathColorsForObject(objectIndex);
pathColorsBufferTexture.upload(this.gl, pathColors);
this.pathColorsBufferTextures.push(pathColorsBufferTexture);
}
}
uploadPathTransforms(objectCount: number) {
this.pathTransformBufferTextures = [];
for (let objectIndex = 0; objectIndex < objectCount; objectIndex++) {
const pathTransformBufferTexture = new PathfinderBufferTexture(this.gl,
'uPathTransform');
const pathTransforms = this.pathTransformsForObject(objectIndex);
pathTransformBufferTexture.upload(this.gl, pathTransforms);
this.pathTransformBufferTextures.push(pathTransformBufferTexture);
}
}
protected abstract pathColorsForObject(objectIndex: number): Uint8Array;
protected abstract pathTransformsForObject(objectIndex: number): Float32Array;
protected abstract get depthFunction(): number; protected abstract get depthFunction(): number;
protected abstract createAAStrategy(aaType: AntialiasingStrategyName, protected abstract createAAStrategy(aaType: AntialiasingStrategyName,
@ -494,8 +529,6 @@ export abstract class PathfinderDemoView extends PathfinderView {
protected abstract compositeIfNecessary(): void; protected abstract compositeIfNecessary(): void;
protected abstract updateTimings(timings: Timings): void;
abstract get destFramebuffer(): WebGLFramebuffer | null; abstract get destFramebuffer(): WebGLFramebuffer | null;
abstract get destAllocatedSize(): glmatrix.vec2; abstract get destAllocatedSize(): glmatrix.vec2;
@ -536,6 +569,8 @@ export abstract class PathfinderDemoView extends PathfinderView {
private compositingTimerQuery: WebGLQuery; private compositingTimerQuery: WebGLQuery;
private timerQueryPollInterval: number | null; private timerQueryPollInterval: number | null;
protected lastTimings: Timings;
private wantsScreenshot: boolean; private wantsScreenshot: boolean;
} }