/*global define*/
define([
'../Core/defined',
'../Core/defineProperties',
'../Core/destroyObject',
'./ShaderProgram',
'./ShaderSource'
], function(
defined,
defineProperties,
destroyObject,
ShaderProgram,
ShaderSource) {
'use strict';
/**
* @private
*/
function ShaderCache(context) {
this._context = context;
this._shaders = {};
this._numberOfShaders = 0;
this._shadersToRelease = {};
}
defineProperties(ShaderCache.prototype, {
numberOfShaders : {
get : function() {
return this._numberOfShaders;
}
}
});
/**
* Returns a shader program from the cache, or creates and caches a new shader program,
* given the GLSL vertex and fragment shader source and attribute locations.
* <p>
* The difference between this and {@link ShaderCache#getShaderProgram}, is this is used to
* replace an existing reference to a shader program, which is passed as the first argument.
* </p>
*
* @param {Object} options Object with the following properties:
* @param {ShaderProgram} [options.shaderProgram] The shader program that is being reassigned.
* @param {String|ShaderSource} options.vertexShaderSource The GLSL source for the vertex shader.
* @param {String|ShaderSource} options.fragmentShaderSource The GLSL source for the fragment shader.
* @param {Object} options.attributeLocations Indices for the attribute inputs to the vertex shader.
* @returns {ShaderProgram} The cached or newly created shader program.
*
*
* @example
* this._shaderProgram = context.shaderCache.replaceShaderProgram({
* shaderProgram : this._shaderProgram,
* vertexShaderSource : vs,
* fragmentShaderSource : fs,
* attributeLocations : attributeLocations
* });
*
* @see ShaderCache#getShaderProgram
*/
ShaderCache.prototype.replaceShaderProgram = function(options) {
if (defined(options.shaderProgram)) {
options.shaderProgram.destroy();
}
return this.getShaderProgram(options);
};
/**
* Returns a shader program from the cache, or creates and caches a new shader program,
* given the GLSL vertex and fragment shader source and attribute locations.
*
* @param {Object} options Object with the following properties:
* @param {String|ShaderSource} options.vertexShaderSource The GLSL source for the vertex shader.
* @param {String|ShaderSource} options.fragmentShaderSource The GLSL source for the fragment shader.
* @param {Object} options.attributeLocations Indices for the attribute inputs to the vertex shader.
*
* @returns {ShaderProgram} The cached or newly created shader program.
*/
ShaderCache.prototype.getShaderProgram = function(options) {
// convert shaders which are provided as strings into ShaderSource objects
// because ShaderSource handles all the automatic including of built-in functions, etc.
var vertexShaderSource = options.vertexShaderSource;
var fragmentShaderSource = options.fragmentShaderSource;
var attributeLocations = options.attributeLocations;
if (typeof vertexShaderSource === 'string') {
vertexShaderSource = new ShaderSource({
sources : [vertexShaderSource]
});
}
if (typeof fragmentShaderSource === 'string') {
fragmentShaderSource = new ShaderSource({
sources : [fragmentShaderSource]
});
}
var vertexShaderText = vertexShaderSource.createCombinedVertexShader();
var fragmentShaderText = fragmentShaderSource.createCombinedFragmentShader();
var keyword = vertexShaderText + fragmentShaderText + JSON.stringify(attributeLocations);
var cachedShader;
if (this._shaders[keyword]) {
cachedShader = this._shaders[keyword];
// No longer want to release this if it was previously released.
delete this._shadersToRelease[keyword];
} else {
var context = this._context;
var shaderProgram = new ShaderProgram({
gl : context._gl,
logShaderCompilation : context.logShaderCompilation,
debugShaders : context.debugShaders,
vertexShaderSource : vertexShaderSource,
vertexShaderText : vertexShaderText,
fragmentShaderSource : fragmentShaderSource,
fragmentShaderText : fragmentShaderText,
attributeLocations : attributeLocations
});
cachedShader = {
cache : this,
shaderProgram : shaderProgram,
keyword : keyword,
count : 0
};
// A shader can't be in more than one cache.
shaderProgram._cachedShader = cachedShader;
this._shaders[keyword] = cachedShader;
++this._numberOfShaders;
}
++cachedShader.count;
return cachedShader.shaderProgram;
};
ShaderCache.prototype.destroyReleasedShaderPrograms = function() {
var shadersToRelease = this._shadersToRelease;
for ( var keyword in shadersToRelease) {
if (shadersToRelease.hasOwnProperty(keyword)) {
var cachedShader = shadersToRelease[keyword];
delete this._shaders[cachedShader.keyword];
cachedShader.shaderProgram.finalDestroy();
--this._numberOfShaders;
}
}
this._shadersToRelease = {};
};
ShaderCache.prototype.releaseShaderProgram = function(shaderProgram) {
if (defined(shaderProgram)) {
var cachedShader = shaderProgram._cachedShader;
if (cachedShader && (--cachedShader.count === 0)) {
this._shadersToRelease[cachedShader.keyword] = cachedShader;
}
}
};
ShaderCache.prototype.isDestroyed = function() {
return false;
};
ShaderCache.prototype.destroy = function() {
var shaders = this._shaders;
for ( var keyword in shaders) {
if (shaders.hasOwnProperty(keyword)) {
shaders[keyword].shaderProgram.finalDestroy();
}
}
return destroyObject(this);
};
return ShaderCache;
});