Simplify the B-quad vertex position VBO to have a uniform format, and

enable early Z for SVG.

Additionally, this switches the B-quad patches for XCAA to be convex
hulls instead of bounding boxes, reducing fragment shader load.

This is a large speedup for the Ghostscript tiger demo: 5x or more.

Closes #33.
This commit is contained in:
Patrick Walton 2018-01-07 11:14:24 -08:00
parent 67dd6cd8ed
commit 6a640eca74
27 changed files with 734 additions and 994 deletions

View File

@ -7,3 +7,6 @@ members = [
"utils/frontend",
"utils/gamma-lut",
]
[patch.crates-io]
ring = { git = "https://github.com/SergioBenitez/ring", branch = "v0.12" }

View File

@ -18,7 +18,7 @@ import {DemoView} from './view';
export type AntialiasingStrategyName = 'none' | 'ssaa' | 'xcaa';
export type DirectRenderingMode = 'none' | 'color';
export type DirectRenderingMode = 'none' | 'conservative' | 'color';
export type SubpixelAAType = 'none' | 'medium';

View File

@ -44,7 +44,7 @@ export interface EXTDisjointTimerQuery {
export class WebGLQuery {}
export const QUAD_ELEMENTS: Uint8Array = new Uint8Array([2, 0, 1, 1, 3, 2]);
export const QUAD_ELEMENTS: Uint8Array = new Uint8Array([0, 1, 2, 1, 3, 2]);
export function createFramebufferColorTexture(gl: WebGLRenderingContext,
size: glmatrix.vec2,

View File

@ -268,9 +268,7 @@ class MeshDebuggerView extends PathfinderView {
context.font = `12px ${POINT_LABEL_FONT}`;
context.lineWidth = invScaleFactor;
const bQuads = new Uint32Array(meshes.bQuads);
const positions = new Float32Array(meshes.bVertexPositions);
const bVertexNormals = new Float32Array(meshes.bVertexNormals);
const bQuadVertexPositions = new Float32Array(meshes.bQuadVertexPositions);
const normals: NormalsTable<Float32Array> = {
lowerCurve: new Float32Array(0),
@ -279,54 +277,24 @@ class MeshDebuggerView extends PathfinderView {
upperLine: new Float32Array(0),
};
const drawnVertices: boolean[] = [], drawnNormals: boolean[] = [];
// Draw B-quads.
for (let bQuadIndex = 0; bQuadIndex < meshes.bQuadCount; bQuadIndex++) {
for (let bQuadIndex = 0; bQuadIndex < meshes.bQuadVertexPositionCount; bQuadIndex++) {
const bQuadStartOffset = (B_QUAD_SIZE * bQuadIndex) / UINT32_SIZE;
const upperLeftIndex = bQuads[bQuadStartOffset +
B_QUAD_UPPER_LEFT_VERTEX_OFFSET / UINT32_SIZE];
const upperRightIndex = bQuads[bQuadStartOffset +
B_QUAD_UPPER_RIGHT_VERTEX_OFFSET / UINT32_SIZE];
const upperControlPointIndex =
bQuads[bQuadStartOffset + B_QUAD_UPPER_CONTROL_POINT_VERTEX_OFFSET / UINT32_SIZE];
const lowerLeftIndex = bQuads[bQuadStartOffset +
B_QUAD_LOWER_LEFT_VERTEX_OFFSET / UINT32_SIZE];
const lowerRightIndex = bQuads[bQuadStartOffset +
B_QUAD_LOWER_RIGHT_VERTEX_OFFSET / UINT32_SIZE];
const lowerControlPointIndex =
bQuads[bQuadStartOffset + B_QUAD_LOWER_CONTROL_POINT_VERTEX_OFFSET / UINT32_SIZE];
const upperLeftPosition = unwrapNull(getPosition(positions, upperLeftIndex));
const upperRightPosition = unwrapNull(getPosition(positions, upperRightIndex));
const upperControlPointPosition = getPosition(positions, upperControlPointIndex);
const lowerLeftPosition = unwrapNull(getPosition(positions, lowerLeftIndex));
const lowerRightPosition = unwrapNull(getPosition(positions, lowerRightIndex));
const lowerControlPointPosition = getPosition(positions, lowerControlPointIndex);
const upperLeftPosition = getPosition(bQuadVertexPositions, bQuadIndex, 0);
const upperControlPointPosition = getPosition(bQuadVertexPositions, bQuadIndex, 1);
const upperRightPosition = getPosition(bQuadVertexPositions, bQuadIndex, 2);
const lowerRightPosition = getPosition(bQuadVertexPositions, bQuadIndex, 3);
const lowerControlPointPosition = getPosition(bQuadVertexPositions, bQuadIndex, 4);
const lowerLeftPosition = getPosition(bQuadVertexPositions, bQuadIndex, 5);
if (this.drawVertices) {
drawVertexIfNecessary(context,
drawnVertices,
upperLeftIndex,
upperLeftPosition,
invScaleFactor);
drawVertexIfNecessary(context,
drawnVertices,
upperRightIndex,
upperRightPosition,
invScaleFactor);
drawVertexIfNecessary(context,
drawnVertices,
lowerLeftIndex,
lowerLeftPosition,
invScaleFactor);
drawVertexIfNecessary(context,
drawnVertices,
lowerRightIndex,
lowerRightPosition,
invScaleFactor);
drawVertexIfNecessary(context, upperLeftPosition, invScaleFactor);
drawVertexIfNecessary(context, upperRightPosition, invScaleFactor);
drawVertexIfNecessary(context, lowerLeftPosition, invScaleFactor);
drawVertexIfNecessary(context, lowerRightPosition, invScaleFactor);
}
context.beginPath();
context.moveTo(upperLeftPosition[0], -upperLeftPosition[1]);
if (upperControlPointPosition != null) {
@ -366,22 +334,6 @@ class MeshDebuggerView extends PathfinderView {
context.moveTo(lowerLeftPosition[0], -lowerLeftPosition[1]);
context.lineTo(upperLeftPosition[0], -upperLeftPosition[1]);
context.stroke();
// Draw B-quad normals.
const lowerLeftNormal = bVertexNormals[lowerLeftIndex];
const lowerRightNormal = bVertexNormals[lowerRightIndex];
const upperLeftNormal = bVertexNormals[upperLeftIndex];
const upperRightNormal = bVertexNormals[upperRightIndex];
if (this.drawVertices && this.drawNormals) {
drawNormal(context, lowerLeftPosition, lowerLeftNormal, invScaleFactor,
'bVertex');
drawNormal(context, lowerRightPosition, lowerRightNormal, invScaleFactor,
'bVertex');
drawNormal(context, upperLeftPosition, upperLeftNormal, invScaleFactor,
'bVertex');
drawNormal(context, upperRightPosition, upperRightNormal, invScaleFactor,
'bVertex');
}
}
// Draw segments.
@ -429,10 +381,16 @@ class MeshDebuggerView extends PathfinderView {
}
}
function getPosition(positions: Float32Array, vertexIndex: number): glmatrix.vec2 | null {
if (vertexIndex === UINT32_MAX)
return null;
return glmatrix.vec2.clone([positions[vertexIndex * 2 + 0], positions[vertexIndex * 2 + 1]]);
function getPosition(positions: Float32Array, bQuadIndex: number, vertexIndex: number):
glmatrix.vec2 {
return glmatrix.vec2.clone([
positions[(bQuadIndex * 6 + vertexIndex) * 2 + 0],
positions[(bQuadIndex * 6 + vertexIndex) * 2 + 1],
]);
}
function getNormal(normals: Float32Array, bQuadIndex: number, vertexIndex: number): number {
return normals[bQuadIndex * 6 + vertexIndex];
}
function getNormals(normals: NormalsTable<Float32Array>,
@ -526,25 +484,12 @@ function drawSegmentVertices(context: CanvasRenderingContext2D,
}
function drawVertexIfNecessary(context: CanvasRenderingContext2D,
markedVertices: boolean[],
vertexIndex: number,
position: Float32Array,
invScaleFactor: number) {
if (markedVertices[vertexIndex] != null)
return;
markedVertices[vertexIndex] = true;
context.beginPath();
context.moveTo(position[0], -position[1]);
context.arc(position[0], -position[1], POINT_RADIUS * invScaleFactor, 0, 2.0 * Math.PI);
context.fill();
context.save();
context.scale(invScaleFactor, invScaleFactor);
context.fillText("" + vertexIndex,
position[0] / invScaleFactor + POINT_LABEL_OFFSET[0],
-position[1] / invScaleFactor + POINT_LABEL_OFFSET[1]);
context.restore();
}
function drawSegmentVertex(context: CanvasRenderingContext2D,

View File

@ -12,7 +12,7 @@ import * as base64js from 'base64-js';
import * as _ from 'lodash';
import {expectNotNull, FLOAT32_SIZE, panic, PathfinderError, Range, UINT16_SIZE} from './utils';
import {UINT32_MAX, UINT32_SIZE, unwrapNull, unwrapUndef} from './utils';
import {UINT32_MAX, UINT32_SIZE, UINT8_SIZE, unwrapNull, unwrapUndef} from './utils';
interface BufferTypeFourCCTable {
[fourCC: string]: keyof Meshes<void>;
@ -87,24 +87,9 @@ const SEGMENT_LINE_SIZE: number = 4 * 4;
const SEGMENT_CURVE_SIZE: number = 4 * 6;
const MESH_TYPES: Meshes<MeshBufferTypeDescriptor> = {
bQuadVertexInteriorIndices: { type: 'Uint32', size: 1 },
bQuadVertexPositionPathIDs: { type: 'Uint16', size: 6 },
bQuadVertexPositions: { type: 'Float32', size: 12 },
bQuads: { type: 'Uint32', size: B_QUAD_FIELD_COUNT },
bVertexLoopBlinnData: { type: 'Uint32', size: 1 },
bVertexNormals: { type: 'Float32', size: 1 },
bVertexPathIDs: { type: 'Uint16', size: 1 },
bVertexPositions: { type: 'Float32', size: 2 },
coverCurveIndices: { type: 'Uint32', size: 1 },
coverInteriorIndices: { type: 'Uint32', size: 1 },
edgeBoundingBoxPathIDs: { type: 'Uint16', size: 1 },
edgeBoundingBoxVertexPositions: { type: 'Float32', size: 4 },
edgeLowerCurvePathIDs: { type: 'Uint16', size: 1 },
edgeLowerCurveVertexPositions: { type: 'Float32', size: 6 },
edgeLowerLinePathIDs: { type: 'Uint16', size: 1 },
edgeLowerLineVertexPositions: { type: 'Float32', size: 4 },
edgeUpperCurvePathIDs: { type: 'Uint16', size: 1 },
edgeUpperCurveVertexPositions: { type: 'Float32', size: 6 },
edgeUpperLinePathIDs: { type: 'Uint16', size: 1 },
edgeUpperLineVertexPositions: { type: 'Float32', size: 4 },
segmentCurveNormals: { type: 'Float32', size: 3 },
segmentCurvePathIDs: { type: 'Uint16', size: 1 },
segmentCurves: { type: 'Float32', size: 6 },
@ -114,24 +99,9 @@ const MESH_TYPES: Meshes<MeshBufferTypeDescriptor> = {
};
const BUFFER_TYPES: Meshes<BufferType> = {
bQuadVertexInteriorIndices: 'ELEMENT_ARRAY_BUFFER',
bQuadVertexPositionPathIDs: 'ARRAY_BUFFER',
bQuadVertexPositions: 'ARRAY_BUFFER',
bQuads: 'ARRAY_BUFFER',
bVertexLoopBlinnData: 'ARRAY_BUFFER',
bVertexNormals: 'ARRAY_BUFFER',
bVertexPathIDs: 'ARRAY_BUFFER',
bVertexPositions: 'ARRAY_BUFFER',
coverCurveIndices: 'ELEMENT_ARRAY_BUFFER',
coverInteriorIndices: 'ELEMENT_ARRAY_BUFFER',
edgeBoundingBoxPathIDs: 'ARRAY_BUFFER',
edgeBoundingBoxVertexPositions: 'ARRAY_BUFFER',
edgeLowerCurvePathIDs: 'ARRAY_BUFFER',
edgeLowerCurveVertexPositions: 'ARRAY_BUFFER',
edgeLowerLinePathIDs: 'ARRAY_BUFFER',
edgeLowerLineVertexPositions: 'ARRAY_BUFFER',
edgeUpperCurvePathIDs: 'ARRAY_BUFFER',
edgeUpperCurveVertexPositions: 'ARRAY_BUFFER',
edgeUpperLinePathIDs: 'ARRAY_BUFFER',
edgeUpperLineVertexPositions: 'ARRAY_BUFFER',
segmentCurveNormals: 'ARRAY_BUFFER',
segmentCurvePathIDs: 'ARRAY_BUFFER',
segmentCurves: 'ARRAY_BUFFER',
@ -148,18 +118,8 @@ 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',
bqii: 'bQuadVertexInteriorIndices',
bqvp: 'bQuadVertexPositions',
bvlb: 'bVertexLoopBlinnData',
bvno: 'bVertexNormals',
bvpo: 'bVertexPositions',
cvci: 'coverCurveIndices',
cvii: 'coverInteriorIndices',
ebbv: 'edgeBoundingBoxVertexPositions',
elcv: 'edgeLowerCurveVertexPositions',
ellv: 'edgeLowerLineVertexPositions',
eucv: 'edgeUpperCurveVertexPositions',
eulv: 'edgeUpperLineVertexPositions',
scur: 'segmentCurves',
slin: 'segmentLines',
sncu: 'segmentCurveNormals',
@ -169,57 +129,28 @@ const BUFFER_TYPE_FOURCCS: BufferTypeFourCCTable = {
// Must match the FourCCs in
// `pathfinder_partitioner::mesh_library::MeshLibrary::serialize_into::write_path_ranges()`.
const PATH_RANGE_TYPE_FOURCCS: PathRangeTypeFourCCTable = {
bqua: 'bQuadPathRanges',
bqii: 'bQuadVertexInteriorIndexPathRanges',
bqvp: 'bQuadVertexPositionPathRanges',
bver: 'bVertexPathRanges',
cvci: 'coverCurveIndexRanges',
cvii: 'coverInteriorIndexRanges',
ebbo: 'edgeBoundingBoxRanges',
elci: 'edgeLowerCurveIndexRanges',
elli: 'edgeLowerLineIndexRanges',
euci: 'edgeUpperCurveIndexRanges',
euli: 'edgeUpperLineIndexRanges',
scur: 'segmentCurveRanges',
slin: 'segmentLineRanges',
};
const RANGE_TO_COUNT_TABLE: RangeToCountTable = {
bQuadPathRanges: 'bQuadCount',
bQuadVertexInteriorIndexPathRanges: 'bQuadVertexInteriorIndexCount',
bQuadVertexPositionPathRanges: 'bQuadVertexPositionCount',
bVertexPathRanges: 'bVertexCount',
coverCurveIndexRanges: 'coverCurveCount',
coverInteriorIndexRanges: 'coverInteriorCount',
edgeBoundingBoxRanges: 'edgeBoundingBoxCount',
edgeLowerCurveIndexRanges: 'edgeLowerCurveCount',
edgeLowerLineIndexRanges: 'edgeLowerLineCount',
edgeUpperCurveIndexRanges: 'edgeUpperCurveCount',
edgeUpperLineIndexRanges: 'edgeUpperLineCount',
segmentCurveRanges: 'segmentCurveCount',
segmentLineRanges: 'segmentLineCount',
};
const RANGE_TO_RANGE_BUFFER_TABLE: RangeToRangeBufferTable = {
bVertexPathRanges: 'bVertexPathIDs',
edgeBoundingBoxRanges: 'edgeBoundingBoxPathIDs',
edgeLowerCurveIndexRanges: 'edgeLowerCurvePathIDs',
edgeLowerLineIndexRanges: 'edgeLowerLinePathIDs',
edgeUpperCurveIndexRanges: 'edgeUpperCurvePathIDs',
edgeUpperLineIndexRanges: 'edgeUpperLinePathIDs',
bQuadVertexPositionPathRanges: 'bQuadVertexPositionPathIDs',
segmentCurveRanges: 'segmentCurvePathIDs',
segmentLineRanges: 'segmentLinePathIDs',
};
const RANGE_KEYS: Array<keyof PathRanges> = [
'bQuadPathRanges',
'bQuadVertexPositionPathRanges',
'bVertexPathRanges',
'coverInteriorIndexRanges',
'coverCurveIndexRanges',
'edgeBoundingBoxRanges',
'edgeUpperLineIndexRanges',
'edgeUpperCurveIndexRanges',
'edgeLowerLineIndexRanges',
'edgeLowerCurveIndexRanges',
'bQuadVertexInteriorIndexPathRanges',
'segmentCurveRanges',
'segmentLineRanges',
];
@ -227,113 +158,51 @@ const RANGE_KEYS: Array<keyof PathRanges> = [
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;
readonly coverInteriorIndices: T;
readonly coverCurveIndices: T;
readonly edgeBoundingBoxVertexPositions: T;
readonly edgeLowerCurveVertexPositions: T;
readonly edgeLowerLineVertexPositions: T;
readonly edgeUpperCurveVertexPositions: T;
readonly edgeUpperLineVertexPositions: T;
readonly bQuadVertexInteriorIndices: T;
readonly segmentLines: T;
readonly segmentCurves: T;
readonly segmentLineNormals: T;
readonly segmentCurveNormals: T;
bVertexPathIDs: T;
edgeBoundingBoxPathIDs: T;
bQuadVertexPositionPathIDs: T;
segmentLinePathIDs: T;
segmentCurvePathIDs: T;
edgeLowerCurvePathIDs: T;
edgeLowerLinePathIDs: T;
edgeUpperCurvePathIDs: T;
edgeUpperLinePathIDs: T;
}
interface MeshDataCounts {
readonly bQuadCount: number;
readonly bQuadVertexPositionCount: number;
readonly bVertexCount: number;
readonly coverCurveCount: number;
readonly coverInteriorCount: number;
readonly edgeBoundingBoxCount: number;
readonly edgeLowerCurveCount: number;
readonly edgeUpperCurveCount: number;
readonly edgeLowerLineCount: number;
readonly edgeUpperLineCount: number;
readonly bQuadVertexInteriorIndexCount: number;
readonly segmentLineCount: number;
readonly segmentCurveCount: number;
}
interface PathRanges {
bQuadPathRanges: Range[];
bQuadVertexPositionPathRanges: Range[];
bVertexPathRanges: Range[];
coverInteriorIndexRanges: Range[];
coverCurveIndexRanges: Range[];
edgeBoundingBoxRanges: Range[];
edgeUpperLineIndexRanges: Range[];
edgeUpperCurveIndexRanges: Range[];
edgeLowerLineIndexRanges: Range[];
edgeLowerCurveIndexRanges: Range[];
bQuadVertexInteriorIndexPathRanges: Range[];
segmentCurveRanges: Range[];
segmentLineRanges: Range[];
}
export class PathfinderMeshData implements Meshes<ArrayBuffer>, MeshDataCounts, PathRanges {
readonly bQuads: ArrayBuffer;
readonly bQuadVertexPositions: ArrayBuffer;
readonly bVertexPositions: ArrayBuffer;
readonly bVertexLoopBlinnData: ArrayBuffer;
readonly bVertexNormals: ArrayBuffer;
readonly coverInteriorIndices: ArrayBuffer;
readonly coverCurveIndices: ArrayBuffer;
readonly edgeBoundingBoxVertexPositions: ArrayBuffer;
readonly edgeLowerCurveVertexPositions: ArrayBuffer;
readonly edgeLowerLineVertexPositions: ArrayBuffer;
readonly edgeUpperCurveVertexPositions: ArrayBuffer;
readonly edgeUpperLineVertexPositions: ArrayBuffer;
readonly bQuadVertexInteriorIndices: ArrayBuffer;
readonly segmentLines: ArrayBuffer;
readonly segmentCurves: ArrayBuffer;
readonly segmentLineNormals: ArrayBuffer;
readonly segmentCurveNormals: ArrayBuffer;
readonly bQuadCount: number;
readonly bQuadVertexPositionCount: number;
readonly bVertexCount: number;
readonly coverCurveCount: number;
readonly coverInteriorCount: number;
readonly edgeBoundingBoxCount: number;
readonly edgeLowerCurveCount: number;
readonly edgeUpperCurveCount: number;
readonly edgeLowerLineCount: number;
readonly edgeUpperLineCount: number;
readonly bQuadVertexInteriorIndexCount: number;
readonly segmentLineCount: number;
readonly segmentCurveCount: number;
bVertexPathIDs: ArrayBuffer;
edgeBoundingBoxPathIDs: ArrayBuffer;
edgeLowerCurvePathIDs: ArrayBuffer;
edgeLowerLinePathIDs: ArrayBuffer;
edgeUpperCurvePathIDs: ArrayBuffer;
edgeUpperLinePathIDs: ArrayBuffer;
bQuadVertexPositionPathIDs: ArrayBuffer;
segmentCurvePathIDs: ArrayBuffer;
segmentLinePathIDs: ArrayBuffer;
bQuadPathRanges: Range[];
bQuadVertexPositionPathRanges: Range[];
bVertexPathRanges: Range[];
coverInteriorIndexRanges: Range[];
coverCurveIndexRanges: Range[];
edgeBoundingBoxRanges: Range[];
edgeUpperLineIndexRanges: Range[];
edgeUpperCurveIndexRanges: Range[];
edgeLowerLineIndexRanges: Range[];
edgeLowerCurveIndexRanges: Range[];
bQuadVertexInteriorIndexPathRanges: Range[];
segmentCurveRanges: Range[];
segmentLineRanges: Range[];
@ -368,22 +237,10 @@ export class PathfinderMeshData implements Meshes<ArrayBuffer>, MeshDataCounts,
this[range] = ranges[range];
}
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;
this.edgeBoundingBoxCount = this.edgeBoundingBoxVertexPositions.byteLength /
EDGE_BOUNDING_BOX_VERTEX_POSITION_SIZE;
this.edgeUpperLineCount = this.edgeUpperLineVertexPositions.byteLength /
EDGE_UPPER_LINE_VERTEX_POSITION_SIZE;
this.edgeLowerLineCount = this.edgeLowerLineVertexPositions.byteLength /
EDGE_LOWER_LINE_VERTEX_POSITION_SIZE;
this.edgeUpperCurveCount = this.edgeUpperCurveVertexPositions.byteLength /
EDGE_UPPER_CURVE_VERTEX_POSITION_SIZE;
this.edgeLowerCurveCount = this.edgeLowerCurveVertexPositions.byteLength /
EDGE_LOWER_CURVE_VERTEX_POSITION_SIZE;
this.bQuadVertexInteriorIndexCount = this.bQuadVertexInteriorIndices.byteLength /
INDEX_SIZE;
this.segmentCurveCount = this.segmentCurves.byteLength / SEGMENT_CURVE_SIZE;
this.segmentLineCount = this.segmentLines.byteLength / SEGMENT_LINE_SIZE;
@ -418,26 +275,8 @@ export class PathfinderMeshData implements Meshes<ArrayBuffer>, MeshDataCounts,
const expandedPathID = newPathIndex + 1;
const originalPathID = pathIDs[newPathIndex];
const bVertexCopyResult = copyVertices(['bVertexPositions',
'bVertexLoopBlinnData',
'bVertexNormals'],
'bVertexPathRanges',
expandedArrays,
expandedRanges,
originalBuffers,
originalRanges,
expandedPathID,
originalPathID);
if (bVertexCopyResult == null)
continue;
const firstExpandedBVertexIndex = bVertexCopyResult.expandedStartIndex;
const firstBVertexIndex = bVertexCopyResult.originalStartIndex;
const lastBVertexIndex = bVertexCopyResult.originalEndIndex;
// Copy over B-quad vertex positions.
copyVertices(['bQuadVertexPositions'],
const bQuadVertexCopyResult = copyVertices(['bQuadVertexPositions'],
'bQuadVertexPositionPathRanges',
expandedArrays,
expandedRanges,
@ -446,62 +285,21 @@ export class PathfinderMeshData implements Meshes<ArrayBuffer>, MeshDataCounts,
expandedPathID,
originalPathID);
// Copy over edge data.
copyVertices(['edgeBoundingBoxVertexPositions'],
'edgeBoundingBoxRanges',
expandedArrays,
expandedRanges,
originalBuffers,
originalRanges,
expandedPathID,
originalPathID);
for (const edgeBufferName of EDGE_BUFFER_NAMES) {
copyVertices([`edge${edgeBufferName}VertexPositions` as keyof Meshes<void>],
`edge${edgeBufferName}IndexRanges` as keyof PathRanges,
expandedArrays,
expandedRanges,
originalBuffers,
originalRanges,
expandedPathID,
originalPathID);
}
if (bQuadVertexCopyResult == null)
continue;
const firstExpandedBQuadVertexIndex = bQuadVertexCopyResult.expandedStartIndex;
const firstBQuadVertexIndex = bQuadVertexCopyResult.originalStartIndex;
const lastBQuadVertexIndex = bQuadVertexCopyResult.originalEndIndex;
// Copy over indices.
copyIndices(expandedArrays.coverInteriorIndices,
expandedRanges.coverInteriorIndexRanges,
originalBuffers.coverInteriorIndices as Uint32Array,
firstExpandedBVertexIndex,
firstBVertexIndex,
lastBVertexIndex,
copyIndices(expandedArrays.bQuadVertexInteriorIndices,
expandedRanges.bQuadVertexInteriorIndexPathRanges,
originalBuffers.bQuadVertexInteriorIndices as Uint32Array,
firstExpandedBQuadVertexIndex * 6,
firstBQuadVertexIndex * 6,
lastBQuadVertexIndex * 6,
expandedPathID);
copyIndices(expandedArrays.coverCurveIndices,
expandedRanges.coverCurveIndexRanges,
originalBuffers.coverCurveIndices as Uint32Array,
firstExpandedBVertexIndex,
firstBVertexIndex,
lastBVertexIndex,
expandedPathID);
// Copy over B-quads.
const originalBQuadRange = originalRanges.bQuadPathRanges[originalPathID - 1];
const firstExpandedBQuadIndex = expandedArrays.bQuads.length / B_QUAD_FIELD_COUNT;
expandedRanges.bQuadPathRanges[expandedPathID - 1] =
new Range(firstExpandedBQuadIndex,
firstExpandedBQuadIndex + originalBQuadRange.length);
const indexDelta = firstExpandedBVertexIndex - firstBVertexIndex;
for (let bQuadIndex = originalBQuadRange.start;
bQuadIndex < originalBQuadRange.end;
bQuadIndex++) {
const bQuad = originalBuffers.bQuads[bQuadIndex];
for (let indexIndex = 0; indexIndex < B_QUAD_FIELD_COUNT; indexIndex++) {
const srcIndex = originalBuffers.bQuads[bQuadIndex * B_QUAD_FIELD_COUNT +
indexIndex];
if (srcIndex === UINT32_MAX)
expandedArrays.bQuads.push(srcIndex);
else
expandedArrays.bQuads.push(srcIndex + indexDelta);
}
}
// Copy over segments.
copySegments(['segmentLines', 'segmentLineNormals'],
@ -560,43 +358,33 @@ export class PathfinderMeshData implements Meshes<ArrayBuffer>, MeshDataCounts,
if (!RANGE_TO_RANGE_BUFFER_TABLE.hasOwnProperty(rangeKey))
continue;
const count = this[RANGE_TO_COUNT_TABLE[rangeKey]];
const rangeBufferKey = RANGE_TO_RANGE_BUFFER_TABLE[rangeKey];
const instanceCount = this[RANGE_TO_COUNT_TABLE[rangeKey]];
const fieldCount = MESH_TYPES[rangeBufferKey].size;
const ranges = this[rangeKey as keyof PathRanges];
const destBuffer = new Uint16Array(count);
const destBuffer = new Uint16Array(instanceCount * fieldCount);
let destIndex = 0;
for (let pathIndex = 0; pathIndex < ranges.length; pathIndex++) {
const range = ranges[pathIndex];
for (let subindex = range.start; subindex < range.end; subindex++) {
for (let fieldIndex = 0; fieldIndex < fieldCount; fieldIndex++) {
destBuffer[destIndex] = pathIndex + 1;
destIndex++;
}
}
}
(this as any)[RANGE_TO_RANGE_BUFFER_TABLE[rangeKey]] = destBuffer;
(this as any)[rangeBufferKey] = destBuffer.buffer as ArrayBuffer;
}
}
}
export class PathfinderMeshBuffers implements Meshes<WebGLBuffer>, PathRanges {
readonly bQuads: WebGLBuffer;
readonly bQuadVertexPositions: WebGLBuffer;
readonly bVertexPositions: WebGLBuffer;
readonly bVertexPathIDs: WebGLBuffer;
readonly bVertexLoopBlinnData: WebGLBuffer;
readonly bVertexNormals: WebGLBuffer;
readonly coverInteriorIndices: WebGLBuffer;
readonly coverCurveIndices: WebGLBuffer;
readonly edgeBoundingBoxPathIDs: WebGLBuffer;
readonly edgeBoundingBoxVertexPositions: WebGLBuffer;
readonly edgeLowerCurvePathIDs: WebGLBuffer;
readonly edgeLowerCurveVertexPositions: WebGLBuffer;
readonly edgeLowerLinePathIDs: WebGLBuffer;
readonly edgeLowerLineVertexPositions: WebGLBuffer;
readonly edgeUpperCurvePathIDs: WebGLBuffer;
readonly edgeUpperCurveVertexPositions: WebGLBuffer;
readonly edgeUpperLinePathIDs: WebGLBuffer;
readonly edgeUpperLineVertexPositions: WebGLBuffer;
readonly bQuadVertexPositionPathIDs: WebGLBuffer;
readonly bQuadVertexInteriorIndices: WebGLBuffer;
readonly segmentLines: WebGLBuffer;
readonly segmentCurves: WebGLBuffer;
readonly segmentLinePathIDs: WebGLBuffer;
@ -604,16 +392,8 @@ export class PathfinderMeshBuffers implements Meshes<WebGLBuffer>, PathRanges {
readonly segmentLineNormals: WebGLBuffer;
readonly segmentCurveNormals: WebGLBuffer;
readonly bQuadPathRanges: Range[];
readonly bQuadVertexPositionPathRanges: Range[];
readonly bVertexPathRanges: Range[];
readonly coverInteriorIndexRanges: Range[];
readonly coverCurveIndexRanges: Range[];
readonly edgeBoundingBoxRanges: Range[];
readonly edgeUpperLineIndexRanges: Range[];
readonly edgeUpperCurveIndexRanges: Range[];
readonly edgeLowerLineIndexRanges: Range[];
readonly edgeLowerCurveIndexRanges: Range[];
readonly bQuadVertexInteriorIndexPathRanges: Range[];
readonly segmentCurveRanges: Range[];
readonly segmentLineRanges: Range[];

View File

@ -11,7 +11,8 @@
import * as glmatrix from 'gl-matrix';
import * as _ from 'lodash';
import {AntialiasingStrategy, AntialiasingStrategyName, GammaCorrectionMode} from './aa-strategy';
import {AntialiasingStrategy, AntialiasingStrategyName, DirectRenderingMode} from './aa-strategy';
import {GammaCorrectionMode} from './aa-strategy';
import {TileInfo} from './aa-strategy';
import {NoAAStrategy, StemDarkeningMode, SubpixelAAType} from './aa-strategy';
import {AAOptions} from './app-controller';
@ -24,6 +25,8 @@ import {RenderContext, Timings} from "./view";
const MAX_PATHS: number = 65535;
const MAX_VERTICES: number = 4 * 1024 * 1024;
const TIME_INTERVAL_DELAY: number = 32;
const B_LOOP_BLINN_DATA_SIZE: number = 4;
@ -92,6 +95,7 @@ export abstract class Renderer {
private gammaLUTTexture: WebGLTexture | null;
private instancedPathIDVBO: WebGLBuffer | null;
private vertexIDVBO: WebGLBuffer | null;
private timerQueryPollInterval: number | null;
constructor(renderContext: RenderContext) {
@ -110,6 +114,7 @@ export abstract class Renderer {
if (this.pathIDsAreInstanced)
this.initInstancedPathIDVBO();
this.initVertexIDVBO();
this.initGammaLUTTexture();
this.antialiasingStrategy = new NoAAStrategy(0, 'none');
@ -154,6 +159,17 @@ export abstract class Renderer {
const objectCount = this.objectCount;
for (let objectIndex = 0; objectIndex < objectCount; objectIndex++) {
if (antialiasingStrategy.directRenderingMode !== 'none') {
// Prepare for direct rendering.
antialiasingStrategy.prepareToRenderObject(this, objectIndex);
// Clear.
this.clearForDirectRendering(objectIndex);
// Perform direct rendering (Loop-Blinn).
this.directlyRenderObject(pass, objectIndex);
}
// Antialias.
antialiasingStrategy.antialiasObject(this, objectIndex);
@ -168,17 +184,6 @@ export abstract class Renderer {
renderContext.compositingTimerQuery);
}
// Perform direct rendering (Loop-Blinn).
if (antialiasingStrategy.directRenderingMode !== 'none') {
// Prepare for direct rendering.
antialiasingStrategy.prepareToRenderObject(this, objectIndex);
// Clear.
this.clearForDirectRendering(objectIndex);
this.directlyRenderObject(pass, objectIndex);
}
// Perform post-antialiasing tasks.
antialiasingStrategy.finishAntialiasingObject(this, objectIndex);
@ -351,7 +356,7 @@ export abstract class Renderer {
pathRangeForObject(objectIndex: number): Range {
if (this.meshes == null)
return new Range(0, 0);
const bVertexPathRanges = this.meshes[objectIndex].bVertexPathRanges;
const bVertexPathRanges = this.meshes[objectIndex].bQuadVertexPositionPathRanges;
return new Range(1, bVertexPathRanges.length + 1);
}
@ -382,7 +387,8 @@ export abstract class Renderer {
PathTransformBuffers<Float32Array>;
protected abstract directCurveProgramName(): keyof ShaderMap<void>;
protected abstract directInteriorProgramName(): keyof ShaderMap<void>;
protected abstract directInteriorProgramName(renderingMode: DirectRenderingMode):
keyof ShaderMap<void>;
protected drawSceneryIfNecessary(): void {}
@ -409,17 +415,9 @@ export abstract class Renderer {
return;
gl.clearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]);
switch (renderingMode) {
case 'color':
gl.clearDepth(0.0);
gl.depthMask(true);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
break;
case 'none':
// Nothing to do.
break;
}
}
protected getModelviewTransform(pathIndex: number): glmatrix.mat4 {
@ -460,24 +458,27 @@ export abstract class Renderer {
const pathRange = this.pathRangeForObject(objectIndex);
const meshIndex = this.meshIndexForObject(objectIndex);
const meshes = this.meshes[meshIndex];
const meshData = this.meshData[meshIndex];
const meshes = this.meshes![meshIndex];
const meshData = this.meshData![meshIndex];
// Set up implicit cover state.
gl.depthFunc(gl.GREATER);
gl.depthMask(true);
gl.enable(gl.DEPTH_TEST);
gl.disable(gl.BLEND);
gl.cullFace(gl.BACK);
gl.frontFace(gl.CCW);
gl.enable(gl.CULL_FACE);
// Set up the implicit cover interior VAO.
const directInteriorProgramName = this.directInteriorProgramName();
const directInteriorProgramName = this.directInteriorProgramName(renderingMode);
const directInteriorProgram = renderContext.shaderPrograms[directInteriorProgramName];
if (this.implicitCoverInteriorVAO == null) {
this.implicitCoverInteriorVAO = renderContext.vertexArrayObjectExt
.createVertexArrayOES();
}
renderContext.vertexArrayObjectExt.bindVertexArrayOES(this.implicitCoverInteriorVAO);
this.initImplicitCoverInteriorVAO(objectIndex, instanceRange);
this.initImplicitCoverInteriorVAO(objectIndex, instanceRange, renderingMode);
// Draw direct interior parts.
this.setTransformUniform(directInteriorProgram.uniforms, pass, objectIndex);
@ -489,21 +490,26 @@ export abstract class Renderer {
this.pathTransformBufferTextures[meshIndex]
.ext
.bind(gl, directInteriorProgram.uniforms, 2);
const coverInteriorRange = getMeshIndexRange(meshes.coverInteriorIndexRanges, pathRange);
const bQuadInteriorRange = getMeshIndexRange(meshes.bQuadVertexInteriorIndexPathRanges,
pathRange);
if (!this.pathIDsAreInstanced) {
gl.drawElements(gl.TRIANGLES,
coverInteriorRange.length,
bQuadInteriorRange.length,
gl.UNSIGNED_INT,
coverInteriorRange.start * UINT32_SIZE);
bQuadInteriorRange.start * UINT32_SIZE);
} else {
renderContext.instancedArraysExt
.drawElementsInstancedANGLE(gl.TRIANGLES,
coverInteriorRange.length,
bQuadInteriorRange.length,
gl.UNSIGNED_INT,
0,
instanceRange.length);
}
gl.disable(gl.CULL_FACE);
// Render curves, if applicable.
if (renderingMode !== 'conservative') {
// Set up direct curve state.
gl.depthMask(false);
gl.enable(gl.BLEND);
@ -515,8 +521,10 @@ export abstract class Renderer {
// TODO(pcwalton): Cache these.
const directCurveProgramName = this.directCurveProgramName();
const directCurveProgram = renderContext.shaderPrograms[directCurveProgramName];
if (this.implicitCoverCurveVAO == null)
this.implicitCoverCurveVAO = renderContext.vertexArrayObjectExt.createVertexArrayOES();
if (this.implicitCoverCurveVAO == null) {
this.implicitCoverCurveVAO = renderContext.vertexArrayObjectExt
.createVertexArrayOES();
}
renderContext.vertexArrayObjectExt.bindVertexArrayOES(this.implicitCoverCurveVAO);
this.initImplicitCoverCurveVAO(objectIndex, instanceRange);
@ -526,21 +534,24 @@ export abstract class Renderer {
this.setHintsUniform(directCurveProgram.uniforms);
this.setPathColorsUniform(objectIndex, directCurveProgram.uniforms, 0);
this.setEmboldenAmountUniform(objectIndex, directCurveProgram.uniforms);
this.pathTransformBufferTextures[meshIndex].st.bind(gl, directCurveProgram.uniforms, 1);
this.pathTransformBufferTextures[meshIndex].ext.bind(gl, directCurveProgram.uniforms, 2);
const coverCurveRange = getMeshIndexRange(meshes.coverCurveIndexRanges, pathRange);
this.pathTransformBufferTextures[meshIndex]
.st
.bind(gl, directCurveProgram.uniforms, 1);
this.pathTransformBufferTextures[meshIndex]
.ext
.bind(gl, directCurveProgram.uniforms, 2);
const coverCurveRange = getMeshIndexRange(meshes.bQuadVertexPositionPathRanges,
pathRange);
if (!this.pathIDsAreInstanced) {
gl.drawElements(gl.TRIANGLES,
coverCurveRange.length,
gl.UNSIGNED_INT,
coverCurveRange.start * UINT32_SIZE);
gl.drawArrays(gl.TRIANGLES, coverCurveRange.start * 6, coverCurveRange.length * 6);
} else {
renderContext.instancedArraysExt.drawElementsInstancedANGLE(gl.TRIANGLES,
coverCurveRange.length,
gl.UNSIGNED_INT,
renderContext.instancedArraysExt
.drawArraysInstancedANGLE(gl.TRIANGLES,
0,
coverCurveRange.length * 6,
instanceRange.length);
}
}
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
@ -548,7 +559,7 @@ export abstract class Renderer {
antialiasingStrategy.finishDirectlyRenderingObject(this, objectIndex);
}
private finishTiming() {
private finishTiming(): void {
const renderContext = this.renderContext;
if (this.timerQueryPollInterval != null)
@ -613,17 +624,20 @@ export abstract class Renderer {
const meshIndex = this.meshIndexForObject(objectIndex);
const meshes = this.meshes[meshIndex];
const meshData = unwrapNull(this.meshData)[meshIndex];
const directCurveProgramName = this.directCurveProgramName();
const directCurveProgram = renderContext.shaderPrograms[directCurveProgramName];
gl.useProgram(directCurveProgram.program);
gl.bindBuffer(gl.ARRAY_BUFFER, meshes.bVertexPositions);
gl.bindBuffer(gl.ARRAY_BUFFER, meshes.bQuadVertexPositions);
gl.vertexAttribPointer(directCurveProgram.attributes.aPosition, 2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexIDVBO);
gl.vertexAttribPointer(directCurveProgram.attributes.aVertexID, 1, gl.FLOAT, false, 0, 0);
if (this.pathIDsAreInstanced)
gl.bindBuffer(gl.ARRAY_BUFFER, this.instancedPathIDVBO);
else
gl.bindBuffer(gl.ARRAY_BUFFER, meshes.bVertexPathIDs);
gl.bindBuffer(gl.ARRAY_BUFFER, meshes.bQuadVertexPositionPathIDs);
gl.vertexAttribPointer(directCurveProgram.attributes.aPathID,
1,
gl.UNSIGNED_SHORT,
@ -635,35 +649,15 @@ export abstract class Renderer {
.vertexAttribDivisorANGLE(directCurveProgram.attributes.aPathID, 1);
}
gl.bindBuffer(gl.ARRAY_BUFFER, meshes.bVertexLoopBlinnData);
gl.vertexAttribPointer(directCurveProgram.attributes.aTexCoord,
2,
gl.UNSIGNED_BYTE,
false,
B_LOOP_BLINN_DATA_SIZE,
B_LOOP_BLINN_DATA_TEX_COORD_OFFSET);
gl.vertexAttribPointer(directCurveProgram.attributes.aSign,
1,
gl.BYTE,
false,
B_LOOP_BLINN_DATA_SIZE,
B_LOOP_BLINN_DATA_SIGN_OFFSET);
gl.bindBuffer(gl.ARRAY_BUFFER, meshes.bVertexNormals);
gl.vertexAttribPointer(directCurveProgram.attributes.aNormalAngle,
1,
gl.FLOAT,
false,
FLOAT32_SIZE,
0);
gl.enableVertexAttribArray(directCurveProgram.attributes.aPosition);
gl.enableVertexAttribArray(directCurveProgram.attributes.aTexCoord);
gl.enableVertexAttribArray(directCurveProgram.attributes.aVertexID);
gl.enableVertexAttribArray(directCurveProgram.attributes.aPathID);
gl.enableVertexAttribArray(directCurveProgram.attributes.aSign);
gl.enableVertexAttribArray(directCurveProgram.attributes.aNormalAngle);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, meshes.coverCurveIndices);
}
private initImplicitCoverInteriorVAO(objectIndex: number, instanceRange: Range): void {
private initImplicitCoverInteriorVAO(objectIndex: number,
instanceRange: Range,
renderingMode: DirectRenderingMode):
void {
if (this.meshes == null)
return;
@ -673,10 +667,10 @@ export abstract class Renderer {
const meshIndex = this.meshIndexForObject(objectIndex);
const meshes = this.meshes[meshIndex];
const directInteriorProgramName = this.directInteriorProgramName();
const directInteriorProgramName = this.directInteriorProgramName(renderingMode);
const directInteriorProgram = renderContext.shaderPrograms[directInteriorProgramName];
gl.useProgram(directInteriorProgram.program);
gl.bindBuffer(gl.ARRAY_BUFFER, meshes.bVertexPositions);
gl.bindBuffer(gl.ARRAY_BUFFER, meshes.bQuadVertexPositions);
gl.vertexAttribPointer(directInteriorProgram.attributes.aPosition,
2,
gl.FLOAT,
@ -687,7 +681,7 @@ export abstract class Renderer {
if (this.pathIDsAreInstanced)
gl.bindBuffer(gl.ARRAY_BUFFER, this.instancedPathIDVBO);
else
gl.bindBuffer(gl.ARRAY_BUFFER, meshes.bVertexPathIDs);
gl.bindBuffer(gl.ARRAY_BUFFER, meshes.bQuadVertexPositionPathIDs);
gl.vertexAttribPointer(directInteriorProgram.attributes.aPathID,
1,
gl.UNSIGNED_SHORT,
@ -699,18 +693,21 @@ export abstract class Renderer {
.vertexAttribDivisorANGLE(directInteriorProgram.attributes.aPathID, 1);
}
gl.bindBuffer(gl.ARRAY_BUFFER, meshes.bVertexNormals);
gl.vertexAttribPointer(directInteriorProgram.attributes.aNormalAngle,
if (directInteriorProgramName === 'conservativeInterior') {
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexIDVBO);
gl.vertexAttribPointer(directInteriorProgram.attributes.aVertexID,
1,
gl.FLOAT,
false,
FLOAT32_SIZE,
0,
0);
}
gl.enableVertexAttribArray(directInteriorProgram.attributes.aPosition);
gl.enableVertexAttribArray(directInteriorProgram.attributes.aPathID);
gl.enableVertexAttribArray(directInteriorProgram.attributes.aNormalAngle);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, meshes.coverInteriorIndices);
if (directInteriorProgramName === 'conservativeInterior')
gl.enableVertexAttribArray(directInteriorProgram.attributes.aVertexID);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, meshes.bQuadVertexInteriorIndices);
}
private initInstancedPathIDVBO(): void {
@ -721,11 +718,25 @@ export abstract class Renderer {
for (let pathIndex = 0; pathIndex < MAX_PATHS; pathIndex++)
pathIDs[pathIndex] = pathIndex + 1;
this.instancedPathIDVBO = renderContext.gl.createBuffer();
this.instancedPathIDVBO = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this.instancedPathIDVBO);
gl.bufferData(gl.ARRAY_BUFFER, pathIDs, gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
}
private initVertexIDVBO(): void {
const renderContext = this.renderContext;
const gl = renderContext.gl;
const vertexIDs = new Float32Array(MAX_VERTICES);
for (let vertexID = 0; vertexID < MAX_VERTICES; vertexID++)
vertexIDs[vertexID] = vertexID;
this.vertexIDVBO = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexIDVBO);
gl.bufferData(gl.ARRAY_BUFFER, vertexIDs, gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
}
}
function getMeshIndexRange(indexRanges: Range[], pathRange: Range): Range {

View File

@ -14,6 +14,7 @@ import {expectNotNull, PathfinderError, unwrapNull} from './utils';
export interface ShaderMap<T> {
blitLinear: T;
blitGamma: T;
conservativeInterior: T;
demo3DDistantGlyph: T;
demo3DMonument: T;
directCurve: T;
@ -39,6 +40,7 @@ const COMMON_SHADER_URL: string = '/glsl/gles2/common.inc.glsl';
export const SHADER_NAMES: Array<keyof ShaderMap<void>> = [
'blitLinear',
'blitGamma',
'conservativeInterior',
'directCurve',
'directInterior',
'direct3DCurve',
@ -63,6 +65,10 @@ const SHADER_URLS: ShaderMap<ShaderProgramURLs> = {
fragment: "/glsl/gles2/blit-linear.fs.glsl",
vertex: "/glsl/gles2/blit.vs.glsl",
},
conservativeInterior: {
fragment: "/glsl/gles2/direct-interior.fs.glsl",
vertex: "/glsl/gles2/conservative-interior.vs.glsl",
},
demo3DDistantGlyph: {
fragment: "/glsl/gles2/demo-3d-distant-glyph.fs.glsl",
vertex: "/glsl/gles2/demo-3d-distant-glyph.vs.glsl",

View File

@ -10,7 +10,8 @@
import * as glmatrix from 'gl-matrix';
import {AntialiasingStrategy, AntialiasingStrategyName, NoAAStrategy} from './aa-strategy';
import {AntialiasingStrategy, AntialiasingStrategyName, DirectRenderingMode} from './aa-strategy';
import {NoAAStrategy} from './aa-strategy';
import {SubpixelAAType} from './aa-strategy';
import {OrthographicCamera} from "./camera";
import {UniformMap} from './gl-utils';
@ -163,8 +164,9 @@ export abstract class SVGRenderer extends Renderer {
return 'directCurve';
}
protected directInteriorProgramName(): keyof ShaderMap<void> {
return 'directInterior';
protected directInteriorProgramName(renderingMode: DirectRenderingMode):
keyof ShaderMap<void> {
return renderingMode === 'conservative' ? 'conservativeInterior' : 'directInterior';
}
protected pathColorsForObject(objectIndex: number): Uint8Array {

View File

@ -11,8 +11,8 @@
import * as glmatrix from 'gl-matrix';
export const FLOAT32_SIZE: number = 4;
export const UINT16_SIZE: number = 2;
export const UINT8_SIZE: number = 1;
export const UINT32_MAX: number = 0xffffffff;
export const UINT32_SIZE: number = 4;

View File

@ -23,17 +23,17 @@ import {ShaderProgramSource, UnlinkedShaderProgram} from './shader-loader';
import {expectNotNull, PathfinderError, UINT32_SIZE, unwrapNull} from './utils';
const QUAD_POSITIONS: Float32Array = new Float32Array([
0.0, 1.0,
1.0, 1.0,
0.0, 0.0,
1.0, 0.0,
0.0, 1.0,
1.0, 1.0,
]);
const QUAD_TEX_COORDS: Float32Array = new Float32Array([
0.0, 1.0,
1.0, 1.0,
0.0, 0.0,
1.0, 0.0,
0.0, 1.0,
1.0, 1.0,
]);
export const TIMINGS: {[name: string]: string} = {

View File

@ -33,15 +33,33 @@ type Direction = 'upper' | 'lower';
const DIRECTIONS: Direction[] = ['upper', 'lower'];
const PATCH_VERTICES: Float32Array = new Float32Array([
0.0, 0.0,
0.5, 0.0,
1.0, 0.0,
0.0, 1.0,
0.5, 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 ECAA_CURVE_PATCH_INDICES: Uint8Array = new Uint8Array([0, 1, 2, 0, 2, 3, 2, 5, 3]);
export abstract class XCAAStrategy extends AntialiasingStrategy {
abstract readonly directRenderingMode: DirectRenderingMode;
protected patchVertexBuffer: WebGLBuffer | null;
protected patchIndexBuffer: WebGLBuffer | null;
get passCount(): number {
return 1;
}
protected abstract get usesDilationTransforms(): boolean;
protected abstract get patchIndices(): Uint8Array;
protected pathBoundsBufferTextures: PathfinderBufferTexture[];
protected supersampledFramebufferSize: glmatrix.vec2;
@ -72,8 +90,22 @@ export abstract class XCAAStrategy extends AntialiasingStrategy {
attachMeshes(renderer: Renderer): void {
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
this.createResolveVAO(renderer);
this.pathBoundsBufferTextures = [];
this.patchVertexBuffer = unwrapNull(gl.createBuffer());
gl.bindBuffer(gl.ARRAY_BUFFER, this.patchVertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, PATCH_VERTICES.buffer as ArrayBuffer, gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
this.patchIndexBuffer = unwrapNull(gl.createBuffer());
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.patchIndexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,
this.patchIndices.buffer as ArrayBuffer,
gl.STATIC_DRAW);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
}
setFramebufferSize(renderer: Renderer): void {
@ -336,6 +368,10 @@ export abstract class XCAAStrategy extends AntialiasingStrategy {
export class MCAAStrategy extends XCAAStrategy {
protected vao: WebGLVertexArrayObject | null;
protected get patchIndices(): Uint8Array {
return MCAA_PATCH_INDICES;
}
protected get usesDilationTransforms(): boolean {
return true;
}
@ -348,7 +384,10 @@ export class MCAAStrategy extends XCAAStrategy {
super.attachMeshes(renderer);
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
this.vao = renderContext.vertexArrayObjectExt.createVertexArrayOES();
}
antialiasObject(renderer: Renderer, objectIndex: number): void {
@ -372,14 +411,13 @@ export class MCAAStrategy extends XCAAStrategy {
}
protected clearForAA(renderer: Renderer): void {
if (!this.usesAAFramebuffer(renderer))
return;
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
if (renderer.isMulticolor)
gl.clearColor(1.0, 1.0, 1.0, 1.0);
else
gl.clearColor(0.0, 0.0, 0.0, 0.0);
gl.clearDepth(0.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
}
@ -388,7 +426,17 @@ export class MCAAStrategy extends XCAAStrategy {
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
if (this.directRenderingMode !== 'conservative') {
gl.disable(gl.DEPTH_TEST);
return;
}
gl.depthFunc(gl.GREATER);
gl.depthMask(false);
gl.enable(gl.DEPTH_TEST);
gl.frontFace(gl.CCW);
gl.cullFace(gl.BACK);
gl.enable(gl.CULL_FACE);
}
protected clearForResolve(renderer: Renderer): void {
@ -396,7 +444,7 @@ export class MCAAStrategy extends XCAAStrategy {
const gl = renderContext.gl;
if (!renderer.isMulticolor) {
gl.clearColor(1.0, 1.0, 0.0, 1.0);
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
}
}
@ -437,53 +485,80 @@ export class MCAAStrategy extends XCAAStrategy {
const vao = this.vao;
renderContext.vertexArrayObjectExt.bindVertexArrayOES(vao);
const bQuadRanges = renderer.meshData[meshIndex].bQuadPathRanges;
const bQuadRanges = renderer.meshData[meshIndex].bQuadVertexPositionPathRanges;
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, this.patchVertexBuffer);
gl.vertexAttribPointer(attributes.aTessCoord, 2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, renderer.meshes[meshIndex].bQuadVertexPositions);
gl.vertexAttribPointer(attributes.aUpperEndpointPositions,
4,
gl.vertexAttribPointer(attributes.aUpperLeftEndpointPosition,
2,
gl.FLOAT,
false,
FLOAT32_SIZE * 12,
FLOAT32_SIZE * 12 * offset);
gl.vertexAttribPointer(attributes.aLowerEndpointPositions,
4,
FLOAT32_SIZE * 12 * offset + FLOAT32_SIZE * 0);
gl.vertexAttribPointer(attributes.aUpperControlPointPosition,
2,
gl.FLOAT,
false,
FLOAT32_SIZE * 12,
FLOAT32_SIZE * 12 * offset + FLOAT32_SIZE * 2);
gl.vertexAttribPointer(attributes.aUpperRightEndpointPosition,
2,
gl.FLOAT,
false,
FLOAT32_SIZE * 12,
FLOAT32_SIZE * 12 * offset + FLOAT32_SIZE * 4);
gl.vertexAttribPointer(attributes.aControlPointPositions,
4,
gl.vertexAttribPointer(attributes.aLowerRightEndpointPosition,
2,
gl.FLOAT,
false,
FLOAT32_SIZE * 12,
FLOAT32_SIZE * 12 * offset + FLOAT32_SIZE * 6);
gl.vertexAttribPointer(attributes.aLowerControlPointPosition,
2,
gl.FLOAT,
false,
FLOAT32_SIZE * 12,
FLOAT32_SIZE * 12 * offset + FLOAT32_SIZE * 8);
gl.vertexAttribPointer(attributes.aLowerLeftEndpointPosition,
2,
gl.FLOAT,
false,
FLOAT32_SIZE * 12,
FLOAT32_SIZE * 12 * offset + FLOAT32_SIZE * 10);
renderContext.instancedArraysExt
.vertexAttribDivisorANGLE(attributes.aUpperEndpointPositions, 1);
.vertexAttribDivisorANGLE(attributes.aUpperLeftEndpointPosition, 1);
renderContext.instancedArraysExt
.vertexAttribDivisorANGLE(attributes.aLowerEndpointPositions, 1);
.vertexAttribDivisorANGLE(attributes.aUpperControlPointPosition, 1);
renderContext.instancedArraysExt
.vertexAttribDivisorANGLE(attributes.aControlPointPositions, 1);
.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].edgeBoundingBoxPathIDs);
gl.bindBuffer(gl.ARRAY_BUFFER, renderer.meshes[meshIndex].bQuadVertexPositionPathIDs);
gl.vertexAttribPointer(attributes.aPathID,
1,
gl.UNSIGNED_SHORT,
false,
0,
UINT16_SIZE * 6,
UINT16_SIZE * offset);
renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aPathID, 1);
gl.enableVertexAttribArray(attributes.aQuadPosition);
gl.enableVertexAttribArray(attributes.aUpperEndpointPositions);
gl.enableVertexAttribArray(attributes.aLowerEndpointPositions);
gl.enableVertexAttribArray(attributes.aControlPointPositions);
gl.enableVertexAttribArray(attributes.aTessCoord);
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, renderContext.quadElementsBuffer);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.patchIndexBuffer);
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
}
@ -516,18 +591,22 @@ export class MCAAStrategy extends XCAAStrategy {
renderContext.vertexArrayObjectExt.bindVertexArrayOES(vao);
this.setBlendModeForAA(renderer);
this.setAADepthState(renderer);
const bQuadRanges = renderer.meshData[meshIndex].bQuadPathRanges;
const bQuadRanges = renderer.meshData[meshIndex].bQuadVertexPositionPathRanges;
const count = calculateCountFromIndexRanges(pathRange, bQuadRanges);
renderContext.instancedArraysExt
.drawElementsInstancedANGLE(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0, count);
.drawElementsInstancedANGLE(gl.TRIANGLES, 12, gl.UNSIGNED_BYTE, 0, count);
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
gl.disable(gl.DEPTH_TEST);
gl.disable(gl.CULL_FACE);
}
get directRenderingMode(): DirectRenderingMode {
return 'none';
// FIXME(pcwalton): Only in multicolor mode?
return 'conservative';
}
protected setAAUniforms(renderer: Renderer, uniforms: UniformMap, objectIndex: number):
@ -544,6 +623,10 @@ export class MCAAStrategy extends XCAAStrategy {
}
export class ECAAStrategy extends XCAAStrategy {
protected get patchIndices(): Uint8Array {
return ECAA_CURVE_PATCH_INDICES;
}
protected get lineShaderProgramNames(): Array<keyof ShaderMap<void>> {
return ['ecaaLine'];
}
@ -588,6 +671,11 @@ export class ECAAStrategy extends XCAAStrategy {
objectIndex,
this.curveShaderProgramNames[0],
this.curveShaderProgramNames[1]);
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
gl.disable(gl.CULL_FACE);
}
protected usesAAFramebuffer(): boolean {
@ -619,7 +707,13 @@ export class ECAAStrategy extends XCAAStrategy {
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);
gl.frontFace(gl.CCW);
gl.cullFace(gl.BACK);
gl.enable(gl.CULL_FACE);
}
protected clearForResolve(renderer: Renderer): void {
@ -699,7 +793,7 @@ export class ECAAStrategy extends XCAAStrategy {
// FIXME(pcwalton): Only render the appropriate instances.
const count = renderer.meshData[meshIndex].segmentCurveCount;
renderContext.instancedArraysExt
.drawElementsInstancedANGLE(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0, count);
.drawElementsInstancedANGLE(gl.TRIANGLES, 9, gl.UNSIGNED_BYTE, 0, count);
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
}
@ -735,7 +829,7 @@ export class ECAAStrategy extends XCAAStrategy {
gl.useProgram(lineProgram.program);
gl.bindBuffer(gl.ARRAY_BUFFER, renderContext.quadPositionsBuffer);
gl.vertexAttribPointer(attributes.aQuadPosition, 2, gl.FLOAT, false, 0, 0);
gl.vertexAttribPointer(attributes.aTessCoord, 2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, lineVertexPositionsBuffer);
gl.vertexAttribPointer(attributes.aLeftPosition,
2,
@ -752,7 +846,7 @@ export class ECAAStrategy extends XCAAStrategy {
gl.bindBuffer(gl.ARRAY_BUFFER, linePathIDsBuffer);
gl.vertexAttribPointer(attributes.aPathID, 1, gl.UNSIGNED_SHORT, false, 0, 0);
gl.enableVertexAttribArray(attributes.aQuadPosition);
gl.enableVertexAttribArray(attributes.aTessCoord);
gl.enableVertexAttribArray(attributes.aLeftPosition);
gl.enableVertexAttribArray(attributes.aRightPosition);
gl.enableVertexAttribArray(attributes.aPathID);
@ -805,8 +899,8 @@ export class ECAAStrategy extends XCAAStrategy {
const curveNormalsBuffer = renderer.meshes[0].segmentCurveNormals;
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, this.patchVertexBuffer);
gl.vertexAttribPointer(attributes.aTessCoord, 2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, curveVertexPositionsBuffer);
gl.vertexAttribPointer(attributes.aLeftPosition,
2,
@ -829,7 +923,7 @@ export class ECAAStrategy extends XCAAStrategy {
gl.bindBuffer(gl.ARRAY_BUFFER, curvePathIDsBuffer);
gl.vertexAttribPointer(attributes.aPathID, 1, gl.UNSIGNED_SHORT, false, 0, 0);
gl.enableVertexAttribArray(attributes.aQuadPosition);
gl.enableVertexAttribArray(attributes.aTessCoord);
gl.enableVertexAttribArray(attributes.aLeftPosition);
gl.enableVertexAttribArray(attributes.aControlPointPosition);
gl.enableVertexAttribArray(attributes.aRightPosition);
@ -857,7 +951,7 @@ export class ECAAStrategy extends XCAAStrategy {
.vertexAttribDivisorANGLE(attributes.aNormalAngles, 1);
}
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, renderContext.quadElementsBuffer);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.patchIndexBuffer);
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);

View File

@ -17,9 +17,6 @@ image = "0.17"
lazy_static = "0.2"
log = "0.3"
lru-cache = "0.1"
rocket = "0.3"
rocket_codegen = "0.3"
rocket_contrib = "0.3"
serde = "1.0"
serde_derive = "1.0"
serde_json = "1.0"
@ -36,6 +33,15 @@ optional = true
[dependencies.fontsan]
git = "https://github.com/servo/fontsan.git"
[dependencies.rocket]
git = "https://github.com/SergioBenitez/rocket"
[dependencies.rocket_codegen]
git = "https://github.com/SergioBenitez/rocket"
[dependencies.rocket_contrib]
git = "https://github.com/SergioBenitez/rocket"
[dependencies.pathfinder_font_renderer]
path = "../../font-renderer"
@ -44,3 +50,6 @@ path = "../../partitioner"
[dependencies.pathfinder_path_utils]
path = "../../path-utils"
[patch.crates-io]
ring = { git = "https://github.com/SergioBenitez/ring", branch = "v0.12" }

View File

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(plugin)]
#![feature(decl_macro, plugin)]
#![plugin(rocket_codegen)]
extern crate app_units;

View File

@ -112,16 +112,6 @@ pub unsafe extern fn pf_partitioner_b_vertex_loop_blinn_data<'a>(
b_vertex_loop_blinn_data.as_ptr() as *const BVertexLoopBlinnData
}
#[no_mangle]
pub unsafe extern fn pf_partitioner_cover_indices<'a>(partitioner: *const Partitioner<'a>,
out_cover_indices: *mut CoverIndices) {
let cover_indices = &(*partitioner).library().cover_indices;
(*out_cover_indices).interior_indices = cover_indices.interior_indices.as_ptr();
(*out_cover_indices).interior_indices_len = cover_indices.interior_indices.len() as u32;
(*out_cover_indices).curve_indices = cover_indices.curve_indices.as_ptr();
(*out_cover_indices).curve_indices_len = cover_indices.curve_indices.len() as u32;
}
#[no_mangle]
pub unsafe extern fn pf_init_env_logger() -> u32 {
env_logger::init().is_ok() as u32

View File

@ -103,9 +103,12 @@ impl BQuad {
#[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize)]
pub struct BQuadVertexPositions {
pub upper_endpoint_positions: [Point2D<f32>; 2],
pub lower_endpoint_positions: [Point2D<f32>; 2],
pub control_point_positions: [Point2D<f32>; 2],
pub upper_left_vertex_position: Point2D<f32>,
pub upper_control_point_position: Point2D<f32>,
pub upper_right_vertex_position: Point2D<f32>,
pub lower_right_vertex_position: Point2D<f32>,
pub lower_control_point_position: Point2D<f32>,
pub lower_left_vertex_position: Point2D<f32>,
}
#[derive(Clone, Copy, PartialEq, Debug)]
@ -124,7 +127,7 @@ pub(crate) enum BVertexKind {
ConcaveControlPoint,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct BVertexLoopBlinnData {
pub tex_coord: [u8; 2],

View File

@ -26,11 +26,10 @@ pub struct MeshLibrary {
pub b_quads: Vec<BQuad>,
// FIXME(pcwalton): Merge with `b_vertex_positions` below.
pub b_quad_vertex_positions: Vec<BQuadVertexPositions>,
pub b_quad_vertex_interior_indices: Vec<u32>,
pub b_vertex_positions: Vec<Point2D<f32>>,
pub b_vertex_loop_blinn_data: Vec<BVertexLoopBlinnData>,
pub b_vertex_normals: Vec<f32>,
pub cover_indices: MeshLibraryCoverIndices,
pub edge_data: MeshLibraryEdgeData,
pub segments: MeshLibrarySegments,
pub segment_normals: MeshLibrarySegmentNormals,
}
@ -42,11 +41,10 @@ impl MeshLibrary {
path_ranges: vec![],
b_quads: vec![],
b_quad_vertex_positions: vec![],
b_quad_vertex_interior_indices: vec![],
b_vertex_positions: vec![],
b_vertex_loop_blinn_data: vec![],
b_vertex_normals: vec![],
cover_indices: MeshLibraryCoverIndices::new(),
edge_data: MeshLibraryEdgeData::new(),
segments: MeshLibrarySegments::new(),
segment_normals: MeshLibrarySegmentNormals::new(),
}
@ -56,11 +54,10 @@ impl MeshLibrary {
self.path_ranges.clear();
self.b_quads.clear();
self.b_quad_vertex_positions.clear();
self.b_quad_vertex_interior_indices.clear();
self.b_vertex_positions.clear();
self.b_vertex_loop_blinn_data.clear();
self.b_vertex_normals.clear();
self.cover_indices.clear();
self.edge_data.clear();
self.segments.clear();
self.segment_normals.clear();
}
@ -86,85 +83,99 @@ impl MeshLibrary {
self.b_quads.push(*b_quad);
let upper_left_position =
&self.b_vertex_positions[b_quad.upper_left_vertex_index as usize];
self.b_vertex_positions[b_quad.upper_left_vertex_index as usize];
let upper_right_position =
&self.b_vertex_positions[b_quad.upper_right_vertex_index as usize];
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];
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];
self.b_vertex_positions[b_quad.lower_right_vertex_index as usize];
let mut b_quad_vertex_positions = BQuadVertexPositions {
upper_endpoint_positions: [*upper_left_position, *upper_right_position],
lower_endpoint_positions: [*lower_left_position, *lower_right_position],
control_point_positions: [Point2D::zero(), Point2D::zero()],
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,
};
let upper_left_bounding_box_position =
Point2D::new(upper_left_position.x,
f32::max(upper_left_position.y, upper_right_position.y));
let lower_right_bounding_box_position =
Point2D::new(lower_right_position.x,
f32::max(lower_left_position.y, lower_right_position.y));
self.edge_data.bounding_box_vertex_positions.push(EdgeBoundingBoxVertexPositions {
upper_left: upper_left_bounding_box_position,
lower_right: lower_right_bounding_box_position,
});
if b_quad.upper_control_point_vertex_index == u32::MAX {
self.edge_data.upper_line_vertex_positions.push(EdgeLineVertexPositions {
left: *upper_left_position,
right: *upper_right_position,
});
} else {
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.control_point_positions[0] = *upper_control_point_position;
self.edge_data.upper_curve_vertex_positions.push(EdgeCurveVertexPositions {
left: *upper_left_position,
control_point: *upper_control_point_position,
right: *upper_right_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 {
self.edge_data.lower_line_vertex_positions.push(EdgeLineVertexPositions {
left: *lower_left_position,
right: *lower_right_position,
});
} else {
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.control_point_positions[1] = *lower_control_point_position;
self.edge_data.lower_curve_vertex_positions.push(EdgeCurveVertexPositions {
left: *lower_left_position,
control_point: *lower_control_point_position,
right: *lower_right_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;
}
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,
&b_quad_vertex_positions);
self.b_quad_vertex_positions.push(b_quad_vertex_positions);
}
fn push_b_quad_vertex_position_interior_indices(&mut self,
first_vertex_index: u32,
b_quad: &BQuadVertexPositions) {
let upper_curve_is_concave =
(b_quad.upper_right_vertex_position - b_quad.upper_left_vertex_position).cross(
b_quad.upper_control_point_position - b_quad.upper_left_vertex_position) > 0.0;
let lower_curve_is_concave =
(b_quad.lower_left_vertex_position - b_quad.lower_right_vertex_position).cross(
b_quad.lower_control_point_position - b_quad.lower_right_vertex_position) > 0.0;
let indices: &'static [u32] = match (upper_curve_is_concave, lower_curve_is_concave) {
(false, false) => &[UL, UR, LL, UR, LR, LL],
(true, false) => &[UL, UC, LL, UC, LR, LL, UR, LR, UC],
(false, true) => &[UL, LC, LL, UL, UR, LC, UR, LR, LC],
(true, true) => &[UL, UC, LL, UC, LC, LL, UR, LC, UC, UR, LR, LC],
};
self.b_quad_vertex_interior_indices
.extend(indices.into_iter().map(|index| index + first_vertex_index));
const UL: u32 = 0;
const UC: u32 = 1;
const UR: u32 = 2;
const LR: u32 = 3;
const LC: u32 = 4;
const LL: u32 = 5;
}
/// Reverses interior indices so that they draw front-to-back.
///
/// This enables early Z optimizations.
pub fn optimize(&mut self) {
let mut new_interior_indices =
Vec::with_capacity(self.cover_indices.interior_indices.len());
reverse_indices(&mut self.path_ranges,
&mut self.b_quad_vertex_interior_indices,
|path_ranges| path_ranges.b_quad_vertex_interior_indices.clone(),
|path_ranges, new_range| {
path_ranges.b_quad_vertex_interior_indices = new_range
});
for path_range in self.path_ranges.iter_mut().rev() {
let old_interior_indices = &self.cover_indices.interior_indices[..];
let old_range = path_range.cover_interior_indices.clone();
fn reverse_indices<G, S>(path_ranges: &mut [PathRanges],
indices: &mut Vec<u32>,
mut getter: G,
mut setter: S)
where G: FnMut(&PathRanges) -> Range<u32>,
S: FnMut(&mut PathRanges, Range<u32>) {
let mut new_indices = Vec::with_capacity(indices.len());
for path_range in path_ranges.iter_mut().rev() {
let old_range = getter(path_range);
let old_range = (old_range.start as usize)..(old_range.end as usize);
let new_start_index = new_interior_indices.len() as u32;
new_interior_indices.extend_from_slice(&old_interior_indices[old_range]);
let new_end_index = new_interior_indices.len() as u32;
path_range.cover_interior_indices = new_start_index..new_end_index;
let new_start_index = new_indices.len() as u32;
new_indices.extend_from_slice(&indices[old_range]);
let new_end_index = new_indices.len() as u32;
setter(path_range, new_start_index..new_end_index);
}
self.cover_indices.interior_indices = new_interior_indices
*indices = new_indices
}
}
pub fn push_segments<I>(&mut self, path_id: u16, stream: I)
@ -220,16 +231,7 @@ impl MeshLibrary {
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));
try!(write_simple_chunk(writer, b"cvii", &self.cover_indices.interior_indices));
try!(write_simple_chunk(writer, b"cvci", &self.cover_indices.curve_indices));
try!(write_simple_chunk(writer, b"ebbv", &self.edge_data.bounding_box_vertex_positions));
try!(write_simple_chunk(writer, b"eulv", &self.edge_data.upper_line_vertex_positions));
try!(write_simple_chunk(writer, b"ellv", &self.edge_data.lower_line_vertex_positions));
try!(write_simple_chunk(writer, b"eucv", &self.edge_data.upper_curve_vertex_positions));
try!(write_simple_chunk(writer, b"elcv", &self.edge_data.lower_curve_vertex_positions));
try!(write_simple_chunk(writer, b"bqii", &self.b_quad_vertex_interior_indices));
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"snli", &self.segment_normals.line_normals));
@ -274,35 +276,11 @@ impl MeshLibrary {
b"bqvp",
path_ranges,
|ranges| &ranges.b_quad_vertex_positions));
try!(write_path_range(writer,
b"bqii",
path_ranges,
|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"cvii",
path_ranges,
|ranges| &ranges.cover_interior_indices));
try!(write_path_range(writer,
b"cvci",
path_ranges,
|ranges| &ranges.cover_curve_indices));
try!(write_path_range(writer,
b"ebbo",
path_ranges,
|ranges| &ranges.edge_bounding_box_indices));
try!(write_path_range(writer,
b"euli",
path_ranges,
|ranges| &ranges.edge_upper_line_indices));
try!(write_path_range(writer,
b"euci",
path_ranges,
|ranges| &ranges.edge_upper_curve_indices));
try!(write_path_range(writer,
b"elli",
path_ranges,
|ranges| &ranges.edge_lower_line_indices));
try!(write_path_range(writer,
b"elci",
path_ranges,
|ranges| &ranges.edge_lower_curve_indices));
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));
Ok(())
@ -329,14 +307,8 @@ impl MeshLibrary {
MeshLibraryLengths {
b_quads: self.b_quads.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_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,
edge_bounding_box_indices: self.edge_data.bounding_box_vertex_positions.len() as u32,
edge_upper_line_indices: self.edge_data.upper_line_vertex_positions.len() as u32,
edge_upper_curve_indices: self.edge_data.upper_curve_vertex_positions.len() as u32,
edge_lower_line_indices: self.edge_data.lower_line_vertex_positions.len() as u32,
edge_lower_curve_indices: self.edge_data.lower_curve_vertex_positions.len() as u32,
}
}
}
@ -347,46 +319,19 @@ pub struct MeshLibraryCoverIndices {
pub curve_indices: Vec<u32>,
}
impl MeshLibraryCoverIndices {
#[inline]
fn new() -> MeshLibraryCoverIndices {
MeshLibraryCoverIndices {
interior_indices: vec![],
curve_indices: vec![],
}
}
fn clear(&mut self) {
self.interior_indices.clear();
self.curve_indices.clear();
}
}
pub(crate) struct MeshLibraryLengths {
b_quads: u32,
pub(crate) b_quads: u32,
b_quad_vertex_positions: u32,
b_quad_vertex_interior_indices: u32,
b_vertices: u32,
cover_interior_indices: u32,
cover_curve_indices: u32,
edge_bounding_box_indices: u32,
edge_upper_line_indices: u32,
edge_upper_curve_indices: u32,
edge_lower_line_indices: u32,
edge_lower_curve_indices: u32,
}
#[derive(Clone, Debug)]
pub struct PathRanges {
pub b_quads: Range<u32>,
pub b_quad_vertex_positions: Range<u32>,
pub b_quad_vertex_interior_indices: Range<u32>,
pub b_vertices: Range<u32>,
pub cover_interior_indices: Range<u32>,
pub cover_curve_indices: Range<u32>,
pub edge_bounding_box_indices: Range<u32>,
pub edge_upper_line_indices: Range<u32>,
pub edge_upper_curve_indices: Range<u32>,
pub edge_lower_line_indices: Range<u32>,
pub edge_lower_curve_indices: Range<u32>,
pub segment_lines: Range<u32>,
pub segment_curves: Range<u32>,
}
@ -396,14 +341,8 @@ impl PathRanges {
PathRanges {
b_quads: 0..0,
b_quad_vertex_positions: 0..0,
b_quad_vertex_interior_indices: 0..0,
b_vertices: 0..0,
cover_interior_indices: 0..0,
cover_curve_indices: 0..0,
edge_bounding_box_indices: 0..0,
edge_upper_line_indices: 0..0,
edge_upper_curve_indices: 0..0,
edge_lower_line_indices: 0..0,
edge_lower_curve_indices: 0..0,
segment_lines: 0..0,
segment_curves: 0..0,
}
@ -414,46 +353,9 @@ impl PathRanges {
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_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.cover_interior_indices = start.cover_interior_indices..end.cover_interior_indices;
self.cover_curve_indices = start.cover_curve_indices..end.cover_curve_indices;
self.edge_bounding_box_indices =
start.edge_bounding_box_indices..end.edge_bounding_box_indices;
self.edge_upper_line_indices = start.edge_upper_line_indices..end.edge_upper_line_indices;
self.edge_upper_curve_indices =
start.edge_upper_curve_indices..end.edge_upper_curve_indices;
self.edge_lower_line_indices = start.edge_lower_line_indices..end.edge_lower_line_indices;
self.edge_lower_curve_indices =
start.edge_lower_curve_indices..end.edge_lower_curve_indices;
}
}
#[derive(Clone, Debug)]
pub struct MeshLibraryEdgeData {
pub bounding_box_vertex_positions: Vec<EdgeBoundingBoxVertexPositions>,
pub upper_line_vertex_positions: Vec<EdgeLineVertexPositions>,
pub lower_line_vertex_positions: Vec<EdgeLineVertexPositions>,
pub upper_curve_vertex_positions: Vec<EdgeCurveVertexPositions>,
pub lower_curve_vertex_positions: Vec<EdgeCurveVertexPositions>,
}
impl MeshLibraryEdgeData {
fn new() -> MeshLibraryEdgeData {
MeshLibraryEdgeData {
bounding_box_vertex_positions: vec![],
upper_line_vertex_positions: vec![],
lower_line_vertex_positions: vec![],
upper_curve_vertex_positions: vec![],
lower_curve_vertex_positions: vec![],
}
}
fn clear(&mut self) {
self.bounding_box_vertex_positions.clear();
self.upper_line_vertex_positions.clear();
self.upper_curve_vertex_positions.clear();
self.lower_line_vertex_positions.clear();
self.lower_curve_vertex_positions.clear();
}
}
@ -497,25 +399,6 @@ impl MeshLibrarySegmentNormals {
}
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct EdgeBoundingBoxVertexPositions {
pub upper_left: Point2D<f32>,
pub lower_right: Point2D<f32>,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct EdgeLineVertexPositions {
pub left: Point2D<f32>,
pub right: Point2D<f32>,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct EdgeCurveVertexPositions {
pub left: Point2D<f32>,
pub control_point: Point2D<f32>,
pub right: Point2D<f32>,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct LineSegment {
pub endpoint_0: Point2D<f32>,

View File

@ -177,24 +177,10 @@ impl<'a> Partitioner<'a> {
let next_active_edge_index = self.find_point_between_active_edges(endpoint_index);
let endpoint = &self.endpoints[endpoint_index as usize];
let emission_result = self.emit_b_quads_around_active_edge(next_active_edge_index,
endpoint.position.x);
self.emit_b_quads_around_active_edge(next_active_edge_index, endpoint.position.x);
self.add_new_edges_for_min_point(endpoint_index, next_active_edge_index);
// Add supporting interior triangles if necessary.
match emission_result {
BQuadEmissionResult::BQuadEmittedAbove | BQuadEmissionResult::BQuadEmittedAround => {
self.add_supporting_interior_triangle(next_active_edge_index,
next_active_edge_index - 1,
next_active_edge_index + 2);
self.add_supporting_interior_triangle(next_active_edge_index + 1,
next_active_edge_index - 1,
next_active_edge_index + 2);
}
_ => {}
}
let prev_endpoint_index = self.prev_endpoint_of(endpoint_index);
let next_endpoint_index = self.next_endpoint_of(endpoint_index);
let new_point = self.create_point_from_endpoint(next_endpoint_index);
@ -291,29 +277,10 @@ impl<'a> Partitioner<'a> {
// TODO(pcwalton): Collapse the two duplicate endpoints that this will create together if
// possible (i.e. if they have the same parity).
let b_quad_emission_results = [
self.emit_b_quads_around_active_edge(active_edge_indices[0], endpoint.position.x),
self.emit_b_quads_around_active_edge(active_edge_indices[1], endpoint.position.x),
];
self.emit_b_quads_around_active_edge(active_edge_indices[0], endpoint.position.x);
self.emit_b_quads_around_active_edge(active_edge_indices[1], endpoint.position.x);
// Add supporting interior triangles if necessary.
match b_quad_emission_results[0] {
BQuadEmissionResult::BQuadEmittedAbove | BQuadEmissionResult::BQuadEmittedAround => {
self.add_supporting_interior_triangle(active_edge_indices[0],
active_edge_indices[0] - 1,
active_edge_indices[0] + 2)
}
_ => {}
}
match b_quad_emission_results[1] {
BQuadEmissionResult::BQuadEmittedBelow | BQuadEmissionResult::BQuadEmittedAround => {
self.add_supporting_interior_triangle(active_edge_indices[1],
active_edge_indices[1] - 2,
active_edge_indices[1] + 1)
}
_ => {}
}
self.heap.pop();
// FIXME(pcwalton): This is twice as slow as it needs to be.
@ -360,6 +327,7 @@ impl<'a> Partitioner<'a> {
}
fn write_normals_to_library(&mut self) {
// Write B-vertex normals.
for (b_vertex_index, vertex_normal) in self.vertex_normals.iter().enumerate() {
debug_assert!(b_vertex_index <= self.library.b_vertex_normals.len());
@ -695,115 +663,6 @@ impl<'a> Partitioner<'a> {
lower_active_edge.toggle_parity();
}
match (upper_shape, lower_shape) {
(Shape::Flat, Shape::Flat) |
(Shape::Flat, Shape::Convex) |
(Shape::Convex, Shape::Flat) |
(Shape::Convex, Shape::Convex) => {
self.library.cover_indices.interior_indices.extend([
upper_subdivision.left_curve_left,
upper_subdivision.middle_point,
lower_subdivision.left_curve_left,
lower_subdivision.middle_point,
lower_subdivision.left_curve_left,
upper_subdivision.middle_point,
].into_iter());
if upper_shape != Shape::Flat {
self.library.cover_indices.curve_indices.extend([
upper_subdivision.left_curve_control_point,
upper_subdivision.middle_point,
upper_subdivision.left_curve_left,
].into_iter())
}
if lower_shape != Shape::Flat {
self.library.cover_indices.curve_indices.extend([
lower_subdivision.left_curve_control_point,
lower_subdivision.left_curve_left,
lower_subdivision.middle_point,
].into_iter())
}
}
(Shape::Concave, Shape::Flat) |
(Shape::Concave, Shape::Convex) => {
self.library.cover_indices.interior_indices.extend([
upper_subdivision.left_curve_left,
upper_subdivision.left_curve_control_point,
lower_subdivision.left_curve_left,
upper_subdivision.middle_point,
lower_subdivision.middle_point,
upper_subdivision.left_curve_control_point,
lower_subdivision.middle_point,
lower_subdivision.left_curve_left,
upper_subdivision.left_curve_control_point,
].into_iter());
self.library.cover_indices.curve_indices.extend([
upper_subdivision.left_curve_control_point,
upper_subdivision.left_curve_left,
upper_subdivision.middle_point,
].into_iter());
if lower_shape != Shape::Flat {
self.library.cover_indices.curve_indices.extend([
lower_subdivision.left_curve_control_point,
lower_subdivision.left_curve_left,
lower_subdivision.middle_point,
].into_iter())
}
}
(Shape::Flat, Shape::Concave) |
(Shape::Convex, Shape::Concave) => {
self.library.cover_indices.interior_indices.extend([
upper_subdivision.left_curve_left,
upper_subdivision.middle_point,
lower_subdivision.left_curve_control_point,
upper_subdivision.middle_point,
lower_subdivision.middle_point,
lower_subdivision.left_curve_control_point,
upper_subdivision.left_curve_left,
lower_subdivision.left_curve_control_point,
lower_subdivision.left_curve_left,
].into_iter());
self.library.cover_indices.curve_indices.extend([
lower_subdivision.left_curve_control_point,
lower_subdivision.middle_point,
lower_subdivision.left_curve_left,
].into_iter());
if upper_shape != Shape::Flat {
self.library.cover_indices.curve_indices.extend([
upper_subdivision.left_curve_control_point,
upper_subdivision.middle_point,
upper_subdivision.left_curve_left,
].into_iter())
}
}
(Shape::Concave, Shape::Concave) => {
self.library.cover_indices.interior_indices.extend([
upper_subdivision.left_curve_left,
upper_subdivision.left_curve_control_point,
lower_subdivision.left_curve_left,
lower_subdivision.left_curve_left,
upper_subdivision.left_curve_control_point,
lower_subdivision.left_curve_control_point,
upper_subdivision.middle_point,
lower_subdivision.left_curve_control_point,
upper_subdivision.left_curve_control_point,
upper_subdivision.middle_point,
lower_subdivision.middle_point,
lower_subdivision.left_curve_control_point,
].into_iter());
self.library.cover_indices.curve_indices.extend([
upper_subdivision.left_curve_control_point,
upper_subdivision.left_curve_left,
upper_subdivision.middle_point,
lower_subdivision.left_curve_control_point,
lower_subdivision.middle_point,
lower_subdivision.left_curve_left,
].into_iter());
}
}
let b_quad = BQuad::new(upper_subdivision.left_curve_left,
upper_subdivision.left_curve_control_point,
upper_subdivision.middle_point,
@ -872,17 +731,6 @@ impl<'a> Partitioner<'a> {
self.subdivide_active_edge_again_at_t(subdivision, t, bottom)
}
fn add_supporting_interior_triangle(&mut self,
active_edge_index: u32,
upper_active_edge_index: u32,
lower_active_edge_index: u32) {
self.library.cover_indices.interior_indices.extend([
self.active_edges[active_edge_index as usize].left_vertex_index,
self.active_edges[upper_active_edge_index as usize].left_vertex_index,
self.active_edges[lower_active_edge_index as usize].left_vertex_index,
].into_iter());
}
fn already_visited_point(&self, point: &Point) -> bool {
// FIXME(pcwalton): This makes the visited vector too big.
let index = point.endpoint_index as usize;

View File

@ -117,6 +117,12 @@ vec2 dilatePosition(vec2 position, float normalAngle, vec2 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,

View File

@ -0,0 +1,72 @@
// pathfinder/shaders/gles2/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.
//! Renders polygonal portions of a mesh, only filling pixels that are fully
//! covered.
//!
//! Typically, you will run this shader before running XCAA.
//! Remember to enable the depth test with a `GREATER` depth function for optimal
//! performance.
precision highp float;
/// A 3D transform to be applied to all points.
uniform mat4 uTransform;
/// Vertical snapping positions.
uniform vec4 uHints;
/// The framebuffer size in pixels.
uniform ivec2 uFramebufferSize;
/// The size of the path colors texture in texels.
uniform ivec2 uPathColorsDimensions;
/// The fill color for each path.
uniform sampler2D uPathColors;
/// The size of the path transform buffer texture in texels.
uniform ivec2 uPathTransformSTDimensions;
/// The path transform buffer texture, one path dilation per texel.
uniform sampler2D uPathTransformST;
/// The size of the extra path transform factors buffer texture in texels.
uniform ivec2 uPathTransformExtDimensions;
/// The extra path transform factors buffer texture, packed two path transforms per texel.
uniform sampler2D uPathTransformExt;
/// The amount of faux-bold to apply, in local path units.
uniform vec2 uEmboldenAmount;
/// The 2D position of this point.
attribute vec2 aPosition;
/// The path ID, starting from 1.
attribute float aPathID;
/// The vertex ID. In OpenGL 3.0+, this can be omitted in favor of `gl_VertexID`.
attribute float aVertexID;
/// The color of this path.
varying vec4 vColor;
void main() {
int pathID = int(aPathID);
int vertexID = int(aVertexID);
vec2 pathTransformExt;
vec4 pathTransformST = fetchPathAffineTransform(pathTransformExt,
uPathTransformST,
uPathTransformSTDimensions,
uPathTransformExt,
uPathTransformExtDimensions,
pathID);
vec2 position = hintPosition(aPosition, uHints);
position = transformVertexPositionAffine(position, pathTransformST, pathTransformExt);
position = transformVertexPosition(position, uTransform);
position = offsetPositionVertically(position, uFramebufferSize, imod(vertexID, 6) < 3);
float depth = convertPathIndexToViewportDepthValue(pathID);
gl_Position = vec4(position, depth, 1.0);
vColor = fetchFloat4Data(uPathColors, pathID, uPathColorsDimensions);
}

View File

@ -35,24 +35,21 @@ uniform vec2 uEmboldenAmount;
/// The 2D position of this point.
attribute vec2 aPosition;
/// The abstract Loop-Blinn texture coordinate for this point.
attribute vec2 aTexCoord;
/// The path ID, starting from 1.
attribute float aPathID;
/// Specifies whether this is a concave or convex curve.
attribute float aSign;
/// The angle of the 2D normal for this point.
attribute float aNormalAngle;
/// The vertex ID. In OpenGL 3.0+, this can be omitted in favor of `gl_VertexID`.
attribute float aVertexID;
/// The path ID, starting from 1.
attribute float aPathID;
/// The fill color of this path.
varying vec4 vColor;
/// The outgoing abstract Loop-Blinn texture coordinate.
varying vec2 vTexCoord;
/// Specifies whether this is a concave or convex curve.
varying float vSign;
void main() {
int pathID = int(aPathID);
int vertexID = int(aVertexID);
vec2 pathTransformExt;
vec4 pathTransformST = fetchPathAffineTransform(pathTransformExt,
@ -67,7 +64,9 @@ void main() {
gl_Position = uTransform * vec4(position, 0.0, 1.0);
int vertexIndex = imod(vertexID, 3);
vec2 texCoord = vec2(float(vertexIndex) * 0.5, float(vertexIndex == 2));
vColor = fetchFloat4Data(uPathColors, pathID, uPathColorsDimensions);
vTexCoord = vec2(aTexCoord) / 2.0;
vSign = aSign;
vTexCoord = texCoord;
}

View File

@ -25,11 +25,11 @@ precision highp float;
varying vec4 vColor;
/// The abstract Loop-Blinn texture coordinate.
varying vec2 vTexCoord;
/// Specifies whether this is a concave or convex curve.
varying float vSign;
void main() {
float side = vTexCoord.x * vTexCoord.x - vTexCoord.y;
float alpha = float(sign(side) == sign(vSign));
float side = sign(vTexCoord.x * vTexCoord.x - vTexCoord.y);
float winding = gl_FrontFacing ? -1.0 : 1.0;
float alpha = float(side == winding);
//float alpha = mod(gl_FragCoord.x, 2.0) < 1.0 ? 1.0 : 0.0;
gl_FragColor = alpha * vColor;
}

View File

@ -37,29 +37,22 @@ uniform sampler2D uPathTransformST;
uniform ivec2 uPathTransformExtDimensions;
/// The extra path transform factors buffer texture, packed two path transforms per texel.
uniform sampler2D uPathTransformExt;
/// The amount of faux-bold to apply, in local path units.
uniform vec2 uEmboldenAmount;
/// The 2D position of this point.
attribute vec2 aPosition;
/// The abstract Loop-Blinn texture coordinate for this point.
attribute vec2 aTexCoord;
/// The vertex ID. In OpenGL 3.0+, this can be omitted in favor of `gl_VertexID`.
attribute float aVertexID;
/// The path ID, starting from 1.
attribute float aPathID;
/// Specifies whether this is a concave or convex curve.
attribute float aSign;
/// The angle of the 2D normal for this point.
attribute float aNormalAngle;
/// The fill color of this path.
varying vec4 vColor;
/// The outgoing abstract Loop-Blinn texture coordinate.
varying vec2 vTexCoord;
/// Specifies whether this is a concave or convex curve.
varying float vSign;
void main() {
int pathID = int(aPathID);
int vertexID = int(aVertexID);
vec2 pathTransformExt;
vec4 pathTransformST = fetchPathAffineTransform(pathTransformExt,
@ -69,15 +62,16 @@ void main() {
uPathTransformExtDimensions,
pathID);
vec2 position = dilatePosition(aPosition, aNormalAngle, uEmboldenAmount);
position = hintPosition(position, uHints);
vec2 position = hintPosition(aPosition, uHints);
position = transformVertexPositionAffine(position, pathTransformST, pathTransformExt);
position = transformVertexPosition(position, uTransform);
float depth = convertPathIndexToViewportDepthValue(pathID);
gl_Position = vec4(position, depth, 1.0);
int vertexIndex = imod(vertexID, 3);
vec2 texCoord = vec2(float(vertexIndex) * 0.5, float(vertexIndex == 2));
vColor = fetchFloat4Data(uPathColors, pathID, uPathColorsDimensions);
vTexCoord = vec2(aTexCoord) / 2.0;
vSign = aSign;
vTexCoord = texCoord;
}

View File

@ -32,12 +32,11 @@ uniform sampler2D uPathTransformST;
uniform ivec2 uPathTransformExtDimensions;
/// The extra path transform factors buffer texture, packed two path transforms per texel.
uniform sampler2D uPathTransformExt;
/// The amount of faux-bold to apply, in local path units.
uniform vec2 uEmboldenAmount;
/// The 2D position of this point.
attribute vec2 aPosition;
/// The path ID, starting from 1.
attribute float aPathID;
attribute float aNormalAngle;
/// The color of this path.
varying vec4 vColor;
@ -53,8 +52,7 @@ void main() {
uPathTransformExtDimensions,
pathID);
vec2 position = dilatePosition(aPosition, aNormalAngle, uEmboldenAmount);
position = hintPosition(position, uHints);
vec2 position = hintPosition(aPosition, uHints);
position = transformVertexPositionAffine(position, pathTransformST, pathTransformExt);
position = transformVertexPosition(position, uTransform);

View File

@ -47,8 +47,8 @@ uniform sampler2D uPathTransformExt;
/// The amount of faux-bold to apply, in local path units.
uniform vec2 uEmboldenAmount;
/// The abstract quad position: (0.0, 0.0) to (1.0, 1.0).
attribute vec2 aQuadPosition;
/// The abstract position within the quad: (0.0, 0.0) to (1.0, 1.0).
attribute vec2 aTessCoord;
/// The position of the left endpoint.
attribute vec2 aLeftPosition;
/// The position of the control point.
@ -82,31 +82,63 @@ void main() {
vec4 bounds = fetchFloat4Data(uPathBounds, pathID, uPathBoundsDimensions);
// Transform the points, and compute the position of this vertex.
vec2 position;
float winding;
if (computeECAAQuadPosition(position,
winding,
leftPosition,
rightPosition,
aQuadPosition,
uFramebufferSize,
pathTransformST,
pathTransformExt,
uTransform,
uHints,
bounds,
leftRightNormalAngles,
uEmboldenAmount)) {
controlPointPosition = computeECAAPosition(controlPointPosition,
controlPointNormalAngle,
leftPosition = computeECAAPosition(leftPosition,
aNormalAngles.x,
uEmboldenAmount,
uHints,
pathTransformST,
pathTransformExt,
uTransform,
uFramebufferSize);
rightPosition = computeECAAPosition(rightPosition,
aNormalAngles.z,
uEmboldenAmount,
uHints,
pathTransformST,
pathTransformExt,
uTransform,
uFramebufferSize);
controlPointPosition = computeECAAPosition(controlPointPosition,
aNormalAngles.y,
uEmboldenAmount,
uHints,
pathTransformST,
pathTransformExt,
uTransform,
uFramebufferSize);
float winding = computeECAAWinding(leftPosition, rightPosition);
if (winding == 0.0) {
gl_Position = vec4(0.0);
return;
}
vec2 edgeBL = bounds.xy, edgeTL = bounds.xw, edgeTR = bounds.zw, edgeBR = bounds.zy;
edgeBL = transformECAAPosition(edgeBL, pathTransformST, pathTransformExt, uTransform);
edgeBR = transformECAAPosition(edgeBR, pathTransformST, pathTransformExt, uTransform);
edgeTL = transformECAAPosition(edgeTL, pathTransformST, pathTransformExt, uTransform);
edgeTR = transformECAAPosition(edgeTR, pathTransformST, pathTransformExt, uTransform);
// Find the bottom of the path, and convert to clip space.
//
// FIXME(pcwalton): Speed this up somehow?
float pathBottomY = max(max(edgeBL.y, edgeBR.y), max(edgeTL.y, edgeTR.y));
pathBottomY = (pathBottomY + 1.0) * 0.5 * float(uFramebufferSize.y);
vec2 position;
if (aTessCoord.x < 0.25)
position = vec2(floor(leftPosition.x), leftPosition.y);
else if (aTessCoord.x < 0.75)
position = controlPointPosition;
else
position = vec2(ceil(rightPosition.x), rightPosition.y);
// FIXME(pcwalton): Only compute path bottom Y if necessary.
if (aTessCoord.y < 0.5)
position.y = floor(position.y - 1.0);
else
position.y = pathBottomY;
position = convertScreenToClipSpace(position, uFramebufferSize);
float depth = convertPathIndexToViewportDepthValue(pathID);
gl_Position = vec4(position, depth, 1.0);

View File

@ -46,7 +46,7 @@ uniform sampler2D uPathTransformExt;
uniform vec2 uEmboldenAmount;
/// The abstract quad position: (0.0, 0.0) to (1.0, 1.0).
attribute vec2 aQuadPosition;
attribute vec2 aTessCoord;
/// The position of the left endpoint.
attribute vec2 aLeftPosition;
/// The position of the right endpoint.
@ -75,22 +75,53 @@ void main() {
vec4 bounds = fetchFloat4Data(uPathBounds, pathID, uPathBoundsDimensions);
// Transform the points, and compute the position of this vertex.
vec2 position;
float winding;
computeECAAQuadPosition(position,
winding,
leftPosition,
rightPosition,
aQuadPosition,
uFramebufferSize,
leftPosition = computeECAAPosition(leftPosition,
aNormalAngles.x,
uEmboldenAmount,
uHints,
pathTransformST,
pathTransformExt,
uTransform,
uFramebufferSize);
rightPosition = computeECAAPosition(rightPosition,
aNormalAngles.y,
uEmboldenAmount,
uHints,
bounds,
leftRightNormalAngles,
uEmboldenAmount);
pathTransformST,
pathTransformExt,
uTransform,
uFramebufferSize);
float winding = computeECAAWinding(leftPosition, rightPosition);
if (winding == 0.0) {
gl_Position = vec4(0.0);
return;
}
vec2 edgeBL = bounds.xy, edgeTL = bounds.xw, edgeTR = bounds.zw, edgeBR = bounds.zy;
edgeBL = transformECAAPosition(edgeBL, pathTransformST, pathTransformExt, uTransform);
edgeBR = transformECAAPosition(edgeBR, pathTransformST, pathTransformExt, uTransform);
edgeTL = transformECAAPosition(edgeTL, pathTransformST, pathTransformExt, uTransform);
edgeTR = transformECAAPosition(edgeTR, pathTransformST, pathTransformExt, uTransform);
// Find the bottom of the path, and convert to clip space.
//
// FIXME(pcwalton): Speed this up somehow?
float pathBottomY = max(max(edgeBL.y, edgeBR.y), max(edgeTL.y, edgeTR.y));
pathBottomY = (pathBottomY + 1.0) * 0.5 * float(uFramebufferSize.y);
vec2 position;
if (aTessCoord.x < 0.5)
position = vec2(floor(leftPosition.x), leftPosition.y);
else
position = vec2(ceil(rightPosition.x), rightPosition.y);
// FIXME(pcwalton): Only compute path bottom Y if necessary.
if (aTessCoord.y < 0.5)
position.y = floor(position.y - 1.0);
else
position.y = pathBottomY;
position = convertScreenToClipSpace(position, uFramebufferSize);
float depth = convertPathIndexToViewportDepthValue(pathID);
gl_Position = vec4(position, depth, 1.0);

View File

@ -57,7 +57,7 @@ uniform vec2 uEmboldenAmount;
uniform int uPassIndex;
/// The abstract quad position: (0.0, 0.0) to (1.0, 1.0).
attribute vec2 aQuadPosition;
attribute vec2 aTessCoord;
/// The position of the left endpoint.
attribute vec2 aLeftPosition;
/// The position of the control point.
@ -126,16 +126,33 @@ void main() {
return;
}
vec2 position = computeECAAQuadPositionFromTransformedPositions(leftPosition,
rightPosition,
aQuadPosition,
uFramebufferSize,
pathTransformST,
pathTransformExt,
uTransform,
bounds,
leftTopRightEdges);
vec2 edgeBL = bounds.xy, edgeTL = bounds.xw, edgeTR = bounds.zw, edgeBR = bounds.zy;
edgeBL = transformECAAPosition(edgeBL, pathTransformST, pathTransformExt, uTransform);
edgeBR = transformECAAPosition(edgeBR, pathTransformST, pathTransformExt, uTransform);
edgeTL = transformECAAPosition(edgeTL, pathTransformST, pathTransformExt, uTransform);
edgeTR = transformECAAPosition(edgeTR, pathTransformST, pathTransformExt, uTransform);
// Find the bottom of the path, and convert to clip space.
//
// FIXME(pcwalton): Speed this up somehow?
float pathBottomY = max(max(edgeBL.y, edgeBR.y), max(edgeTL.y, edgeTR.y));
pathBottomY = (pathBottomY + 1.0) * 0.5 * float(uFramebufferSize.y);
vec2 position;
if (aTessCoord.x < 0.25)
position = vec2(floor(leftPosition.x), leftPosition.y);
else if (aTessCoord.x < 0.75)
position = controlPointPosition;
else
position = vec2(ceil(rightPosition.x), rightPosition.y);
// FIXME(pcwalton): Only compute path bottom Y if necessary.
if (aTessCoord.y < 0.5)
position.y = floor(position.y - 1.0);
else
position.y = pathBottomY;
position = convertScreenToClipSpace(position, uFramebufferSize);
float depth = convertPathIndexToViewportDepthValue(pathID);
gl_Position = vec4(position, depth, 1.0);

View File

@ -53,10 +53,13 @@ uniform sampler2D uPathColors;
/// If this is true, then points will be snapped to the nearest pixel.
uniform bool uMulticolor;
attribute vec2 aQuadPosition;
attribute vec4 aUpperEndpointPositions;
attribute vec4 aLowerEndpointPositions;
attribute vec4 aControlPointPositions;
attribute vec2 aTessCoord;
attribute vec2 aUpperLeftEndpointPosition;
attribute vec2 aUpperControlPointPosition;
attribute vec2 aUpperRightEndpointPosition;
attribute vec2 aLowerRightEndpointPosition;
attribute vec2 aLowerControlPointPosition;
attribute vec2 aLowerLeftEndpointPosition;
attribute float aPathID;
varying vec4 vUpperEndpoints;
@ -65,13 +68,13 @@ 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;
vec2 tlPosition = aUpperLeftEndpointPosition;
vec2 tcPosition = aUpperControlPointPosition;
vec2 trPosition = aUpperRightEndpointPosition;
vec2 blPosition = aLowerLeftEndpointPosition;
vec2 bcPosition = aLowerControlPointPosition;
vec2 brPosition = aLowerRightEndpointPosition;
vec2 tessCoord = aTessCoord;
int pathID = int(floor(aPathID));
vec4 transformST = fetchFloat4Data(uPathTransformST, pathID, uPathTransformSTDimensions);
@ -129,19 +132,33 @@ void main() {
float depth = convertPathIndexToViewportDepthValue(pathID);
// Use the same side--in this case, the top--or else floating point error during partitioning
// can occasionally cause inconsistent rounding, resulting in cracks.
// Use the same side--in this case, the top--or else floating point error during
// partitioning can occasionally cause inconsistent rounding, resulting in cracks.
vec2 position;
if (uMulticolor)
position.x = quadPosition.x < 0.5 ? tlPosition.x : trPosition.x;
if (tessCoord.y < 0.5) {
if (tessCoord.x < 0.25)
position = tlPosition;
else if (tessCoord.x < 0.75)
position = tcPosition;
else
position.x = quadPosition.x < 0.5 ? floor(tlPosition.x) : ceil(trPosition.x);
if (quadPosition.y < 0.5)
position.y = floor(min(tlPosition.y, trPosition.y));
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.y = ceil(max(blPosition.y, brPosition.y));
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);