diff --git a/demo/client/src/3d-demo.ts b/demo/client/src/3d-demo.ts index 91973ad1..4a5db9fd 100644 --- a/demo/client/src/3d-demo.ts +++ b/demo/client/src/3d-demo.ts @@ -364,6 +364,10 @@ class ThreeDRenderer extends Renderer { camera: PerspectiveCamera; + get isMulticolor(): boolean { + return false; + } + get usesSTTransform(): boolean { return this.camera.usesSTTransform; } @@ -702,7 +706,7 @@ class ThreeDRenderer extends Renderer { gl.disable(gl.SCISSOR_TEST); gl.enable(gl.BLEND); gl.blendEquation(gl.FUNC_ADD); - gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE); + gl.blendFuncSeparate(gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE); // Draw textures for distant glyphs. const cameraTransform = this.calculateCameraTransform(glmatrix.vec3.create(), TEXT_SCALE); diff --git a/demo/client/src/benchmark.ts b/demo/client/src/benchmark.ts index 04516574..12a8f234 100644 --- a/demo/client/src/benchmark.ts +++ b/demo/client/src/benchmark.ts @@ -423,6 +423,10 @@ class BenchmarkTextRenderer extends Renderer { camera: OrthographicCamera; + get isMulticolor(): boolean { + return false; + } + get usesSTTransform(): boolean { return this.camera.usesSTTransform; } diff --git a/demo/client/src/reference-test.ts b/demo/client/src/reference-test.ts index 34fa41ac..10e3f188 100644 --- a/demo/client/src/reference-test.ts +++ b/demo/client/src/reference-test.ts @@ -668,6 +668,10 @@ class ReferenceTestTextRenderer extends Renderer { return null; } + get isMulticolor(): boolean { + return false; + } + get bgColor(): glmatrix.vec4 { return glmatrix.vec4.clone([1.0, 1.0, 1.0, 1.0]); } diff --git a/demo/client/src/renderer.ts b/demo/client/src/renderer.ts index 610a90c7..b1b3ea48 100644 --- a/demo/client/src/renderer.ts +++ b/demo/client/src/renderer.ts @@ -67,6 +67,8 @@ export abstract class Renderer { return this.meshes != null && this.meshData != null; } + abstract get isMulticolor(): boolean; + abstract get destFramebuffer(): WebGLFramebuffer | null; abstract get destAllocatedSize(): glmatrix.vec2; abstract get destUsedSize(): glmatrix.vec2; @@ -131,14 +133,14 @@ export abstract class Renderer { if (this.meshes == null) return; + this.clearDestFramebuffer(); + // Start timing rendering. if (this.timerQueryPollInterval == null) { renderContext.timerQueryExt.beginQueryEXT(renderContext.timerQueryExt.TIME_ELAPSED_EXT, renderContext.atlasRenderingTimerQuery); } - this.clearDestFramebuffer(); - const antialiasingStrategy = unwrapNull(this.antialiasingStrategy); antialiasingStrategy.prepareForRendering(this); @@ -155,8 +157,16 @@ export abstract class Renderer { // Antialias. antialiasingStrategy.antialiasObject(this, objectIndex); - // Perform post-antialiasing tasks. - antialiasingStrategy.finishAntialiasingObject(this, objectIndex); + // End the timer, and start a new one. + // FIXME(pcwalton): This is kinda bogus for multipass. + if (this.timerQueryPollInterval == null && objectIndex === objectCount - 1 && + pass === passCount - 1) { + renderContext.timerQueryExt + .endQueryEXT(renderContext.timerQueryExt.TIME_ELAPSED_EXT); + renderContext.timerQueryExt + .beginQueryEXT(renderContext.timerQueryExt.TIME_ELAPSED_EXT, + renderContext.compositingTimerQuery); + } // Perform direct rendering (Loop-Blinn). if (antialiasingStrategy.directRenderingMode !== 'none') { @@ -169,20 +179,15 @@ export abstract class Renderer { this.directlyRenderObject(pass, objectIndex); } + // Perform post-antialiasing tasks. + antialiasingStrategy.finishAntialiasingObject(this, objectIndex); + antialiasingStrategy.resolveAAForObject(this, objectIndex); } antialiasingStrategy.resolve(pass, this); } - // End the timer, and start a new one. - // FIXME(pcwalton): Removed this for multipass; get it split out again somehow. - if (this.timerQueryPollInterval == null) { - renderContext.timerQueryExt.endQueryEXT(renderContext.timerQueryExt.TIME_ELAPSED_EXT); - renderContext.timerQueryExt.beginQueryEXT(renderContext.timerQueryExt.TIME_ELAPSED_EXT, - renderContext.compositingTimerQuery); - } - // Draw the glyphs with the resolved atlas to the default framebuffer. this.compositeIfNecessary(); @@ -306,22 +311,24 @@ export abstract class Renderer { uploadPathTransforms(objectCount: number): void { const renderContext = this.renderContext; + const gl = renderContext.gl; + for (let objectIndex = 0; objectIndex < objectCount; objectIndex++) { const pathTransforms = this.pathTransformsForObject(objectIndex); let pathTransformBufferTextures; if (objectIndex >= this.pathTransformBufferTextures.length) { pathTransformBufferTextures = { - ext: new PathfinderBufferTexture(renderContext.gl, 'uPathTransformExt'), - st: new PathfinderBufferTexture(renderContext.gl, 'uPathTransformST'), + ext: new PathfinderBufferTexture(gl, 'uPathTransformExt'), + st: new PathfinderBufferTexture(gl, 'uPathTransformST'), }; this.pathTransformBufferTextures[objectIndex] = pathTransformBufferTextures; } else { pathTransformBufferTextures = this.pathTransformBufferTextures[objectIndex]; } - pathTransformBufferTextures.st.upload(renderContext.gl, pathTransforms.st); - pathTransformBufferTextures.ext.upload(renderContext.gl, pathTransforms.ext); + pathTransformBufferTextures.st.upload(gl, pathTransforms.st); + pathTransformBufferTextures.ext.upload(gl, pathTransforms.ext); } } @@ -501,7 +508,7 @@ export abstract class Renderer { gl.depthMask(false); gl.enable(gl.BLEND); gl.blendEquation(gl.FUNC_ADD); - gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE); + gl.blendFuncSeparate(gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE); // Set up the direct curve VAO. // diff --git a/demo/client/src/shader-loader.ts b/demo/client/src/shader-loader.ts index fc9db455..f3ef0798 100644 --- a/demo/client/src/shader-loader.ts +++ b/demo/client/src/shader-loader.ts @@ -23,10 +23,7 @@ export interface ShaderMap { ecaaLine: T; ecaaCurve: T; ecaaTransformedCurve: T; - mcaaCover: T; - mcaaLine: T; - mcaaCurve: T; - mcaaMulti: T; + mcaa: T; ssaaSubpixelResolve: T; xcaaMonoResolve: T; xcaaMonoSubpixelResolve: T; @@ -47,10 +44,7 @@ export const SHADER_NAMES: Array> = [ 'direct3DCurve', 'direct3DInterior', 'ssaaSubpixelResolve', - 'mcaaCover', - 'mcaaLine', - 'mcaaCurve', - 'mcaaMulti', + 'mcaa', 'ecaaLine', 'ecaaCurve', 'ecaaTransformedCurve', @@ -94,32 +88,20 @@ const SHADER_URLS: ShaderMap = { vertex: "/glsl/gles2/direct-interior.vs.glsl", }, ecaaCurve: { - fragment: "/glsl/gles2/xcaa-curve.fs.glsl", + fragment: "/glsl/gles2/ecaa-curve.fs.glsl", vertex: "/glsl/gles2/ecaa-curve.vs.glsl", }, ecaaLine: { - fragment: "/glsl/gles2/xcaa-line.fs.glsl", + fragment: "/glsl/gles2/ecaa-line.fs.glsl", vertex: "/glsl/gles2/ecaa-line.vs.glsl", }, ecaaTransformedCurve: { - fragment: "/glsl/gles2/xcaa-curve.fs.glsl", + fragment: "/glsl/gles2/ecaa-curve.fs.glsl", vertex: "/glsl/gles2/ecaa-transformed-curve.vs.glsl", }, - mcaaCover: { - fragment: "/glsl/gles2/mcaa-cover.fs.glsl", - vertex: "/glsl/gles2/mcaa-cover.vs.glsl", - }, - mcaaCurve: { - fragment: "/glsl/gles2/xcaa-curve.fs.glsl", - vertex: "/glsl/gles2/mcaa-curve.vs.glsl", - }, - mcaaLine: { - fragment: "/glsl/gles2/xcaa-line.fs.glsl", - vertex: "/glsl/gles2/mcaa-line.vs.glsl", - }, - mcaaMulti: { - fragment: "/glsl/gles2/mcaa-multi.fs.glsl", - vertex: "/glsl/gles2/mcaa-multi.vs.glsl", + mcaa: { + fragment: "/glsl/gles2/mcaa.fs.glsl", + vertex: "/glsl/gles2/mcaa.vs.glsl", }, ssaaSubpixelResolve: { fragment: "/glsl/gles2/ssaa-subpixel-resolve.fs.glsl", diff --git a/demo/client/src/svg-renderer.ts b/demo/client/src/svg-renderer.ts index bb3e3053..7f3ff42c 100644 --- a/demo/client/src/svg-renderer.ts +++ b/demo/client/src/svg-renderer.ts @@ -21,18 +21,18 @@ import SSAAStrategy from './ssaa-strategy'; import {SVGLoader} from './svg-loader'; import {Range} from './utils'; import {RenderContext} from './view'; -import {MCAAMulticolorStrategy, XCAAStrategy} from './xcaa-strategy'; +import {MCAAStrategy, XCAAStrategy} from './xcaa-strategy'; interface AntialiasingStrategyTable { none: typeof NoAAStrategy; ssaa: typeof SSAAStrategy; - xcaa: typeof XCAAStrategy; + xcaa: typeof MCAAStrategy; } const ANTIALIASING_STRATEGIES: AntialiasingStrategyTable = { none: NoAAStrategy, ssaa: SSAAStrategy, - xcaa: MCAAMulticolorStrategy, + xcaa: MCAAStrategy, }; export abstract class SVGRenderer extends Renderer { @@ -40,6 +40,11 @@ export abstract class SVGRenderer extends Renderer { camera: OrthographicCamera; + get isMulticolor(): boolean { + // FIXME(pcwalton): Only if the SVG is actually multicolor! + return true; + } + get bgColor(): glmatrix.vec4 { return glmatrix.vec4.clone([1.0, 1.0, 1.0, 1.0]); } diff --git a/demo/client/src/text-renderer.ts b/demo/client/src/text-renderer.ts index fb432da9..849d1461 100644 --- a/demo/client/src/text-renderer.ts +++ b/demo/client/src/text-renderer.ts @@ -64,6 +64,10 @@ export abstract class TextRenderer extends Renderer { atlasFramebuffer: WebGLFramebuffer; atlasDepthTexture: WebGLTexture; + get isMulticolor(): boolean { + return false; + } + get usesSTTransform(): boolean { return this.camera.usesSTTransform; } @@ -146,12 +150,15 @@ export abstract class TextRenderer extends Renderer { } setHintsUniform(uniforms: UniformMap): void { + const renderContext = this.renderContext; + const gl = renderContext.gl; + const hint = this.createHint(); - this.renderContext.gl.uniform4f(uniforms.uHints, - hint.xHeight, - hint.hintedXHeight, - hint.stemHeight, - hint.hintedStemHeight); + gl.uniform4f(uniforms.uHints, + hint.xHeight, + hint.hintedXHeight, + hint.stemHeight, + hint.hintedStemHeight); } pathBoundingRects(objectIndex: number): Float32Array { diff --git a/demo/client/src/xcaa-strategy.ts b/demo/client/src/xcaa-strategy.ts index 46581684..2ccaac44 100644 --- a/demo/client/src/xcaa-strategy.ts +++ b/demo/client/src/xcaa-strategy.ts @@ -41,9 +41,8 @@ export abstract class XCAAStrategy extends AntialiasingStrategy { } protected abstract get usesDilationTransforms(): boolean; - protected abstract get usesAAFramebuffer(): boolean; - protected pathBoundsBufferTexture: PathfinderBufferTexture; + protected pathBoundsBufferTextures: PathfinderBufferTexture[]; protected supersampledFramebufferSize: glmatrix.vec2; protected destFramebufferSize: glmatrix.vec2; @@ -72,6 +71,7 @@ export abstract class XCAAStrategy extends AntialiasingStrategy { attachMeshes(renderer: Renderer): void { const renderContext = renderer.renderContext; this.createResolveVAO(renderer); + this.pathBoundsBufferTextures = []; } setFramebufferSize(renderer: Renderer): void { @@ -97,17 +97,18 @@ export abstract class XCAAStrategy extends AntialiasingStrategy { this.initResolveFramebufferForObject(renderer, objectIndex); - if (this.usesAAFramebuffer) { - const usedSize = this.supersampledUsedSize(renderer); - gl.scissor(0, 0, usedSize[0], usedSize[1]); - gl.enable(gl.SCISSOR_TEST); + if (!this.usesAAFramebuffer(renderer)) + return; - // Clear out the color and depth textures. - gl.clearColor(1.0, 1.0, 1.0, 1.0); - gl.clearDepth(0.0); - gl.depthMask(true); - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - } + const usedSize = this.supersampledUsedSize(renderer); + gl.scissor(0, 0, usedSize[0], usedSize[1]); + gl.enable(gl.SCISSOR_TEST); + + // Clear out the color and depth textures. + gl.clearColor(1.0, 1.0, 1.0, 1.0); + gl.clearDepth(0.0); + gl.depthMask(true); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); } prepareToRenderObject(renderer: Renderer, objectIndex: number): void {} @@ -118,7 +119,7 @@ export abstract class XCAAStrategy extends AntialiasingStrategy { antialiasObject(renderer: Renderer, objectIndex: number): void { // Perform early preparations. - this.createPathBoundsBufferTextureForObject(renderer, objectIndex); + this.createPathBoundsBufferTextureForObjectIfNecessary(renderer, objectIndex); // Set up antialiasing. this.prepareAA(renderer); @@ -131,7 +132,7 @@ export abstract class XCAAStrategy extends AntialiasingStrategy { const renderContext = renderer.renderContext; const gl = renderContext.gl; - const resolveProgram = this.getResolveProgram(renderContext); + const resolveProgram = this.getResolveProgram(renderer); if (resolveProgram == null) return; @@ -172,6 +173,8 @@ export abstract class XCAAStrategy extends AntialiasingStrategy { return glmatrix.mat4.create(); } + protected abstract usesAAFramebuffer(renderer: Renderer): boolean; + protected supersampledUsedSize(renderer: Renderer): glmatrix.vec2 { const usedSize = glmatrix.vec2.create(); glmatrix.vec2.mul(usedSize, renderer.destUsedSize, this.supersampleScale); @@ -223,7 +226,7 @@ export abstract class XCAAStrategy extends AntialiasingStrategy { this.supersampledFramebufferSize[1]); renderer.pathTransformBufferTextures[0].ext.bind(gl, uniforms, 0); renderer.pathTransformBufferTextures[0].st.bind(gl, uniforms, 1); - this.pathBoundsBufferTexture.bind(gl, uniforms, 2); + this.pathBoundsBufferTextures[objectIndex].bind(gl, uniforms, 2); renderer.setHintsUniform(uniforms); } @@ -239,8 +242,7 @@ export abstract class XCAAStrategy extends AntialiasingStrategy { void {} protected abstract clearForAA(renderer: Renderer): void; - protected abstract getResolveProgram(renderContext: RenderContext): - PathfinderShaderProgram | null; + protected abstract getResolveProgram(renderer: Renderer): PathfinderShaderProgram | null; protected abstract setAADepthState(renderer: Renderer): void; protected abstract clearForResolve(renderer: Renderer): void; @@ -254,7 +256,7 @@ export abstract class XCAAStrategy extends AntialiasingStrategy { } private initAAAlphaFramebuffer(renderer: Renderer): void { - if (!this.usesAAFramebuffer) { + if (!this.usesAAFramebuffer(renderer)) { this.aaAlphaTexture = null; this.aaDepthTexture = null; this.aaFramebuffer = null; @@ -282,20 +284,27 @@ export abstract class XCAAStrategy extends AntialiasingStrategy { this.aaFramebuffer = createFramebuffer(gl, this.aaAlphaTexture, this.aaDepthTexture); } - private createPathBoundsBufferTextureForObject(renderer: Renderer, objectIndex: number): void { + private createPathBoundsBufferTextureForObjectIfNecessary(renderer: Renderer, + objectIndex: number): + void { const renderContext = renderer.renderContext; const gl = renderContext.gl; const pathBounds = renderer.pathBoundingRects(objectIndex); - this.pathBoundsBufferTexture = new PathfinderBufferTexture(gl, 'uPathBounds'); - this.pathBoundsBufferTexture.upload(gl, pathBounds); + + if (this.pathBoundsBufferTextures[objectIndex] == null) { + this.pathBoundsBufferTextures[objectIndex] = + new PathfinderBufferTexture(gl, 'uPathBounds'); + } + + this.pathBoundsBufferTextures[objectIndex].upload(gl, pathBounds); } private createResolveVAO(renderer: Renderer): void { const renderContext = renderer.renderContext; const gl = renderContext.gl; - const resolveProgram = this.getResolveProgram(renderContext); + const resolveProgram = this.getResolveProgram(renderer); if (resolveProgram == null) return; @@ -317,11 +326,8 @@ export abstract class XCAAStrategy extends AntialiasingStrategy { } } -export class MCAAMonochromeStrategy extends XCAAStrategy { - protected coverVAO: WebGLVertexArrayObject | null; - - protected lineVAOs: FastEdgeVAOs; - protected curveVAOs: FastEdgeVAOs; +export class MCAAStrategy extends XCAAStrategy { + protected vao: WebGLVertexArrayObject | null; protected get usesDilationTransforms(): boolean { return true; @@ -330,27 +336,25 @@ export class MCAAMonochromeStrategy extends XCAAStrategy { attachMeshes(renderer: Renderer): void { super.attachMeshes(renderer); - this.createCoverVAOIfNecessary(renderer); - this.createLineVAOs(renderer); - this.createCurveVAOs(renderer); + const renderContext = renderer.renderContext; + this.vao = renderContext.vertexArrayObjectExt.createVertexArrayOES(); } antialiasObject(renderer: Renderer, objectIndex: number): void { super.antialiasObject(renderer, objectIndex); - // Conservatively cover. - this.coverObjectIfNecessary(renderer, objectIndex); - - // Antialias. - this.antialiasLinesOfObject(renderer, objectIndex); - this.antialiasCurvesOfObject(renderer, objectIndex); + const shaderProgram = this.edgeProgram(renderer); + this.antialiasEdgesOfObjectWithProgram(renderer, objectIndex, shaderProgram); } - protected get usesAAFramebuffer(): boolean { - return true; + protected usesAAFramebuffer(renderer: Renderer): boolean { + return !renderer.isMulticolor; } - protected getResolveProgram(renderContext: RenderContext): PathfinderShaderProgram { + protected getResolveProgram(renderer: Renderer): PathfinderShaderProgram | null { + const renderContext = renderer.renderContext; + if (renderer.isMulticolor) + return null; if (this.subpixelAA !== 'none') return renderContext.shaderPrograms.xcaaMonoSubpixelResolve; return renderContext.shaderPrograms.xcaaMonoResolve; @@ -360,60 +364,126 @@ export class MCAAMonochromeStrategy extends XCAAStrategy { const renderContext = renderer.renderContext; const gl = renderContext.gl; - gl.clearColor(0.0, 0.0, 0.0, 0.0); + 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); } protected setAADepthState(renderer: Renderer): void { const renderContext = renderer.renderContext; - renderContext.gl.disable(renderContext.gl.DEPTH_TEST); + const gl = renderContext.gl; + + gl.disable(gl.DEPTH_TEST); } protected clearForResolve(renderer: Renderer): void { const renderContext = renderer.renderContext; const gl = renderContext.gl; - gl.clearColor(1.0, 1.0, 1.0, 1.0); - gl.clear(gl.COLOR_BUFFER_BIT); - } - - protected createCoverVAOIfNecessary(renderer: Renderer): void { - this.coverVAO = renderer.renderContext.vertexArrayObjectExt.createVertexArrayOES(); + if (!renderer.isMulticolor) { + gl.clearColor(1.0, 1.0, 1.0, 1.0); + gl.clear(gl.COLOR_BUFFER_BIT); + } } protected setBlendModeForAA(renderer: Renderer): void { const renderContext = renderer.renderContext; const gl = renderContext.gl; + if (renderer.isMulticolor) + gl.blendFuncSeparate(gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE); + else + gl.blendFunc(gl.ONE, gl.ONE); + gl.blendEquation(gl.FUNC_ADD); - gl.blendFunc(gl.ONE, gl.ONE); gl.enable(gl.BLEND); } protected prepareAA(renderer: Renderer): void { super.prepareAA(renderer); - this.setCoverDepthState(renderer); + this.setBlendModeForAA(renderer); + } + + protected initVAOForObject(renderer: Renderer, objectIndex: number): void { + if (renderer.meshes == null || renderer.meshData == null) + return; const renderContext = renderer.renderContext; const gl = renderContext.gl; - gl.blendEquation(gl.FUNC_ADD); - gl.blendFunc(gl.ONE, gl.ONE); - gl.enable(gl.BLEND); + const pathRange = renderer.pathRangeForObject(objectIndex); + const meshIndex = renderer.meshIndexForObject(objectIndex); - this.clearForAA(renderer); + const shaderProgram = this.edgeProgram(renderer); + const attributes = shaderProgram.attributes; + + // FIXME(pcwalton): Refactor. + const vao = this.vao; + renderContext.vertexArrayObjectExt.bindVertexArrayOES(vao); + + const bQuadRanges = renderer.meshData[meshIndex].bQuadPathRanges; + const offset = calculateStartFromIndexRanges(pathRange, bQuadRanges); + + gl.useProgram(shaderProgram.program); + gl.bindBuffer(gl.ARRAY_BUFFER, renderContext.quadPositionsBuffer); + gl.vertexAttribPointer(attributes.aQuadPosition, 2, gl.FLOAT, false, 0, 0); + gl.bindBuffer(gl.ARRAY_BUFFER, renderer.meshes[meshIndex].bQuadVertexPositions); + gl.vertexAttribPointer(attributes.aUpperEndpointPositions, + 4, + gl.FLOAT, + false, + FLOAT32_SIZE * 12, + FLOAT32_SIZE * 12 * offset); + gl.vertexAttribPointer(attributes.aLowerEndpointPositions, + 4, + gl.FLOAT, + false, + FLOAT32_SIZE * 12, + FLOAT32_SIZE * 12 * offset + FLOAT32_SIZE * 4); + gl.vertexAttribPointer(attributes.aControlPointPositions, + 4, + gl.FLOAT, + false, + FLOAT32_SIZE * 12, + FLOAT32_SIZE * 12 * offset + FLOAT32_SIZE * 8); + renderContext.instancedArraysExt + .vertexAttribDivisorANGLE(attributes.aUpperEndpointPositions, 1); + renderContext.instancedArraysExt + .vertexAttribDivisorANGLE(attributes.aLowerEndpointPositions, 1); + renderContext.instancedArraysExt + .vertexAttribDivisorANGLE(attributes.aControlPointPositions, 1); + + gl.bindBuffer(gl.ARRAY_BUFFER, renderer.meshes[meshIndex].edgeBoundingBoxPathIDs); + gl.vertexAttribPointer(attributes.aPathID, + 1, + gl.UNSIGNED_SHORT, + false, + 0, + 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.aPathID); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, renderContext.quadElementsBuffer); + + renderContext.vertexArrayObjectExt.bindVertexArrayOES(null); } - protected setCoverDepthState(renderer: Renderer): void { - const renderContext = renderer.renderContext; - renderContext.gl.disable(renderContext.gl.DEPTH_TEST); + protected edgeProgram(renderer: Renderer): PathfinderShaderProgram { + return renderer.renderContext.shaderPrograms.mcaa; } - protected antialiasLinesOfObjectWithProgram(renderer: Renderer, + protected antialiasEdgesOfObjectWithProgram(renderer: Renderer, objectIndex: number, - lineProgram: PathfinderShaderProgram): + shaderProgram: PathfinderShaderProgram): void { if (renderer.meshData == null) return; @@ -424,364 +494,60 @@ export class MCAAMonochromeStrategy extends XCAAStrategy { const pathRange = renderer.pathRangeForObject(objectIndex); const meshIndex = renderer.meshIndexForObject(objectIndex); - this.initLineVAOsForObject(renderer, objectIndex); + this.initVAOForObject(renderer, objectIndex); - gl.useProgram(lineProgram.program); - const uniforms = lineProgram.uniforms; + gl.useProgram(shaderProgram.program); + const uniforms = shaderProgram.uniforms; this.setAAUniforms(renderer, uniforms, objectIndex); - for (const direction of DIRECTIONS) { - const vao = this.lineVAOs[direction]; - renderContext.vertexArrayObjectExt.bindVertexArrayOES(vao); + // FIXME(pcwalton): Refactor. + const vao = this.vao; + renderContext.vertexArrayObjectExt.bindVertexArrayOES(vao); - this.setBlendModeForAA(renderer); - gl.uniform1i(uniforms.uWinding, direction === 'upper' ? 1 : 0); + this.setBlendModeForAA(renderer); - const indexRanges = { - lower: renderer.meshData[meshIndex].edgeLowerLineIndexRanges, - upper: renderer.meshData[meshIndex].edgeUpperLineIndexRanges, - }[direction]; - const count = calculateCountFromIndexRanges(pathRange, indexRanges); - - renderContext.instancedArraysExt - .drawElementsInstancedANGLE(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0, count); - } - - renderContext.vertexArrayObjectExt.bindVertexArrayOES(null); - } - - protected coverObjectIfNecessary(renderer: Renderer, objectIndex: number): void { - if (renderer.meshes == null || renderer.meshData == null) - return; - - const renderContext = renderer.renderContext; - const gl = renderContext.gl; - - const pathRange = renderer.pathRangeForObject(objectIndex); - const meshIndex = renderer.meshIndexForObject(objectIndex); - - this.initCoverVAOForObject(renderer, objectIndex); - - // Conservatively cover. - const coverProgram = renderContext.shaderPrograms.mcaaCover; - gl.useProgram(coverProgram.program); - renderContext.vertexArrayObjectExt.bindVertexArrayOES(this.coverVAO); - this.setAAUniforms(renderer, coverProgram.uniforms, objectIndex); - - const bQuadRange = renderer.meshData[meshIndex].bQuadPathRanges; - const count = calculateCountFromIndexRanges(pathRange, bQuadRange); + const bQuadRanges = renderer.meshData[meshIndex].bQuadPathRanges; + const count = calculateCountFromIndexRanges(pathRange, bQuadRanges); renderContext.instancedArraysExt .drawElementsInstancedANGLE(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0, count); - renderContext.vertexArrayObjectExt.bindVertexArrayOES(null); - } - - protected initCurveVAOsForObject(renderer: Renderer, objectIndex: number): void { - if (renderer.meshes == null || renderer.meshData == null) - return; - - const renderContext = renderer.renderContext; - const gl = renderContext.gl; - - const pathRange = renderer.pathRangeForObject(objectIndex); - const meshIndex = renderer.meshIndexForObject(objectIndex); - - const curveProgram = this.curveProgram(renderer); - const attributes = curveProgram.attributes; - - for (const direction of DIRECTIONS) { - const vao = this.curveVAOs[direction]; - renderContext.vertexArrayObjectExt.bindVertexArrayOES(vao); - - const curveVertexPositionsBuffer = { - lower: renderer.meshes[meshIndex].edgeLowerCurveVertexPositions, - upper: renderer.meshes[meshIndex].edgeUpperCurveVertexPositions, - }[direction]; - const curvePathIDsBuffer = { - lower: renderer.meshes[meshIndex].edgeLowerCurvePathIDs, - upper: renderer.meshes[meshIndex].edgeUpperCurvePathIDs, - }[direction]; - const curveIndexRanges = { - lower: renderer.meshData[meshIndex].edgeLowerCurveIndexRanges, - upper: renderer.meshData[meshIndex].edgeUpperCurveIndexRanges, - }[direction]; - - const offset = calculateStartFromIndexRanges(pathRange, curveIndexRanges); - - gl.useProgram(curveProgram.program); - gl.bindBuffer(gl.ARRAY_BUFFER, renderContext.quadPositionsBuffer); - gl.vertexAttribPointer(attributes.aQuadPosition, 2, gl.FLOAT, false, 0, 0); - gl.bindBuffer(gl.ARRAY_BUFFER, curveVertexPositionsBuffer); - gl.vertexAttribPointer(attributes.aLeftPosition, - 2, - gl.FLOAT, - false, - FLOAT32_SIZE * 6, - FLOAT32_SIZE * 6 * offset); - gl.vertexAttribPointer(attributes.aControlPointPosition, - 2, - gl.FLOAT, - false, - FLOAT32_SIZE * 6, - FLOAT32_SIZE * 6 * offset + FLOAT32_SIZE * 2); - gl.vertexAttribPointer(attributes.aRightPosition, - 2, - gl.FLOAT, - false, - FLOAT32_SIZE * 6, - FLOAT32_SIZE * 6 * offset + FLOAT32_SIZE * 4); - gl.bindBuffer(gl.ARRAY_BUFFER, curvePathIDsBuffer); - gl.vertexAttribPointer(attributes.aPathID, - 1, - gl.UNSIGNED_SHORT, - false, - 0, - UINT16_SIZE * offset); - - gl.enableVertexAttribArray(attributes.aQuadPosition); - gl.enableVertexAttribArray(attributes.aLeftPosition); - gl.enableVertexAttribArray(attributes.aControlPointPosition); - gl.enableVertexAttribArray(attributes.aRightPosition); - gl.enableVertexAttribArray(attributes.aPathID); - - renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aLeftPosition, 1); - renderContext.instancedArraysExt - .vertexAttribDivisorANGLE(attributes.aControlPointPosition, 1); - renderContext.instancedArraysExt - .vertexAttribDivisorANGLE(attributes.aRightPosition, 1); - renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aPathID, 1); - - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, renderContext.quadElementsBuffer); - } renderContext.vertexArrayObjectExt.bindVertexArrayOES(null); } - protected antialiasCurvesOfObjectWithProgram(renderer: Renderer, - objectIndex: number, - curveProgram: PathfinderShaderProgram): - void { - if (renderer.meshData == null) - return; - - const renderContext = renderer.renderContext; - const gl = renderContext.gl; - - const pathRange = renderer.pathRangeForObject(objectIndex); - const meshIndex = renderer.meshIndexForObject(objectIndex); - - this.initCurveVAOsForObject(renderer, objectIndex); - - gl.useProgram(curveProgram.program); - const uniforms = curveProgram.uniforms; - this.setAAUniforms(renderer, uniforms, objectIndex); - - for (const direction of DIRECTIONS) { - const vao = this.curveVAOs[direction]; - renderContext.vertexArrayObjectExt.bindVertexArrayOES(vao); - - this.setBlendModeForAA(renderer); - gl.uniform1i(uniforms.uWinding, direction === 'upper' ? 1 : 0); - - const indexRanges = { - lower: renderer.meshData[meshIndex].edgeLowerCurveIndexRanges, - upper: renderer.meshData[meshIndex].edgeUpperCurveIndexRanges, - }[direction]; - const count = calculateCountFromIndexRanges(pathRange, indexRanges); - - renderContext.instancedArraysExt - .drawElementsInstancedANGLE(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0, count); - } - - renderContext.vertexArrayObjectExt.bindVertexArrayOES(null); - } - - protected lineProgram(renderer: Renderer): PathfinderShaderProgram { - return renderer.renderContext.shaderPrograms.mcaaLine; - } - - protected curveProgram(renderer: Renderer): PathfinderShaderProgram { - return renderer.renderContext.shaderPrograms.mcaaCurve; - } - - private createLineVAOs(renderer: Renderer): void { - const renderContext = renderer.renderContext; - - const vaos: Partial = {}; - for (const direction of DIRECTIONS) - vaos[direction] = renderContext.vertexArrayObjectExt.createVertexArrayOES(); - this.lineVAOs = vaos as FastEdgeVAOs; - } - - private initLineVAOsForObject(renderer: Renderer, objectIndex: number): void { - if (renderer.meshes == null || renderer.meshData == null) - return; - - const renderContext = renderer.renderContext; - const gl = renderContext.gl; - - const pathRange = renderer.pathRangeForObject(objectIndex); - const meshIndex = renderer.meshIndexForObject(objectIndex); - - const lineProgram = this.lineProgram(renderer); - const attributes = lineProgram.attributes; - - for (const direction of DIRECTIONS) { - const vao = this.lineVAOs[direction]; - renderContext.vertexArrayObjectExt.bindVertexArrayOES(vao); - - const lineVertexPositionsBuffer = { - lower: renderer.meshes[meshIndex].edgeLowerLineVertexPositions, - upper: renderer.meshes[meshIndex].edgeUpperLineVertexPositions, - }[direction]; - const linePathIDsBuffer = { - lower: renderer.meshes[meshIndex].edgeLowerLinePathIDs, - upper: renderer.meshes[meshIndex].edgeUpperLinePathIDs, - }[direction]; - const lineIndexRanges = { - lower: renderer.meshData[meshIndex].edgeLowerLineIndexRanges, - upper: renderer.meshData[meshIndex].edgeUpperLineIndexRanges, - }[direction]; - - const offset = calculateStartFromIndexRanges(pathRange, lineIndexRanges); - - gl.useProgram(lineProgram.program); - gl.bindBuffer(gl.ARRAY_BUFFER, renderContext.quadPositionsBuffer); - gl.vertexAttribPointer(attributes.aQuadPosition, 2, gl.FLOAT, false, 0, 0); - gl.bindBuffer(gl.ARRAY_BUFFER, lineVertexPositionsBuffer); - gl.vertexAttribPointer(attributes.aLeftPosition, - 2, - gl.FLOAT, - false, - FLOAT32_SIZE * 4, - offset * FLOAT32_SIZE * 4); - gl.vertexAttribPointer(attributes.aRightPosition, - 2, - gl.FLOAT, - false, - FLOAT32_SIZE * 4, - offset * FLOAT32_SIZE * 4 + FLOAT32_SIZE * 2); - gl.bindBuffer(gl.ARRAY_BUFFER, linePathIDsBuffer); - gl.vertexAttribPointer(attributes.aPathID, - 1, - gl.UNSIGNED_SHORT, - false, - 0, - offset * UINT16_SIZE); - - gl.enableVertexAttribArray(attributes.aQuadPosition); - gl.enableVertexAttribArray(attributes.aLeftPosition); - gl.enableVertexAttribArray(attributes.aRightPosition); - gl.enableVertexAttribArray(attributes.aPathID); - - renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aLeftPosition, 1); - renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aRightPosition, - 1); - renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aPathID, 1); - - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, renderContext.quadElementsBuffer); - } - - renderContext.vertexArrayObjectExt.bindVertexArrayOES(null); - } - - private createCurveVAOs(renderer: Renderer): void { - const renderContext = renderer.renderContext; - const gl = renderContext.gl; - - const vaos: Partial = {}; - for (const direction of DIRECTIONS) - vaos[direction] = renderContext.vertexArrayObjectExt.createVertexArrayOES(); - this.curveVAOs = vaos as FastEdgeVAOs; - } - get directRenderingMode(): DirectRenderingMode { return 'none'; } - private initCoverVAOForObject(renderer: Renderer, objectIndex: number): void { - if (renderer.meshes == null || renderer.meshData == null) - return; + protected setAAUniforms(renderer: Renderer, uniforms: UniformMap, objectIndex: number): + void { + super.setAAUniforms(renderer, uniforms, objectIndex); const renderContext = renderer.renderContext; const gl = renderContext.gl; - const pathRange = renderer.pathRangeForObject(objectIndex); - const meshIndex = renderer.meshIndexForObject(objectIndex); + renderer.setPathColorsUniform(0, uniforms, 3); - renderContext.vertexArrayObjectExt.bindVertexArrayOES(this.coverVAO); - - const bQuadRanges = renderer.meshData[meshIndex].bQuadPathRanges; - const offset = calculateStartFromIndexRanges(pathRange, bQuadRanges); - - const coverProgram = renderContext.shaderPrograms.mcaaCover; - const attributes = coverProgram.attributes; - gl.useProgram(coverProgram.program); - gl.bindBuffer(gl.ARRAY_BUFFER, renderContext.quadPositionsBuffer); - gl.vertexAttribPointer(attributes.aQuadPosition, 2, gl.FLOAT, false, 0, 0); - gl.bindBuffer(gl.ARRAY_BUFFER, renderer.meshes[meshIndex].edgeBoundingBoxVertexPositions); - gl.vertexAttribPointer(attributes.aUpperLeftPosition, - 2, - gl.FLOAT, - false, - FLOAT32_SIZE * 4, - FLOAT32_SIZE * 4 * offset); - gl.vertexAttribPointer(attributes.aLowerRightPosition, - 2, - gl.FLOAT, - false, - FLOAT32_SIZE * 4, - FLOAT32_SIZE * 4 * offset + FLOAT32_SIZE * 2); - gl.bindBuffer(gl.ARRAY_BUFFER, renderer.meshes[meshIndex].edgeBoundingBoxPathIDs); - gl.vertexAttribPointer(attributes.aPathID, - 1, - gl.UNSIGNED_SHORT, - false, - 0, - UINT16_SIZE * offset); - gl.enableVertexAttribArray(attributes.aQuadPosition); - gl.enableVertexAttribArray(attributes.aUpperLeftPosition); - gl.enableVertexAttribArray(attributes.aLowerRightPosition); - gl.enableVertexAttribArray(attributes.aPathID); - renderContext.instancedArraysExt - .vertexAttribDivisorANGLE(attributes.aUpperLeftPosition, 1); - renderContext.instancedArraysExt - .vertexAttribDivisorANGLE(attributes.aLowerRightPosition, 1); - renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aPathID, 1); - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, renderContext.quadElementsBuffer); - - renderContext.vertexArrayObjectExt.bindVertexArrayOES(null); - } - - private antialiasLinesOfObject(renderer: Renderer, objectIndex: number): void { - const renderContext = renderer.renderContext; - this.setAAState(renderer); - - const lineProgram = this.lineProgram(renderer); - renderContext.gl.useProgram(lineProgram.program); - - // FIXME(pcwalton): Refactor. - this.antialiasLinesOfObjectWithProgram(renderer, objectIndex, lineProgram); - } - - private antialiasCurvesOfObject(renderer: Renderer, objectIndex: number): void { - const renderContext = renderer.renderContext; - this.setAAState(renderer); - - const curveProgram = this.curveProgram(renderer); - renderContext.gl.useProgram(curveProgram.program); - - this.antialiasCurvesOfObjectWithProgram(renderer, objectIndex, curveProgram); + gl.uniform1i(uniforms.uSnapToPixelGrid, renderer.isMulticolor ? 1 : 0); } } -export abstract class ECAAStrategy extends XCAAStrategy { - protected abstract get lineShaderProgramNames(): Array>; - protected abstract get curveShaderProgramNames(): Array>; +export class ECAAStrategy extends XCAAStrategy { + protected get lineShaderProgramNames(): Array> { + return ['ecaaLine']; + } + + protected get curveShaderProgramNames(): Array> { + return ['ecaaCurve', 'ecaaTransformedCurve']; + } private lineVAOs: Partial>; private curveVAOs: Partial>; + protected get usesDilationTransforms(): boolean { + return false; + } + get directRenderingMode(): DirectRenderingMode { return 'none'; } @@ -809,13 +575,19 @@ export abstract class ECAAStrategy extends XCAAStrategy { this.curveShaderProgramNames[1]); } + protected usesAAFramebuffer(): boolean { + return true; + } + protected setAAUniforms(renderer: Renderer, uniforms: UniformMap, objectIndex: number): void { super.setAAUniforms(renderer, uniforms, objectIndex); renderer.setEmboldenAmountUniform(objectIndex, uniforms); } - protected getResolveProgram(renderContext: RenderContext): PathfinderShaderProgram { + protected getResolveProgram(renderer: Renderer): PathfinderShaderProgram { + const renderContext = renderer.renderContext; + if (this.subpixelAA !== 'none') return renderContext.shaderPrograms.xcaaMonoSubpixelResolve; return renderContext.shaderPrograms.xcaaMonoResolve; @@ -1089,212 +861,11 @@ export abstract class ECAAStrategy extends XCAAStrategy { } } -export class ECAAMonochromeStrategy extends ECAAStrategy { - protected get usesDilationTransforms(): boolean { - return false; - } - - protected get usesAAFramebuffer(): boolean { - return true; - } - - protected get lineShaderProgramNames(): Array> { - return ['ecaaLine']; - } - - protected get curveShaderProgramNames(): Array> { - return ['ecaaCurve', 'ecaaTransformedCurve']; - } -} - -export class MCAAMulticolorStrategy extends XCAAStrategy { - protected vao: WebGLVertexArrayObject; - - protected get usesDilationTransforms(): boolean { - return true; - } - - attachMeshes(renderer: Renderer): void { - super.attachMeshes(renderer); - - const renderContext = renderer.renderContext; - this.vao = renderContext.vertexArrayObjectExt.createVertexArrayOES(); - } - - antialiasObject(renderer: Renderer, objectIndex: number): void { - super.antialiasObject(renderer, objectIndex); - - const shaderProgram = this.edgeProgram(renderer); - this.antialiasEdgesOfObjectWithProgram(renderer, objectIndex, shaderProgram); - } - - protected get usesAAFramebuffer(): boolean { - return false; - } - - protected getResolveProgram(renderContext: RenderContext): PathfinderShaderProgram | null { - return null; - } - - protected prepareAA(renderer: Renderer): void { - super.prepareAA(renderer); - - this.clearForAA(renderer); - } - - protected coverObjectIfNecessary(renderer: Renderer, objectIndex: number): void {} - - protected antialiasEdgesOfObjectWithProgram(renderer: Renderer, - objectIndex: number, - shaderProgram: PathfinderShaderProgram): - void { - if (renderer.meshData == null) - return; - - const renderContext = renderer.renderContext; - const gl = renderContext.gl; - - const pathRange = renderer.pathRangeForObject(objectIndex); - const meshIndex = renderer.meshIndexForObject(objectIndex); - - this.initVAOForObject(renderer, objectIndex); - - gl.useProgram(shaderProgram.program); - const uniforms = shaderProgram.uniforms; - this.setAAUniforms(renderer, uniforms, objectIndex); - - // FIXME(pcwalton): Refactor. - const vao = this.vao; - renderContext.vertexArrayObjectExt.bindVertexArrayOES(vao); - - this.setBlendModeForAA(renderer); - - const bQuadRanges = renderer.meshData[meshIndex].bQuadPathRanges; - const count = calculateCountFromIndexRanges(pathRange, bQuadRanges); - - renderContext.instancedArraysExt - .drawElementsInstancedANGLE(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0, count); - - renderContext.vertexArrayObjectExt.bindVertexArrayOES(null); - } - - protected clearForAA(renderer: Renderer): void { - const renderContext = renderer.renderContext; - const gl = renderContext.gl; - - gl.clearColor(1.0, 1.0, 1.0, 1.0); - gl.clearDepth(0.0); - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - } - - protected setAADepthState(renderer: Renderer): void { - const renderContext = renderer.renderContext; - const gl = renderContext.gl; - - gl.disable(gl.DEPTH_TEST); - } - - protected clearForResolve(renderer: Renderer): void {} - - protected setBlendModeForAA(renderer: Renderer): void { - const renderContext = renderer.renderContext; - const gl = renderContext.gl; - - gl.blendEquation(gl.FUNC_ADD); - gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE); - gl.enable(gl.BLEND); - } - - protected setAAUniforms(renderer: Renderer, uniforms: UniformMap, objectIndex: number): - void { - super.setAAUniforms(renderer, uniforms, objectIndex); - renderer.setPathColorsUniform(0, uniforms, 2); - } - - protected initVAOForObject(renderer: Renderer, objectIndex: number): void { - if (renderer.meshes == null || renderer.meshData == null) - return; - - const renderContext = renderer.renderContext; - const gl = renderContext.gl; - - const pathRange = renderer.pathRangeForObject(objectIndex); - const meshIndex = renderer.meshIndexForObject(objectIndex); - - const shaderProgram = this.edgeProgram(renderer); - const attributes = shaderProgram.attributes; - - // FIXME(pcwalton): Refactor. - const vao = this.vao; - renderContext.vertexArrayObjectExt.bindVertexArrayOES(vao); - - const bQuadRanges = renderer.meshData[meshIndex].bQuadPathRanges; - const offset = calculateStartFromIndexRanges(pathRange, bQuadRanges); - - gl.useProgram(shaderProgram.program); - gl.bindBuffer(gl.ARRAY_BUFFER, renderContext.quadPositionsBuffer); - gl.vertexAttribPointer(attributes.aQuadPosition, 2, gl.FLOAT, false, 0, 0); - gl.bindBuffer(gl.ARRAY_BUFFER, renderer.meshes[meshIndex].bQuadVertexPositions); - gl.vertexAttribPointer(attributes.aUpperEndpointPositions, - 4, - gl.FLOAT, - false, - FLOAT32_SIZE * 12, - FLOAT32_SIZE * 12 * offset); - gl.vertexAttribPointer(attributes.aLowerEndpointPositions, - 4, - gl.FLOAT, - false, - FLOAT32_SIZE * 12, - FLOAT32_SIZE * 12 * offset + FLOAT32_SIZE * 4); - gl.vertexAttribPointer(attributes.aControlPointPositions, - 4, - gl.FLOAT, - false, - FLOAT32_SIZE * 12, - FLOAT32_SIZE * 12 * offset + FLOAT32_SIZE * 8); - gl.bindBuffer(gl.ARRAY_BUFFER, renderer.meshes[meshIndex].edgeBoundingBoxPathIDs); - gl.vertexAttribPointer(attributes.aPathID, - 1, - gl.UNSIGNED_SHORT, - false, - 0, - UINT16_SIZE * offset); - gl.enableVertexAttribArray(attributes.aQuadPosition); - gl.enableVertexAttribArray(attributes.aUpperEndpointPositions); - gl.enableVertexAttribArray(attributes.aLowerEndpointPositions); - gl.enableVertexAttribArray(attributes.aControlPointPositions); - gl.enableVertexAttribArray(attributes.aPathID); - renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aPathID, 1); - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, renderContext.quadElementsBuffer); - - renderContext.instancedArraysExt - .vertexAttribDivisorANGLE(attributes.aUpperEndpointPositions, 1); - renderContext.instancedArraysExt - .vertexAttribDivisorANGLE(attributes.aLowerEndpointPositions, 1); - renderContext.instancedArraysExt - .vertexAttribDivisorANGLE(attributes.aControlPointPositions, 1); - renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aPathID, 1); - - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, renderContext.quadElementsBuffer); - - renderContext.vertexArrayObjectExt.bindVertexArrayOES(null); - } - - protected edgeProgram(renderer: Renderer): PathfinderShaderProgram { - return renderer.renderContext.shaderPrograms.mcaaMulti; - } - - get directRenderingMode(): DirectRenderingMode { - return 'none'; - } -} - /// Switches between the mesh-based MCAA and ECAA depending on whether stem darkening is enabled. /// /// FIXME(pcwalton): Share textures and FBOs between the two strategies. export class AdaptiveMonochromeXCAAStrategy implements AntialiasingStrategy { - private mcaaStrategy: MCAAMonochromeStrategy; + private mcaaStrategy: MCAAStrategy; private ecaaStrategy: ECAAStrategy; get directRenderingMode(): DirectRenderingMode { @@ -1306,8 +877,8 @@ export class AdaptiveMonochromeXCAAStrategy implements AntialiasingStrategy { } constructor(level: number, subpixelAA: SubpixelAAType) { - this.mcaaStrategy = new MCAAMonochromeStrategy(level, subpixelAA); - this.ecaaStrategy = new ECAAMonochromeStrategy(level, subpixelAA); + this.mcaaStrategy = new MCAAStrategy(level, subpixelAA); + this.ecaaStrategy = new ECAAStrategy(level, subpixelAA); } init(renderer: Renderer): void { diff --git a/partitioner/src/lib.rs b/partitioner/src/lib.rs index 7cc8a0fb..e9761181 100644 --- a/partitioner/src/lib.rs +++ b/partitioner/src/lib.rs @@ -87,6 +87,13 @@ impl BQuad { } } +#[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize)] +pub struct BQuadVertexPositions { + pub upper_endpoint_positions: [Point2D; 2], + pub lower_endpoint_positions: [Point2D; 2], + pub control_point_positions: [Point2D; 2], +} + #[derive(Clone, Copy, PartialEq, Debug)] #[repr(u8)] pub enum AntialiasingMode { diff --git a/partitioner/src/mesh_library.rs b/partitioner/src/mesh_library.rs index 205ac958..771ad50b 100644 --- a/partitioner/src/mesh_library.rs +++ b/partitioner/src/mesh_library.rs @@ -18,14 +18,14 @@ use std::ops::Range; use std::u32; use normal; -use {BQuad, BVertexLoopBlinnData}; +use {BQuad, BQuadVertexPositions, BVertexLoopBlinnData}; #[derive(Debug, Clone)] pub struct MeshLibrary { pub path_ranges: Vec, pub b_quads: Vec, // FIXME(pcwalton): Merge with `b_vertex_positions` below. - pub b_quad_vertex_positions: Vec>, + pub b_quad_vertex_positions: Vec, pub b_vertex_positions: Vec>, pub b_vertex_loop_blinn_data: Vec, pub b_vertex_normals: Vec, @@ -94,12 +94,11 @@ impl MeshLibrary { let lower_right_position = &self.b_vertex_positions[b_quad.lower_right_vertex_index as usize]; - self.b_quad_vertex_positions.extend_from_slice(&[ - *upper_left_position, - *upper_right_position, - *lower_left_position, - *lower_right_position, - ]); + let 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()], + }; let upper_left_bounding_box_position = Point2D::new(upper_left_position.x, @@ -114,7 +113,6 @@ impl MeshLibrary { }); if b_quad.upper_control_point_vertex_index == u32::MAX { - self.b_quad_vertex_positions.push(Point2D::zero()); self.edge_data.upper_line_vertex_positions.push(EdgeLineVertexPositions { left: *upper_left_position, right: *upper_right_position, @@ -122,7 +120,7 @@ impl MeshLibrary { } else { let upper_control_point_position = &self.b_vertex_positions[b_quad.upper_control_point_vertex_index as usize]; - self.b_quad_vertex_positions.push(*upper_control_point_position); + 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, @@ -131,7 +129,6 @@ impl MeshLibrary { } if b_quad.lower_control_point_vertex_index == u32::MAX { - self.b_quad_vertex_positions.push(Point2D::zero()); self.edge_data.lower_line_vertex_positions.push(EdgeLineVertexPositions { left: *lower_left_position, right: *lower_right_position, @@ -139,13 +136,15 @@ impl MeshLibrary { } else { let lower_control_point_position = &self.b_vertex_positions[b_quad.lower_control_point_vertex_index as usize]; - self.b_quad_vertex_positions.push(*lower_control_point_position); + 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_quad_vertex_positions.push(b_quad_vertex_positions); } /// Reverses interior indices so that they draw front-to-back. diff --git a/shaders/gles2/common.inc.glsl b/shaders/gles2/common.inc.glsl index 24b76d9b..396e571b 100644 --- a/shaders/gles2/common.inc.glsl +++ b/shaders/gles2/common.inc.glsl @@ -145,17 +145,23 @@ vec2 computeMCAASnappedPosition(vec2 position, vec4 localTransformST, vec4 globalTransformST, ivec2 framebufferSize, - float slope) { + float slope, + bool snapToPixelGrid) { position = hintPosition(position, hints); position = transformVertexPositionST(position, localTransformST); position = transformVertexPositionST(position, globalTransformST); position = convertClipToScreenSpace(position, framebufferSize); - float xNudge = fract(position.x); - if (xNudge < 0.5) - xNudge = -xNudge; - else - xNudge = 1.0 - xNudge; + 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); } diff --git a/shaders/gles2/direct-curve.fs.glsl b/shaders/gles2/direct-curve.fs.glsl index ebdc0785..721c8f33 100644 --- a/shaders/gles2/direct-curve.fs.glsl +++ b/shaders/gles2/direct-curve.fs.glsl @@ -28,5 +28,5 @@ varying float vSign; void main() { float side = vTexCoord.x * vTexCoord.x - vTexCoord.y; float alpha = float(sign(side) == sign(vSign)); - gl_FragColor = vec4(vColor.rgb, vColor.a * alpha); + gl_FragColor = alpha * vColor; } diff --git a/shaders/gles2/xcaa-curve.fs.glsl b/shaders/gles2/ecaa-curve.fs.glsl similarity index 96% rename from shaders/gles2/xcaa-curve.fs.glsl rename to shaders/gles2/ecaa-curve.fs.glsl index 11a904b9..4004030f 100644 --- a/shaders/gles2/xcaa-curve.fs.glsl +++ b/shaders/gles2/ecaa-curve.fs.glsl @@ -1,4 +1,4 @@ -// pathfinder/shaders/gles2/xcaa-curve.fs.glsl +// pathfinder/shaders/gles2/ecaa-curve.fs.glsl // // Copyright (c) 2017 The Pathfinder Project Developers. // diff --git a/shaders/gles2/ecaa-curve.vs.glsl b/shaders/gles2/ecaa-curve.vs.glsl index 2eeaba88..b3c02184 100644 --- a/shaders/gles2/ecaa-curve.vs.glsl +++ b/shaders/gles2/ecaa-curve.vs.glsl @@ -16,10 +16,10 @@ //! Use this shader only when *all* of the following are true: //! //! 1. You are only rendering monochrome paths such as text. (Otherwise, -//! consider `mcaa-multi`.) +//! consider MCAA.) //! //! 2. The paths are relatively small, so overdraw is not a concern. -//! (Otherwise, consider the MCAA shaders.) +//! (Otherwise, consider MCAA.) //! //! 3. Your transform is only a scale and/or translation, not a perspective, //! rotation, or skew. (Otherwise, consider `ecaa-transformed-curve`.) diff --git a/shaders/gles2/xcaa-line.fs.glsl b/shaders/gles2/ecaa-line.fs.glsl similarity index 95% rename from shaders/gles2/xcaa-line.fs.glsl rename to shaders/gles2/ecaa-line.fs.glsl index 0b84fe22..0dd2407b 100644 --- a/shaders/gles2/xcaa-line.fs.glsl +++ b/shaders/gles2/ecaa-line.fs.glsl @@ -1,4 +1,4 @@ -// pathfinder/shaders/gles2/xcaa-line.fs.glsl +// pathfinder/shaders/gles2/ecaa-line.fs.glsl // // Copyright (c) 2017 The Pathfinder Project Developers. // diff --git a/shaders/gles2/ecaa-line.vs.glsl b/shaders/gles2/ecaa-line.vs.glsl index 852bc3a8..56904126 100644 --- a/shaders/gles2/ecaa-line.vs.glsl +++ b/shaders/gles2/ecaa-line.vs.glsl @@ -17,10 +17,10 @@ //! Use this shader only when *both* of the following are true: //! //! 1. You are only rendering monochrome paths such as text. (Otherwise, -//! consider `mcaa-multi`.) +//! consider MCAA.) //! //! 2. The paths are relatively small, so overdraw is not a concern. -//! (Otherwise, consider the MCAA shaders.) +//! (Otherwise, consider MCAA.) precision highp float; diff --git a/shaders/gles2/ecaa-transformed-curve.vs.glsl b/shaders/gles2/ecaa-transformed-curve.vs.glsl index f725fec4..973efdd7 100644 --- a/shaders/gles2/ecaa-transformed-curve.vs.glsl +++ b/shaders/gles2/ecaa-transformed-curve.vs.glsl @@ -20,10 +20,10 @@ //! Use this shader only when *all* of the following are true: //! //! 1. You are only rendering monochrome paths such as text. (Otherwise, -//! consider `mcaa-multi`.) +//! consider MCAA.) //! //! 2. The paths are relatively small, so overdraw is not a concern. -//! (Otherwise, consider the MCAA shaders.) +//! (Otherwise, consider MCAA.) //! //! 3. Your transform contains perspective, rotation, or skew. (Otherwise, //! consider `ecaa-curve`, which is faster and saves a pass.) diff --git a/shaders/gles2/mcaa-cover.fs.glsl b/shaders/gles2/mcaa-cover.fs.glsl deleted file mode 100644 index 762b6b4c..00000000 --- a/shaders/gles2/mcaa-cover.fs.glsl +++ /dev/null @@ -1,33 +0,0 @@ -// pathfinder/shaders/gles2/mcaa-cover.fs.glsl -// -// Copyright (c) 2017 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Performs the conservative coverage step for *mesh coverage antialiasing* -//! (MCAA). -//! -//! This shader expects to render to the red channel of a floating point color -//! buffer. Half precision floating point should be sufficient. -//! -//! Use this shader only when *both* of the following are true: -//! -//! 1. You are only rendering monochrome paths such as text. (Otherwise, -//! consider `mcaa-multi`.) -//! -//! 2. Your transform is only a scale and/or translation, not a perspective, -//! rotation, or skew. (Otherwise, consider the ECAA shaders.) - -precision highp float; - -varying vec2 vHorizontalExtents; - -void main() { - vec2 sides = gl_FragCoord.xx + vec2(-0.5, 0.5); - vec2 clampedSides = clamp(vHorizontalExtents, sides.x, sides.y); - gl_FragColor = vec4(vec3(clampedSides.y - clampedSides.x), 1.0); -} diff --git a/shaders/gles2/mcaa-cover.vs.glsl b/shaders/gles2/mcaa-cover.vs.glsl deleted file mode 100644 index a3afd436..00000000 --- a/shaders/gles2/mcaa-cover.vs.glsl +++ /dev/null @@ -1,63 +0,0 @@ -// pathfinder/shaders/gles2/mcaa-cover.vs.glsl -// -// Copyright (c) 2017 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Performs the conservative coverage step for *mesh coverage antialiasing* -//! (MCAA). -//! -//! This shader expects to render to the red channel of a floating point color -//! buffer. Half precision floating point should be sufficient. -//! -//! Use this shader only when *both* of the following are true: -//! -//! 1. You are only rendering monochrome paths such as text. (Otherwise, -//! consider `mcaa-multi`.) -//! -//! 2. Your transform is only a scale and/or translation, not a perspective, -//! rotation, or skew. (Otherwise, consider the ECAA shaders.) - -precision highp float; - -uniform vec4 uTransformST; -uniform vec4 uHints; -uniform ivec2 uFramebufferSize; -uniform ivec2 uPathTransformSTDimensions; -uniform sampler2D uPathTransformST; - -attribute vec2 aQuadPosition; -attribute vec2 aUpperLeftPosition; -attribute vec2 aLowerRightPosition; -attribute float aPathID; - -varying vec2 vHorizontalExtents; - -void main() { - int pathID = int(aPathID); - - vec4 transformST = fetchFloat4Data(uPathTransformST, pathID, uPathTransformSTDimensions); - - vec2 upperLeftPosition = computeMCAAPosition(aUpperLeftPosition, - uHints, - transformST, - uTransformST, - uFramebufferSize); - vec2 lowerRightPosition = computeMCAAPosition(aLowerRightPosition, - uHints, - transformST, - uTransformST, - uFramebufferSize); - - vHorizontalExtents = vec2(upperLeftPosition.x, lowerRightPosition.x); - - vec4 extents = vec4(upperLeftPosition.x, ceil(upperLeftPosition.y), lowerRightPosition); - vec2 position = computeXCAAClipSpaceQuadPosition(extents, aQuadPosition, uFramebufferSize); - float depth = convertPathIndexToViewportDepthValue(pathID); - - gl_Position = vec4(position, depth, 1.0); -} diff --git a/shaders/gles2/mcaa-curve.vs.glsl b/shaders/gles2/mcaa-curve.vs.glsl deleted file mode 100644 index 6ce4328d..00000000 --- a/shaders/gles2/mcaa-curve.vs.glsl +++ /dev/null @@ -1,75 +0,0 @@ -// pathfinder/shaders/gles2/mcaa-curve.vs.glsl -// -// Copyright (c) 2017 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Renders the curved edges of paths when performing *mesh coverage -//! antialiasing* (MCAA). -//! -//! This shader expects to render to the red channel of a floating point color -//! buffer. Half precision floating point should be sufficient. -//! -//! Use this shader only when *both* of the following are true: -//! -//! 1. You are only rendering monochrome paths such as text. (Otherwise, -//! consider `mcaa-multi`.) -//! -//! 2. Your transform is only a scale and/or translation, not a perspective, -//! rotation, or skew. (Otherwise, consider the ECAA shaders.) - -precision highp float; - -uniform vec4 uTransformST; -uniform vec4 uHints; -uniform ivec2 uFramebufferSize; -uniform ivec2 uPathTransformSTDimensions; -uniform sampler2D uPathTransformST; -uniform bool uWinding; - -attribute vec2 aQuadPosition; -attribute vec2 aLeftPosition; -attribute vec2 aControlPointPosition; -attribute vec2 aRightPosition; -attribute float aPathID; - -varying vec4 vEndpoints; -varying vec2 vControlPoint; -varying float vWinding; - -void main() { - vec2 leftPosition = aLeftPosition; - vec2 controlPointPosition = aControlPointPosition; - vec2 rightPosition = aRightPosition; - int pathID = int(aPathID); - - vec4 transformST = fetchFloat4Data(uPathTransformST, pathID, uPathTransformSTDimensions); - - // Transform the points, and compute the position of this vertex. - vec2 position; - if (computeMCAAQuadPosition(position, - leftPosition, - rightPosition, - aQuadPosition, - uFramebufferSize, - transformST, - uTransformST, - uHints)) { - controlPointPosition = computeMCAAPosition(controlPointPosition, - uHints, - transformST, - uTransformST, - uFramebufferSize); - } - - float depth = convertPathIndexToViewportDepthValue(pathID); - - gl_Position = vec4(position, depth, 1.0); - vEndpoints = vec4(leftPosition, rightPosition); - vControlPoint = controlPointPosition; - vWinding = uWinding ? 1.0 : -1.0; -} diff --git a/shaders/gles2/mcaa-line.vs.glsl b/shaders/gles2/mcaa-line.vs.glsl deleted file mode 100644 index 4bb99996..00000000 --- a/shaders/gles2/mcaa-line.vs.glsl +++ /dev/null @@ -1,62 +0,0 @@ -// pathfinder/shaders/gles2/mcaa-line.vs.glsl -// -// Copyright (c) 2017 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Renders the straight line segments of paths when performing *mesh coverage -//! antialiasing* (MCAA). -//! -//! Use this shader only when *both* of the following are true: -//! -//! 1. You are only rendering monochrome paths such as text. (Otherwise, -//! consider `mcaa-multi`.) -//! -//! 2. Your transform is only a scale and/or translation, not a perspective, -//! rotation, or skew. (Otherwise, consider the ECAA shaders.) - -precision highp float; - -uniform vec4 uTransformST; -uniform vec4 uHints; -uniform ivec2 uFramebufferSize; -uniform ivec2 uPathTransformSTDimensions; -uniform sampler2D uPathTransformST; -uniform bool uWinding; - -attribute vec2 aQuadPosition; -attribute vec2 aLeftPosition; -attribute vec2 aRightPosition; -attribute float aPathID; - -varying vec4 vEndpoints; -varying float vWinding; - -void main() { - vec2 leftPosition = aLeftPosition; - vec2 rightPosition = aRightPosition; - int pathID = int(aPathID); - - vec4 transformST = fetchFloat4Data(uPathTransformST, pathID, uPathTransformSTDimensions); - - // Transform the points, and compute the position of this vertex. - vec2 position; - computeMCAAQuadPosition(position, - leftPosition, - rightPosition, - aQuadPosition, - uFramebufferSize, - transformST, - uTransformST, - uHints); - - float depth = convertPathIndexToViewportDepthValue(pathID); - - gl_Position = vec4(position, depth, 1.0); - vEndpoints = vec4(leftPosition, rightPosition); - vWinding = uWinding ? 1.0 : -1.0; -} diff --git a/shaders/gles2/mcaa-multi.vs.glsl b/shaders/gles2/mcaa-multi.vs.glsl deleted file mode 100644 index 453a199d..00000000 --- a/shaders/gles2/mcaa-multi.vs.glsl +++ /dev/null @@ -1,126 +0,0 @@ -// pathfinder/shaders/gles2/mcaa-multi.vs.glsl -// -// Copyright (c) 2017 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Renders paths when performing multicolor *mesh coverage antialiasing* -//! (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.) - -#define MAX_SLOPE 10.0 - -precision highp float; - -uniform vec4 uTransformST; -uniform vec4 uHints; -uniform ivec2 uFramebufferSize; -uniform ivec2 uPathTransformSTDimensions; -uniform sampler2D uPathTransformST; -uniform ivec2 uPathColorsDimensions; -uniform sampler2D uPathColors; - -attribute vec2 aQuadPosition; -attribute vec4 aUpperEndpointPositions; -attribute vec4 aLowerEndpointPositions; -attribute vec4 aControlPointPositions; -attribute float aPathID; - -varying vec4 vUpperEndpoints; -varying vec4 vLowerEndpoints; -varying vec4 vControlPoints; -varying vec4 vColor; - -void main() { - vec2 tlPosition = aUpperEndpointPositions.xy; - vec2 tcPosition = aControlPointPositions.xy; - vec2 trPosition = aUpperEndpointPositions.zw; - vec2 blPosition = aLowerEndpointPositions.xy; - vec2 bcPosition = aControlPointPositions.zw; - vec2 brPosition = aLowerEndpointPositions.zw; - vec2 quadPosition = aQuadPosition; - int pathID = int(aPathID); - - vec4 transformST = fetchFloat4Data(uPathTransformST, pathID, uPathTransformSTDimensions); - - vec4 color = fetchFloat4Data(uPathColors, pathID, uPathColorsDimensions); - - vec2 topVector = trPosition - tlPosition, bottomVector = brPosition - blPosition; - - float 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; - - // Transform the points, and compute the position of this vertex. - tlPosition = computeMCAASnappedPosition(tlPosition, - uHints, - transformST, - uTransformST, - uFramebufferSize, - topSlope); - trPosition = computeMCAASnappedPosition(trPosition, - uHints, - transformST, - uTransformST, - uFramebufferSize, - topSlope); - tcPosition = computeMCAAPosition(tcPosition, - uHints, - transformST, - uTransformST, - uFramebufferSize); - blPosition = computeMCAASnappedPosition(blPosition, - uHints, - transformST, - uTransformST, - uFramebufferSize, - bottomSlope); - brPosition = computeMCAASnappedPosition(brPosition, - uHints, - transformST, - uTransformST, - uFramebufferSize, - bottomSlope); - bcPosition = computeMCAAPosition(bcPosition, - uHints, - transformST, - uTransformST, - uFramebufferSize); - - 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; - position.x = quadPosition.x < 0.5 ? tlPosition.x : trPosition.x; - - if (quadPosition.y < 0.5) - position.y = floor(min(tlPosition.y, trPosition.y)); - else - position.y = ceil(max(blPosition.y, brPosition.y)); - position = convertScreenToClipSpace(position, uFramebufferSize); - - gl_Position = vec4(position, depth, 1.0); - vUpperEndpoints = vec4(tlPosition, trPosition); - vLowerEndpoints = vec4(blPosition, brPosition); - vControlPoints = vec4(tcPosition, bcPosition); - vColor = color; -} diff --git a/shaders/gles2/mcaa-multi.fs.glsl b/shaders/gles2/mcaa.fs.glsl similarity index 96% rename from shaders/gles2/mcaa-multi.fs.glsl rename to shaders/gles2/mcaa.fs.glsl index 4e4f5a52..1fc9fd85 100644 --- a/shaders/gles2/mcaa-multi.fs.glsl +++ b/shaders/gles2/mcaa.fs.glsl @@ -1,4 +1,4 @@ -// pathfinder/shaders/gles2/mcaa-multi.fs.glsl +// pathfinder/shaders/gles2/mcaa.fs.glsl // // Copyright (c) 2017 The Pathfinder Project Developers. // @@ -69,5 +69,5 @@ void main() { 1.0); // Compute area. - gl_FragColor = vec4(vColor.rgb, vColor.a * alpha); + gl_FragColor = alpha * vColor; } diff --git a/shaders/gles2/mcaa.vs.glsl b/shaders/gles2/mcaa.vs.glsl new file mode 100644 index 00000000..ff5cbfee --- /dev/null +++ b/shaders/gles2/mcaa.vs.glsl @@ -0,0 +1,146 @@ +// pathfinder/shaders/gles2/mcaa.vs.glsl +// +// Copyright (c) 2017 The Pathfinder Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Renders paths when performing *mesh coverage antialiasing* (MCAA). This +//! one shader handles both lines and curves. +//! +//! This shader expects to render to a standard RGB color buffer in the +//! multicolor case or a single-channel floating-point color buffer in the +//! monochrome case. +//! +//! Set state as follows depending on whether multiple overlapping multicolor +//! paths are present: +//! +//! * When paths of multiple colors are present, use +//! `glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE)` and +//! set `uSnapToPixelGrid` to 1. +//! +//! * Otherwise, if only one color of path is present, use +//! `glBlendFunc(GL_ONE, GL_ONE)` and set `uSnapToPixelGrid` to 0. +//! +//! Use this shader only when 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.) + +#define MAX_SLOPE 10.0 + +precision highp float; + +uniform vec4 uTransformST; +uniform vec4 uHints; +uniform ivec2 uFramebufferSize; +uniform ivec2 uPathTransformSTDimensions; +uniform sampler2D uPathTransformST; +uniform ivec2 uPathColorsDimensions; +uniform sampler2D uPathColors; +uniform bool uSnapToPixelGrid; + +attribute vec2 aQuadPosition; +attribute vec4 aUpperEndpointPositions; +attribute vec4 aLowerEndpointPositions; +attribute vec4 aControlPointPositions; +attribute float aPathID; + +varying vec4 vUpperEndpoints; +varying vec4 vLowerEndpoints; +varying vec4 vControlPoints; +varying vec4 vColor; + +void main() { + vec2 tlPosition = aUpperEndpointPositions.xy; + vec2 tcPosition = aControlPointPositions.xy; + vec2 trPosition = aUpperEndpointPositions.zw; + vec2 blPosition = aLowerEndpointPositions.xy; + vec2 bcPosition = aControlPointPositions.zw; + vec2 brPosition = aLowerEndpointPositions.zw; + vec2 quadPosition = aQuadPosition; + int pathID = int(floor(aPathID)); + + 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; + + 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; + + // Transform the points, and compute the position of this vertex. + tlPosition = computeMCAASnappedPosition(tlPosition, + uHints, + transformST, + uTransformST, + uFramebufferSize, + topSlope, + uSnapToPixelGrid); + trPosition = computeMCAASnappedPosition(trPosition, + uHints, + transformST, + uTransformST, + uFramebufferSize, + topSlope, + uSnapToPixelGrid); + tcPosition = computeMCAAPosition(tcPosition, + uHints, + transformST, + uTransformST, + uFramebufferSize); + blPosition = computeMCAASnappedPosition(blPosition, + uHints, + transformST, + uTransformST, + uFramebufferSize, + bottomSlope, + uSnapToPixelGrid); + brPosition = computeMCAASnappedPosition(brPosition, + uHints, + transformST, + uTransformST, + uFramebufferSize, + bottomSlope, + uSnapToPixelGrid); + bcPosition = computeMCAAPosition(bcPosition, + uHints, + transformST, + uTransformST, + uFramebufferSize); + + 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 (uSnapToPixelGrid) + position.x = quadPosition.x < 0.5 ? tlPosition.x : trPosition.x; + 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)); + else + position.y = ceil(max(blPosition.y, brPosition.y)); + + position = convertScreenToClipSpace(position, uFramebufferSize); + + gl_Position = vec4(position, depth, 1.0); + vUpperEndpoints = vec4(tlPosition, trPosition); + vLowerEndpoints = vec4(blPosition, brPosition); + vControlPoints = vec4(tcPosition, bcPosition); + vColor = color; + } else { + gl_Position = vec4(0.0); + } +} diff --git a/shaders/gles2/xcaa-mono-subpixel-resolve.fs.glsl b/shaders/gles2/xcaa-mono-subpixel-resolve.fs.glsl index 918d143f..50e21966 100644 --- a/shaders/gles2/xcaa-mono-subpixel-resolve.fs.glsl +++ b/shaders/gles2/xcaa-mono-subpixel-resolve.fs.glsl @@ -41,5 +41,6 @@ void main() { lcdFilter(shadeL.x, shade0, shadeR.x, shadeR.y, shadeR.z)); vec3 color = mix(uBGColor.rgb, uFGColor.rgb, shades); - gl_FragColor = vec4(color, any(greaterThan(shades, vec3(0.0))) ? uFGColor.a : uBGColor.a); + float alpha = any(greaterThan(shades, vec3(0.0))) ? uFGColor.a : uBGColor.a; + gl_FragColor = alpha * vec4(color, 1.0); }