Get basic ECAA fully working

This commit is contained in:
Patrick Walton 2017-08-18 20:23:45 -07:00
parent 343c67aa33
commit 37c947c751
5 changed files with 145 additions and 77 deletions

View File

@ -5,7 +5,7 @@
const base64js = require('base64-js');
const opentype = require('opentype.js');
const TEXT: string = "G";
const TEXT: string = "O";
const FONT_SIZE: number = 16.0;
const PARTITION_FONT_ENDPOINT_URL: string = "/partition-font";
@ -568,7 +568,9 @@ class PathfinderView {
pathColors[pathIndex * 4 + 3] = 0xff; // alpha
}
this.pathColorsBufferTexture = new PathfinderBufferTexture(this.gl, pathColors);
this.pathColorsBufferTexture = new PathfinderBufferTexture(this.gl,
pathColors,
'uPathColors');
}
attachMeshes(meshes: PathfinderMeshData) {
@ -625,6 +627,15 @@ class PathfinderView {
this.dirty = false;
}
setTransformUniform(uniforms: UniformMap) {
this.gl.uniformMatrix4fv(uniforms.uTransform, false, IDENTITY);
}
setFramebufferSizeUniform(uniforms: UniformMap) {
const currentViewport = this.gl.getParameter(this.gl.VIEWPORT);
this.gl.uniform2i(uniforms.uFramebufferSize, currentViewport[2], currentViewport[3]);
}
renderDirect() {
// Set up implicit cover state.
this.gl.depthFunc(this.gl.GREATER);
@ -654,16 +665,9 @@ class PathfinderView {
this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.meshes.coverInteriorIndices);
// Draw direct interior parts.
this.gl.activeTexture(this.gl.TEXTURE0);
this.gl.bindTexture(this.gl.TEXTURE_2D, this.pathColorsBufferTexture.texture);
this.gl.uniformMatrix4fv(directInteriorProgram.uniforms.uTransform, false, IDENTITY);
this.gl.uniform2i(directInteriorProgram.uniforms.uFramebufferSize,
this.canvas.width,
this.canvas.height);
this.gl.uniform2i(directInteriorProgram.uniforms.uPathColorsDimensions,
this.pathColorsBufferTexture.size.width,
this.pathColorsBufferTexture.size.height);
this.gl.uniform1i(directInteriorProgram.uniforms.uPathColors, 0);
this.setTransformUniform(directInteriorProgram.uniforms);
this.setFramebufferSizeUniform(directInteriorProgram.uniforms);
this.pathColorsBufferTexture.bind(this.gl, directInteriorProgram.uniforms, 0);
let indexCount = this.gl.getBufferParameter(this.gl.ELEMENT_ARRAY_BUFFER,
this.gl.BUFFER_SIZE) / UINT32_SIZE;
this.gl.drawElements(this.gl.TRIANGLES, indexCount, this.gl.UNSIGNED_INT, 0);
@ -711,16 +715,10 @@ class PathfinderView {
this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.meshes.coverCurveIndices);
// Draw direct curve parts.
this.gl.activeTexture(this.gl.TEXTURE0);
this.gl.bindTexture(this.gl.TEXTURE_2D, this.pathColorsBufferTexture.texture);
this.gl.uniformMatrix4fv(directCurveProgram.uniforms.uTransform, false, IDENTITY);
this.gl.uniform2i(directCurveProgram.uniforms.uFramebufferSize,
this.canvas.width,
this.canvas.height);
this.gl.uniform2i(directCurveProgram.uniforms.uPathColorsDimensions,
this.pathColorsBufferTexture.size.width,
this.pathColorsBufferTexture.size.height);
this.gl.uniform1i(directCurveProgram.uniforms.uPathColors, 0);
this.setTransformUniform(directCurveProgram.uniforms);
this.setFramebufferSizeUniform(directCurveProgram.uniforms);
this.pathColorsBufferTexture.bind(this.gl, directCurveProgram.uniforms, 0);
indexCount = this.gl.getBufferParameter(this.gl.ELEMENT_ARRAY_BUFFER,
this.gl.BUFFER_SIZE) / UINT32_SIZE;
this.gl.drawElements(this.gl.TRIANGLES, indexCount, this.gl.UNSIGNED_INT, 0);
@ -784,12 +782,16 @@ class PathfinderShaderProgram {
}
class PathfinderBufferTexture {
constructor(gl: WebGLRenderingContext, data: Float32Array | Uint8Array) {
constructor(gl: WebGLRenderingContext,
data: Float32Array | Uint8Array,
uniformName: string) {
const pixelCount = Math.ceil(data.length / 4);
const width = Math.ceil(Math.sqrt(pixelCount));
const height = Math.ceil(pixelCount / width);
this.size = { width: width, height: height };
this.uniformName = uniformName;
// Pad out with zeroes as necessary.
//
// FIXME(pcwalton): Do this earlier to save a copy here.
@ -809,8 +811,16 @@ class PathfinderBufferTexture {
setTextureParameters(gl, gl.NEAREST);
}
bind(gl: WebGLRenderingContext, uniforms: UniformMap, textureUnit: number) {
gl.activeTexture(gl.TEXTURE0 + textureUnit);
gl.bindTexture(gl.TEXTURE_2D, this.texture);
gl.uniform2i(uniforms[`${this.uniformName}Dimensions`], this.size.width, this.size.height);
gl.uniform1i(uniforms[this.uniformName], textureUnit);
}
readonly texture: WebGLTexture;
readonly size: Size2D;
readonly uniformName: string;
}
class NoAAStrategy implements AntialiasingStrategy {
@ -937,12 +947,17 @@ class ECAAStrategy implements AntialiasingStrategy {
attachMeshes(view: PathfinderView) {
const bVertexPositions = new Float32Array(view.meshData.bVertexPositions);
const bVertexPathIDs = new Uint8Array(view.meshData.bVertexPathIDs);
this.bVertexPositionBufferTexture = new PathfinderBufferTexture(view.gl, bVertexPositions);
this.bVertexPathIDBufferTexture = new PathfinderBufferTexture(view.gl, bVertexPathIDs);
this.bVertexPositionBufferTexture = new PathfinderBufferTexture(view.gl,
bVertexPositions,
'uBVertexPosition');
this.bVertexPathIDBufferTexture = new PathfinderBufferTexture(view.gl,
bVertexPathIDs,
'uBVertexPathID');
this.createEdgeDetectVAO(view);
this.createCoverVAO(view);
this.createLineVAO(view);
this.createLineVAOs(view);
this.createCurveVAOs(view);
this.createResolveVAO(view);
}
@ -1031,7 +1046,7 @@ class ECAAStrategy implements AntialiasingStrategy {
view.vertexArrayObjectExt.bindVertexArrayOES(null);
}
createLineVAO(view: PathfinderView) {
createLineVAOs(view: PathfinderView) {
const lineProgram = view.shaderPrograms.ecaaLine;
const attributes = lineProgram.attributes;
@ -1063,7 +1078,50 @@ class ECAAStrategy implements AntialiasingStrategy {
view.vertexArrayObjectExt.bindVertexArrayOES(null);
this.edgeVAOs = vaos as UpperAndLower<WebGLVertexArrayObject>;
this.lineVAOs = vaos as UpperAndLower<WebGLVertexArrayObject>;
}
createCurveVAOs(view: PathfinderView) {
const curveProgram = view.shaderPrograms.ecaaCurve;
const attributes = curveProgram.attributes;
const vaos: Partial<UpperAndLower<WebGLVertexArrayObject>> = {};
for (const direction of ['upper', 'lower'] as Array<'upper' | 'lower'>) {
vaos[direction] = view.vertexArrayObjectExt.createVertexArrayOES();
view.vertexArrayObjectExt.bindVertexArrayOES(vaos[direction]);
const curveIndexBuffer = {
upper: view.meshes.edgeUpperCurveIndices,
lower: view.meshes.edgeLowerCurveIndices,
}[direction];
view.gl.useProgram(curveProgram.program);
view.gl.bindBuffer(view.gl.ARRAY_BUFFER, view.quadPositionsBuffer);
view.gl.vertexAttribPointer(attributes.aQuadPosition, 2, view.gl.FLOAT, false, 0, 0);
view.gl.bindBuffer(view.gl.ARRAY_BUFFER, curveIndexBuffer);
view.gl.vertexAttribPointer(attributes.aCurveEndpointIndices,
4,
view.gl.UNSIGNED_SHORT,
false,
UINT32_SIZE * 4,
0);
view.gl.vertexAttribPointer(attributes.aCurveControlPointIndex,
2,
view.gl.UNSIGNED_SHORT,
false,
UINT32_SIZE * 4,
UINT32_SIZE * 2);
view.gl.enableVertexAttribArray(attributes.aQuadPosition);
view.gl.enableVertexAttribArray(attributes.aCurveEndpointIndices);
view.gl.enableVertexAttribArray(attributes.aCurveControlPointIndex);
view.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aCurveEndpointIndices, 1);
view.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aCurveControlPointIndex, 1);
view.gl.bindBuffer(view.gl.ELEMENT_ARRAY_BUFFER, view.quadElementsBuffer);
}
view.vertexArrayObjectExt.bindVertexArrayOES(null);
this.curveVAOs = vaos as UpperAndLower<WebGLVertexArrayObject>;
}
createResolveVAO(view: PathfinderView) {
@ -1115,6 +1173,7 @@ class ECAAStrategy implements AntialiasingStrategy {
// Antialias.
this.antialiasLines(view);
this.antialiasCurves(view);
// Resolve the antialiasing.
this.resolveAA(view);
@ -1178,22 +1237,10 @@ class ECAAStrategy implements AntialiasingStrategy {
view.gl.useProgram(coverProgram.program);
view.vertexArrayObjectExt.bindVertexArrayOES(this.coverVAO);
const uniforms = coverProgram.uniforms;
view.gl.uniformMatrix4fv(uniforms.uTransform, false, IDENTITY);
view.gl.uniform2i(uniforms.uFramebufferSize,
this.framebufferSize.width,
this.framebufferSize.height);
view.gl.uniform2i(uniforms.uBVertexPositionDimensions,
this.bVertexPositionBufferTexture.size.width,
this.bVertexPositionBufferTexture.size.height);
view.gl.uniform2i(uniforms.uBVertexPathIDDimensions,
this.bVertexPathIDBufferTexture.size.width,
this.bVertexPathIDBufferTexture.size.height);
view.gl.activeTexture(view.gl.TEXTURE0);
view.gl.bindTexture(view.gl.TEXTURE_2D, this.bVertexPositionBufferTexture.texture);
view.gl.uniform1i(uniforms.uBVertexPosition, 0);
view.gl.activeTexture(view.gl.TEXTURE1);
view.gl.bindTexture(view.gl.TEXTURE_2D, this.bVertexPathIDBufferTexture.texture);
view.gl.uniform1i(uniforms.uBVertexPathID, 1);
view.setTransformUniform(uniforms);
view.setFramebufferSizeUniform(uniforms);
this.bVertexPositionBufferTexture.bind(view.gl, uniforms, 0);
this.bVertexPathIDBufferTexture.bind(view.gl, uniforms, 1);
view.instancedArraysExt.drawElementsInstancedANGLE(view.gl.TRIANGLES,
6,
view.gl.UNSIGNED_BYTE,
@ -1202,9 +1249,7 @@ class ECAAStrategy implements AntialiasingStrategy {
view.vertexArrayObjectExt.bindVertexArrayOES(null);
}
antialiasLines(view: PathfinderView) {
// Set state for line antialiasing.
const lineProgram = view.shaderPrograms.ecaaLine;
setAAState(view: PathfinderView) {
view.gl.bindFramebuffer(view.gl.FRAMEBUFFER, this.aaFramebuffer);
view.gl.viewport(0, 0, this.framebufferSize.width, this.framebufferSize.height);
@ -1215,29 +1260,25 @@ class ECAAStrategy implements AntialiasingStrategy {
view.gl.blendEquation(view.gl.FUNC_REVERSE_SUBTRACT);
view.gl.blendFunc(view.gl.ONE, view.gl.ONE);
view.gl.enable(view.gl.BLEND);
}
// Antialias lines.
setAAUniforms(view: PathfinderView, uniforms: UniformMap) {
view.setTransformUniform(uniforms);
view.setFramebufferSizeUniform(uniforms);
this.bVertexPositionBufferTexture.bind(view.gl, uniforms, 0);
this.bVertexPathIDBufferTexture.bind(view.gl, uniforms, 1);
}
antialiasLines(view: PathfinderView) {
this.setAAState(view);
const lineProgram = view.shaderPrograms.ecaaLine;
view.gl.useProgram(lineProgram.program);
const uniforms = lineProgram.uniforms;
view.gl.uniformMatrix4fv(uniforms.uTransform, false, IDENTITY);
view.gl.uniform2i(uniforms.uFramebufferSize,
this.framebufferSize.width,
this.framebufferSize.height);
view.gl.uniform2i(uniforms.uBVertexPositionDimensions,
this.bVertexPositionBufferTexture.size.width,
this.bVertexPositionBufferTexture.size.height);
view.gl.uniform2i(uniforms.uBVertexPathIDDimensions,
this.bVertexPathIDBufferTexture.size.width,
this.bVertexPathIDBufferTexture.size.height);
view.gl.activeTexture(view.gl.TEXTURE0);
view.gl.bindTexture(view.gl.TEXTURE_2D, this.bVertexPositionBufferTexture.texture);
view.gl.uniform1i(uniforms.uBVertexPosition, 0);
view.gl.activeTexture(view.gl.TEXTURE1);
view.gl.bindTexture(view.gl.TEXTURE_2D, this.bVertexPathIDBufferTexture.texture);
view.gl.uniform1i(uniforms.uBVertexPathID, 1);
this.setAAUniforms(view, uniforms);
for (const direction of ['upper', 'lower'] as Array<keyof UpperAndLower<void>>) {
view.vertexArrayObjectExt.bindVertexArrayOES(this.edgeVAOs[direction]);
view.vertexArrayObjectExt.bindVertexArrayOES(this.lineVAOs[direction]);
view.gl.uniform1i(uniforms.uLowerPart, direction === 'lower' ? 1 : 0);
const count = {
upper: view.meshData.edgeUpperLineIndexCount,
@ -1253,6 +1294,31 @@ class ECAAStrategy implements AntialiasingStrategy {
view.vertexArrayObjectExt.bindVertexArrayOES(null);
}
antialiasCurves(view: PathfinderView) {
this.setAAState(view);
const curveProgram = view.shaderPrograms.ecaaCurve;
view.gl.useProgram(curveProgram.program);
const uniforms = curveProgram.uniforms;
this.setAAUniforms(view, uniforms);
for (const direction of ['upper', 'lower'] as Array<keyof UpperAndLower<void>>) {
view.vertexArrayObjectExt.bindVertexArrayOES(this.curveVAOs[direction]);
view.gl.uniform1i(uniforms.uLowerPart, direction === 'lower' ? 1 : 0);
const count = {
upper: view.meshData.edgeUpperCurveIndexCount,
lower: view.meshData.edgeLowerCurveIndexCount,
}[direction];
view.instancedArraysExt.drawElementsInstancedANGLE(view.gl.TRIANGLES,
6,
view.gl.UNSIGNED_BYTE,
0,
count);
}
view.vertexArrayObjectExt.bindVertexArrayOES(null);
}
resolveAA(view: PathfinderView) {
// Set state for ECAA resolve.
view.gl.bindFramebuffer(view.gl.FRAMEBUFFER, null);
@ -1265,9 +1331,7 @@ class ECAAStrategy implements AntialiasingStrategy {
const resolveProgram = view.shaderPrograms.ecaaResolve;
view.gl.useProgram(resolveProgram.program);
view.vertexArrayObjectExt.bindVertexArrayOES(this.resolveVAO);
view.gl.uniform2i(resolveProgram.uniforms.uFramebufferSize,
this.framebufferSize.width,
this.framebufferSize.height);
view.setFramebufferSizeUniform(resolveProgram.uniforms);
view.gl.activeTexture(view.gl.TEXTURE0);
view.gl.bindTexture(view.gl.TEXTURE_2D, this.bgColorTexture);
view.gl.uniform1i(resolveProgram.uniforms.uBGColor, 0);
@ -1295,7 +1359,8 @@ class ECAAStrategy implements AntialiasingStrategy {
aaFramebuffer: WebGLFramebuffer;
edgeDetectVAO: WebGLVertexArrayObject;
coverVAO: WebGLVertexArrayObject;
edgeVAOs: UpperAndLower<WebGLVertexArrayObject>;
lineVAOs: UpperAndLower<WebGLVertexArrayObject>;
curveVAOs: UpperAndLower<WebGLVertexArrayObject>;
resolveVAO: WebGLVertexArrayObject;
framebufferSize: Size2D;
}

View File

@ -45,19 +45,19 @@ bool computeQuadPosition(out vec2 outPosition,
vec2 quadPosition,
ivec2 framebufferSize,
mat4 transform) {
leftPosition = transformVertexPosition(leftPosition, transform);
rightPosition = transformVertexPosition(rightPosition, transform);
if (abs(leftPosition.x - rightPosition.x) <= EPSILON) {
outPosition = vec2(0.0);
return false;
}
leftPosition = transformVertexPosition(leftPosition, transform);
rightPosition = transformVertexPosition(rightPosition, transform);
vec2 verticalExtents = vec2(min(leftPosition.y, rightPosition.y),
max(leftPosition.y, rightPosition.y));
vec4 roundedExtents = vec4(floor(vec2(leftPosition.x, verticalExtents.x)),
ceil(vec2(rightPosition.x, verticalExtents.y)));
ceil(vec2(rightPosition.x, verticalExtents.y)));
// FIXME(pcwalton): Use a separate VBO for this.
quadPosition = (quadPosition + 1.0) * 0.5;
@ -76,6 +76,8 @@ bool computeQuadPosition(out vec2 outPosition,
// * `pixelExtents` are the boundaries of the pixel (left/right/bottom/top respectively).
// * `p` and `q` are the Liang-Barsky clipping distances.
// * `lowerPart` is true if this is the lower half of the B-quad.
//
// FIXME(pcwalton): This API is ludicrous. Clean it up!
float computeCoverage(vec2 p0,
vec2 p1,
vec2 spanP0,

View File

@ -61,5 +61,5 @@ void main() {
float depth = convertPathIndexToDepthValue(pathID);
gl_Position = vec4(position, depth, 1.0);
vHorizontalExtents = roundedExtents.xz;
vHorizontalExtents = extents.xz;
}

View File

@ -45,11 +45,13 @@ void main() {
vec2 spanP1 = mix(mix(p0, cp, t.y), mix(cp, p1, t.y), t.y);
p0 = spanP0;
p1 = spanP1;
t = vec2(0.0, 1.0);
// Set up Liang-Barsky clipping.
vec4 p = (p1 - p0).xxyy, q = pixelExtents - p0.xxyy;
t = clamp(q.xy / p.xy, 0.0, 1.0);
spanP0 = p0 + p.yw * t.x;
spanP1 = p0 + p.yw * t.y;
// Compute area.
gl_FragColor = vec4(computeCoverage(p0, p1, p0, p1, t, pixelExtents, p, q, uLowerPart));
gl_FragColor = vec4(computeCoverage(p0, p1, spanP0, spanP1, t, pixelExtents, p, q, uLowerPart));
}

View File

@ -15,6 +15,5 @@ void main() {
vec4 bgColor = texture2D(uBGColor, vTexCoord);
vec4 fgColor = texture2D(uFGColor, vTexCoord);
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 = mix(bgColor, fgColor, alpha);
}