Use a single-pass pixel-snapping MCAA algorithm instead of a multipass

ECAA algorithm for multicolor SVG.

This is much faster than both Skia and the previous XCAA algorithm
while maintaining slightly higher quality than the latter.

There are a couple of known issues:

* Vertical inflection points of hairlines with very steep slopes can
  become very light or even drop out occasionally. I suspect this is due
  to floating point error.

* Rarely, single columns of pixels can disappear from a mesh. Cause TBD.

Besides these bugs, this technique can be cleaned up and probably made
faster, but it's a sizable improvement as is.
This commit is contained in:
Patrick Walton 2017-12-15 15:41:27 -06:00
parent 5da56fea0b
commit cfe72f486e
22 changed files with 684 additions and 897 deletions

View File

@ -18,7 +18,7 @@ import {DemoView} from './view';
export type AntialiasingStrategyName = 'none' | 'ssaa' | 'xcaa';
export type DirectRenderingMode = 'none' | 'color' | 'color-depth';
export type DirectRenderingMode = 'none' | 'color';
export type SubpixelAAType = 'none' | 'medium';
@ -73,6 +73,9 @@ export abstract class AntialiasingStrategy {
abstract antialiasObject(renderer: Renderer, objectIndex: number): void;
// Called after antialiasing each object.
abstract finishAntialiasingObject(renderer: Renderer, objectIndex: number): void;
// Called before rendering each object directly.
abstract resolveAAForObject(renderer: Renderer, objectIndex: number): void;
// Called after antialiasing.
@ -175,6 +178,8 @@ export class NoAAStrategy extends AntialiasingStrategy {
antialiasObject(renderer: Renderer, objectIndex: number): void {}
finishAntialiasingObject(renderer: Renderer, objectIndex: number): void {}
resolveAAForObject(renderer: Renderer): void {}
resolve(pass: number, renderer: Renderer): void {}

View File

@ -76,6 +76,7 @@ const B_QUAD_FIELD_COUNT: number = B_QUAD_SIZE / UINT32_SIZE;
// FIXME(pcwalton): This duplicates information below in `MESH_TYPES`.
const INDEX_SIZE: number = 4;
const B_QUAD_VERTEX_POSITION_SIZE: number = 12 * 4;
const B_VERTEX_POSITION_SIZE: number = 4 * 2;
const EDGE_BOUNDING_BOX_VERTEX_POSITION_SIZE: number = 4 * 4;
const EDGE_UPPER_LINE_VERTEX_POSITION_SIZE: number = 4 * 4;
@ -86,6 +87,7 @@ const SEGMENT_LINE_SIZE: number = 4 * 4;
const SEGMENT_CURVE_SIZE: number = 4 * 6;
const MESH_TYPES: Meshes<MeshBufferTypeDescriptor> = {
bQuadVertexPositions: { type: 'Float32', size: 12 },
bQuads: { type: 'Uint32', size: B_QUAD_FIELD_COUNT },
bVertexLoopBlinnData: { type: 'Uint32', size: 1 },
bVertexNormals: { type: 'Float32', size: 1 },
@ -112,6 +114,7 @@ const MESH_TYPES: Meshes<MeshBufferTypeDescriptor> = {
};
const BUFFER_TYPES: Meshes<BufferType> = {
bQuadVertexPositions: 'ARRAY_BUFFER',
bQuads: 'ARRAY_BUFFER',
bVertexLoopBlinnData: 'ARRAY_BUFFER',
bVertexNormals: 'ARRAY_BUFFER',
@ -146,6 +149,7 @@ const MESH_LIBRARY_FOURCC: string = 'PFML';
// Must match the FourCCs in `pathfinder_partitioner::mesh_library::MeshLibrary::serialize_into()`.
const BUFFER_TYPE_FOURCCS: BufferTypeFourCCTable = {
bqua: 'bQuads',
bqvp: 'bQuadVertexPositions',
bvlb: 'bVertexLoopBlinnData',
bvno: 'bVertexNormals',
bvpo: 'bVertexPositions',
@ -166,6 +170,7 @@ const BUFFER_TYPE_FOURCCS: BufferTypeFourCCTable = {
// `pathfinder_partitioner::mesh_library::MeshLibrary::serialize_into::write_path_ranges()`.
const PATH_RANGE_TYPE_FOURCCS: PathRangeTypeFourCCTable = {
bqua: 'bQuadPathRanges',
bqvp: 'bQuadVertexPositionPathRanges',
bver: 'bVertexPathRanges',
cvci: 'coverCurveIndexRanges',
cvii: 'coverInteriorIndexRanges',
@ -180,6 +185,7 @@ const PATH_RANGE_TYPE_FOURCCS: PathRangeTypeFourCCTable = {
const RANGE_TO_COUNT_TABLE: RangeToCountTable = {
bQuadPathRanges: 'bQuadCount',
bQuadVertexPositionPathRanges: 'bQuadVertexPositionCount',
bVertexPathRanges: 'bVertexCount',
coverCurveIndexRanges: 'coverCurveCount',
coverInteriorIndexRanges: 'coverInteriorCount',
@ -205,6 +211,7 @@ const RANGE_TO_RANGE_BUFFER_TABLE: RangeToRangeBufferTable = {
const RANGE_KEYS: Array<keyof PathRanges> = [
'bQuadPathRanges',
'bQuadVertexPositionPathRanges',
'bVertexPathRanges',
'coverInteriorIndexRanges',
'coverCurveIndexRanges',
@ -221,6 +228,7 @@ type BufferType = 'ARRAY_BUFFER' | 'ELEMENT_ARRAY_BUFFER';
export interface Meshes<T> {
readonly bQuads: T;
readonly bQuadVertexPositions: T;
readonly bVertexPositions: T;
readonly bVertexLoopBlinnData: T;
readonly bVertexNormals: T;
@ -248,6 +256,7 @@ export interface Meshes<T> {
interface MeshDataCounts {
readonly bQuadCount: number;
readonly bQuadVertexPositionCount: number;
readonly bVertexCount: number;
readonly coverCurveCount: number;
readonly coverInteriorCount: number;
@ -262,6 +271,7 @@ interface MeshDataCounts {
interface PathRanges {
bQuadPathRanges: Range[];
bQuadVertexPositionPathRanges: Range[];
bVertexPathRanges: Range[];
coverInteriorIndexRanges: Range[];
coverCurveIndexRanges: Range[];
@ -276,6 +286,7 @@ interface PathRanges {
export class PathfinderMeshData implements Meshes<ArrayBuffer>, MeshDataCounts, PathRanges {
readonly bQuads: ArrayBuffer;
readonly bQuadVertexPositions: ArrayBuffer;
readonly bVertexPositions: ArrayBuffer;
readonly bVertexLoopBlinnData: ArrayBuffer;
readonly bVertexNormals: ArrayBuffer;
@ -292,6 +303,7 @@ export class PathfinderMeshData implements Meshes<ArrayBuffer>, MeshDataCounts,
readonly segmentCurveNormals: ArrayBuffer;
readonly bQuadCount: number;
readonly bQuadVertexPositionCount: number;
readonly bVertexCount: number;
readonly coverCurveCount: number;
readonly coverInteriorCount: number;
@ -313,6 +325,7 @@ export class PathfinderMeshData implements Meshes<ArrayBuffer>, MeshDataCounts,
segmentLinePathIDs: ArrayBuffer;
bQuadPathRanges: Range[];
bQuadVertexPositionPathRanges: Range[];
bVertexPathRanges: Range[];
coverInteriorIndexRanges: Range[];
coverCurveIndexRanges: Range[];
@ -356,6 +369,8 @@ export class PathfinderMeshData implements Meshes<ArrayBuffer>, MeshDataCounts,
}
this.bQuadCount = this.bQuads.byteLength / B_QUAD_SIZE;
this.bQuadVertexPositionCount = this.bQuadVertexPositions.byteLength /
B_QUAD_VERTEX_POSITION_SIZE;
this.bVertexCount = this.bVertexPositions.byteLength / B_VERTEX_POSITION_SIZE;
this.coverCurveCount = this.coverCurveIndices.byteLength / INDEX_SIZE;
this.coverInteriorCount = this.coverInteriorIndices.byteLength / INDEX_SIZE;
@ -421,6 +436,16 @@ export class PathfinderMeshData implements Meshes<ArrayBuffer>, MeshDataCounts,
const firstBVertexIndex = bVertexCopyResult.originalStartIndex;
const lastBVertexIndex = bVertexCopyResult.originalEndIndex;
// Copy over B-quad vertex positions.
copyVertices(['bQuadVertexPositions'],
'bQuadVertexPositionPathRanges',
expandedArrays,
expandedRanges,
originalBuffers,
originalRanges,
expandedPathID,
originalPathID);
// Copy over edge data.
copyVertices(['edgeBoundingBoxVertexPositions'],
'edgeBoundingBoxRanges',
@ -555,6 +580,7 @@ export class PathfinderMeshData implements Meshes<ArrayBuffer>, MeshDataCounts,
export class PathfinderMeshBuffers implements Meshes<WebGLBuffer>, PathRanges {
readonly bQuads: WebGLBuffer;
readonly bQuadVertexPositions: WebGLBuffer;
readonly bVertexPositions: WebGLBuffer;
readonly bVertexPathIDs: WebGLBuffer;
readonly bVertexLoopBlinnData: WebGLBuffer;
@ -579,6 +605,7 @@ export class PathfinderMeshBuffers implements Meshes<WebGLBuffer>, PathRanges {
readonly segmentCurveNormals: WebGLBuffer;
readonly bQuadPathRanges: Range[];
readonly bQuadVertexPositionPathRanges: Range[];
readonly bVertexPathRanges: Range[];
readonly coverInteriorIndexRanges: Range[];
readonly coverCurveIndexRanges: Range[];

View File

@ -22,7 +22,6 @@ import {CompositingOperation, RenderTaskType} from './render-task';
import {ShaderMap} from './shader-loader';
import {FLOAT32_SIZE, Range, UINT16_SIZE, UINT32_SIZE, unwrapNull, unwrapUndef} from './utils';
import {RenderContext, Timings} from "./view";
import {ECAAMulticolorStrategy} from './xcaa-strategy';
const MAX_PATHS: number = 65535;
@ -161,11 +160,14 @@ export abstract class Renderer {
// Antialias.
antialiasingStrategy.antialiasObject(this, objectIndex);
// Prepare for direct rendering.
antialiasingStrategy.prepareToRenderObject(this, objectIndex);
// Perform post-antialiasing tasks.
antialiasingStrategy.finishAntialiasingObject(this, objectIndex);
// Perform direct rendering (Loop-Blinn).
if (antialiasingStrategy.directRenderingMode !== 'none') {
// Prepare for direct rendering.
antialiasingStrategy.prepareToRenderObject(this, objectIndex);
// Clear.
this.clearForDirectRendering(objectIndex);
@ -420,9 +422,6 @@ export abstract class Renderer {
gl.depthMask(true);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
break;
case 'color-depth':
gl.clear(gl.COLOR_BUFFER_BIT);
break;
case 'none':
// Nothing to do.
break;
@ -496,10 +495,6 @@ export abstract class Renderer {
this.pathTransformBufferTextures[meshIndex]
.ext
.bind(gl, directInteriorProgram.uniforms, 2);
if (renderingMode === 'color-depth') {
const strategy = antialiasingStrategy as ECAAMulticolorStrategy;
strategy.bindEdgeDepthTexture(gl, directInteriorProgram.uniforms, 3);
}
const coverInteriorRange = getMeshIndexRange(meshes.coverInteriorIndexRanges, pathRange);
if (!this.pathIDsAreInstanced) {
gl.drawElements(gl.TRIANGLES,
@ -516,7 +511,7 @@ export abstract class Renderer {
}
// Set up direct curve state.
gl.depthMask(renderingMode === 'color-depth');
gl.depthMask(false);
gl.enable(gl.BLEND);
gl.blendEquation(gl.FUNC_ADD);
gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE);
@ -539,10 +534,6 @@ export abstract class Renderer {
this.setEmboldenAmountUniform(objectIndex, directCurveProgram.uniforms);
this.pathTransformBufferTextures[meshIndex].st.bind(gl, directCurveProgram.uniforms, 1);
this.pathTransformBufferTextures[meshIndex].ext.bind(gl, directCurveProgram.uniforms, 2);
if (renderingMode === 'color-depth') {
const strategy = antialiasingStrategy as ECAAMulticolorStrategy;
strategy.bindEdgeDepthTexture(gl, directCurveProgram.uniforms, 3);
}
const coverCurveRange = getMeshIndexRange(meshes.coverCurveIndexRanges, pathRange);
if (!this.pathIDsAreInstanced) {
gl.drawElements(gl.TRIANGLES,

View File

@ -27,15 +27,10 @@ export interface ShaderMap<T> {
mcaaCover: T;
mcaaLine: T;
mcaaCurve: T;
mcaaMulti: T;
ssaaSubpixelResolve: T;
xcaaMonoResolve: T;
xcaaMonoSubpixelResolve: T;
xcaaMultiDirectCurve: T;
xcaaMultiDirectInterior: T;
xcaaMultiEdgeMaskCurve: T;
xcaaMultiEdgeMaskTransformedCurve: T;
xcaaMultiEdgeMaskLine: T;
xcaaMultiResolve: T;
}
export interface UnlinkedShaderProgram {
@ -57,17 +52,12 @@ export const SHADER_NAMES: Array<keyof ShaderMap<void>> = [
'mcaaCover',
'mcaaLine',
'mcaaCurve',
'mcaaMulti',
'ecaaLine',
'ecaaCurve',
'ecaaTransformedCurve',
'xcaaMonoResolve',
'xcaaMonoSubpixelResolve',
'xcaaMultiDirectCurve',
'xcaaMultiDirectInterior',
'xcaaMultiEdgeMaskCurve',
'xcaaMultiEdgeMaskTransformedCurve',
'xcaaMultiEdgeMaskLine',
'xcaaMultiResolve',
'demo3DDistantGlyph',
'demo3DMonument',
];
@ -133,6 +123,10 @@ const SHADER_URLS: ShaderMap<ShaderProgramURLs> = {
fragment: "/glsl/gles2/xcaa-line.fs.glsl",
vertex: "/glsl/gles2/mcaa-line.vs.glsl",
},
mcaaMulti: {
fragment: "/glsl/gles2/mcaa-multi.fs.glsl",
vertex: "/glsl/gles2/mcaa-multi.vs.glsl",
},
ssaaSubpixelResolve: {
fragment: "/glsl/gles2/ssaa-subpixel-resolve.fs.glsl",
vertex: "/glsl/gles2/blit.vs.glsl",
@ -145,30 +139,6 @@ 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",
},
xcaaMultiDirectInterior: {
fragment: "/glsl/gles2/xcaa-multi-direct-interior.fs.glsl",
vertex: "/glsl/gles2/xcaa-multi-direct-interior.vs.glsl",
},
xcaaMultiEdgeMaskCurve: {
fragment: "/glsl/gles2/xcaa-multi-edge-mask-curve.fs.glsl",
vertex: "/glsl/gles2/ecaa-multi-edge-mask-curve.vs.glsl",
},
xcaaMultiEdgeMaskLine: {
fragment: "/glsl/gles2/xcaa-multi-edge-mask-line.fs.glsl",
vertex: "/glsl/gles2/ecaa-multi-edge-mask-line.vs.glsl",
},
xcaaMultiEdgeMaskTransformedCurve: {
fragment: "/glsl/gles2/xcaa-multi-edge-mask-curve.fs.glsl",
vertex: "/glsl/gles2/ecaa-multi-edge-mask-transformed-curve.vs.glsl",
},
xcaaMultiResolve: {
fragment: "/glsl/gles2/xcaa-multi-resolve.fs.glsl",
vertex: "/glsl/gles2/xcaa-multi-resolve.vs.glsl",
},
};
export interface ShaderProgramSource {

View File

@ -167,6 +167,8 @@ export default class SSAAStrategy extends AntialiasingStrategy {
antialiasObject(renderer: Renderer): void {}
finishAntialiasingObject(renderer: Renderer, objectIndex: number): void {}
resolveAAForObject(renderer: Renderer): void {}
resolve(pass: number, renderer: Renderer): void {

View File

@ -22,7 +22,7 @@ import SSAAStrategy from './ssaa-strategy';
import {SVGLoader} from './svg-loader';
import {Range} from './utils';
import {RenderContext} from './view';
import {ECAAMulticolorStrategy, XCAAStrategy} from './xcaa-strategy';
import {MCAAMulticolorStrategy, XCAAStrategy} from './xcaa-strategy';
interface AntialiasingStrategyTable {
none: typeof NoAAStrategy;
@ -33,7 +33,7 @@ interface AntialiasingStrategyTable {
const ANTIALIASING_STRATEGIES: AntialiasingStrategyTable = {
none: NoAAStrategy,
ssaa: SSAAStrategy,
xcaa: ECAAMulticolorStrategy,
xcaa: MCAAMulticolorStrategy,
};
export abstract class SVGRenderer extends Renderer {
@ -41,6 +41,14 @@ export abstract class SVGRenderer extends Renderer {
camera: OrthographicCamera;
get bgColor(): glmatrix.vec4 {
return glmatrix.vec4.clone([1.0, 1.0, 1.0, 1.0]);
}
get fgColor(): glmatrix.vec4 {
return glmatrix.vec4.clone([0.0, 0.0, 0.0, 1.0]);
}
get usesSTTransform(): boolean {
return this.camera.usesSTTransform;
}
@ -150,14 +158,10 @@ export abstract class SVGRenderer extends Renderer {
}
protected directCurveProgramName(): keyof ShaderMap<void> {
if (this.antialiasingStrategy instanceof XCAAStrategy)
return 'xcaaMultiDirectCurve';
return 'directCurve';
}
protected directInteriorProgramName(): keyof ShaderMap<void> {
if (this.antialiasingStrategy instanceof XCAAStrategy)
return 'xcaaMultiDirectInterior';
return 'directInterior';
}

View File

@ -41,6 +41,7 @@ export abstract class XCAAStrategy extends AntialiasingStrategy {
}
protected abstract get usesDilationTransforms(): boolean;
protected abstract get usesAAFramebuffer(): boolean;
protected pathBoundsBufferTexture: PathfinderBufferTexture;
@ -49,11 +50,11 @@ export abstract class XCAAStrategy extends AntialiasingStrategy {
protected subpixelAA: SubpixelAAType;
protected resolveVAO: WebGLVertexArrayObject;
protected resolveVAO: WebGLVertexArrayObject | null;
protected aaAlphaTexture: WebGLTexture;
protected aaDepthTexture: WebGLTexture;
protected aaFramebuffer: WebGLFramebuffer;
protected aaAlphaTexture: WebGLTexture | null;
protected aaDepthTexture: WebGLTexture | null;
protected aaFramebuffer: WebGLFramebuffer | null;
protected renderTargetColorTextures: WebGLTexture[];
protected renderTargetDepthTextures: WebGLTexture[];
@ -98,23 +99,27 @@ export abstract class XCAAStrategy extends AntialiasingStrategy {
prepareForDirectRendering(renderer: Renderer): void {}
prepareToRenderObject(renderer: Renderer, objectIndex: number): void {
finishAntialiasingObject(renderer: Renderer, objectIndex: number): void {
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
this.initResolveFramebufferForObject(renderer, objectIndex);
const usedSize = this.supersampledUsedSize(renderer);
gl.scissor(0, 0, usedSize[0], usedSize[1]);
gl.enable(gl.SCISSOR_TEST);
if (this.usesAAFramebuffer) {
const usedSize = this.supersampledUsedSize(renderer);
gl.scissor(0, 0, usedSize[0], usedSize[1]);
gl.enable(gl.SCISSOR_TEST);
// Clear out the color and depth textures.
gl.clearColor(1.0, 1.0, 1.0, 1.0);
gl.clearDepth(0.0);
gl.depthMask(true);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
// Clear out the color and depth textures.
gl.clearColor(1.0, 1.0, 1.0, 1.0);
gl.clearDepth(0.0);
gl.depthMask(true);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
}
}
prepareToRenderObject(renderer: Renderer, objectIndex: number): void {}
finishDirectlyRenderingObject(renderer: Renderer, objectIndex: number): void {
// TODO(pcwalton)
}
@ -123,9 +128,6 @@ export abstract class XCAAStrategy extends AntialiasingStrategy {
// Perform early preparations.
this.createPathBoundsBufferTextureForObject(renderer, objectIndex);
// Mask edges if necessary.
this.maskEdgesOfObjectIfNecessary(renderer, objectIndex);
// Set up antialiasing.
this.prepareAA(renderer);
@ -137,6 +139,10 @@ export abstract class XCAAStrategy extends AntialiasingStrategy {
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
const resolveProgram = this.getResolveProgram(renderContext);
if (resolveProgram == null)
return;
// Set state for ECAA resolve.
const usedSize = renderer.destUsedSize;
gl.scissor(0, 0, usedSize[0], usedSize[1]);
@ -147,7 +153,6 @@ export abstract class XCAAStrategy extends AntialiasingStrategy {
this.clearForResolve(renderer);
// Resolve.
const resolveProgram = this.getResolveProgram(renderContext);
gl.useProgram(resolveProgram.program);
renderContext.vertexArrayObjectExt.bindVertexArrayOES(this.resolveVAO);
gl.uniform2i(resolveProgram.uniforms.uFramebufferSize,
@ -257,8 +262,8 @@ export abstract class XCAAStrategy extends AntialiasingStrategy {
void {}
protected abstract clearForAA(renderer: Renderer): void;
protected abstract getResolveProgram(renderContext: RenderContext): PathfinderShaderProgram;
protected abstract maskEdgesOfObjectIfNecessary(renderer: Renderer, objectIndex: number): void;
protected abstract getResolveProgram(renderContext: RenderContext):
PathfinderShaderProgram | null;
protected abstract setAADepthState(renderer: Renderer): void;
protected abstract clearForResolve(renderer: Renderer): void;
@ -295,6 +300,13 @@ export abstract class XCAAStrategy extends AntialiasingStrategy {
}
private initAAAlphaFramebuffer(renderer: Renderer): void {
if (!this.usesAAFramebuffer) {
this.aaAlphaTexture = null;
this.aaDepthTexture = null;
this.aaFramebuffer = null;
return;
}
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
@ -329,10 +341,13 @@ export abstract class XCAAStrategy extends AntialiasingStrategy {
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
const resolveProgram = this.getResolveProgram(renderContext);
if (resolveProgram == null)
return;
this.resolveVAO = renderContext.vertexArrayObjectExt.createVertexArrayOES();
renderContext.vertexArrayObjectExt.bindVertexArrayOES(this.resolveVAO);
const resolveProgram = this.getResolveProgram(renderContext);
gl.useProgram(resolveProgram.program);
renderContext.initQuadVAO(resolveProgram.attributes);
@ -348,15 +363,20 @@ export abstract class XCAAStrategy extends AntialiasingStrategy {
}
}
export abstract class MCAAStrategy extends XCAAStrategy {
private coverVAO: WebGLVertexArrayObject;
private lineVAOs: FastEdgeVAOs;
private curveVAOs: FastEdgeVAOs;
export class MCAAMonochromeStrategy extends XCAAStrategy {
protected coverVAO: WebGLVertexArrayObject | null;
protected lineVAOs: FastEdgeVAOs;
protected curveVAOs: FastEdgeVAOs;
protected get usesDilationTransforms(): boolean {
return true;
}
attachMeshes(renderer: Renderer): void {
super.attachMeshes(renderer);
this.createCoverVAO(renderer);
this.createCoverVAOIfNecessary(renderer);
this.createLineVAOs(renderer);
this.createCurveVAOs(renderer);
}
@ -365,13 +385,58 @@ export abstract class MCAAStrategy extends XCAAStrategy {
super.antialiasObject(renderer, objectIndex);
// Conservatively cover.
this.coverObject(renderer, objectIndex);
this.coverObjectIfNecessary(renderer, objectIndex);
// Antialias.
this.antialiasLinesOfObject(renderer, objectIndex);
this.antialiasCurvesOfObject(renderer, objectIndex);
}
protected get usesAAFramebuffer(): boolean {
return true;
}
protected getResolveProgram(renderContext: RenderContext): PathfinderShaderProgram {
if (this.subpixelAA !== 'none')
return renderContext.shaderPrograms.xcaaMonoSubpixelResolve;
return renderContext.shaderPrograms.xcaaMonoResolve;
}
protected clearForAA(renderer: Renderer): void {
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
gl.clearColor(0.0, 0.0, 0.0, 0.0);
gl.clearDepth(0.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
}
protected setAADepthState(renderer: Renderer): void {
const renderContext = renderer.renderContext;
renderContext.gl.disable(renderContext.gl.DEPTH_TEST);
}
protected clearForResolve(renderer: Renderer): void {
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
gl.clearColor(1.0, 1.0, 1.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
}
protected createCoverVAOIfNecessary(renderer: Renderer): void {
this.coverVAO = renderer.renderContext.vertexArrayObjectExt.createVertexArrayOES();
}
protected setBlendModeForAA(renderer: Renderer): void {
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
gl.blendEquation(gl.FUNC_ADD);
gl.blendFunc(gl.ONE, gl.ONE);
gl.enable(gl.BLEND);
}
protected prepareAA(renderer: Renderer): void {
super.prepareAA(renderer);
@ -415,7 +480,7 @@ export abstract class MCAAStrategy extends XCAAStrategy {
const vao = this.lineVAOs[direction];
renderContext.vertexArrayObjectExt.bindVertexArrayOES(vao);
this.setBlendModeForAA(renderer, direction);
this.setBlendModeForAA(renderer);
gl.uniform1i(uniforms.uWinding, direction === 'upper' ? 1 : 0);
const indexRanges = {
@ -431,6 +496,113 @@ export abstract class MCAAStrategy extends XCAAStrategy {
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
}
protected coverObjectIfNecessary(renderer: Renderer, objectIndex: number): void {
if (renderer.meshes == null || renderer.meshData == null)
return;
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
const pathRange = renderer.pathRangeForObject(objectIndex);
const meshIndex = renderer.meshIndexForObject(objectIndex);
this.initCoverVAOForObject(renderer, objectIndex);
// Conservatively cover.
const coverProgram = renderContext.shaderPrograms.mcaaCover;
gl.useProgram(coverProgram.program);
renderContext.vertexArrayObjectExt.bindVertexArrayOES(this.coverVAO);
this.setAAUniforms(renderer, coverProgram.uniforms, objectIndex);
const bQuadRange = renderer.meshData[meshIndex].bQuadPathRanges;
const count = calculateCountFromIndexRanges(pathRange, bQuadRange);
renderContext.instancedArraysExt
.drawElementsInstancedANGLE(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0, count);
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
}
protected initCurveVAOsForObject(renderer: Renderer, objectIndex: number): void {
if (renderer.meshes == null || renderer.meshData == null)
return;
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
const pathRange = renderer.pathRangeForObject(objectIndex);
const meshIndex = renderer.meshIndexForObject(objectIndex);
const curveProgram = this.curveProgram(renderer);
const attributes = curveProgram.attributes;
for (const direction of DIRECTIONS) {
const vao = this.curveVAOs[direction];
renderContext.vertexArrayObjectExt.bindVertexArrayOES(vao);
const curveVertexPositionsBuffer = {
lower: renderer.meshes[meshIndex].edgeLowerCurveVertexPositions,
upper: renderer.meshes[meshIndex].edgeUpperCurveVertexPositions,
}[direction];
const curvePathIDsBuffer = {
lower: renderer.meshes[meshIndex].edgeLowerCurvePathIDs,
upper: renderer.meshes[meshIndex].edgeUpperCurvePathIDs,
}[direction];
const curveIndexRanges = {
lower: renderer.meshData[meshIndex].edgeLowerCurveIndexRanges,
upper: renderer.meshData[meshIndex].edgeUpperCurveIndexRanges,
}[direction];
const offset = calculateStartFromIndexRanges(pathRange, curveIndexRanges);
gl.useProgram(curveProgram.program);
gl.bindBuffer(gl.ARRAY_BUFFER, renderContext.quadPositionsBuffer);
gl.vertexAttribPointer(attributes.aQuadPosition, 2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, curveVertexPositionsBuffer);
gl.vertexAttribPointer(attributes.aLeftPosition,
2,
gl.FLOAT,
false,
FLOAT32_SIZE * 6,
FLOAT32_SIZE * 6 * offset);
gl.vertexAttribPointer(attributes.aControlPointPosition,
2,
gl.FLOAT,
false,
FLOAT32_SIZE * 6,
FLOAT32_SIZE * 6 * offset + FLOAT32_SIZE * 2);
gl.vertexAttribPointer(attributes.aRightPosition,
2,
gl.FLOAT,
false,
FLOAT32_SIZE * 6,
FLOAT32_SIZE * 6 * offset + FLOAT32_SIZE * 4);
gl.bindBuffer(gl.ARRAY_BUFFER, curvePathIDsBuffer);
gl.vertexAttribPointer(attributes.aPathID,
1,
gl.UNSIGNED_SHORT,
false,
0,
UINT16_SIZE * offset);
gl.enableVertexAttribArray(attributes.aQuadPosition);
gl.enableVertexAttribArray(attributes.aLeftPosition);
gl.enableVertexAttribArray(attributes.aControlPointPosition);
gl.enableVertexAttribArray(attributes.aRightPosition);
gl.enableVertexAttribArray(attributes.aPathID);
renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aLeftPosition, 1);
renderContext.instancedArraysExt
.vertexAttribDivisorANGLE(attributes.aControlPointPosition, 1);
renderContext.instancedArraysExt
.vertexAttribDivisorANGLE(attributes.aRightPosition, 1);
renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aPathID, 1);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, renderContext.quadElementsBuffer);
}
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
}
protected antialiasCurvesOfObjectWithProgram(renderer: Renderer,
objectIndex: number,
curveProgram: PathfinderShaderProgram):
@ -454,7 +626,7 @@ export abstract class MCAAStrategy extends XCAAStrategy {
const vao = this.curveVAOs[direction];
renderContext.vertexArrayObjectExt.bindVertexArrayOES(vao);
this.setBlendModeForAA(renderer, direction);
this.setBlendModeForAA(renderer);
gl.uniform1i(uniforms.uWinding, direction === 'upper' ? 1 : 0);
const indexRanges = {
@ -470,62 +642,12 @@ export abstract class MCAAStrategy extends XCAAStrategy {
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
}
private createCoverVAO(renderer: Renderer): void {
this.coverVAO = renderer.renderContext.vertexArrayObjectExt.createVertexArrayOES();
protected lineProgram(renderer: Renderer): PathfinderShaderProgram {
return renderer.renderContext.shaderPrograms.mcaaLine;
}
private initCoverVAOForObject(renderer: Renderer, objectIndex: number): void {
if (renderer.meshes == null || renderer.meshData == null)
return;
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
const pathRange = renderer.pathRangeForObject(objectIndex);
const meshIndex = renderer.meshIndexForObject(objectIndex);
renderContext.vertexArrayObjectExt.bindVertexArrayOES(this.coverVAO);
const bQuadRanges = renderer.meshData[meshIndex].bQuadPathRanges;
const offset = calculateStartFromIndexRanges(pathRange, bQuadRanges);
const coverProgram = renderContext.shaderPrograms.mcaaCover;
const attributes = coverProgram.attributes;
gl.useProgram(coverProgram.program);
gl.bindBuffer(gl.ARRAY_BUFFER, renderContext.quadPositionsBuffer);
gl.vertexAttribPointer(attributes.aQuadPosition, 2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, renderer.meshes[meshIndex].edgeBoundingBoxVertexPositions);
gl.vertexAttribPointer(attributes.aUpperLeftPosition,
2,
gl.FLOAT,
false,
FLOAT32_SIZE * 4,
FLOAT32_SIZE * 4 * offset);
gl.vertexAttribPointer(attributes.aLowerRightPosition,
2,
gl.FLOAT,
false,
FLOAT32_SIZE * 4,
FLOAT32_SIZE * 4 * offset + FLOAT32_SIZE * 2);
gl.bindBuffer(gl.ARRAY_BUFFER, renderer.meshes[meshIndex].edgeBoundingBoxPathIDs);
gl.vertexAttribPointer(attributes.aPathID,
1,
gl.UNSIGNED_SHORT,
false,
0,
UINT16_SIZE * offset);
gl.enableVertexAttribArray(attributes.aQuadPosition);
gl.enableVertexAttribArray(attributes.aUpperLeftPosition);
gl.enableVertexAttribArray(attributes.aLowerRightPosition);
gl.enableVertexAttribArray(attributes.aPathID);
renderContext.instancedArraysExt
.vertexAttribDivisorANGLE(attributes.aUpperLeftPosition, 1);
renderContext.instancedArraysExt
.vertexAttribDivisorANGLE(attributes.aLowerRightPosition, 1);
renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aPathID, 1);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, renderContext.quadElementsBuffer);
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
protected curveProgram(renderer: Renderer): PathfinderShaderProgram {
return renderer.renderContext.shaderPrograms.mcaaCurve;
}
private createLineVAOs(renderer: Renderer): void {
@ -547,7 +669,7 @@ export abstract class MCAAStrategy extends XCAAStrategy {
const pathRange = renderer.pathRangeForObject(objectIndex);
const meshIndex = renderer.meshIndexForObject(objectIndex);
const lineProgram = renderContext.shaderPrograms.mcaaLine;
const lineProgram = this.lineProgram(renderer);
const attributes = lineProgram.attributes;
for (const direction of DIRECTIONS) {
@ -619,88 +741,11 @@ export abstract class MCAAStrategy extends XCAAStrategy {
this.curveVAOs = vaos as FastEdgeVAOs;
}
private initCurveVAOsForObject(renderer: Renderer, objectIndex: number): void {
if (renderer.meshes == null || renderer.meshData == null)
return;
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
const pathRange = renderer.pathRangeForObject(objectIndex);
const meshIndex = renderer.meshIndexForObject(objectIndex);
const curveProgram = renderContext.shaderPrograms.mcaaCurve;
const attributes = curveProgram.attributes;
for (const direction of DIRECTIONS) {
const vao = this.curveVAOs[direction];
renderContext.vertexArrayObjectExt.bindVertexArrayOES(vao);
const curveVertexPositionsBuffer = {
lower: renderer.meshes[meshIndex].edgeLowerCurveVertexPositions,
upper: renderer.meshes[meshIndex].edgeUpperCurveVertexPositions,
}[direction];
const curvePathIDsBuffer = {
lower: renderer.meshes[meshIndex].edgeLowerCurvePathIDs,
upper: renderer.meshes[meshIndex].edgeUpperCurvePathIDs,
}[direction];
const curveIndexRanges = {
lower: renderer.meshData[meshIndex].edgeLowerCurveIndexRanges,
upper: renderer.meshData[meshIndex].edgeUpperCurveIndexRanges,
}[direction];
const offset = calculateStartFromIndexRanges(pathRange, curveIndexRanges);
gl.useProgram(curveProgram.program);
gl.bindBuffer(gl.ARRAY_BUFFER, renderContext.quadPositionsBuffer);
gl.vertexAttribPointer(attributes.aQuadPosition, 2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, curveVertexPositionsBuffer);
gl.vertexAttribPointer(attributes.aLeftPosition,
2,
gl.FLOAT,
false,
FLOAT32_SIZE * 6,
FLOAT32_SIZE * 6 * offset);
gl.vertexAttribPointer(attributes.aControlPointPosition,
2,
gl.FLOAT,
false,
FLOAT32_SIZE * 6,
FLOAT32_SIZE * 6 * offset + FLOAT32_SIZE * 2);
gl.vertexAttribPointer(attributes.aRightPosition,
2,
gl.FLOAT,
false,
FLOAT32_SIZE * 6,
FLOAT32_SIZE * 6 * offset + FLOAT32_SIZE * 4);
gl.bindBuffer(gl.ARRAY_BUFFER, curvePathIDsBuffer);
gl.vertexAttribPointer(attributes.aPathID,
1,
gl.UNSIGNED_SHORT,
false,
0,
UINT16_SIZE * offset);
gl.enableVertexAttribArray(attributes.aQuadPosition);
gl.enableVertexAttribArray(attributes.aLeftPosition);
gl.enableVertexAttribArray(attributes.aControlPointPosition);
gl.enableVertexAttribArray(attributes.aRightPosition);
gl.enableVertexAttribArray(attributes.aPathID);
renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aLeftPosition, 1);
renderContext.instancedArraysExt
.vertexAttribDivisorANGLE(attributes.aControlPointPosition, 1);
renderContext.instancedArraysExt
.vertexAttribDivisorANGLE(attributes.aRightPosition, 1);
renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aPathID, 1);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, renderContext.quadElementsBuffer);
}
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
get directRenderingMode(): DirectRenderingMode {
return 'none';
}
private coverObject(renderer: Renderer, objectIndex: number): void {
private initCoverVAOForObject(renderer: Renderer, objectIndex: number): void {
if (renderer.meshes == null || renderer.meshData == null)
return;
@ -710,36 +755,55 @@ export abstract class MCAAStrategy extends XCAAStrategy {
const pathRange = renderer.pathRangeForObject(objectIndex);
const meshIndex = renderer.meshIndexForObject(objectIndex);
this.initCoverVAOForObject(renderer, objectIndex);
// Conservatively cover.
const coverProgram = renderContext.shaderPrograms.mcaaCover;
gl.useProgram(coverProgram.program);
renderContext.vertexArrayObjectExt.bindVertexArrayOES(this.coverVAO);
this.setAAUniforms(renderer, coverProgram.uniforms, objectIndex);
const bQuadRange = renderer.meshData[meshIndex].bQuadPathRanges;
const count = calculateCountFromIndexRanges(pathRange, bQuadRange);
const bQuadRanges = renderer.meshData[meshIndex].bQuadPathRanges;
const offset = calculateStartFromIndexRanges(pathRange, bQuadRanges);
const coverProgram = renderContext.shaderPrograms.mcaaCover;
const attributes = coverProgram.attributes;
gl.useProgram(coverProgram.program);
gl.bindBuffer(gl.ARRAY_BUFFER, renderContext.quadPositionsBuffer);
gl.vertexAttribPointer(attributes.aQuadPosition, 2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, renderer.meshes[meshIndex].edgeBoundingBoxVertexPositions);
gl.vertexAttribPointer(attributes.aUpperLeftPosition,
2,
gl.FLOAT,
false,
FLOAT32_SIZE * 4,
FLOAT32_SIZE * 4 * offset);
gl.vertexAttribPointer(attributes.aLowerRightPosition,
2,
gl.FLOAT,
false,
FLOAT32_SIZE * 4,
FLOAT32_SIZE * 4 * offset + FLOAT32_SIZE * 2);
gl.bindBuffer(gl.ARRAY_BUFFER, renderer.meshes[meshIndex].edgeBoundingBoxPathIDs);
gl.vertexAttribPointer(attributes.aPathID,
1,
gl.UNSIGNED_SHORT,
false,
0,
UINT16_SIZE * offset);
gl.enableVertexAttribArray(attributes.aQuadPosition);
gl.enableVertexAttribArray(attributes.aUpperLeftPosition);
gl.enableVertexAttribArray(attributes.aLowerRightPosition);
gl.enableVertexAttribArray(attributes.aPathID);
renderContext.instancedArraysExt
.drawElementsInstancedANGLE(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0, count);
.vertexAttribDivisorANGLE(attributes.aUpperLeftPosition, 1);
renderContext.instancedArraysExt
.vertexAttribDivisorANGLE(attributes.aLowerRightPosition, 1);
renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aPathID, 1);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, renderContext.quadElementsBuffer);
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
}
private setBlendModeForAA(renderer: Renderer, direction: 'upper' | 'lower'): void {
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
gl.blendEquation(gl.FUNC_ADD);
gl.blendFunc(gl.ONE, gl.ONE);
gl.enable(gl.BLEND);
}
private antialiasLinesOfObject(renderer: Renderer, objectIndex: number): void {
const renderContext = renderer.renderContext;
this.setAAState(renderer);
const lineProgram = renderContext.shaderPrograms.mcaaLine;
const lineProgram = this.lineProgram(renderer);
renderContext.gl.useProgram(lineProgram.program);
// FIXME(pcwalton): Refactor.
@ -750,7 +814,7 @@ export abstract class MCAAStrategy extends XCAAStrategy {
const renderContext = renderer.renderContext;
this.setAAState(renderer);
const curveProgram = renderContext.shaderPrograms.mcaaCurve;
const curveProgram = this.curveProgram(renderer);
renderContext.gl.useProgram(curveProgram.program);
this.antialiasCurvesOfObjectWithProgram(renderer, objectIndex, curveProgram);
@ -803,8 +867,6 @@ export abstract class ECAAStrategy extends XCAAStrategy {
return renderContext.shaderPrograms.xcaaMonoResolve;
}
protected maskEdgesOfObjectIfNecessary(renderer: Renderer, objectIndex: number): void {}
protected clearForAA(renderer: Renderer): void {
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
@ -1078,6 +1140,10 @@ export class ECAAMonochromeStrategy extends ECAAStrategy {
return false;
}
protected get usesAAFramebuffer(): boolean {
return true;
}
protected get lineShaderProgramNames(): Array<keyof ShaderMap<void>> {
return ['ecaaLine'];
}
@ -1087,39 +1153,182 @@ export class ECAAMonochromeStrategy extends ECAAStrategy {
}
}
export class MCAAMonochromeStrategy extends MCAAStrategy {
export class MCAAMulticolorStrategy extends XCAAStrategy {
protected vao: WebGLVertexArrayObject;
protected get usesDilationTransforms(): boolean {
return true;
}
protected getResolveProgram(renderContext: RenderContext): PathfinderShaderProgram {
if (this.subpixelAA !== 'none')
return renderContext.shaderPrograms.xcaaMonoSubpixelResolve;
return renderContext.shaderPrograms.xcaaMonoResolve;
attachMeshes(renderer: Renderer): void {
super.attachMeshes(renderer);
const renderContext = renderer.renderContext;
this.vao = renderContext.vertexArrayObjectExt.createVertexArrayOES();
}
protected maskEdgesOfObjectIfNecessary(renderer: Renderer, objectIndex: number): void {}
antialiasObject(renderer: Renderer, objectIndex: number): void {
super.antialiasObject(renderer, objectIndex);
const shaderProgram = this.edgeProgram(renderer);
this.antialiasEdgesOfObjectWithProgram(renderer, objectIndex, shaderProgram);
}
protected get usesAAFramebuffer(): boolean {
return false;
}
protected getResolveProgram(renderContext: RenderContext): PathfinderShaderProgram | null {
return null;
}
protected prepareAA(renderer: Renderer): void {
super.prepareAA(renderer);
this.clearForAA(renderer);
}
protected coverObjectIfNecessary(renderer: Renderer, objectIndex: number): void {}
protected antialiasEdgesOfObjectWithProgram(renderer: Renderer,
objectIndex: number,
shaderProgram: PathfinderShaderProgram):
void {
if (renderer.meshData == null)
return;
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
const pathRange = renderer.pathRangeForObject(objectIndex);
const meshIndex = renderer.meshIndexForObject(objectIndex);
this.initVAOForObject(renderer, objectIndex);
gl.useProgram(shaderProgram.program);
const uniforms = shaderProgram.uniforms;
this.setAAUniforms(renderer, uniforms, objectIndex);
// FIXME(pcwalton): Refactor.
const vao = this.vao;
renderContext.vertexArrayObjectExt.bindVertexArrayOES(vao);
this.setBlendModeForAA(renderer);
const bQuadRanges = renderer.meshData[meshIndex].bQuadPathRanges;
const count = calculateCountFromIndexRanges(pathRange, bQuadRanges);
renderContext.instancedArraysExt
.drawElementsInstancedANGLE(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0, count);
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
}
protected clearForAA(renderer: Renderer): void {
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
gl.clearColor(0.0, 0.0, 0.0, 0.0);
gl.clearColor(1.0, 1.0, 1.0, 1.0);
gl.clearDepth(0.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
}
protected setAADepthState(renderer: Renderer): void {
const renderContext = renderer.renderContext;
renderContext.gl.disable(renderContext.gl.DEPTH_TEST);
const gl = renderContext.gl;
gl.disable(gl.DEPTH_TEST);
}
protected clearForResolve(renderer: Renderer): void {
protected clearForResolve(renderer: Renderer): void {}
protected setBlendModeForAA(renderer: Renderer): void {
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
gl.clearColor(0.0, 0.0, 0.0, 0.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.blendEquation(gl.FUNC_ADD);
gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE);
gl.enable(gl.BLEND);
}
protected setAAUniforms(renderer: Renderer, uniforms: UniformMap, objectIndex: number):
void {
super.setAAUniforms(renderer, uniforms, objectIndex);
renderer.setPathColorsUniform(0, uniforms, 2);
}
protected initVAOForObject(renderer: Renderer, objectIndex: number): void {
if (renderer.meshes == null || renderer.meshData == null)
return;
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
const pathRange = renderer.pathRangeForObject(objectIndex);
const meshIndex = renderer.meshIndexForObject(objectIndex);
const shaderProgram = this.edgeProgram(renderer);
const attributes = shaderProgram.attributes;
// FIXME(pcwalton): Refactor.
const vao = this.vao;
renderContext.vertexArrayObjectExt.bindVertexArrayOES(vao);
const bQuadRanges = renderer.meshData[meshIndex].bQuadPathRanges;
const offset = calculateStartFromIndexRanges(pathRange, bQuadRanges);
gl.useProgram(shaderProgram.program);
gl.bindBuffer(gl.ARRAY_BUFFER, renderContext.quadPositionsBuffer);
gl.vertexAttribPointer(attributes.aQuadPosition, 2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, renderer.meshes[meshIndex].bQuadVertexPositions);
gl.vertexAttribPointer(attributes.aUpperEndpointPositions,
4,
gl.FLOAT,
false,
FLOAT32_SIZE * 12,
FLOAT32_SIZE * 12 * offset);
gl.vertexAttribPointer(attributes.aLowerEndpointPositions,
4,
gl.FLOAT,
false,
FLOAT32_SIZE * 12,
FLOAT32_SIZE * 12 * offset + FLOAT32_SIZE * 4);
gl.vertexAttribPointer(attributes.aControlPointPositions,
4,
gl.FLOAT,
false,
FLOAT32_SIZE * 12,
FLOAT32_SIZE * 12 * offset + FLOAT32_SIZE * 8);
gl.bindBuffer(gl.ARRAY_BUFFER, renderer.meshes[meshIndex].edgeBoundingBoxPathIDs);
gl.vertexAttribPointer(attributes.aPathID,
1,
gl.UNSIGNED_SHORT,
false,
0,
UINT16_SIZE * offset);
gl.enableVertexAttribArray(attributes.aQuadPosition);
gl.enableVertexAttribArray(attributes.aUpperEndpointPositions);
gl.enableVertexAttribArray(attributes.aLowerEndpointPositions);
gl.enableVertexAttribArray(attributes.aControlPointPositions);
gl.enableVertexAttribArray(attributes.aPathID);
renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aPathID, 1);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, renderContext.quadElementsBuffer);
renderContext.instancedArraysExt
.vertexAttribDivisorANGLE(attributes.aUpperEndpointPositions, 1);
renderContext.instancedArraysExt
.vertexAttribDivisorANGLE(attributes.aLowerEndpointPositions, 1);
renderContext.instancedArraysExt
.vertexAttribDivisorANGLE(attributes.aControlPointPositions, 1);
renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aPathID, 1);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, renderContext.quadElementsBuffer);
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
}
protected edgeProgram(renderer: Renderer): PathfinderShaderProgram {
return renderer.renderContext.shaderPrograms.mcaaMulti;
}
get directRenderingMode(): DirectRenderingMode {
@ -1174,6 +1383,10 @@ export class AdaptiveMonochromeXCAAStrategy implements AntialiasingStrategy {
this.getAppropriateStrategy(renderer).prepareForDirectRendering(renderer);
}
finishAntialiasingObject(renderer: Renderer, objectIndex: number): void {
this.getAppropriateStrategy(renderer).finishAntialiasingObject(renderer, objectIndex);
}
prepareToRenderObject(renderer: Renderer, objectIndex: number): void {
this.getAppropriateStrategy(renderer).prepareToRenderObject(renderer, objectIndex);
}
@ -1208,132 +1421,6 @@ export class AdaptiveMonochromeXCAAStrategy implements AntialiasingStrategy {
}
}
export class ECAAMulticolorStrategy extends ECAAStrategy {
protected get usesDilationTransforms(): boolean {
return false;
}
protected get lineShaderProgramNames(): Array<keyof ShaderMap<void>> {
return ['ecaaLine', 'xcaaMultiEdgeMaskLine'];
}
protected get curveShaderProgramNames(): Array<keyof ShaderMap<void>> {
return [
'ecaaCurve',
'ecaaTransformedCurve',
'xcaaMultiEdgeMaskCurve',
'xcaaMultiEdgeMaskTransformedCurve',
];
}
private edgeMaskVAO: WebGLVertexArrayObject;
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);
gl.activeTexture(gl.TEXTURE0 + textureUnit + 1);
gl.bindTexture(gl.TEXTURE_2D, this.aaAlphaTexture);
gl.uniform1i(uniforms.uEdgeAlpha, textureUnit);
}
protected getResolveProgram(renderContext: RenderContext): PathfinderShaderProgram {
return renderContext.shaderPrograms.xcaaMultiResolve;
}
protected initDirectFramebufferIfNecessary(renderer: Renderer): void {}
protected maskEdgesOfObjectIfNecessary(renderer: Renderer, objectIndex: number): void {
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
// Set state for edge masking.
gl.bindFramebuffer(gl.FRAMEBUFFER, this.aaFramebuffer);
gl.viewport(0,
0,
this.supersampledFramebufferSize[0],
this.supersampledFramebufferSize[1]);
gl.colorMask(true, true, true, true);
gl.depthMask(true);
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 masking.
gl.colorMask(false, false, false, false);
this.antialiasLinesOfObjectWithProgram(renderer, objectIndex, 'xcaaMultiEdgeMaskLine');
this.antialiasCurvesOfObjectWithPrograms(renderer,
objectIndex,
'xcaaMultiEdgeMaskCurve',
'xcaaMultiEdgeMaskTransformedCurve');
gl.colorMask(true, true, true, true);
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
}
protected setCoverDepthState(renderer: Renderer): void {
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
gl.depthMask(false);
gl.depthFunc(gl.EQUAL);
gl.enable(gl.DEPTH_TEST);
}
protected clearForAA(renderer: Renderer): void {}
protected setAADepthState(renderer: Renderer): void {
const renderContext = renderer.renderContext;
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): void {}
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);
}
get directRenderingMode(): DirectRenderingMode {
return 'color-depth';
}
protected get directDepthTexture(): WebGLTexture | null {
return null;
}
}
function calculateStartFromIndexRanges(pathRange: Range, indexRanges: Range[]): number {
return indexRanges.length === 0 ? 0 : indexRanges[pathRange.start - 1].start;
}

View File

@ -24,6 +24,8 @@ use {BQuad, BVertexLoopBlinnData};
pub struct MeshLibrary {
pub path_ranges: Vec<PathRanges>,
pub b_quads: Vec<BQuad>,
// FIXME(pcwalton): Merge with `b_vertex_positions` below.
pub b_quad_vertex_positions: Vec<Point2D<f32>>,
pub b_vertex_positions: Vec<Point2D<f32>>,
pub b_vertex_loop_blinn_data: Vec<BVertexLoopBlinnData>,
pub b_vertex_normals: Vec<f32>,
@ -39,6 +41,7 @@ impl MeshLibrary {
MeshLibrary {
path_ranges: vec![],
b_quads: vec![],
b_quad_vertex_positions: vec![],
b_vertex_positions: vec![],
b_vertex_loop_blinn_data: vec![],
b_vertex_normals: vec![],
@ -52,6 +55,7 @@ impl MeshLibrary {
pub fn clear(&mut self) {
self.path_ranges.clear();
self.b_quads.clear();
self.b_quad_vertex_positions.clear();
self.b_vertex_positions.clear();
self.b_vertex_loop_blinn_data.clear();
self.b_vertex_normals.clear();
@ -90,6 +94,13 @@ impl MeshLibrary {
let lower_right_position =
&self.b_vertex_positions[b_quad.lower_right_vertex_index as usize];
self.b_quad_vertex_positions.extend_from_slice(&[
*upper_left_position,
*upper_right_position,
*lower_left_position,
*lower_right_position,
]);
let upper_left_bounding_box_position =
Point2D::new(upper_left_position.x,
f32::max(upper_left_position.y, upper_right_position.y));
@ -103,6 +114,7 @@ impl MeshLibrary {
});
if b_quad.upper_control_point_vertex_index == u32::MAX {
self.b_quad_vertex_positions.push(Point2D::zero());
self.edge_data.upper_line_vertex_positions.push(EdgeLineVertexPositions {
left: *upper_left_position,
right: *upper_right_position,
@ -110,6 +122,7 @@ impl MeshLibrary {
} else {
let upper_control_point_position =
&self.b_vertex_positions[b_quad.upper_control_point_vertex_index as usize];
self.b_quad_vertex_positions.push(*upper_control_point_position);
self.edge_data.upper_curve_vertex_positions.push(EdgeCurveVertexPositions {
left: *upper_left_position,
control_point: *upper_control_point_position,
@ -118,6 +131,7 @@ impl MeshLibrary {
}
if b_quad.lower_control_point_vertex_index == u32::MAX {
self.b_quad_vertex_positions.push(Point2D::zero());
self.edge_data.lower_line_vertex_positions.push(EdgeLineVertexPositions {
left: *lower_left_position,
right: *lower_right_position,
@ -125,6 +139,7 @@ impl MeshLibrary {
} else {
let lower_control_point_position =
&self.b_vertex_positions[b_quad.lower_control_point_vertex_index as usize];
self.b_quad_vertex_positions.push(*lower_control_point_position);
self.edge_data.lower_curve_vertex_positions.push(EdgeCurveVertexPositions {
left: *lower_left_position,
control_point: *lower_control_point_position,
@ -205,6 +220,7 @@ impl MeshLibrary {
try!(write_chunk(writer, b"prng", |writer| write_path_ranges(writer, &self.path_ranges)));
try!(write_simple_chunk(writer, b"bqua", &self.b_quads));
try!(write_simple_chunk(writer, b"bqvp", &self.b_quad_vertex_positions));
try!(write_simple_chunk(writer, b"bvpo", &self.b_vertex_positions));
try!(write_simple_chunk(writer, b"bvlb", &self.b_vertex_loop_blinn_data));
try!(write_simple_chunk(writer, b"bvno", &self.b_vertex_normals));
@ -255,6 +271,10 @@ impl MeshLibrary {
fn write_path_ranges<W>(writer: &mut W, path_ranges: &[PathRanges]) -> io::Result<()>
where W: Write + Seek {
try!(write_path_range(writer, b"bqua", path_ranges, |ranges| &ranges.b_quads));
try!(write_path_range(writer,
b"bqvp",
path_ranges,
|ranges| &ranges.b_quad_vertex_positions));
try!(write_path_range(writer, b"bver", path_ranges, |ranges| &ranges.b_vertices));
try!(write_path_range(writer,
b"cvii",
@ -309,6 +329,7 @@ impl MeshLibrary {
pub(crate) fn snapshot_lengths(&self) -> MeshLibraryLengths {
MeshLibraryLengths {
b_quads: self.b_quads.len() as u32,
b_quad_vertex_positions: self.b_quad_vertex_positions.len() as u32,
b_vertices: self.b_vertex_positions.len() as u32,
cover_interior_indices: self.cover_indices.interior_indices.len() as u32,
cover_curve_indices: self.cover_indices.curve_indices.len() as u32,
@ -344,6 +365,7 @@ impl MeshLibraryCoverIndices {
pub(crate) struct MeshLibraryLengths {
b_quads: u32,
b_quad_vertex_positions: u32,
b_vertices: u32,
cover_interior_indices: u32,
cover_curve_indices: u32,
@ -357,6 +379,7 @@ pub(crate) struct MeshLibraryLengths {
#[derive(Clone, Debug)]
pub struct PathRanges {
pub b_quads: Range<u32>,
pub b_quad_vertex_positions: Range<u32>,
pub b_vertices: Range<u32>,
pub cover_interior_indices: Range<u32>,
pub cover_curve_indices: Range<u32>,
@ -373,6 +396,7 @@ impl PathRanges {
fn new() -> PathRanges {
PathRanges {
b_quads: 0..0,
b_quad_vertex_positions: 0..0,
b_vertices: 0..0,
cover_interior_indices: 0..0,
cover_curve_indices: 0..0,
@ -390,6 +414,7 @@ impl PathRanges {
start: &MeshLibraryLengths,
end: &MeshLibraryLengths) {
self.b_quads = start.b_quads..end.b_quads;
self.b_quad_vertex_positions = start.b_quad_vertex_positions..end.b_quad_vertex_positions;
self.b_vertices = start.b_vertices..end.b_vertices;
self.cover_interior_indices = start.cover_interior_indices..end.cover_interior_indices;
self.cover_curve_indices = start.cover_curve_indices..end.cover_curve_indices;

View File

@ -131,12 +131,39 @@ vec2 computeMCAAPosition(vec2 position,
vec4 localTransformST,
vec4 globalTransformST,
ivec2 framebufferSize) {
if (position == vec2(0.0))
return position;
position = hintPosition(position, hints);
position = transformVertexPositionST(position, localTransformST);
position = transformVertexPositionST(position, globalTransformST);
return convertClipToScreenSpace(position, framebufferSize);
}
vec2 computeMCAASnappedPosition(vec2 position,
vec4 hints,
vec4 localTransformST,
vec4 globalTransformST,
ivec2 framebufferSize,
float tanTheta) {
position = hintPosition(position, hints);
position = transformVertexPositionST(position, localTransformST);
position = transformVertexPositionST(position, globalTransformST);
position = convertClipToScreenSpace(position, framebufferSize);
//position.x = abs(mod(position.x, 1.0)) < 0.5 ? floor(position.x) : ceil(position.x);
float xNudge = fract(position.x);
if (xNudge < 0.5)
xNudge = -xNudge;
else
xNudge = 1.0 - xNudge;
position.x += xNudge;
position.y += xNudge * tanTheta;
return position;
}
bool computeMCAAQuadPosition(out vec2 outPosition,
inout vec2 leftPosition,
inout vec2 rightPosition,
@ -440,6 +467,13 @@ vec2 solveCurveT(float p0x, float p1x, float p2x, vec2 x) {
return 2.0 * c / (-b - sqrt(b * b - 4.0 * a * c));
}
vec2 solveCurveT1(float p0x, float p1x, float p2x, vec2 x) {
float a = p0x - 2.0 * p1x + p2x;
float b = 2.0 * p1x - 2.0 * p0x;
vec2 c = p0x - x;
return (-b + sqrt(b * b - 4.0 * a * c)) / (2.0 * a);
}
// 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,77 +0,0 @@
// pathfinder/shaders/gles2/ecaa-multi-edge-mask-curve.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;
uniform mat4 uTransform;
uniform ivec2 uFramebufferSize;
uniform ivec2 uPathTransformSTDimensions;
uniform sampler2D uPathTransformST;
uniform ivec2 uPathTransformExtDimensions;
uniform sampler2D uPathTransformExt;
attribute vec2 aQuadPosition;
attribute vec2 aLeftPosition;
attribute vec2 aControlPointPosition;
attribute vec2 aRightPosition;
attribute float aPathID;
varying vec4 vEndpoints;
varying vec2 vControlPoint;
void main() {
vec2 leftPosition = aLeftPosition;
vec2 controlPointPosition = aControlPointPosition;
vec2 rightPosition = aRightPosition;
int pathID = int(aPathID);
vec2 pathTransformExt;
vec4 pathTransformST = fetchPathAffineTransform(pathTransformExt,
uPathTransformST,
uPathTransformSTDimensions,
uPathTransformExt,
uPathTransformExtDimensions,
pathID);
// Transform the points.
leftPosition = transformECAAPositionToScreenSpace(leftPosition,
pathTransformST,
pathTransformExt,
uTransform,
uFramebufferSize);
rightPosition = transformECAAPositionToScreenSpace(rightPosition,
pathTransformST,
pathTransformExt,
uTransform,
uFramebufferSize);
controlPointPosition = transformECAAPositionToScreenSpace(controlPointPosition,
pathTransformST,
pathTransformExt,
uTransform,
uFramebufferSize);
float winding = computeECAAWinding(leftPosition, rightPosition);
if (winding == 0.0) {
gl_Position = vec4(0.0);
return;
}
vec4 extents = vec4(leftPosition.x,
min(leftPosition.y, rightPosition.y),
rightPosition.x,
max(leftPosition.y, rightPosition.y));
vec2 position = computeXCAAClipSpaceQuadPosition(extents, aQuadPosition, uFramebufferSize);
float depth = convertPathIndexToViewportDepthValue(pathID);
gl_Position = vec4(position, depth, 1.0);
vEndpoints = vec4(leftPosition, rightPosition);
vControlPoint = controlPointPosition;
}

View File

@ -1,69 +0,0 @@
// pathfinder/shaders/gles2/ecaa-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;
uniform mat4 uTransform;
uniform vec4 uHints;
uniform ivec2 uFramebufferSize;
uniform ivec2 uPathTransformSTDimensions;
uniform sampler2D uPathTransformST;
uniform ivec2 uPathTransformExtDimensions;
uniform sampler2D uPathTransformExt;
attribute vec2 aQuadPosition;
attribute vec2 aLeftPosition;
attribute vec2 aRightPosition;
attribute float aPathID;
varying vec4 vEndpoints;
void main() {
vec2 leftPosition = aLeftPosition;
vec2 rightPosition = aRightPosition;
int pathID = int(aPathID);
vec2 pathTransformExt;
vec4 pathTransformST = fetchPathAffineTransform(pathTransformExt,
uPathTransformST,
uPathTransformSTDimensions,
uPathTransformExt,
uPathTransformExtDimensions,
pathID);
// Transform the points.
leftPosition = transformECAAPositionToScreenSpace(leftPosition,
pathTransformST,
pathTransformExt,
uTransform,
uFramebufferSize);
rightPosition = transformECAAPositionToScreenSpace(rightPosition,
pathTransformST,
pathTransformExt,
uTransform,
uFramebufferSize);
float winding = computeECAAWinding(leftPosition, rightPosition);
if (winding == 0.0) {
gl_Position = vec4(0.0);
return;
}
vec4 extents = vec4(min(leftPosition.x, rightPosition.x),
min(leftPosition.y, rightPosition.y),
max(rightPosition.x, rightPosition.x),
max(leftPosition.y, rightPosition.y));
vec2 position = computeXCAAClipSpaceQuadPosition(extents, aQuadPosition, uFramebufferSize);
float depth = convertPathIndexToViewportDepthValue(pathID);
gl_Position = vec4(position, depth, 1.0);
vEndpoints = vec4(leftPosition, rightPosition);
}

View File

@ -1,83 +0,0 @@
// pathfinder/shaders/gles2/ecaa-multi-edge-mask-transformed-curve.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;
uniform mat4 uTransform;
uniform ivec2 uFramebufferSize;
uniform ivec2 uPathTransformSTDimensions;
uniform sampler2D uPathTransformST;
uniform ivec2 uPathTransformExtDimensions;
uniform sampler2D uPathTransformExt;
uniform int uPassIndex;
attribute vec2 aQuadPosition;
attribute vec2 aLeftPosition;
attribute vec2 aControlPointPosition;
attribute vec2 aRightPosition;
attribute float aPathID;
varying vec4 vEndpoints;
varying vec2 vControlPoint;
void main() {
vec2 leftPosition = aLeftPosition;
vec2 controlPointPosition = aControlPointPosition;
vec2 rightPosition = aRightPosition;
int pathID = int(aPathID);
vec2 pathTransformExt;
vec4 pathTransformST = fetchPathAffineTransform(pathTransformExt,
uPathTransformST,
uPathTransformSTDimensions,
uPathTransformExt,
uPathTransformExtDimensions,
pathID);
// Transform the points.
leftPosition = transformECAAPositionToScreenSpace(leftPosition,
pathTransformST,
pathTransformExt,
uTransform,
uFramebufferSize);
rightPosition = transformECAAPositionToScreenSpace(rightPosition,
pathTransformST,
pathTransformExt,
uTransform,
uFramebufferSize);
controlPointPosition = transformECAAPositionToScreenSpace(controlPointPosition,
pathTransformST,
pathTransformExt,
uTransform,
uFramebufferSize);
float winding;
vec3 leftTopRightEdges;
if (!splitCurveAndComputeECAAWinding(winding,
leftTopRightEdges,
leftPosition,
rightPosition,
controlPointPosition,
uPassIndex)) {
gl_Position = vec4(0.0);
return;
}
vec4 extents = vec4(leftTopRightEdges,
max(max(leftPosition.y, rightPosition.y), controlPointPosition.y));
vec2 position = computeXCAAClipSpaceQuadPosition(extents, aQuadPosition, uFramebufferSize);
float depth = convertPathIndexToViewportDepthValue(pathID);
gl_Position = vec4(position, depth, 1.0);
vEndpoints = vec4(leftPosition, rightPosition);
vControlPoint = controlPointPosition;
}

View File

@ -0,0 +1,58 @@
// pathfinder/shaders/gles2/mcaa-multi.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 vUpperEndpoints;
varying vec4 vLowerEndpoints;
varying vec4 vControlPoints;
varying vec4 vColor;
float computeCoverageForSide(vec2 p0, vec2 cp, vec2 p1, float winding) {
// Compute pixel extents.
vec2 pixelCenter = gl_FragCoord.xy;
vec2 pixelColumnBounds = pixelCenter.xx + vec2(-0.5, 0.5);
vec2 clippedP0, clippedDP;
if (cp == vec2(0.0)) {
vec4 p0DPX = clipLineToPixelColumn(p0, p1 - p0, pixelCenter.x);
clippedP0 = p0DPX.xy;
clippedDP = p0DPX.zw;
} else {
// Clip the curve to the left and right edges to create a line.
vec2 t = solveCurveT(p0.x, cp.x, p1.x, pixelColumnBounds);
// Handle endpoints properly. These tests are negated to handle NaNs.
if (!(p0.x < pixelColumnBounds.x))
t.x = 0.0;
if (!(p1.x > pixelColumnBounds.y))
t.y = 1.0;
clippedP0 = mix(mix(p0, cp, t.x), mix(cp, p1, t.x), t.x);
clippedDP = mix(mix(p0, cp, t.y), mix(cp, p1, t.y), t.y) - clippedP0;
}
return computeCoverage(clippedP0, clippedDP, pixelCenter.y, winding);
}
void main() {
float alpha = computeCoverageForSide(vLowerEndpoints.xy,
vControlPoints.zw,
vLowerEndpoints.zw,
-1.0);
alpha += computeCoverageForSide(vUpperEndpoints.xy,
vControlPoints.xy,
vUpperEndpoints.zw,
1.0);
// Compute area.
gl_FragColor = vec4(vColor.rgb, vColor.a * alpha);
}

View File

@ -0,0 +1,101 @@
// pathfinder/shaders/gles2/mcaa-multi.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;
uniform vec4 uTransformST;
uniform vec4 uHints;
uniform ivec2 uFramebufferSize;
uniform ivec2 uPathTransformSTDimensions;
uniform sampler2D uPathTransformST;
uniform ivec2 uPathColorsDimensions;
uniform sampler2D uPathColors;
attribute vec2 aQuadPosition;
attribute vec4 aUpperEndpointPositions;
attribute vec4 aLowerEndpointPositions;
attribute vec4 aControlPointPositions;
attribute float aPathID;
varying vec4 vUpperEndpoints;
varying vec4 vLowerEndpoints;
varying vec4 vControlPoints;
varying vec4 vColor;
void main() {
vec2 tlPosition = aUpperEndpointPositions.xy;
vec2 tcPosition = aControlPointPositions.xy;
vec2 trPosition = aUpperEndpointPositions.zw;
vec2 blPosition = aLowerEndpointPositions.xy;
vec2 bcPosition = aControlPointPositions.zw;
vec2 brPosition = aLowerEndpointPositions.zw;
vec2 quadPosition = aQuadPosition;
int pathID = int(aPathID);
vec4 transformST = fetchFloat4Data(uPathTransformST, pathID, uPathTransformSTDimensions);
vec4 color = fetchFloat4Data(uPathColors, pathID, uPathColorsDimensions);
vec2 topVector = trPosition - tlPosition, bottomVector = brPosition - blPosition;
float topTanTheta = topVector.y / topVector.x;
float bottomTanTheta = bottomVector.y / bottomVector.x;
// Transform the points, and compute the position of this vertex.
tlPosition = computeMCAASnappedPosition(tlPosition,
uHints,
transformST,
uTransformST,
uFramebufferSize,
topTanTheta);
trPosition = computeMCAASnappedPosition(trPosition,
uHints,
transformST,
uTransformST,
uFramebufferSize,
topTanTheta);
tcPosition = computeMCAAPosition(tcPosition,
uHints,
transformST,
uTransformST,
uFramebufferSize);
blPosition = computeMCAASnappedPosition(blPosition,
uHints,
transformST,
uTransformST,
uFramebufferSize,
bottomTanTheta);
brPosition = computeMCAASnappedPosition(brPosition,
uHints,
transformST,
uTransformST,
uFramebufferSize,
bottomTanTheta);
bcPosition = computeMCAAPosition(bcPosition,
uHints,
transformST,
uTransformST,
uFramebufferSize);
float depth = convertPathIndexToViewportDepthValue(pathID);
vec2 position;
position.x = mix(tlPosition.x, brPosition.x, quadPosition.x);
if (quadPosition.y < 0.5)
position.y = floor(min(tlPosition.y, trPosition.y));
else
position.y = ceil(max(blPosition.y, brPosition.y));
position = convertScreenToClipSpace(position, uFramebufferSize);
gl_Position = vec4(position, depth, 1.0);
vUpperEndpoints = vec4(tlPosition, trPosition);
vLowerEndpoints = vec4(blPosition, brPosition);
vControlPoints = vec4(tcPosition, bcPosition);
vColor = color;
}

View File

@ -1,36 +0,0 @@
// pathfinder/shaders/gles2/xcaa-multi-direct-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.
// This shader implements the quadratic Loop-Blinn formulation.
precision highp float;
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): Get back early Z somehow?
if (depth == texture2D(uEdgeDepth, texCoord).r)
discard;
float side = vTexCoord.x * vTexCoord.x - vTexCoord.y;
if (sign(side) != sign(vSign))
discard;
gl_FragColor = vColor;
}

View File

@ -1,55 +0,0 @@
// pathfinder/shaders/gles2/xcaa-multi-direct-curve.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;
uniform mat4 uTransform;
uniform vec4 uHints;
uniform vec2 uEmboldenAmount;
uniform ivec2 uPathColorsDimensions;
uniform sampler2D uPathColors;
uniform ivec2 uPathTransformSTDimensions;
uniform sampler2D uPathTransformST;
uniform ivec2 uPathTransformExtDimensions;
uniform sampler2D uPathTransformExt;
attribute vec2 aPosition;
attribute vec2 aTexCoord;
attribute float aPathID;
attribute float aSign;
attribute float aNormalAngle;
varying vec4 vColor;
varying vec2 vTexCoord;
varying float vSign;
void main() {
int pathID = int(aPathID);
vec2 pathTransformExt;
vec4 pathTransformST = fetchPathAffineTransform(pathTransformExt,
uPathTransformST,
uPathTransformSTDimensions,
uPathTransformExt,
uPathTransformExtDimensions,
pathID);
vec2 position = dilatePosition(aPosition, aNormalAngle, uEmboldenAmount);
position = hintPosition(position, uHints);
position = transformVertexPositionAffine(position, pathTransformST, pathTransformExt);
position = transformVertexPosition(position, uTransform);
float depth = convertPathIndexToViewportDepthValue(pathID);
gl_Position = vec4(position, depth, 1.0);
vColor = fetchFloat4Data(uPathColors, pathID, uPathColorsDimensions);
vTexCoord = vec2(aTexCoord) / 2.0;
vSign = aSign;
}

View File

@ -1,29 +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;
uniform ivec2 uFramebufferSize;
uniform sampler2D uEdgeAlpha;
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);
vec4 color = vColor;
if (depth == texture2D(uEdgeDepth, texCoord).r)
color.a = texture2D(uEdgeAlpha, texCoord).r;
gl_FragColor = vColor;
}

View File

@ -1,50 +0,0 @@
// pathfinder/shaders/gles2/xcaa-multi-direct-interior.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;
uniform mat4 uTransform;
uniform vec4 uHints;
uniform vec2 uEmboldenAmount;
uniform ivec2 uPathColorsDimensions;
uniform sampler2D uPathColors;
uniform ivec2 uPathTransformSTDimensions;
uniform sampler2D uPathTransformST;
uniform ivec2 uPathTransformExtDimensions;
uniform sampler2D uPathTransformExt;
attribute vec2 aPosition;
attribute float aPathID;
attribute float aNormalAngle;
varying vec4 vColor;
varying float vSign;
void main() {
int pathID = int(aPathID);
vec2 pathTransformExt;
vec4 pathTransformST = fetchPathAffineTransform(pathTransformExt,
uPathTransformST,
uPathTransformSTDimensions,
uPathTransformExt,
uPathTransformExtDimensions,
pathID);
vec2 position = dilatePosition(aPosition, aNormalAngle, uEmboldenAmount);
position = hintPosition(position, uHints);
position = transformVertexPositionAffine(position, pathTransformST, pathTransformExt);
position = transformVertexPosition(position, uTransform);
float depth = convertPathIndexToViewportDepthValue(pathID);
gl_Position = vec4(position, depth, 1.0);
vColor = fetchFloat4Data(uPathColors, pathID, uPathColorsDimensions);
}

View File

@ -1,41 +0,0 @@
// 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;
void main() {
// Unpack.
vec2 pixelCenter = gl_FragCoord.xy;
vec2 p0 = vEndpoints.xy, p1 = vEndpoints.zw;
vec2 cp = vControlPoint;
// Clip to left and right pixel boundaries.
vec2 pixelColumnBounds = pixelCenter.xx + vec2(-0.5, 0.5);
// Clip the curve to the left and right edges to create a line.
vec2 t = solveCurveT(p0.x, cp.x, p1.x, pixelColumnBounds);
// Handle endpoints properly. These tests are negated to handle NaNs.
if (!(p0.x < pixelColumnBounds.x))
t.x = 0.0;
if (!(p1.x > pixelColumnBounds.y))
t.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, pixelCenter.y))
discard;
gl_FragColor = vec4(1.0);
}

View File

@ -1,28 +0,0 @@
// 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;
void main() {
// Unpack.
vec2 pixelCenter = gl_FragCoord.xy;
vec2 p0 = vEndpoints.xy, p1 = vEndpoints.zw;
// Clip to left and right pixel boundaries.
vec2 dP = p1 - p0;
vec4 p0DPX = clipLineToPixelColumn(p0, dP, pixelCenter.x);
// Discard if not edge.
if (!isPartiallyCovered(p0DPX.xy, p0DPX.zw, pixelCenter.y))
discard;
gl_FragColor = vec4(1.0);
}

View File

@ -1,27 +0,0 @@
// pathfinder/shaders/gles2/xcaa-multi-resolve.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 uPathColorsDimensions;
uniform sampler2D uAAAlpha;
uniform sampler2D uAADepth;
uniform sampler2D uPathColors;
varying vec2 vTexCoord;
void main() {
float edgeDepth = texture2D(uAADepth, vTexCoord).r;
int edgePathID = convertWindowDepthValueToPathIndex(edgeDepth);
vec4 edgeColor = fetchFloat4Data(uPathColors, edgePathID, uPathColorsDimensions);
float edgeAlpha = abs(texture2D(uAAAlpha, vTexCoord).r);
gl_FragColor = vec4(edgeColor.rgb, edgeColor.a * edgeAlpha);
gl_FragDepthEXT = edgeDepth;
}

View File

@ -1,22 +0,0 @@
// pathfinder/shaders/gles2/xcaa-multi-resolve.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;
attribute vec2 aPosition;
attribute vec2 aTexCoord;
varying vec2 vTexCoord;
void main() {
float depth = convertPathIndexToViewportDepthValue(0);
gl_Position = vec4(mix(vec2(-1.0), vec2(1.0), aPosition), depth, 1.0);
vTexCoord = aTexCoord;
}