Replace the MCAA shader with an extended Loop-Blinn approach and an

approximation of area based on approximate first-order line distance.

This enables support for full affine transforms in MCAA. It also greatly
simplifies the shader and reduces the amount of work that the GPU needs
to do for fragment shading.

Experimental testing has shown the area approximation to be accurate to
about 4%.
This commit is contained in:
Patrick Walton 2018-01-30 09:34:34 -08:00
parent 5bd68dec65
commit 5879d9778d
9 changed files with 483 additions and 360 deletions

View File

@ -1,6 +1,6 @@
// pathfinder/client/src/meshes.ts // pathfinder/client/src/meshes.ts
// //
// Copyright © 2017 The Pathfinder Project Developers. // Copyright © 2018 The Pathfinder Project Developers.
// //
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
@ -87,6 +87,8 @@ const SEGMENT_LINE_SIZE: number = 4 * 4;
const SEGMENT_CURVE_SIZE: number = 4 * 6; const SEGMENT_CURVE_SIZE: number = 4 * 6;
const MESH_TYPES: Meshes<MeshBufferTypeDescriptor> = { const MESH_TYPES: Meshes<MeshBufferTypeDescriptor> = {
bBoxPathIDs: { type: 'Uint16', size: 1 },
bBoxes: { type: 'Float32', size: 20 },
bQuadVertexInteriorIndices: { type: 'Uint32', size: 1 }, bQuadVertexInteriorIndices: { type: 'Uint32', size: 1 },
bQuadVertexPositionPathIDs: { type: 'Uint16', size: 6 }, bQuadVertexPositionPathIDs: { type: 'Uint16', size: 6 },
bQuadVertexPositions: { type: 'Float32', size: 12 }, bQuadVertexPositions: { type: 'Float32', size: 12 },
@ -99,6 +101,8 @@ const MESH_TYPES: Meshes<MeshBufferTypeDescriptor> = {
}; };
const BUFFER_TYPES: Meshes<BufferType> = { const BUFFER_TYPES: Meshes<BufferType> = {
bBoxPathIDs: 'ARRAY_BUFFER',
bBoxes: 'ARRAY_BUFFER',
bQuadVertexInteriorIndices: 'ELEMENT_ARRAY_BUFFER', bQuadVertexInteriorIndices: 'ELEMENT_ARRAY_BUFFER',
bQuadVertexPositionPathIDs: 'ARRAY_BUFFER', bQuadVertexPositionPathIDs: 'ARRAY_BUFFER',
bQuadVertexPositions: 'ARRAY_BUFFER', bQuadVertexPositions: 'ARRAY_BUFFER',
@ -118,6 +122,7 @@ const MESH_LIBRARY_FOURCC: string = 'PFML';
// Must match the FourCCs in `pathfinder_partitioner::mesh_library::MeshLibrary::serialize_into()`. // Must match the FourCCs in `pathfinder_partitioner::mesh_library::MeshLibrary::serialize_into()`.
const BUFFER_TYPE_FOURCCS: BufferTypeFourCCTable = { const BUFFER_TYPE_FOURCCS: BufferTypeFourCCTable = {
bbox: 'bBoxes',
bqii: 'bQuadVertexInteriorIndices', bqii: 'bQuadVertexInteriorIndices',
bqvp: 'bQuadVertexPositions', bqvp: 'bQuadVertexPositions',
scur: 'segmentCurves', scur: 'segmentCurves',
@ -129,6 +134,7 @@ const BUFFER_TYPE_FOURCCS: BufferTypeFourCCTable = {
// Must match the FourCCs in // Must match the FourCCs in
// `pathfinder_partitioner::mesh_library::MeshLibrary::serialize_into::write_path_ranges()`. // `pathfinder_partitioner::mesh_library::MeshLibrary::serialize_into::write_path_ranges()`.
const PATH_RANGE_TYPE_FOURCCS: PathRangeTypeFourCCTable = { const PATH_RANGE_TYPE_FOURCCS: PathRangeTypeFourCCTable = {
bbox: 'bBoxPathRanges',
bqii: 'bQuadVertexInteriorIndexPathRanges', bqii: 'bQuadVertexInteriorIndexPathRanges',
bqvp: 'bQuadVertexPositionPathRanges', bqvp: 'bQuadVertexPositionPathRanges',
scur: 'segmentCurveRanges', scur: 'segmentCurveRanges',
@ -136,6 +142,7 @@ const PATH_RANGE_TYPE_FOURCCS: PathRangeTypeFourCCTable = {
}; };
const RANGE_TO_COUNT_TABLE: RangeToCountTable = { const RANGE_TO_COUNT_TABLE: RangeToCountTable = {
bBoxPathRanges: 'bBoxCount',
bQuadVertexInteriorIndexPathRanges: 'bQuadVertexInteriorIndexCount', bQuadVertexInteriorIndexPathRanges: 'bQuadVertexInteriorIndexCount',
bQuadVertexPositionPathRanges: 'bQuadVertexPositionCount', bQuadVertexPositionPathRanges: 'bQuadVertexPositionCount',
segmentCurveRanges: 'segmentCurveCount', segmentCurveRanges: 'segmentCurveCount',
@ -143,6 +150,7 @@ const RANGE_TO_COUNT_TABLE: RangeToCountTable = {
}; };
const RANGE_TO_RANGE_BUFFER_TABLE: RangeToRangeBufferTable = { const RANGE_TO_RANGE_BUFFER_TABLE: RangeToRangeBufferTable = {
bBoxPathRanges: 'bBoxPathIDs',
bQuadVertexPositionPathRanges: 'bQuadVertexPositionPathIDs', bQuadVertexPositionPathRanges: 'bQuadVertexPositionPathIDs',
segmentCurveRanges: 'segmentCurvePathIDs', segmentCurveRanges: 'segmentCurvePathIDs',
segmentLineRanges: 'segmentLinePathIDs', segmentLineRanges: 'segmentLinePathIDs',
@ -151,6 +159,7 @@ const RANGE_TO_RANGE_BUFFER_TABLE: RangeToRangeBufferTable = {
const RANGE_KEYS: Array<keyof PathRanges> = [ const RANGE_KEYS: Array<keyof PathRanges> = [
'bQuadVertexPositionPathRanges', 'bQuadVertexPositionPathRanges',
'bQuadVertexInteriorIndexPathRanges', 'bQuadVertexInteriorIndexPathRanges',
'bBoxPathRanges',
'segmentCurveRanges', 'segmentCurveRanges',
'segmentLineRanges', 'segmentLineRanges',
]; ];
@ -160,12 +169,14 @@ type BufferType = 'ARRAY_BUFFER' | 'ELEMENT_ARRAY_BUFFER';
export interface Meshes<T> { export interface Meshes<T> {
readonly bQuadVertexPositions: T; readonly bQuadVertexPositions: T;
readonly bQuadVertexInteriorIndices: T; readonly bQuadVertexInteriorIndices: T;
readonly bBoxes: T;
readonly segmentLines: T; readonly segmentLines: T;
readonly segmentCurves: T; readonly segmentCurves: T;
readonly segmentLineNormals: T; readonly segmentLineNormals: T;
readonly segmentCurveNormals: T; readonly segmentCurveNormals: T;
bQuadVertexPositionPathIDs: T; bQuadVertexPositionPathIDs: T;
bBoxPathIDs: T;
segmentLinePathIDs: T; segmentLinePathIDs: T;
segmentCurvePathIDs: T; segmentCurvePathIDs: T;
} }
@ -173,6 +184,7 @@ export interface Meshes<T> {
interface MeshDataCounts { interface MeshDataCounts {
readonly bQuadVertexPositionCount: number; readonly bQuadVertexPositionCount: number;
readonly bQuadVertexInteriorIndexCount: number; readonly bQuadVertexInteriorIndexCount: number;
readonly bBoxCount: number;
readonly segmentLineCount: number; readonly segmentLineCount: number;
readonly segmentCurveCount: number; readonly segmentCurveCount: number;
} }
@ -180,6 +192,7 @@ interface MeshDataCounts {
interface PathRanges { interface PathRanges {
bQuadVertexPositionPathRanges: Range[]; bQuadVertexPositionPathRanges: Range[];
bQuadVertexInteriorIndexPathRanges: Range[]; bQuadVertexInteriorIndexPathRanges: Range[];
bBoxPathRanges: Range[];
segmentCurveRanges: Range[]; segmentCurveRanges: Range[];
segmentLineRanges: Range[]; segmentLineRanges: Range[];
} }
@ -187,6 +200,9 @@ interface PathRanges {
export class PathfinderMeshData implements Meshes<ArrayBuffer>, MeshDataCounts, PathRanges { export class PathfinderMeshData implements Meshes<ArrayBuffer>, MeshDataCounts, PathRanges {
readonly bQuadVertexPositions: ArrayBuffer; readonly bQuadVertexPositions: ArrayBuffer;
readonly bQuadVertexInteriorIndices: ArrayBuffer; readonly bQuadVertexInteriorIndices: ArrayBuffer;
readonly bBoxes: ArrayBuffer;
readonly bBoxSigns: ArrayBuffer;
readonly bBoxIndices: ArrayBuffer;
readonly segmentLines: ArrayBuffer; readonly segmentLines: ArrayBuffer;
readonly segmentCurves: ArrayBuffer; readonly segmentCurves: ArrayBuffer;
readonly segmentLineNormals: ArrayBuffer; readonly segmentLineNormals: ArrayBuffer;
@ -194,15 +210,18 @@ export class PathfinderMeshData implements Meshes<ArrayBuffer>, MeshDataCounts,
readonly bQuadVertexPositionCount: number; readonly bQuadVertexPositionCount: number;
readonly bQuadVertexInteriorIndexCount: number; readonly bQuadVertexInteriorIndexCount: number;
readonly bBoxCount: number;
readonly segmentLineCount: number; readonly segmentLineCount: number;
readonly segmentCurveCount: number; readonly segmentCurveCount: number;
bQuadVertexPositionPathIDs: ArrayBuffer; bQuadVertexPositionPathIDs: ArrayBuffer;
bBoxPathIDs: ArrayBuffer;
segmentCurvePathIDs: ArrayBuffer; segmentCurvePathIDs: ArrayBuffer;
segmentLinePathIDs: ArrayBuffer; segmentLinePathIDs: ArrayBuffer;
bQuadVertexPositionPathRanges: Range[]; bQuadVertexPositionPathRanges: Range[];
bQuadVertexInteriorIndexPathRanges: Range[]; bQuadVertexInteriorIndexPathRanges: Range[];
bBoxPathRanges: Range[];
segmentCurveRanges: Range[]; segmentCurveRanges: Range[];
segmentLineRanges: Range[]; segmentLineRanges: Range[];
@ -241,6 +260,7 @@ export class PathfinderMeshData implements Meshes<ArrayBuffer>, MeshDataCounts,
B_QUAD_VERTEX_POSITION_SIZE; B_QUAD_VERTEX_POSITION_SIZE;
this.bQuadVertexInteriorIndexCount = this.bQuadVertexInteriorIndices.byteLength / this.bQuadVertexInteriorIndexCount = this.bQuadVertexInteriorIndices.byteLength /
INDEX_SIZE; INDEX_SIZE;
this.bBoxCount = this.bBoxes.byteLength / (FLOAT32_SIZE * 6);
this.segmentCurveCount = this.segmentCurves.byteLength / SEGMENT_CURVE_SIZE; this.segmentCurveCount = this.segmentCurves.byteLength / SEGMENT_CURVE_SIZE;
this.segmentLineCount = this.segmentLines.byteLength / SEGMENT_LINE_SIZE; this.segmentLineCount = this.segmentLines.byteLength / SEGMENT_LINE_SIZE;
@ -292,7 +312,7 @@ export class PathfinderMeshData implements Meshes<ArrayBuffer>, MeshDataCounts,
const firstBQuadVertexIndex = bQuadVertexCopyResult.originalStartIndex; const firstBQuadVertexIndex = bQuadVertexCopyResult.originalStartIndex;
const lastBQuadVertexIndex = bQuadVertexCopyResult.originalEndIndex; const lastBQuadVertexIndex = bQuadVertexCopyResult.originalEndIndex;
// Copy over indices. // Copy over B-vertex indices.
copyIndices(expandedArrays.bQuadVertexInteriorIndices, copyIndices(expandedArrays.bQuadVertexInteriorIndices,
expandedRanges.bQuadVertexInteriorIndexPathRanges, expandedRanges.bQuadVertexInteriorIndexPathRanges,
originalBuffers.bQuadVertexInteriorIndices as Uint32Array, originalBuffers.bQuadVertexInteriorIndices as Uint32Array,
@ -301,6 +321,23 @@ export class PathfinderMeshData implements Meshes<ArrayBuffer>, MeshDataCounts,
lastBQuadVertexIndex * 6, lastBQuadVertexIndex * 6,
expandedPathID); expandedPathID);
// Copy over B-boxes.
const bBoxVertexCopyResult = copyVertices(['bBoxes'],
'bBoxPathRanges',
expandedArrays,
expandedRanges,
originalBuffers,
originalRanges,
expandedPathID,
originalPathID);
if (bBoxVertexCopyResult == null)
continue;
const firstExpandedBBoxIndex = bBoxVertexCopyResult.expandedStartIndex;
const firstBBoxIndex = bBoxVertexCopyResult.originalStartIndex;
const lastBBoxIndex = bBoxVertexCopyResult.originalEndIndex;
// Copy over segments. // Copy over segments.
copySegments(['segmentLines', 'segmentLineNormals'], copySegments(['segmentLines', 'segmentLineNormals'],
'segmentLineRanges', 'segmentLineRanges',
@ -361,9 +398,11 @@ export class PathfinderMeshData implements Meshes<ArrayBuffer>, MeshDataCounts,
const rangeBufferKey = RANGE_TO_RANGE_BUFFER_TABLE[rangeKey]; const rangeBufferKey = RANGE_TO_RANGE_BUFFER_TABLE[rangeKey];
const instanceCount = this[RANGE_TO_COUNT_TABLE[rangeKey]]; const instanceCount = this[RANGE_TO_COUNT_TABLE[rangeKey]];
const fieldCount = MESH_TYPES[rangeBufferKey].size;
const ranges = this[rangeKey as keyof PathRanges]; const ranges = this[rangeKey as keyof PathRanges];
const meshType = MESH_TYPES[rangeBufferKey];
const fieldCount = meshType.size;
const destBuffer = new Uint16Array(instanceCount * fieldCount); const destBuffer = new Uint16Array(instanceCount * fieldCount);
let destIndex = 0; let destIndex = 0;
for (let pathIndex = 0; pathIndex < ranges.length; pathIndex++) { for (let pathIndex = 0; pathIndex < ranges.length; pathIndex++) {
@ -385,6 +424,10 @@ export class PathfinderMeshBuffers implements Meshes<WebGLBuffer>, PathRanges {
readonly bQuadVertexPositions: WebGLBuffer; readonly bQuadVertexPositions: WebGLBuffer;
readonly bQuadVertexPositionPathIDs: WebGLBuffer; readonly bQuadVertexPositionPathIDs: WebGLBuffer;
readonly bQuadVertexInteriorIndices: WebGLBuffer; readonly bQuadVertexInteriorIndices: WebGLBuffer;
readonly bBoxes: WebGLBuffer;
readonly bBoxSigns: WebGLBuffer;
readonly bBoxIndices: WebGLBuffer;
readonly bBoxPathIDs: WebGLBuffer;
readonly segmentLines: WebGLBuffer; readonly segmentLines: WebGLBuffer;
readonly segmentCurves: WebGLBuffer; readonly segmentCurves: WebGLBuffer;
readonly segmentLinePathIDs: WebGLBuffer; readonly segmentLinePathIDs: WebGLBuffer;
@ -394,6 +437,7 @@ export class PathfinderMeshBuffers implements Meshes<WebGLBuffer>, PathRanges {
readonly bQuadVertexPositionPathRanges: Range[]; readonly bQuadVertexPositionPathRanges: Range[];
readonly bQuadVertexInteriorIndexPathRanges: Range[]; readonly bQuadVertexInteriorIndexPathRanges: Range[];
readonly bBoxPathRanges: Range[];
readonly segmentCurveRanges: Range[]; readonly segmentCurveRanges: Range[];
readonly segmentLineRanges: Range[]; readonly segmentLineRanges: Range[];

View File

@ -267,14 +267,7 @@ export abstract class Renderer {
} }
setTransformUniform(uniforms: UniformMap, pass: number, objectIndex: number): void { setTransformUniform(uniforms: UniformMap, pass: number, objectIndex: number): void {
let transform; const transform = this.computeTransform(pass, objectIndex);
if (this.antialiasingStrategy == null)
transform = glmatrix.mat4.create();
else
transform = this.antialiasingStrategy.worldTransformForPass(this, pass);
glmatrix.mat4.mul(transform, transform, this.worldTransform);
glmatrix.mat4.mul(transform, transform, this.getModelviewTransform(objectIndex));
this.renderContext.gl.uniformMatrix4fv(uniforms.uTransform, false, transform); this.renderContext.gl.uniformMatrix4fv(uniforms.uTransform, false, transform);
} }
@ -284,10 +277,7 @@ export abstract class Renderer {
const renderContext = this.renderContext; const renderContext = this.renderContext;
const gl = renderContext.gl; const gl = renderContext.gl;
const transform = glmatrix.mat4.clone(this.worldTransform); const transform = this.computeTransform(0, objectIndex);
glmatrix.mat4.mul(transform, transform, this.getModelviewTransform(objectIndex));
const translation = glmatrix.vec4.clone([transform[12], transform[13], 0.0, 1.0]);
gl.uniform4f(uniforms.uTransformST, gl.uniform4f(uniforms.uTransformST,
transform[0], transform[0],
@ -296,6 +286,22 @@ export abstract class Renderer {
transform[13]); transform[13]);
} }
setTransformAffineUniforms(uniforms: UniformMap, objectIndex: number): void {
// FIXME(pcwalton): Lossy conversion from a 4x4 matrix to an affine matrix is ugly and
// fragile. Refactor.
const renderContext = this.renderContext;
const gl = renderContext.gl;
const transform = this.computeTransform(0, objectIndex);
gl.uniform4f(uniforms.uTransformST,
transform[0],
transform[5],
transform[12],
transform[13]);
gl.uniform2f(uniforms.uTransformExt, transform[1], transform[4]);
}
uploadPathColors(objectCount: number): void { uploadPathColors(objectCount: number): void {
const renderContext = this.renderContext; const renderContext = this.renderContext;
for (let objectIndex = 0; objectIndex < objectCount; objectIndex++) { for (let objectIndex = 0; objectIndex < objectCount; objectIndex++) {
@ -481,7 +487,10 @@ export abstract class Renderer {
this.initImplicitCoverInteriorVAO(objectIndex, instanceRange, renderingMode); this.initImplicitCoverInteriorVAO(objectIndex, instanceRange, renderingMode);
// Draw direct interior parts. // Draw direct interior parts.
this.setTransformUniform(directInteriorProgram.uniforms, pass, objectIndex); if (renderingMode === 'conservative')
this.setTransformAffineUniforms(directInteriorProgram.uniforms, objectIndex);
else
this.setTransformUniform(directInteriorProgram.uniforms, pass, objectIndex);
this.setFramebufferSizeUniform(directInteriorProgram.uniforms); this.setFramebufferSizeUniform(directInteriorProgram.uniforms);
this.setHintsUniform(directInteriorProgram.uniforms); this.setHintsUniform(directInteriorProgram.uniforms);
this.setPathColorsUniform(objectIndex, directInteriorProgram.uniforms, 0); this.setPathColorsUniform(objectIndex, directInteriorProgram.uniforms, 0);
@ -737,6 +746,18 @@ export abstract class Renderer {
gl.bufferData(gl.ARRAY_BUFFER, vertexIDs, gl.STATIC_DRAW); gl.bufferData(gl.ARRAY_BUFFER, vertexIDs, gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, null); gl.bindBuffer(gl.ARRAY_BUFFER, null);
} }
private computeTransform(pass: number, objectIndex: number): glmatrix.mat4 {
let transform;
if (this.antialiasingStrategy == null)
transform = glmatrix.mat4.create();
else
transform = this.antialiasingStrategy.worldTransformForPass(this, pass);
glmatrix.mat4.mul(transform, transform, this.worldTransform);
glmatrix.mat4.mul(transform, transform, this.getModelviewTransform(objectIndex));
return transform;
}
} }
function getMeshIndexRange(indexRanges: Range[], pathRange: Range): Range { function getMeshIndexRange(indexRanges: Range[], pathRange: Range): Range {

View File

@ -272,6 +272,7 @@ export abstract class DemoView extends PathfinderView implements RenderContext {
this.vertexArrayObjectExt = this.gl.getExtension('OES_vertex_array_object'); this.vertexArrayObjectExt = this.gl.getExtension('OES_vertex_array_object');
this.gl.getExtension('EXT_frag_depth'); this.gl.getExtension('EXT_frag_depth');
this.gl.getExtension('OES_element_index_uint'); this.gl.getExtension('OES_element_index_uint');
this.gl.getExtension('OES_standard_derivatives');
this.gl.getExtension('OES_texture_float'); this.gl.getExtension('OES_texture_float');
this.gl.getExtension('WEBGL_depth_texture'); this.gl.getExtension('WEBGL_depth_texture');

View File

@ -42,10 +42,12 @@ const PATCH_VERTICES: Float32Array = new Float32Array([
1.0, 1.0, 1.0, 1.0,
]); ]);
const MCAA_PATCH_INDICES: Uint8Array = new Uint8Array([0, 1, 2, 0, 2, 3, 2, 5, 3, 3, 5, 4]); const MCAA_PATCH_INDICES: Uint8Array = new Uint8Array([0, 1, 2, 1, 3, 2]);
const ECAA_CURVE_PATCH_INDICES: Uint8Array = new Uint8Array([0, 1, 2, 0, 2, 3, 2, 5, 3]); const ECAA_CURVE_PATCH_INDICES: Uint8Array = new Uint8Array([0, 1, 2, 0, 2, 3, 2, 5, 3]);
export type TransformType = 'dilation' | 'affine' | '3d';
export abstract class XCAAStrategy extends AntialiasingStrategy { export abstract class XCAAStrategy extends AntialiasingStrategy {
abstract readonly directRenderingMode: DirectRenderingMode; abstract readonly directRenderingMode: DirectRenderingMode;
@ -56,7 +58,7 @@ export abstract class XCAAStrategy extends AntialiasingStrategy {
return 1; return 1;
} }
protected abstract get usesDilationTransforms(): boolean; protected abstract get transformType(): TransformType;
protected abstract get patchIndices(): Uint8Array; protected abstract get patchIndices(): Uint8Array;
@ -255,10 +257,17 @@ export abstract class XCAAStrategy extends AntialiasingStrategy {
const renderContext = renderer.renderContext; const renderContext = renderer.renderContext;
const gl = renderContext.gl; const gl = renderContext.gl;
if (this.usesDilationTransforms) switch (this.transformType) {
case 'dilation':
renderer.setTransformSTUniform(uniforms, 0); renderer.setTransformSTUniform(uniforms, 0);
else break;
case 'affine':
renderer.setTransformAffineUniforms(uniforms, 0);
break;
case '3d':
renderer.setTransformUniform(uniforms, 0, 0); renderer.setTransformUniform(uniforms, 0, 0);
break;
}
gl.uniform2i(uniforms.uFramebufferSize, gl.uniform2i(uniforms.uFramebufferSize,
this.supersampledFramebufferSize[0], this.supersampledFramebufferSize[0],
@ -372,8 +381,8 @@ export class MCAAStrategy extends XCAAStrategy {
return MCAA_PATCH_INDICES; return MCAA_PATCH_INDICES;
} }
protected get usesDilationTransforms(): boolean { protected get transformType(): TransformType {
return true; return 'affine';
} }
protected get mightUseAAFramebuffer(): boolean { protected get mightUseAAFramebuffer(): boolean {
@ -434,9 +443,7 @@ export class MCAAStrategy extends XCAAStrategy {
gl.depthFunc(gl.GREATER); gl.depthFunc(gl.GREATER);
gl.depthMask(false); gl.depthMask(false);
gl.enable(gl.DEPTH_TEST); gl.enable(gl.DEPTH_TEST);
gl.frontFace(gl.CCW); gl.disable(gl.CULL_FACE);
gl.cullFace(gl.BACK);
gl.enable(gl.CULL_FACE);
} }
protected clearForResolve(renderer: Renderer): void { protected clearForResolve(renderer: Renderer): void {
@ -485,80 +492,68 @@ export class MCAAStrategy extends XCAAStrategy {
const vao = this.vao; const vao = this.vao;
renderContext.vertexArrayObjectExt.bindVertexArrayOES(vao); renderContext.vertexArrayObjectExt.bindVertexArrayOES(vao);
const bQuadRanges = renderer.meshData[meshIndex].bQuadVertexPositionPathRanges; const bBoxRanges = renderer.meshData[meshIndex].bBoxPathRanges;
const offset = calculateStartFromIndexRanges(pathRange, bQuadRanges); const offset = calculateStartFromIndexRanges(pathRange, bBoxRanges);
gl.useProgram(shaderProgram.program); gl.useProgram(shaderProgram.program);
gl.bindBuffer(gl.ARRAY_BUFFER, this.patchVertexBuffer); gl.bindBuffer(gl.ARRAY_BUFFER, renderer.renderContext.quadPositionsBuffer);
gl.vertexAttribPointer(attributes.aTessCoord, 2, gl.FLOAT, false, 0, 0); gl.vertexAttribPointer(attributes.aTessCoord, 2, gl.FLOAT, false, FLOAT32_SIZE * 2, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, renderer.meshes[meshIndex].bQuadVertexPositions); gl.bindBuffer(gl.ARRAY_BUFFER, renderer.meshes[meshIndex].bBoxes);
gl.vertexAttribPointer(attributes.aUpperLeftEndpointPosition, gl.vertexAttribPointer(attributes.aRect,
2, 4,
gl.FLOAT, gl.FLOAT,
false, false,
FLOAT32_SIZE * 12, FLOAT32_SIZE * 20,
FLOAT32_SIZE * 12 * offset + FLOAT32_SIZE * 0); FLOAT32_SIZE * 0 + offset * FLOAT32_SIZE * 20);
gl.vertexAttribPointer(attributes.aUpperControlPointPosition, gl.vertexAttribPointer(attributes.aUV,
2, 4,
gl.FLOAT, gl.FLOAT,
false, false,
FLOAT32_SIZE * 12, FLOAT32_SIZE * 20,
FLOAT32_SIZE * 12 * offset + FLOAT32_SIZE * 2); FLOAT32_SIZE * 4 + offset * FLOAT32_SIZE * 20);
gl.vertexAttribPointer(attributes.aUpperRightEndpointPosition, gl.vertexAttribPointer(attributes.aDUVDX,
2, 4,
gl.FLOAT, gl.FLOAT,
false, false,
FLOAT32_SIZE * 12, FLOAT32_SIZE * 20,
FLOAT32_SIZE * 12 * offset + FLOAT32_SIZE * 4); FLOAT32_SIZE * 8 + offset * FLOAT32_SIZE * 20);
gl.vertexAttribPointer(attributes.aLowerRightEndpointPosition, gl.vertexAttribPointer(attributes.aDUVDY,
2, 4,
gl.FLOAT, gl.FLOAT,
false, false,
FLOAT32_SIZE * 12, FLOAT32_SIZE * 20,
FLOAT32_SIZE * 12 * offset + FLOAT32_SIZE * 6); FLOAT32_SIZE * 12 + offset * FLOAT32_SIZE * 20);
gl.vertexAttribPointer(attributes.aLowerControlPointPosition, gl.vertexAttribPointer(attributes.aSignMode,
2, 4,
gl.FLOAT, gl.FLOAT,
false, false,
FLOAT32_SIZE * 12, FLOAT32_SIZE * 20,
FLOAT32_SIZE * 12 * offset + FLOAT32_SIZE * 8); FLOAT32_SIZE * 16 + offset * FLOAT32_SIZE * 20);
gl.vertexAttribPointer(attributes.aLowerLeftEndpointPosition,
2,
gl.FLOAT,
false,
FLOAT32_SIZE * 12,
FLOAT32_SIZE * 12 * offset + FLOAT32_SIZE * 10);
renderContext.instancedArraysExt
.vertexAttribDivisorANGLE(attributes.aUpperLeftEndpointPosition, 1);
renderContext.instancedArraysExt
.vertexAttribDivisorANGLE(attributes.aUpperControlPointPosition, 1);
renderContext.instancedArraysExt
.vertexAttribDivisorANGLE(attributes.aUpperRightEndpointPosition, 1);
renderContext.instancedArraysExt
.vertexAttribDivisorANGLE(attributes.aLowerRightEndpointPosition, 1);
renderContext.instancedArraysExt
.vertexAttribDivisorANGLE(attributes.aLowerControlPointPosition, 1);
renderContext.instancedArraysExt
.vertexAttribDivisorANGLE(attributes.aLowerLeftEndpointPosition, 1);
gl.bindBuffer(gl.ARRAY_BUFFER, renderer.meshes[meshIndex].bQuadVertexPositionPathIDs); gl.enableVertexAttribArray(attributes.aTessCoord);
gl.enableVertexAttribArray(attributes.aRect);
gl.enableVertexAttribArray(attributes.aUV);
gl.enableVertexAttribArray(attributes.aDUVDX);
gl.enableVertexAttribArray(attributes.aDUVDY);
gl.enableVertexAttribArray(attributes.aSignMode);
renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aRect, 1);
renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aUV, 1);
renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aDUVDX, 1);
renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aDUVDY, 1);
renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aSignMode, 1);
gl.bindBuffer(gl.ARRAY_BUFFER, renderer.meshes[meshIndex].bBoxPathIDs);
gl.vertexAttribPointer(attributes.aPathID, gl.vertexAttribPointer(attributes.aPathID,
1, 1,
gl.UNSIGNED_SHORT, gl.UNSIGNED_SHORT,
false, false,
UINT16_SIZE * 6, UINT16_SIZE,
UINT16_SIZE * offset); offset * UINT16_SIZE);
gl.enableVertexAttribArray(attributes.aPathID);
renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aPathID, 1); renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aPathID, 1);
gl.enableVertexAttribArray(attributes.aTessCoord); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, renderer.renderContext.quadElementsBuffer);
gl.enableVertexAttribArray(attributes.aUpperLeftEndpointPosition);
gl.enableVertexAttribArray(attributes.aUpperControlPointPosition);
gl.enableVertexAttribArray(attributes.aUpperRightEndpointPosition);
gl.enableVertexAttribArray(attributes.aLowerRightEndpointPosition);
gl.enableVertexAttribArray(attributes.aLowerControlPointPosition);
gl.enableVertexAttribArray(attributes.aLowerLeftEndpointPosition);
gl.enableVertexAttribArray(attributes.aPathID);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.patchIndexBuffer);
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null); renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
} }
@ -571,7 +566,7 @@ export class MCAAStrategy extends XCAAStrategy {
objectIndex: number, objectIndex: number,
shaderProgram: PathfinderShaderProgram): shaderProgram: PathfinderShaderProgram):
void { void {
if (renderer.meshData == null) if (renderer.meshes == null || renderer.meshData == null)
return; return;
const renderContext = renderer.renderContext; const renderContext = renderer.renderContext;
@ -593,11 +588,13 @@ export class MCAAStrategy extends XCAAStrategy {
this.setBlendModeForAA(renderer); this.setBlendModeForAA(renderer);
this.setAADepthState(renderer); this.setAADepthState(renderer);
const bQuadRanges = renderer.meshData[meshIndex].bQuadVertexPositionPathRanges; gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, renderContext.quadElementsBuffer);
const count = calculateCountFromIndexRanges(pathRange, bQuadRanges);
const bBoxRanges = renderer.meshData[meshIndex].bBoxPathRanges;
const count = calculateCountFromIndexRanges(pathRange, bBoxRanges);
renderContext.instancedArraysExt renderContext.instancedArraysExt
.drawElementsInstancedANGLE(gl.TRIANGLES, 12, gl.UNSIGNED_BYTE, 0, count); .drawElementsInstancedANGLE(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0, count);
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null); renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
gl.disable(gl.DEPTH_TEST); gl.disable(gl.DEPTH_TEST);
@ -641,8 +638,8 @@ export class ECAAStrategy extends XCAAStrategy {
private lineVAOs: Partial<ShaderMap<WebGLVertexArrayObject>>; private lineVAOs: Partial<ShaderMap<WebGLVertexArrayObject>>;
private curveVAOs: Partial<ShaderMap<WebGLVertexArrayObject>>; private curveVAOs: Partial<ShaderMap<WebGLVertexArrayObject>>;
protected get usesDilationTransforms(): boolean { protected get transformType(): TransformType {
return false; return '3d';
} }
get directRenderingMode(): DirectRenderingMode { get directRenderingMode(): DirectRenderingMode {

View File

@ -10,7 +10,8 @@
use bincode::{self, Infinite}; use bincode::{self, Infinite};
use byteorder::{LittleEndian, WriteBytesExt}; use byteorder::{LittleEndian, WriteBytesExt};
use euclid::{Point2D, Vector2D}; use euclid::approxeq::ApproxEq;
use euclid::{Point2D, Rect, Size2D, Vector2D};
use lyon_path::PathEvent; use lyon_path::PathEvent;
use lyon_path::iterator::PathIterator; use lyon_path::iterator::PathIterator;
use pathfinder_path_utils::normals::PathNormals; use pathfinder_path_utils::normals::PathNormals;
@ -32,6 +33,7 @@ pub struct MeshLibrary {
pub b_quad_vertex_interior_indices: Vec<u32>, pub b_quad_vertex_interior_indices: Vec<u32>,
pub b_vertex_positions: Vec<Point2D<f32>>, pub b_vertex_positions: Vec<Point2D<f32>>,
pub b_vertex_loop_blinn_data: Vec<BVertexLoopBlinnData>, pub b_vertex_loop_blinn_data: Vec<BVertexLoopBlinnData>,
pub b_boxes: Vec<BBox>,
pub segments: MeshLibrarySegments, pub segments: MeshLibrarySegments,
pub segment_normals: MeshLibrarySegmentNormals, pub segment_normals: MeshLibrarySegmentNormals,
} }
@ -46,6 +48,7 @@ impl MeshLibrary {
b_quad_vertex_interior_indices: vec![], b_quad_vertex_interior_indices: vec![],
b_vertex_positions: vec![], b_vertex_positions: vec![],
b_vertex_loop_blinn_data: vec![], b_vertex_loop_blinn_data: vec![],
b_boxes: vec![],
segments: MeshLibrarySegments::new(), segments: MeshLibrarySegments::new(),
segment_normals: MeshLibrarySegmentNormals::new(), segment_normals: MeshLibrarySegmentNormals::new(),
} }
@ -58,6 +61,7 @@ impl MeshLibrary {
self.b_quad_vertex_interior_indices.clear(); self.b_quad_vertex_interior_indices.clear();
self.b_vertex_positions.clear(); self.b_vertex_positions.clear();
self.b_vertex_loop_blinn_data.clear(); self.b_vertex_loop_blinn_data.clear();
self.b_boxes.clear();
self.segments.clear(); self.segments.clear();
self.segment_normals.clear(); self.segment_normals.clear();
} }
@ -78,45 +82,121 @@ impl MeshLibrary {
} }
pub(crate) fn add_b_quad(&mut self, b_quad: &BQuad) { pub(crate) fn add_b_quad(&mut self, b_quad: &BQuad) {
let BQuadVertexPositions {
upper_left_vertex_position: ul,
upper_right_vertex_position: ur,
lower_left_vertex_position: ll,
lower_right_vertex_position: lr,
..
} = self.get_b_quad_vertex_positions(b_quad);
if ul.x.approx_eq(&ur.x) || ll.x.approx_eq(&lr.x) {
return
}
self.b_quads.push(*b_quad); self.b_quads.push(*b_quad);
let upper_left_position = self.add_b_quad_vertex_positions(b_quad);
self.b_vertex_positions[b_quad.upper_left_vertex_index as usize]; self.add_b_box(b_quad);
let upper_right_position = }
self.b_vertex_positions[b_quad.upper_right_vertex_index as usize];
let lower_left_position =
self.b_vertex_positions[b_quad.lower_left_vertex_index as usize];
let lower_right_position =
self.b_vertex_positions[b_quad.lower_right_vertex_index as usize];
let mut b_quad_vertex_positions = BQuadVertexPositions {
upper_left_vertex_position: upper_left_position,
upper_control_point_position: upper_left_position,
upper_right_vertex_position: upper_right_position,
lower_left_vertex_position: lower_left_position,
lower_control_point_position: lower_right_position,
lower_right_vertex_position: lower_right_position,
};
if b_quad.upper_control_point_vertex_index != u32::MAX {
let upper_control_point_position =
self.b_vertex_positions[b_quad.upper_control_point_vertex_index as usize];
b_quad_vertex_positions.upper_control_point_position = upper_control_point_position;
}
if b_quad.lower_control_point_vertex_index != u32::MAX {
let lower_control_point_position =
self.b_vertex_positions[b_quad.lower_control_point_vertex_index as usize];
b_quad_vertex_positions.lower_control_point_position = lower_control_point_position;
}
fn add_b_quad_vertex_positions(&mut self, b_quad: &BQuad) {
let b_quad_vertex_positions = self.get_b_quad_vertex_positions(b_quad);
let first_b_quad_vertex_position_index = (self.b_quad_vertex_positions.len() as u32) * 6; let first_b_quad_vertex_position_index = (self.b_quad_vertex_positions.len() as u32) * 6;
self.push_b_quad_vertex_position_interior_indices(first_b_quad_vertex_position_index, self.push_b_quad_vertex_position_interior_indices(first_b_quad_vertex_position_index,
&b_quad_vertex_positions); &b_quad_vertex_positions);
self.b_quad_vertex_positions.push(b_quad_vertex_positions); self.b_quad_vertex_positions.push(b_quad_vertex_positions);
} }
fn add_b_box(&mut self, b_quad: &BQuad) {
let BQuadVertexPositions {
upper_left_vertex_position: ul,
upper_control_point_position: uc,
upper_right_vertex_position: ur,
lower_left_vertex_position: ll,
lower_control_point_position: lc,
lower_right_vertex_position: lr,
} = self.get_b_quad_vertex_positions(b_quad);
let rect = Rect::from_points([ul, uc, ur, ll, lc, lr].into_iter());
let (edge_ucl, edge_urc, edge_ulr) = (uc - ul, ur - uc, ul - ur);
let (edge_lcl, edge_lrc, edge_llr) = (lc - ll, lr - lc, ll - lr);
let (edge_len_ucl, edge_len_urc) = (edge_ucl.length(), edge_urc.length());
let (edge_len_lcl, edge_len_lrc) = (edge_lcl.length(), edge_lrc.length());
let (edge_len_ulr, edge_len_llr) = (edge_ulr.length(), edge_llr.length());
let (uv_upper, uv_lower, sign_upper, sign_lower, mode_upper, mode_lower);
if edge_len_ucl < 0.01 || edge_len_urc < 0.01 || edge_len_ulr < 0.01 ||
edge_ucl.dot(-edge_ulr) > 0.9999 * edge_len_ucl * edge_len_ulr {
uv_upper = Uv::line(&rect, &ul, &ur);
sign_upper = -1.0;
mode_upper = -1.0;
} else {
uv_upper = Uv::curve(&rect, &ul, &uc, &ur);
sign_upper = (edge_ucl.cross(-edge_ulr)).signum();
mode_upper = 1.0;
}
if edge_len_lcl < 0.01 || edge_len_lrc < 0.01 || edge_len_llr < 0.01 ||
edge_lcl.dot(-edge_llr) > 0.9999 * edge_len_lcl * edge_len_llr {
uv_lower = Uv::line(&rect, &ll, &lr);
sign_lower = 1.0;
mode_lower = -1.0;
} else {
uv_lower = Uv::curve(&rect, &ll, &lc, &lr);
sign_lower = -(edge_lcl.cross(-edge_llr)).signum();
mode_lower = 1.0;
}
let b_box = BBox {
upper_left_position: rect.origin,
lower_right_position: rect.bottom_right(),
upper_left_uv_upper: uv_upper.origin,
upper_left_uv_lower: uv_lower.origin,
d_upper_uv_dx: uv_upper.d_uv_dx,
d_lower_uv_dx: uv_lower.d_uv_dx,
d_upper_uv_dy: uv_upper.d_uv_dy,
d_lower_uv_dy: uv_lower.d_uv_dy,
upper_sign: sign_upper,
lower_sign: sign_lower,
upper_mode: mode_upper,
lower_mode: mode_lower,
};
self.b_boxes.push(b_box);
}
fn get_b_quad_vertex_positions(&self, b_quad: &BQuad) -> BQuadVertexPositions {
let ul = self.b_vertex_positions[b_quad.upper_left_vertex_index as usize];
let ur = self.b_vertex_positions[b_quad.upper_right_vertex_index as usize];
let ll = self.b_vertex_positions[b_quad.lower_left_vertex_index as usize];
let lr = self.b_vertex_positions[b_quad.lower_right_vertex_index as usize];
let mut b_quad_vertex_positions = BQuadVertexPositions {
upper_left_vertex_position: ul,
upper_control_point_position: ul,
upper_right_vertex_position: ur,
lower_left_vertex_position: ll,
lower_control_point_position: lr,
lower_right_vertex_position: lr,
};
if b_quad.upper_control_point_vertex_index != u32::MAX {
let uc = &self.b_vertex_positions[b_quad.upper_control_point_vertex_index as usize];
b_quad_vertex_positions.upper_control_point_position = *uc;
}
if b_quad.lower_control_point_vertex_index != u32::MAX {
let lc = &self.b_vertex_positions[b_quad.lower_control_point_vertex_index as usize];
b_quad_vertex_positions.lower_control_point_position = *lc;
}
b_quad_vertex_positions
}
fn push_b_quad_vertex_position_interior_indices(&mut self, fn push_b_quad_vertex_position_interior_indices(&mut self,
first_vertex_index: u32, first_vertex_index: u32,
b_quad: &BQuadVertexPositions) { b_quad: &BQuadVertexPositions) {
@ -282,6 +362,7 @@ impl MeshLibrary {
try!(write_simple_chunk(writer, b"bqua", &self.b_quads)); 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"bqvp", &self.b_quad_vertex_positions));
try!(write_simple_chunk(writer, b"bqii", &self.b_quad_vertex_interior_indices)); try!(write_simple_chunk(writer, b"bqii", &self.b_quad_vertex_interior_indices));
try!(write_simple_chunk(writer, b"bbox", &self.b_boxes));
try!(write_simple_chunk(writer, b"slin", &self.segments.lines)); try!(write_simple_chunk(writer, b"slin", &self.segments.lines));
try!(write_simple_chunk(writer, b"scur", &self.segments.curves)); try!(write_simple_chunk(writer, b"scur", &self.segments.curves));
try!(write_simple_chunk(writer, b"snli", &self.segment_normals.line_normals)); try!(write_simple_chunk(writer, b"snli", &self.segment_normals.line_normals));
@ -331,6 +412,7 @@ impl MeshLibrary {
path_ranges, path_ranges,
|ranges| &ranges.b_quad_vertex_interior_indices)); |ranges| &ranges.b_quad_vertex_interior_indices));
try!(write_path_range(writer, b"bver", path_ranges, |ranges| &ranges.b_vertices)); try!(write_path_range(writer, b"bver", path_ranges, |ranges| &ranges.b_vertices));
try!(write_path_range(writer, b"bbox", path_ranges, |ranges| &ranges.b_boxes));
try!(write_path_range(writer, b"slin", path_ranges, |ranges| &ranges.segment_lines)); try!(write_path_range(writer, b"slin", path_ranges, |ranges| &ranges.segment_lines));
try!(write_path_range(writer, b"scur", path_ranges, |ranges| &ranges.segment_curves)); try!(write_path_range(writer, b"scur", path_ranges, |ranges| &ranges.segment_curves));
Ok(()) Ok(())
@ -359,6 +441,7 @@ impl MeshLibrary {
b_quad_vertex_positions: self.b_quad_vertex_positions.len() as u32, b_quad_vertex_positions: self.b_quad_vertex_positions.len() as u32,
b_quad_vertex_interior_indices: self.b_quad_vertex_interior_indices.len() as u32, b_quad_vertex_interior_indices: self.b_quad_vertex_interior_indices.len() as u32,
b_vertices: self.b_vertex_positions.len() as u32, b_vertices: self.b_vertex_positions.len() as u32,
b_boxes: self.b_boxes.len() as u32,
} }
} }
} }
@ -374,6 +457,7 @@ pub(crate) struct MeshLibraryLengths {
b_quad_vertex_positions: u32, b_quad_vertex_positions: u32,
b_quad_vertex_interior_indices: u32, b_quad_vertex_interior_indices: u32,
b_vertices: u32, b_vertices: u32,
b_boxes: u32,
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -382,6 +466,7 @@ pub struct PathRanges {
pub b_quad_vertex_positions: Range<u32>, pub b_quad_vertex_positions: Range<u32>,
pub b_quad_vertex_interior_indices: Range<u32>, pub b_quad_vertex_interior_indices: Range<u32>,
pub b_vertices: Range<u32>, pub b_vertices: Range<u32>,
pub b_boxes: Range<u32>,
pub segment_lines: Range<u32>, pub segment_lines: Range<u32>,
pub segment_curves: Range<u32>, pub segment_curves: Range<u32>,
} }
@ -393,6 +478,7 @@ impl PathRanges {
b_quad_vertex_positions: 0..0, b_quad_vertex_positions: 0..0,
b_quad_vertex_interior_indices: 0..0, b_quad_vertex_interior_indices: 0..0,
b_vertices: 0..0, b_vertices: 0..0,
b_boxes: 0..0,
segment_lines: 0..0, segment_lines: 0..0,
segment_curves: 0..0, segment_curves: 0..0,
} }
@ -406,6 +492,7 @@ impl PathRanges {
self.b_quad_vertex_interior_indices = self.b_quad_vertex_interior_indices =
start.b_quad_vertex_interior_indices..end.b_quad_vertex_interior_indices; start.b_quad_vertex_interior_indices..end.b_quad_vertex_interior_indices;
self.b_vertices = start.b_vertices..end.b_vertices; self.b_vertices = start.b_vertices..end.b_vertices;
self.b_boxes = start.b_boxes..end.b_boxes;
} }
} }
@ -474,3 +561,134 @@ pub struct CurveSegmentNormals {
pub control_point: f32, pub control_point: f32,
pub endpoint_1: f32, pub endpoint_1: f32,
} }
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct BBox {
pub upper_left_position: Point2D<f32>,
pub lower_right_position: Point2D<f32>,
pub upper_left_uv_upper: Point2D<f32>,
pub upper_left_uv_lower: Point2D<f32>,
pub d_upper_uv_dx: Vector2D<f32>,
pub d_lower_uv_dx: Vector2D<f32>,
pub d_upper_uv_dy: Vector2D<f32>,
pub d_lower_uv_dy: Vector2D<f32>,
pub upper_sign: f32,
pub lower_sign: f32,
pub upper_mode: f32,
pub lower_mode: f32,
}
#[derive(Clone, Copy, Debug)]
struct CornerPositions {
upper_left: Point2D<f32>,
upper_right: Point2D<f32>,
lower_left: Point2D<f32>,
lower_right: Point2D<f32>,
}
#[derive(Clone, Copy, Debug)]
struct CornerValues {
upper_left: Point2D<f32>,
upper_right: Point2D<f32>,
lower_left: Point2D<f32>,
lower_right: Point2D<f32>,
}
#[derive(Clone, Copy, Debug)]
struct Uv {
origin: Point2D<f32>,
d_uv_dx: Vector2D<f32>,
d_uv_dy: Vector2D<f32>,
}
impl Uv {
fn from_values(origin: &Point2D<f32>, origin_right: &Point2D<f32>, origin_down: &Point2D<f32>)
-> Uv {
Uv {
origin: *origin,
d_uv_dx: *origin_right - *origin,
d_uv_dy: *origin_down - *origin,
}
}
fn curve(rect: &Rect<f32>, left: &Point2D<f32>, ctrl: &Point2D<f32>, right: &Point2D<f32>)
-> Uv {
let origin_right = rect.top_right();
let origin_down = rect.bottom_left();
let (lambda_origin, denom) = to_barycentric(left, ctrl, right, &rect.origin);
let (lambda_origin_right, _) = to_barycentric(left, ctrl, right, &origin_right);
let (lambda_origin_down, _) = to_barycentric(left, ctrl, right, &origin_down);
let uv_origin = lambda_to_uv(&lambda_origin, denom);
let uv_origin_right = lambda_to_uv(&lambda_origin_right, denom);
let uv_origin_down = lambda_to_uv(&lambda_origin_down, denom);
return Uv::from_values(&uv_origin, &uv_origin_right, &uv_origin_down);
// https://gamedev.stackexchange.com/a/23745
fn to_barycentric(a: &Point2D<f32>, b: &Point2D<f32>, c: &Point2D<f32>, p: &Point2D<f32>)
-> ([f64; 2], f64) {
let (a, b, c, p) = (a.to_f64(), b.to_f64(), c.to_f64(), p.to_f64());
let (v0, v1, v2) = (b - a, c - a, p - a);
let (d00, d01) = (v0.dot(v0), v0.dot(v1));
let d11 = v1.dot(v1);
let (d20, d21) = (v2.dot(v0), v2.dot(v1));
let denom = d00 * d11 - d01 * d01;
([(d11 * d20 - d01 * d21), (d00 * d21 - d01 * d20)], denom)
}
fn lambda_to_uv(lambda: &[f64; 2], denom: f64) -> Point2D<f32> {
(Point2D::new(lambda[0] * 0.5 + lambda[1], lambda[1]) / denom).to_f32()
}
}
fn line(rect: &Rect<f32>, left: &Point2D<f32>, right: &Point2D<f32>) -> Uv {
let (values, line_bounds);
if f32::abs(left.y - right.y) < 0.01 {
values = CornerValues {
upper_left: Point2D::new(0.0, 0.5),
upper_right: Point2D::new(0.5, 1.0),
lower_right: Point2D::new(1.0, 0.5),
lower_left: Point2D::new(0.5, 0.0),
};
line_bounds = Rect::new(*left + Vector2D::new(0.0, -1.0),
Size2D::new(right.x - left.x, 2.0));
} else {
if left.y < right.y {
values = CornerValues {
upper_left: Point2D::new(1.0, 1.0),
upper_right: Point2D::new(0.0, 1.0),
lower_left: Point2D::new(1.0, 0.0),
lower_right: Point2D::new(0.0, 0.0),
};
} else {
values = CornerValues {
upper_left: Point2D::new(0.0, 1.0),
upper_right: Point2D::new(1.0, 1.0),
lower_left: Point2D::new(0.0, 0.0),
lower_right: Point2D::new(1.0, 0.0),
};
}
line_bounds = Rect::from_points([*left, *right].into_iter());
}
let origin_right = rect.top_right();
let origin_down = rect.bottom_left();
let uv_origin = bilerp(&line_bounds, &values, &rect.origin);
let uv_origin_right = bilerp(&line_bounds, &values, &origin_right);
let uv_origin_down = bilerp(&line_bounds, &values, &origin_down);
return Uv::from_values(&uv_origin, &uv_origin_right, &uv_origin_down);
fn bilerp(rect: &Rect<f32>, values: &CornerValues, position: &Point2D<f32>)
-> Point2D<f32> {
let upper = values.upper_left.lerp(values.upper_right,
(position.x - rect.min_x()) / rect.size.width);
let lower = values.lower_left.lerp(values.lower_right,
(position.x - rect.min_x()) / rect.size.width);
upper.lerp(lower, (position.y - rect.min_y()) / rect.size.height)
}
}
}

View File

@ -11,6 +11,7 @@
#version 100 #version 100
#extension GL_EXT_frag_depth : require #extension GL_EXT_frag_depth : require
#extension GL_OES_standard_derivatives : require
#define LCD_FILTER_FACTOR_0 (86.0 / 255.0) #define LCD_FILTER_FACTOR_0 (86.0 / 255.0)
#define LCD_FILTER_FACTOR_1 (77.0 / 255.0) #define LCD_FILTER_FACTOR_1 (77.0 / 255.0)
@ -104,65 +105,11 @@ float convertPathIndexToViewportDepthValue(int pathIndex) {
return float(pathIndex) / float(MAX_PATHS) * 2.0 - 1.0; return float(pathIndex) / float(MAX_PATHS) * 2.0 - 1.0;
} }
/// Packs the given path ID into a floating point value suitable for storage in the depth buffer.
///
/// This function returns values in window space (i.e. what `gl_FragDepth`/`gl_FragDepthEXT` is
/// in).
float convertPathIndexToWindowDepthValue(int pathIndex) {
return float(pathIndex) / float(MAX_PATHS);
}
/// Displaces the given point by the given distance in the direction of the normal angle. /// Displaces the given point by the given distance in the direction of the normal angle.
vec2 dilatePosition(vec2 position, float normalAngle, vec2 amount) { vec2 dilatePosition(vec2 position, float normalAngle, vec2 amount) {
return position + vec2(cos(normalAngle), -sin(normalAngle)) * amount; return position + vec2(cos(normalAngle), -sin(normalAngle)) * amount;
} }
vec2 offsetPositionVertically(vec2 position, ivec2 framebufferSize, bool roundUp) {
position = convertClipToScreenSpace(position, framebufferSize);
position.y = roundUp ? ceil(position.y + 1.0) : floor(position.y - 1.0);
return convertScreenToClipSpace(position, framebufferSize);
}
vec2 computeMCAAPosition(vec2 position,
vec4 hints,
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 slope,
bool snapToPixelGrid) {
position = hintPosition(position, hints);
position = transformVertexPositionST(position, localTransformST);
position = transformVertexPositionST(position, globalTransformST);
position = convertClipToScreenSpace(position, framebufferSize);
float xNudge;
if (snapToPixelGrid) {
xNudge = fract(position.x);
if (xNudge < 0.5)
xNudge = -xNudge;
else
xNudge = 1.0 - xNudge;
} else {
xNudge = 0.0;
}
return position + vec2(xNudge, xNudge * slope);
}
vec2 transformECAAPosition(vec2 position, vec2 transformECAAPosition(vec2 position,
vec4 localTransformST, vec4 localTransformST,
vec2 localTransformExt, vec2 localTransformExt,
@ -409,21 +356,6 @@ float computeCoverage(vec2 p0X, vec2 dPX, float pixelCenterY, float winding) {
return abs(area) * winding * 0.5; return abs(area) * winding * 0.5;
} }
/// Returns true if the line runs through this pixel or false otherwise.
///
/// The line must run left-to-right and must already be clipped to the left and right sides of the
/// pixel, which implies that `dP.x` must be within the range [0.0, 1.0].
///
/// * `p0X` is the start point of the line.
/// * `dPX` is the vector from the start point to the endpoint of the line.
/// * `pixelCenterY` is the Y coordinate of the center of the pixel in window coordinates (i.e.
/// `gl_FragCoord.y`).
bool isPartiallyCovered(vec2 p0X, vec2 dPX, float pixelCenterY) {
float pixelTop;
vec2 dP = clipLineToPixelRow(p0X, dPX, pixelCenterY, pixelTop).zw;
return !isNearZero(dP.x) || !isNearZero(dP.y);
}
/// Solves the equation: /// Solves the equation:
/// ///
/// x = p0x + t^2 * (p0x - 2*p1x + p2x) + t*(2*p1x - 2*p0x) /// x = p0x + t^2 * (p0x - 2*p1x + p2x) + t*(2*p1x - 2*p0x)
@ -482,3 +414,11 @@ vec4 fetchPathAffineTransform(out vec2 outPathTransformExt,
pathTransformExtDimensions); pathTransformExtDimensions);
return fetchFloat4Data(pathTransformSTTexture, pathID, pathTransformSTDimensions); return fetchFloat4Data(pathTransformSTTexture, pathID, pathTransformSTDimensions);
} }
float detMat2(mat2 m) {
return m[0][0] * m[1][1] - m[0][1] * m[1][0];
}
mat2 invMat2(mat2 m) {
return mat2(m[1][1], -m[0][1], -m[1][0], m[0][0]) / detMat2(m);
}

View File

@ -1,6 +1,6 @@
// pathfinder/shaders/gles2/direct-interior.vs.glsl // pathfinder/shaders/gles2/conservative-interior.vs.glsl
// //
// Copyright (c) 2017 The Pathfinder Project Developers. // Copyright (c) 2018 The Pathfinder Project Developers.
// //
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
@ -17,8 +17,9 @@
precision highp float; precision highp float;
/// A 3D transform to be applied to all points. /// An affine transform to be applied to all points.
uniform mat4 uTransform; uniform vec4 uTransformST;
uniform vec2 uTransformExt;
/// Vertical snapping positions. /// Vertical snapping positions.
uniform vec4 uHints; uniform vec4 uHints;
/// The framebuffer size in pixels. /// The framebuffer size in pixels.
@ -52,21 +53,21 @@ void main() {
int pathID = int(aPathID); int pathID = int(aPathID);
int vertexID = int(aVertexID); int vertexID = int(aVertexID);
vec2 pathTransformExt; vec4 transformST = fetchFloat4Data(uPathTransformST, pathID, uPathTransformSTDimensions);
vec4 pathTransformST = fetchPathAffineTransform(pathTransformExt,
uPathTransformST,
uPathTransformSTDimensions,
uPathTransformExt,
uPathTransformExtDimensions,
pathID);
vec2 position = hintPosition(aPosition, uHints); mat2 globalTransformLinear = mat2(uTransformST.x, uTransformExt, uTransformST.y);
position = transformVertexPositionAffine(position, pathTransformST, pathTransformExt); mat2 localTransformLinear = mat2(transformST.x, 0.0, 0.0, transformST.y);
position = transformVertexPosition(position, uTransform); mat2 transformLinear = globalTransformLinear * localTransformLinear;
position = offsetPositionVertically(position, uFramebufferSize, imod(vertexID, 6) < 3);
vec2 translation = uTransformST.zw + globalTransformLinear * transformST.zw;
float onePixel = 2.0 / float(uFramebufferSize.y);
float dilation = length(invMat2(transformLinear) * vec2(0.0, onePixel));
vec2 position = aPosition + vec2(0.0, imod(vertexID, 6) < 3 ? dilation : -dilation);
position = transformLinear * position + translation;
float depth = convertPathIndexToViewportDepthValue(pathID); float depth = convertPathIndexToViewportDepthValue(pathID);
gl_Position = vec4(position, depth, 1.0);
gl_Position = vec4(position, depth, 1.0);
vColor = fetchFloat4Data(uPathColors, pathID, uPathColorsDimensions); vColor = fetchFloat4Data(uPathColors, pathID, uPathColorsDimensions);
} }

View File

@ -1,6 +1,6 @@
// pathfinder/shaders/gles2/mcaa.fs.glsl // pathfinder/shaders/gles2/mcaa.fs.glsl
// //
// Copyright (c) 2017 The Pathfinder Project Developers. // Copyright (c) 2018 The Pathfinder Project Developers.
// //
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
@ -12,68 +12,43 @@
//! (MCAA). This one shader handles both lines and curves. //! (MCAA). This one shader handles both lines and curves.
//! //!
//! This shader expects to render to a standard RGB color buffer. //! This shader expects to render to a standard RGB color buffer.
//!
//! Use this shader only when *both* of the following are true:
//!
//! 1. You are rendering multiple multicolor paths. (Otherwise, consider the
//! other MCAA shaders, which render with higher quality.)
//!
//! 2. Your transform is only a scale and/or translation, not a perspective,
//! rotation, or skew. (Otherwise, consider repartitioning the path to
//! generate a new mesh, or, alternatively, use the direct Loop-Blinn
//! shaders.)
precision highp float; precision highp float;
/// True if multiple colors are being rendered; false otherwise.
///
/// If this is true, then points will be snapped to the nearest pixel.
uniform bool uMulticolor;
varying vec4 vUpperEndpoints;
varying vec4 vLowerEndpoints;
varying vec4 vControlPoints;
varying vec4 vColor; varying vec4 vColor;
varying vec4 vUV;
varying vec4 vSignMode;
float computeCoverageForSide(vec2 p0, vec2 cp, vec2 p1, float winding) { // Cubic approximation to the square area coverage, accurate to about 4%.
// Compute pixel extents. float estimateArea(float dist) {
vec2 pixelCenter = gl_FragCoord.xy; if (dist >= 0.707107)
vec2 pixelColumnBounds = pixelCenter.xx + vec2(-0.5, 0.5); return 1.0;
// Catch NaNs here.
if (!(dist > -0.707107))
return 0.0;
return 0.5 + 1.14191 * dist - 0.83570 * dist * dist * dist;
}
vec2 clippedP0, clippedDP; float computeAlpha(vec2 uv, float curveSign, float mode) {
if (cp == vec2(0.0)) { vec2 dUVDX = dFdx(uv), dUVDY = dFdy(uv);
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. // u^2 - v for curves inside uv square; u - v otherwise.
if (!(p0.x < pixelColumnBounds.x)) float g = uv.x;
t.x = 0.0; vec2 dG = vec2(dUVDX.x, dUVDY.x);
if (!(p1.x > pixelColumnBounds.y)) if (mode > 0.0 && uv.x > 0.0 && uv.x < 1.0 && uv.y > 0.0 && uv.y < 1.0) {
t.y = 1.0; g *= uv.x;
dG *= 2.0 * uv.x;
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;
} }
g -= uv.y;
dG -= vec2(dUVDX.y, dUVDY.y);
return computeCoverage(clippedP0, clippedDP, pixelCenter.y, winding); float signedDistance = g / length(dG);
return estimateArea(signedDistance * curveSign);
} }
void main() { void main() {
float alpha = computeCoverageForSide(vLowerEndpoints.xy, float alpha = 1.0;
vControlPoints.zw, alpha -= computeAlpha(vUV.xy, vSignMode.x, vSignMode.z);
vLowerEndpoints.zw, alpha -= computeAlpha(vUV.zw, vSignMode.y, vSignMode.w);
-1.0); gl_FragColor = alpha * vColor;
alpha += computeCoverageForSide(vUpperEndpoints.xy,
vControlPoints.xy,
vUpperEndpoints.zw,
1.0);
// Compute area.
vec4 color = uMulticolor ? vColor : vec4(1.0);
gl_FragColor = alpha * color;
} }

View File

@ -34,10 +34,9 @@
precision highp float; precision highp float;
/// A dilation (scale and translation) to be applied to the object. /// A scale and transform to be applied to the object.
uniform vec4 uTransformST; uniform vec4 uTransformST;
/// Vertical snapping positions. uniform vec2 uTransformExt;
uniform vec4 uHints;
/// The framebuffer size in pixels. /// The framebuffer size in pixels.
uniform ivec2 uFramebufferSize; uniform ivec2 uFramebufferSize;
/// The size of the path transform buffer texture in texels. /// The size of the path transform buffer texture in texels.
@ -54,120 +53,47 @@ uniform sampler2D uPathColors;
uniform bool uMulticolor; uniform bool uMulticolor;
attribute vec2 aTessCoord; attribute vec2 aTessCoord;
attribute vec2 aUpperLeftEndpointPosition; attribute vec4 aRect;
attribute vec2 aUpperControlPointPosition; attribute vec4 aUV;
attribute vec2 aUpperRightEndpointPosition; attribute vec4 aDUVDX;
attribute vec2 aLowerRightEndpointPosition; attribute vec4 aDUVDY;
attribute vec2 aLowerControlPointPosition; attribute vec4 aSignMode;
attribute vec2 aLowerLeftEndpointPosition;
attribute float aPathID; attribute float aPathID;
varying vec4 vUpperEndpoints;
varying vec4 vLowerEndpoints;
varying vec4 vControlPoints;
varying vec4 vColor; varying vec4 vColor;
varying vec4 vUV;
varying vec4 vSignMode;
void main() { void main() {
vec2 tlPosition = aUpperLeftEndpointPosition;
vec2 tcPosition = aUpperControlPointPosition;
vec2 trPosition = aUpperRightEndpointPosition;
vec2 blPosition = aLowerLeftEndpointPosition;
vec2 bcPosition = aLowerControlPointPosition;
vec2 brPosition = aLowerRightEndpointPosition;
vec2 tessCoord = aTessCoord; vec2 tessCoord = aTessCoord;
int pathID = int(floor(aPathID)); int pathID = int(floor(aPathID));
vec4 color;
if (uMulticolor)
color = fetchFloat4Data(uPathColors, pathID, uPathColorsDimensions);
else
color = vec4(1.0);
vec4 transformST = fetchFloat4Data(uPathTransformST, pathID, uPathTransformSTDimensions); vec4 transformST = fetchFloat4Data(uPathTransformST, pathID, uPathTransformSTDimensions);
if (abs(transformST.x) > 0.001 && abs(transformST.y) > 0.001) {
vec4 color = fetchFloat4Data(uPathColors, pathID, uPathColorsDimensions);
vec2 topVector = trPosition - tlPosition, bottomVector = brPosition - blPosition; mat2 globalTransformLinear = mat2(uTransformST.x, uTransformExt, uTransformST.y);
mat2 localTransformLinear = mat2(transformST.x, 0.0, 0.0, transformST.y);
mat2 rectTransformLinear = mat2(aRect.z - aRect.x, 0.0, 0.0, aRect.w - aRect.y);
mat2 transformLinear = globalTransformLinear * localTransformLinear * rectTransformLinear;
float topSlope = topVector.y / topVector.x; vec2 translation = transformST.zw + localTransformLinear * aRect.xy;
float bottomSlope = bottomVector.y / bottomVector.x; translation = uTransformST.zw + globalTransformLinear * translation;
if (abs(topSlope) > MAX_SLOPE)
topSlope = sign(topSlope) * MAX_SLOPE;
if (abs(bottomSlope) > MAX_SLOPE)
bottomSlope = sign(bottomSlope) * MAX_SLOPE;
// Transform the points, and compute the position of this vertex. float onePixel = 2.0 / float(uFramebufferSize.y);
tlPosition = computeMCAASnappedPosition(tlPosition, float dilation = length(invMat2(transformLinear) * vec2(0.0, onePixel));
uHints, tessCoord.y += tessCoord.y < 0.5 ? -dilation : dilation;
transformST,
uTransformST,
uFramebufferSize,
topSlope,
uMulticolor);
trPosition = computeMCAASnappedPosition(trPosition,
uHints,
transformST,
uTransformST,
uFramebufferSize,
topSlope,
uMulticolor);
tcPosition = computeMCAAPosition(tcPosition,
uHints,
transformST,
uTransformST,
uFramebufferSize);
blPosition = computeMCAASnappedPosition(blPosition,
uHints,
transformST,
uTransformST,
uFramebufferSize,
bottomSlope,
uMulticolor);
brPosition = computeMCAASnappedPosition(brPosition,
uHints,
transformST,
uTransformST,
uFramebufferSize,
bottomSlope,
uMulticolor);
bcPosition = computeMCAAPosition(bcPosition,
uHints,
transformST,
uTransformST,
uFramebufferSize);
float depth = convertPathIndexToViewportDepthValue(pathID); vec2 position = transformLinear * tessCoord + translation;
vec4 uv = aUV + tessCoord.x * aDUVDX + tessCoord.y * aDUVDY;
float depth = convertPathIndexToViewportDepthValue(pathID);
// Use the same side--in this case, the top--or else floating point error during gl_Position = vec4(position, depth, 1.0);
// partitioning can occasionally cause inconsistent rounding, resulting in cracks. vColor = color;
vec2 position; vUV = uv;
if (tessCoord.y < 0.5) { vSignMode = aSignMode;
if (tessCoord.x < 0.25)
position = tlPosition;
else if (tessCoord.x < 0.75)
position = tcPosition;
else
position = trPosition;
position.y = floor(position.y - 0.5);
} else {
if (tessCoord.x < 0.25)
position = blPosition;
else if (tessCoord.x < 0.75)
position = bcPosition;
else
position = brPosition;
position.y = ceil(position.y + 0.5);
}
if (!uMulticolor) {
if (tessCoord.x < 0.25)
position.x = floor(position.x);
else if (tessCoord.x >= 0.75)
position.x = ceil(position.x);
}
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;
} else {
gl_Position = vec4(0.0);
}
} }