diff --git a/demo/client/src/index.ts b/demo/client/src/index.ts index 7fd48d6b..2c5f8f3f 100644 --- a/demo/client/src/index.ts +++ b/demo/client/src/index.ts @@ -8,6 +8,8 @@ const FONT_SIZE: number = 16.0; const PARTITION_FONT_ENDPOINT_URL: string = "/partition-font"; +const COMMON_SHADER_URL: string = '/glsl/gles2/common.inc.glsl'; + const SHADER_URLS: ShaderURLMap = { directCurve: { vertex: "/glsl/gles2/direct-curve.vs.glsl", @@ -20,7 +22,7 @@ const SHADER_URLS: ShaderURLMap = { }; interface ShaderURLMap { - [shaderName: string]: { 'vertex': string, 'fragment': string } + [shaderName: string]: { 'vertex': string, 'fragment': string }; } interface ShaderMap { @@ -37,15 +39,21 @@ type ShaderTypeName = 'vertex' | 'fragment'; function expect(value: T | null, message: string): T { if (value == null) - throw new Error(message); + throw new PathfinderError(message); return value; } +class PathfinderError extends Error { + constructor(message?: string | undefined) { + super(message); + } +} + class PathfinderMeshes { constructor(encodedResponse: string) { const response = JSON.parse(encodedResponse); if (!('Ok' in response)) - throw new Error("Failed to partition the font!"); + throw new PathfinderError("Failed to partition the font!"); const meshes = response.Ok; this.bQuadPositions = base64js.toByteArray(meshes.bQuadPositions); this.bQuadInfo = base64js.toByteArray(meshes.bQuadInfo); @@ -80,7 +88,7 @@ class AppController { fontLoaded() { this.font = opentype.parse(this.fontData); if (!this.font.supported) - throw new Error("The font type is unsupported."); + throw new PathfinderError("The font type is unsupported."); const glyphIDs = this.font.stringToGlyphs(TEXT).map((glyph: any) => glyph.index); @@ -120,7 +128,7 @@ class PathfinderView { this.initContext(); - this.loadShaders().then(shaders => this.shaderProgramsPromise = this.linkShaders(shaders)); + this.shaderProgramsPromise = this.loadShaders().then(shaders => this.linkShaders(shaders)); window.addEventListener('resize', () => this.resizeToFit(), false); this.resizeToFit(); @@ -137,38 +145,65 @@ class PathfinderView { loadShaders(): Promise { let shaders: ShaderMap = {}; - const shaderKeys = Object.keys(SHADER_URLS); + return window.fetch(COMMON_SHADER_URL) + .then((response) => response.text()) + .then((commonSource) => { + const shaderKeys = Object.keys(SHADER_URLS); - let promises = []; - for (const shaderKey of shaderKeys) { - for (const typeName of ['vertex', 'fragment'] as Array) { - const type = { - vertex: this.gl.VERTEX_SHADER, - fragment: this.gl.FRAGMENT_SHADER, - }[typeName]; + let promises = []; + for (const shaderKey of shaderKeys) { + for (const typeName of ['vertex', 'fragment'] as Array) { + const type = { + vertex: this.gl.VERTEX_SHADER, + fragment: this.gl.FRAGMENT_SHADER, + }[typeName]; - const url = SHADER_URLS[shaderKey][typeName]; - promises.push(window.fetch(url).then((response) => { - return response.text().then((source) => { + const url = SHADER_URLS[shaderKey][typeName]; + promises.push(window.fetch(url) + .then(response => response.text()) + .then(source => { const shader = this.gl.createShader(type); if (shader == null) - throw new Error("Failed to create shader!"); - this.gl.shaderSource(shader, source); + throw new PathfinderError("Failed to create shader!"); + this.gl.shaderSource(shader, commonSource + "\n#line 1\n" + source); this.gl.compileShader(shader); + if (this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS) == 0) { + const infoLog = this.gl.getShaderInfoLog(shader); + throw new PathfinderError(`Failed to compile ${typeName} shader ` + + `"${shaderKey}":\n${infoLog}`); + } + if (!(shaderKey in shaders)) shaders[shaderKey] = {}; shaders[shaderKey][type] = shader; - }); - })); + })); + } } - } - return Promise.all(promises).then(() => shaders); + return Promise.all(promises); + }).then(() => shaders); } linkShaders(shaders: ShaderMap): Promise { - // TODO(pcwalton) - throw new Error("TODO"); + return new Promise((resolve, reject) => { + let shaderProgramMap: ShaderProgramMap = {}; + for (const shaderKey of Object.keys(shaders)) { + const program = expect(this.gl.createProgram(), "Failed to create shader program!"); + const compiledShaders = shaders[shaderKey]; + for (const compiledShader of Object.values(compiledShaders)) + this.gl.attachShader(program, compiledShader); + this.gl.linkProgram(program); + + if (this.gl.getProgramParameter(program, this.gl.LINK_STATUS) == 0) { + const infoLog = this.gl.getProgramInfoLog(program); + throw new PathfinderError(`Failed to link program "${program}":\n${infoLog}`); + } + + shaderProgramMap[shaderKey] = program; + } + + resolve(shaderProgramMap); + }); } resizeToFit() { @@ -182,7 +217,7 @@ class PathfinderView { } class PathfinderShaderProgram { - constructor(vertexShaderSource: string, fragmentShaderSource: string) { + constructor(vertexShader: WebGLShader, fragmentShader: WebGLShader) { // TODO(pcwalton) } } diff --git a/demo/client/tsconfig.json b/demo/client/tsconfig.json index 1417c77c..ed668865 100644 --- a/demo/client/tsconfig.json +++ b/demo/client/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "es6", + "target": "ES2017", "module": "commonjs", "types": [ "node" diff --git a/partitioner/src/lib.rs b/partitioner/src/lib.rs index 84bb79f9..2428d5a1 100644 --- a/partitioner/src/lib.rs +++ b/partitioner/src/lib.rs @@ -80,13 +80,13 @@ pub enum AntialiasingMode { Ecaa = 1, } -#[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize)] +#[derive(Clone, Copy, PartialEq, Debug)] #[repr(u8)] -pub enum BVertexKind { - Endpoint0 = 0, - Endpoint1 = 1, - ConvexControlPoint = 2, - ConcaveControlPoint = 3, +pub(crate) enum BVertexKind { + Endpoint0, + Endpoint1, + ConvexControlPoint, + ConcaveControlPoint, } #[derive(Clone, Copy, Debug, Serialize, Deserialize)] @@ -94,22 +94,23 @@ pub enum BVertexKind { pub struct BVertexInfo { pub path_id: u32, pub tex_coord: [u8; 2], - pub kind: BVertexKind, + pub sign: i8, pad: u8, } impl BVertexInfo { #[inline] - pub fn new(kind: BVertexKind, path_id: u32) -> BVertexInfo { - let tex_coord = match kind { - BVertexKind::Endpoint0 => [0, 0], - BVertexKind::Endpoint1 => [2, 2], - BVertexKind::ConcaveControlPoint | BVertexKind::ConvexControlPoint => [1, 0], + pub(crate) fn new(kind: BVertexKind, path_id: u32) -> BVertexInfo { + let (tex_coord, sign) = match kind { + BVertexKind::Endpoint0 => ([0, 0], 0), + BVertexKind::Endpoint1 => ([2, 2], 0), + BVertexKind::ConcaveControlPoint => ([1, 0], 1), + BVertexKind::ConvexControlPoint => ([1, 0], -1), }; BVertexInfo { path_id: path_id, tex_coord: tex_coord, - kind: kind, + sign: sign, pad: 0, } } diff --git a/partitioner/src/partitioner.h b/partitioner/src/partitioner.h index acc1d085..c315f896 100644 --- a/partitioner/src/partitioner.h +++ b/partitioner/src/partitioner.h @@ -9,11 +9,6 @@ #define PF_ANTIALIASING_MODE_MSAA 0 #define PF_ANTIALIASING_MODE_ECAA 1 -#define PF_B_VERTEX_KIND_ENDPOINT_0 0 -#define PF_B_VERTEX_KIND_ENDPOINT_1 1 -#define PF_B_VERTEX_KIND_CONVEX_CONTROL_POINT 2 -#define PF_B_VERTEX_KIND_CONCAVE_CONTROL_POINT 3 - #ifdef __cplusplus extern "C" { #endif @@ -22,8 +17,6 @@ typedef uint8_t pf_antialiasing_mode_t; typedef uint16_t pf_float16_t; -typedef uint8_t pf_b_vertex_kind_t; - struct pf_point2d_f32 { float x, y; }; @@ -40,7 +33,7 @@ typedef struct pf_matrix2d_f32 pf_matrix2d_f32_t; struct pf_b_vertex_info { uint32_t path_id; uint8_t tex_coord[2]; - pf_b_vertex_kind_t kind; + int8_t sign; uint8_t pad; }; diff --git a/partitioner/src/partitioner.rs b/partitioner/src/partitioner.rs index 6aa1ccd5..a7200024 100644 --- a/partitioner/src/partitioner.rs +++ b/partitioner/src/partitioner.rs @@ -1017,11 +1017,11 @@ struct SubdividedActiveEdge { impl SubdividedActiveEdge { fn shape(&self, b_vertex_info: &[BVertexInfo]) -> Shape { if self.left_curve_control_point == u32::MAX { - return Shape::Flat - } - match b_vertex_info[self.left_curve_control_point as usize].kind { - BVertexKind::ConvexControlPoint => Shape::Convex, - _ => Shape::Concave, + Shape::Flat + } else if b_vertex_info[self.left_curve_control_point as usize].sign < 0 { + Shape::Convex + } else { + Shape::Concave } } } diff --git a/shaders/gles2/common.inc.glsl b/shaders/gles2/common.inc.glsl index 0963cbd4..29fe9db8 100644 --- a/shaders/gles2/common.inc.glsl +++ b/shaders/gles2/common.inc.glsl @@ -2,8 +2,19 @@ // // Copyright (c) 2017 Mozilla Foundation +#version 100 + #define MAX_PATHS 65536 +precision highp float; + +// https://stackoverflow.com/a/36078859 +int imod(int ia, int ib) { + float a = float(ia), b = float(ib); + float m = a - floor((a + 0.5) / b) * b; + return int(floor(m + 0.5)); +} + vec2 transformVertexPosition(vec2 position, mat4 transform) { return (transform * vec4(position, 0.0, 1.0)).xy; } @@ -17,5 +28,10 @@ float convertPathIndexToDepthValue(int pathIndex) { } vec4 fetchFloat4Data(sampler2D dataTexture, int index, ivec2 dimensions) { - return texture2D(dataTexture, (float(index) + 0.5) / vec2(dimensions)); + ivec2 pixelCoord = ivec2(imod(index, dimensions.x), index / dimensions.x); + return texture2D(dataTexture, (vec2(pixelCoord) + 0.5) / vec2(dimensions)); +} + +vec4 fetchFloat4NormIndexedData(sampler2D dataTexture, float normIndex, ivec2 dimensions) { + return fetchFloat4Data(dataTexture, int(normIndex * float(dimensions.x)), dimensions); } diff --git a/shaders/gles2/direct-curve.fs.glsl b/shaders/gles2/direct-curve.fs.glsl index d83b6e43..80ad6c58 100644 --- a/shaders/gles2/direct-curve.fs.glsl +++ b/shaders/gles2/direct-curve.fs.glsl @@ -6,8 +6,6 @@ // It is therefore unsuitable for ECAA, but it's fast (specifically, preserving early Z) and // compatible with OpenGL ES 2.0. -#version 100 - precision highp float; varying vec4 vColor; diff --git a/shaders/gles2/direct-curve.vs.glsl b/shaders/gles2/direct-curve.vs.glsl index f05e2679..ebd1e2bf 100644 --- a/shaders/gles2/direct-curve.vs.glsl +++ b/shaders/gles2/direct-curve.vs.glsl @@ -2,8 +2,6 @@ // // Copyright (c) 2017 Mozilla Foundation -#version 100 - precision highp float; uniform mat4 uTransform; @@ -12,8 +10,9 @@ uniform ivec2 uPathColorsDimensions; uniform sampler2D uPathColors; attribute vec2 aPosition; -attribute ivec2 aTexCoord; -attribute int aKind; +attribute vec2 aTexCoord; +attribute float aPathDepth; +attribute float aSign; varying vec4 vColor; varying vec2 vTexCoord; @@ -22,20 +21,9 @@ varying float vSign; void main() { vec2 position = transformVertexPosition(aPosition, uTransform); position = convertScreenToClipSpace(position, uFramebufferSize); - float depth = convertPathIndexToDepthValue(aPathIndex); - gl_Position = vec4(position, depth, 1.0); + gl_Position = vec4(position, aPathDepth, 1.0); - vColor = fetchFloat4Data(uPathColors, aPathIndex, uPathColorsDimensions); + vColor = fetchFloat4NormIndexedData(uPathColors, aPathDepth, uPathColorsDimensions); vTexCoord = vec2(aTexCoord) / 2.0; - - switch (aKind) { - case PF_B_VERTEX_KIND_CONVEX_CONTROL_POINT: - vSign = -1.0; - break; - case PF_B_VERTEX_KIND_CONCAVE_CONTROL_POINT: - vSign = 1.0; - break; - default: - vSign = 0.0; - } + vSign = aSign; } diff --git a/shaders/gles2/direct-interior.fs.glsl b/shaders/gles2/direct-interior.fs.glsl index a864f43f..6f153a87 100644 --- a/shaders/gles2/direct-interior.fs.glsl +++ b/shaders/gles2/direct-interior.fs.glsl @@ -2,8 +2,6 @@ // // Copyright (c) 2017 Mozilla Foundation -#version 100 - precision highp float; varying vec4 vColor; diff --git a/shaders/gles2/direct-interior.vs.glsl b/shaders/gles2/direct-interior.vs.glsl index 27a77bdd..7417af9f 100644 --- a/shaders/gles2/direct-interior.vs.glsl +++ b/shaders/gles2/direct-interior.vs.glsl @@ -2,8 +2,6 @@ // // Copyright (c) 2017 Mozilla Foundation -#version 100 - precision highp float; uniform mat4 uTransform; @@ -13,15 +11,14 @@ uniform ivec2 uPathColorsDimensions; uniform sampler2D uPathColors; attribute vec2 aPosition; -attribute int aPathIndex; +attribute float aPathDepth; varying vec4 vColor; void main() { vec2 position = transformVertexPosition(aPosition, uTransform); position = convertScreenToClipSpace(position, uFramebufferSize); - float depth = convertPathIndexToDepthValue(aPathIndex); - gl_Position = vec4(position, depth, 1.0); + gl_Position = vec4(position, aPathDepth, 1.0); - vColor = fetchFloat4Data(uPathColors, aPathIndex, uPathColorsDimensions); + vColor = fetchFloat4NormIndexedData(uPathColors, aPathDepth, uPathColorsDimensions); }