diff --git a/demo/client/src/meshes.ts b/demo/client/src/meshes.ts index c9ba93cc..29f65c64 100644 --- a/demo/client/src/meshes.ts +++ b/demo/client/src/meshes.ts @@ -1,6 +1,6 @@ // pathfinder/client/src/meshes.ts // -// Copyright © 2017 The Pathfinder Project Developers. +// Copyright © 2018 The Pathfinder Project Developers. // // Licensed under the Apache License, Version 2.0 or the MIT license @@ -87,6 +87,8 @@ const SEGMENT_LINE_SIZE: number = 4 * 4; const SEGMENT_CURVE_SIZE: number = 4 * 6; const MESH_TYPES: Meshes = { + bBoxPathIDs: { type: 'Uint16', size: 1 }, + bBoxes: { type: 'Float32', size: 20 }, bQuadVertexInteriorIndices: { type: 'Uint32', size: 1 }, bQuadVertexPositionPathIDs: { type: 'Uint16', size: 6 }, bQuadVertexPositions: { type: 'Float32', size: 12 }, @@ -99,6 +101,8 @@ const MESH_TYPES: Meshes = { }; const BUFFER_TYPES: Meshes = { + bBoxPathIDs: 'ARRAY_BUFFER', + bBoxes: 'ARRAY_BUFFER', bQuadVertexInteriorIndices: 'ELEMENT_ARRAY_BUFFER', bQuadVertexPositionPathIDs: 'ARRAY_BUFFER', bQuadVertexPositions: 'ARRAY_BUFFER', @@ -118,6 +122,7 @@ const MESH_LIBRARY_FOURCC: string = 'PFML'; // Must match the FourCCs in `pathfinder_partitioner::mesh_library::MeshLibrary::serialize_into()`. const BUFFER_TYPE_FOURCCS: BufferTypeFourCCTable = { + bbox: 'bBoxes', bqii: 'bQuadVertexInteriorIndices', bqvp: 'bQuadVertexPositions', scur: 'segmentCurves', @@ -129,6 +134,7 @@ 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 = { + bbox: 'bBoxPathRanges', bqii: 'bQuadVertexInteriorIndexPathRanges', bqvp: 'bQuadVertexPositionPathRanges', scur: 'segmentCurveRanges', @@ -136,6 +142,7 @@ const PATH_RANGE_TYPE_FOURCCS: PathRangeTypeFourCCTable = { }; const RANGE_TO_COUNT_TABLE: RangeToCountTable = { + bBoxPathRanges: 'bBoxCount', bQuadVertexInteriorIndexPathRanges: 'bQuadVertexInteriorIndexCount', bQuadVertexPositionPathRanges: 'bQuadVertexPositionCount', segmentCurveRanges: 'segmentCurveCount', @@ -143,6 +150,7 @@ const RANGE_TO_COUNT_TABLE: RangeToCountTable = { }; const RANGE_TO_RANGE_BUFFER_TABLE: RangeToRangeBufferTable = { + bBoxPathRanges: 'bBoxPathIDs', bQuadVertexPositionPathRanges: 'bQuadVertexPositionPathIDs', segmentCurveRanges: 'segmentCurvePathIDs', segmentLineRanges: 'segmentLinePathIDs', @@ -151,6 +159,7 @@ const RANGE_TO_RANGE_BUFFER_TABLE: RangeToRangeBufferTable = { const RANGE_KEYS: Array = [ 'bQuadVertexPositionPathRanges', 'bQuadVertexInteriorIndexPathRanges', + 'bBoxPathRanges', 'segmentCurveRanges', 'segmentLineRanges', ]; @@ -160,12 +169,14 @@ type BufferType = 'ARRAY_BUFFER' | 'ELEMENT_ARRAY_BUFFER'; export interface Meshes { readonly bQuadVertexPositions: T; readonly bQuadVertexInteriorIndices: T; + readonly bBoxes: T; readonly segmentLines: T; readonly segmentCurves: T; readonly segmentLineNormals: T; readonly segmentCurveNormals: T; bQuadVertexPositionPathIDs: T; + bBoxPathIDs: T; segmentLinePathIDs: T; segmentCurvePathIDs: T; } @@ -173,6 +184,7 @@ export interface Meshes { interface MeshDataCounts { readonly bQuadVertexPositionCount: number; readonly bQuadVertexInteriorIndexCount: number; + readonly bBoxCount: number; readonly segmentLineCount: number; readonly segmentCurveCount: number; } @@ -180,6 +192,7 @@ interface MeshDataCounts { interface PathRanges { bQuadVertexPositionPathRanges: Range[]; bQuadVertexInteriorIndexPathRanges: Range[]; + bBoxPathRanges: Range[]; segmentCurveRanges: Range[]; segmentLineRanges: Range[]; } @@ -187,6 +200,9 @@ interface PathRanges { export class PathfinderMeshData implements Meshes, MeshDataCounts, PathRanges { readonly bQuadVertexPositions: ArrayBuffer; readonly bQuadVertexInteriorIndices: ArrayBuffer; + readonly bBoxes: ArrayBuffer; + readonly bBoxSigns: ArrayBuffer; + readonly bBoxIndices: ArrayBuffer; readonly segmentLines: ArrayBuffer; readonly segmentCurves: ArrayBuffer; readonly segmentLineNormals: ArrayBuffer; @@ -194,15 +210,18 @@ export class PathfinderMeshData implements Meshes, MeshDataCounts, readonly bQuadVertexPositionCount: number; readonly bQuadVertexInteriorIndexCount: number; + readonly bBoxCount: number; readonly segmentLineCount: number; readonly segmentCurveCount: number; bQuadVertexPositionPathIDs: ArrayBuffer; + bBoxPathIDs: ArrayBuffer; segmentCurvePathIDs: ArrayBuffer; segmentLinePathIDs: ArrayBuffer; bQuadVertexPositionPathRanges: Range[]; bQuadVertexInteriorIndexPathRanges: Range[]; + bBoxPathRanges: Range[]; segmentCurveRanges: Range[]; segmentLineRanges: Range[]; @@ -241,6 +260,7 @@ export class PathfinderMeshData implements Meshes, MeshDataCounts, B_QUAD_VERTEX_POSITION_SIZE; this.bQuadVertexInteriorIndexCount = this.bQuadVertexInteriorIndices.byteLength / INDEX_SIZE; + this.bBoxCount = this.bBoxes.byteLength / (FLOAT32_SIZE * 6); this.segmentCurveCount = this.segmentCurves.byteLength / SEGMENT_CURVE_SIZE; this.segmentLineCount = this.segmentLines.byteLength / SEGMENT_LINE_SIZE; @@ -292,7 +312,7 @@ export class PathfinderMeshData implements Meshes, MeshDataCounts, const firstBQuadVertexIndex = bQuadVertexCopyResult.originalStartIndex; const lastBQuadVertexIndex = bQuadVertexCopyResult.originalEndIndex; - // Copy over indices. + // Copy over B-vertex indices. copyIndices(expandedArrays.bQuadVertexInteriorIndices, expandedRanges.bQuadVertexInteriorIndexPathRanges, originalBuffers.bQuadVertexInteriorIndices as Uint32Array, @@ -301,6 +321,23 @@ export class PathfinderMeshData implements Meshes, MeshDataCounts, lastBQuadVertexIndex * 6, expandedPathID); + // Copy over B-boxes. + const bBoxVertexCopyResult = copyVertices(['bBoxes'], + 'bBoxPathRanges', + expandedArrays, + expandedRanges, + originalBuffers, + originalRanges, + expandedPathID, + originalPathID); + + if (bBoxVertexCopyResult == null) + continue; + + const firstExpandedBBoxIndex = bBoxVertexCopyResult.expandedStartIndex; + const firstBBoxIndex = bBoxVertexCopyResult.originalStartIndex; + const lastBBoxIndex = bBoxVertexCopyResult.originalEndIndex; + // Copy over segments. copySegments(['segmentLines', 'segmentLineNormals'], 'segmentLineRanges', @@ -361,9 +398,11 @@ export class PathfinderMeshData implements Meshes, MeshDataCounts, 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 meshType = MESH_TYPES[rangeBufferKey]; + const fieldCount = meshType.size; + const destBuffer = new Uint16Array(instanceCount * fieldCount); let destIndex = 0; for (let pathIndex = 0; pathIndex < ranges.length; pathIndex++) { @@ -385,6 +424,10 @@ export class PathfinderMeshBuffers implements Meshes, PathRanges { readonly bQuadVertexPositions: WebGLBuffer; readonly bQuadVertexPositionPathIDs: WebGLBuffer; readonly bQuadVertexInteriorIndices: WebGLBuffer; + readonly bBoxes: WebGLBuffer; + readonly bBoxSigns: WebGLBuffer; + readonly bBoxIndices: WebGLBuffer; + readonly bBoxPathIDs: WebGLBuffer; readonly segmentLines: WebGLBuffer; readonly segmentCurves: WebGLBuffer; readonly segmentLinePathIDs: WebGLBuffer; @@ -394,6 +437,7 @@ export class PathfinderMeshBuffers implements Meshes, PathRanges { readonly bQuadVertexPositionPathRanges: Range[]; readonly bQuadVertexInteriorIndexPathRanges: Range[]; + readonly bBoxPathRanges: Range[]; readonly segmentCurveRanges: Range[]; readonly segmentLineRanges: Range[]; diff --git a/demo/client/src/renderer.ts b/demo/client/src/renderer.ts index 8e40b178..e480f8dd 100644 --- a/demo/client/src/renderer.ts +++ b/demo/client/src/renderer.ts @@ -267,14 +267,7 @@ export abstract class Renderer { } setTransformUniform(uniforms: UniformMap, pass: number, objectIndex: number): void { - let transform; - if (this.antialiasingStrategy == null) - transform = glmatrix.mat4.create(); - else - transform = this.antialiasingStrategy.worldTransformForPass(this, pass); - - glmatrix.mat4.mul(transform, transform, this.worldTransform); - glmatrix.mat4.mul(transform, transform, this.getModelviewTransform(objectIndex)); + const transform = this.computeTransform(pass, objectIndex); this.renderContext.gl.uniformMatrix4fv(uniforms.uTransform, false, transform); } @@ -284,10 +277,7 @@ export abstract class Renderer { const renderContext = this.renderContext; const gl = renderContext.gl; - const transform = glmatrix.mat4.clone(this.worldTransform); - glmatrix.mat4.mul(transform, transform, this.getModelviewTransform(objectIndex)); - - const translation = glmatrix.vec4.clone([transform[12], transform[13], 0.0, 1.0]); + const transform = this.computeTransform(0, objectIndex); gl.uniform4f(uniforms.uTransformST, transform[0], @@ -296,6 +286,22 @@ export abstract class Renderer { transform[13]); } + setTransformAffineUniforms(uniforms: UniformMap, objectIndex: number): void { + // FIXME(pcwalton): Lossy conversion from a 4x4 matrix to an affine matrix is ugly and + // fragile. Refactor. + const renderContext = this.renderContext; + const gl = renderContext.gl; + + const transform = this.computeTransform(0, objectIndex); + + gl.uniform4f(uniforms.uTransformST, + transform[0], + transform[5], + transform[12], + transform[13]); + gl.uniform2f(uniforms.uTransformExt, transform[1], transform[4]); + } + uploadPathColors(objectCount: number): void { const renderContext = this.renderContext; for (let objectIndex = 0; objectIndex < objectCount; objectIndex++) { @@ -481,7 +487,10 @@ export abstract class Renderer { this.initImplicitCoverInteriorVAO(objectIndex, instanceRange, renderingMode); // Draw direct interior parts. - this.setTransformUniform(directInteriorProgram.uniforms, pass, objectIndex); + if (renderingMode === 'conservative') + this.setTransformAffineUniforms(directInteriorProgram.uniforms, objectIndex); + else + this.setTransformUniform(directInteriorProgram.uniforms, pass, objectIndex); this.setFramebufferSizeUniform(directInteriorProgram.uniforms); this.setHintsUniform(directInteriorProgram.uniforms); this.setPathColorsUniform(objectIndex, directInteriorProgram.uniforms, 0); @@ -737,6 +746,18 @@ export abstract class Renderer { gl.bufferData(gl.ARRAY_BUFFER, vertexIDs, gl.STATIC_DRAW); gl.bindBuffer(gl.ARRAY_BUFFER, null); } + + private computeTransform(pass: number, objectIndex: number): glmatrix.mat4 { + let transform; + if (this.antialiasingStrategy == null) + transform = glmatrix.mat4.create(); + else + transform = this.antialiasingStrategy.worldTransformForPass(this, pass); + + glmatrix.mat4.mul(transform, transform, this.worldTransform); + glmatrix.mat4.mul(transform, transform, this.getModelviewTransform(objectIndex)); + return transform; + } } function getMeshIndexRange(indexRanges: Range[], pathRange: Range): Range { diff --git a/demo/client/src/view.ts b/demo/client/src/view.ts index 1595402a..02c06ecf 100644 --- a/demo/client/src/view.ts +++ b/demo/client/src/view.ts @@ -272,6 +272,7 @@ export abstract class DemoView extends PathfinderView implements RenderContext { this.vertexArrayObjectExt = this.gl.getExtension('OES_vertex_array_object'); this.gl.getExtension('EXT_frag_depth'); this.gl.getExtension('OES_element_index_uint'); + this.gl.getExtension('OES_standard_derivatives'); this.gl.getExtension('OES_texture_float'); this.gl.getExtension('WEBGL_depth_texture'); diff --git a/demo/client/src/xcaa-strategy.ts b/demo/client/src/xcaa-strategy.ts index ed4d9141..ee9aa29d 100644 --- a/demo/client/src/xcaa-strategy.ts +++ b/demo/client/src/xcaa-strategy.ts @@ -42,10 +42,12 @@ const PATCH_VERTICES: Float32Array = new Float32Array([ 1.0, 1.0, ]); -const MCAA_PATCH_INDICES: Uint8Array = new Uint8Array([0, 1, 2, 0, 2, 3, 2, 5, 3, 3, 5, 4]); +const MCAA_PATCH_INDICES: Uint8Array = new Uint8Array([0, 1, 2, 1, 3, 2]); const ECAA_CURVE_PATCH_INDICES: Uint8Array = new Uint8Array([0, 1, 2, 0, 2, 3, 2, 5, 3]); +export type TransformType = 'dilation' | 'affine' | '3d'; + export abstract class XCAAStrategy extends AntialiasingStrategy { abstract readonly directRenderingMode: DirectRenderingMode; @@ -56,7 +58,7 @@ export abstract class XCAAStrategy extends AntialiasingStrategy { return 1; } - protected abstract get usesDilationTransforms(): boolean; + protected abstract get transformType(): TransformType; protected abstract get patchIndices(): Uint8Array; @@ -255,10 +257,17 @@ export abstract class XCAAStrategy extends AntialiasingStrategy { const renderContext = renderer.renderContext; const gl = renderContext.gl; - if (this.usesDilationTransforms) + switch (this.transformType) { + case 'dilation': renderer.setTransformSTUniform(uniforms, 0); - else + break; + case 'affine': + renderer.setTransformAffineUniforms(uniforms, 0); + break; + case '3d': renderer.setTransformUniform(uniforms, 0, 0); + break; + } gl.uniform2i(uniforms.uFramebufferSize, this.supersampledFramebufferSize[0], @@ -372,8 +381,8 @@ export class MCAAStrategy extends XCAAStrategy { return MCAA_PATCH_INDICES; } - protected get usesDilationTransforms(): boolean { - return true; + protected get transformType(): TransformType { + return 'affine'; } protected get mightUseAAFramebuffer(): boolean { @@ -434,9 +443,7 @@ export class MCAAStrategy extends XCAAStrategy { 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); + gl.disable(gl.CULL_FACE); } protected clearForResolve(renderer: Renderer): void { @@ -485,80 +492,68 @@ export class MCAAStrategy extends XCAAStrategy { const vao = this.vao; renderContext.vertexArrayObjectExt.bindVertexArrayOES(vao); - const bQuadRanges = renderer.meshData[meshIndex].bQuadVertexPositionPathRanges; - const offset = calculateStartFromIndexRanges(pathRange, bQuadRanges); + const bBoxRanges = renderer.meshData[meshIndex].bBoxPathRanges; + const offset = calculateStartFromIndexRanges(pathRange, bBoxRanges); gl.useProgram(shaderProgram.program); - 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.aUpperLeftEndpointPosition, - 2, + gl.bindBuffer(gl.ARRAY_BUFFER, renderer.renderContext.quadPositionsBuffer); + gl.vertexAttribPointer(attributes.aTessCoord, 2, gl.FLOAT, false, FLOAT32_SIZE * 2, 0); + gl.bindBuffer(gl.ARRAY_BUFFER, renderer.meshes[meshIndex].bBoxes); + gl.vertexAttribPointer(attributes.aRect, + 4, gl.FLOAT, false, - FLOAT32_SIZE * 12, - FLOAT32_SIZE * 12 * offset + FLOAT32_SIZE * 0); - gl.vertexAttribPointer(attributes.aUpperControlPointPosition, - 2, + FLOAT32_SIZE * 20, + FLOAT32_SIZE * 0 + offset * FLOAT32_SIZE * 20); + gl.vertexAttribPointer(attributes.aUV, + 4, gl.FLOAT, false, - FLOAT32_SIZE * 12, - FLOAT32_SIZE * 12 * offset + FLOAT32_SIZE * 2); - gl.vertexAttribPointer(attributes.aUpperRightEndpointPosition, - 2, + FLOAT32_SIZE * 20, + FLOAT32_SIZE * 4 + offset * FLOAT32_SIZE * 20); + gl.vertexAttribPointer(attributes.aDUVDX, + 4, gl.FLOAT, false, - FLOAT32_SIZE * 12, - FLOAT32_SIZE * 12 * offset + FLOAT32_SIZE * 4); - gl.vertexAttribPointer(attributes.aLowerRightEndpointPosition, - 2, + FLOAT32_SIZE * 20, + FLOAT32_SIZE * 8 + offset * FLOAT32_SIZE * 20); + gl.vertexAttribPointer(attributes.aDUVDY, + 4, gl.FLOAT, false, - FLOAT32_SIZE * 12, - FLOAT32_SIZE * 12 * offset + FLOAT32_SIZE * 6); - gl.vertexAttribPointer(attributes.aLowerControlPointPosition, - 2, + FLOAT32_SIZE * 20, + FLOAT32_SIZE * 12 + offset * FLOAT32_SIZE * 20); + gl.vertexAttribPointer(attributes.aSignMode, + 4, 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.aUpperLeftEndpointPosition, 1); - renderContext.instancedArraysExt - .vertexAttribDivisorANGLE(attributes.aUpperControlPointPosition, 1); - renderContext.instancedArraysExt - .vertexAttribDivisorANGLE(attributes.aUpperRightEndpointPosition, 1); - renderContext.instancedArraysExt - .vertexAttribDivisorANGLE(attributes.aLowerRightEndpointPosition, 1); - renderContext.instancedArraysExt - .vertexAttribDivisorANGLE(attributes.aLowerControlPointPosition, 1); - renderContext.instancedArraysExt - .vertexAttribDivisorANGLE(attributes.aLowerLeftEndpointPosition, 1); + FLOAT32_SIZE * 20, + FLOAT32_SIZE * 16 + offset * FLOAT32_SIZE * 20); - gl.bindBuffer(gl.ARRAY_BUFFER, renderer.meshes[meshIndex].bQuadVertexPositionPathIDs); + gl.enableVertexAttribArray(attributes.aTessCoord); + gl.enableVertexAttribArray(attributes.aRect); + gl.enableVertexAttribArray(attributes.aUV); + gl.enableVertexAttribArray(attributes.aDUVDX); + gl.enableVertexAttribArray(attributes.aDUVDY); + gl.enableVertexAttribArray(attributes.aSignMode); + + renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aRect, 1); + renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aUV, 1); + renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aDUVDX, 1); + renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aDUVDY, 1); + renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aSignMode, 1); + + gl.bindBuffer(gl.ARRAY_BUFFER, renderer.meshes[meshIndex].bBoxPathIDs); gl.vertexAttribPointer(attributes.aPathID, 1, gl.UNSIGNED_SHORT, false, - UINT16_SIZE * 6, - UINT16_SIZE * offset); + UINT16_SIZE, + offset * UINT16_SIZE); + gl.enableVertexAttribArray(attributes.aPathID); renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aPathID, 1); - 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, this.patchIndexBuffer); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, renderer.renderContext.quadElementsBuffer); renderContext.vertexArrayObjectExt.bindVertexArrayOES(null); } @@ -571,7 +566,7 @@ export class MCAAStrategy extends XCAAStrategy { objectIndex: number, shaderProgram: PathfinderShaderProgram): void { - if (renderer.meshData == null) + if (renderer.meshes == null || renderer.meshData == null) return; const renderContext = renderer.renderContext; @@ -593,11 +588,13 @@ export class MCAAStrategy extends XCAAStrategy { this.setBlendModeForAA(renderer); this.setAADepthState(renderer); - const bQuadRanges = renderer.meshData[meshIndex].bQuadVertexPositionPathRanges; - const count = calculateCountFromIndexRanges(pathRange, bQuadRanges); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, renderContext.quadElementsBuffer); + + const bBoxRanges = renderer.meshData[meshIndex].bBoxPathRanges; + const count = calculateCountFromIndexRanges(pathRange, bBoxRanges); renderContext.instancedArraysExt - .drawElementsInstancedANGLE(gl.TRIANGLES, 12, gl.UNSIGNED_BYTE, 0, count); + .drawElementsInstancedANGLE(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0, count); renderContext.vertexArrayObjectExt.bindVertexArrayOES(null); gl.disable(gl.DEPTH_TEST); @@ -641,8 +638,8 @@ export class ECAAStrategy extends XCAAStrategy { private lineVAOs: Partial>; private curveVAOs: Partial>; - protected get usesDilationTransforms(): boolean { - return false; + protected get transformType(): TransformType { + return '3d'; } get directRenderingMode(): DirectRenderingMode { diff --git a/partitioner/src/mesh_library.rs b/partitioner/src/mesh_library.rs index 4e65d4b7..b65514f6 100644 --- a/partitioner/src/mesh_library.rs +++ b/partitioner/src/mesh_library.rs @@ -10,7 +10,8 @@ use bincode::{self, Infinite}; use byteorder::{LittleEndian, WriteBytesExt}; -use euclid::{Point2D, Vector2D}; +use euclid::approxeq::ApproxEq; +use euclid::{Point2D, Rect, Size2D, Vector2D}; use lyon_path::PathEvent; use lyon_path::iterator::PathIterator; use pathfinder_path_utils::normals::PathNormals; @@ -32,6 +33,7 @@ pub struct MeshLibrary { pub b_quad_vertex_interior_indices: Vec, pub b_vertex_positions: Vec>, pub b_vertex_loop_blinn_data: Vec, + pub b_boxes: Vec, pub segments: MeshLibrarySegments, pub segment_normals: MeshLibrarySegmentNormals, } @@ -46,6 +48,7 @@ impl MeshLibrary { b_quad_vertex_interior_indices: vec![], b_vertex_positions: vec![], b_vertex_loop_blinn_data: vec![], + b_boxes: vec![], segments: MeshLibrarySegments::new(), segment_normals: MeshLibrarySegmentNormals::new(), } @@ -58,6 +61,7 @@ impl MeshLibrary { self.b_quad_vertex_interior_indices.clear(); self.b_vertex_positions.clear(); self.b_vertex_loop_blinn_data.clear(); + self.b_boxes.clear(); self.segments.clear(); self.segment_normals.clear(); } @@ -78,45 +82,121 @@ impl MeshLibrary { } pub(crate) fn add_b_quad(&mut self, b_quad: &BQuad) { + let BQuadVertexPositions { + upper_left_vertex_position: ul, + upper_right_vertex_position: ur, + lower_left_vertex_position: ll, + lower_right_vertex_position: lr, + .. + } = self.get_b_quad_vertex_positions(b_quad); + + if ul.x.approx_eq(&ur.x) || ll.x.approx_eq(&lr.x) { + return + } + self.b_quads.push(*b_quad); - let upper_left_position = - 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]; - let lower_left_position = - self.b_vertex_positions[b_quad.lower_left_vertex_index as usize]; - let lower_right_position = - self.b_vertex_positions[b_quad.lower_right_vertex_index as usize]; - - let mut b_quad_vertex_positions = BQuadVertexPositions { - upper_left_vertex_position: upper_left_position, - upper_control_point_position: upper_left_position, - upper_right_vertex_position: upper_right_position, - lower_left_vertex_position: lower_left_position, - lower_control_point_position: lower_right_position, - lower_right_vertex_position: lower_right_position, - }; - - if b_quad.upper_control_point_vertex_index != u32::MAX { - let upper_control_point_position = - self.b_vertex_positions[b_quad.upper_control_point_vertex_index as usize]; - b_quad_vertex_positions.upper_control_point_position = upper_control_point_position; - } - - if b_quad.lower_control_point_vertex_index != u32::MAX { - let lower_control_point_position = - self.b_vertex_positions[b_quad.lower_control_point_vertex_index as usize]; - b_quad_vertex_positions.lower_control_point_position = lower_control_point_position; - } + self.add_b_quad_vertex_positions(b_quad); + self.add_b_box(b_quad); + } + fn add_b_quad_vertex_positions(&mut self, b_quad: &BQuad) { + let b_quad_vertex_positions = self.get_b_quad_vertex_positions(b_quad); let first_b_quad_vertex_position_index = (self.b_quad_vertex_positions.len() as u32) * 6; 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 add_b_box(&mut self, b_quad: &BQuad) { + let BQuadVertexPositions { + upper_left_vertex_position: ul, + upper_control_point_position: uc, + upper_right_vertex_position: ur, + lower_left_vertex_position: ll, + lower_control_point_position: lc, + lower_right_vertex_position: lr, + } = self.get_b_quad_vertex_positions(b_quad); + + let rect = Rect::from_points([ul, uc, ur, ll, lc, lr].into_iter()); + + let (edge_ucl, edge_urc, edge_ulr) = (uc - ul, ur - uc, ul - ur); + let (edge_lcl, edge_lrc, edge_llr) = (lc - ll, lr - lc, ll - lr); + + let (edge_len_ucl, edge_len_urc) = (edge_ucl.length(), edge_urc.length()); + let (edge_len_lcl, edge_len_lrc) = (edge_lcl.length(), edge_lrc.length()); + let (edge_len_ulr, edge_len_llr) = (edge_ulr.length(), edge_llr.length()); + + let (uv_upper, uv_lower, sign_upper, sign_lower, mode_upper, mode_lower); + + if edge_len_ucl < 0.01 || edge_len_urc < 0.01 || edge_len_ulr < 0.01 || + edge_ucl.dot(-edge_ulr) > 0.9999 * edge_len_ucl * edge_len_ulr { + uv_upper = Uv::line(&rect, &ul, &ur); + sign_upper = -1.0; + mode_upper = -1.0; + } else { + uv_upper = Uv::curve(&rect, &ul, &uc, &ur); + sign_upper = (edge_ucl.cross(-edge_ulr)).signum(); + mode_upper = 1.0; + } + + if edge_len_lcl < 0.01 || edge_len_lrc < 0.01 || edge_len_llr < 0.01 || + edge_lcl.dot(-edge_llr) > 0.9999 * edge_len_lcl * edge_len_llr { + uv_lower = Uv::line(&rect, &ll, &lr); + sign_lower = 1.0; + mode_lower = -1.0; + } else { + uv_lower = Uv::curve(&rect, &ll, &lc, &lr); + sign_lower = -(edge_lcl.cross(-edge_llr)).signum(); + mode_lower = 1.0; + } + + let b_box = BBox { + upper_left_position: rect.origin, + lower_right_position: rect.bottom_right(), + upper_left_uv_upper: uv_upper.origin, + upper_left_uv_lower: uv_lower.origin, + d_upper_uv_dx: uv_upper.d_uv_dx, + d_lower_uv_dx: uv_lower.d_uv_dx, + d_upper_uv_dy: uv_upper.d_uv_dy, + d_lower_uv_dy: uv_lower.d_uv_dy, + upper_sign: sign_upper, + lower_sign: sign_lower, + upper_mode: mode_upper, + lower_mode: mode_lower, + }; + + self.b_boxes.push(b_box); + } + + fn get_b_quad_vertex_positions(&self, b_quad: &BQuad) -> BQuadVertexPositions { + let ul = self.b_vertex_positions[b_quad.upper_left_vertex_index as usize]; + let ur = self.b_vertex_positions[b_quad.upper_right_vertex_index as usize]; + let ll = self.b_vertex_positions[b_quad.lower_left_vertex_index as usize]; + let lr = self.b_vertex_positions[b_quad.lower_right_vertex_index as usize]; + + let mut b_quad_vertex_positions = BQuadVertexPositions { + upper_left_vertex_position: ul, + upper_control_point_position: ul, + upper_right_vertex_position: ur, + lower_left_vertex_position: ll, + lower_control_point_position: lr, + lower_right_vertex_position: lr, + }; + + if b_quad.upper_control_point_vertex_index != u32::MAX { + let uc = &self.b_vertex_positions[b_quad.upper_control_point_vertex_index as usize]; + b_quad_vertex_positions.upper_control_point_position = *uc; + } + + if b_quad.lower_control_point_vertex_index != u32::MAX { + let lc = &self.b_vertex_positions[b_quad.lower_control_point_vertex_index as usize]; + b_quad_vertex_positions.lower_control_point_position = *lc; + } + + b_quad_vertex_positions + } + fn push_b_quad_vertex_position_interior_indices(&mut self, first_vertex_index: u32, b_quad: &BQuadVertexPositions) { @@ -282,6 +362,7 @@ impl MeshLibrary { try!(write_simple_chunk(writer, b"bqua", &self.b_quads)); try!(write_simple_chunk(writer, b"bqvp", &self.b_quad_vertex_positions)); try!(write_simple_chunk(writer, b"bqii", &self.b_quad_vertex_interior_indices)); + try!(write_simple_chunk(writer, b"bbox", &self.b_boxes)); 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)); @@ -331,6 +412,7 @@ impl MeshLibrary { 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"bbox", path_ranges, |ranges| &ranges.b_boxes)); try!(write_path_range(writer, b"slin", path_ranges, |ranges| &ranges.segment_lines)); try!(write_path_range(writer, b"scur", path_ranges, |ranges| &ranges.segment_curves)); Ok(()) @@ -359,6 +441,7 @@ impl MeshLibrary { 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, + b_boxes: self.b_boxes.len() as u32, } } } @@ -374,6 +457,7 @@ pub(crate) struct MeshLibraryLengths { b_quad_vertex_positions: u32, b_quad_vertex_interior_indices: u32, b_vertices: u32, + b_boxes: u32, } #[derive(Clone, Debug)] @@ -382,6 +466,7 @@ pub struct PathRanges { pub b_quad_vertex_positions: Range, pub b_quad_vertex_interior_indices: Range, pub b_vertices: Range, + pub b_boxes: Range, pub segment_lines: Range, pub segment_curves: Range, } @@ -393,6 +478,7 @@ impl PathRanges { b_quad_vertex_positions: 0..0, b_quad_vertex_interior_indices: 0..0, b_vertices: 0..0, + b_boxes: 0..0, segment_lines: 0..0, segment_curves: 0..0, } @@ -406,6 +492,7 @@ impl PathRanges { 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.b_boxes = start.b_boxes..end.b_boxes; } } @@ -474,3 +561,134 @@ pub struct CurveSegmentNormals { pub control_point: f32, pub endpoint_1: f32, } + +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +pub struct BBox { + pub upper_left_position: Point2D, + pub lower_right_position: Point2D, + pub upper_left_uv_upper: Point2D, + pub upper_left_uv_lower: Point2D, + pub d_upper_uv_dx: Vector2D, + pub d_lower_uv_dx: Vector2D, + pub d_upper_uv_dy: Vector2D, + pub d_lower_uv_dy: Vector2D, + pub upper_sign: f32, + pub lower_sign: f32, + pub upper_mode: f32, + pub lower_mode: f32, +} + +#[derive(Clone, Copy, Debug)] +struct CornerPositions { + upper_left: Point2D, + upper_right: Point2D, + lower_left: Point2D, + lower_right: Point2D, +} + +#[derive(Clone, Copy, Debug)] +struct CornerValues { + upper_left: Point2D, + upper_right: Point2D, + lower_left: Point2D, + lower_right: Point2D, +} + +#[derive(Clone, Copy, Debug)] +struct Uv { + origin: Point2D, + d_uv_dx: Vector2D, + d_uv_dy: Vector2D, +} + +impl Uv { + fn from_values(origin: &Point2D, origin_right: &Point2D, origin_down: &Point2D) + -> Uv { + Uv { + origin: *origin, + d_uv_dx: *origin_right - *origin, + d_uv_dy: *origin_down - *origin, + } + } + + fn curve(rect: &Rect, left: &Point2D, ctrl: &Point2D, right: &Point2D) + -> Uv { + let origin_right = rect.top_right(); + let origin_down = rect.bottom_left(); + + let (lambda_origin, denom) = to_barycentric(left, ctrl, right, &rect.origin); + let (lambda_origin_right, _) = to_barycentric(left, ctrl, right, &origin_right); + let (lambda_origin_down, _) = to_barycentric(left, ctrl, right, &origin_down); + + let uv_origin = lambda_to_uv(&lambda_origin, denom); + let uv_origin_right = lambda_to_uv(&lambda_origin_right, denom); + let uv_origin_down = lambda_to_uv(&lambda_origin_down, denom); + + return Uv::from_values(&uv_origin, &uv_origin_right, &uv_origin_down); + + // https://gamedev.stackexchange.com/a/23745 + fn to_barycentric(a: &Point2D, b: &Point2D, c: &Point2D, p: &Point2D) + -> ([f64; 2], f64) { + let (a, b, c, p) = (a.to_f64(), b.to_f64(), c.to_f64(), p.to_f64()); + let (v0, v1, v2) = (b - a, c - a, p - a); + let (d00, d01) = (v0.dot(v0), v0.dot(v1)); + let d11 = v1.dot(v1); + let (d20, d21) = (v2.dot(v0), v2.dot(v1)); + let denom = d00 * d11 - d01 * d01; + ([(d11 * d20 - d01 * d21), (d00 * d21 - d01 * d20)], denom) + } + + fn lambda_to_uv(lambda: &[f64; 2], denom: f64) -> Point2D { + (Point2D::new(lambda[0] * 0.5 + lambda[1], lambda[1]) / denom).to_f32() + } + } + + fn line(rect: &Rect, left: &Point2D, right: &Point2D) -> Uv { + let (values, line_bounds); + if f32::abs(left.y - right.y) < 0.01 { + values = CornerValues { + upper_left: Point2D::new(0.0, 0.5), + upper_right: Point2D::new(0.5, 1.0), + lower_right: Point2D::new(1.0, 0.5), + lower_left: Point2D::new(0.5, 0.0), + }; + line_bounds = Rect::new(*left + Vector2D::new(0.0, -1.0), + Size2D::new(right.x - left.x, 2.0)); + } else { + if left.y < right.y { + values = CornerValues { + upper_left: Point2D::new(1.0, 1.0), + upper_right: Point2D::new(0.0, 1.0), + lower_left: Point2D::new(1.0, 0.0), + lower_right: Point2D::new(0.0, 0.0), + }; + } else { + values = CornerValues { + upper_left: Point2D::new(0.0, 1.0), + upper_right: Point2D::new(1.0, 1.0), + lower_left: Point2D::new(0.0, 0.0), + lower_right: Point2D::new(1.0, 0.0), + }; + } + line_bounds = Rect::from_points([*left, *right].into_iter()); + } + + let origin_right = rect.top_right(); + let origin_down = rect.bottom_left(); + + let uv_origin = bilerp(&line_bounds, &values, &rect.origin); + let uv_origin_right = bilerp(&line_bounds, &values, &origin_right); + let uv_origin_down = bilerp(&line_bounds, &values, &origin_down); + + return Uv::from_values(&uv_origin, &uv_origin_right, &uv_origin_down); + + fn bilerp(rect: &Rect, values: &CornerValues, position: &Point2D) + -> Point2D { + let upper = values.upper_left.lerp(values.upper_right, + (position.x - rect.min_x()) / rect.size.width); + let lower = values.lower_left.lerp(values.lower_right, + (position.x - rect.min_x()) / rect.size.width); + upper.lerp(lower, (position.y - rect.min_y()) / rect.size.height) + } + } +} diff --git a/shaders/gles2/common.inc.glsl b/shaders/gles2/common.inc.glsl index 58278be5..116e2cdd 100644 --- a/shaders/gles2/common.inc.glsl +++ b/shaders/gles2/common.inc.glsl @@ -11,6 +11,7 @@ #version 100 #extension GL_EXT_frag_depth : require +#extension GL_OES_standard_derivatives : require #define LCD_FILTER_FACTOR_0 (86.0 / 255.0) #define LCD_FILTER_FACTOR_1 (77.0 / 255.0) @@ -104,65 +105,11 @@ float convertPathIndexToViewportDepthValue(int pathIndex) { return float(pathIndex) / float(MAX_PATHS) * 2.0 - 1.0; } -/// Packs the given path ID into a floating point value suitable for storage in the depth buffer. -/// -/// This function returns values in window space (i.e. what `gl_FragDepth`/`gl_FragDepthEXT` is -/// in). -float convertPathIndexToWindowDepthValue(int pathIndex) { - return float(pathIndex) / float(MAX_PATHS); -} - /// Displaces the given point by the given distance in the direction of the normal angle. 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, - vec4 globalTransformST, - ivec2 framebufferSize) { - if (position == vec2(0.0)) - return position; - - position = hintPosition(position, hints); - position = transformVertexPositionST(position, localTransformST); - position = transformVertexPositionST(position, globalTransformST); - return convertClipToScreenSpace(position, framebufferSize); -} - -vec2 computeMCAASnappedPosition(vec2 position, - vec4 hints, - vec4 localTransformST, - vec4 globalTransformST, - ivec2 framebufferSize, - float slope, - bool snapToPixelGrid) { - position = hintPosition(position, hints); - position = transformVertexPositionST(position, localTransformST); - position = transformVertexPositionST(position, globalTransformST); - position = convertClipToScreenSpace(position, framebufferSize); - - float xNudge; - if (snapToPixelGrid) { - xNudge = fract(position.x); - if (xNudge < 0.5) - xNudge = -xNudge; - else - xNudge = 1.0 - xNudge; - } else { - xNudge = 0.0; - } - - return position + vec2(xNudge, xNudge * slope); -} - vec2 transformECAAPosition(vec2 position, vec4 localTransformST, vec2 localTransformExt, @@ -409,21 +356,6 @@ float computeCoverage(vec2 p0X, vec2 dPX, float pixelCenterY, float winding) { return abs(area) * winding * 0.5; } -/// Returns true if the line runs through this pixel or false otherwise. -/// -/// The line must run left-to-right and must already be clipped to the left and right sides of the -/// pixel, which implies that `dP.x` must be within the range [0.0, 1.0]. -/// -/// * `p0X` is the start point of the line. -/// * `dPX` is the vector from the start point to the endpoint of the line. -/// * `pixelCenterY` is the Y coordinate of the center of the pixel in window coordinates (i.e. -/// `gl_FragCoord.y`). -bool isPartiallyCovered(vec2 p0X, vec2 dPX, float pixelCenterY) { - float pixelTop; - vec2 dP = clipLineToPixelRow(p0X, dPX, pixelCenterY, pixelTop).zw; - return !isNearZero(dP.x) || !isNearZero(dP.y); -} - /// Solves the equation: /// /// x = p0x + t^2 * (p0x - 2*p1x + p2x) + t*(2*p1x - 2*p0x) @@ -482,3 +414,11 @@ vec4 fetchPathAffineTransform(out vec2 outPathTransformExt, pathTransformExtDimensions); return fetchFloat4Data(pathTransformSTTexture, pathID, pathTransformSTDimensions); } + +float detMat2(mat2 m) { + return m[0][0] * m[1][1] - m[0][1] * m[1][0]; +} + +mat2 invMat2(mat2 m) { + return mat2(m[1][1], -m[0][1], -m[1][0], m[0][0]) / detMat2(m); +} diff --git a/shaders/gles2/conservative-interior.vs.glsl b/shaders/gles2/conservative-interior.vs.glsl index be76e8b7..84499e39 100644 --- a/shaders/gles2/conservative-interior.vs.glsl +++ b/shaders/gles2/conservative-interior.vs.glsl @@ -1,6 +1,6 @@ -// pathfinder/shaders/gles2/direct-interior.vs.glsl +// pathfinder/shaders/gles2/conservative-interior.vs.glsl // -// Copyright (c) 2017 The Pathfinder Project Developers. +// Copyright (c) 2018 The Pathfinder Project Developers. // // Licensed under the Apache License, Version 2.0 or the MIT license @@ -17,8 +17,9 @@ precision highp float; -/// A 3D transform to be applied to all points. -uniform mat4 uTransform; +/// An affine transform to be applied to all points. +uniform vec4 uTransformST; +uniform vec2 uTransformExt; /// Vertical snapping positions. uniform vec4 uHints; /// The framebuffer size in pixels. @@ -52,21 +53,21 @@ void main() { int pathID = int(aPathID); int vertexID = int(aVertexID); - vec2 pathTransformExt; - vec4 pathTransformST = fetchPathAffineTransform(pathTransformExt, - uPathTransformST, - uPathTransformSTDimensions, - uPathTransformExt, - uPathTransformExtDimensions, - pathID); + vec4 transformST = fetchFloat4Data(uPathTransformST, pathID, uPathTransformSTDimensions); - vec2 position = hintPosition(aPosition, uHints); - position = transformVertexPositionAffine(position, pathTransformST, pathTransformExt); - position = transformVertexPosition(position, uTransform); - position = offsetPositionVertically(position, uFramebufferSize, imod(vertexID, 6) < 3); + mat2 globalTransformLinear = mat2(uTransformST.x, uTransformExt, uTransformST.y); + mat2 localTransformLinear = mat2(transformST.x, 0.0, 0.0, transformST.y); + mat2 transformLinear = globalTransformLinear * localTransformLinear; + vec2 translation = uTransformST.zw + globalTransformLinear * transformST.zw; + + float onePixel = 2.0 / float(uFramebufferSize.y); + float dilation = length(invMat2(transformLinear) * vec2(0.0, onePixel)); + + vec2 position = aPosition + vec2(0.0, imod(vertexID, 6) < 3 ? dilation : -dilation); + position = transformLinear * position + translation; float depth = convertPathIndexToViewportDepthValue(pathID); - gl_Position = vec4(position, depth, 1.0); + gl_Position = vec4(position, depth, 1.0); vColor = fetchFloat4Data(uPathColors, pathID, uPathColorsDimensions); } diff --git a/shaders/gles2/mcaa.fs.glsl b/shaders/gles2/mcaa.fs.glsl index 3ad7fae3..83b8baab 100644 --- a/shaders/gles2/mcaa.fs.glsl +++ b/shaders/gles2/mcaa.fs.glsl @@ -1,6 +1,6 @@ // pathfinder/shaders/gles2/mcaa.fs.glsl // -// Copyright (c) 2017 The Pathfinder Project Developers. +// Copyright (c) 2018 The Pathfinder Project Developers. // // Licensed under the Apache License, Version 2.0 or the MIT license @@ -12,68 +12,43 @@ //! (MCAA). This one shader handles both lines and curves. //! //! This shader expects to render to a standard RGB color buffer. -//! -//! Use this shader only when *both* of the following are true: -//! -//! 1. You are rendering multiple multicolor paths. (Otherwise, consider the -//! other MCAA shaders, which render with higher quality.) -//! -//! 2. Your transform is only a scale and/or translation, not a perspective, -//! rotation, or skew. (Otherwise, consider repartitioning the path to -//! generate a new mesh, or, alternatively, use the direct Loop-Blinn -//! shaders.) precision highp float; -/// True if multiple colors are being rendered; false otherwise. -/// -/// If this is true, then points will be snapped to the nearest pixel. -uniform bool uMulticolor; - -varying vec4 vUpperEndpoints; -varying vec4 vLowerEndpoints; -varying vec4 vControlPoints; varying vec4 vColor; +varying vec4 vUV; +varying vec4 vSignMode; -float computeCoverageForSide(vec2 p0, vec2 cp, vec2 p1, float winding) { - // Compute pixel extents. - vec2 pixelCenter = gl_FragCoord.xy; - vec2 pixelColumnBounds = pixelCenter.xx + vec2(-0.5, 0.5); +// Cubic approximation to the square area coverage, accurate to about 4%. +float estimateArea(float dist) { + if (dist >= 0.707107) + return 1.0; + // Catch NaNs here. + if (!(dist > -0.707107)) + return 0.0; + return 0.5 + 1.14191 * dist - 0.83570 * dist * dist * dist; +} - vec2 clippedP0, clippedDP; - if (cp == vec2(0.0)) { - vec4 p0DPX = clipLineToPixelColumn(p0, p1 - p0, pixelCenter.x); - clippedP0 = p0DPX.xy; - clippedDP = p0DPX.zw; - } else { - // Clip the curve to the left and right edges to create a line. - vec2 t = solveCurveT(p0.x, cp.x, p1.x, pixelColumnBounds); +float computeAlpha(vec2 uv, float curveSign, float mode) { + vec2 dUVDX = dFdx(uv), dUVDY = dFdy(uv); - // Handle endpoints properly. These tests are negated to handle NaNs. - if (!(p0.x < pixelColumnBounds.x)) - t.x = 0.0; - if (!(p1.x > pixelColumnBounds.y)) - t.y = 1.0; - - clippedP0 = mix(mix(p0, cp, t.x), mix(cp, p1, t.x), t.x); - clippedDP = mix(mix(p0, cp, t.y), mix(cp, p1, t.y), t.y) - clippedP0; + // u^2 - v for curves inside uv square; u - v otherwise. + float g = uv.x; + vec2 dG = vec2(dUVDX.x, dUVDY.x); + if (mode > 0.0 && uv.x > 0.0 && uv.x < 1.0 && uv.y > 0.0 && uv.y < 1.0) { + g *= uv.x; + dG *= 2.0 * uv.x; } + g -= uv.y; + dG -= vec2(dUVDX.y, dUVDY.y); - return computeCoverage(clippedP0, clippedDP, pixelCenter.y, winding); + float signedDistance = g / length(dG); + return estimateArea(signedDistance * curveSign); } void main() { - float alpha = computeCoverageForSide(vLowerEndpoints.xy, - vControlPoints.zw, - vLowerEndpoints.zw, - -1.0); - - alpha += computeCoverageForSide(vUpperEndpoints.xy, - vControlPoints.xy, - vUpperEndpoints.zw, - 1.0); - - // Compute area. - vec4 color = uMulticolor ? vColor : vec4(1.0); - gl_FragColor = alpha * color; + float alpha = 1.0; + alpha -= computeAlpha(vUV.xy, vSignMode.x, vSignMode.z); + alpha -= computeAlpha(vUV.zw, vSignMode.y, vSignMode.w); + gl_FragColor = alpha * vColor; } diff --git a/shaders/gles2/mcaa.vs.glsl b/shaders/gles2/mcaa.vs.glsl index 79cc0c0e..34035d88 100644 --- a/shaders/gles2/mcaa.vs.glsl +++ b/shaders/gles2/mcaa.vs.glsl @@ -34,10 +34,9 @@ precision highp float; -/// A dilation (scale and translation) to be applied to the object. +/// A scale and transform to be applied to the object. uniform vec4 uTransformST; -/// Vertical snapping positions. -uniform vec4 uHints; +uniform vec2 uTransformExt; /// The framebuffer size in pixels. uniform ivec2 uFramebufferSize; /// The size of the path transform buffer texture in texels. @@ -54,120 +53,47 @@ uniform sampler2D uPathColors; uniform bool uMulticolor; attribute vec2 aTessCoord; -attribute vec2 aUpperLeftEndpointPosition; -attribute vec2 aUpperControlPointPosition; -attribute vec2 aUpperRightEndpointPosition; -attribute vec2 aLowerRightEndpointPosition; -attribute vec2 aLowerControlPointPosition; -attribute vec2 aLowerLeftEndpointPosition; +attribute vec4 aRect; +attribute vec4 aUV; +attribute vec4 aDUVDX; +attribute vec4 aDUVDY; +attribute vec4 aSignMode; attribute float aPathID; -varying vec4 vUpperEndpoints; -varying vec4 vLowerEndpoints; -varying vec4 vControlPoints; varying vec4 vColor; +varying vec4 vUV; +varying vec4 vSignMode; void main() { - 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 color; + if (uMulticolor) + color = fetchFloat4Data(uPathColors, pathID, uPathColorsDimensions); + else + color = vec4(1.0); + vec4 transformST = fetchFloat4Data(uPathTransformST, pathID, uPathTransformSTDimensions); - if (abs(transformST.x) > 0.001 && abs(transformST.y) > 0.001) { - vec4 color = fetchFloat4Data(uPathColors, pathID, uPathColorsDimensions); - vec2 topVector = trPosition - tlPosition, bottomVector = brPosition - blPosition; + mat2 globalTransformLinear = mat2(uTransformST.x, uTransformExt, uTransformST.y); + mat2 localTransformLinear = mat2(transformST.x, 0.0, 0.0, transformST.y); + mat2 rectTransformLinear = mat2(aRect.z - aRect.x, 0.0, 0.0, aRect.w - aRect.y); + mat2 transformLinear = globalTransformLinear * localTransformLinear * rectTransformLinear; - float topSlope = topVector.y / topVector.x; - float bottomSlope = bottomVector.y / bottomVector.x; - if (abs(topSlope) > MAX_SLOPE) - topSlope = sign(topSlope) * MAX_SLOPE; - if (abs(bottomSlope) > MAX_SLOPE) - bottomSlope = sign(bottomSlope) * MAX_SLOPE; + vec2 translation = transformST.zw + localTransformLinear * aRect.xy; + translation = uTransformST.zw + globalTransformLinear * translation; - // Transform the points, and compute the position of this vertex. - tlPosition = computeMCAASnappedPosition(tlPosition, - uHints, - transformST, - uTransformST, - uFramebufferSize, - topSlope, - uMulticolor); - trPosition = computeMCAASnappedPosition(trPosition, - uHints, - transformST, - uTransformST, - uFramebufferSize, - topSlope, - uMulticolor); - tcPosition = computeMCAAPosition(tcPosition, - uHints, - transformST, - uTransformST, - uFramebufferSize); - blPosition = computeMCAASnappedPosition(blPosition, - uHints, - transformST, - uTransformST, - uFramebufferSize, - bottomSlope, - uMulticolor); - brPosition = computeMCAASnappedPosition(brPosition, - uHints, - transformST, - uTransformST, - uFramebufferSize, - bottomSlope, - uMulticolor); - bcPosition = computeMCAAPosition(bcPosition, - uHints, - transformST, - uTransformST, - uFramebufferSize); + float onePixel = 2.0 / float(uFramebufferSize.y); + float dilation = length(invMat2(transformLinear) * vec2(0.0, onePixel)); + tessCoord.y += tessCoord.y < 0.5 ? -dilation : dilation; - float depth = convertPathIndexToViewportDepthValue(pathID); + vec2 position = transformLinear * tessCoord + translation; + vec4 uv = aUV + tessCoord.x * aDUVDX + tessCoord.y * aDUVDY; + float depth = convertPathIndexToViewportDepthValue(pathID); - // Use the same side--in this case, the top--or else floating point error during - // partitioning can occasionally cause inconsistent rounding, resulting in cracks. - vec2 position; - if (tessCoord.y < 0.5) { - if (tessCoord.x < 0.25) - position = tlPosition; - else if (tessCoord.x < 0.75) - position = tcPosition; - else - position = trPosition; - position.y = floor(position.y - 0.5); - } else { - if (tessCoord.x < 0.25) - position = blPosition; - else if (tessCoord.x < 0.75) - position = bcPosition; - else - position = brPosition; - position.y = ceil(position.y + 0.5); - } - - if (!uMulticolor) { - if (tessCoord.x < 0.25) - position.x = floor(position.x); - else if (tessCoord.x >= 0.75) - position.x = ceil(position.x); - } - - position = convertScreenToClipSpace(position, uFramebufferSize); - - gl_Position = vec4(position, depth, 1.0); - vUpperEndpoints = vec4(tlPosition, trPosition); - vLowerEndpoints = vec4(blPosition, brPosition); - vControlPoints = vec4(tcPosition, bcPosition); - vColor = color; - } else { - gl_Position = vec4(0.0); - } + gl_Position = vec4(position, depth, 1.0); + vColor = color; + vUV = uv; + vSignMode = aSignMode; }