diff --git a/demo/client/src/index.ts b/demo/client/src/index.ts index 409431bc..11244894 100644 --- a/demo/client/src/index.ts +++ b/demo/client/src/index.ts @@ -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; + this.lineVAOs = vaos as UpperAndLower; + } + + createCurveVAOs(view: PathfinderView) { + const curveProgram = view.shaderPrograms.ecaaCurve; + const attributes = curveProgram.attributes; + + const vaos: Partial> = {}; + 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; } 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>) { - 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>) { + 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; + lineVAOs: UpperAndLower; + curveVAOs: UpperAndLower; resolveVAO: WebGLVertexArrayObject; framebufferSize: Size2D; } diff --git a/shaders/gles2/common.inc.glsl b/shaders/gles2/common.inc.glsl index 85e42130..dc493f4b 100644 --- a/shaders/gles2/common.inc.glsl +++ b/shaders/gles2/common.inc.glsl @@ -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, diff --git a/shaders/gles2/ecaa-cover.vs.glsl b/shaders/gles2/ecaa-cover.vs.glsl index f94c3d39..8a49edf3 100644 --- a/shaders/gles2/ecaa-cover.vs.glsl +++ b/shaders/gles2/ecaa-cover.vs.glsl @@ -61,5 +61,5 @@ void main() { float depth = convertPathIndexToDepthValue(pathID); gl_Position = vec4(position, depth, 1.0); - vHorizontalExtents = roundedExtents.xz; + vHorizontalExtents = extents.xz; } diff --git a/shaders/gles2/ecaa-curve.fs.glsl b/shaders/gles2/ecaa-curve.fs.glsl index 49b978d0..861c7fc4 100644 --- a/shaders/gles2/ecaa-curve.fs.glsl +++ b/shaders/gles2/ecaa-curve.fs.glsl @@ -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)); } diff --git a/shaders/gles2/ecaa-resolve.fs.glsl b/shaders/gles2/ecaa-resolve.fs.glsl index 058d18d9..76800e72 100644 --- a/shaders/gles2/ecaa-resolve.fs.glsl +++ b/shaders/gles2/ecaa-resolve.fs.glsl @@ -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); }