Get edge detection working in the demo

This commit is contained in:
Patrick Walton 2017-08-16 16:37:39 -07:00
parent b10807d217
commit 4b62ec8fa7
8 changed files with 306 additions and 155 deletions

View File

@ -9,6 +9,9 @@
<script type="text/javascript" src="js/bootstrap/bootstrap.js"></script>
<script type="text/javascript" src="js/pathfinder.js"></script>
<style type="text/css">
body {
background: lightslategray;
}
.pf-bottom-control {
position: fixed;
bottom: 1em;

View File

@ -14,10 +14,10 @@ const UINT32_SIZE: number = 4;
const B_POSITION_SIZE: number = 8;
const B_VERTEX_QUAD_SIZE: number = 8;
const B_VERTEX_QUAD_PATH_ID_OFFSET: number = 0;
const B_VERTEX_QUAD_TEX_COORD_OFFSET: number = 4;
const B_VERTEX_QUAD_SIGN_OFFSET: number = 6;
const B_VERTEX_SIZE: number = 8;
const B_VERTEX_PATH_ID_OFFSET: number = 0;
const B_VERTEX_TEX_COORD_OFFSET: number = 4;
const B_VERTEX_SIGN_OFFSET: number = 6;
const IDENTITY: Matrix4D = [
1.0, 0.0, 0.0, 0.0,
@ -43,6 +43,10 @@ const SHADER_URLS: ShaderMap<ShaderProgramURLs> = {
vertex: "/glsl/gles2/ecaa-edge-detect.vs.glsl",
fragment: "/glsl/gles2/ecaa-edge-detect.fs.glsl",
},
ecaaResolve: {
vertex: "/glsl/gles2/ecaa-resolve.vs.glsl",
fragment: "/glsl/gles2/ecaa-resolve.fs.glsl",
},
};
interface UnlinkedShaderProgram {
@ -72,6 +76,7 @@ interface ShaderMap<T> {
directCurve: T;
directInterior: T;
ecaaEdgeDetect: T;
ecaaResolve: T;
}
interface UniformMap {
@ -150,6 +155,44 @@ class PathfinderError extends Error {
// GL utilities
function createFramebufferColorTexture(gl: WebGLRenderingContext, size: Size2D): WebGLTexture {
// Firefox seems to have a bug whereby textures don't get marked as initialized when cleared
// if they're anything other than the first attachment of an FBO. To work around this, supply
// zero data explicitly when initializing the texture.
const texture = unwrapNull(gl.createTexture());
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D,
0,
gl.RGBA,
size.width,
size.height,
0,
gl.RGBA,
gl.UNSIGNED_BYTE,
new Uint8Array(size.width * size.height * 4));
setTextureParameters(gl, gl.NEAREST);
return texture;
}
function createFramebufferDepthTexture(gl: WebGLRenderingContext, size: Size2D): WebGLTexture {
const texture = unwrapNull(gl.createTexture());
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D,
0,
gl.DEPTH_COMPONENT,
size.width,
size.height,
0,
gl.DEPTH_COMPONENT,
gl.UNSIGNED_INT,
null);
setTextureParameters(gl, gl.NEAREST);
return texture;
}
function setTextureParameters(gl: WebGLRenderingContext, filter: number) {
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
@ -157,6 +200,47 @@ function setTextureParameters(gl: WebGLRenderingContext, filter: number) {
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, filter);
}
function createFramebuffer(gl: WebGLRenderingContext,
drawBuffersExt: any,
colorAttachments: WebGLTexture[],
depthAttachment: WebGLTexture | null):
WebGLFramebuffer {
const framebuffer = unwrapNull(gl.createFramebuffer());
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
const colorAttachmentCount = colorAttachments.length;
for (let colorAttachmentIndex = 0;
colorAttachmentIndex < colorAttachmentCount;
colorAttachmentIndex++) {
gl.framebufferTexture2D(gl.FRAMEBUFFER,
drawBuffersExt[`COLOR_ATTACHMENT${colorAttachmentIndex}_WEBGL`],
gl.TEXTURE_2D,
colorAttachments[colorAttachmentIndex],
0);
}
if (depthAttachment != null) {
gl.framebufferTexture2D(gl.FRAMEBUFFER,
gl.DEPTH_ATTACHMENT,
gl.TEXTURE_2D,
depthAttachment,
0);
}
assert(gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE,
"Framebuffer was incomplete!");
return framebuffer;
}
function initQuadVAO(view: PathfinderView, attributes: any) {
view.gl.bindBuffer(view.gl.ARRAY_BUFFER, view.quadPositionsBuffer);
view.gl.vertexAttribPointer(attributes.aPosition, 2, view.gl.FLOAT, false, 0, 0);
view.gl.bindBuffer(view.gl.ARRAY_BUFFER, view.quadTexCoordsBuffer);
view.gl.vertexAttribPointer(attributes.aTexCoord, 2, view.gl.FLOAT, false, 0, 0);
view.gl.enableVertexAttribArray(attributes.aPosition);
view.gl.enableVertexAttribArray(attributes.aTexCoord);
}
interface Meshes<T> {
readonly bQuads: T;
readonly bVertexPositions: T;
@ -446,12 +530,6 @@ class PathfinderView {
// Prepare for direct rendering.
this.antialiasingStrategy.prepare(this);
// Clear.
this.gl.clearColor(1.0, 1.0, 1.0, 1.0);
this.gl.clearDepth(0.0);
this.gl.depthMask(true);
this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);
// Perform direct rendering (Loop-Blinn).
this.renderDirect(shaderPrograms);
@ -464,10 +542,11 @@ class PathfinderView {
}
renderDirect(shaderPrograms: ShaderMap<PathfinderShaderProgram>) {
// Set up the depth buffer.
// Set up implicit cover state.
this.gl.depthFunc(this.gl.GREATER);
this.gl.depthMask(true);
this.gl.enable(this.gl.DEPTH_TEST);
this.gl.disable(this.gl.BLEND);
// Set up the implicit cover interior VAO.
const directInteriorProgram = shaderPrograms.directInterior;
@ -484,10 +563,10 @@ class PathfinderView {
1,
this.gl.UNSIGNED_SHORT, // FIXME(pcwalton)
false,
B_VERTEX_QUAD_SIZE,
B_VERTEX_QUAD_PATH_ID_OFFSET);
B_VERTEX_SIZE,
B_VERTEX_PATH_ID_OFFSET);
this.gl.enableVertexAttribArray(directInteriorProgram.attributes.aPosition);
this.gl.enableVertexAttribArray(directInteriorProgram.attributes.aPathDepth);
this.gl.enableVertexAttribArray(directInteriorProgram.attributes.aPathID);
this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.meshes.coverInteriorIndices);
// Draw direct interior parts.
@ -505,10 +584,13 @@ class PathfinderView {
this.gl.BUFFER_SIZE) / UINT32_SIZE;
this.gl.drawElements(this.gl.TRIANGLES, indexCount, this.gl.UNSIGNED_INT, 0);
// Disable depth writing.
// Set up direct curve state.
this.gl.depthMask(false);
this.gl.enable(this.gl.BLEND);
this.gl.blendEquation(this.gl.FUNC_ADD);
this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA);
// Set up the implicit cover curve VAO.
// Set up the direct curve VAO.
const directCurveProgram = shaderPrograms.directCurve;
this.gl.useProgram(directCurveProgram.program);
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.meshes.bVertexPositions);
@ -523,23 +605,23 @@ class PathfinderView {
2,
this.gl.UNSIGNED_BYTE,
false,
B_VERTEX_QUAD_SIZE,
B_VERTEX_QUAD_TEX_COORD_OFFSET);
B_VERTEX_SIZE,
B_VERTEX_TEX_COORD_OFFSET);
this.gl.vertexAttribPointer(directCurveProgram.attributes.aPathID,
1,
this.gl.UNSIGNED_SHORT, // FIXME(pcwalton)
false,
B_VERTEX_QUAD_SIZE,
B_VERTEX_QUAD_PATH_ID_OFFSET);
B_VERTEX_SIZE,
B_VERTEX_PATH_ID_OFFSET);
this.gl.vertexAttribPointer(directCurveProgram.attributes.aSign,
1,
this.gl.BYTE,
false,
B_VERTEX_QUAD_SIZE,
B_VERTEX_QUAD_SIGN_OFFSET);
B_VERTEX_SIZE,
B_VERTEX_SIGN_OFFSET);
this.gl.enableVertexAttribArray(directCurveProgram.attributes.aPosition);
this.gl.enableVertexAttribArray(directCurveProgram.attributes.aTexCoord);
this.gl.enableVertexAttribArray(directCurveProgram.attributes.aPathDepth);
this.gl.enableVertexAttribArray(directCurveProgram.attributes.aPathID);
this.gl.enableVertexAttribArray(directCurveProgram.attributes.aSign);
this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.meshes.coverCurveIndices);
@ -639,6 +721,12 @@ class NoAAStrategy implements AntialiasingStrategy {
prepare(view: PathfinderView) {
view.gl.viewport(0, 0, this.framebufferSize.width, this.framebufferSize.height);
// Clear.
view.gl.clearColor(1.0, 1.0, 1.0, 1.0);
view.gl.clearDepth(0.0);
view.gl.depthMask(true);
view.gl.clear(view.gl.COLOR_BUFFER_BIT | view.gl.DEPTH_BUFFER_BIT);
}
resolve(view: PathfinderView, shaders: ShaderMap<PathfinderShaderProgram>) {}
@ -661,6 +749,7 @@ class SSAAStrategy implements AntialiasingStrategy {
};
this.supersampledColorTexture = unwrapNull(view.gl.createTexture());
view.gl.activeTexture(view.gl.TEXTURE0);
view.gl.bindTexture(view.gl.TEXTURE_2D, this.supersampledColorTexture);
view.gl.texImage2D(view.gl.TEXTURE_2D,
0,
@ -673,33 +762,13 @@ class SSAAStrategy implements AntialiasingStrategy {
null);
setTextureParameters(view.gl, view.gl.LINEAR);
this.supersampledDepthTexture = unwrapNull(view.gl.createTexture());
view.gl.bindTexture(view.gl.TEXTURE_2D, this.supersampledDepthTexture);
view.gl.texImage2D(view.gl.TEXTURE_2D,
0,
view.gl.DEPTH_COMPONENT,
this.supersampledFramebufferSize.width,
this.supersampledFramebufferSize.height,
0,
view.gl.DEPTH_COMPONENT,
view.gl.UNSIGNED_INT,
null);
setTextureParameters(view.gl, view.gl.NEAREST);
this.supersampledDepthTexture =
createFramebufferDepthTexture(view.gl, this.supersampledFramebufferSize);
this.supersampledFramebuffer = unwrapNull(view.gl.createFramebuffer());
view.gl.bindFramebuffer(view.gl.FRAMEBUFFER, this.supersampledFramebuffer);
view.gl.framebufferTexture2D(view.gl.FRAMEBUFFER,
view.gl.COLOR_ATTACHMENT0,
view.gl.TEXTURE_2D,
this.supersampledColorTexture,
0);
view.gl.framebufferTexture2D(view.gl.FRAMEBUFFER,
view.gl.DEPTH_ATTACHMENT,
view.gl.TEXTURE_2D,
this.supersampledDepthTexture,
0);
assert(view.gl.checkFramebufferStatus(view.gl.FRAMEBUFFER) == view.gl.FRAMEBUFFER_COMPLETE,
"The SSAA framebuffer was incomplete!");
this.supersampledFramebuffer = createFramebuffer(view.gl,
view.drawBuffersExt,
[this.supersampledColorTexture],
this.supersampledDepthTexture);
view.gl.bindFramebuffer(view.gl.FRAMEBUFFER, null);
}
@ -708,6 +777,12 @@ class SSAAStrategy implements AntialiasingStrategy {
const size = this.supersampledFramebufferSize;
view.gl.bindFramebuffer(view.gl.FRAMEBUFFER, this.supersampledFramebuffer);
view.gl.viewport(0, 0, size.width, size.height);
// Clear.
view.gl.clearColor(1.0, 1.0, 1.0, 1.0);
view.gl.clearDepth(0.0);
view.gl.depthMask(true);
view.gl.clear(view.gl.COLOR_BUFFER_BIT | view.gl.DEPTH_BUFFER_BIT);
}
resolve(view: PathfinderView, shaders: ShaderMap<PathfinderShaderProgram>) {
@ -718,22 +793,7 @@ class SSAAStrategy implements AntialiasingStrategy {
// Set up the blit program VAO.
const blitProgram = shaders.blit;
view.gl.useProgram(blitProgram.program);
view.gl.bindBuffer(view.gl.ARRAY_BUFFER, view.quadPositionsBuffer);
view.gl.vertexAttribPointer(blitProgram.attributes.aPosition,
2,
view.gl.FLOAT,
false,
0,
0);
view.gl.bindBuffer(view.gl.ARRAY_BUFFER, view.quadTexCoordsBuffer);
view.gl.vertexAttribPointer(blitProgram.attributes.aTexCoord,
2,
view.gl.FLOAT,
false,
0,
0);
view.gl.enableVertexAttribArray(blitProgram.attributes.aPosition);
view.gl.enableVertexAttribArray(blitProgram.attributes.aTexCoord);
initQuadVAO(view, blitProgram.attributes);
// Resolve framebuffer.
view.gl.activeTexture(view.gl.TEXTURE0);
@ -757,82 +817,129 @@ class ECAAStrategy implements AntialiasingStrategy {
init(view: PathfinderView, framebufferSize: Size2D) {
this.framebufferSize = framebufferSize;
this.directColorTexture = unwrapNull(view.gl.createTexture());
view.gl.bindTexture(view.gl.TEXTURE_2D, this.directColorTexture);
view.gl.texImage2D(view.gl.TEXTURE_2D,
0,
view.gl.RGBA,
framebufferSize.width,
framebufferSize.height,
0,
view.gl.RGBA,
view.gl.UNSIGNED_BYTE,
null);
setTextureParameters(view.gl, view.gl.NEAREST);
this.directPathIDTexture = unwrapNull(view.gl.createTexture());
view.gl.bindTexture(view.gl.TEXTURE_2D, this.directPathIDTexture);
view.gl.texImage2D(view.gl.TEXTURE_2D,
0,
view.gl.RGBA, // really should be RG
framebufferSize.width,
framebufferSize.height,
0,
view.gl.RGBA,
view.gl.UNSIGNED_BYTE,
null);
setTextureParameters(view.gl, view.gl.NEAREST);
this.directDepthTexture = unwrapNull(view.gl.createTexture());
view.gl.bindTexture(view.gl.TEXTURE_2D, this.directDepthTexture);
view.gl.texImage2D(view.gl.TEXTURE_2D,
0,
view.gl.DEPTH_COMPONENT,
framebufferSize.width,
framebufferSize.height,
0,
view.gl.DEPTH_COMPONENT,
view.gl.UNSIGNED_INT,
null);
setTextureParameters(view.gl, view.gl.NEAREST);
this.directFramebuffer = unwrapNull(view.gl.createFramebuffer());
view.gl.bindFramebuffer(view.gl.FRAMEBUFFER, this.directFramebuffer);
view.gl.framebufferTexture2D(view.gl.FRAMEBUFFER,
view.drawBuffersExt.COLOR_ATTACHMENT0_WEBGL,
view.gl.TEXTURE_2D,
this.directColorTexture,
0);
view.gl.framebufferTexture2D(view.gl.FRAMEBUFFER,
view.drawBuffersExt.COLOR_ATTACHMENT1_WEBGL,
view.gl.TEXTURE_2D,
this.directPathIDTexture,
0);
view.gl.framebufferTexture2D(view.gl.FRAMEBUFFER,
view.gl.DEPTH_ATTACHMENT,
view.gl.TEXTURE_2D,
this.directDepthTexture,
0);
assert(view.gl.checkFramebufferStatus(view.gl.FRAMEBUFFER) == view.gl.FRAMEBUFFER_COMPLETE,
"The direct framebuffer was incomplete!");
this.initDirectFramebuffer(view);
this.initEdgeDetectFramebuffer(view);
view.gl.bindFramebuffer(view.gl.FRAMEBUFFER, null);
}
initDirectFramebuffer(view: PathfinderView) {
this.directColorTexture = createFramebufferColorTexture(view.gl, this.framebufferSize);
this.directPathIDTexture = createFramebufferColorTexture(view.gl, this.framebufferSize);
this.directDepthTexture = createFramebufferDepthTexture(view.gl, this.framebufferSize);
this.directFramebuffer =
createFramebuffer(view.gl,
view.drawBuffersExt,
[this.directColorTexture, this.directPathIDTexture],
this.directDepthTexture);
}
initEdgeDetectFramebuffer(view: PathfinderView) {
this.bgColorTexture = createFramebufferColorTexture(view.gl, this.framebufferSize);
this.fgColorTexture = createFramebufferColorTexture(view.gl, this.framebufferSize);
this.aaDepthTexture = createFramebufferDepthTexture(view.gl, this.framebufferSize);
this.edgeDetectFramebuffer = createFramebuffer(view.gl,
view.drawBuffersExt,
[this.bgColorTexture, this.fgColorTexture],
this.aaDepthTexture);
}
prepare(view: PathfinderView) {
view.gl.bindFramebuffer(view.gl.FRAMEBUFFER, this.directFramebuffer);
view.gl.viewport(0, 0, this.framebufferSize.width, this.framebufferSize.height);
// Clear out the color and depth textures.
view.drawBuffersExt.drawBuffersWEBGL([
view.drawBuffersExt.COLOR_ATTACHMENT0_WEBGL,
view.drawBuffersExt.NONE,
]);
view.gl.clearColor(1.0, 1.0, 1.0, 1.0);
view.gl.clearDepth(0.0);
view.gl.depthMask(true);
view.gl.clear(view.gl.COLOR_BUFFER_BIT | view.gl.DEPTH_BUFFER_BIT);
// Clear out the path ID texture.
view.drawBuffersExt.drawBuffersWEBGL([
view.drawBuffersExt.NONE,
view.drawBuffersExt.COLOR_ATTACHMENT1_WEBGL,
]);
view.gl.clearColor(0.0, 0.0, 0.0, 0.0);
view.gl.clear(view.gl.COLOR_BUFFER_BIT);
// Render to both textures.
view.drawBuffersExt.drawBuffersWEBGL([
view.drawBuffersExt.COLOR_ATTACHMENT0_WEBGL,
view.drawBuffersExt.COLOR_ATTACHMENT1_WEBGL,
]);
}
resolve(view: PathfinderView, shaders: ShaderMap<PathfinderShaderProgram>) {
// TODO(pcwalton)
// Set state for edge detection.
view.gl.bindFramebuffer(view.gl.FRAMEBUFFER, this.edgeDetectFramebuffer);
view.gl.viewport(0, 0, this.framebufferSize.width, this.framebufferSize.height);
view.drawBuffersExt.drawBuffersWEBGL([
view.drawBuffersExt.COLOR_ATTACHMENT0_WEBGL,
view.drawBuffersExt.COLOR_ATTACHMENT1_WEBGL,
]);
view.gl.depthMask(true);
view.gl.depthFunc(view.gl.ALWAYS);
view.gl.enable(view.gl.DEPTH_TEST);
view.gl.disable(view.gl.BLEND);
view.gl.clearDepth(0.0);
view.gl.clearColor(0.0, 0.0, 0.0, 0.0);
view.gl.clear(view.gl.COLOR_BUFFER_BIT | view.gl.DEPTH_BUFFER_BIT);
// Set up the edge detection VAO.
const edgeDetectProgram = shaders.ecaaEdgeDetect;
view.gl.useProgram(edgeDetectProgram.program);
initQuadVAO(view, edgeDetectProgram.attributes);
// Perform edge detection.
view.gl.uniform2i(edgeDetectProgram.uniforms.uFramebufferSize,
this.framebufferSize.width,
this.framebufferSize.height);
view.gl.activeTexture(view.gl.TEXTURE0);
view.gl.bindTexture(view.gl.TEXTURE_2D, this.directColorTexture);
view.gl.uniform1i(edgeDetectProgram.uniforms.uColor, 0);
view.gl.activeTexture(view.gl.TEXTURE1);
view.gl.bindTexture(view.gl.TEXTURE_2D, this.directPathIDTexture);
view.gl.uniform1i(edgeDetectProgram.uniforms.uPathID, 1);
view.gl.drawArrays(view.gl.TRIANGLE_STRIP, 0, 4);
// Set state for ECAA resolve.
view.gl.bindFramebuffer(view.gl.FRAMEBUFFER, null);
view.gl.viewport(0, 0, this.framebufferSize.width, this.framebufferSize.height);
view.gl.disable(view.gl.DEPTH_TEST);
view.drawBuffersExt.drawBuffersWEBGL([view.gl.BACK]);
// Set up the ECAA resolve VAO.
const resolveProgram = shaders.ecaaResolve;
view.gl.useProgram(resolveProgram.program);
initQuadVAO(view, resolveProgram.attributes);
// Perform ECAA resolution.
view.gl.uniform2i(resolveProgram.uniforms.uFramebufferSize,
this.framebufferSize.width,
this.framebufferSize.height);
view.gl.activeTexture(view.gl.TEXTURE0);
view.gl.bindTexture(view.gl.TEXTURE_2D, this.bgColorTexture);
view.gl.uniform1i(resolveProgram.uniforms.uBGColor, 0);
view.gl.activeTexture(view.gl.TEXTURE1);
view.gl.bindTexture(view.gl.TEXTURE_2D, this.fgColorTexture);
view.gl.uniform1i(resolveProgram.uniforms.uFGColor, 1);
view.gl.drawArrays(view.gl.TRIANGLE_STRIP, 0, 4);
}
directColorTexture: WebGLTexture;
directPathIDTexture: WebGLTexture;
directDepthTexture: WebGLTexture;
directFramebuffer: WebGLFramebuffer;
bgColorTexture: WebGLTexture;
fgColorTexture: WebGLTexture;
aaDepthTexture: WebGLTexture;
edgeDetectFramebuffer: WebGLFramebuffer;
framebufferSize: Size2D;
}

View File

@ -224,7 +224,7 @@ fn partition_font(request: Json<PartitionFontRequest>)
}
};
partitioner.partition(path_index as u32,
partitioner.partition((path_index + 1) as u32,
decoded_outline_indices.subpath_indices.start as u32,
decoded_outline_indices.subpath_indices.end as u32);

View File

@ -27,7 +27,7 @@ vec2 convertScreenToClipSpace(vec2 position, ivec2 framebufferSize) {
}
float convertPathIndexToDepthValue(int pathIndex) {
return float(pathIndex + 1) / float(MAX_PATHS);
return mix(-1.0, 1.0, float(pathIndex) / float(MAX_PATHS));
}
vec4 fetchFloat4Data(sampler2D dataTexture, int index, ivec2 dimensions) {
@ -42,3 +42,8 @@ vec4 fetchFloat4NormIndexedData(sampler2D dataTexture, float normIndex, ivec2 di
vec2 packPathID(int pathID) {
return vec2(imod(pathID, 256), pathID / 256) / 255.0;
}
int unpackPathID(vec2 packedPathID) {
ivec2 pathIDBytes = ivec2(floor(packedPathID * 255.0));
return pathIDBytes.y * 256 + pathIDBytes.x;
}

View File

@ -17,5 +17,5 @@ void main() {
float side = vTexCoord.x * vTexCoord.x - vTexCoord.y;
float alpha = float(sign(side) == sign(vSign));
gl_FragData[0] = vec4(vColor.rgb, vColor.a * alpha);
gl_FragData[1] = vec4(vPathID, 1.0, 1.0);
gl_FragData[1] = vec4(vPathID, 0.0, alpha);
}

View File

@ -6,12 +6,15 @@ precision highp float;
uniform ivec2 uFramebufferSize;
uniform sampler2D uColor;
uniform sampler2D uDepth;
uniform sampler2D uPathID;
varying vec2 vTexCoord;
vec3 checkFG(vec3 fgPosition, vec3 queryPosition) {
return queryPosition.z > fgPosition.z ? queryPosition : fgPosition;
void checkFG(out vec2 fgPosition, out int fgPathID, vec2 queryPosition, int queryPathID) {
if (queryPathID > fgPathID) {
fgPosition = queryPosition;
fgPathID = queryPathID;
}
}
void main() {
@ -22,47 +25,48 @@ void main() {
vec2 onePixel = 1.0 / vec2(uFramebufferSize);
vec2 positionL = position + vec2(-onePixel.x, 0.0);
vec2 positionR = position + vec2( onePixel.x, 0.0);
vec2 positionB = position + vec2(0.0, -onePixel.x);
vec2 positionT = position + vec2(0.0, onePixel.x);
vec2 positionB = position + vec2(0.0, -onePixel.y);
vec2 positionT = position + vec2(0.0, onePixel.y);
// Determine the topmost path.
float centerDepth = texture2D(uDepth, position).r;
vec4 neighborDepths = vec4(texture2D(uDepth, positionL).r,
texture2D(uDepth, positionR).r,
texture2D(uDepth, positionT).r,
texture2D(uDepth, positionB).r);
int centerPathID = unpackPathID(texture2D(uPathID, position).rg);
ivec4 neighborPathIDs = ivec4(unpackPathID(texture2D(uPathID, positionL).rg),
unpackPathID(texture2D(uPathID, positionR).rg),
unpackPathID(texture2D(uPathID, positionB).rg),
unpackPathID(texture2D(uPathID, positionT).rg));
// Determine the position of the foreground color.
vec3 fgPosition = vec3(position, centerDepth);
fgPosition = checkFG(fgPosition, vec3(positionL, neighborDepths.x));
fgPosition = checkFG(fgPosition, vec3(positionR, neighborDepths.y));
fgPosition = checkFG(fgPosition, vec3(positionT, neighborDepths.z));
fgPosition = checkFG(fgPosition, vec3(positionB, neighborDepths.w));
vec2 fgPosition = position;
int fgPathID = centerPathID;
checkFG(fgPosition, fgPathID, positionL, neighborPathIDs.x);
checkFG(fgPosition, fgPathID, positionR, neighborPathIDs.y);
checkFG(fgPosition, fgPathID, positionB, neighborPathIDs.z);
checkFG(fgPosition, fgPathID, positionT, neighborPathIDs.w);
// Determine the position of the background color.
vec2 bgPosition;
if (fgPosition.z != centerDepth)
bgPosition = fgPosition.xy;
else if (fgPosition.z != neighborDepths.x)
if (fgPathID != centerPathID)
bgPosition = position;
else if (fgPathID != neighborPathIDs.x)
bgPosition = positionL;
else if (fgPosition.z != neighborDepths.y)
else if (fgPathID != neighborPathIDs.y)
bgPosition = positionR;
else if (fgPosition.z != neighborDepths.z)
bgPosition = positionT;
else
else if (fgPathID != neighborPathIDs.z)
bgPosition = positionB;
else
bgPosition = positionT;
// Determine the foreground and background colors.
vec4 fgColor = texture2D(uColor, fgPosition.st);
vec4 fgColor = texture2D(uColor, fgPosition);
vec4 bgColor = texture2D(uColor, bgPosition);
// Determine the depth.
//
// If all colors are the same, avoid touching this pixel in any further passes.
float outDepth = fgColor == bgColor ? 0.0 : fgPosition.z;
float outDepth = fgColor == bgColor ? -1.0 : convertPathIndexToDepthValue(fgPathID);
// Output results.
gl_FragData[0] = fgColor;
gl_FragData[1] = bgColor;
gl_FragData[0] = bgColor;
gl_FragData[1] = fgColor;
gl_FragDepthEXT = outDepth;
}

View File

@ -0,0 +1,17 @@
// pathfinder/shaders/gles2/ecaa-resolve.fs.glsl
//
// Copyright (c) 2017 Mozilla Foundation
precision highp float;
uniform ivec2 uFramebufferSize;
uniform sampler2D uBGColor;
uniform sampler2D uFGColor;
varying vec2 vTexCoord;
void main() {
vec4 bgColor = texture2D(uBGColor, vTexCoord);
vec4 fgColor = texture2D(uFGColor, vTexCoord);
gl_FragColor = bgColor != fgColor ? vec4(1.0, 0.0, 0.0, 1.0) : bgColor;
}

View File

@ -0,0 +1,15 @@
// pathfinder/shaders/gles2/ecaa-resolve.vs.glsl
//
// Copyright (c) 2017 Mozilla Foundation
precision highp float;
attribute vec2 aPosition;
attribute vec2 aTexCoord;
varying vec2 vTexCoord;
void main() {
gl_Position = vec4(aPosition, 0.0, 1.0);
vTexCoord = aTexCoord;
}