Factor out ol.webgl.Context

This commit is contained in:
Tom Payne
2013-10-02 14:56:13 +02:00
parent fb2878c200
commit 10c41cd064
3 changed files with 267 additions and 166 deletions
+19 -162
View File
@@ -25,12 +25,11 @@ goog.require('ol.renderer.webgl.map.shader.Color');
goog.require('ol.renderer.webgl.map.shader.Default'); goog.require('ol.renderer.webgl.map.shader.Default');
goog.require('ol.source.State'); goog.require('ol.source.State');
goog.require('ol.structs.Buffer'); goog.require('ol.structs.Buffer');
goog.require('ol.structs.IntegerSet');
goog.require('ol.structs.LRUCache'); goog.require('ol.structs.LRUCache');
goog.require('ol.structs.PriorityQueue'); goog.require('ol.structs.PriorityQueue');
goog.require('ol.webgl'); goog.require('ol.webgl');
goog.require('ol.webgl.Context');
goog.require('ol.webgl.WebGLContextEventType'); 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; 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}} * @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_)); 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, goog.events.listen(this.canvas_, ol.webgl.WebGLContextEventType.LOST,
this.handleWebGLContextLost, false, this); this.handleWebGLContextLost, false, this);
goog.events.listen(this.canvas_, ol.webgl.WebGLContextEventType.RESTORED, goog.events.listen(this.canvas_, ol.webgl.WebGLContextEventType.RESTORED,
@@ -119,24 +116,6 @@ ol.renderer.webgl.Map = function(container, map) {
1, 1, 1, 1 1, 1, 1, 1
]); ]);
/**
* @private
* @type {Object.<number, ol.renderer.webgl.BufferCacheEntry>}
*/
this.bufferCache_ = {};
/**
* @private
* @type {Object.<number, WebGLShader>}
*/
this.shaderCache_ = {};
/**
* @private
* @type {Object.<string, WebGLProgram>}
*/
this.programCache_ = {};
/** /**
* @private * @private
* @type {ol.structs.LRUCache} * @type {ol.structs.LRUCache}
@@ -200,47 +179,6 @@ ol.renderer.webgl.Map = function(container, map) {
goog.inherits(ol.renderer.webgl.Map, ol.renderer.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 {ol.Tile} tile Tile.
* @param {number} magFilter Mag filter. * @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 * @inheritDoc
*/ */
ol.renderer.webgl.Map.prototype.disposeInternal = function() { ol.renderer.webgl.Map.prototype.disposeInternal = function() {
var gl = this.getGL(); var gl = this.getGL();
goog.object.forEach(this.bufferCache_, function(bufferCacheEntry) {
bufferCacheEntry.buf.removeDirtySet(bufferCacheEntry.dirtySet);
});
if (!gl.isContextLost()) { 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) { this.textureCache_.forEach(function(textureCacheEntry) {
if (!goog.isNull(textureCacheEntry)) { if (!goog.isNull(textureCacheEntry)) {
gl.deleteTexture(textureCacheEntry.texture); gl.deleteTexture(textureCacheEntry.texture);
} }
}); });
} }
goog.dispose(this.context_);
goog.base(this, 'disposeInternal'); 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. * @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. * @return {ol.structs.PriorityQueue} Tile texture queue.
*/ */
@@ -462,9 +321,6 @@ ol.renderer.webgl.Map.prototype.handleWebGLContextLost = function(event) {
event.preventDefault(); event.preventDefault();
this.colorLocations_ = null; this.colorLocations_ = null;
this.defaultLocations_ = null; this.defaultLocations_ = null;
this.bufferCache_ = {};
this.shaderCache_ = {};
this.programCache_ = {};
this.textureCache_.clear(); this.textureCache_.clear();
this.textureCacheFrameMarkerCount_ = 0; this.textureCacheFrameMarkerCount_ = 0;
goog.object.forEach(this.getLayerRenderers(), function(layerRenderer) { 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) { ol.renderer.webgl.Map.prototype.renderFrame = function(frameState) {
var context = this.getContext();
var gl = this.getGL(); var gl = this.getGL();
if (gl.isContextLost()) { if (gl.isContextLost()) {
@@ -566,7 +423,7 @@ ol.renderer.webgl.Map.prototype.renderFrame = function(frameState) {
gl.enable(goog.webgl.BLEND); gl.enable(goog.webgl.BLEND);
gl.viewport(0, 0, size[0], size[1]); 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 currentProgram = null;
var locations; var locations;
@@ -595,7 +452,7 @@ ol.renderer.webgl.Map.prototype.renderFrame = function(frameState) {
vertexShader = ol.renderer.webgl.map.shader.DefaultVertex.getInstance(); vertexShader = ol.renderer.webgl.map.shader.DefaultVertex.getInstance();
} }
var program = this.getProgram(fragmentShader, vertexShader); var program = context.getProgram(fragmentShader, vertexShader);
if (program != currentProgram) { if (program != currentProgram) {
gl.useProgram(program); gl.useProgram(program);
@@ -87,7 +87,8 @@ goog.inherits(ol.renderer.webgl.TileLayer, ol.renderer.webgl.Layer);
*/ */
ol.renderer.webgl.TileLayer.prototype.disposeInternal = function() { ol.renderer.webgl.TileLayer.prototype.disposeInternal = function() {
var mapRenderer = this.getWebGLMapRenderer(); var mapRenderer = this.getWebGLMapRenderer();
mapRenderer.deleteBuffer(this.arrayBuffer_); var context = mapRenderer.getContext();
context.deleteBuffer(this.arrayBuffer_);
goog.base(this, 'disposeInternal'); goog.base(this, 'disposeInternal');
}; };
@@ -117,6 +118,7 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame =
function(frameState, layerState) { function(frameState, layerState) {
var mapRenderer = this.getWebGLMapRenderer(); var mapRenderer = this.getWebGLMapRenderer();
var context = mapRenderer.getContext();
var gl = mapRenderer.getGL(); var gl = mapRenderer.getGL();
var view2DState = frameState.view2DState; var view2DState = frameState.view2DState;
@@ -172,15 +174,14 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame =
gl.clear(goog.webgl.COLOR_BUFFER_BIT); gl.clear(goog.webgl.COLOR_BUFFER_BIT);
gl.disable(goog.webgl.BLEND); gl.disable(goog.webgl.BLEND);
var program = mapRenderer.getProgram( var program = context.getProgram(this.fragmentShader_, this.vertexShader_);
this.fragmentShader_, this.vertexShader_);
gl.useProgram(program); gl.useProgram(program);
if (goog.isNull(this.locations_)) { if (goog.isNull(this.locations_)) {
this.locations_ = this.locations_ =
new ol.renderer.webgl.tilelayer.shader.Locations(gl, program); 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.enableVertexAttribArray(this.locations_.a_position);
gl.vertexAttribPointer( gl.vertexAttribPointer(
this.locations_.a_position, 2, goog.webgl.FLOAT, false, 16, 0); this.locations_.a_position, 2, goog.webgl.FLOAT, false, 16, 0);
+243
View File
@@ -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.<number, ol.webgl.BufferCacheEntry>}
*/
this.bufferCache_ = {};
/**
* @private
* @type {Object.<number, WebGLShader>}
*/
this.shaderCache_ = {};
/**
* @private
* @type {Object.<string, WebGLProgram>}
*/
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');