ShaderCache.js 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. import defined from '../Core/defined.js';
  2. import defineProperties from '../Core/defineProperties.js';
  3. import destroyObject from '../Core/destroyObject.js';
  4. import ShaderProgram from './ShaderProgram.js';
  5. import ShaderSource from './ShaderSource.js';
  6. /**
  7. * @private
  8. */
  9. function ShaderCache(context) {
  10. this._context = context;
  11. this._shaders = {};
  12. this._numberOfShaders = 0;
  13. this._shadersToRelease = {};
  14. }
  15. defineProperties(ShaderCache.prototype, {
  16. numberOfShaders : {
  17. get : function() {
  18. return this._numberOfShaders;
  19. }
  20. }
  21. });
  22. /**
  23. * Returns a shader program from the cache, or creates and caches a new shader program,
  24. * given the GLSL vertex and fragment shader source and attribute locations.
  25. * <p>
  26. * The difference between this and {@link ShaderCache#getShaderProgram}, is this is used to
  27. * replace an existing reference to a shader program, which is passed as the first argument.
  28. * </p>
  29. *
  30. * @param {Object} options Object with the following properties:
  31. * @param {ShaderProgram} [options.shaderProgram] The shader program that is being reassigned.
  32. * @param {String|ShaderSource} options.vertexShaderSource The GLSL source for the vertex shader.
  33. * @param {String|ShaderSource} options.fragmentShaderSource The GLSL source for the fragment shader.
  34. * @param {Object} options.attributeLocations Indices for the attribute inputs to the vertex shader.
  35. * @returns {ShaderProgram} The cached or newly created shader program.
  36. *
  37. *
  38. * @example
  39. * this._shaderProgram = context.shaderCache.replaceShaderProgram({
  40. * shaderProgram : this._shaderProgram,
  41. * vertexShaderSource : vs,
  42. * fragmentShaderSource : fs,
  43. * attributeLocations : attributeLocations
  44. * });
  45. *
  46. * @see ShaderCache#getShaderProgram
  47. */
  48. ShaderCache.prototype.replaceShaderProgram = function(options) {
  49. if (defined(options.shaderProgram)) {
  50. options.shaderProgram.destroy();
  51. }
  52. return this.getShaderProgram(options);
  53. };
  54. /**
  55. * Returns a shader program from the cache, or creates and caches a new shader program,
  56. * given the GLSL vertex and fragment shader source and attribute locations.
  57. *
  58. * @param {Object} options Object with the following properties:
  59. * @param {String|ShaderSource} options.vertexShaderSource The GLSL source for the vertex shader.
  60. * @param {String|ShaderSource} options.fragmentShaderSource The GLSL source for the fragment shader.
  61. * @param {Object} options.attributeLocations Indices for the attribute inputs to the vertex shader.
  62. *
  63. * @returns {ShaderProgram} The cached or newly created shader program.
  64. */
  65. ShaderCache.prototype.getShaderProgram = function(options) {
  66. // convert shaders which are provided as strings into ShaderSource objects
  67. // because ShaderSource handles all the automatic including of built-in functions, etc.
  68. var vertexShaderSource = options.vertexShaderSource;
  69. var fragmentShaderSource = options.fragmentShaderSource;
  70. var attributeLocations = options.attributeLocations;
  71. if (typeof vertexShaderSource === 'string') {
  72. vertexShaderSource = new ShaderSource({
  73. sources : [vertexShaderSource]
  74. });
  75. }
  76. if (typeof fragmentShaderSource === 'string') {
  77. fragmentShaderSource = new ShaderSource({
  78. sources : [fragmentShaderSource]
  79. });
  80. }
  81. var vertexShaderText = vertexShaderSource.createCombinedVertexShader(this._context);
  82. var fragmentShaderText = fragmentShaderSource.createCombinedFragmentShader(this._context);
  83. var keyword = vertexShaderText + fragmentShaderText + JSON.stringify(attributeLocations);
  84. var cachedShader;
  85. if (defined(this._shaders[keyword])) {
  86. cachedShader = this._shaders[keyword];
  87. // No longer want to release this if it was previously released.
  88. delete this._shadersToRelease[keyword];
  89. } else {
  90. var context = this._context;
  91. var shaderProgram = new ShaderProgram({
  92. gl : context._gl,
  93. logShaderCompilation : context.logShaderCompilation,
  94. debugShaders : context.debugShaders,
  95. vertexShaderSource : vertexShaderSource,
  96. vertexShaderText : vertexShaderText,
  97. fragmentShaderSource : fragmentShaderSource,
  98. fragmentShaderText : fragmentShaderText,
  99. attributeLocations : attributeLocations
  100. });
  101. cachedShader = {
  102. cache : this,
  103. shaderProgram : shaderProgram,
  104. keyword : keyword,
  105. derivedKeywords : [],
  106. count : 0
  107. };
  108. // A shader can't be in more than one cache.
  109. shaderProgram._cachedShader = cachedShader;
  110. this._shaders[keyword] = cachedShader;
  111. ++this._numberOfShaders;
  112. }
  113. ++cachedShader.count;
  114. return cachedShader.shaderProgram;
  115. };
  116. ShaderCache.prototype.replaceDerivedShaderProgram = function(shaderProgram, keyword, options) {
  117. var cachedShader = shaderProgram._cachedShader;
  118. var derivedKeyword = keyword + cachedShader.keyword;
  119. var cachedDerivedShader = this._shaders[derivedKeyword];
  120. if (defined(cachedDerivedShader)) {
  121. destroyShader(this, cachedDerivedShader);
  122. var index = cachedShader.derivedKeywords.indexOf(keyword);
  123. if (index > -1) {
  124. cachedShader.derivedKeywords.splice(index, 1);
  125. }
  126. }
  127. return this.createDerivedShaderProgram(shaderProgram, keyword, options);
  128. };
  129. ShaderCache.prototype.getDerivedShaderProgram = function(shaderProgram, keyword) {
  130. var cachedShader = shaderProgram._cachedShader;
  131. var derivedKeyword = keyword + cachedShader.keyword;
  132. var cachedDerivedShader = this._shaders[derivedKeyword];
  133. if (!defined(cachedDerivedShader)) {
  134. return undefined;
  135. }
  136. return cachedDerivedShader.shaderProgram;
  137. };
  138. ShaderCache.prototype.createDerivedShaderProgram = function(shaderProgram, keyword, options) {
  139. var cachedShader = shaderProgram._cachedShader;
  140. var derivedKeyword = keyword + cachedShader.keyword;
  141. var vertexShaderSource = options.vertexShaderSource;
  142. var fragmentShaderSource = options.fragmentShaderSource;
  143. var attributeLocations = options.attributeLocations;
  144. if (typeof vertexShaderSource === 'string') {
  145. vertexShaderSource = new ShaderSource({
  146. sources : [vertexShaderSource]
  147. });
  148. }
  149. if (typeof fragmentShaderSource === 'string') {
  150. fragmentShaderSource = new ShaderSource({
  151. sources : [fragmentShaderSource]
  152. });
  153. }
  154. var context = this._context;
  155. var vertexShaderText = vertexShaderSource.createCombinedVertexShader(context);
  156. var fragmentShaderText = fragmentShaderSource.createCombinedFragmentShader(context);
  157. var derivedShaderProgram = new ShaderProgram({
  158. gl : context._gl,
  159. logShaderCompilation : context.logShaderCompilation,
  160. debugShaders : context.debugShaders,
  161. vertexShaderSource : vertexShaderSource,
  162. vertexShaderText : vertexShaderText,
  163. fragmentShaderSource : fragmentShaderSource,
  164. fragmentShaderText : fragmentShaderText,
  165. attributeLocations : attributeLocations
  166. });
  167. var derivedCachedShader = {
  168. cache : this,
  169. shaderProgram : derivedShaderProgram,
  170. keyword : derivedKeyword,
  171. derivedKeywords : [],
  172. count : 0
  173. };
  174. cachedShader.derivedKeywords.push(keyword);
  175. derivedShaderProgram._cachedShader = derivedCachedShader;
  176. this._shaders[derivedKeyword] = derivedCachedShader;
  177. return derivedShaderProgram;
  178. };
  179. function destroyShader(cache, cachedShader) {
  180. var derivedKeywords = cachedShader.derivedKeywords;
  181. var length = derivedKeywords.length;
  182. for (var i = 0; i < length; ++i) {
  183. var keyword = derivedKeywords[i] + cachedShader.keyword;
  184. var derivedCachedShader = cache._shaders[keyword];
  185. destroyShader(cache, derivedCachedShader);
  186. }
  187. delete cache._shaders[cachedShader.keyword];
  188. cachedShader.shaderProgram.finalDestroy();
  189. }
  190. ShaderCache.prototype.destroyReleasedShaderPrograms = function() {
  191. var shadersToRelease = this._shadersToRelease;
  192. for ( var keyword in shadersToRelease) {
  193. if (shadersToRelease.hasOwnProperty(keyword)) {
  194. var cachedShader = shadersToRelease[keyword];
  195. destroyShader(this, cachedShader);
  196. --this._numberOfShaders;
  197. }
  198. }
  199. this._shadersToRelease = {};
  200. };
  201. ShaderCache.prototype.releaseShaderProgram = function(shaderProgram) {
  202. if (defined(shaderProgram)) {
  203. var cachedShader = shaderProgram._cachedShader;
  204. if (cachedShader && (--cachedShader.count === 0)) {
  205. this._shadersToRelease[cachedShader.keyword] = cachedShader;
  206. }
  207. }
  208. };
  209. ShaderCache.prototype.isDestroyed = function() {
  210. return false;
  211. };
  212. ShaderCache.prototype.destroy = function() {
  213. var shaders = this._shaders;
  214. for (var keyword in shaders) {
  215. if (shaders.hasOwnProperty(keyword)) {
  216. shaders[keyword].shaderProgram.finalDestroy();
  217. }
  218. }
  219. return destroyObject(this);
  220. };
  221. export default ShaderCache;