diff --git a/src/ol/renderer/webgl/webglmaprenderer.js b/src/ol/renderer/webgl/webglmaprenderer.js index eddfab1d73..c1c5561f07 100644 --- a/src/ol/renderer/webgl/webglmaprenderer.js +++ b/src/ol/renderer/webgl/webglmaprenderer.js @@ -25,12 +25,11 @@ goog.require('ol.renderer.webgl.map.shader.Color'); goog.require('ol.renderer.webgl.map.shader.Default'); goog.require('ol.source.State'); goog.require('ol.structs.Buffer'); -goog.require('ol.structs.IntegerSet'); goog.require('ol.structs.LRUCache'); goog.require('ol.structs.PriorityQueue'); goog.require('ol.webgl'); +goog.require('ol.webgl.Context'); goog.require('ol.webgl.WebGLContextEventType'); -goog.require('ol.webgl.shader'); /** @@ -39,14 +38,6 @@ goog.require('ol.webgl.shader'); ol.WEBGL_TEXTURE_CACHE_HIGH_WATER_MARK = 1024; -/** - * @typedef {{buf: ol.structs.Buffer, - * buffer: WebGLBuffer, - * dirtySet: ol.structs.IntegerSet}} - */ -ol.renderer.webgl.BufferCacheEntry; - - /** * @typedef {{magFilter: number, minFilter: number, texture: WebGLTexture}} */ @@ -91,6 +82,12 @@ ol.renderer.webgl.Map = function(container, map) { }); goog.asserts.assert(!goog.isNull(this.gl_)); + /** + * @private + * @type {ol.webgl.Context} + */ + this.context_ = new ol.webgl.Context(this.canvas_, this.gl_); + goog.events.listen(this.canvas_, ol.webgl.WebGLContextEventType.LOST, this.handleWebGLContextLost, false, this); goog.events.listen(this.canvas_, ol.webgl.WebGLContextEventType.RESTORED, @@ -119,24 +116,6 @@ ol.renderer.webgl.Map = function(container, map) { 1, 1, 1, 1 ]); - /** - * @private - * @type {Object.} - */ - this.bufferCache_ = {}; - - /** - * @private - * @type {Object.} - */ - this.shaderCache_ = {}; - - /** - * @private - * @type {Object.} - */ - this.programCache_ = {}; - /** * @private * @type {ol.structs.LRUCache} @@ -200,47 +179,6 @@ ol.renderer.webgl.Map = function(container, map) { goog.inherits(ol.renderer.webgl.Map, ol.renderer.Map); -/** - * @param {number} target Target. - * @param {ol.structs.Buffer} buf Buffer. - */ -ol.renderer.webgl.Map.prototype.bindBuffer = function(target, buf) { - var gl = this.getGL(); - var arr = buf.getArray(); - var bufferKey = goog.getUid(buf); - if (bufferKey in this.bufferCache_) { - var bufferCacheEntry = this.bufferCache_[bufferKey]; - gl.bindBuffer(target, bufferCacheEntry.buffer); - bufferCacheEntry.dirtySet.forEachRange(function(start, stop) { - // FIXME check if slice is really efficient here - var slice = arr.slice(start, stop); - gl.bufferSubData( - target, - start, - target == goog.webgl.ARRAY_BUFFER ? - new Float32Array(slice) : - new Uint16Array(slice)); - }); - bufferCacheEntry.dirtySet.clear(); - } else { - var buffer = gl.createBuffer(); - gl.bindBuffer(target, buffer); - gl.bufferData( - target, - target == goog.webgl.ARRAY_BUFFER ? - new Float32Array(arr) : new Uint16Array(arr), - buf.getUsage()); - var dirtySet = new ol.structs.IntegerSet(); - buf.addDirtySet(dirtySet); - this.bufferCache_[bufferKey] = { - buf: buf, - buffer: buffer, - dirtySet: dirtySet - }; - } -}; - - /** * @param {ol.Tile} tile Tile. * @param {number} magFilter Mag filter. @@ -300,46 +238,19 @@ ol.renderer.webgl.Map.prototype.createLayerRenderer = function(layer) { }; -/** - * @param {ol.structs.Buffer} buf Buffer. - */ -ol.renderer.webgl.Map.prototype.deleteBuffer = function(buf) { - var gl = this.getGL(); - var bufferKey = goog.getUid(buf); - goog.asserts.assert(bufferKey in this.bufferCache_); - var bufferCacheEntry = this.bufferCache_[bufferKey]; - bufferCacheEntry.buf.removeDirtySet(bufferCacheEntry.dirtySet); - if (!gl.isContextLost()) { - gl.deleteBuffer(bufferCacheEntry.buffer); - } - delete this.bufferCache_[bufferKey]; -}; - - /** * @inheritDoc */ ol.renderer.webgl.Map.prototype.disposeInternal = function() { var gl = this.getGL(); - goog.object.forEach(this.bufferCache_, function(bufferCacheEntry) { - bufferCacheEntry.buf.removeDirtySet(bufferCacheEntry.dirtySet); - }); if (!gl.isContextLost()) { - goog.object.forEach(this.bufferCache_, function(bufferCacheEntry) { - gl.deleteBuffer(bufferCacheEntry.buffer); - }); - goog.object.forEach(this.programCache_, function(program) { - gl.deleteProgram(program); - }); - goog.object.forEach(this.shaderCache_, function(shader) { - gl.deleteShader(shader); - }); this.textureCache_.forEach(function(textureCacheEntry) { if (!goog.isNull(textureCacheEntry)) { gl.deleteTexture(textureCacheEntry.texture); } }); } + goog.dispose(this.context_); goog.base(this, 'disposeInternal'); }; @@ -378,6 +289,14 @@ ol.renderer.webgl.Map.prototype.getCanvas = function() { }; +/** + * @return {ol.webgl.Context} + */ +ol.renderer.webgl.Map.prototype.getContext = function() { + return this.context_; +}; + + /** * @return {WebGLRenderingContext} GL. */ @@ -386,66 +305,6 @@ ol.renderer.webgl.Map.prototype.getGL = function() { }; -/** - * @param {ol.webgl.shader.Fragment} fragmentShaderObject Fragment shader. - * @param {ol.webgl.shader.Vertex} vertexShaderObject Vertex shader. - * @return {WebGLProgram} Program. - */ -ol.renderer.webgl.Map.prototype.getProgram = function( - fragmentShaderObject, vertexShaderObject) { - var programKey = - goog.getUid(fragmentShaderObject) + '/' + goog.getUid(vertexShaderObject); - if (programKey in this.programCache_) { - return this.programCache_[programKey]; - } else { - var gl = this.getGL(); - var program = gl.createProgram(); - gl.attachShader(program, this.getShader(fragmentShaderObject)); - gl.attachShader(program, this.getShader(vertexShaderObject)); - gl.linkProgram(program); - if (goog.DEBUG) { - if (!gl.getProgramParameter(program, goog.webgl.LINK_STATUS) && - !gl.isContextLost()) { - goog.log.error(this.logger_, gl.getProgramInfoLog(program)); - } - } - goog.asserts.assert( - gl.getProgramParameter(program, goog.webgl.LINK_STATUS) || - gl.isContextLost()); - this.programCache_[programKey] = program; - return program; - } -}; - - -/** - * @param {ol.webgl.Shader} shaderObject Shader object. - * @return {WebGLShader} Shader. - */ -ol.renderer.webgl.Map.prototype.getShader = function(shaderObject) { - var shaderKey = goog.getUid(shaderObject); - if (shaderKey in this.shaderCache_) { - return this.shaderCache_[shaderKey]; - } else { - var gl = this.getGL(); - var shader = gl.createShader(shaderObject.getType()); - gl.shaderSource(shader, shaderObject.getSource()); - gl.compileShader(shader); - if (goog.DEBUG) { - if (!gl.getShaderParameter(shader, goog.webgl.COMPILE_STATUS) && - !gl.isContextLost()) { - goog.log.error(this.logger_, gl.getShaderInfoLog(shader)); - } - } - goog.asserts.assert( - gl.getShaderParameter(shader, goog.webgl.COMPILE_STATUS) || - gl.isContextLost()); - this.shaderCache_[shaderKey] = shader; - return shader; - } -}; - - /** * @return {ol.structs.PriorityQueue} Tile texture queue. */ @@ -462,9 +321,6 @@ ol.renderer.webgl.Map.prototype.handleWebGLContextLost = function(event) { event.preventDefault(); this.colorLocations_ = null; this.defaultLocations_ = null; - this.bufferCache_ = {}; - this.shaderCache_ = {}; - this.programCache_ = {}; this.textureCache_.clear(); this.textureCacheFrameMarkerCount_ = 0; goog.object.forEach(this.getLayerRenderers(), function(layerRenderer) { @@ -519,6 +375,7 @@ ol.renderer.webgl.Map.prototype.logger_ = */ ol.renderer.webgl.Map.prototype.renderFrame = function(frameState) { + var context = this.getContext(); var gl = this.getGL(); if (gl.isContextLost()) { @@ -566,7 +423,7 @@ ol.renderer.webgl.Map.prototype.renderFrame = function(frameState) { gl.enable(goog.webgl.BLEND); gl.viewport(0, 0, size[0], size[1]); - this.bindBuffer(goog.webgl.ARRAY_BUFFER, this.arrayBuffer_); + context.bindBuffer(goog.webgl.ARRAY_BUFFER, this.arrayBuffer_); var currentProgram = null; var locations; @@ -595,7 +452,7 @@ ol.renderer.webgl.Map.prototype.renderFrame = function(frameState) { vertexShader = ol.renderer.webgl.map.shader.DefaultVertex.getInstance(); } - var program = this.getProgram(fragmentShader, vertexShader); + var program = context.getProgram(fragmentShader, vertexShader); if (program != currentProgram) { gl.useProgram(program); diff --git a/src/ol/renderer/webgl/webgltilelayerrenderer.js b/src/ol/renderer/webgl/webgltilelayerrenderer.js index 63e336e0d5..00e40b9bc7 100644 --- a/src/ol/renderer/webgl/webgltilelayerrenderer.js +++ b/src/ol/renderer/webgl/webgltilelayerrenderer.js @@ -87,7 +87,8 @@ goog.inherits(ol.renderer.webgl.TileLayer, ol.renderer.webgl.Layer); */ ol.renderer.webgl.TileLayer.prototype.disposeInternal = function() { var mapRenderer = this.getWebGLMapRenderer(); - mapRenderer.deleteBuffer(this.arrayBuffer_); + var context = mapRenderer.getContext(); + context.deleteBuffer(this.arrayBuffer_); goog.base(this, 'disposeInternal'); }; @@ -117,6 +118,7 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame = function(frameState, layerState) { var mapRenderer = this.getWebGLMapRenderer(); + var context = mapRenderer.getContext(); var gl = mapRenderer.getGL(); var view2DState = frameState.view2DState; @@ -172,15 +174,14 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame = gl.clear(goog.webgl.COLOR_BUFFER_BIT); gl.disable(goog.webgl.BLEND); - var program = mapRenderer.getProgram( - this.fragmentShader_, this.vertexShader_); + var program = context.getProgram(this.fragmentShader_, this.vertexShader_); gl.useProgram(program); if (goog.isNull(this.locations_)) { this.locations_ = new ol.renderer.webgl.tilelayer.shader.Locations(gl, program); } - mapRenderer.bindBuffer(goog.webgl.ARRAY_BUFFER, this.arrayBuffer_); + context.bindBuffer(goog.webgl.ARRAY_BUFFER, this.arrayBuffer_); gl.enableVertexAttribArray(this.locations_.a_position); gl.vertexAttribPointer( this.locations_.a_position, 2, goog.webgl.FLOAT, false, 16, 0); diff --git a/src/ol/webgl/context.js b/src/ol/webgl/context.js new file mode 100644 index 0000000000..404fe4361e --- /dev/null +++ b/src/ol/webgl/context.js @@ -0,0 +1,243 @@ +goog.provide('ol.webgl.Context'); + +goog.require('goog.asserts'); +goog.require('goog.events'); +goog.require('goog.log'); +goog.require('goog.object'); +goog.require('ol.structs.Buffer'); +goog.require('ol.structs.IntegerSet'); +goog.require('ol.webgl.WebGLContextEventType'); + + +/** + * @typedef {{buf: ol.structs.Buffer, + * buffer: WebGLBuffer, + * dirtySet: ol.structs.IntegerSet}} + */ +ol.webgl.BufferCacheEntry; + + + +/** + * @constructor + * @extends {goog.events.EventTarget} + * @param {HTMLCanvasElement} canvas Canvas. + * @param {WebGLRenderingContext} gl GL. + */ +ol.webgl.Context = function(canvas, gl) { + + /** + * @private + * @type {HTMLCanvasElement} + */ + this.canvas_ = canvas; + + /** + * @private + * @type {WebGLRenderingContext} + */ + this.gl_ = gl; + + /** + * @private + * @type {Object.} + */ + this.bufferCache_ = {}; + + /** + * @private + * @type {Object.} + */ + this.shaderCache_ = {}; + + /** + * @private + * @type {Object.} + */ + this.programCache_ = {}; + + goog.events.listen(this.canvas_, ol.webgl.WebGLContextEventType.LOST, + this.handleWebGLContextLost, false, this); + goog.events.listen(this.canvas_, ol.webgl.WebGLContextEventType.RESTORED, + this.handleWebGLContextRestored, false, this); + +}; + + +/** + * @param {number} target Target. + * @param {ol.structs.Buffer} buf Buffer. + */ +ol.webgl.Context.prototype.bindBuffer = function(target, buf) { + var gl = this.getGL(); + var arr = buf.getArray(); + var bufferKey = goog.getUid(buf); + if (bufferKey in this.bufferCache_) { + var bufferCacheEntry = this.bufferCache_[bufferKey]; + gl.bindBuffer(target, bufferCacheEntry.buffer); + bufferCacheEntry.dirtySet.forEachRange(function(start, stop) { + // FIXME check if slice is really efficient here + var slice = arr.slice(start, stop); + gl.bufferSubData( + target, + start, + target == goog.webgl.ARRAY_BUFFER ? + new Float32Array(slice) : + new Uint16Array(slice)); + }); + bufferCacheEntry.dirtySet.clear(); + } else { + var buffer = gl.createBuffer(); + gl.bindBuffer(target, buffer); + gl.bufferData( + target, + target == goog.webgl.ARRAY_BUFFER ? + new Float32Array(arr) : new Uint16Array(arr), + buf.getUsage()); + var dirtySet = new ol.structs.IntegerSet(); + buf.addDirtySet(dirtySet); + this.bufferCache_[bufferKey] = { + buf: buf, + buffer: buffer, + dirtySet: dirtySet + }; + } +}; + + +/** + * @param {ol.structs.Buffer} buf Buffer. + */ +ol.webgl.Context.prototype.deleteBuffer = function(buf) { + var gl = this.getGL(); + var bufferKey = goog.getUid(buf); + goog.asserts.assert(bufferKey in this.bufferCache_); + var bufferCacheEntry = this.bufferCache_[bufferKey]; + bufferCacheEntry.buf.removeDirtySet(bufferCacheEntry.dirtySet); + if (!gl.isContextLost()) { + gl.deleteBuffer(bufferCacheEntry.buffer); + } + delete this.bufferCache_[bufferKey]; +}; + + +/** + * @inheritDoc + */ +ol.webgl.Context.prototype.disposeInternal = function() { + goog.object.forEach(this.bufferCache_, function(bufferCacheEntry) { + bufferCacheEntry.buf.removeDirtySet(bufferCacheEntry.dirtySet); + }); + var gl = this.getGL(); + if (!gl.isContextLost()) { + goog.object.forEach(this.bufferCache_, function(bufferCacheEntry) { + gl.deleteBuffer(bufferCacheEntry.buffer); + }); + goog.object.forEach(this.programCache_, function(program) { + gl.deleteProgram(program); + }); + goog.object.forEach(this.shaderCache_, function(shader) { + gl.deleteShader(shader); + }); + } +}; + + +/** + * @return {HTMLCanvasElement} Canvas. + */ +ol.webgl.Context.prototype.getCanvas = function() { + return this.canvas_; +}; + + +/** + * @return {WebGLRenderingContext} GL. + */ +ol.webgl.Context.prototype.getGL = function() { + return this.gl_; +}; + + +/** + * @param {ol.webgl.Shader} shaderObject Shader object. + * @return {WebGLShader} Shader. + */ +ol.webgl.Context.prototype.getShader = function(shaderObject) { + var shaderKey = goog.getUid(shaderObject); + if (shaderKey in this.shaderCache_) { + return this.shaderCache_[shaderKey]; + } else { + var gl = this.getGL(); + var shader = gl.createShader(shaderObject.getType()); + gl.shaderSource(shader, shaderObject.getSource()); + gl.compileShader(shader); + if (goog.DEBUG) { + if (!gl.getShaderParameter(shader, goog.webgl.COMPILE_STATUS) && + !gl.isContextLost()) { + goog.log.error(this.logger_, gl.getShaderInfoLog(shader)); + } + } + goog.asserts.assert( + gl.getShaderParameter(shader, goog.webgl.COMPILE_STATUS) || + gl.isContextLost()); + this.shaderCache_[shaderKey] = shader; + return shader; + } +}; + + +/** + * @param {ol.webgl.shader.Fragment} fragmentShaderObject Fragment shader. + * @param {ol.webgl.shader.Vertex} vertexShaderObject Vertex shader. + * @return {WebGLProgram} Program. + */ +ol.webgl.Context.prototype.getProgram = function( + fragmentShaderObject, vertexShaderObject) { + var programKey = + goog.getUid(fragmentShaderObject) + '/' + goog.getUid(vertexShaderObject); + if (programKey in this.programCache_) { + return this.programCache_[programKey]; + } else { + var gl = this.getGL(); + var program = gl.createProgram(); + gl.attachShader(program, this.getShader(fragmentShaderObject)); + gl.attachShader(program, this.getShader(vertexShaderObject)); + gl.linkProgram(program); + if (goog.DEBUG) { + if (!gl.getProgramParameter(program, goog.webgl.LINK_STATUS) && + !gl.isContextLost()) { + goog.log.error(this.logger_, gl.getProgramInfoLog(program)); + } + } + goog.asserts.assert( + gl.getProgramParameter(program, goog.webgl.LINK_STATUS) || + gl.isContextLost()); + this.programCache_[programKey] = program; + return program; + } +}; + + +/** + * FIXME empy description for jsdoc + */ +ol.webgl.Context.prototype.handleWebGLContextLost = function() { + goog.object.clear(this.bufferCache_); + goog.object.clear(this.shaderCache_); + goog.object.clear(this.programCache_); +}; + + +/** + * FIXME empy description for jsdoc + */ +ol.webgl.Context.prototype.handleWebGLContextRestored = function() { +}; + + +/** + * @private + * @type {goog.log.Logger} + */ +ol.webgl.Context.prototype.logger_ = goog.log.getLogger('ol.webgl.Context');