From 6b82cf0b84429a7347200c045cb616bef11928c9 Mon Sep 17 00:00:00 2001 From: jahow Date: Mon, 19 Nov 2018 00:05:06 +0100 Subject: [PATCH] Simplified the API of WebGLHelper Now the shader and program caches are simply arrays of native WebGL created objects. The WebGLHelper simply takes the sources of the frag and vert shader and produces a program. This removes 2 classes & reduces the general verbosity of the API. Also a `getShaderCompilationErrors` was added on `WebGLHelper` to help debug GLSL errors. --- src/ol/renderer/webgl/PointsLayer.js | 19 +++-- src/ol/webgl/Fragment.js | 26 ------ src/ol/webgl/Helper.js | 120 +++++++++++++++++---------- src/ol/webgl/Vertex.js | 26 ------ test/spec/ol/webgl/helper.test.js | 34 ++++---- 5 files changed, 105 insertions(+), 120 deletions(-) delete mode 100644 src/ol/webgl/Fragment.js delete mode 100644 src/ol/webgl/Vertex.js diff --git a/src/ol/renderer/webgl/PointsLayer.js b/src/ol/renderer/webgl/PointsLayer.js index 87b981f475..5d7082ab47 100644 --- a/src/ol/renderer/webgl/PointsLayer.js +++ b/src/ol/renderer/webgl/PointsLayer.js @@ -5,8 +5,6 @@ import LayerRenderer from '../Layer'; import WebGLArrayBuffer from '../../webgl/Buffer'; import {DYNAMIC_DRAW, ARRAY_BUFFER, ELEMENT_ARRAY_BUFFER, FLOAT} from '../../webgl'; import WebGLHelper, {DefaultAttrib, DefaultUniform} from '../../webgl/Helper'; -import WebGLVertex from '../../webgl/Vertex'; -import WebGLFragment from '../../webgl/Fragment'; import GeometryType from '../../geom/GeometryType'; const VERTEX_SHADER = ` @@ -146,9 +144,11 @@ class WebGLPointsLayerRenderer extends LayerRenderer { this.verticesBuffer_ = new WebGLArrayBuffer([], DYNAMIC_DRAW); this.indicesBuffer_ = new WebGLArrayBuffer([], DYNAMIC_DRAW); - const vertexShader = new WebGLVertex(options.vertexShader || VERTEX_SHADER); - const fragmentShader = new WebGLFragment(options.fragmentShader || FRAGMENT_SHADER); - this.program_ = this.context_.getProgram(fragmentShader, vertexShader); + this.program_ = this.context_.getProgram( + options.fragmentShader || FRAGMENT_SHADER, + options.vertexShader || VERTEX_SHADER + ); + this.context_.useProgram(this.program_); this.sizeCallback_ = options.sizeCallback || function(feature) { @@ -229,6 +229,15 @@ class WebGLPointsLayerRenderer extends LayerRenderer { return true; } + + /** + * Will return the last shader compilation errors. If no error happened, will return null; + * @return {string|null} Errors, or null if last compilation was successful + * @api + */ + getShaderCompileErrors() { + return this.context_.getShaderCompileErrors(); + } } export default WebGLPointsLayerRenderer; diff --git a/src/ol/webgl/Fragment.js b/src/ol/webgl/Fragment.js deleted file mode 100644 index aeb90368f2..0000000000 --- a/src/ol/webgl/Fragment.js +++ /dev/null @@ -1,26 +0,0 @@ -/** - * @module ol/webgl/Fragment - */ - -import {FRAGMENT_SHADER} from '../webgl.js'; -import WebGLShader from '../webgl/Shader.js'; - -class WebGLFragment extends WebGLShader { - - /** - * @param {string} source Source. - */ - constructor(source) { - super(source); - } - - /** - * @inheritDoc - */ - getType() { - return FRAGMENT_SHADER; - } -} - - -export default WebGLFragment; diff --git a/src/ol/webgl/Helper.js b/src/ol/webgl/Helper.js index ec9f07e1db..05acb07e57 100644 --- a/src/ol/webgl/Helper.js +++ b/src/ol/webgl/Helper.js @@ -26,6 +26,15 @@ import WebGLPostProcessingPass from './PostProcessingPass'; * @property {WebGLBuffer} buffer */ +/** + * Shader types, either `FRAGMENT_SHADER` or `VERTEX_SHADER` + * @enum {number} + */ +export const ShaderType = { + FRAGMENT_SHADER: 0x8B30, + VERTEX_SHADER: 0x8B31 +}; + /** * Uniform names used in the default shaders. * @const @@ -222,15 +231,15 @@ class WebGLHelper extends Disposable { /** * @private - * @type {!Object} + * @type {!Array} */ - this.shaderCache_ = {}; + this.shaderCache_ = []; /** * @private - * @type {!Object} + * @type {!Array} */ - this.programCache_ = {}; + this.programCache_ = []; /** * @private @@ -319,6 +328,12 @@ class WebGLHelper extends Disposable { uniforms: options.uniforms }); }) : [new WebGLPostProcessingPass({webGlContext: gl})]; + + /** + * @type {string|null} + * @private + */ + this.shaderCompileErrors_ = null; } /** @@ -552,28 +567,6 @@ class WebGLHelper extends Disposable { }.bind(this)); } - /** - * Get shader from the cache if it's in the cache. Otherwise, create - * the WebGL shader, compile it, and add entry to cache. - * TODO: make compilation errors show up - * @param {import("./Shader.js").default} shaderObject Shader object. - * @return {WebGLShader} Shader. - * @api - */ - getShader(shaderObject) { - const shaderKey = getUid(shaderObject); - if (shaderKey in this.shaderCache_) { - return this.shaderCache_[shaderKey]; - } else { - const gl = this.getGL(); - const shader = gl.createShader(shaderObject.getType()); - gl.shaderSource(shader, shaderObject.getSource()); - gl.compileShader(shader); - this.shaderCache_[shaderKey] = shader; - return shader; - } - } - /** * Use a program. If the program is already in use, this will return `false`. * @param {WebGLProgram} program Program. @@ -594,27 +587,62 @@ class WebGLHelper extends Disposable { } /** - * Get the program from the cache if it's in the cache. Otherwise create - * the WebGL program, attach the shaders to it, and add an entry to the - * cache. - * @param {import("./Fragment.js").default} fragmentShaderObject Fragment shader. - * @param {import("./Vertex.js").default} vertexShaderObject Vertex shader. - * @return {WebGLProgram} Program. + * Will attempt to compile a vertex or fragment shader based on source + * On error, the shader will be returned but + * `gl.getShaderParameter(shader, gl.COMPILE_STATUS)` will return `true` + * Use `gl.getShaderInfoLog(shader)` to have details + * @param {string} source Shader source + * @param {ShaderType} type VERTEX_SHADER or FRAGMENT_SHADER + * @return {WebGLShader} Shader object + */ + compileShader(source, type) { + const gl = this.getGL(); + const shader = gl.createShader(type); + gl.shaderSource(shader, source); + gl.compileShader(shader); + this.shaderCache_.push(shader); + return shader; + } + + /** + * Create a program for a vertex and fragment shader. The shaders compilation may have failed: + * use `WebGLHelper.getShaderCompileErrors()`to have details if any. + * @param {string} fragmentShaderSource Fragment shader source. + * @param {string} vertexShaderSource Vertex shader source. + * @return {WebGLProgram} Program * @api */ - getProgram(fragmentShaderObject, vertexShaderObject) { - const programKey = getUid(fragmentShaderObject) + '/' + getUid(vertexShaderObject); - if (programKey in this.programCache_) { - return this.programCache_[programKey]; - } else { - const gl = this.getGL(); - const program = gl.createProgram(); - gl.attachShader(program, this.getShader(fragmentShaderObject)); - gl.attachShader(program, this.getShader(vertexShaderObject)); - gl.linkProgram(program); - this.programCache_[programKey] = program; - return program; + getProgram(fragmentShaderSource, vertexShaderSource) { + const gl = this.getGL(); + + const fragmentShader = this.compileShader(fragmentShaderSource, gl.FRAGMENT_SHADER); + const vertexShader = this.compileShader(vertexShaderSource, gl.VERTEX_SHADER); + this.shaderCompileErrors_ = null; + + if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) { + this.shaderCompileErrors_ = + `Fragment shader compilation failed:\n${gl.getShaderInfoLog(fragmentShader)}`; } + if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) { + this.shaderCompileErrors_ = (this.shaderCompileErrors_ || '') + + `Vertex shader compilation failed:\n${gl.getShaderInfoLog(vertexShader)}`; + } + + const program = gl.createProgram(); + gl.attachShader(program, fragmentShader); + gl.attachShader(program, vertexShader); + gl.linkProgram(program); + this.programCache_.push(program); + return program; + } + + /** + * Will return the last shader compilation errors. If no error happened, will return null; + * @return {string|null} Errors description, or null if last compilation was successful + * @api + */ + getShaderCompileErrors() { + return this.shaderCompileErrors_; } /** @@ -624,7 +652,7 @@ class WebGLHelper extends Disposable { * @api */ getUniformLocation(name) { - if (!this.uniformLocations_[name]) { + if (this.uniformLocations_[name] === undefined) { this.uniformLocations_[name] = this.getGL().getUniformLocation(this.currentProgram_, name); } return this.uniformLocations_[name]; @@ -637,7 +665,7 @@ class WebGLHelper extends Disposable { * @api */ getAttributeLocation(name) { - if (!this.attribLocations_[name]) { + if (this.attribLocations_[name] === undefined) { this.attribLocations_[name] = this.getGL().getAttribLocation(this.currentProgram_, name); } return this.attribLocations_[name]; diff --git a/src/ol/webgl/Vertex.js b/src/ol/webgl/Vertex.js deleted file mode 100644 index 579aaa2bae..0000000000 --- a/src/ol/webgl/Vertex.js +++ /dev/null @@ -1,26 +0,0 @@ -/** - * @module ol/webgl/Vertex - */ - -import {VERTEX_SHADER} from '../webgl.js'; -import WebGLShader from '../webgl/Shader.js'; - -class WebGLVertex extends WebGLShader { - - /** - * @param {string} source Source. - */ - constructor(source) { - super(source); - } - - /** - * @inheritDoc - */ - getType() { - return VERTEX_SHADER; - } -} - - -export default WebGLVertex; diff --git a/test/spec/ol/webgl/helper.test.js b/test/spec/ol/webgl/helper.test.js index c35f2e8dd9..0a48a337d6 100644 --- a/test/spec/ol/webgl/helper.test.js +++ b/test/spec/ol/webgl/helper.test.js @@ -1,6 +1,4 @@ import WebGLHelper from '../../../../src/ol/webgl/Helper'; -import WebGLVertex from '../../../../src/ol/webgl/Vertex'; -import WebGLFragment from '../../../../src/ol/webgl/Fragment'; const VERTEX_SHADER = ` @@ -20,11 +18,15 @@ const VERTEX_SHADER = ` const INVALID_VERTEX_SHADER = ` precision mediump float; - attribute float a_test; + uniform mat4 u_projectionMatrix; + uniform mat4 u_offsetScaleMatrix; + uniform mat4 u_offsetRotateMatrix; + + bla uniform float u_test; void main(void) { - gl_Position = vec4(1, 1, 1, 0); + gl_Position = vec4(u_test, a_test, 0.0, 1.0); }`; const FRAGMENT_SHADER = ` @@ -96,9 +98,7 @@ describe('ol.webgl.WebGLHelper', function() { u_test3: document.createElement('canvas') } }); - const vertexShader = new WebGLVertex(VERTEX_SHADER); - const fragmentShader = new WebGLFragment(FRAGMENT_SHADER); - h.useProgram(h.getProgram(fragmentShader, vertexShader)); + h.useProgram(h.getProgram(FRAGMENT_SHADER, VERTEX_SHADER)); h.prepareDraw({ pixelRatio: 2, size: [50, 80], @@ -133,9 +133,7 @@ describe('ol.webgl.WebGLHelper', function() { beforeEach(function() { h = new WebGLHelper(); - const vertexShader = new WebGLVertex(VERTEX_SHADER); - const fragmentShader = new WebGLFragment(FRAGMENT_SHADER); - p = h.getProgram(fragmentShader, vertexShader); + p = h.getProgram(FRAGMENT_SHADER, VERTEX_SHADER); h.useProgram(p); }); @@ -143,6 +141,10 @@ describe('ol.webgl.WebGLHelper', function() { expect(h.currentProgram_).to.eql(p); }); + it('has no shader compilation error', function() { + expect(h.shaderCompileErrors_).to.eql(null); + }); + it('can find the uniform location', function() { expect(h.getUniformLocation('u_test')).to.not.eql(null); }); @@ -162,9 +164,7 @@ describe('ol.webgl.WebGLHelper', function() { beforeEach(function() { h = new WebGLHelper(); - const vertexShader = new WebGLVertex(INVALID_VERTEX_SHADER); - const fragmentShader = new WebGLFragment(FRAGMENT_SHADER); - p = h.getProgram(fragmentShader, vertexShader); + p = h.getProgram(FRAGMENT_SHADER, INVALID_VERTEX_SHADER); h.useProgram(p); }); @@ -172,12 +172,12 @@ describe('ol.webgl.WebGLHelper', function() { expect(h.currentProgram_).to.eql(p); }); - it('cannot find the uniform location', function() { - expect(h.getUniformLocation('u_test')).to.eql(null); + it('has shader compilation errors', function() { + expect(h.shaderCompileErrors_).to.not.eql(null); }); - it('cannot find the attribute location', function() { - expect(h.getAttributeLocation('a_test')).to.eql(-1); + it('cannot find the uniform location', function() { + expect(h.getUniformLocation('u_test')).to.eql(null); }); });