Initial setup for ECAA in the demo

This commit is contained in:
Patrick Walton 2017-08-15 22:09:09 -07:00
parent 3f7fc8baf6
commit b10807d217
8 changed files with 169 additions and 69 deletions

5
.gitignore vendored
View File

@ -1,6 +1,11 @@
/font-renderer/target /font-renderer/target
/partitioner/target /partitioner/target
/demo/client/target /demo/client/target
/demo/client/*.js
/demo/client/src/*.js
/demo/client/src/*.js.map
/demo/client/node_modules
/demo/client/package-lock.json
/demo/server/target /demo/server/target
Cargo.lock Cargo.lock

View File

@ -44,6 +44,7 @@
<div class="pf-bottom-control" id="pf-rendering-options-group"> <div class="pf-bottom-control" id="pf-rendering-options-group">
<select class="custom-select" id="pf-aa-level-select"> <select class="custom-select" id="pf-aa-level-select">
<option data-pf-type="none" data-pf-level="0" selected>No AA</option> <option data-pf-type="none" data-pf-level="0" selected>No AA</option>
<option data-pf-type="ecaa" data-pf-level="0">ECAA</option>
<option data-pf-type="ssaa" data-pf-level="2">2&times;SSAA</option> <option data-pf-type="ssaa" data-pf-level="2">2&times;SSAA</option>
<option data-pf-type="ssaa" data-pf-level="4">4&times;SSAA</option> <option data-pf-type="ssaa" data-pf-level="4">4&times;SSAA</option>
</select> </select>

View File

@ -84,7 +84,7 @@ interface AttributeMap {
interface AntialiasingStrategy { interface AntialiasingStrategy {
// Prepares any OpenGL data. This is only called on startup and canvas resize. // Prepares any OpenGL data. This is only called on startup and canvas resize.
init(gl: WebGLRenderingContext, framebufferSize: Size2D): void; init(view: PathfinderView, framebufferSize: Size2D): void;
// Called before direct rendering. // Called before direct rendering.
// //
@ -95,11 +95,6 @@ interface AntialiasingStrategy {
// //
// This usually performs the actual antialiasing and blits to the real framebuffer. // This usually performs the actual antialiasing and blits to the real framebuffer.
resolve(view: PathfinderView, shaders: ShaderMap<PathfinderShaderProgram>): void; resolve(view: PathfinderView, shaders: ShaderMap<PathfinderShaderProgram>): void;
// Returns the size of the framebuffer for direct rendering.
//
// For supersampling-based techniques, this may be larger than the actual framebuffer.
getFramebufferSize(): Size2D;
} }
type ShaderType = number; type ShaderType = number;
@ -318,7 +313,7 @@ class PathfinderView {
this.antialiasingStrategy = new (ANTIALIASING_STRATEGIES[aaType])(aaLevel); this.antialiasingStrategy = new (ANTIALIASING_STRATEGIES[aaType])(aaLevel);
let canvas = this.canvas; let canvas = this.canvas;
this.antialiasingStrategy.init(this.gl, { width: canvas.width, height: canvas.height }); this.antialiasingStrategy.init(this, { width: canvas.width, height: canvas.height });
this.setDirty(); this.setDirty();
} }
@ -327,10 +322,10 @@ class PathfinderView {
// Initialize the OpenGL context. // Initialize the OpenGL context.
this.gl = expectNotNull(this.canvas.getContext('webgl', { antialias: false, depth: true }), this.gl = expectNotNull(this.canvas.getContext('webgl', { antialias: false, depth: true }),
"Failed to initialize WebGL! Check that your browser supports it."); "Failed to initialize WebGL! Check that your browser supports it.");
this.drawBuffersExt = this.gl.getExtension('WEBGL_draw_buffers');
this.gl.getExtension('EXT_frag_depth'); this.gl.getExtension('EXT_frag_depth');
this.gl.getExtension('OES_element_index_uint'); this.gl.getExtension('OES_element_index_uint');
this.gl.getExtension('WEBGL_depth_texture'); this.gl.getExtension('WEBGL_depth_texture');
this.gl.getExtension('WEBGL_draw_buffers');
// Upload quad buffers. // Upload quad buffers.
this.quadPositionsBuffer = unwrapNull(this.gl.createBuffer()); this.quadPositionsBuffer = unwrapNull(this.gl.createBuffer());
@ -436,7 +431,7 @@ class PathfinderView {
this.canvas.width = framebufferSize.width; this.canvas.width = framebufferSize.width;
this.canvas.height = framebufferSize.height; this.canvas.height = framebufferSize.height;
this.antialiasingStrategy.init(this.gl, framebufferSize); this.antialiasingStrategy.init(this, framebufferSize);
this.setDirty(); this.setDirty();
} }
@ -485,10 +480,10 @@ class PathfinderView {
0, 0,
0); 0);
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.meshes.bVertexInfo); this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.meshes.bVertexInfo);
this.gl.vertexAttribPointer(directInteriorProgram.attributes.aPathDepth, this.gl.vertexAttribPointer(directInteriorProgram.attributes.aPathID,
1, 1,
this.gl.UNSIGNED_SHORT, // FIXME(pcwalton) this.gl.UNSIGNED_SHORT, // FIXME(pcwalton)
true, false,
B_VERTEX_QUAD_SIZE, B_VERTEX_QUAD_SIZE,
B_VERTEX_QUAD_PATH_ID_OFFSET); B_VERTEX_QUAD_PATH_ID_OFFSET);
this.gl.enableVertexAttribArray(directInteriorProgram.attributes.aPosition); this.gl.enableVertexAttribArray(directInteriorProgram.attributes.aPosition);
@ -530,10 +525,10 @@ class PathfinderView {
false, false,
B_VERTEX_QUAD_SIZE, B_VERTEX_QUAD_SIZE,
B_VERTEX_QUAD_TEX_COORD_OFFSET); B_VERTEX_QUAD_TEX_COORD_OFFSET);
this.gl.vertexAttribPointer(directCurveProgram.attributes.aPathDepth, this.gl.vertexAttribPointer(directCurveProgram.attributes.aPathID,
1, 1,
this.gl.UNSIGNED_SHORT, // FIXME(pcwalton) this.gl.UNSIGNED_SHORT, // FIXME(pcwalton)
true, false,
B_VERTEX_QUAD_SIZE, B_VERTEX_QUAD_SIZE,
B_VERTEX_QUAD_PATH_ID_OFFSET); B_VERTEX_QUAD_PATH_ID_OFFSET);
this.gl.vertexAttribPointer(directCurveProgram.attributes.aSign, this.gl.vertexAttribPointer(directCurveProgram.attributes.aSign,
@ -566,6 +561,7 @@ class PathfinderView {
canvas: HTMLCanvasElement; canvas: HTMLCanvasElement;
gl: WebGLRenderingContext; gl: WebGLRenderingContext;
drawBuffersExt: any;
antialiasingStrategy: AntialiasingStrategy; antialiasingStrategy: AntialiasingStrategy;
shaderProgramsPromise: Promise<ShaderMap<PathfinderShaderProgram>>; shaderProgramsPromise: Promise<ShaderMap<PathfinderShaderProgram>>;
meshes: PathfinderMeshBuffers; meshes: PathfinderMeshBuffers;
@ -637,7 +633,7 @@ class NoAAStrategy implements AntialiasingStrategy {
this.framebufferSize = { width: 0, height: 0 }; this.framebufferSize = { width: 0, height: 0 };
} }
init(gl: WebGLRenderingContext, framebufferSize: Size2D) { init(view: PathfinderView, framebufferSize: Size2D) {
this.framebufferSize = framebufferSize; this.framebufferSize = framebufferSize;
} }
@ -647,10 +643,6 @@ class NoAAStrategy implements AntialiasingStrategy {
resolve(view: PathfinderView, shaders: ShaderMap<PathfinderShaderProgram>) {} resolve(view: PathfinderView, shaders: ShaderMap<PathfinderShaderProgram>) {}
getFramebufferSize() {
return this.framebufferSize;
}
framebufferSize: Size2D; framebufferSize: Size2D;
} }
@ -661,55 +653,55 @@ class SSAAStrategy implements AntialiasingStrategy {
this.supersampledFramebufferSize = { width: 0, height: 0 }; this.supersampledFramebufferSize = { width: 0, height: 0 };
} }
init(gl: WebGLRenderingContext, framebufferSize: Size2D) { init(view: PathfinderView, framebufferSize: Size2D) {
this.canvasFramebufferSize = framebufferSize; this.canvasFramebufferSize = framebufferSize;
this.supersampledFramebufferSize = { this.supersampledFramebufferSize = {
width: framebufferSize.width * 2, width: framebufferSize.width * 2,
height: framebufferSize.height * (this.level == 2 ? 1 : 2), height: framebufferSize.height * (this.level == 2 ? 1 : 2),
}; };
this.supersampledColorTexture = unwrapNull(gl.createTexture()); this.supersampledColorTexture = unwrapNull(view.gl.createTexture());
gl.bindTexture(gl.TEXTURE_2D, this.supersampledColorTexture); view.gl.bindTexture(view.gl.TEXTURE_2D, this.supersampledColorTexture);
gl.texImage2D(gl.TEXTURE_2D, view.gl.texImage2D(view.gl.TEXTURE_2D,
0, 0,
gl.RGBA, view.gl.RGBA,
this.supersampledFramebufferSize.width, this.supersampledFramebufferSize.width,
this.supersampledFramebufferSize.height, this.supersampledFramebufferSize.height,
0, 0,
gl.RGBA, view.gl.RGBA,
gl.UNSIGNED_BYTE, view.gl.UNSIGNED_BYTE,
null); null);
setTextureParameters(gl, gl.LINEAR); setTextureParameters(view.gl, view.gl.LINEAR);
this.supersampledDepthTexture = unwrapNull(gl.createTexture()); this.supersampledDepthTexture = unwrapNull(view.gl.createTexture());
gl.bindTexture(gl.TEXTURE_2D, this.supersampledDepthTexture); view.gl.bindTexture(view.gl.TEXTURE_2D, this.supersampledDepthTexture);
gl.texImage2D(gl.TEXTURE_2D, view.gl.texImage2D(view.gl.TEXTURE_2D,
0, 0,
gl.DEPTH_COMPONENT, view.gl.DEPTH_COMPONENT,
this.supersampledFramebufferSize.width, this.supersampledFramebufferSize.width,
this.supersampledFramebufferSize.height, this.supersampledFramebufferSize.height,
0, 0,
gl.DEPTH_COMPONENT, view.gl.DEPTH_COMPONENT,
gl.UNSIGNED_INT, view.gl.UNSIGNED_INT,
null); null);
setTextureParameters(gl, gl.NEAREST); setTextureParameters(view.gl, view.gl.NEAREST);
this.supersampledFramebuffer = unwrapNull(gl.createFramebuffer()); this.supersampledFramebuffer = unwrapNull(view.gl.createFramebuffer());
gl.bindFramebuffer(gl.FRAMEBUFFER, this.supersampledFramebuffer); view.gl.bindFramebuffer(view.gl.FRAMEBUFFER, this.supersampledFramebuffer);
gl.framebufferTexture2D(gl.FRAMEBUFFER, view.gl.framebufferTexture2D(view.gl.FRAMEBUFFER,
gl.COLOR_ATTACHMENT0, view.gl.COLOR_ATTACHMENT0,
gl.TEXTURE_2D, view.gl.TEXTURE_2D,
this.supersampledColorTexture, this.supersampledColorTexture,
0); 0);
gl.framebufferTexture2D(gl.FRAMEBUFFER, view.gl.framebufferTexture2D(view.gl.FRAMEBUFFER,
gl.DEPTH_ATTACHMENT, view.gl.DEPTH_ATTACHMENT,
gl.TEXTURE_2D, view.gl.TEXTURE_2D,
this.supersampledDepthTexture, this.supersampledDepthTexture,
0); 0);
assert(gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE, assert(view.gl.checkFramebufferStatus(view.gl.FRAMEBUFFER) == view.gl.FRAMEBUFFER_COMPLETE,
"The SSAA framebuffer was incomplete!"); "The SSAA framebuffer was incomplete!");
gl.bindFramebuffer(gl.FRAMEBUFFER, null); view.gl.bindFramebuffer(view.gl.FRAMEBUFFER, null);
} }
prepare(view: PathfinderView) { prepare(view: PathfinderView) {
@ -750,10 +742,6 @@ class SSAAStrategy implements AntialiasingStrategy {
view.gl.drawArrays(view.gl.TRIANGLE_STRIP, 0, 4); view.gl.drawArrays(view.gl.TRIANGLE_STRIP, 0, 4);
} }
getFramebufferSize() {
return this.supersampledFramebufferSize;
}
level: number; level: number;
canvasFramebufferSize: Readonly<Size2D>; canvasFramebufferSize: Readonly<Size2D>;
supersampledFramebufferSize: Readonly<Size2D>; supersampledFramebufferSize: Readonly<Size2D>;
@ -762,14 +750,102 @@ class SSAAStrategy implements AntialiasingStrategy {
supersampledFramebuffer: WebGLFramebuffer; supersampledFramebuffer: WebGLFramebuffer;
} }
class ECAAStrategy implements AntialiasingStrategy {
constructor(level: number) {
this.framebufferSize = { width: 0, height: 0 };
}
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!");
view.gl.bindFramebuffer(view.gl.FRAMEBUFFER, null);
}
prepare(view: PathfinderView) {
view.gl.bindFramebuffer(view.gl.FRAMEBUFFER, this.directFramebuffer);
view.gl.viewport(0, 0, this.framebufferSize.width, this.framebufferSize.height);
}
resolve(view: PathfinderView, shaders: ShaderMap<PathfinderShaderProgram>) {
// TODO(pcwalton)
}
directColorTexture: WebGLTexture;
directPathIDTexture: WebGLTexture;
directDepthTexture: WebGLTexture;
directFramebuffer: WebGLFramebuffer;
framebufferSize: Size2D;
}
interface AntialiasingStrategyTable { interface AntialiasingStrategyTable {
none: typeof NoAAStrategy; none: typeof NoAAStrategy;
ssaa: typeof SSAAStrategy; ssaa: typeof SSAAStrategy;
ecaa: typeof ECAAStrategy;
} }
const ANTIALIASING_STRATEGIES: AntialiasingStrategyTable = { const ANTIALIASING_STRATEGIES: AntialiasingStrategyTable = {
none: NoAAStrategy, none: NoAAStrategy,
ssaa: SSAAStrategy, ssaa: SSAAStrategy,
ecaa: ECAAStrategy,
}; };
function main() { function main() {

View File

@ -38,3 +38,7 @@ vec4 fetchFloat4Data(sampler2D dataTexture, int index, ivec2 dimensions) {
vec4 fetchFloat4NormIndexedData(sampler2D dataTexture, float normIndex, ivec2 dimensions) { vec4 fetchFloat4NormIndexedData(sampler2D dataTexture, float normIndex, ivec2 dimensions) {
return fetchFloat4Data(dataTexture, int(normIndex * float(dimensions.x)), dimensions); return fetchFloat4Data(dataTexture, int(normIndex * float(dimensions.x)), dimensions);
} }
vec2 packPathID(int pathID) {
return vec2(imod(pathID, 256), pathID / 256) / 255.0;
}

View File

@ -9,11 +9,13 @@
precision highp float; precision highp float;
varying vec4 vColor; varying vec4 vColor;
varying vec2 vPathID;
varying vec2 vTexCoord; varying vec2 vTexCoord;
varying float vSign; varying float vSign;
void main() { void main() {
float side = vTexCoord.x * vTexCoord.x - vTexCoord.y; float side = vTexCoord.x * vTexCoord.x - vTexCoord.y;
float alpha = float(sign(side) == sign(vSign)); float alpha = float(sign(side) == sign(vSign));
gl_FragColor = vec4(vColor.rgb, vColor.a * alpha); gl_FragData[0] = vec4(vColor.rgb, vColor.a * alpha);
gl_FragData[1] = vec4(vPathID, 1.0, 1.0);
} }

View File

@ -11,19 +11,24 @@ uniform sampler2D uPathColors;
attribute vec2 aPosition; attribute vec2 aPosition;
attribute vec2 aTexCoord; attribute vec2 aTexCoord;
attribute float aPathDepth; attribute float aPathID;
attribute float aSign; attribute float aSign;
varying vec4 vColor; varying vec4 vColor;
varying vec2 vPathID;
varying vec2 vTexCoord; varying vec2 vTexCoord;
varying float vSign; varying float vSign;
void main() { void main() {
int pathID = int(aPathID);
vec2 position = transformVertexPosition(aPosition, uTransform); vec2 position = transformVertexPosition(aPosition, uTransform);
position = convertScreenToClipSpace(position, uFramebufferSize); position = convertScreenToClipSpace(position, uFramebufferSize);
gl_Position = vec4(position, aPathDepth, 1.0); float depth = convertPathIndexToDepthValue(pathID);
gl_Position = vec4(position, depth, 1.0);
vColor = fetchFloat4NormIndexedData(uPathColors, aPathDepth, uPathColorsDimensions); vColor = fetchFloat4Data(uPathColors, pathID, uPathColorsDimensions);
vPathID = packPathID(pathID);
vTexCoord = vec2(aTexCoord) / 2.0; vTexCoord = vec2(aTexCoord) / 2.0;
vSign = aSign; vSign = aSign;
} }

View File

@ -5,7 +5,9 @@
precision highp float; precision highp float;
varying vec4 vColor; varying vec4 vColor;
varying vec2 vPathID;
void main() { void main() {
gl_FragColor = vColor; gl_FragData[0] = vColor;
gl_FragData[1] = vec4(vPathID, 1.0, 1.0);
} }

View File

@ -10,14 +10,19 @@ uniform ivec2 uPathColorsDimensions;
uniform sampler2D uPathColors; uniform sampler2D uPathColors;
attribute vec2 aPosition; attribute vec2 aPosition;
attribute float aPathDepth; attribute float aPathID;
varying vec4 vColor; varying vec4 vColor;
varying vec2 vPathID;
void main() { void main() {
int pathID = int(aPathID);
vec2 position = transformVertexPosition(aPosition, uTransform); vec2 position = transformVertexPosition(aPosition, uTransform);
position = convertScreenToClipSpace(position, uFramebufferSize); position = convertScreenToClipSpace(position, uFramebufferSize);
gl_Position = vec4(position, aPathDepth, 1.0); float depth = convertPathIndexToDepthValue(pathID);
gl_Position = vec4(position, depth, 1.0);
vColor = fetchFloat4NormIndexedData(uPathColors, aPathDepth, uPathColorsDimensions); vColor = fetchFloat4Data(uPathColors, pathID, uPathColorsDimensions);
vPathID = packPathID(pathID);
} }