Implement subpixel ECAA

This commit is contained in:
Patrick Walton 2017-09-07 14:58:41 -07:00
parent 6cbc7dc082
commit 3ee066bdf0
12 changed files with 168 additions and 35 deletions

View File

@ -28,7 +28,11 @@ interface UpperAndLower<T> {
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,

View File

@ -30,6 +30,7 @@ export const SHADER_NAMES: Array<keyof ShaderMap<void>> = [
'ecaaLine',
'ecaaCurve',
'ecaaMonoResolve',
'ecaaMonoSubpixelResolve',
'ecaaMultiResolve',
'demo3DMonument',
];
@ -79,6 +80,10 @@ const SHADER_URLS: ShaderMap<ShaderProgramURLs> = {
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<T> {
ecaaLine: T;
ecaaCurve: T;
ecaaMonoResolve: T;
ecaaMonoSubpixelResolve: T;
ecaaMultiResolve: T;
demo3DMonument: T;
}

View File

@ -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,

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
precision mediump float;

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
precision highp float;

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
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));
}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
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;
}

View File

@ -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);