diff --git a/src/PenSkin.js b/src/PenSkin.js index da269bdc2..0ee4d4f5d 100644 --- a/src/PenSkin.js +++ b/src/PenSkin.js @@ -113,7 +113,7 @@ class PenSkin extends Skin { this._lineShader = this._renderer._shaderManager.getShader(ShaderManager.DRAW_MODE.line, NO_EFFECTS); /** @type {twgl.ProgramInfo} */ - this._triangleShader = this._renderer._shaderManager.getShader(ShaderManager.DRAW_MODE.triangle, NO_EFFECTS); + this._triangleShader = this._renderer._shaderManager.getTriangleShader(); this._triangle_glbuffer = gl.createBuffer(); this._triangle_position_loc = gl.getAttribLocation(this._triangleShader.program, 'a_position'); this._triangle_color_loc = gl.getAttribLocation(this._triangleShader.program, 'a_triangleColor'); diff --git a/src/ShaderManager.js b/src/ShaderManager.js index 234ff244e..a1d577d52 100644 --- a/src/ShaderManager.js +++ b/src/ShaderManager.js @@ -20,6 +20,13 @@ class ShaderManager { this._shaderCache[modeName] = []; } } + + /** + * The pen triangle shader, built on first use. + * @type {?ProgramInfo} + * @private + */ + this._triangleShader = null; } /** @@ -68,6 +75,37 @@ class ShaderManager { const fsFullText = definesText + require('raw-loader!./shaders/sprite.frag'); /* eslint-enable global-require */ + return this._compileProgram(vsFullText, fsFullText, `mode ${drawMode}, effects ${effectBits}`); + } + + /** + * Fetch the shader that draws batched pen triangles with a color per vertex. + * It is built from its own source and kept out of the sprite shader cache because extensions + * rewrite the sprite shader text inside _buildShader and delete every cached sprite program. + * @returns {ProgramInfo} The shader's program info. + */ + getTriangleShader () { + if (!this._triangleShader) { + /* eslint-disable global-require */ + this._triangleShader = this._compileProgram( + require('raw-loader!./shaders/triangle.vert'), + require('raw-loader!./shaders/triangle.frag'), + 'pen triangles' + ); + /* eslint-enable global-require */ + } + return this._triangleShader; + } + + /** + * Compile and link a vertex and fragment shader. + * @param {string} vsFullText The vertex shader source. + * @param {string} fsFullText The fragment shader source. + * @param {string} description What the shader is for, used in the error message. + * @returns {ProgramInfo} The new shader's program info. + * @private + */ + _compileProgram (vsFullText, fsFullText, description) { let errorMessage = null; const onError = newError => { // twgl won't log the error when we provide a custom error callback, so log it ourselves @@ -87,7 +125,7 @@ class ShaderManager { const preview = `vertex starts "${vsFullText.slice(0, 80)}", ` + `fragment starts "${fsFullText.slice(0, 80)}"`; throw new Error( - `Failed to compile shader (mode ${drawMode}, effects ${effectBits}): ${reason} (${preview})` + `Failed to compile shader (${description}): ${reason} (${preview})` ); } return program; @@ -203,12 +241,7 @@ ShaderManager.DRAW_MODE = { /** * Draw the background in a certain color. Must sometimes be used instead of gl.clear. */ - background: 'background', - - /** - * Draw batched pen triangles with a color per vertex. - */ - triangle: 'triangle' + background: 'background' }; module.exports = ShaderManager; diff --git a/src/shaders/sprite.frag b/src/shaders/sprite.frag index ed2598ade..a2de79ba5 100644 --- a/src/shaders/sprite.frag +++ b/src/shaders/sprite.frag @@ -43,10 +43,6 @@ varying float v_lineLength; uniform vec4 u_backgroundColor; #endif // DRAW_MODE_background -#ifdef DRAW_MODE_triangle -varying vec4 v_triangleColor; -#endif // DRAW_MODE_triangle - uniform sampler2D u_skin; #ifndef DRAW_MODE_background @@ -119,7 +115,7 @@ const vec2 kCenter = vec2(0.5, 0.5); void main() { - #if !(defined(DRAW_MODE_line) || defined(DRAW_MODE_background) || defined(DRAW_MODE_triangle)) + #if !(defined(DRAW_MODE_line) || defined(DRAW_MODE_background)) vec2 texcoord0 = v_texCoord; #ifdef ENABLE_mosaic @@ -225,7 +221,7 @@ void main() gl_FragColor.rgb /= gl_FragColor.a + epsilon; #endif - #endif // !(defined(DRAW_MODE_line) || defined(DRAW_MODE_background) || defined(DRAW_MODE_triangle)) + #endif // !(defined(DRAW_MODE_line) || defined(DRAW_MODE_background)) #ifdef DRAW_MODE_line // Maaaaagic antialiased-line-with-round-caps shader. @@ -247,10 +243,6 @@ void main() gl_FragColor = v_lineColor * clamp(1.0 - line, 0.0, 1.0); #endif // DRAW_MODE_line - #ifdef DRAW_MODE_triangle - gl_FragColor = v_triangleColor; - #endif // DRAW_MODE_triangle - #ifdef DRAW_MODE_background gl_FragColor = u_backgroundColor; #endif diff --git a/src/shaders/sprite.vert b/src/shaders/sprite.vert index 9f612f696..3157e1666 100644 --- a/src/shaders/sprite.vert +++ b/src/shaders/sprite.vert @@ -16,12 +16,7 @@ varying vec4 v_penPoints; const float epsilon = 1e-3; #endif -#ifdef DRAW_MODE_triangle -attribute vec4 a_triangleColor; -varying vec4 v_triangleColor; -#endif - -#if !(defined(DRAW_MODE_line) || defined(DRAW_MODE_background) || defined(DRAW_MODE_triangle)) +#if !(defined(DRAW_MODE_line) || defined(DRAW_MODE_background)) uniform mat4 u_projectionMatrix; uniform mat4 u_modelMatrix; attribute vec2 a_texCoord; @@ -80,9 +75,6 @@ void main() { v_penPoints = a_penPoints; #elif defined(DRAW_MODE_background) gl_Position = vec4(a_position * 2.0, 0, 1); - #elif defined(DRAW_MODE_triangle) - gl_Position = vec4(a_position * 2.0, 0, 1); - v_triangleColor = a_triangleColor; #else gl_Position = u_projectionMatrix * u_modelMatrix * vec4(a_position, 0, 1); v_texCoord = a_texCoord; diff --git a/src/shaders/triangle.frag b/src/shaders/triangle.frag new file mode 100644 index 000000000..254edeb71 --- /dev/null +++ b/src/shaders/triangle.frag @@ -0,0 +1,8 @@ +precision mediump float; + +varying vec4 v_triangleColor; + +void main() +{ + gl_FragColor = v_triangleColor; +} diff --git a/src/shaders/triangle.vert b/src/shaders/triangle.vert new file mode 100644 index 000000000..392a6aafa --- /dev/null +++ b/src/shaders/triangle.vert @@ -0,0 +1,11 @@ +precision mediump float; + +attribute vec2 a_position; +attribute vec4 a_triangleColor; + +varying vec4 v_triangleColor; + +void main() { + gl_Position = vec4(a_position * 2.0, 0, 1); + v_triangleColor = a_triangleColor; +} diff --git a/test/unit/ShaderSourceTests.js b/test/unit/ShaderSourceTests.js new file mode 100644 index 000000000..985850434 --- /dev/null +++ b/test/unit/ShaderSourceTests.js @@ -0,0 +1,27 @@ +const test = require('tap').test; +const fs = require('fs'); +const path = require('path'); + +const readShader = name => fs.readFileSync(path.join(__dirname, '../../src/shaders', name), 'utf8'); + +// Extensions such as SharkPool's Looks Expanded patch the sprite shaders with find-and-replace, +// so these lines have to stay exactly as they are in TurboWarp. +test('sprite shaders keep the lines extensions patch', t => { + const vert = readShader('sprite.vert'); + t.ok(vert.includes('#if !(defined(DRAW_MODE_line) || defined(DRAW_MODE_background))\n')); + t.ok(vert.includes('void main() {')); + t.ok(vert.includes('v_texCoord = a_texCoord;')); + t.ok(vert.includes('vec4(a_position, 0, 1)')); + + const frag = readShader('sprite.frag'); + t.ok(frag.includes('uniform sampler2D u_skin;')); + t.ok(frag.includes('#if defined(ENABLE_color) || defined(ENABLE_brightness)')); + t.ok(frag.includes('gl_FragColor.rgb = clamp(gl_FragColor.rgb / (gl_FragColor.a + epsilon), 0.0, 1.0);')); + t.end(); +}); + +test('pen triangles are not drawn with the sprite shaders', t => { + t.notOk(readShader('sprite.vert').includes('DRAW_MODE_triangle')); + t.notOk(readShader('sprite.frag').includes('DRAW_MODE_triangle')); + t.end(); +});