diff --git a/demo/client/src/ecaa-strategy.ts b/demo/client/src/ecaa-strategy.ts index f56a0757..579558ca 100644 --- a/demo/client/src/ecaa-strategy.ts +++ b/demo/client/src/ecaa-strategy.ts @@ -28,7 +28,11 @@ interface UpperAndLower { export abstract class ECAAStrategy extends AntialiasingStrategy { constructor(level: number, subpixelAA: boolean) { super(); - this.framebufferSize = glmatrix.vec2.create(); + + this.subpixelAA = subpixelAA; + + this.supersampledFramebufferSize = glmatrix.vec2.create(); + this.destFramebufferSize = glmatrix.vec2.create(); } init(view: MonochromePathfinderView) { @@ -52,7 +56,10 @@ export abstract class ECAAStrategy extends AntialiasingStrategy { } setFramebufferSize(view: MonochromePathfinderView) { - this.framebufferSize = view.destAllocatedSize; + this.destFramebufferSize = glmatrix.vec2.clone(view.destAllocatedSize); + glmatrix.vec2.mul(this.supersampledFramebufferSize, + this.destFramebufferSize, + this.supersampleScale); this.initDirectFramebuffer(view); this.initEdgeDetectFramebuffer(view); @@ -65,8 +72,9 @@ export abstract class ECAAStrategy extends AntialiasingStrategy { } protected initDirectFramebuffer(view: MonochromePathfinderView) { - this.directColorTexture = createFramebufferColorTexture(view.gl, this.framebufferSize); - this.directPathIDTexture = createFramebufferColorTexture(view.gl, this.framebufferSize); + this.directColorTexture = createFramebufferColorTexture(view.gl, this.destFramebufferSize); + this.directPathIDTexture = createFramebufferColorTexture(view.gl, + this.destFramebufferSize); this.directFramebuffer = createFramebuffer(view.gl, view.drawBuffersExt, @@ -81,15 +89,16 @@ export abstract class ECAAStrategy extends AntialiasingStrategy { view.gl.texImage2D(view.gl.TEXTURE_2D, 0, view.gl.RGB, - this.framebufferSize[0], - this.framebufferSize[1], + this.supersampledFramebufferSize[0], + this.supersampledFramebufferSize[1], 0, view.gl.RGB, view.textureHalfFloatExt.HALF_FLOAT_OES, null); setTextureParameters(view.gl, view.gl.NEAREST); - this.aaDepthTexture = createFramebufferDepthTexture(view.gl, this.framebufferSize); + this.aaDepthTexture = createFramebufferDepthTexture(view.gl, + this.supersampledFramebufferSize); this.aaFramebuffer = createFramebuffer(view.gl, view.drawBuffersExt, @@ -219,9 +228,12 @@ export abstract class ECAAStrategy extends AntialiasingStrategy { } prepare(view: MonochromePathfinderView) { - const usedSize = view.destUsedSize; + const usedSize = this.supersampledUsedSize(view);; view.gl.bindFramebuffer(view.gl.FRAMEBUFFER, this.directFramebuffer); - view.gl.viewport(0, 0, this.framebufferSize[0], this.framebufferSize[1]); + view.gl.viewport(0, + 0, + this.supersampledFramebufferSize[0], + this.supersampledFramebufferSize[1]); view.gl.scissor(0, 0, usedSize[0], usedSize[1]); view.gl.enable(view.gl.SCISSOR_TEST); @@ -268,9 +280,12 @@ export abstract class ECAAStrategy extends AntialiasingStrategy { private cover(view: MonochromePathfinderView) { // Set state for conservative coverage. const coverProgram = view.shaderPrograms.ecaaCover; - const usedSize = view.destUsedSize; + const usedSize = this.supersampledUsedSize(view); view.gl.bindFramebuffer(view.gl.FRAMEBUFFER, this.aaFramebuffer); - view.gl.viewport(0, 0, this.framebufferSize[0], this.framebufferSize[1]); + view.gl.viewport(0, + 0, + this.supersampledFramebufferSize[0], + this.supersampledFramebufferSize[1]); view.gl.scissor(0, 0, usedSize[0], usedSize[1]); view.gl.enable(view.gl.SCISSOR_TEST); @@ -290,6 +305,7 @@ export abstract class ECAAStrategy extends AntialiasingStrategy { this.bVertexPositionBufferTexture.bind(view.gl, uniforms, 0); this.bVertexPathIDBufferTexture.bind(view.gl, uniforms, 1); view.pathTransformBufferTexture.bind(view.gl, uniforms, 2); + view.gl.uniform1f(uniforms.uScaleX, this.supersampleScale[0]); view.instancedArraysExt.drawElementsInstancedANGLE(view.gl.TRIANGLES, 6, view.gl.UNSIGNED_BYTE, @@ -299,9 +315,12 @@ export abstract class ECAAStrategy extends AntialiasingStrategy { } private setAAState(view: MonochromePathfinderView) { - const usedSize = view.destUsedSize; + const usedSize = this.supersampledUsedSize(view); view.gl.bindFramebuffer(view.gl.FRAMEBUFFER, this.aaFramebuffer); - view.gl.viewport(0, 0, this.framebufferSize[0], this.framebufferSize[1]); + view.gl.viewport(0, + 0, + this.supersampledFramebufferSize[0], + this.supersampledFramebufferSize[1]); view.gl.scissor(0, 0, usedSize[0], usedSize[1]); view.gl.enable(view.gl.SCISSOR_TEST); @@ -317,6 +336,7 @@ export abstract class ECAAStrategy extends AntialiasingStrategy { this.bVertexPositionBufferTexture.bind(view.gl, uniforms, 0); this.bVertexPathIDBufferTexture.bind(view.gl, uniforms, 1); view.pathTransformBufferTexture.bind(view.gl, uniforms, 2); + view.gl.uniform1f(uniforms.uScaleX, this.supersampleScale[0]); } private antialiasLines(view: MonochromePathfinderView) { @@ -373,7 +393,7 @@ export abstract class ECAAStrategy extends AntialiasingStrategy { // Set state for ECAA resolve. const usedSize = view.destUsedSize; view.gl.bindFramebuffer(view.gl.FRAMEBUFFER, view.destFramebuffer); - view.gl.viewport(0, 0, this.framebufferSize[0], this.framebufferSize[1]); + view.gl.viewport(0, 0, this.destFramebufferSize[0], this.destFramebufferSize[1]); view.gl.scissor(0, 0, usedSize[0], usedSize[1]); view.gl.enable(view.gl.SCISSOR_TEST); this.setResolveDepthState(view); @@ -391,6 +411,9 @@ export abstract class ECAAStrategy extends AntialiasingStrategy { view.gl.activeTexture(view.gl.TEXTURE0); view.gl.bindTexture(view.gl.TEXTURE_2D, this.aaAlphaTexture); view.gl.uniform1i(resolveProgram.uniforms.uAAAlpha, 0); + view.gl.uniform2i(resolveProgram.uniforms.uAAAlphaDimensions, + this.supersampledFramebufferSize[0], + this.supersampledFramebufferSize[1]); this.setResolveUniforms(view, resolveProgram); view.setTransformSTAndTexScaleUniformsForDest(resolveProgram.uniforms); view.gl.drawElements(view.gl.TRIANGLES, 6, view.gl.UNSIGNED_BYTE, 0); @@ -403,6 +426,12 @@ export abstract class ECAAStrategy extends AntialiasingStrategy { protected setResolveDepthState(view: MonochromePathfinderView): void {} + protected supersampledUsedSize(view: MonochromePathfinderView): glmatrix.vec2 { + const usedSize = glmatrix.vec2.create(); + glmatrix.vec2.mul(usedSize, view.destUsedSize, this.supersampleScale); + return usedSize; + } + protected abstract getResolveProgram(view: MonochromePathfinderView): PathfinderShaderProgram; protected abstract initEdgeDetectFramebuffer(view: MonochromePathfinderView): void; protected abstract createEdgeDetectVAO(view: MonochromePathfinderView): void; @@ -417,6 +446,10 @@ export abstract class ECAAStrategy extends AntialiasingStrategy { return null; } + protected get supersampleScale(): glmatrix.vec2 { + return glmatrix.vec2.fromValues(this.subpixelAA ? 3.0 : 1.0, 1.0); + } + abstract shouldRenderDirect: boolean; private bVertexPositionBufferTexture: PathfinderBufferTexture; @@ -432,11 +465,17 @@ export abstract class ECAAStrategy extends AntialiasingStrategy { protected directColorTexture: WebGLTexture; protected directPathIDTexture: WebGLTexture; protected aaDepthTexture: WebGLTexture; - protected framebufferSize: glmatrix.vec2; + + protected supersampledFramebufferSize: glmatrix.vec2; + protected destFramebufferSize: glmatrix.vec2; + + protected subpixelAA: boolean; } export class ECAAMonochromeStrategy extends ECAAStrategy { protected getResolveProgram(view: MonochromePathfinderView): PathfinderShaderProgram { + if (this.subpixelAA) + return view.shaderPrograms.ecaaMonoSubpixelResolve; return view.shaderPrograms.ecaaMonoResolve; } @@ -477,13 +516,16 @@ export class ECAAMulticolorStrategy extends ECAAStrategy { } protected initDirectFramebuffer(view: MonochromePathfinderView) { - this._directDepthTexture = createFramebufferDepthTexture(view.gl, this.framebufferSize); + this._directDepthTexture = + createFramebufferDepthTexture(view.gl, this.supersampledFramebufferSize); super.initDirectFramebuffer(view); } protected initEdgeDetectFramebuffer(view: MonochromePathfinderView) { - this.bgColorTexture = createFramebufferColorTexture(view.gl, this.framebufferSize); - this.fgColorTexture = createFramebufferColorTexture(view.gl, this.framebufferSize); + this.bgColorTexture = createFramebufferColorTexture(view.gl, + this.supersampledFramebufferSize); + this.fgColorTexture = createFramebufferColorTexture(view.gl, + this.supersampledFramebufferSize); this.edgeDetectFramebuffer = createFramebuffer(view.gl, view.drawBuffersExt, [this.bgColorTexture, this.fgColorTexture], @@ -505,7 +547,10 @@ export class ECAAMulticolorStrategy extends ECAAStrategy { // Set state for edge detection. const edgeDetectProgram = view.shaderPrograms.ecaaEdgeDetect; view.gl.bindFramebuffer(view.gl.FRAMEBUFFER, this.edgeDetectFramebuffer); - view.gl.viewport(0, 0, this.framebufferSize[0], this.framebufferSize[1]); + view.gl.viewport(0, + 0, + this.supersampledFramebufferSize[0], + this.supersampledFramebufferSize[1]); view.drawBuffersExt.drawBuffersWEBGL([ view.drawBuffersExt.COLOR_ATTACHMENT0_WEBGL, diff --git a/demo/client/src/shader-loader.ts b/demo/client/src/shader-loader.ts index df91e302..cd78adb8 100644 --- a/demo/client/src/shader-loader.ts +++ b/demo/client/src/shader-loader.ts @@ -30,6 +30,7 @@ export const SHADER_NAMES: Array> = [ 'ecaaLine', 'ecaaCurve', 'ecaaMonoResolve', + 'ecaaMonoSubpixelResolve', 'ecaaMultiResolve', 'demo3DMonument', ]; @@ -79,6 +80,10 @@ const SHADER_URLS: ShaderMap = { vertex: "/glsl/gles2/ecaa-mono-resolve.vs.glsl", fragment: "/glsl/gles2/ecaa-mono-resolve.fs.glsl", }, + ecaaMonoSubpixelResolve: { + vertex: "/glsl/gles2/ecaa-mono-subpixel-resolve.vs.glsl", + fragment: "/glsl/gles2/ecaa-mono-subpixel-resolve.fs.glsl", + }, ecaaMultiResolve: { vertex: "/glsl/gles2/ecaa-multi-resolve.vs.glsl", fragment: "/glsl/gles2/ecaa-multi-resolve.fs.glsl", @@ -101,6 +106,7 @@ export interface ShaderMap { ecaaLine: T; ecaaCurve: T; ecaaMonoResolve: T; + ecaaMonoSubpixelResolve: T; ecaaMultiResolve: T; demo3DMonument: T; } diff --git a/demo/client/src/ssaa-strategy.ts b/demo/client/src/ssaa-strategy.ts index b56e5182..64480cb9 100644 --- a/demo/client/src/ssaa-strategy.ts +++ b/demo/client/src/ssaa-strategy.ts @@ -27,7 +27,7 @@ export default class SSAAStrategy extends AntialiasingStrategy { attachMeshes(view: PathfinderDemoView) {} setFramebufferSize(view: PathfinderDemoView) { - this.destFramebufferSize = view.destAllocatedSize; + this.destFramebufferSize = glmatrix.vec2.clone(view.destAllocatedSize); this.supersampledFramebufferSize = glmatrix.vec2.create(); glmatrix.vec2.mul(this.supersampledFramebufferSize, diff --git a/shaders/gles2/common.inc.glsl b/shaders/gles2/common.inc.glsl index 67fb3ce5..68c36a4d 100644 --- a/shaders/gles2/common.inc.glsl +++ b/shaders/gles2/common.inc.glsl @@ -7,6 +7,10 @@ #extension GL_EXT_draw_buffers : require #extension GL_EXT_frag_depth : require +#define LCD_FILTER_FACTOR_0 (86.0 / 255.0) +#define LCD_FILTER_FACTOR_1 (77.0 / 255.0) +#define LCD_FILTER_FACTOR_2 (8.0 / 255.0) + #define MAX_PATHS 65536 #define EPSILON 0.001 @@ -133,6 +137,15 @@ float computeCoverage(vec2 p0, return area * (slopeNegative ? 0.5 : -0.5); } +// 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 + + LCD_FILTER_FACTOR_1 * shadeL1 + + LCD_FILTER_FACTOR_0 * shade0 + + LCD_FILTER_FACTOR_1 * shadeR1 + + LCD_FILTER_FACTOR_2 * shadeR2; +} + int unpackUInt16(vec2 packedValue) { ivec2 valueBytes = ivec2(floor(packedValue * 255.0)); return valueBytes.y * 256 + valueBytes.x; diff --git a/shaders/gles2/ecaa-cover.vs.glsl b/shaders/gles2/ecaa-cover.vs.glsl index 970081aa..c5f1665e 100644 --- a/shaders/gles2/ecaa-cover.vs.glsl +++ b/shaders/gles2/ecaa-cover.vs.glsl @@ -5,6 +5,7 @@ precision highp float; uniform ivec2 uFramebufferSize; +uniform float uScaleX; uniform ivec2 uBVertexPositionDimensions; uniform ivec2 uBVertexPathIDDimensions; uniform ivec2 uPathTransformDimensions; @@ -41,6 +42,7 @@ void main() { int pathID = fetchUInt16Data(uBVertexPathID, pointIndices.x, uBVertexPathIDDimensions); vec4 transform = fetchFloat4Data(uPathTransform, pathID, uPathTransformDimensions); + transform.xz *= uScaleX; upperLeftPosition = transformVertexPositionST(upperLeftPosition, transform); upperRightPosition = transformVertexPositionST(upperRightPosition, transform); diff --git a/shaders/gles2/ecaa-curve.vs.glsl b/shaders/gles2/ecaa-curve.vs.glsl index 6f6458be..cd5fdabb 100644 --- a/shaders/gles2/ecaa-curve.vs.glsl +++ b/shaders/gles2/ecaa-curve.vs.glsl @@ -5,6 +5,7 @@ precision highp float; uniform ivec2 uFramebufferSize; +uniform float uScaleX; uniform ivec2 uBVertexPositionDimensions; uniform ivec2 uBVertexPathIDDimensions; uniform ivec2 uPathTransformDimensions; @@ -38,6 +39,7 @@ void main() { int pathID = fetchUInt16Data(uBVertexPathID, pointIndices.x, uBVertexPathIDDimensions); vec4 transform = fetchFloat4Data(uPathTransform, pathID, uPathTransformDimensions); + transform.xz *= uScaleX; // Transform the points, and compute the position of this vertex. vec2 position; diff --git a/shaders/gles2/ecaa-line.vs.glsl b/shaders/gles2/ecaa-line.vs.glsl index e44a480f..0d61d40c 100644 --- a/shaders/gles2/ecaa-line.vs.glsl +++ b/shaders/gles2/ecaa-line.vs.glsl @@ -5,6 +5,7 @@ precision highp float; uniform mat4 uTransform; +uniform float uScaleX; uniform ivec2 uFramebufferSize; uniform ivec2 uBVertexPositionDimensions; uniform ivec2 uBVertexPathIDDimensions; @@ -33,6 +34,7 @@ void main() { int pathID = fetchUInt16Data(uBVertexPathID, pointIndices.x, uBVertexPathIDDimensions); vec4 transform = fetchFloat4Data(uPathTransform, pathID, uPathTransformDimensions); + transform.xz *= uScaleX; // Transform the points, and compute the position of this vertex. vec2 position; diff --git a/shaders/gles2/ecaa-mono-resolve.fs.glsl b/shaders/gles2/ecaa-mono-resolve.fs.glsl index efe5ba2f..19fc49a1 100644 --- a/shaders/gles2/ecaa-mono-resolve.fs.glsl +++ b/shaders/gles2/ecaa-mono-resolve.fs.glsl @@ -1,6 +1,12 @@ // pathfinder/shaders/gles2/ecaa-mono-resolve.fs.glsl // -// Copyright (c) 2017 Mozilla Foundation +// 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 mediump float; diff --git a/shaders/gles2/ecaa-mono-resolve.vs.glsl b/shaders/gles2/ecaa-mono-resolve.vs.glsl index aa848a15..4609a11d 100644 --- a/shaders/gles2/ecaa-mono-resolve.vs.glsl +++ b/shaders/gles2/ecaa-mono-resolve.vs.glsl @@ -1,6 +1,12 @@ // pathfinder/shaders/gles2/ecaa-mono-resolve.vs.glsl // -// Copyright (c) 2017 Mozilla Foundation +// 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; diff --git a/shaders/gles2/ecaa-mono-subpixel-resolve.fs.glsl b/shaders/gles2/ecaa-mono-subpixel-resolve.fs.glsl new file mode 100644 index 00000000..c9f94c32 --- /dev/null +++ b/shaders/gles2/ecaa-mono-subpixel-resolve.fs.glsl @@ -0,0 +1,40 @@ +// pathfinder/shaders/gles2/ecaa-subpixel-mono-resolve.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 mediump float; + +uniform vec4 uBGColor; +uniform vec4 uFGColor; +uniform sampler2D uAAAlpha; +uniform ivec2 uAAAlphaDimensions; + +varying vec2 vTexCoord; + +float sampleSource(float deltaX) { + return texture2D(uAAAlpha, vec2(vTexCoord.s + deltaX, vTexCoord.y)).r; +} + +void main() { + float onePixel = 1.0 / float(uAAAlphaDimensions.x); + + float shade0 = sampleSource(0.0); + vec3 shadeL = vec3(sampleSource(-1.0 * onePixel), + sampleSource(-2.0 * onePixel), + sampleSource(-3.0 * onePixel)); + vec3 shadeR = vec3(sampleSource(1.0 * onePixel), + sampleSource(2.0 * onePixel), + sampleSource(3.0 * onePixel)); + + vec3 alpha = vec3(lcdFilter(shadeL.z, shadeL.y, shadeL.x, shade0, shadeR.x), + lcdFilter(shadeL.y, shadeL.x, shade0, shadeR.x, shadeR.y), + lcdFilter(shadeL.x, shade0, shadeR.x, shadeR.y, shadeR.z)); + + gl_FragColor = mix(uBGColor, uFGColor, vec4(alpha, 1.0)); +} diff --git a/shaders/gles2/ecaa-mono-subpixel-resolve.vs.glsl b/shaders/gles2/ecaa-mono-subpixel-resolve.vs.glsl new file mode 100644 index 00000000..418ba60c --- /dev/null +++ b/shaders/gles2/ecaa-mono-subpixel-resolve.vs.glsl @@ -0,0 +1,24 @@ +// pathfinder/shaders/gles2/ecaa-subpixel-mono-resolve.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; + +uniform vec4 uTransformST; +uniform vec2 uTexScale; + +attribute vec2 aPosition; +attribute vec2 aTexCoord; + +varying vec2 vTexCoord; + +void main() { + gl_Position = vec4(transformVertexPositionST(aPosition, uTransformST), -1.0, 1.0); + vTexCoord = aTexCoord * uTexScale; +} diff --git a/shaders/gles2/ssaa-subpixel-resolve.fs.glsl b/shaders/gles2/ssaa-subpixel-resolve.fs.glsl index f5e4eef0..67cc1328 100644 --- a/shaders/gles2/ssaa-subpixel-resolve.fs.glsl +++ b/shaders/gles2/ssaa-subpixel-resolve.fs.glsl @@ -15,23 +15,10 @@ uniform ivec2 uSourceDimensions; varying vec2 vTexCoord; -#define FILTER_0 (86.0 / 255.0) -#define FILTER_1 (77.0 / 255.0) -#define FILTER_2 (8.0 / 255.0) - float sampleSource(float deltaX) { return texture2D(uSource, vec2(vTexCoord.s + deltaX, vTexCoord.y)).r; } -// https://www.freetype.org/freetype2/docs/reference/ft2-lcd_filtering.html -float lcdFilter(float shadeL2, float shadeL1, float shade0, float shadeR1, float shadeR2) { - return FILTER_2 * shadeL2 + - FILTER_1 * shadeL1 + - FILTER_0 * shade0 + - FILTER_1 * shadeR1 + - FILTER_2 * shadeR2; -} - void main() { float onePixel = 1.0 / float(uSourceDimensions.x);