Replace the multicolor (SVG) XCAA with a multipass compositing algorithm

This commit is contained in:
Patrick Walton 2017-10-30 13:34:55 -07:00
parent b87d4126b0
commit 2604151521
19 changed files with 566 additions and 423 deletions

View File

@ -359,14 +359,6 @@ class ThreeDRenderer extends Renderer {
return this.destAllocatedSize;
}
protected get directCurveProgramName(): keyof ShaderMap<void> {
return 'direct3DCurve';
}
protected get directInteriorProgramName(): keyof ShaderMap<void> {
return 'direct3DInterior';
}
protected get depthFunction(): GLenum {
return this.renderContext.gl.LESS;
}
@ -528,6 +520,14 @@ class ThreeDRenderer extends Renderer {
this.renderContext.appController.newTimingsReceived(newTimings);
}
protected directCurveProgramNameForPass(pass: number): keyof ShaderMap<void> {
return 'direct3DCurve';
}
protected directInteriorProgramNameForPass(pass: number): keyof ShaderMap<void> {
return 'direct3DInterior';
}
// Cheap but effective backface culling.
private objectIsVisible(objectIndex: number): boolean {
const textFrameIndex = this.renderContext

View File

@ -15,7 +15,7 @@ import {DemoView} from './view';
export type AntialiasingStrategyName = 'none' | 'ssaa' | 'xcaa';
export type DirectRenderingMode = 'none' | 'color' | 'pathID';
export type DirectRenderingMode = 'none' | 'color' | 'two-pass';
export type SubpixelAAType = 'none' | 'medium';
@ -42,7 +42,7 @@ export abstract class AntialiasingStrategy {
// Called before direct rendering.
//
// Typically, this redirects direct rendering to a framebuffer of some sort.
abstract prepare(renderer: Renderer): void;
abstract prepareForDirectRendering(renderer: Renderer): void;
// Called after direct rendering.
//
@ -73,7 +73,7 @@ export class NoAAStrategy extends AntialiasingStrategy {
return glmatrix.mat4.create();
}
prepare(renderer: Renderer) {
prepareForDirectRendering(renderer: Renderer) {
const renderContext = renderer.renderContext;
renderContext.gl.bindFramebuffer(renderContext.gl.FRAMEBUFFER, renderer.destFramebuffer);
renderContext.gl.viewport(0, 0, this.framebufferSize[0], this.framebufferSize[1]);

View File

@ -309,14 +309,6 @@ class BenchmarkRenderer extends Renderer {
return glmatrix.vec2.clone([1.0, 1.0]);
}
protected get directCurveProgramName(): keyof ShaderMap<void> {
return 'directCurve';
}
protected get directInteriorProgramName(): keyof ShaderMap<void> {
return 'directInterior';
}
protected get depthFunction(): number {
return this.renderContext.gl.GREATER;
}
@ -445,6 +437,14 @@ class BenchmarkRenderer extends Renderer {
return pathTransforms;
}
protected directCurveProgramNameForPass(pass: number): keyof ShaderMap<void> {
return 'directCurve';
}
protected directInteriorProgramNameForPass(pass: number): keyof ShaderMap<void> {
return 'directInterior';
}
}
function computeMedian(values: number[]): number | null {

View File

@ -18,6 +18,7 @@ import {PathfinderMeshBuffers, PathfinderMeshData} from "./meshes";
import {ShaderMap} from './shader-loader';
import {FLOAT32_SIZE, Range, UINT16_SIZE, UINT32_SIZE, unwrapNull} from './utils';
import {RenderContext, Timings} from "./view";
import {MCAAMulticolorStrategy} from './xcaa-strategy';
const MAX_PATHS: number = 65535;
@ -64,8 +65,6 @@ export abstract class Renderer {
}
protected abstract get depthFunction(): GLenum;
protected abstract get directCurveProgramName(): keyof ShaderMap<void>;
protected abstract get directInteriorProgramName(): keyof ShaderMap<void>;
protected abstract get usedSizeFactor(): glmatrix.vec2;
protected abstract get worldTransform(): glmatrix.mat4;
@ -112,22 +111,22 @@ export abstract class Renderer {
renderContext.atlasRenderingTimerQuery);
}
// Prepare for direct rendering.
// Draw "scenery" (used in the 3D view).
this.drawSceneryIfNecessary();
// Antialias.
const antialiasingStrategy = unwrapNull(this.antialiasingStrategy);
antialiasingStrategy.prepare(this);
antialiasingStrategy.antialias(this);
// Prepare for direct rendering.
antialiasingStrategy.prepareForDirectRendering(this);
// Clear.
this.clearForDirectRendering();
// Draw "scenery" (used in the 3D view).
this.drawSceneryIfNecessary();
// Perform direct rendering (Loop-Blinn).
if (antialiasingStrategy.directRenderingMode !== 'none')
this.renderDirect();
// Antialias.
antialiasingStrategy.antialias(this);
this.renderDirect(0);
// End the timer, and start a new one.
if (this.timerQueryPollInterval == null) {
@ -138,6 +137,9 @@ export abstract class Renderer {
antialiasingStrategy.resolve(this);
if (antialiasingStrategy.directRenderingMode === 'two-pass')
this.renderDirect(1);
// Draw the glyphs with the resolved atlas to the default framebuffer.
this.compositeIfNecessary();
@ -167,9 +169,10 @@ export abstract class Renderer {
}
setFramebufferSizeUniform(uniforms: UniformMap) {
this.renderContext.gl.uniform2i(uniforms.uFramebufferSize,
this.destAllocatedSize[0],
this.destAllocatedSize[1]);
const gl = this.renderContext.gl;
gl.uniform2i(uniforms.uFramebufferSize,
this.destAllocatedSize[0],
this.destAllocatedSize[1]);
}
setTransformAndTexScaleUniformsForDest(uniforms: UniformMap): void {
@ -186,29 +189,29 @@ export abstract class Renderer {
setTransformSTAndTexScaleUniformsForDest(uniforms: UniformMap): void {
const renderContext = this.renderContext;
const gl = renderContext.gl;
const usedSize = this.usedSizeFactor;
renderContext.gl.uniform4f(uniforms.uTransformST,
2.0 * usedSize[0],
2.0 * usedSize[1],
-1.0,
-1.0);
renderContext.gl.uniform2f(uniforms.uTexScale, usedSize[0], usedSize[1]);
gl.uniform4f(uniforms.uTransformST, 2.0 * usedSize[0], 2.0 * usedSize[1], -1.0, -1.0);
gl.uniform2f(uniforms.uTexScale, usedSize[0], usedSize[1]);
}
setTransformSTUniform(uniforms: UniformMap, objectIndex: number) {
// FIXME(pcwalton): Lossy conversion from a 4x4 matrix to an ST matrix is ugly and fragile.
// Refactor.
const renderContext = this.renderContext;
const gl = renderContext.gl;
const transform = glmatrix.mat4.clone(this.worldTransform);
glmatrix.mat4.mul(transform, transform, this.getModelviewTransform(objectIndex));
const translation = glmatrix.vec4.clone([transform[12], transform[13], 0.0, 1.0]);
renderContext.gl.uniform4f(uniforms.uTransformST,
transform[0],
transform[5],
transform[12],
transform[13]);
gl.uniform4f(uniforms.uTransformST,
transform[0],
transform[5],
transform[12],
transform[13]);
}
uploadPathColors(objectCount: number) {
@ -248,9 +251,8 @@ export abstract class Renderer {
}
setPathColorsUniform(objectIndex: number, uniforms: UniformMap, textureUnit: number): void {
this.pathColorsBufferTextures[objectIndex].bind(this.renderContext.gl,
uniforms,
textureUnit);
const gl = this.renderContext.gl;
this.pathColorsBufferTextures[objectIndex].bind(gl, uniforms, textureUnit);
}
protected abstract createAAStrategy(aaType: AntialiasingStrategyName,
@ -262,6 +264,9 @@ export abstract class Renderer {
protected abstract pathColorsForObject(objectIndex: number): Uint8Array;
protected abstract pathTransformsForObject(objectIndex: number): Float32Array;
protected abstract directCurveProgramNameForPass(pass: number): keyof ShaderMap<void>;
protected abstract directInteriorProgramNameForPass(pass: number): keyof ShaderMap<void>;
protected drawSceneryIfNecessary(): void {}
protected clearForDirectRendering(): void {
@ -269,22 +274,21 @@ export abstract class Renderer {
const renderContext = this.renderContext;
const gl = renderContext.gl;
const clearColor = this.backgroundColor;
gl.clearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]);
switch (renderingMode) {
case 'color':
const clearColor = this.backgroundColor;
gl.clearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]);
gl.clearDepth(0.0);
gl.depthMask(true);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
break;
case 'pathID':
gl.clearColor(0.0, 0.0, 0.0, 0.0);
gl.clearDepth(0.0);
gl.depthMask(true);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
case 'two-pass':
gl.clear(gl.COLOR_BUFFER_BIT);
break;
case 'none':
// Nothing to do.
break;
}
}
@ -300,11 +304,13 @@ export abstract class Renderer {
/// Called whenever new GPU timing statistics are available.
protected newTimingsReceived(): void {}
private renderDirect(): void {
const renderingMode = unwrapNull(this.antialiasingStrategy).directRenderingMode;
private renderDirect(pass: number): void {
const renderContext = this.renderContext;
const gl = renderContext.gl;
const antialiasingStrategy = unwrapNull(this.antialiasingStrategy);
const renderingMode = antialiasingStrategy.directRenderingMode;
for (let objectIndex = 0; objectIndex < this.meshes.length; objectIndex++) {
const instanceRange = this.instanceRangeForObject(objectIndex);
if (instanceRange.isEmpty)
@ -319,23 +325,26 @@ export abstract class Renderer {
gl.disable(gl.BLEND);
// Set up the implicit cover interior VAO.
const directInteriorProgram =
renderContext.shaderPrograms[this.directInteriorProgramName];
const directInteriorProgramName = this.directInteriorProgramNameForPass(pass);
const directInteriorProgram = renderContext.shaderPrograms[directInteriorProgramName];
if (this.implicitCoverInteriorVAO == null) {
this.implicitCoverInteriorVAO = renderContext.vertexArrayObjectExt
.createVertexArrayOES();
}
renderContext.vertexArrayObjectExt.bindVertexArrayOES(this.implicitCoverInteriorVAO);
this.initImplicitCoverInteriorVAO(objectIndex, instanceRange);
this.initImplicitCoverInteriorVAO(pass, objectIndex, instanceRange);
// Draw direct interior parts.
this.setTransformUniform(directInteriorProgram.uniforms, objectIndex);
this.setFramebufferSizeUniform(directInteriorProgram.uniforms);
this.setHintsUniform(directInteriorProgram.uniforms);
if (renderingMode === 'color')
this.setPathColorsUniform(objectIndex, directInteriorProgram.uniforms, 0);
this.setPathColorsUniform(objectIndex, directInteriorProgram.uniforms, 0);
this.pathTransformBufferTextures[objectIndex]
.bind(gl, directInteriorProgram.uniforms, 1);
if (renderingMode === 'two-pass') {
const strategy = antialiasingStrategy as MCAAMulticolorStrategy;
strategy.bindEdgeDepthTexture(gl, directInteriorProgram.uniforms, 2);
}
let indexCount = meshData.coverInteriorIndices.byteLength / UINT32_SIZE;
if (!this.pathIDsAreInstanced) {
gl.drawElements(gl.TRIANGLES, indexCount, gl.UNSIGNED_INT, 0);
@ -356,21 +365,25 @@ export abstract class Renderer {
// Set up the direct curve VAO.
//
// TODO(pcwalton): Cache these.
const directCurveProgram = renderContext.shaderPrograms[this.directCurveProgramName];
const directCurveProgramName = this.directCurveProgramNameForPass(pass);
const directCurveProgram = renderContext.shaderPrograms[directCurveProgramName];
if (this.implicitCoverCurveVAO == null) {
this.implicitCoverCurveVAO = renderContext.vertexArrayObjectExt
.createVertexArrayOES();
}
renderContext.vertexArrayObjectExt.bindVertexArrayOES(this.implicitCoverCurveVAO);
this.initImplicitCoverCurveVAO(objectIndex, instanceRange);
this.initImplicitCoverCurveVAO(pass, objectIndex, instanceRange);
// Draw direct curve parts.
this.setTransformUniform(directCurveProgram.uniforms, objectIndex);
this.setFramebufferSizeUniform(directCurveProgram.uniforms);
this.setHintsUniform(directCurveProgram.uniforms);
if (renderingMode === 'color')
this.setPathColorsUniform(objectIndex, directCurveProgram.uniforms, 0);
this.setPathColorsUniform(objectIndex, directCurveProgram.uniforms, 0);
this.pathTransformBufferTextures[objectIndex].bind(gl, directCurveProgram.uniforms, 1);
if (renderingMode === 'two-pass') {
const strategy = antialiasingStrategy as MCAAMulticolorStrategy;
strategy.bindEdgeDepthTexture(gl, directCurveProgram.uniforms, 2);
}
indexCount = meshData.coverCurveIndices.byteLength / UINT32_SIZE;
if (!this.pathIDsAreInstanced) {
gl.drawElements(gl.TRIANGLES, indexCount, gl.UNSIGNED_INT, 0);
@ -426,104 +439,104 @@ export abstract class Renderer {
}, TIME_INTERVAL_DELAY);
}
private initImplicitCoverCurveVAO(objectIndex: number, instanceRange: Range): void {
private initImplicitCoverCurveVAO(pass: number, objectIndex: number, instanceRange: Range):
void {
const renderContext = this.renderContext;
const gl = renderContext.gl;
const meshes = this.meshes[objectIndex];
const directCurveProgram = renderContext.shaderPrograms[this.directCurveProgramName];
renderContext.gl.useProgram(directCurveProgram.program);
renderContext.gl.bindBuffer(renderContext.gl.ARRAY_BUFFER, meshes.bVertexPositions);
renderContext.gl.vertexAttribPointer(directCurveProgram.attributes.aPosition,
2,
renderContext.gl.FLOAT,
false,
0,
0);
const directCurveProgramName = this.directCurveProgramNameForPass(pass);
const directCurveProgram = renderContext.shaderPrograms[directCurveProgramName];
gl.useProgram(directCurveProgram.program);
gl.bindBuffer(gl.ARRAY_BUFFER, meshes.bVertexPositions);
gl.vertexAttribPointer(directCurveProgram.attributes.aPosition, 2, gl.FLOAT, false, 0, 0);
if (this.pathIDsAreInstanced)
renderContext.gl.bindBuffer(renderContext.gl.ARRAY_BUFFER, this.instancedPathIDVBO);
gl.bindBuffer(gl.ARRAY_BUFFER, this.instancedPathIDVBO);
else
renderContext.gl.bindBuffer(renderContext.gl.ARRAY_BUFFER, meshes.bVertexPathIDs);
renderContext.gl.vertexAttribPointer(directCurveProgram.attributes.aPathID,
1,
renderContext.gl.UNSIGNED_SHORT,
false,
0,
instanceRange.start * UINT16_SIZE);
gl.bindBuffer(gl.ARRAY_BUFFER, meshes.bVertexPathIDs);
gl.vertexAttribPointer(directCurveProgram.attributes.aPathID,
1,
gl.UNSIGNED_SHORT,
false,
0,
instanceRange.start * UINT16_SIZE);
if (this.pathIDsAreInstanced) {
renderContext.instancedArraysExt
.vertexAttribDivisorANGLE(directCurveProgram.attributes.aPathID, 1);
}
renderContext.gl.bindBuffer(renderContext.gl.ARRAY_BUFFER, meshes.bVertexLoopBlinnData);
renderContext.gl.vertexAttribPointer(directCurveProgram.attributes.aTexCoord,
2,
renderContext.gl.UNSIGNED_BYTE,
false,
B_LOOP_BLINN_DATA_SIZE,
B_LOOP_BLINN_DATA_TEX_COORD_OFFSET);
renderContext.gl.vertexAttribPointer(directCurveProgram.attributes.aSign,
1,
renderContext.gl.BYTE,
false,
B_LOOP_BLINN_DATA_SIZE,
B_LOOP_BLINN_DATA_SIGN_OFFSET);
renderContext.gl.enableVertexAttribArray(directCurveProgram.attributes.aPosition);
renderContext.gl.enableVertexAttribArray(directCurveProgram.attributes.aTexCoord);
renderContext.gl.enableVertexAttribArray(directCurveProgram.attributes.aPathID);
renderContext.gl.enableVertexAttribArray(directCurveProgram.attributes.aSign);
renderContext.gl.bindBuffer(renderContext.gl.ELEMENT_ARRAY_BUFFER,
meshes.coverCurveIndices);
gl.bindBuffer(gl.ARRAY_BUFFER, meshes.bVertexLoopBlinnData);
gl.vertexAttribPointer(directCurveProgram.attributes.aTexCoord,
2,
gl.UNSIGNED_BYTE,
false,
B_LOOP_BLINN_DATA_SIZE,
B_LOOP_BLINN_DATA_TEX_COORD_OFFSET);
gl.vertexAttribPointer(directCurveProgram.attributes.aSign,
1,
gl.BYTE,
false,
B_LOOP_BLINN_DATA_SIZE,
B_LOOP_BLINN_DATA_SIGN_OFFSET);
gl.enableVertexAttribArray(directCurveProgram.attributes.aPosition);
gl.enableVertexAttribArray(directCurveProgram.attributes.aTexCoord);
gl.enableVertexAttribArray(directCurveProgram.attributes.aPathID);
gl.enableVertexAttribArray(directCurveProgram.attributes.aSign);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, meshes.coverCurveIndices);
}
private initImplicitCoverInteriorVAO(objectIndex: number, instanceRange: Range): void {
private initImplicitCoverInteriorVAO(pass: number,
objectIndex: number,
instanceRange: Range):
void {
const renderContext = this.renderContext;
const gl = renderContext.gl;
const meshes = this.meshes[objectIndex];
const directInteriorProgram = renderContext.shaderPrograms[this.directInteriorProgramName];
renderContext.gl.useProgram(directInteriorProgram.program);
renderContext.gl.bindBuffer(renderContext.gl.ARRAY_BUFFER, meshes.bVertexPositions);
renderContext.gl.vertexAttribPointer(directInteriorProgram.attributes.aPosition,
2,
renderContext.gl.FLOAT,
false,
0,
0);
const directInteriorProgramName = this.directInteriorProgramNameForPass(pass);
const directInteriorProgram = renderContext.shaderPrograms[directInteriorProgramName];
gl.useProgram(directInteriorProgram.program);
gl.bindBuffer(gl.ARRAY_BUFFER, meshes.bVertexPositions);
gl.vertexAttribPointer(directInteriorProgram.attributes.aPosition,
2,
gl.FLOAT,
false,
0,
0);
if (this.pathIDsAreInstanced)
renderContext.gl.bindBuffer(renderContext.gl.ARRAY_BUFFER, this.instancedPathIDVBO);
gl.bindBuffer(gl.ARRAY_BUFFER, this.instancedPathIDVBO);
else
renderContext.gl.bindBuffer(renderContext.gl.ARRAY_BUFFER, meshes.bVertexPathIDs);
renderContext.gl.vertexAttribPointer(directInteriorProgram.attributes.aPathID,
1,
renderContext.gl.UNSIGNED_SHORT,
false,
0,
instanceRange.start * UINT16_SIZE);
gl.bindBuffer(gl.ARRAY_BUFFER, meshes.bVertexPathIDs);
gl.vertexAttribPointer(directInteriorProgram.attributes.aPathID,
1,
gl.UNSIGNED_SHORT,
false,
0,
instanceRange.start * UINT16_SIZE);
if (this.pathIDsAreInstanced) {
renderContext.instancedArraysExt
.vertexAttribDivisorANGLE(directInteriorProgram.attributes.aPathID, 1);
}
renderContext.gl.enableVertexAttribArray(directInteriorProgram.attributes.aPosition);
renderContext.gl.enableVertexAttribArray(directInteriorProgram.attributes.aPathID);
renderContext.gl.bindBuffer(renderContext.gl.ELEMENT_ARRAY_BUFFER,
meshes.coverInteriorIndices);
gl.enableVertexAttribArray(directInteriorProgram.attributes.aPosition);
gl.enableVertexAttribArray(directInteriorProgram.attributes.aPathID);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, meshes.coverInteriorIndices);
}
private initInstancedPathIDVBO(): void {
const renderContext = this.renderContext;
const gl = renderContext.gl;
const pathIDs = new Uint16Array(MAX_PATHS);
for (let pathIndex = 0; pathIndex < MAX_PATHS; pathIndex++)
pathIDs[pathIndex] = pathIndex + 1;
this.instancedPathIDVBO = renderContext.gl.createBuffer();
renderContext.gl.bindBuffer(renderContext.gl.ARRAY_BUFFER, this.instancedPathIDVBO);
renderContext.gl.bufferData(renderContext.gl.ARRAY_BUFFER,
pathIDs,
renderContext.gl.STATIC_DRAW);
renderContext.gl.bindBuffer(renderContext.gl.ARRAY_BUFFER, null);
gl.bindBuffer(gl.ARRAY_BUFFER, this.instancedPathIDVBO);
gl.bufferData(gl.ARRAY_BUFFER, pathIDs, gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
}
private setTransformUniform(uniforms: UniformMap, objectIndex: number) {

View File

@ -25,11 +25,12 @@ export interface ShaderMap<T> {
mcaaLine: T;
mcaaCurve: T;
ssaaSubpixelResolve: T;
xcaaEdgeDetect: T;
xcaaMonoResolve: T;
xcaaMonoSubpixelResolve: T;
xcaaMultiDirectCurve: T;
xcaaMultiDirectInterior: T;
xcaaMultiBGDirectCurve: T;
xcaaMultiBGDirectInterior: T;
xcaaMultiEdgeMaskCurve: T;
xcaaMultiEdgeMaskLine: T;
xcaaMultiResolve: T;
}
@ -47,7 +48,6 @@ export const SHADER_NAMES: Array<keyof ShaderMap<void>> = [
'direct3DCurve',
'direct3DInterior',
'ssaaSubpixelResolve',
'xcaaEdgeDetect',
'mcaaCover',
'mcaaLine',
'mcaaCurve',
@ -55,8 +55,10 @@ export const SHADER_NAMES: Array<keyof ShaderMap<void>> = [
'ecaaCurve',
'xcaaMonoResolve',
'xcaaMonoSubpixelResolve',
'xcaaMultiDirectCurve',
'xcaaMultiDirectInterior',
'xcaaMultiBGDirectCurve',
'xcaaMultiBGDirectInterior',
'xcaaMultiEdgeMaskCurve',
'xcaaMultiEdgeMaskLine',
'xcaaMultiResolve',
'demo3DDistantGlyph',
'demo3DMonument',
@ -115,10 +117,6 @@ const SHADER_URLS: ShaderMap<ShaderProgramURLs> = {
fragment: "/glsl/gles2/ssaa-subpixel-resolve.fs.glsl",
vertex: "/glsl/gles2/ssaa-subpixel-resolve.vs.glsl",
},
xcaaEdgeDetect: {
fragment: "/glsl/gles2/xcaa-edge-detect.fs.glsl",
vertex: "/glsl/gles2/xcaa-edge-detect.vs.glsl",
},
xcaaMonoResolve: {
fragment: "/glsl/gles2/xcaa-mono-resolve.fs.glsl",
vertex: "/glsl/gles2/xcaa-mono-resolve.vs.glsl",
@ -127,13 +125,21 @@ const SHADER_URLS: ShaderMap<ShaderProgramURLs> = {
fragment: "/glsl/gles2/xcaa-mono-subpixel-resolve.fs.glsl",
vertex: "/glsl/gles2/xcaa-mono-subpixel-resolve.vs.glsl",
},
xcaaMultiDirectCurve: {
fragment: "/glsl/gles2/xcaa-multi-direct-curve.fs.glsl",
vertex: "/glsl/gles2/xcaa-multi-direct-curve.vs.glsl",
xcaaMultiBGDirectCurve: {
fragment: "/glsl/gles2/xcaa-multi-bg-direct-curve.fs.glsl",
vertex: "/glsl/gles2/xcaa-multi-bg-direct-curve.vs.glsl",
},
xcaaMultiDirectInterior: {
fragment: "/glsl/gles2/xcaa-multi-direct-interior.fs.glsl",
vertex: "/glsl/gles2/xcaa-multi-direct-interior.vs.glsl",
xcaaMultiBGDirectInterior: {
fragment: "/glsl/gles2/xcaa-multi-bg-direct-interior.fs.glsl",
vertex: "/glsl/gles2/xcaa-multi-bg-direct-interior.vs.glsl",
},
xcaaMultiEdgeMaskCurve: {
fragment: "/glsl/gles2/xcaa-multi-edge-mask-curve.fs.glsl",
vertex: "/glsl/gles2/mcaa-curve.vs.glsl",
},
xcaaMultiEdgeMaskLine: {
fragment: "/glsl/gles2/xcaa-multi-edge-mask-line.fs.glsl",
vertex: "/glsl/gles2/mcaa-line.vs.glsl",
},
xcaaMultiResolve: {
fragment: "/glsl/gles2/xcaa-multi-resolve.fs.glsl",

View File

@ -38,6 +38,7 @@ export default class SSAAStrategy extends AntialiasingStrategy {
setFramebufferSize(renderer: Renderer) {
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
this.destFramebufferSize = glmatrix.vec2.clone(renderer.destAllocatedSize);
@ -46,28 +47,28 @@ export default class SSAAStrategy extends AntialiasingStrategy {
this.destFramebufferSize,
this.supersampleScale);
this.supersampledColorTexture = unwrapNull(renderContext.gl.createTexture());
renderContext.gl.activeTexture(renderContext.gl.TEXTURE0);
renderContext.gl.bindTexture(renderContext.gl.TEXTURE_2D, this.supersampledColorTexture);
renderContext.gl.texImage2D(renderContext.gl.TEXTURE_2D,
0,
renderContext.colorAlphaFormat,
this.supersampledFramebufferSize[0],
this.supersampledFramebufferSize[1],
0,
renderContext.colorAlphaFormat,
renderContext.gl.UNSIGNED_BYTE,
null);
setTextureParameters(renderContext.gl, renderContext.gl.LINEAR);
this.supersampledColorTexture = unwrapNull(gl.createTexture());
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, this.supersampledColorTexture);
gl.texImage2D(gl.TEXTURE_2D,
0,
renderContext.colorAlphaFormat,
this.supersampledFramebufferSize[0],
this.supersampledFramebufferSize[1],
0,
renderContext.colorAlphaFormat,
gl.UNSIGNED_BYTE,
null);
setTextureParameters(gl, gl.LINEAR);
this.supersampledDepthTexture =
createFramebufferDepthTexture(renderContext.gl, this.supersampledFramebufferSize);
createFramebufferDepthTexture(gl, this.supersampledFramebufferSize);
this.supersampledFramebuffer = createFramebuffer(renderContext.gl,
this.supersampledFramebuffer = createFramebuffer(gl,
this.supersampledColorTexture,
this.supersampledDepthTexture);
renderContext.gl.bindFramebuffer(renderContext.gl.FRAMEBUFFER, null);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
}
get transform(): glmatrix.mat4 {
@ -79,7 +80,7 @@ export default class SSAAStrategy extends AntialiasingStrategy {
return transform;
}
prepare(renderer: Renderer) {
prepareForDirectRendering(renderer: Renderer) {
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
@ -95,13 +96,12 @@ export default class SSAAStrategy extends AntialiasingStrategy {
resolve(renderer: Renderer) {
const renderContext = renderer.renderContext;
renderContext.gl.bindFramebuffer(renderContext.gl.FRAMEBUFFER, renderer.destFramebuffer);
renderContext.gl.viewport(0,
0,
renderer.destAllocatedSize[0],
renderer.destAllocatedSize[1]);
renderContext.gl.disable(renderContext.gl.DEPTH_TEST);
renderContext.gl.disable(renderContext.gl.BLEND);
const gl = renderContext.gl;
gl.bindFramebuffer(gl.FRAMEBUFFER, renderer.destFramebuffer);
gl.viewport(0, 0, renderer.destAllocatedSize[0], renderer.destAllocatedSize[1]);
gl.disable(gl.DEPTH_TEST);
gl.disable(gl.BLEND);
// Set up the blit program VAO.
let resolveProgram;
@ -109,23 +109,19 @@ export default class SSAAStrategy extends AntialiasingStrategy {
resolveProgram = renderContext.shaderPrograms.ssaaSubpixelResolve;
else
resolveProgram = renderContext.shaderPrograms.blit;
renderContext.gl.useProgram(resolveProgram.program);
gl.useProgram(resolveProgram.program);
renderContext.initQuadVAO(resolveProgram.attributes);
// Resolve framebuffer.
renderContext.gl.activeTexture(renderContext.gl.TEXTURE0);
renderContext.gl.bindTexture(renderContext.gl.TEXTURE_2D, this.supersampledColorTexture);
renderContext.gl.uniform1i(resolveProgram.uniforms.uSource, 0);
renderContext.gl.uniform2i(resolveProgram.uniforms.uSourceDimensions,
this.supersampledFramebufferSize[0],
this.supersampledFramebufferSize[1]);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, this.supersampledColorTexture);
gl.uniform1i(resolveProgram.uniforms.uSource, 0);
gl.uniform2i(resolveProgram.uniforms.uSourceDimensions,
this.supersampledFramebufferSize[0],
this.supersampledFramebufferSize[1]);
renderer.setTransformAndTexScaleUniformsForDest(resolveProgram.uniforms);
renderContext.gl.bindBuffer(renderContext.gl.ELEMENT_ARRAY_BUFFER,
renderContext.quadElementsBuffer);
renderContext.gl.drawElements(renderContext.gl.TRIANGLES,
6,
renderContext.gl.UNSIGNED_BYTE,
0);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, renderContext.quadElementsBuffer);
gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0);
}
get directRenderingMode(): DirectRenderingMode {

View File

@ -115,10 +115,8 @@ class SVGDemoRenderer extends Renderer {
camera: OrthographicCamera;
get destAllocatedSize(): glmatrix.vec2 {
return glmatrix.vec2.clone([
this.renderContext.canvas.width,
this.renderContext.canvas.height,
]);
const canvas = this.renderContext.canvas;
return glmatrix.vec2.clone([canvas.width, canvas.height]);
}
get destFramebuffer(): WebGLFramebuffer | null {
@ -183,15 +181,15 @@ class SVGDemoRenderer extends Renderer {
return transform;
}
protected get directCurveProgramName(): keyof ShaderMap<void> {
if (this.antialiasingStrategy instanceof XCAAStrategy)
return 'xcaaMultiDirectCurve';
protected directCurveProgramNameForPass(pass: number): keyof ShaderMap<void> {
if (this.antialiasingStrategy instanceof XCAAStrategy && pass === 0)
return 'xcaaMultiBGDirectCurve';
return 'directCurve';
}
protected get directInteriorProgramName(): keyof ShaderMap<void> {
if (this.antialiasingStrategy instanceof XCAAStrategy)
return 'xcaaMultiDirectInterior';
protected directInteriorProgramNameForPass(pass: number): keyof ShaderMap<void> {
if (this.antialiasingStrategy instanceof XCAAStrategy && pass === 0)
return 'xcaaMultiBGDirectInterior';
return 'directInterior';
}

View File

@ -18,6 +18,8 @@ import {panic, unwrapNull} from "./utils";
export const BUILTIN_SVG_URI: string = "/svg/demo";
const parseColor = require('parse-color');
const PARTITION_SVG_PATHS_ENDPOINT_URL: string = "/partition-svg-paths";
/// The minimum size of a stroke.
@ -105,9 +107,9 @@ export class SVGLoader {
if (element instanceof SVGPathElement) {
const style = window.getComputedStyle(element);
if (style.fill !== 'none')
if (hasRenderingOperation(style.fill))
this.pathInstances.push({ element: element, stroke: 'fill' });
if (style.stroke !== 'none') {
if (hasRenderingOperation(style.stroke)) {
this.pathInstances.push({
element: element,
stroke: parseInt(style.strokeWidth!, 10),
@ -159,3 +161,7 @@ export class SVGLoader {
this.bounds = glmatrix.vec4.clone([minX, minY, maxX, maxY]);
}
}
function hasRenderingOperation(style: string | null): boolean {
return style != null && style !== 'none' && parseColor(style).rgba[3] > 0.0;
}

View File

@ -109,14 +109,6 @@ export abstract class TextRenderer extends Renderer {
return glmatrix.vec2.create();
}
protected get directCurveProgramName(): keyof ShaderMap<void> {
return 'directCurve';
}
protected get directInteriorProgramName(): keyof ShaderMap<void> {
return 'directInterior';
}
protected get depthFunction(): number {
return this.renderContext.gl.GREATER;
}
@ -272,4 +264,12 @@ export abstract class TextRenderer extends Renderer {
this.displayPixelsPerUnit,
this.renderContext.useHinting);
}
protected directCurveProgramNameForPass(pass: number): keyof ShaderMap<void> {
return 'directCurve';
}
protected directInteriorProgramNameForPass(pass: number): keyof ShaderMap<void> {
return 'directInterior';
}
}

View File

@ -47,9 +47,9 @@ export abstract class XCAAStrategy extends AntialiasingStrategy {
protected resolveVAO: WebGLVertexArrayObject;
protected aaAlphaTexture: WebGLTexture;
protected aaFramebuffer: WebGLFramebuffer;
private directFramebuffer: WebGLFramebuffer;
private aaFramebuffer: WebGLFramebuffer;
constructor(level: number, subpixelAA: SubpixelAAType) {
super();
@ -66,7 +66,6 @@ export abstract class XCAAStrategy extends AntialiasingStrategy {
attachMeshes(renderer: Renderer) {
const renderContext = renderer.renderContext;
this.createEdgeDetectVAO(renderContext);
this.createResolveVAO(renderer);
}
@ -78,13 +77,12 @@ export abstract class XCAAStrategy extends AntialiasingStrategy {
this.destFramebufferSize,
this.supersampleScale);
this.initDirectFramebuffer(renderer);
this.initDirectFramebufferIfNecessary(renderer);
this.initAAAlphaFramebuffer(renderer);
this.initEdgeDetectFramebuffer(renderer);
renderContext.gl.bindFramebuffer(renderContext.gl.FRAMEBUFFER, null);
}
prepare(renderer: Renderer) {
prepareForDirectRendering(renderer: Renderer) {
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
@ -105,14 +103,17 @@ export abstract class XCAAStrategy extends AntialiasingStrategy {
}
antialias(renderer: Renderer) {
// Detect edges if necessary.
this.detectEdgesIfNecessary(renderer);
// Perform early preparations.
this.createPathBoundsBufferTexture(renderer);
// Mask edges if necessary.
this.maskEdgesIfNecessary(renderer);
// Set up antialiasing.
this.prepareAA(renderer);
// Clear.
this.clear(renderer);
this.clearForAA(renderer);
}
resolve(renderer: Renderer) {
@ -124,20 +125,14 @@ export abstract class XCAAStrategy extends AntialiasingStrategy {
return glmatrix.mat4.create();
}
protected initDirectFramebuffer(renderer: Renderer) {
protected initDirectFramebufferIfNecessary(renderer: Renderer) {
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
let textureFormat;
if (this.directRenderingMode === 'pathID')
textureFormat = gl.RGBA;
else
textureFormat = renderContext.colorAlphaFormat;
this.directTexture = createFramebufferColorTexture(gl,
this.destFramebufferSize,
textureFormat);
this.directFramebuffer = createFramebuffer(renderContext.gl,
renderContext.colorAlphaFormat);
this.directFramebuffer = createFramebuffer(gl,
this.directTexture,
this.directDepthTexture);
}
@ -159,8 +154,6 @@ export abstract class XCAAStrategy extends AntialiasingStrategy {
this.supersampledFramebufferSize[1]);
renderContext.gl.scissor(0, 0, usedSize[0], usedSize[1]);
renderContext.gl.enable(renderContext.gl.SCISSOR_TEST);
this.createPathBoundsBufferTexture(renderer);
}
protected setAAState(renderer: Renderer) {
@ -200,8 +193,7 @@ export abstract class XCAAStrategy extends AntialiasingStrategy {
gl.viewport(0, 0, this.destFramebufferSize[0], this.destFramebufferSize[1]);
gl.scissor(0, 0, usedSize[0], usedSize[1]);
gl.enable(gl.SCISSOR_TEST);
gl.disable(gl.DEPTH_TEST);
gl.disable(gl.BLEND);
this.setDepthAndBlendModeForResolve(renderContext);
// Clear out the resolve buffer, if necessary.
this.clearForResolve(renderer);
@ -224,15 +216,25 @@ export abstract class XCAAStrategy extends AntialiasingStrategy {
if (renderer.fgColor != null)
gl.uniform4fv(resolveProgram.uniforms.uFGColor, renderer.fgColor);
renderer.setTransformSTAndTexScaleUniformsForDest(resolveProgram.uniforms);
this.setAdditionalStateForResolveIfNecessary(renderer, resolveProgram, 1);
gl.drawElements(renderContext.gl.TRIANGLES, 6, renderContext.gl.UNSIGNED_BYTE, 0);
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
}
protected abstract clear(renderer: Renderer): void;
protected setDepthAndBlendModeForResolve(renderContext: RenderContext): void {
const gl = renderContext.gl;
gl.disable(gl.DEPTH_TEST);
gl.disable(gl.BLEND);
}
protected setAdditionalStateForResolveIfNecessary(renderer: Renderer,
resolveProgram: PathfinderShaderProgram,
firstFreeTextureUnit: number):
void {}
protected abstract clearForAA(renderer: Renderer): void;
protected abstract getResolveProgram(renderContext: RenderContext): PathfinderShaderProgram;
protected abstract initEdgeDetectFramebuffer(renderer: Renderer): void;
protected abstract createEdgeDetectVAO(renderContext: RenderContext): void;
protected abstract detectEdgesIfNecessary(renderer: Renderer): void;
protected abstract maskEdgesIfNecessary(renderer: Renderer): void;
protected abstract setAADepthState(renderer: Renderer): void;
protected abstract clearForResolve(renderer: Renderer): void;
@ -323,7 +325,7 @@ export abstract class MCAAStrategy extends XCAAStrategy {
renderContext.gl.blendFunc(renderContext.gl.ONE, renderContext.gl.ONE);
renderContext.gl.enable(renderContext.gl.BLEND);
this.clear(renderer);
this.clearForAA(renderer);
}
protected setCoverDepthState(renderer: Renderer): void {
@ -331,6 +333,64 @@ export abstract class MCAAStrategy extends XCAAStrategy {
renderContext.gl.disable(renderContext.gl.DEPTH_TEST);
}
protected antialiasLinesWithProgram(renderer: Renderer,
lineProgram: PathfinderShaderProgram): void {
const renderContext = renderer.renderContext;
const uniforms = lineProgram.uniforms;
this.setAAUniforms(renderer, uniforms);
for (const direction of DIRECTIONS) {
const vao = this.lineVAOs[direction];
renderContext.vertexArrayObjectExt.bindVertexArrayOES(vao);
this.setBlendModeForAA(renderer, direction);
renderContext.gl.uniform1i(uniforms.uWinding, direction === 'upper' ? 1 : 0);
const count = {
lower: renderer.meshData[0].edgeLowerLineCount,
upper: renderer.meshData[0].edgeUpperLineCount,
}[direction];
renderContext.instancedArraysExt
.drawElementsInstancedANGLE(renderContext.gl.TRIANGLES,
6,
renderContext.gl.UNSIGNED_BYTE,
0,
count);
}
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
}
protected antialiasCurvesWithProgram(renderer: Renderer,
curveProgram: PathfinderShaderProgram): void {
const renderContext = renderer.renderContext;
const uniforms = curveProgram.uniforms;
this.setAAUniforms(renderer, uniforms);
for (const direction of DIRECTIONS) {
const vao = this.curveVAOs[direction];
renderContext.vertexArrayObjectExt.bindVertexArrayOES(vao);
this.setBlendModeForAA(renderer, direction);
renderContext.gl.uniform1i(uniforms.uWinding, direction === 'upper' ? 1 : 0);
const count = {
lower: renderer.meshData[0].edgeLowerCurveCount,
upper: renderer.meshData[0].edgeUpperCurveCount,
}[direction];
renderContext.instancedArraysExt
.drawElementsInstancedANGLE(renderContext.gl.TRIANGLES,
6,
renderContext.gl.UNSIGNED_BYTE,
0,
count);
}
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
}
private createCoverVAO(renderer: Renderer) {
const renderContext = renderer.renderContext;
@ -561,35 +621,13 @@ export abstract class MCAAStrategy extends XCAAStrategy {
private antialiasLines(renderer: Renderer) {
const renderContext = renderer.renderContext;
this.setAAState(renderer);
const lineProgram = renderContext.shaderPrograms.mcaaLine;
renderContext.gl.useProgram(lineProgram.program);
const uniforms = lineProgram.uniforms;
this.setAAUniforms(renderer, uniforms);
for (const direction of DIRECTIONS) {
const vao = this.lineVAOs[direction];
renderContext.vertexArrayObjectExt.bindVertexArrayOES(vao);
this.setBlendModeForAA(renderer, direction);
renderContext.gl.uniform1i(uniforms.uWinding, direction === 'upper' ? 1 : 0);
const count = {
lower: renderer.meshData[0].edgeLowerLineCount,
upper: renderer.meshData[0].edgeUpperLineCount,
}[direction];
renderContext.instancedArraysExt
.drawElementsInstancedANGLE(renderContext.gl.TRIANGLES,
6,
renderContext.gl.UNSIGNED_BYTE,
0,
count);
}
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
// FIXME(pcwalton): Refactor.
this.antialiasLinesWithProgram(renderer, lineProgram);
}
private antialiasCurves(renderer: Renderer) {
@ -600,30 +638,10 @@ export abstract class MCAAStrategy extends XCAAStrategy {
const curveProgram = renderContext.shaderPrograms.mcaaCurve;
renderContext.gl.useProgram(curveProgram.program);
const uniforms = curveProgram.uniforms;
this.setAAUniforms(renderer, uniforms);
for (const direction of DIRECTIONS) {
const vao = this.curveVAOs[direction];
renderContext.vertexArrayObjectExt.bindVertexArrayOES(vao);
this.setBlendModeForAA(renderer, direction);
renderContext.gl.uniform1i(uniforms.uWinding, direction === 'upper' ? 1 : 0);
const count = {
lower: renderer.meshData[0].edgeLowerCurveCount,
upper: renderer.meshData[0].edgeUpperCurveCount,
}[direction];
renderContext.instancedArraysExt
.drawElementsInstancedANGLE(renderContext.gl.TRIANGLES,
6,
renderContext.gl.UNSIGNED_BYTE,
0,
count);
}
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
this.antialiasCurvesWithProgram(renderer, curveProgram);
}
}
export class ECAAStrategy extends XCAAStrategy {
@ -663,13 +681,9 @@ export class ECAAStrategy extends XCAAStrategy {
return renderContext.shaderPrograms.xcaaMonoResolve;
}
protected initEdgeDetectFramebuffer(renderer: Renderer) {}
protected maskEdgesIfNecessary(renderer: Renderer) {}
protected createEdgeDetectVAO(renderContext: RenderContext) {}
protected detectEdgesIfNecessary(renderer: Renderer) {}
protected clear(renderer: Renderer) {
protected clearForAA(renderer: Renderer) {
const renderContext = renderer.renderContext;
renderContext.gl.clearColor(0.0, 0.0, 0.0, 0.0);
renderContext.gl.clearDepth(0.0);
@ -910,13 +924,9 @@ export class MCAAMonochromeStrategy extends MCAAStrategy {
return renderContext.shaderPrograms.xcaaMonoResolve;
}
protected initEdgeDetectFramebuffer(renderer: Renderer) {}
protected maskEdgesIfNecessary(renderer: Renderer) {}
protected createEdgeDetectVAO(renderContext: RenderContext) {}
protected detectEdgesIfNecessary(renderer: Renderer) {}
protected clear(renderer: Renderer) {
protected clearForAA(renderer: Renderer) {
const renderContext = renderer.renderContext;
renderContext.gl.clearColor(0.0, 0.0, 0.0, 0.0);
renderContext.gl.clearDepth(0.0);
@ -975,8 +985,8 @@ export class AdaptiveMonochromeXCAAStrategy implements AntialiasingStrategy {
return this.mcaaStrategy.transform;
}
prepare(renderer: Renderer): void {
this.getAppropriateStrategy(renderer).prepare(renderer);
prepareForDirectRendering(renderer: Renderer): void {
this.getAppropriateStrategy(renderer).prepareForDirectRendering(renderer);
}
antialias(renderer: Renderer): void {
@ -995,120 +1005,54 @@ export class AdaptiveMonochromeXCAAStrategy implements AntialiasingStrategy {
}
export class MCAAMulticolorStrategy extends MCAAStrategy {
private _directDepthTexture: WebGLTexture;
private edgeMaskVAO: WebGLVertexArrayObject;
private edgeDetectFramebuffer: WebGLFramebuffer;
private edgeDetectVAO: WebGLVertexArrayObject;
private edgePathIDTexture: WebGLTexture;
bindEdgeDepthTexture(gl: WebGLRenderingContext, uniforms: UniformMap, textureUnit: number):
void {
gl.activeTexture(gl.TEXTURE0 + textureUnit);
gl.bindTexture(gl.TEXTURE_2D, this.aaDepthTexture);
gl.uniform1i(uniforms.uEdgeDepth, textureUnit);
}
protected getResolveProgram(renderContext: RenderContext): PathfinderShaderProgram {
return renderContext.shaderPrograms.xcaaMultiResolve;
}
protected initDirectFramebuffer(renderer: Renderer) {
const renderContext = renderer.renderContext;
this._directDepthTexture = createFramebufferDepthTexture(renderContext.gl,
this.supersampledFramebufferSize);
super.initDirectFramebuffer(renderer);
}
protected initDirectFramebufferIfNecessary(renderer: Renderer) {}
protected initEdgeDetectFramebuffer(renderer: Renderer) {
const renderContext = renderer.renderContext;
this.edgePathIDTexture = createFramebufferColorTexture(renderContext.gl,
this.supersampledFramebufferSize,
renderContext.gl.RGBA);
this.edgeDetectFramebuffer = createFramebuffer(renderContext.gl,
this.edgePathIDTexture,
this.aaDepthTexture);
}
protected createEdgeDetectVAO(renderContext: RenderContext) {
this.edgeDetectVAO = renderContext.vertexArrayObjectExt.createVertexArrayOES();
renderContext.vertexArrayObjectExt.bindVertexArrayOES(this.edgeDetectVAO);
const edgeDetectProgram = renderContext.shaderPrograms.xcaaEdgeDetect;
renderContext.gl.useProgram(edgeDetectProgram.program);
renderContext.initQuadVAO(edgeDetectProgram.attributes);
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
}
protected detectEdgesIfNecessary(renderer: Renderer) {
protected maskEdgesIfNecessary(renderer: Renderer) {
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
// Set state for edge detection.
const edgeDetectProgram = renderContext.shaderPrograms.xcaaEdgeDetect;
gl.bindFramebuffer(gl.FRAMEBUFFER, this.edgeDetectFramebuffer);
// Set state for edge masking.
gl.bindFramebuffer(gl.FRAMEBUFFER, this.aaFramebuffer);
gl.viewport(0,
0,
this.supersampledFramebufferSize[0],
this.supersampledFramebufferSize[1]);
gl.colorMask(false, false, false, false);
gl.depthMask(true);
gl.depthFunc(renderContext.gl.ALWAYS);
gl.enable(renderContext.gl.DEPTH_TEST);
gl.disable(renderContext.gl.BLEND);
gl.depthFunc(gl.GREATER);
gl.enable(gl.DEPTH_TEST);
gl.disable(gl.BLEND);
gl.clearDepth(0.0);
gl.clearColor(0.0, 0.0, 0.0, 0.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
// Perform edge detection.
gl.useProgram(edgeDetectProgram.program);
renderContext.vertexArrayObjectExt.bindVertexArrayOES(this.edgeDetectVAO);
renderer.setFramebufferSizeUniform(edgeDetectProgram.uniforms);
renderer.setTransformSTAndTexScaleUniformsForDest(edgeDetectProgram.uniforms);
renderer.setPathColorsUniform(0, edgeDetectProgram.uniforms, 0);
gl.activeTexture(renderContext.gl.TEXTURE1);
gl.bindTexture(renderContext.gl.TEXTURE_2D, this.directTexture);
gl.uniform1i(edgeDetectProgram.uniforms.uPathID, 1);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, renderContext.quadElementsBuffer);
gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0);
// Perform edge masking.
const edgeMaskLineProgram = renderer.renderContext.shaderPrograms.xcaaMultiEdgeMaskLine;
gl.useProgram(edgeMaskLineProgram.program);
this.antialiasLinesWithProgram(renderer, edgeMaskLineProgram);
const edgeMaskCurveProgram = renderer.renderContext.shaderPrograms.xcaaMultiEdgeMaskCurve;
gl.useProgram(edgeMaskCurveProgram.program);
this.antialiasCurvesWithProgram(renderer, edgeMaskCurveProgram);
gl.colorMask(true, true, true, true);
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
}
protected resolveAA(renderer: Renderer) {
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
// Set state for ECAA resolve.
const usedSize = renderer.destUsedSize;
renderContext.gl.bindFramebuffer(renderContext.gl.FRAMEBUFFER, renderer.destFramebuffer);
renderContext.gl.viewport(0, 0, this.destFramebufferSize[0], this.destFramebufferSize[1]);
renderContext.gl.scissor(0, 0, usedSize[0], usedSize[1]);
renderContext.gl.enable(renderContext.gl.SCISSOR_TEST);
renderContext.gl.disable(renderContext.gl.DEPTH_TEST);
renderContext.gl.disable(renderContext.gl.BLEND);
// Resolve.
const resolveProgram = this.getResolveProgram(renderContext);
gl.useProgram(resolveProgram.program);
renderContext.vertexArrayObjectExt.bindVertexArrayOES(this.resolveVAO);
gl.uniform2i(resolveProgram.uniforms.uFramebufferSize,
this.destFramebufferSize[0],
this.destFramebufferSize[1]);
renderContext.gl.activeTexture(renderContext.gl.TEXTURE0);
renderContext.gl.bindTexture(renderContext.gl.TEXTURE_2D, this.aaAlphaTexture);
renderContext.gl.uniform1i(resolveProgram.uniforms.uAAAlpha, 0);
renderContext.gl.uniform2i(resolveProgram.uniforms.uAAAlphaDimensions,
this.supersampledFramebufferSize[0],
this.supersampledFramebufferSize[1]);
renderContext.gl.activeTexture(renderContext.gl.TEXTURE1);
renderContext.gl.bindTexture(renderContext.gl.TEXTURE_2D, this.edgePathIDTexture);
renderContext.gl.uniform1i(resolveProgram.uniforms.uBGFGPathID, 1);
renderer.setPathColorsUniform(0, resolveProgram.uniforms, 2);
renderer.setTransformSTAndTexScaleUniformsForDest(resolveProgram.uniforms);
renderContext.gl.drawElements(renderContext.gl.TRIANGLES,
6,
renderContext.gl.UNSIGNED_BYTE,
0);
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
renderContext.gl.bindFramebuffer(renderContext.gl.FRAMEBUFFER, null);
}
protected setCoverDepthState(renderer: Renderer) {
const renderContext = renderer.renderContext;
renderContext.gl.depthMask(false);
@ -1116,26 +1060,56 @@ export class MCAAMulticolorStrategy extends MCAAStrategy {
renderContext.gl.enable(renderContext.gl.DEPTH_TEST);
}
protected clear(renderer: Renderer) {
protected clearForAA(renderer: Renderer) {
const renderContext = renderer.renderContext;
renderContext.gl.clearColor(0.0, 0.0, 0.0, 0.0);
renderContext.gl.clear(renderContext.gl.COLOR_BUFFER_BIT);
const gl = renderContext.gl;
gl.clearColor(0.0, 0.0, 0.0, 0.0);
gl.clear(gl.COLOR_BUFFER_BIT);
}
protected setAADepthState(renderer: Renderer) {
const renderContext = renderer.renderContext;
renderContext.gl.depthMask(false);
renderContext.gl.depthFunc(renderContext.gl.EQUAL);
renderContext.gl.enable(renderContext.gl.DEPTH_TEST);
const gl = renderContext.gl;
gl.depthMask(false);
gl.depthFunc(gl.EQUAL);
gl.enable(gl.DEPTH_TEST);
}
protected setDepthAndBlendModeForResolve(renderContext: RenderContext): void {
const gl = renderContext.gl;
gl.depthMask(true);
gl.depthFunc(gl.GREATER);
gl.enable(gl.DEPTH_TEST);
gl.blendEquation(gl.FUNC_ADD);
gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE);
gl.enable(gl.BLEND);
}
protected clearForResolve(renderer: Renderer) {}
get directRenderingMode(): DirectRenderingMode {
return 'pathID';
protected setAdditionalStateForResolveIfNecessary(renderer: Renderer,
resolveProgram: PathfinderShaderProgram,
firstFreeTextureUnit: number):
void {
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
gl.activeTexture(gl.TEXTURE0 + firstFreeTextureUnit + 0);
gl.bindTexture(gl.TEXTURE_2D, this.aaDepthTexture);
gl.uniform1i(resolveProgram.uniforms.uAADepth, firstFreeTextureUnit + 0);
renderer.setPathColorsUniform(0, resolveProgram.uniforms, firstFreeTextureUnit + 1);
}
protected get directDepthTexture(): WebGLTexture {
return this._directDepthTexture;
get directRenderingMode(): DirectRenderingMode {
return 'two-pass';
}
protected get directDepthTexture(): WebGLTexture | null {
return null;
}
}

View File

@ -109,6 +109,11 @@ float convertPathIndexToWindowDepthValue(int pathIndex) {
return float(pathIndex) / float(MAX_PATHS);
}
int convertWindowDepthValueToPathIndex(float depthValue) {
float pathIndex = floor(depthValue * float(MAX_PATHS));
return int(pathIndex);
}
bool computeMCAAQuadPosition(out vec2 outPosition,
inout vec2 leftPosition,
inout vec2 rightPosition,
@ -261,6 +266,37 @@ float computeCoverage(vec2 p0, vec2 dp, vec2 center, float winding) {
return abs(area) * winding * 0.5;
}
// FIXME(pcwalton): Combine with `computeCoverage`.
bool isPartiallyCovered(vec2 p0, vec2 dp, vec2 center, float winding) {
// Set some flags.
bool slopeNegative = dp.y < -0.001;
bool slopeZero = abs(dp.y) < 0.001;
// Determine the bounds of this pixel.
vec4 pixelExtents = center.xxyy + vec4(-0.5, 0.5, -0.5, 0.5);
// Set up Liang-Barsky clipping.
vec4 q = pixelExtents - p0.xxyy;
// Use Liang-Barsky to clip to the left and right sides of this pixel.
vec2 t = clamp(q.xy / dp.xx, 0.0, 1.0);
vec2 spanP0 = p0 + dp * t.x, spanP1 = p0 + dp * t.y;
// Likewise, clip to the to the bottom and top.
if (!slopeZero) {
vec2 tVertical = q.zw / dp.yy;
if (slopeNegative)
tVertical.xy = tVertical.yx; // FIXME(pcwalton): Can this be removed?
t = vec2(max(t.x, tVertical.x), min(t.y, tVertical.y));
}
// If the line doesn't pass through this pixel, detect that and bail.
//
// This should be worth a branch because it's very common for fragment blocks to all hit this
// path.
return !(t.x >= t.y || (slopeZero && (p0.y < pixelExtents.z || p0.y > pixelExtents.w)));
}
// https://www.freetype.org/freetype2/docs/reference/ft2-lcd_filtering.html
float lcdFilter(float shadeL2, float shadeL1, float shade0, float shadeR1, float shadeR2) {
return LCD_FILTER_FACTOR_2 * shadeL2 +

View File

@ -1,4 +1,4 @@
// pathfinder/shaders/gles2/xcaa-multi-direct-curve.fs.glsl
// pathfinder/shaders/gles2/xcaa-multi-bg-direct-curve.fs.glsl
//
// Copyright (c) 2017 The Pathfinder Project Developers.
//
@ -12,12 +12,23 @@
precision highp float;
varying vec2 vPathID;
uniform ivec2 uFramebufferSize;
uniform sampler2D uEdgeDepth;
varying vec4 vColor;
varying vec2 vTexCoord;
varying float vSign;
void main() {
vec2 center = floor(gl_FragCoord.xy);
float depth = gl_FragCoord.z;
vec2 texCoord = floor(center) / vec2(uFramebufferSize);
// TODO(pcwalton): Remove this if possible?
if (depth >= texture2D(uEdgeDepth, texCoord).r)
discard;
float side = vTexCoord.x * vTexCoord.x - vTexCoord.y;
float alpha = float(sign(side) == sign(vSign));
gl_FragColor = vec4(vPathID, 0.0, alpha);
gl_FragColor = vec4(vColor.rgb, vColor.a * alpha);
}

View File

@ -1,4 +1,4 @@
// pathfinder/shaders/gles2/xcaa-multi-direct-curve.vs.glsl
// pathfinder/shaders/gles2/xcaa-multi-bg-direct-curve.vs.glsl
//
// Copyright (c) 2017 The Pathfinder Project Developers.
//
@ -12,6 +12,8 @@ precision highp float;
uniform mat4 uTransform;
uniform vec4 uHints;
uniform ivec2 uPathColorsDimensions;
uniform sampler2D uPathColors;
uniform ivec2 uPathTransformDimensions;
uniform sampler2D uPathTransform;
@ -20,7 +22,7 @@ attribute vec2 aTexCoord;
attribute float aPathID;
attribute float aSign;
varying vec2 vPathID;
varying vec4 vColor;
varying vec2 vTexCoord;
varying float vSign;
@ -36,7 +38,7 @@ void main() {
float depth = convertPathIndexToViewportDepthValue(pathID);
gl_Position = vec4(position, depth, 1.0);
vPathID = packPathID(pathID);
vColor = fetchFloat4Data(uPathColors, pathID, uPathColorsDimensions);
vTexCoord = vec2(aTexCoord) / 2.0;
vSign = aSign;
}

View File

@ -0,0 +1,28 @@
// pathfinder/shaders/gles2/xcaa-multi-bg-direct-interior.fs.glsl
//
// Copyright (c) 2017 The Pathfinder Project Developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
precision highp float;
uniform ivec2 uFramebufferSize;
uniform sampler2D uEdgeDepth;
varying vec4 vColor;
void main() {
vec2 center = floor(gl_FragCoord.xy);
float depth = gl_FragCoord.z;
vec2 texCoord = floor(center) / vec2(uFramebufferSize);
// TODO(pcwalton): Remove this if possible?
if (depth >= texture2D(uEdgeDepth, texCoord).r)
discard;
gl_FragColor = vColor;
}

View File

@ -1,4 +1,4 @@
// pathfinder/shaders/gles2/xcaa-multi-direct-interior.vs.glsl
// pathfinder/shaders/gles2/xcaa-multi-bg-direct-interior.vs.glsl
//
// Copyright (c) 2017 The Pathfinder Project Developers.
//
@ -12,13 +12,16 @@ precision highp float;
uniform mat4 uTransform;
uniform vec4 uHints;
uniform ivec2 uPathColorsDimensions;
uniform ivec2 uPathTransformDimensions;
uniform sampler2D uPathColors;
uniform sampler2D uPathTransform;
attribute vec2 aPosition;
attribute float aPathID;
varying vec2 vPathID;
varying vec4 vColor;
varying float vSign;
void main() {
int pathID = int(aPathID);
@ -32,5 +35,5 @@ void main() {
float depth = convertPathIndexToViewportDepthValue(pathID);
gl_Position = vec4(position, depth, 1.0);
vPathID = packPathID(pathID);
vColor = fetchFloat4Data(uPathColors, pathID, uPathColorsDimensions);
}

View File

@ -1,17 +0,0 @@
// pathfinder/shaders/gles2/xcaa-multi-direct-interior.fs.glsl
//
// Copyright (c) 2017 The Pathfinder Project Developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
precision highp float;
varying vec2 vPathID;
void main() {
gl_FragColor = vec4(vPathID, 0.0, 1.0);
}

View File

@ -0,0 +1,56 @@
// pathfinder/shaders/gles2/xcaa-multi-edge-mask-curve.fs.glsl
//
// Copyright (c) 2017 The Pathfinder Project Developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
precision highp float;
varying vec4 vEndpoints;
varying vec2 vControlPoint;
varying float vWinding;
// Solve the equation:
//
// x = p0x + t^2 * (p0x - 2*p1x + p2x) + t*(2*p1x - 2*p0x)
//
// We use the Citardauq Formula to avoid floating point precision issues.
float solveCurveT(float p0x, float p1x, float p2x, float x) {
float a = p0x - 2.0 * p1x + p2x;
float b = 2.0 * p1x - 2.0 * p0x;
float c = p0x - x;
return 2.0 * c / (-b - sqrt(b * b - 4.0 * a * c));
}
void main() {
// Unpack.
vec2 center = gl_FragCoord.xy;
vec2 p0 = vEndpoints.xy, p1 = vEndpoints.zw;
vec2 cp = vControlPoint;
// Compute pixel extents.
vec4 pixelExtents = center.xxyy + vec4(-0.5, 0.5, -0.5, 0.5);
// Clip the curve to the left and right edges to create a line.
//
// TODO(pcwalton): Consider clipping to the bottom and top edges properly too. (I kind of doubt
// it's worth it to do this, though, given that the maximum error doing it this way will always
// be less than a pixel, and it saves a lot of time.)
//
// FIXME(pcwalton): Factor out shared terms to avoid computing them multiple times.
vec2 t = vec2(p0.x < pixelExtents.x ? solveCurveT(p0.x, cp.x, p1.x, pixelExtents.x) : 0.0,
p1.x > pixelExtents.y ? solveCurveT(p0.x, cp.x, p1.x, pixelExtents.y) : 1.0);
vec2 clippedP0 = mix(mix(p0, cp, t.x), mix(cp, p1, t.x), t.x);
vec2 clippedP1 = mix(mix(p0, cp, t.y), mix(cp, p1, t.y), t.y);
// Discard if not edge.
if (!isPartiallyCovered(clippedP0, clippedP1 - clippedP0, center, vWinding))
discard;
gl_FragColor = vec4(1.0);
}

View File

@ -0,0 +1,34 @@
// pathfinder/shaders/gles2/xcaa-multi-edge-mask-line.vs.glsl
//
// Copyright (c) 2017 The Pathfinder Project Developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
precision highp float;
varying vec4 vEndpoints;
varying float vWinding;
void main() {
// Unpack.
vec2 center = gl_FragCoord.xy;
vec2 p0 = vEndpoints.xy, p1 = vEndpoints.zw;
// Set up Liang-Barsky clipping.
vec4 pixelExtents = center.xxyy + vec4(-0.5, 0.5, -0.5, 0.5);
vec2 dp = p1 - p0;
vec4 q = pixelExtents - p0.xxyy;
// Use Liang-Barsky to clip to the left and right sides of this pixel.
vec2 t = clamp(q.xy / dp.xx, 0.0, 1.0);
vec2 spanP0 = p0 + dp * t.x, spanP1 = p0 + dp * t.y;
// Discard if not edge.
if (!isPartiallyCovered(p0, dp, center, vWinding))
discard;
gl_FragColor = vec4(1.0);
}

View File

@ -11,20 +11,17 @@
precision highp float;
uniform ivec2 uPathColorsDimensions;
uniform sampler2D uBGFGPathID;
uniform sampler2D uAAAlpha;
uniform sampler2D uAADepth;
uniform sampler2D uPathColors;
varying vec2 vTexCoord;
void main() {
vec4 packedPathIDsBGFG = texture2D(uBGFGPathID, vTexCoord);
int pathIDBG = unpackPathID(packedPathIDsBGFG.xy);
int pathIDFG = unpackPathID(packedPathIDsBGFG.zw);
vec4 bgColor = fetchFloat4Data(uPathColors, pathIDBG, uPathColorsDimensions);
vec4 fgColor = fetchFloat4Data(uPathColors, pathIDFG, uPathColorsDimensions);
float alpha = clamp(texture2D(uAAAlpha, vTexCoord).r, 0.0, 1.0);
gl_FragColor = mix(bgColor, fgColor, alpha);
//gl_FragColor = vec4(vec3(alpha), 1.0);
//gl_FragColor = bgColor != fgColor ? vec4(1.0, 0.0, 0.0, 1.0) : vec4(1.0);
float edgeDepth = texture2D(uAADepth, vTexCoord).r;
int edgePathID = convertWindowDepthValueToPathIndex(edgeDepth);
vec4 edgeColor = fetchFloat4Data(uPathColors, edgePathID, uPathColorsDimensions);
float edgeAlpha = texture2D(uAAAlpha, vTexCoord).r;
gl_FragColor = vec4(edgeColor.rgb, edgeColor.a * edgeAlpha);
gl_FragDepthEXT = edgeDepth;
}