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); }); });