From 260415152130eaf386cc2656a39f00a9b19adaba Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Mon, 30 Oct 2017 13:34:55 -0700 Subject: [PATCH] Replace the multicolor (SVG) XCAA with a multipass compositing algorithm --- demo/client/src/3d-demo.ts | 16 +- demo/client/src/aa-strategy.ts | 6 +- demo/client/src/benchmark.ts | 16 +- demo/client/src/renderer.ts | 233 ++++++------ demo/client/src/shader-loader.ts | 38 +- demo/client/src/ssaa-strategy.ts | 70 ++-- demo/client/src/svg-demo.ts | 18 +- demo/client/src/svg-loader.ts | 10 +- demo/client/src/text-renderer.ts | 16 +- demo/client/src/xcaa-strategy.ts | 344 ++++++++---------- shaders/gles2/common.inc.glsl | 36 ++ ...lsl => xcaa-multi-bg-direct-curve.fs.glsl} | 17 +- ...lsl => xcaa-multi-bg-direct-curve.vs.glsl} | 8 +- .../xcaa-multi-bg-direct-interior.fs.glsl | 28 ++ ... => xcaa-multi-bg-direct-interior.vs.glsl} | 9 +- .../gles2/xcaa-multi-direct-interior.fs.glsl | 17 - .../gles2/xcaa-multi-edge-mask-curve.fs.glsl | 56 +++ .../gles2/xcaa-multi-edge-mask-line.fs.glsl | 34 ++ shaders/gles2/xcaa-multi-resolve.fs.glsl | 17 +- 19 files changed, 566 insertions(+), 423 deletions(-) rename shaders/gles2/{xcaa-multi-direct-curve.fs.glsl => xcaa-multi-bg-direct-curve.fs.glsl} (57%) rename shaders/gles2/{xcaa-multi-direct-curve.vs.glsl => xcaa-multi-bg-direct-curve.vs.glsl} (83%) create mode 100644 shaders/gles2/xcaa-multi-bg-direct-interior.fs.glsl rename shaders/gles2/{xcaa-multi-direct-interior.vs.glsl => xcaa-multi-bg-direct-interior.vs.glsl} (79%) delete mode 100644 shaders/gles2/xcaa-multi-direct-interior.fs.glsl create mode 100644 shaders/gles2/xcaa-multi-edge-mask-curve.fs.glsl create mode 100644 shaders/gles2/xcaa-multi-edge-mask-line.fs.glsl diff --git a/demo/client/src/3d-demo.ts b/demo/client/src/3d-demo.ts index 67263f7c..63d58233 100644 --- a/demo/client/src/3d-demo.ts +++ b/demo/client/src/3d-demo.ts @@ -359,14 +359,6 @@ class ThreeDRenderer extends Renderer { return this.destAllocatedSize; } - protected get directCurveProgramName(): keyof ShaderMap { - return 'direct3DCurve'; - } - - protected get directInteriorProgramName(): keyof ShaderMap { - return 'direct3DInterior'; - } - protected get depthFunction(): GLenum { return this.renderContext.gl.LESS; } @@ -528,6 +520,14 @@ class ThreeDRenderer extends Renderer { this.renderContext.appController.newTimingsReceived(newTimings); } + protected directCurveProgramNameForPass(pass: number): keyof ShaderMap { + return 'direct3DCurve'; + } + + protected directInteriorProgramNameForPass(pass: number): keyof ShaderMap { + return 'direct3DInterior'; + } + // Cheap but effective backface culling. private objectIsVisible(objectIndex: number): boolean { const textFrameIndex = this.renderContext diff --git a/demo/client/src/aa-strategy.ts b/demo/client/src/aa-strategy.ts index bf673800..45c3588c 100644 --- a/demo/client/src/aa-strategy.ts +++ b/demo/client/src/aa-strategy.ts @@ -15,7 +15,7 @@ import {DemoView} from './view'; export type AntialiasingStrategyName = 'none' | 'ssaa' | 'xcaa'; -export type DirectRenderingMode = 'none' | 'color' | 'pathID'; +export type DirectRenderingMode = 'none' | 'color' | 'two-pass'; export type SubpixelAAType = 'none' | 'medium'; @@ -42,7 +42,7 @@ export abstract class AntialiasingStrategy { // Called before direct rendering. // // Typically, this redirects direct rendering to a framebuffer of some sort. - abstract prepare(renderer: Renderer): void; + abstract prepareForDirectRendering(renderer: Renderer): void; // Called after direct rendering. // @@ -73,7 +73,7 @@ export class NoAAStrategy extends AntialiasingStrategy { return glmatrix.mat4.create(); } - prepare(renderer: Renderer) { + prepareForDirectRendering(renderer: Renderer) { const renderContext = renderer.renderContext; renderContext.gl.bindFramebuffer(renderContext.gl.FRAMEBUFFER, renderer.destFramebuffer); renderContext.gl.viewport(0, 0, this.framebufferSize[0], this.framebufferSize[1]); diff --git a/demo/client/src/benchmark.ts b/demo/client/src/benchmark.ts index 5cf4cc11..2af71168 100644 --- a/demo/client/src/benchmark.ts +++ b/demo/client/src/benchmark.ts @@ -309,14 +309,6 @@ class BenchmarkRenderer extends Renderer { return glmatrix.vec2.clone([1.0, 1.0]); } - protected get directCurveProgramName(): keyof ShaderMap { - return 'directCurve'; - } - - protected get directInteriorProgramName(): keyof ShaderMap { - return 'directInterior'; - } - protected get depthFunction(): number { return this.renderContext.gl.GREATER; } @@ -445,6 +437,14 @@ class BenchmarkRenderer extends Renderer { return pathTransforms; } + + protected directCurveProgramNameForPass(pass: number): keyof ShaderMap { + return 'directCurve'; + } + + protected directInteriorProgramNameForPass(pass: number): keyof ShaderMap { + return 'directInterior'; + } } function computeMedian(values: number[]): number | null { diff --git a/demo/client/src/renderer.ts b/demo/client/src/renderer.ts index 4627eedd..5fc62824 100644 --- a/demo/client/src/renderer.ts +++ b/demo/client/src/renderer.ts @@ -18,6 +18,7 @@ import {PathfinderMeshBuffers, PathfinderMeshData} from "./meshes"; import {ShaderMap} from './shader-loader'; import {FLOAT32_SIZE, Range, UINT16_SIZE, UINT32_SIZE, unwrapNull} from './utils'; import {RenderContext, Timings} from "./view"; +import {MCAAMulticolorStrategy} from './xcaa-strategy'; const MAX_PATHS: number = 65535; @@ -64,8 +65,6 @@ export abstract class Renderer { } protected abstract get depthFunction(): GLenum; - protected abstract get directCurveProgramName(): keyof ShaderMap; - protected abstract get directInteriorProgramName(): keyof ShaderMap; protected abstract get usedSizeFactor(): glmatrix.vec2; protected abstract get worldTransform(): glmatrix.mat4; @@ -112,22 +111,22 @@ export abstract class Renderer { renderContext.atlasRenderingTimerQuery); } - // Prepare for direct rendering. + // Draw "scenery" (used in the 3D view). + this.drawSceneryIfNecessary(); + + // Antialias. const antialiasingStrategy = unwrapNull(this.antialiasingStrategy); - antialiasingStrategy.prepare(this); + antialiasingStrategy.antialias(this); + + // Prepare for direct rendering. + antialiasingStrategy.prepareForDirectRendering(this); // Clear. this.clearForDirectRendering(); - // Draw "scenery" (used in the 3D view). - this.drawSceneryIfNecessary(); - // Perform direct rendering (Loop-Blinn). if (antialiasingStrategy.directRenderingMode !== 'none') - this.renderDirect(); - - // Antialias. - antialiasingStrategy.antialias(this); + this.renderDirect(0); // End the timer, and start a new one. if (this.timerQueryPollInterval == null) { @@ -138,6 +137,9 @@ export abstract class Renderer { antialiasingStrategy.resolve(this); + if (antialiasingStrategy.directRenderingMode === 'two-pass') + this.renderDirect(1); + // Draw the glyphs with the resolved atlas to the default framebuffer. this.compositeIfNecessary(); @@ -167,9 +169,10 @@ export abstract class Renderer { } setFramebufferSizeUniform(uniforms: UniformMap) { - this.renderContext.gl.uniform2i(uniforms.uFramebufferSize, - this.destAllocatedSize[0], - this.destAllocatedSize[1]); + const gl = this.renderContext.gl; + gl.uniform2i(uniforms.uFramebufferSize, + this.destAllocatedSize[0], + this.destAllocatedSize[1]); } setTransformAndTexScaleUniformsForDest(uniforms: UniformMap): void { @@ -186,29 +189,29 @@ export abstract class Renderer { setTransformSTAndTexScaleUniformsForDest(uniforms: UniformMap): void { const renderContext = this.renderContext; + const gl = renderContext.gl; + const usedSize = this.usedSizeFactor; - renderContext.gl.uniform4f(uniforms.uTransformST, - 2.0 * usedSize[0], - 2.0 * usedSize[1], - -1.0, - -1.0); - renderContext.gl.uniform2f(uniforms.uTexScale, usedSize[0], usedSize[1]); + gl.uniform4f(uniforms.uTransformST, 2.0 * usedSize[0], 2.0 * usedSize[1], -1.0, -1.0); + gl.uniform2f(uniforms.uTexScale, usedSize[0], usedSize[1]); } setTransformSTUniform(uniforms: UniformMap, objectIndex: number) { // FIXME(pcwalton): Lossy conversion from a 4x4 matrix to an ST matrix is ugly and fragile. // Refactor. 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]); - renderContext.gl.uniform4f(uniforms.uTransformST, - transform[0], - transform[5], - transform[12], - transform[13]); + gl.uniform4f(uniforms.uTransformST, + transform[0], + transform[5], + transform[12], + transform[13]); } uploadPathColors(objectCount: number) { @@ -248,9 +251,8 @@ export abstract class Renderer { } setPathColorsUniform(objectIndex: number, uniforms: UniformMap, textureUnit: number): void { - this.pathColorsBufferTextures[objectIndex].bind(this.renderContext.gl, - uniforms, - textureUnit); + const gl = this.renderContext.gl; + this.pathColorsBufferTextures[objectIndex].bind(gl, uniforms, textureUnit); } protected abstract createAAStrategy(aaType: AntialiasingStrategyName, @@ -262,6 +264,9 @@ export abstract class Renderer { protected abstract pathColorsForObject(objectIndex: number): Uint8Array; protected abstract pathTransformsForObject(objectIndex: number): Float32Array; + protected abstract directCurveProgramNameForPass(pass: number): keyof ShaderMap; + protected abstract directInteriorProgramNameForPass(pass: number): keyof ShaderMap; + protected drawSceneryIfNecessary(): void {} protected clearForDirectRendering(): void { @@ -269,22 +274,21 @@ export abstract class Renderer { const renderContext = this.renderContext; const gl = renderContext.gl; + const clearColor = this.backgroundColor; + gl.clearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); + switch (renderingMode) { case 'color': - const clearColor = this.backgroundColor; - gl.clearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); gl.clearDepth(0.0); gl.depthMask(true); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); break; - case 'pathID': - gl.clearColor(0.0, 0.0, 0.0, 0.0); - gl.clearDepth(0.0); - gl.depthMask(true); - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + case 'two-pass': + gl.clear(gl.COLOR_BUFFER_BIT); break; case 'none': // Nothing to do. + break; } } @@ -300,11 +304,13 @@ export abstract class Renderer { /// Called whenever new GPU timing statistics are available. protected newTimingsReceived(): void {} - private renderDirect(): void { - const renderingMode = unwrapNull(this.antialiasingStrategy).directRenderingMode; + private renderDirect(pass: number): void { const renderContext = this.renderContext; const gl = renderContext.gl; + const antialiasingStrategy = unwrapNull(this.antialiasingStrategy); + const renderingMode = antialiasingStrategy.directRenderingMode; + for (let objectIndex = 0; objectIndex < this.meshes.length; objectIndex++) { const instanceRange = this.instanceRangeForObject(objectIndex); if (instanceRange.isEmpty) @@ -319,23 +325,26 @@ export abstract class Renderer { gl.disable(gl.BLEND); // Set up the implicit cover interior VAO. - const directInteriorProgram = - renderContext.shaderPrograms[this.directInteriorProgramName]; + const directInteriorProgramName = this.directInteriorProgramNameForPass(pass); + const directInteriorProgram = renderContext.shaderPrograms[directInteriorProgramName]; if (this.implicitCoverInteriorVAO == null) { this.implicitCoverInteriorVAO = renderContext.vertexArrayObjectExt .createVertexArrayOES(); } renderContext.vertexArrayObjectExt.bindVertexArrayOES(this.implicitCoverInteriorVAO); - this.initImplicitCoverInteriorVAO(objectIndex, instanceRange); + this.initImplicitCoverInteriorVAO(pass, objectIndex, instanceRange); // Draw direct interior parts. this.setTransformUniform(directInteriorProgram.uniforms, objectIndex); this.setFramebufferSizeUniform(directInteriorProgram.uniforms); this.setHintsUniform(directInteriorProgram.uniforms); - if (renderingMode === 'color') - this.setPathColorsUniform(objectIndex, directInteriorProgram.uniforms, 0); + this.setPathColorsUniform(objectIndex, directInteriorProgram.uniforms, 0); this.pathTransformBufferTextures[objectIndex] .bind(gl, directInteriorProgram.uniforms, 1); + if (renderingMode === 'two-pass') { + const strategy = antialiasingStrategy as MCAAMulticolorStrategy; + strategy.bindEdgeDepthTexture(gl, directInteriorProgram.uniforms, 2); + } let indexCount = meshData.coverInteriorIndices.byteLength / UINT32_SIZE; if (!this.pathIDsAreInstanced) { gl.drawElements(gl.TRIANGLES, indexCount, gl.UNSIGNED_INT, 0); @@ -356,21 +365,25 @@ export abstract class Renderer { // Set up the direct curve VAO. // // TODO(pcwalton): Cache these. - const directCurveProgram = renderContext.shaderPrograms[this.directCurveProgramName]; + const directCurveProgramName = this.directCurveProgramNameForPass(pass); + const directCurveProgram = renderContext.shaderPrograms[directCurveProgramName]; if (this.implicitCoverCurveVAO == null) { this.implicitCoverCurveVAO = renderContext.vertexArrayObjectExt .createVertexArrayOES(); } renderContext.vertexArrayObjectExt.bindVertexArrayOES(this.implicitCoverCurveVAO); - this.initImplicitCoverCurveVAO(objectIndex, instanceRange); + this.initImplicitCoverCurveVAO(pass, objectIndex, instanceRange); // Draw direct curve parts. this.setTransformUniform(directCurveProgram.uniforms, objectIndex); this.setFramebufferSizeUniform(directCurveProgram.uniforms); this.setHintsUniform(directCurveProgram.uniforms); - if (renderingMode === 'color') - this.setPathColorsUniform(objectIndex, directCurveProgram.uniforms, 0); + this.setPathColorsUniform(objectIndex, directCurveProgram.uniforms, 0); this.pathTransformBufferTextures[objectIndex].bind(gl, directCurveProgram.uniforms, 1); + if (renderingMode === 'two-pass') { + const strategy = antialiasingStrategy as MCAAMulticolorStrategy; + strategy.bindEdgeDepthTexture(gl, directCurveProgram.uniforms, 2); + } indexCount = meshData.coverCurveIndices.byteLength / UINT32_SIZE; if (!this.pathIDsAreInstanced) { gl.drawElements(gl.TRIANGLES, indexCount, gl.UNSIGNED_INT, 0); @@ -426,104 +439,104 @@ export abstract class Renderer { }, TIME_INTERVAL_DELAY); } - private initImplicitCoverCurveVAO(objectIndex: number, instanceRange: Range): void { + private initImplicitCoverCurveVAO(pass: number, objectIndex: number, instanceRange: Range): + void { const renderContext = this.renderContext; + const gl = renderContext.gl; const meshes = this.meshes[objectIndex]; - const directCurveProgram = renderContext.shaderPrograms[this.directCurveProgramName]; - renderContext.gl.useProgram(directCurveProgram.program); - renderContext.gl.bindBuffer(renderContext.gl.ARRAY_BUFFER, meshes.bVertexPositions); - renderContext.gl.vertexAttribPointer(directCurveProgram.attributes.aPosition, - 2, - renderContext.gl.FLOAT, - false, - 0, - 0); + const directCurveProgramName = this.directCurveProgramNameForPass(pass); + const directCurveProgram = renderContext.shaderPrograms[directCurveProgramName]; + gl.useProgram(directCurveProgram.program); + gl.bindBuffer(gl.ARRAY_BUFFER, meshes.bVertexPositions); + gl.vertexAttribPointer(directCurveProgram.attributes.aPosition, 2, gl.FLOAT, false, 0, 0); if (this.pathIDsAreInstanced) - renderContext.gl.bindBuffer(renderContext.gl.ARRAY_BUFFER, this.instancedPathIDVBO); + gl.bindBuffer(gl.ARRAY_BUFFER, this.instancedPathIDVBO); else - renderContext.gl.bindBuffer(renderContext.gl.ARRAY_BUFFER, meshes.bVertexPathIDs); - renderContext.gl.vertexAttribPointer(directCurveProgram.attributes.aPathID, - 1, - renderContext.gl.UNSIGNED_SHORT, - false, - 0, - instanceRange.start * UINT16_SIZE); + gl.bindBuffer(gl.ARRAY_BUFFER, meshes.bVertexPathIDs); + gl.vertexAttribPointer(directCurveProgram.attributes.aPathID, + 1, + gl.UNSIGNED_SHORT, + false, + 0, + instanceRange.start * UINT16_SIZE); if (this.pathIDsAreInstanced) { renderContext.instancedArraysExt .vertexAttribDivisorANGLE(directCurveProgram.attributes.aPathID, 1); } - renderContext.gl.bindBuffer(renderContext.gl.ARRAY_BUFFER, meshes.bVertexLoopBlinnData); - renderContext.gl.vertexAttribPointer(directCurveProgram.attributes.aTexCoord, - 2, - renderContext.gl.UNSIGNED_BYTE, - false, - B_LOOP_BLINN_DATA_SIZE, - B_LOOP_BLINN_DATA_TEX_COORD_OFFSET); - renderContext.gl.vertexAttribPointer(directCurveProgram.attributes.aSign, - 1, - renderContext.gl.BYTE, - false, - B_LOOP_BLINN_DATA_SIZE, - B_LOOP_BLINN_DATA_SIGN_OFFSET); - renderContext.gl.enableVertexAttribArray(directCurveProgram.attributes.aPosition); - renderContext.gl.enableVertexAttribArray(directCurveProgram.attributes.aTexCoord); - renderContext.gl.enableVertexAttribArray(directCurveProgram.attributes.aPathID); - renderContext.gl.enableVertexAttribArray(directCurveProgram.attributes.aSign); - renderContext.gl.bindBuffer(renderContext.gl.ELEMENT_ARRAY_BUFFER, - meshes.coverCurveIndices); + gl.bindBuffer(gl.ARRAY_BUFFER, meshes.bVertexLoopBlinnData); + gl.vertexAttribPointer(directCurveProgram.attributes.aTexCoord, + 2, + gl.UNSIGNED_BYTE, + false, + B_LOOP_BLINN_DATA_SIZE, + B_LOOP_BLINN_DATA_TEX_COORD_OFFSET); + gl.vertexAttribPointer(directCurveProgram.attributes.aSign, + 1, + gl.BYTE, + false, + B_LOOP_BLINN_DATA_SIZE, + B_LOOP_BLINN_DATA_SIGN_OFFSET); + gl.enableVertexAttribArray(directCurveProgram.attributes.aPosition); + gl.enableVertexAttribArray(directCurveProgram.attributes.aTexCoord); + gl.enableVertexAttribArray(directCurveProgram.attributes.aPathID); + gl.enableVertexAttribArray(directCurveProgram.attributes.aSign); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, meshes.coverCurveIndices); } - private initImplicitCoverInteriorVAO(objectIndex: number, instanceRange: Range): void { + private initImplicitCoverInteriorVAO(pass: number, + objectIndex: number, + instanceRange: Range): + void { const renderContext = this.renderContext; + const gl = renderContext.gl; const meshes = this.meshes[objectIndex]; - const directInteriorProgram = renderContext.shaderPrograms[this.directInteriorProgramName]; - renderContext.gl.useProgram(directInteriorProgram.program); - renderContext.gl.bindBuffer(renderContext.gl.ARRAY_BUFFER, meshes.bVertexPositions); - renderContext.gl.vertexAttribPointer(directInteriorProgram.attributes.aPosition, - 2, - renderContext.gl.FLOAT, - false, - 0, - 0); + const directInteriorProgramName = this.directInteriorProgramNameForPass(pass); + const directInteriorProgram = renderContext.shaderPrograms[directInteriorProgramName]; + gl.useProgram(directInteriorProgram.program); + gl.bindBuffer(gl.ARRAY_BUFFER, meshes.bVertexPositions); + gl.vertexAttribPointer(directInteriorProgram.attributes.aPosition, + 2, + gl.FLOAT, + false, + 0, + 0); if (this.pathIDsAreInstanced) - renderContext.gl.bindBuffer(renderContext.gl.ARRAY_BUFFER, this.instancedPathIDVBO); + gl.bindBuffer(gl.ARRAY_BUFFER, this.instancedPathIDVBO); else - renderContext.gl.bindBuffer(renderContext.gl.ARRAY_BUFFER, meshes.bVertexPathIDs); - renderContext.gl.vertexAttribPointer(directInteriorProgram.attributes.aPathID, - 1, - renderContext.gl.UNSIGNED_SHORT, - false, - 0, - instanceRange.start * UINT16_SIZE); + gl.bindBuffer(gl.ARRAY_BUFFER, meshes.bVertexPathIDs); + gl.vertexAttribPointer(directInteriorProgram.attributes.aPathID, + 1, + gl.UNSIGNED_SHORT, + false, + 0, + instanceRange.start * UINT16_SIZE); if (this.pathIDsAreInstanced) { renderContext.instancedArraysExt .vertexAttribDivisorANGLE(directInteriorProgram.attributes.aPathID, 1); } - renderContext.gl.enableVertexAttribArray(directInteriorProgram.attributes.aPosition); - renderContext.gl.enableVertexAttribArray(directInteriorProgram.attributes.aPathID); - renderContext.gl.bindBuffer(renderContext.gl.ELEMENT_ARRAY_BUFFER, - meshes.coverInteriorIndices); + gl.enableVertexAttribArray(directInteriorProgram.attributes.aPosition); + gl.enableVertexAttribArray(directInteriorProgram.attributes.aPathID); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, meshes.coverInteriorIndices); } private initInstancedPathIDVBO(): void { const renderContext = this.renderContext; + const gl = renderContext.gl; const pathIDs = new Uint16Array(MAX_PATHS); for (let pathIndex = 0; pathIndex < MAX_PATHS; pathIndex++) pathIDs[pathIndex] = pathIndex + 1; this.instancedPathIDVBO = renderContext.gl.createBuffer(); - renderContext.gl.bindBuffer(renderContext.gl.ARRAY_BUFFER, this.instancedPathIDVBO); - renderContext.gl.bufferData(renderContext.gl.ARRAY_BUFFER, - pathIDs, - renderContext.gl.STATIC_DRAW); - renderContext.gl.bindBuffer(renderContext.gl.ARRAY_BUFFER, null); + gl.bindBuffer(gl.ARRAY_BUFFER, this.instancedPathIDVBO); + gl.bufferData(gl.ARRAY_BUFFER, pathIDs, gl.STATIC_DRAW); + gl.bindBuffer(gl.ARRAY_BUFFER, null); } private setTransformUniform(uniforms: UniformMap, objectIndex: number) { diff --git a/demo/client/src/shader-loader.ts b/demo/client/src/shader-loader.ts index b98db7fc..081637ac 100644 --- a/demo/client/src/shader-loader.ts +++ b/demo/client/src/shader-loader.ts @@ -25,11 +25,12 @@ export interface ShaderMap { mcaaLine: T; mcaaCurve: T; ssaaSubpixelResolve: T; - xcaaEdgeDetect: T; xcaaMonoResolve: T; xcaaMonoSubpixelResolve: T; - xcaaMultiDirectCurve: T; - xcaaMultiDirectInterior: T; + xcaaMultiBGDirectCurve: T; + xcaaMultiBGDirectInterior: T; + xcaaMultiEdgeMaskCurve: T; + xcaaMultiEdgeMaskLine: T; xcaaMultiResolve: T; } @@ -47,7 +48,6 @@ export const SHADER_NAMES: Array> = [ 'direct3DCurve', 'direct3DInterior', 'ssaaSubpixelResolve', - 'xcaaEdgeDetect', 'mcaaCover', 'mcaaLine', 'mcaaCurve', @@ -55,8 +55,10 @@ export const SHADER_NAMES: Array> = [ 'ecaaCurve', 'xcaaMonoResolve', 'xcaaMonoSubpixelResolve', - 'xcaaMultiDirectCurve', - 'xcaaMultiDirectInterior', + 'xcaaMultiBGDirectCurve', + 'xcaaMultiBGDirectInterior', + 'xcaaMultiEdgeMaskCurve', + 'xcaaMultiEdgeMaskLine', 'xcaaMultiResolve', 'demo3DDistantGlyph', 'demo3DMonument', @@ -115,10 +117,6 @@ const SHADER_URLS: ShaderMap = { fragment: "/glsl/gles2/ssaa-subpixel-resolve.fs.glsl", vertex: "/glsl/gles2/ssaa-subpixel-resolve.vs.glsl", }, - xcaaEdgeDetect: { - fragment: "/glsl/gles2/xcaa-edge-detect.fs.glsl", - vertex: "/glsl/gles2/xcaa-edge-detect.vs.glsl", - }, xcaaMonoResolve: { fragment: "/glsl/gles2/xcaa-mono-resolve.fs.glsl", vertex: "/glsl/gles2/xcaa-mono-resolve.vs.glsl", @@ -127,13 +125,21 @@ const SHADER_URLS: ShaderMap = { fragment: "/glsl/gles2/xcaa-mono-subpixel-resolve.fs.glsl", vertex: "/glsl/gles2/xcaa-mono-subpixel-resolve.vs.glsl", }, - xcaaMultiDirectCurve: { - fragment: "/glsl/gles2/xcaa-multi-direct-curve.fs.glsl", - vertex: "/glsl/gles2/xcaa-multi-direct-curve.vs.glsl", + xcaaMultiBGDirectCurve: { + fragment: "/glsl/gles2/xcaa-multi-bg-direct-curve.fs.glsl", + vertex: "/glsl/gles2/xcaa-multi-bg-direct-curve.vs.glsl", }, - xcaaMultiDirectInterior: { - fragment: "/glsl/gles2/xcaa-multi-direct-interior.fs.glsl", - vertex: "/glsl/gles2/xcaa-multi-direct-interior.vs.glsl", + xcaaMultiBGDirectInterior: { + fragment: "/glsl/gles2/xcaa-multi-bg-direct-interior.fs.glsl", + vertex: "/glsl/gles2/xcaa-multi-bg-direct-interior.vs.glsl", + }, + xcaaMultiEdgeMaskCurve: { + fragment: "/glsl/gles2/xcaa-multi-edge-mask-curve.fs.glsl", + vertex: "/glsl/gles2/mcaa-curve.vs.glsl", + }, + xcaaMultiEdgeMaskLine: { + fragment: "/glsl/gles2/xcaa-multi-edge-mask-line.fs.glsl", + vertex: "/glsl/gles2/mcaa-line.vs.glsl", }, xcaaMultiResolve: { fragment: "/glsl/gles2/xcaa-multi-resolve.fs.glsl", diff --git a/demo/client/src/ssaa-strategy.ts b/demo/client/src/ssaa-strategy.ts index 7940594a..7772a5ab 100644 --- a/demo/client/src/ssaa-strategy.ts +++ b/demo/client/src/ssaa-strategy.ts @@ -38,6 +38,7 @@ export default class SSAAStrategy extends AntialiasingStrategy { setFramebufferSize(renderer: Renderer) { const renderContext = renderer.renderContext; + const gl = renderContext.gl; this.destFramebufferSize = glmatrix.vec2.clone(renderer.destAllocatedSize); @@ -46,28 +47,28 @@ export default class SSAAStrategy extends AntialiasingStrategy { this.destFramebufferSize, this.supersampleScale); - this.supersampledColorTexture = unwrapNull(renderContext.gl.createTexture()); - renderContext.gl.activeTexture(renderContext.gl.TEXTURE0); - renderContext.gl.bindTexture(renderContext.gl.TEXTURE_2D, this.supersampledColorTexture); - renderContext.gl.texImage2D(renderContext.gl.TEXTURE_2D, - 0, - renderContext.colorAlphaFormat, - this.supersampledFramebufferSize[0], - this.supersampledFramebufferSize[1], - 0, - renderContext.colorAlphaFormat, - renderContext.gl.UNSIGNED_BYTE, - null); - setTextureParameters(renderContext.gl, renderContext.gl.LINEAR); + this.supersampledColorTexture = unwrapNull(gl.createTexture()); + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, this.supersampledColorTexture); + gl.texImage2D(gl.TEXTURE_2D, + 0, + renderContext.colorAlphaFormat, + this.supersampledFramebufferSize[0], + this.supersampledFramebufferSize[1], + 0, + renderContext.colorAlphaFormat, + gl.UNSIGNED_BYTE, + null); + setTextureParameters(gl, gl.LINEAR); this.supersampledDepthTexture = - createFramebufferDepthTexture(renderContext.gl, this.supersampledFramebufferSize); + createFramebufferDepthTexture(gl, this.supersampledFramebufferSize); - this.supersampledFramebuffer = createFramebuffer(renderContext.gl, + this.supersampledFramebuffer = createFramebuffer(gl, this.supersampledColorTexture, this.supersampledDepthTexture); - renderContext.gl.bindFramebuffer(renderContext.gl.FRAMEBUFFER, null); + gl.bindFramebuffer(gl.FRAMEBUFFER, null); } get transform(): glmatrix.mat4 { @@ -79,7 +80,7 @@ export default class SSAAStrategy extends AntialiasingStrategy { return transform; } - prepare(renderer: Renderer) { + prepareForDirectRendering(renderer: Renderer) { const renderContext = renderer.renderContext; const gl = renderContext.gl; @@ -95,13 +96,12 @@ export default class SSAAStrategy extends AntialiasingStrategy { resolve(renderer: Renderer) { const renderContext = renderer.renderContext; - renderContext.gl.bindFramebuffer(renderContext.gl.FRAMEBUFFER, renderer.destFramebuffer); - renderContext.gl.viewport(0, - 0, - renderer.destAllocatedSize[0], - renderer.destAllocatedSize[1]); - renderContext.gl.disable(renderContext.gl.DEPTH_TEST); - renderContext.gl.disable(renderContext.gl.BLEND); + const gl = renderContext.gl; + + gl.bindFramebuffer(gl.FRAMEBUFFER, renderer.destFramebuffer); + gl.viewport(0, 0, renderer.destAllocatedSize[0], renderer.destAllocatedSize[1]); + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.BLEND); // Set up the blit program VAO. let resolveProgram; @@ -109,23 +109,19 @@ export default class SSAAStrategy extends AntialiasingStrategy { resolveProgram = renderContext.shaderPrograms.ssaaSubpixelResolve; else resolveProgram = renderContext.shaderPrograms.blit; - renderContext.gl.useProgram(resolveProgram.program); + gl.useProgram(resolveProgram.program); renderContext.initQuadVAO(resolveProgram.attributes); // Resolve framebuffer. - renderContext.gl.activeTexture(renderContext.gl.TEXTURE0); - renderContext.gl.bindTexture(renderContext.gl.TEXTURE_2D, this.supersampledColorTexture); - renderContext.gl.uniform1i(resolveProgram.uniforms.uSource, 0); - renderContext.gl.uniform2i(resolveProgram.uniforms.uSourceDimensions, - this.supersampledFramebufferSize[0], - this.supersampledFramebufferSize[1]); + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, this.supersampledColorTexture); + gl.uniform1i(resolveProgram.uniforms.uSource, 0); + gl.uniform2i(resolveProgram.uniforms.uSourceDimensions, + this.supersampledFramebufferSize[0], + this.supersampledFramebufferSize[1]); renderer.setTransformAndTexScaleUniformsForDest(resolveProgram.uniforms); - renderContext.gl.bindBuffer(renderContext.gl.ELEMENT_ARRAY_BUFFER, - renderContext.quadElementsBuffer); - renderContext.gl.drawElements(renderContext.gl.TRIANGLES, - 6, - renderContext.gl.UNSIGNED_BYTE, - 0); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, renderContext.quadElementsBuffer); + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0); } get directRenderingMode(): DirectRenderingMode { diff --git a/demo/client/src/svg-demo.ts b/demo/client/src/svg-demo.ts index cd9df3e5..48322a06 100644 --- a/demo/client/src/svg-demo.ts +++ b/demo/client/src/svg-demo.ts @@ -115,10 +115,8 @@ class SVGDemoRenderer extends Renderer { camera: OrthographicCamera; get destAllocatedSize(): glmatrix.vec2 { - return glmatrix.vec2.clone([ - this.renderContext.canvas.width, - this.renderContext.canvas.height, - ]); + const canvas = this.renderContext.canvas; + return glmatrix.vec2.clone([canvas.width, canvas.height]); } get destFramebuffer(): WebGLFramebuffer | null { @@ -183,15 +181,15 @@ class SVGDemoRenderer extends Renderer { return transform; } - protected get directCurveProgramName(): keyof ShaderMap { - if (this.antialiasingStrategy instanceof XCAAStrategy) - return 'xcaaMultiDirectCurve'; + protected directCurveProgramNameForPass(pass: number): keyof ShaderMap { + if (this.antialiasingStrategy instanceof XCAAStrategy && pass === 0) + return 'xcaaMultiBGDirectCurve'; return 'directCurve'; } - protected get directInteriorProgramName(): keyof ShaderMap { - if (this.antialiasingStrategy instanceof XCAAStrategy) - return 'xcaaMultiDirectInterior'; + protected directInteriorProgramNameForPass(pass: number): keyof ShaderMap { + if (this.antialiasingStrategy instanceof XCAAStrategy && pass === 0) + return 'xcaaMultiBGDirectInterior'; return 'directInterior'; } diff --git a/demo/client/src/svg-loader.ts b/demo/client/src/svg-loader.ts index 5b84e6ce..aff69a3a 100644 --- a/demo/client/src/svg-loader.ts +++ b/demo/client/src/svg-loader.ts @@ -18,6 +18,8 @@ import {panic, unwrapNull} from "./utils"; export const BUILTIN_SVG_URI: string = "/svg/demo"; +const parseColor = require('parse-color'); + const PARTITION_SVG_PATHS_ENDPOINT_URL: string = "/partition-svg-paths"; /// The minimum size of a stroke. @@ -105,9 +107,9 @@ export class SVGLoader { if (element instanceof SVGPathElement) { const style = window.getComputedStyle(element); - if (style.fill !== 'none') + if (hasRenderingOperation(style.fill)) this.pathInstances.push({ element: element, stroke: 'fill' }); - if (style.stroke !== 'none') { + if (hasRenderingOperation(style.stroke)) { this.pathInstances.push({ element: element, stroke: parseInt(style.strokeWidth!, 10), @@ -159,3 +161,7 @@ export class SVGLoader { this.bounds = glmatrix.vec4.clone([minX, minY, maxX, maxY]); } } + +function hasRenderingOperation(style: string | null): boolean { + return style != null && style !== 'none' && parseColor(style).rgba[3] > 0.0; +} diff --git a/demo/client/src/text-renderer.ts b/demo/client/src/text-renderer.ts index e15d1a51..d9f201bc 100644 --- a/demo/client/src/text-renderer.ts +++ b/demo/client/src/text-renderer.ts @@ -109,14 +109,6 @@ export abstract class TextRenderer extends Renderer { return glmatrix.vec2.create(); } - protected get directCurveProgramName(): keyof ShaderMap { - return 'directCurve'; - } - - protected get directInteriorProgramName(): keyof ShaderMap { - return 'directInterior'; - } - protected get depthFunction(): number { return this.renderContext.gl.GREATER; } @@ -272,4 +264,12 @@ export abstract class TextRenderer extends Renderer { this.displayPixelsPerUnit, this.renderContext.useHinting); } + + protected directCurveProgramNameForPass(pass: number): keyof ShaderMap { + return 'directCurve'; + } + + protected directInteriorProgramNameForPass(pass: number): keyof ShaderMap { + return 'directInterior'; + } } diff --git a/demo/client/src/xcaa-strategy.ts b/demo/client/src/xcaa-strategy.ts index bf0072ee..3494a3d2 100644 --- a/demo/client/src/xcaa-strategy.ts +++ b/demo/client/src/xcaa-strategy.ts @@ -47,9 +47,9 @@ export abstract class XCAAStrategy extends AntialiasingStrategy { protected resolveVAO: WebGLVertexArrayObject; protected aaAlphaTexture: WebGLTexture; + protected aaFramebuffer: WebGLFramebuffer; private directFramebuffer: WebGLFramebuffer; - private aaFramebuffer: WebGLFramebuffer; constructor(level: number, subpixelAA: SubpixelAAType) { super(); @@ -66,7 +66,6 @@ export abstract class XCAAStrategy extends AntialiasingStrategy { attachMeshes(renderer: Renderer) { const renderContext = renderer.renderContext; - this.createEdgeDetectVAO(renderContext); this.createResolveVAO(renderer); } @@ -78,13 +77,12 @@ export abstract class XCAAStrategy extends AntialiasingStrategy { this.destFramebufferSize, this.supersampleScale); - this.initDirectFramebuffer(renderer); + this.initDirectFramebufferIfNecessary(renderer); this.initAAAlphaFramebuffer(renderer); - this.initEdgeDetectFramebuffer(renderer); renderContext.gl.bindFramebuffer(renderContext.gl.FRAMEBUFFER, null); } - prepare(renderer: Renderer) { + prepareForDirectRendering(renderer: Renderer) { const renderContext = renderer.renderContext; const gl = renderContext.gl; @@ -105,14 +103,17 @@ export abstract class XCAAStrategy extends AntialiasingStrategy { } antialias(renderer: Renderer) { - // Detect edges if necessary. - this.detectEdgesIfNecessary(renderer); + // Perform early preparations. + this.createPathBoundsBufferTexture(renderer); + + // Mask edges if necessary. + this.maskEdgesIfNecessary(renderer); // Set up antialiasing. this.prepareAA(renderer); // Clear. - this.clear(renderer); + this.clearForAA(renderer); } resolve(renderer: Renderer) { @@ -124,20 +125,14 @@ export abstract class XCAAStrategy extends AntialiasingStrategy { return glmatrix.mat4.create(); } - protected initDirectFramebuffer(renderer: Renderer) { + protected initDirectFramebufferIfNecessary(renderer: Renderer) { const renderContext = renderer.renderContext; const gl = renderContext.gl; - let textureFormat; - if (this.directRenderingMode === 'pathID') - textureFormat = gl.RGBA; - else - textureFormat = renderContext.colorAlphaFormat; - this.directTexture = createFramebufferColorTexture(gl, this.destFramebufferSize, - textureFormat); - this.directFramebuffer = createFramebuffer(renderContext.gl, + renderContext.colorAlphaFormat); + this.directFramebuffer = createFramebuffer(gl, this.directTexture, this.directDepthTexture); } @@ -159,8 +154,6 @@ export abstract class XCAAStrategy extends AntialiasingStrategy { this.supersampledFramebufferSize[1]); renderContext.gl.scissor(0, 0, usedSize[0], usedSize[1]); renderContext.gl.enable(renderContext.gl.SCISSOR_TEST); - - this.createPathBoundsBufferTexture(renderer); } protected setAAState(renderer: Renderer) { @@ -200,8 +193,7 @@ export abstract class XCAAStrategy extends AntialiasingStrategy { gl.viewport(0, 0, this.destFramebufferSize[0], this.destFramebufferSize[1]); gl.scissor(0, 0, usedSize[0], usedSize[1]); gl.enable(gl.SCISSOR_TEST); - gl.disable(gl.DEPTH_TEST); - gl.disable(gl.BLEND); + this.setDepthAndBlendModeForResolve(renderContext); // Clear out the resolve buffer, if necessary. this.clearForResolve(renderer); @@ -224,15 +216,25 @@ export abstract class XCAAStrategy extends AntialiasingStrategy { if (renderer.fgColor != null) gl.uniform4fv(resolveProgram.uniforms.uFGColor, renderer.fgColor); renderer.setTransformSTAndTexScaleUniformsForDest(resolveProgram.uniforms); + this.setAdditionalStateForResolveIfNecessary(renderer, resolveProgram, 1); gl.drawElements(renderContext.gl.TRIANGLES, 6, renderContext.gl.UNSIGNED_BYTE, 0); renderContext.vertexArrayObjectExt.bindVertexArrayOES(null); } - protected abstract clear(renderer: Renderer): void; + protected setDepthAndBlendModeForResolve(renderContext: RenderContext): void { + const gl = renderContext.gl; + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.BLEND); + } + + protected setAdditionalStateForResolveIfNecessary(renderer: Renderer, + resolveProgram: PathfinderShaderProgram, + firstFreeTextureUnit: number): + void {} + + protected abstract clearForAA(renderer: Renderer): void; protected abstract getResolveProgram(renderContext: RenderContext): PathfinderShaderProgram; - protected abstract initEdgeDetectFramebuffer(renderer: Renderer): void; - protected abstract createEdgeDetectVAO(renderContext: RenderContext): void; - protected abstract detectEdgesIfNecessary(renderer: Renderer): void; + protected abstract maskEdgesIfNecessary(renderer: Renderer): void; protected abstract setAADepthState(renderer: Renderer): void; protected abstract clearForResolve(renderer: Renderer): void; @@ -323,7 +325,7 @@ export abstract class MCAAStrategy extends XCAAStrategy { renderContext.gl.blendFunc(renderContext.gl.ONE, renderContext.gl.ONE); renderContext.gl.enable(renderContext.gl.BLEND); - this.clear(renderer); + this.clearForAA(renderer); } protected setCoverDepthState(renderer: Renderer): void { @@ -331,6 +333,64 @@ export abstract class MCAAStrategy extends XCAAStrategy { renderContext.gl.disable(renderContext.gl.DEPTH_TEST); } + protected antialiasLinesWithProgram(renderer: Renderer, + lineProgram: PathfinderShaderProgram): void { + const renderContext = renderer.renderContext; + + const uniforms = lineProgram.uniforms; + this.setAAUniforms(renderer, uniforms); + + for (const direction of DIRECTIONS) { + const vao = this.lineVAOs[direction]; + renderContext.vertexArrayObjectExt.bindVertexArrayOES(vao); + + this.setBlendModeForAA(renderer, direction); + renderContext.gl.uniform1i(uniforms.uWinding, direction === 'upper' ? 1 : 0); + + const count = { + lower: renderer.meshData[0].edgeLowerLineCount, + upper: renderer.meshData[0].edgeUpperLineCount, + }[direction]; + renderContext.instancedArraysExt + .drawElementsInstancedANGLE(renderContext.gl.TRIANGLES, + 6, + renderContext.gl.UNSIGNED_BYTE, + 0, + count); + } + + renderContext.vertexArrayObjectExt.bindVertexArrayOES(null); + } + + protected antialiasCurvesWithProgram(renderer: Renderer, + curveProgram: PathfinderShaderProgram): void { + const renderContext = renderer.renderContext; + + const uniforms = curveProgram.uniforms; + this.setAAUniforms(renderer, uniforms); + + for (const direction of DIRECTIONS) { + const vao = this.curveVAOs[direction]; + renderContext.vertexArrayObjectExt.bindVertexArrayOES(vao); + + this.setBlendModeForAA(renderer, direction); + renderContext.gl.uniform1i(uniforms.uWinding, direction === 'upper' ? 1 : 0); + + const count = { + lower: renderer.meshData[0].edgeLowerCurveCount, + upper: renderer.meshData[0].edgeUpperCurveCount, + }[direction]; + renderContext.instancedArraysExt + .drawElementsInstancedANGLE(renderContext.gl.TRIANGLES, + 6, + renderContext.gl.UNSIGNED_BYTE, + 0, + count); + } + + renderContext.vertexArrayObjectExt.bindVertexArrayOES(null); + } + private createCoverVAO(renderer: Renderer) { const renderContext = renderer.renderContext; @@ -561,35 +621,13 @@ export abstract class MCAAStrategy extends XCAAStrategy { private antialiasLines(renderer: Renderer) { const renderContext = renderer.renderContext; - this.setAAState(renderer); const lineProgram = renderContext.shaderPrograms.mcaaLine; - renderContext.gl.useProgram(lineProgram.program); - const uniforms = lineProgram.uniforms; - this.setAAUniforms(renderer, uniforms); - for (const direction of DIRECTIONS) { - const vao = this.lineVAOs[direction]; - renderContext.vertexArrayObjectExt.bindVertexArrayOES(vao); - - this.setBlendModeForAA(renderer, direction); - renderContext.gl.uniform1i(uniforms.uWinding, direction === 'upper' ? 1 : 0); - - const count = { - lower: renderer.meshData[0].edgeLowerLineCount, - upper: renderer.meshData[0].edgeUpperLineCount, - }[direction]; - renderContext.instancedArraysExt - .drawElementsInstancedANGLE(renderContext.gl.TRIANGLES, - 6, - renderContext.gl.UNSIGNED_BYTE, - 0, - count); - } - - renderContext.vertexArrayObjectExt.bindVertexArrayOES(null); + // FIXME(pcwalton): Refactor. + this.antialiasLinesWithProgram(renderer, lineProgram); } private antialiasCurves(renderer: Renderer) { @@ -600,30 +638,10 @@ export abstract class MCAAStrategy extends XCAAStrategy { const curveProgram = renderContext.shaderPrograms.mcaaCurve; renderContext.gl.useProgram(curveProgram.program); - const uniforms = curveProgram.uniforms; - this.setAAUniforms(renderer, uniforms); - for (const direction of DIRECTIONS) { - const vao = this.curveVAOs[direction]; - renderContext.vertexArrayObjectExt.bindVertexArrayOES(vao); - - this.setBlendModeForAA(renderer, direction); - renderContext.gl.uniform1i(uniforms.uWinding, direction === 'upper' ? 1 : 0); - - const count = { - lower: renderer.meshData[0].edgeLowerCurveCount, - upper: renderer.meshData[0].edgeUpperCurveCount, - }[direction]; - renderContext.instancedArraysExt - .drawElementsInstancedANGLE(renderContext.gl.TRIANGLES, - 6, - renderContext.gl.UNSIGNED_BYTE, - 0, - count); - } - - renderContext.vertexArrayObjectExt.bindVertexArrayOES(null); + this.antialiasCurvesWithProgram(renderer, curveProgram); } + } export class ECAAStrategy extends XCAAStrategy { @@ -663,13 +681,9 @@ export class ECAAStrategy extends XCAAStrategy { return renderContext.shaderPrograms.xcaaMonoResolve; } - protected initEdgeDetectFramebuffer(renderer: Renderer) {} + protected maskEdgesIfNecessary(renderer: Renderer) {} - protected createEdgeDetectVAO(renderContext: RenderContext) {} - - protected detectEdgesIfNecessary(renderer: Renderer) {} - - protected clear(renderer: Renderer) { + protected clearForAA(renderer: Renderer) { const renderContext = renderer.renderContext; renderContext.gl.clearColor(0.0, 0.0, 0.0, 0.0); renderContext.gl.clearDepth(0.0); @@ -910,13 +924,9 @@ export class MCAAMonochromeStrategy extends MCAAStrategy { return renderContext.shaderPrograms.xcaaMonoResolve; } - protected initEdgeDetectFramebuffer(renderer: Renderer) {} + protected maskEdgesIfNecessary(renderer: Renderer) {} - protected createEdgeDetectVAO(renderContext: RenderContext) {} - - protected detectEdgesIfNecessary(renderer: Renderer) {} - - protected clear(renderer: Renderer) { + protected clearForAA(renderer: Renderer) { const renderContext = renderer.renderContext; renderContext.gl.clearColor(0.0, 0.0, 0.0, 0.0); renderContext.gl.clearDepth(0.0); @@ -975,8 +985,8 @@ export class AdaptiveMonochromeXCAAStrategy implements AntialiasingStrategy { return this.mcaaStrategy.transform; } - prepare(renderer: Renderer): void { - this.getAppropriateStrategy(renderer).prepare(renderer); + prepareForDirectRendering(renderer: Renderer): void { + this.getAppropriateStrategy(renderer).prepareForDirectRendering(renderer); } antialias(renderer: Renderer): void { @@ -995,120 +1005,54 @@ export class AdaptiveMonochromeXCAAStrategy implements AntialiasingStrategy { } export class MCAAMulticolorStrategy extends MCAAStrategy { - private _directDepthTexture: WebGLTexture; + private edgeMaskVAO: WebGLVertexArrayObject; - private edgeDetectFramebuffer: WebGLFramebuffer; - private edgeDetectVAO: WebGLVertexArrayObject; - private edgePathIDTexture: WebGLTexture; + bindEdgeDepthTexture(gl: WebGLRenderingContext, uniforms: UniformMap, textureUnit: number): + void { + gl.activeTexture(gl.TEXTURE0 + textureUnit); + gl.bindTexture(gl.TEXTURE_2D, this.aaDepthTexture); + gl.uniform1i(uniforms.uEdgeDepth, textureUnit); + } protected getResolveProgram(renderContext: RenderContext): PathfinderShaderProgram { return renderContext.shaderPrograms.xcaaMultiResolve; } - protected initDirectFramebuffer(renderer: Renderer) { - const renderContext = renderer.renderContext; - this._directDepthTexture = createFramebufferDepthTexture(renderContext.gl, - this.supersampledFramebufferSize); - super.initDirectFramebuffer(renderer); - } + protected initDirectFramebufferIfNecessary(renderer: Renderer) {} - protected initEdgeDetectFramebuffer(renderer: Renderer) { - const renderContext = renderer.renderContext; - this.edgePathIDTexture = createFramebufferColorTexture(renderContext.gl, - this.supersampledFramebufferSize, - renderContext.gl.RGBA); - this.edgeDetectFramebuffer = createFramebuffer(renderContext.gl, - this.edgePathIDTexture, - this.aaDepthTexture); - } - - protected createEdgeDetectVAO(renderContext: RenderContext) { - this.edgeDetectVAO = renderContext.vertexArrayObjectExt.createVertexArrayOES(); - renderContext.vertexArrayObjectExt.bindVertexArrayOES(this.edgeDetectVAO); - - const edgeDetectProgram = renderContext.shaderPrograms.xcaaEdgeDetect; - renderContext.gl.useProgram(edgeDetectProgram.program); - renderContext.initQuadVAO(edgeDetectProgram.attributes); - - renderContext.vertexArrayObjectExt.bindVertexArrayOES(null); - } - - protected detectEdgesIfNecessary(renderer: Renderer) { + protected maskEdgesIfNecessary(renderer: Renderer) { const renderContext = renderer.renderContext; const gl = renderContext.gl; - // Set state for edge detection. - const edgeDetectProgram = renderContext.shaderPrograms.xcaaEdgeDetect; - gl.bindFramebuffer(gl.FRAMEBUFFER, this.edgeDetectFramebuffer); + // Set state for edge masking. + gl.bindFramebuffer(gl.FRAMEBUFFER, this.aaFramebuffer); gl.viewport(0, 0, this.supersampledFramebufferSize[0], this.supersampledFramebufferSize[1]); + gl.colorMask(false, false, false, false); gl.depthMask(true); - gl.depthFunc(renderContext.gl.ALWAYS); - gl.enable(renderContext.gl.DEPTH_TEST); - gl.disable(renderContext.gl.BLEND); + gl.depthFunc(gl.GREATER); + gl.enable(gl.DEPTH_TEST); + gl.disable(gl.BLEND); gl.clearDepth(0.0); gl.clearColor(0.0, 0.0, 0.0, 0.0); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - // Perform edge detection. - gl.useProgram(edgeDetectProgram.program); - renderContext.vertexArrayObjectExt.bindVertexArrayOES(this.edgeDetectVAO); - renderer.setFramebufferSizeUniform(edgeDetectProgram.uniforms); - renderer.setTransformSTAndTexScaleUniformsForDest(edgeDetectProgram.uniforms); - renderer.setPathColorsUniform(0, edgeDetectProgram.uniforms, 0); - gl.activeTexture(renderContext.gl.TEXTURE1); - gl.bindTexture(renderContext.gl.TEXTURE_2D, this.directTexture); - gl.uniform1i(edgeDetectProgram.uniforms.uPathID, 1); - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, renderContext.quadElementsBuffer); - gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0); + // Perform edge masking. + const edgeMaskLineProgram = renderer.renderContext.shaderPrograms.xcaaMultiEdgeMaskLine; + gl.useProgram(edgeMaskLineProgram.program); + this.antialiasLinesWithProgram(renderer, edgeMaskLineProgram); + const edgeMaskCurveProgram = renderer.renderContext.shaderPrograms.xcaaMultiEdgeMaskCurve; + gl.useProgram(edgeMaskCurveProgram.program); + this.antialiasCurvesWithProgram(renderer, edgeMaskCurveProgram); + gl.colorMask(true, true, true, true); renderContext.vertexArrayObjectExt.bindVertexArrayOES(null); } - protected resolveAA(renderer: Renderer) { - const renderContext = renderer.renderContext; - const gl = renderContext.gl; - - // Set state for ECAA resolve. - const usedSize = renderer.destUsedSize; - renderContext.gl.bindFramebuffer(renderContext.gl.FRAMEBUFFER, renderer.destFramebuffer); - renderContext.gl.viewport(0, 0, this.destFramebufferSize[0], this.destFramebufferSize[1]); - renderContext.gl.scissor(0, 0, usedSize[0], usedSize[1]); - renderContext.gl.enable(renderContext.gl.SCISSOR_TEST); - renderContext.gl.disable(renderContext.gl.DEPTH_TEST); - renderContext.gl.disable(renderContext.gl.BLEND); - - // Resolve. - const resolveProgram = this.getResolveProgram(renderContext); - gl.useProgram(resolveProgram.program); - renderContext.vertexArrayObjectExt.bindVertexArrayOES(this.resolveVAO); - gl.uniform2i(resolveProgram.uniforms.uFramebufferSize, - this.destFramebufferSize[0], - this.destFramebufferSize[1]); - renderContext.gl.activeTexture(renderContext.gl.TEXTURE0); - renderContext.gl.bindTexture(renderContext.gl.TEXTURE_2D, this.aaAlphaTexture); - renderContext.gl.uniform1i(resolveProgram.uniforms.uAAAlpha, 0); - renderContext.gl.uniform2i(resolveProgram.uniforms.uAAAlphaDimensions, - this.supersampledFramebufferSize[0], - this.supersampledFramebufferSize[1]); - renderContext.gl.activeTexture(renderContext.gl.TEXTURE1); - renderContext.gl.bindTexture(renderContext.gl.TEXTURE_2D, this.edgePathIDTexture); - renderContext.gl.uniform1i(resolveProgram.uniforms.uBGFGPathID, 1); - renderer.setPathColorsUniform(0, resolveProgram.uniforms, 2); - renderer.setTransformSTAndTexScaleUniformsForDest(resolveProgram.uniforms); - renderContext.gl.drawElements(renderContext.gl.TRIANGLES, - 6, - renderContext.gl.UNSIGNED_BYTE, - 0); - renderContext.vertexArrayObjectExt.bindVertexArrayOES(null); - - renderContext.gl.bindFramebuffer(renderContext.gl.FRAMEBUFFER, null); - } - protected setCoverDepthState(renderer: Renderer) { const renderContext = renderer.renderContext; renderContext.gl.depthMask(false); @@ -1116,26 +1060,56 @@ export class MCAAMulticolorStrategy extends MCAAStrategy { renderContext.gl.enable(renderContext.gl.DEPTH_TEST); } - protected clear(renderer: Renderer) { + protected clearForAA(renderer: Renderer) { const renderContext = renderer.renderContext; - renderContext.gl.clearColor(0.0, 0.0, 0.0, 0.0); - renderContext.gl.clear(renderContext.gl.COLOR_BUFFER_BIT); + const gl = renderContext.gl; + + gl.clearColor(0.0, 0.0, 0.0, 0.0); + gl.clear(gl.COLOR_BUFFER_BIT); } protected setAADepthState(renderer: Renderer) { const renderContext = renderer.renderContext; - renderContext.gl.depthMask(false); - renderContext.gl.depthFunc(renderContext.gl.EQUAL); - renderContext.gl.enable(renderContext.gl.DEPTH_TEST); + const gl = renderContext.gl; + + gl.depthMask(false); + gl.depthFunc(gl.EQUAL); + gl.enable(gl.DEPTH_TEST); + } + + protected setDepthAndBlendModeForResolve(renderContext: RenderContext): void { + const gl = renderContext.gl; + + gl.depthMask(true); + gl.depthFunc(gl.GREATER); + gl.enable(gl.DEPTH_TEST); + + gl.blendEquation(gl.FUNC_ADD); + gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE); + gl.enable(gl.BLEND); } protected clearForResolve(renderer: Renderer) {} - get directRenderingMode(): DirectRenderingMode { - return 'pathID'; + protected setAdditionalStateForResolveIfNecessary(renderer: Renderer, + resolveProgram: PathfinderShaderProgram, + firstFreeTextureUnit: number): + void { + const renderContext = renderer.renderContext; + const gl = renderContext.gl; + + gl.activeTexture(gl.TEXTURE0 + firstFreeTextureUnit + 0); + gl.bindTexture(gl.TEXTURE_2D, this.aaDepthTexture); + gl.uniform1i(resolveProgram.uniforms.uAADepth, firstFreeTextureUnit + 0); + + renderer.setPathColorsUniform(0, resolveProgram.uniforms, firstFreeTextureUnit + 1); } - protected get directDepthTexture(): WebGLTexture { - return this._directDepthTexture; + get directRenderingMode(): DirectRenderingMode { + return 'two-pass'; + } + + protected get directDepthTexture(): WebGLTexture | null { + return null; } } diff --git a/shaders/gles2/common.inc.glsl b/shaders/gles2/common.inc.glsl index d58fc8e3..bb4684b9 100644 --- a/shaders/gles2/common.inc.glsl +++ b/shaders/gles2/common.inc.glsl @@ -109,6 +109,11 @@ float convertPathIndexToWindowDepthValue(int pathIndex) { return float(pathIndex) / float(MAX_PATHS); } +int convertWindowDepthValueToPathIndex(float depthValue) { + float pathIndex = floor(depthValue * float(MAX_PATHS)); + return int(pathIndex); +} + bool computeMCAAQuadPosition(out vec2 outPosition, inout vec2 leftPosition, inout vec2 rightPosition, @@ -261,6 +266,37 @@ float computeCoverage(vec2 p0, vec2 dp, vec2 center, float winding) { return abs(area) * winding * 0.5; } +// FIXME(pcwalton): Combine with `computeCoverage`. +bool isPartiallyCovered(vec2 p0, vec2 dp, vec2 center, float winding) { + // Set some flags. + bool slopeNegative = dp.y < -0.001; + bool slopeZero = abs(dp.y) < 0.001; + + // Determine the bounds of this pixel. + vec4 pixelExtents = center.xxyy + vec4(-0.5, 0.5, -0.5, 0.5); + + // Set up Liang-Barsky clipping. + vec4 q = pixelExtents - p0.xxyy; + + // Use Liang-Barsky to clip to the left and right sides of this pixel. + vec2 t = clamp(q.xy / dp.xx, 0.0, 1.0); + vec2 spanP0 = p0 + dp * t.x, spanP1 = p0 + dp * t.y; + + // Likewise, clip to the to the bottom and top. + if (!slopeZero) { + vec2 tVertical = q.zw / dp.yy; + if (slopeNegative) + tVertical.xy = tVertical.yx; // FIXME(pcwalton): Can this be removed? + t = vec2(max(t.x, tVertical.x), min(t.y, tVertical.y)); + } + + // If the line doesn't pass through this pixel, detect that and bail. + // + // This should be worth a branch because it's very common for fragment blocks to all hit this + // path. + return !(t.x >= t.y || (slopeZero && (p0.y < pixelExtents.z || p0.y > pixelExtents.w))); +} + // https://www.freetype.org/freetype2/docs/reference/ft2-lcd_filtering.html float lcdFilter(float shadeL2, float shadeL1, float shade0, float shadeR1, float shadeR2) { return LCD_FILTER_FACTOR_2 * shadeL2 + diff --git a/shaders/gles2/xcaa-multi-direct-curve.fs.glsl b/shaders/gles2/xcaa-multi-bg-direct-curve.fs.glsl similarity index 57% rename from shaders/gles2/xcaa-multi-direct-curve.fs.glsl rename to shaders/gles2/xcaa-multi-bg-direct-curve.fs.glsl index 1009ba11..ba616729 100644 --- a/shaders/gles2/xcaa-multi-direct-curve.fs.glsl +++ b/shaders/gles2/xcaa-multi-bg-direct-curve.fs.glsl @@ -1,4 +1,4 @@ -// pathfinder/shaders/gles2/xcaa-multi-direct-curve.fs.glsl +// pathfinder/shaders/gles2/xcaa-multi-bg-direct-curve.fs.glsl // // Copyright (c) 2017 The Pathfinder Project Developers. // @@ -12,12 +12,23 @@ precision highp float; -varying vec2 vPathID; +uniform ivec2 uFramebufferSize; +uniform sampler2D uEdgeDepth; + +varying vec4 vColor; varying vec2 vTexCoord; varying float vSign; void main() { + vec2 center = floor(gl_FragCoord.xy); + float depth = gl_FragCoord.z; + vec2 texCoord = floor(center) / vec2(uFramebufferSize); + + // TODO(pcwalton): Remove this if possible? + if (depth >= texture2D(uEdgeDepth, texCoord).r) + discard; + float side = vTexCoord.x * vTexCoord.x - vTexCoord.y; float alpha = float(sign(side) == sign(vSign)); - gl_FragColor = vec4(vPathID, 0.0, alpha); + gl_FragColor = vec4(vColor.rgb, vColor.a * alpha); } diff --git a/shaders/gles2/xcaa-multi-direct-curve.vs.glsl b/shaders/gles2/xcaa-multi-bg-direct-curve.vs.glsl similarity index 83% rename from shaders/gles2/xcaa-multi-direct-curve.vs.glsl rename to shaders/gles2/xcaa-multi-bg-direct-curve.vs.glsl index 4befc366..78b265fe 100644 --- a/shaders/gles2/xcaa-multi-direct-curve.vs.glsl +++ b/shaders/gles2/xcaa-multi-bg-direct-curve.vs.glsl @@ -1,4 +1,4 @@ -// pathfinder/shaders/gles2/xcaa-multi-direct-curve.vs.glsl +// pathfinder/shaders/gles2/xcaa-multi-bg-direct-curve.vs.glsl // // Copyright (c) 2017 The Pathfinder Project Developers. // @@ -12,6 +12,8 @@ precision highp float; uniform mat4 uTransform; uniform vec4 uHints; +uniform ivec2 uPathColorsDimensions; +uniform sampler2D uPathColors; uniform ivec2 uPathTransformDimensions; uniform sampler2D uPathTransform; @@ -20,7 +22,7 @@ attribute vec2 aTexCoord; attribute float aPathID; attribute float aSign; -varying vec2 vPathID; +varying vec4 vColor; varying vec2 vTexCoord; varying float vSign; @@ -36,7 +38,7 @@ void main() { float depth = convertPathIndexToViewportDepthValue(pathID); gl_Position = vec4(position, depth, 1.0); - vPathID = packPathID(pathID); + vColor = fetchFloat4Data(uPathColors, pathID, uPathColorsDimensions); vTexCoord = vec2(aTexCoord) / 2.0; vSign = aSign; } diff --git a/shaders/gles2/xcaa-multi-bg-direct-interior.fs.glsl b/shaders/gles2/xcaa-multi-bg-direct-interior.fs.glsl new file mode 100644 index 00000000..adddc65c --- /dev/null +++ b/shaders/gles2/xcaa-multi-bg-direct-interior.fs.glsl @@ -0,0 +1,28 @@ +// pathfinder/shaders/gles2/xcaa-multi-bg-direct-interior.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. + +precision highp float; + +uniform ivec2 uFramebufferSize; +uniform sampler2D uEdgeDepth; + +varying vec4 vColor; + +void main() { + vec2 center = floor(gl_FragCoord.xy); + float depth = gl_FragCoord.z; + vec2 texCoord = floor(center) / vec2(uFramebufferSize); + + // TODO(pcwalton): Remove this if possible? + if (depth >= texture2D(uEdgeDepth, texCoord).r) + discard; + + gl_FragColor = vColor; +} diff --git a/shaders/gles2/xcaa-multi-direct-interior.vs.glsl b/shaders/gles2/xcaa-multi-bg-direct-interior.vs.glsl similarity index 79% rename from shaders/gles2/xcaa-multi-direct-interior.vs.glsl rename to shaders/gles2/xcaa-multi-bg-direct-interior.vs.glsl index 2b1e18c7..1b35a806 100644 --- a/shaders/gles2/xcaa-multi-direct-interior.vs.glsl +++ b/shaders/gles2/xcaa-multi-bg-direct-interior.vs.glsl @@ -1,4 +1,4 @@ -// pathfinder/shaders/gles2/xcaa-multi-direct-interior.vs.glsl +// pathfinder/shaders/gles2/xcaa-multi-bg-direct-interior.vs.glsl // // Copyright (c) 2017 The Pathfinder Project Developers. // @@ -12,13 +12,16 @@ precision highp float; uniform mat4 uTransform; uniform vec4 uHints; +uniform ivec2 uPathColorsDimensions; uniform ivec2 uPathTransformDimensions; +uniform sampler2D uPathColors; uniform sampler2D uPathTransform; attribute vec2 aPosition; attribute float aPathID; -varying vec2 vPathID; +varying vec4 vColor; +varying float vSign; void main() { int pathID = int(aPathID); @@ -32,5 +35,5 @@ void main() { float depth = convertPathIndexToViewportDepthValue(pathID); gl_Position = vec4(position, depth, 1.0); - vPathID = packPathID(pathID); + vColor = fetchFloat4Data(uPathColors, pathID, uPathColorsDimensions); } diff --git a/shaders/gles2/xcaa-multi-direct-interior.fs.glsl b/shaders/gles2/xcaa-multi-direct-interior.fs.glsl deleted file mode 100644 index c146ec97..00000000 --- a/shaders/gles2/xcaa-multi-direct-interior.fs.glsl +++ /dev/null @@ -1,17 +0,0 @@ -// pathfinder/shaders/gles2/xcaa-multi-direct-interior.fs.glsl -// -// Copyright (c) 2017 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -precision highp float; - -varying vec2 vPathID; - -void main() { - gl_FragColor = vec4(vPathID, 0.0, 1.0); -} diff --git a/shaders/gles2/xcaa-multi-edge-mask-curve.fs.glsl b/shaders/gles2/xcaa-multi-edge-mask-curve.fs.glsl new file mode 100644 index 00000000..32099a0e --- /dev/null +++ b/shaders/gles2/xcaa-multi-edge-mask-curve.fs.glsl @@ -0,0 +1,56 @@ +// pathfinder/shaders/gles2/xcaa-multi-edge-mask-curve.fs.glsl +// +// Copyright (c) 2017 The Pathfinder Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +precision highp float; + +varying vec4 vEndpoints; +varying vec2 vControlPoint; +varying float vWinding; + +// Solve the equation: +// +// x = p0x + t^2 * (p0x - 2*p1x + p2x) + t*(2*p1x - 2*p0x) +// +// We use the Citardauq Formula to avoid floating point precision issues. +float solveCurveT(float p0x, float p1x, float p2x, float x) { + float a = p0x - 2.0 * p1x + p2x; + float b = 2.0 * p1x - 2.0 * p0x; + float c = p0x - x; + return 2.0 * c / (-b - sqrt(b * b - 4.0 * a * c)); +} + +void main() { + // Unpack. + vec2 center = gl_FragCoord.xy; + vec2 p0 = vEndpoints.xy, p1 = vEndpoints.zw; + vec2 cp = vControlPoint; + + // Compute pixel extents. + vec4 pixelExtents = center.xxyy + vec4(-0.5, 0.5, -0.5, 0.5); + + // Clip the curve to the left and right edges to create a line. + // + // TODO(pcwalton): Consider clipping to the bottom and top edges properly too. (I kind of doubt + // it's worth it to do this, though, given that the maximum error doing it this way will always + // be less than a pixel, and it saves a lot of time.) + // + // FIXME(pcwalton): Factor out shared terms to avoid computing them multiple times. + vec2 t = vec2(p0.x < pixelExtents.x ? solveCurveT(p0.x, cp.x, p1.x, pixelExtents.x) : 0.0, + p1.x > pixelExtents.y ? solveCurveT(p0.x, cp.x, p1.x, pixelExtents.y) : 1.0); + + vec2 clippedP0 = mix(mix(p0, cp, t.x), mix(cp, p1, t.x), t.x); + vec2 clippedP1 = mix(mix(p0, cp, t.y), mix(cp, p1, t.y), t.y); + + // Discard if not edge. + if (!isPartiallyCovered(clippedP0, clippedP1 - clippedP0, center, vWinding)) + discard; + gl_FragColor = vec4(1.0); +} + diff --git a/shaders/gles2/xcaa-multi-edge-mask-line.fs.glsl b/shaders/gles2/xcaa-multi-edge-mask-line.fs.glsl new file mode 100644 index 00000000..750eb37e --- /dev/null +++ b/shaders/gles2/xcaa-multi-edge-mask-line.fs.glsl @@ -0,0 +1,34 @@ +// pathfinder/shaders/gles2/xcaa-multi-edge-mask-line.vs.glsl +// +// Copyright (c) 2017 The Pathfinder Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +precision highp float; + +varying vec4 vEndpoints; +varying float vWinding; + +void main() { + // Unpack. + vec2 center = gl_FragCoord.xy; + vec2 p0 = vEndpoints.xy, p1 = vEndpoints.zw; + + // Set up Liang-Barsky clipping. + vec4 pixelExtents = center.xxyy + vec4(-0.5, 0.5, -0.5, 0.5); + vec2 dp = p1 - p0; + vec4 q = pixelExtents - p0.xxyy; + + // Use Liang-Barsky to clip to the left and right sides of this pixel. + vec2 t = clamp(q.xy / dp.xx, 0.0, 1.0); + vec2 spanP0 = p0 + dp * t.x, spanP1 = p0 + dp * t.y; + + // Discard if not edge. + if (!isPartiallyCovered(p0, dp, center, vWinding)) + discard; + gl_FragColor = vec4(1.0); +} diff --git a/shaders/gles2/xcaa-multi-resolve.fs.glsl b/shaders/gles2/xcaa-multi-resolve.fs.glsl index a219584b..cd46bded 100644 --- a/shaders/gles2/xcaa-multi-resolve.fs.glsl +++ b/shaders/gles2/xcaa-multi-resolve.fs.glsl @@ -11,20 +11,17 @@ precision highp float; uniform ivec2 uPathColorsDimensions; -uniform sampler2D uBGFGPathID; uniform sampler2D uAAAlpha; +uniform sampler2D uAADepth; uniform sampler2D uPathColors; varying vec2 vTexCoord; void main() { - vec4 packedPathIDsBGFG = texture2D(uBGFGPathID, vTexCoord); - int pathIDBG = unpackPathID(packedPathIDsBGFG.xy); - int pathIDFG = unpackPathID(packedPathIDsBGFG.zw); - vec4 bgColor = fetchFloat4Data(uPathColors, pathIDBG, uPathColorsDimensions); - vec4 fgColor = fetchFloat4Data(uPathColors, pathIDFG, uPathColorsDimensions); - float alpha = clamp(texture2D(uAAAlpha, vTexCoord).r, 0.0, 1.0); - gl_FragColor = mix(bgColor, fgColor, alpha); - //gl_FragColor = vec4(vec3(alpha), 1.0); - //gl_FragColor = bgColor != fgColor ? vec4(1.0, 0.0, 0.0, 1.0) : vec4(1.0); + float edgeDepth = texture2D(uAADepth, vTexCoord).r; + int edgePathID = convertWindowDepthValueToPathIndex(edgeDepth); + vec4 edgeColor = fetchFloat4Data(uPathColors, edgePathID, uPathColorsDimensions); + float edgeAlpha = texture2D(uAAAlpha, vTexCoord).r; + gl_FragColor = vec4(edgeColor.rgb, edgeColor.a * edgeAlpha); + gl_FragDepthEXT = edgeDepth; }