ShaderCache.js 8.6 KB

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