Get a bare-minimum benchmark up and running
This commit is contained in:
parent
a7d75f913c
commit
2c8c11b303
|
@ -8,6 +8,7 @@
|
|||
</head>
|
||||
<body>
|
||||
{{>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 id="pf-toolbar">
|
||||
<button id="pf-run-benchmark-button" type="button"
|
||||
|
|
|
@ -168,7 +168,8 @@ class ThreeDController extends DemoAppController<ThreeDView> {
|
|||
this.baseMeshes = baseMeshes;
|
||||
this.expandedMeshes = this.glyphStorage.expandMeshes(baseMeshes);
|
||||
this.view.then(view => {
|
||||
view.uploadPathMetadata();
|
||||
view.uploadPathColors(this.expandedMeshes.length);
|
||||
view.uploadPathTransforms(this.expandedMeshes.length);
|
||||
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);
|
||||
}
|
||||
|
||||
uploadPathMetadata() {
|
||||
this.pathColorsBufferTextures = [];
|
||||
this.pathTransformBufferTextures = [];
|
||||
protected pathColorsForObject(textFrameIndex: number): Uint8Array {
|
||||
const textFrame = this.appController.glyphStorage.textFrames[textFrameIndex];
|
||||
const textGlyphs = textFrame.allGlyphs;
|
||||
const pathCount = textGlyphs.length;
|
||||
|
||||
const textFrameCount = this.appController.glyphStorage.textFrames.length;
|
||||
for (let textFrameIndex = 0;
|
||||
textFrameIndex < textFrameCount;
|
||||
textFrameIndex++) {
|
||||
const textFrame = this.appController.glyphStorage.textFrames[textFrameIndex];
|
||||
const textGlyphs = textFrame.allGlyphs;
|
||||
const pathCount = textGlyphs.length;
|
||||
const pathColors = new Uint8Array(4 * (pathCount + 1));
|
||||
for (let pathIndex = 0; pathIndex < pathCount; pathIndex++)
|
||||
pathColors.set(TEXT_COLOR, (pathIndex + 1) * 4);
|
||||
|
||||
const hint = new Hint(this.appController.glyphStorage.font, PIXELS_PER_UNIT, false);
|
||||
return pathColors;
|
||||
}
|
||||
|
||||
const pathColors = new Uint8Array(4 * (pathCount + 1));
|
||||
const pathTransforms = new Float32Array(4 * (pathCount + 1));
|
||||
protected pathTransformsForObject(textFrameIndex: number): Float32Array {
|
||||
const textFrame = this.appController.glyphStorage.textFrames[textFrameIndex];
|
||||
const textGlyphs = textFrame.allGlyphs;
|
||||
const pathCount = textGlyphs.length;
|
||||
|
||||
for (let pathIndex = 0; pathIndex < pathCount; pathIndex++) {
|
||||
const startOffset = (pathIndex + 1) * 4;
|
||||
const hint = new Hint(this.appController.glyphStorage.font, PIXELS_PER_UNIT, false);
|
||||
|
||||
pathColors.set(TEXT_COLOR, startOffset);
|
||||
const pathTransforms = new Float32Array(4 * (pathCount + 1));
|
||||
|
||||
const textGlyph = textGlyphs[pathIndex];
|
||||
const glyphOrigin = textGlyph.calculatePixelOrigin(hint, PIXELS_PER_UNIT);
|
||||
pathTransforms.set([1, 1, glyphOrigin[0], glyphOrigin[1]], startOffset);
|
||||
}
|
||||
|
||||
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);
|
||||
for (let pathIndex = 0; pathIndex < pathCount; pathIndex++) {
|
||||
const textGlyph = textGlyphs[pathIndex];
|
||||
const glyphOrigin = textGlyph.calculatePixelOrigin(hint, PIXELS_PER_UNIT);
|
||||
pathTransforms.set([1, 1, glyphOrigin[0], glyphOrigin[1]], (pathIndex + 1) * 4);
|
||||
}
|
||||
|
||||
return pathTransforms;
|
||||
}
|
||||
|
||||
protected createAAStrategy(aaType: AntialiasingStrategyName,
|
||||
|
@ -304,17 +298,13 @@ class ThreeDView extends PathfinderDemoView {
|
|||
return glmatrix.vec2.fromValues(this.canvas.width, this.canvas.height);
|
||||
}
|
||||
|
||||
get destFramebuffer(): WebGLFramebuffer | null {
|
||||
return null;
|
||||
}
|
||||
destFramebuffer: WebGLFramebuffer | null = null;
|
||||
|
||||
get destUsedSize(): glmatrix.vec2 {
|
||||
return this.destAllocatedSize;
|
||||
}
|
||||
|
||||
protected get usedSizeFactor(): glmatrix.vec2 {
|
||||
return glmatrix.vec2.fromValues(1.0, 1.0);
|
||||
}
|
||||
protected usedSizeFactor: glmatrix.vec2 = glmatrix.vec2.clone([1.0, 1.0]);
|
||||
|
||||
private calculateWorldTransform(modelviewTranslation: glmatrix.vec3,
|
||||
modelviewScale: glmatrix.vec3):
|
||||
|
@ -355,13 +345,8 @@ class ThreeDView extends PathfinderDemoView {
|
|||
return transform;
|
||||
}
|
||||
|
||||
protected get directCurveProgramName(): keyof ShaderMap<void> {
|
||||
return 'direct3DCurve';
|
||||
}
|
||||
|
||||
protected get directInteriorProgramName(): keyof ShaderMap<void> {
|
||||
return 'direct3DInterior';
|
||||
}
|
||||
protected directCurveProgramName: keyof ShaderMap<void> = 'direct3DCurve';
|
||||
protected directInteriorProgramName: keyof ShaderMap<void> = 'direct3DInterior';
|
||||
|
||||
protected depthFunction: number = this.gl.LESS;
|
||||
|
||||
|
|
|
@ -58,22 +58,29 @@ export abstract class DemoAppController<View extends PathfinderDemoView> extends
|
|||
start() {
|
||||
super.start();
|
||||
|
||||
this.settingsCard = document.getElementById('pf-settings') as HTMLElement;
|
||||
this.settingsButton = document.getElementById('pf-settings-button') as HTMLButtonElement;
|
||||
this.settingsCloseButton = document.getElementById('pf-settings-close-button') as
|
||||
HTMLButtonElement;
|
||||
const settingsCard = document.getElementById('pf-settings') as (HTMLElement | null);
|
||||
const settingsButton = document.getElementById('pf-settings-button') as
|
||||
(HTMLButtonElement | null);
|
||||
const settingsCloseButton = document.getElementById('pf-settings-close-button') as
|
||||
(HTMLButtonElement | null);
|
||||
|
||||
this.settingsButton.addEventListener('click', event => {
|
||||
event.stopPropagation();
|
||||
this.settingsCard.classList.toggle('pf-invisible');
|
||||
}, false);
|
||||
this.settingsCloseButton.addEventListener('click', () => {
|
||||
this.settingsCard.classList.add('pf-invisible');
|
||||
}, false);
|
||||
document.body.addEventListener('click', () => {
|
||||
this.settingsCard.classList.add('pf-invisible');
|
||||
}, false);
|
||||
this.settingsCard.addEventListener('click', event => event.stopPropagation(), false);
|
||||
if (settingsButton != null) {
|
||||
settingsButton.addEventListener('click', event => {
|
||||
event.stopPropagation();
|
||||
unwrapNull(settingsCard).classList.toggle('pf-invisible');
|
||||
}, false);
|
||||
}
|
||||
if (settingsCloseButton != null) {
|
||||
settingsCloseButton.addEventListener('click', () => {
|
||||
unwrapNull(settingsCard).classList.add('pf-invisible');
|
||||
}, 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
|
||||
HTMLButtonElement | null;
|
||||
|
@ -124,20 +131,31 @@ export abstract class DemoAppController<View extends PathfinderDemoView> extends
|
|||
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 =
|
||||
document.getElementById('pf-subpixel-aa') as HTMLInputElement | null;
|
||||
this.aaLevelSelect.addEventListener('change', () => this.updateAALevel(), false);
|
||||
if (this.subpixelAASwitch != null)
|
||||
this.subpixelAASwitch.addEventListener('change', () => this.updateAALevel(), false);
|
||||
|
||||
this.updateAALevel();
|
||||
}
|
||||
|
||||
private updateAALevel() {
|
||||
const selectedOption = this.aaLevelSelect.selectedOptions[0];
|
||||
const aaValues = unwrapNull(/^([a-z-]+)(?:-([0-9]+))?$/.exec(selectedOption.value));
|
||||
const aaType = aaValues[1] as AntialiasingStrategyName;
|
||||
const aaLevel = aaValues[2] === "" ? 1 : parseInt(aaValues[2]);
|
||||
let aaType: AntialiasingStrategyName, aaLevel: number;
|
||||
if (this.aaLevelSelect != null) {
|
||||
const selectedOption = this.aaLevelSelect.selectedOptions[0];
|
||||
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;
|
||||
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 shaderSources: ShaderMap<ShaderProgramSource> | null;
|
||||
|
||||
private aaLevelSelect: HTMLSelectElement;
|
||||
private aaLevelSelect: HTMLSelectElement | null;
|
||||
private subpixelAASwitch: HTMLInputElement | null;
|
||||
|
||||
private settingsCard: HTMLElement;
|
||||
private settingsButton: HTMLButtonElement;
|
||||
private settingsCloseButton: HTMLButtonElement;
|
||||
}
|
||||
|
|
|
@ -8,18 +8,44 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
import * as glmatrix from 'gl-matrix';
|
||||
import * as opentype from "opentype.js";
|
||||
|
||||
import {AppController} from "./app-controller";
|
||||
import { AppController, DemoAppController } from "./app-controller";
|
||||
import {PathfinderMeshData} from "./meshes";
|
||||
import {BUILTIN_FONT_URI, GlyphStorage, PathfinderGlyph, TextFrame, TextRun} from "./text";
|
||||
import {assert, unwrapNull} from "./utils";
|
||||
import { BUILTIN_FONT_URI, GlyphStorage, PathfinderGlyph, TextFrame, TextRun, ExpandedMeshData } from "./text";
|
||||
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 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() {
|
||||
super.start();
|
||||
|
||||
|
@ -31,29 +57,176 @@ class BenchmarkAppController extends AppController {
|
|||
|
||||
protected fileLoaded(): void {
|
||||
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 textRun = new TextRun<BenchmarkGlyph>(STRING, [0, 0], font, createGlyph);
|
||||
this.textRun = textRun;
|
||||
const textFrame = new TextFrame([textRun], font);
|
||||
this.glyphStorage = new GlyphStorage(this.fileData, [textFrame], createGlyph, font);
|
||||
|
||||
this.glyphStorage.partition().then(meshes => {
|
||||
this.meshes = meshes;
|
||||
// TODO(pcwalton)
|
||||
// this.renderer.attachMeshes();
|
||||
this.glyphStorage.partition().then(baseMeshes => {
|
||||
this.baseMeshes = baseMeshes;
|
||||
const expandedMeshes = this.glyphStorage.expandMeshes(baseMeshes)[0];
|
||||
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 {
|
||||
// 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 builtinFileURI: string = BUILTIN_FONT_URI;
|
||||
|
||||
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 {}
|
||||
|
||||
function main() {
|
||||
const controller = new BenchmarkAppController;
|
||||
window.addEventListener('load', () => controller.start(), false);
|
||||
}
|
||||
|
||||
main();
|
||||
|
|
|
@ -190,7 +190,8 @@ class SVGDemoController extends DemoAppController<SVGDemoView> {
|
|||
|
||||
private meshesReceived(bounds: glmatrix.vec4): void {
|
||||
this.view.then(view => {
|
||||
view.uploadPathMetadata(this.pathInstances);
|
||||
view.uploadPathColors(1);
|
||||
view.uploadPathTransforms(1);
|
||||
view.attachMeshes([this.meshes]);
|
||||
|
||||
view.camera.bounds = bounds;
|
||||
|
@ -198,8 +199,9 @@ class SVGDemoController extends DemoAppController<SVGDemoView> {
|
|||
})
|
||||
}
|
||||
|
||||
pathInstances: PathInstance[];
|
||||
|
||||
private svg: SVGSVGElement;
|
||||
private pathInstances: PathInstance[];
|
||||
private meshes: PathfinderMeshData;
|
||||
}
|
||||
|
||||
|
@ -228,9 +230,10 @@ class SVGDemoView extends PathfinderDemoView {
|
|||
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 pathTransforms = new Float32Array(4 * (instances.length + 1));
|
||||
|
||||
for (let pathIndex = 0; pathIndex < instances.length; pathIndex++) {
|
||||
const startOffset = (pathIndex + 1) * 4;
|
||||
|
||||
|
@ -241,17 +244,22 @@ class SVGDemoView extends PathfinderDemoView {
|
|||
style[property] === 'none' ? [0, 0, 0, 0] : parseColor(style[property]).rgba;
|
||||
pathColors.set(color.slice(0, 3), startOffset);
|
||||
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.
|
||||
const startOffset = (pathIndex + 1) * 4;
|
||||
pathTransforms.set([1, 1, 0, 0], startOffset);
|
||||
}
|
||||
|
||||
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 = [pathColorsBufferTexture];
|
||||
this.pathTransformBufferTextures = [pathTransformBufferTexture];
|
||||
return pathTransforms;
|
||||
}
|
||||
|
||||
protected createAAStrategy(aaType: AntialiasingStrategyName,
|
||||
|
|
|
@ -178,7 +178,7 @@ class TextDemoController extends DemoAppController<TextDemoView> {
|
|||
this.meshes = meshes;
|
||||
this.view.then(view => {
|
||||
view.attachText();
|
||||
view.uploadPathMetadata(this.layout.glyphStorage.uniqueGlyphs.length);
|
||||
view.uploadPathColors(1);
|
||||
view.attachMeshes([this.meshes]);
|
||||
});
|
||||
});
|
||||
|
@ -262,17 +262,19 @@ class TextDemoView extends MonochromePathfinderView {
|
|||
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));
|
||||
|
||||
for (let pathIndex = 0; pathIndex < pathCount; pathIndex++) {
|
||||
for (let channel = 0; channel < 3; channel++)
|
||||
pathColors[(pathIndex + 1) * 4 + channel] = 0x00; // RGB
|
||||
pathColors[(pathIndex + 1) * 4 + 3] = 0xff; // alpha
|
||||
}
|
||||
|
||||
const pathColorsBufferTexture = new PathfinderBufferTexture(this.gl, 'uPathColors');
|
||||
pathColorsBufferTexture.upload(this.gl, pathColors);
|
||||
this.pathColorsBufferTextures = [pathColorsBufferTexture];
|
||||
return pathColors;
|
||||
}
|
||||
|
||||
/// Lays out glyphs on the canvas.
|
||||
|
@ -342,10 +344,37 @@ class TextDemoView extends MonochromePathfinderView {
|
|||
const uniqueGlyphIndices = uniqueGlyphs.map(glyph => glyph.index);
|
||||
uniqueGlyphIndices.sort((a, b) => a - b);
|
||||
|
||||
this.uploadPathTransforms(1);
|
||||
|
||||
// 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);
|
||||
|
||||
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++) {
|
||||
const glyph = atlasGlyphs[glyphIndex];
|
||||
|
||||
|
@ -359,18 +388,9 @@ class TextDemoView extends MonochromePathfinderView {
|
|||
transforms[pathID * 4 + 1] = pixelsPerUnit;
|
||||
transforms[pathID * 4 + 2] = atlasOrigin[0];
|
||||
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');
|
||||
pathTransformBufferTexture.upload(this.gl, transforms);
|
||||
this.pathTransformBufferTextures = [pathTransformBufferTexture];
|
||||
|
||||
const pathHintsBufferTexture = new PathfinderBufferTexture(this.gl, 'uPathHints');
|
||||
pathHintsBufferTexture.upload(this.gl, pathHints);
|
||||
this.pathHintsBufferTexture = pathHintsBufferTexture;
|
||||
return transforms;
|
||||
}
|
||||
|
||||
private createAtlasFramebuffer() {
|
||||
|
|
|
@ -109,6 +109,8 @@ export abstract class PathfinderDemoView extends PathfinderView {
|
|||
|
||||
this.initContext();
|
||||
|
||||
this.lastTimings = { atlasRendering: 0, compositing: 0 };
|
||||
|
||||
const shaderSource = this.compileShaders(commonShaderSource, shaderSources);
|
||||
this.shaderPrograms = this.linkShaders(shaderSource);
|
||||
|
||||
|
@ -281,6 +283,9 @@ export abstract class PathfinderDemoView extends PathfinderView {
|
|||
// Finish timing.
|
||||
this.finishTiming();
|
||||
|
||||
// Invoke the post-render hook.
|
||||
this.renderingFinished();
|
||||
|
||||
// Take a screenshot if desired.
|
||||
if (this.wantsScreenshot) {
|
||||
this.wantsScreenshot = false;
|
||||
|
@ -288,6 +293,8 @@ export abstract class PathfinderDemoView extends PathfinderView {
|
|||
}
|
||||
}
|
||||
|
||||
protected renderingFinished(): void {}
|
||||
|
||||
private setTransformUniform(uniforms: UniformMap, objectIndex: number) {
|
||||
const transform = glmatrix.mat4.create();
|
||||
if (this.antialiasingStrategy != null)
|
||||
|
@ -423,10 +430,10 @@ export abstract class PathfinderDemoView extends PathfinderView {
|
|||
const compositingTime =
|
||||
this.timerQueryExt.getQueryObjectEXT(this.compositingTimerQuery,
|
||||
this.timerQueryExt.QUERY_RESULT_EXT);
|
||||
this.updateTimings({
|
||||
this.lastTimings = {
|
||||
atlasRendering: atlasRenderingTime / 1000000.0,
|
||||
compositing: compositingTime / 1000000.0,
|
||||
});
|
||||
};
|
||||
|
||||
window.clearInterval(this.timerQueryPollInterval!);
|
||||
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);
|
||||
}
|
||||
|
||||
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 createAAStrategy(aaType: AntialiasingStrategyName,
|
||||
|
@ -494,8 +529,6 @@ export abstract class PathfinderDemoView extends PathfinderView {
|
|||
|
||||
protected abstract compositeIfNecessary(): void;
|
||||
|
||||
protected abstract updateTimings(timings: Timings): void;
|
||||
|
||||
abstract get destFramebuffer(): WebGLFramebuffer | null;
|
||||
|
||||
abstract get destAllocatedSize(): glmatrix.vec2;
|
||||
|
@ -536,6 +569,8 @@ export abstract class PathfinderDemoView extends PathfinderView {
|
|||
private compositingTimerQuery: WebGLQuery;
|
||||
private timerQueryPollInterval: number | null;
|
||||
|
||||
protected lastTimings: Timings;
|
||||
|
||||
private wantsScreenshot: boolean;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue