TextureManager.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. import defined from "../../Core/defined.js";
  2. import destroyObject from "../../Core/destroyObject.js";
  3. import Texture from "../../Renderer/Texture.js";
  4. /**
  5. * An object to manage loading textures
  6. *
  7. * @alias TextureManager
  8. * @constructor
  9. *
  10. * @private
  11. * @experimental This feature is using part of the 3D Tiles spec that is not final and is subject to change without Cesium's standard deprecation policy.
  12. */
  13. export default function TextureManager() {
  14. this._defaultTexture = undefined;
  15. this._textures = {};
  16. this._loadedImages = [];
  17. // Keep track of the last time update() was called to avoid
  18. // calling update() twice.
  19. this._lastUpdatedFrame = -1;
  20. }
  21. /**
  22. * Get one of the loaded textures
  23. * @param {String} textureId The unique ID of the texture loaded by {@link TextureManager#loadTexture2D}
  24. * @return {Texture} The texture or <code>undefined</code> if no texture exists
  25. */
  26. TextureManager.prototype.getTexture = function (textureId) {
  27. return this._textures[textureId];
  28. };
  29. function fetchTexture2D(textureManager, textureId, textureUniform) {
  30. textureUniform.resource
  31. .fetchImage()
  32. .then(function (image) {
  33. textureManager._loadedImages.push({
  34. id: textureId,
  35. image: image,
  36. textureUniform: textureUniform,
  37. });
  38. })
  39. .catch(function () {
  40. const texture = textureManager._textures[textureId];
  41. if (defined(texture) && texture !== textureManager._defaultTexture) {
  42. texture.destroy();
  43. }
  44. textureManager._textures[textureId] = textureManager._defaultTexture;
  45. });
  46. }
  47. /**
  48. * Load a texture 2D asynchronously. Note that {@link TextureManager#update}
  49. * must be called in the render loop to finish processing the textures.
  50. *
  51. * @param {String} textureId A unique ID to identify this texture.
  52. * @param {TextureUniform} textureUniform A description of the texture
  53. *
  54. * @private
  55. */
  56. TextureManager.prototype.loadTexture2D = function (textureId, textureUniform) {
  57. if (defined(textureUniform.typedArray)) {
  58. this._loadedImages.push({
  59. id: textureId,
  60. textureUniform: textureUniform,
  61. });
  62. } else {
  63. fetchTexture2D(this, textureId, textureUniform);
  64. }
  65. };
  66. function createTexture(textureManager, loadedImage, context) {
  67. const id = loadedImage.id;
  68. const textureUniform = loadedImage.textureUniform;
  69. const typedArray = textureUniform.typedArray;
  70. const sampler = textureUniform.sampler;
  71. let texture;
  72. if (defined(typedArray)) {
  73. texture = new Texture({
  74. context: context,
  75. pixelFormat: textureUniform.pixelFormat,
  76. pixelDatatype: textureUniform.pixelDatatype,
  77. source: {
  78. arrayBufferView: typedArray,
  79. width: textureUniform.width,
  80. height: textureUniform.height,
  81. },
  82. sampler: sampler,
  83. flipY: false,
  84. });
  85. } else {
  86. texture = new Texture({
  87. context: context,
  88. source: loadedImage.image,
  89. sampler: sampler,
  90. });
  91. }
  92. // Destroy the old texture once the new one is loaded for more seamless
  93. // transitions between values
  94. const oldTexture = textureManager._textures[id];
  95. if (defined(oldTexture) && oldTexture !== context.defaultTexture) {
  96. oldTexture.destroy();
  97. }
  98. textureManager._textures[id] = texture;
  99. }
  100. TextureManager.prototype.update = function (frameState) {
  101. // update only needs to be called once a frame.
  102. if (frameState.frameNumber === this._lastUpdatedFrame) {
  103. return;
  104. }
  105. this._lastUpdatedFrame = frameState.frameNumber;
  106. const context = frameState.context;
  107. this._defaultTexture = context.defaultTexture;
  108. // If any images were loaded since the last frame, create Textures
  109. // for them and store in the uniform dictionary
  110. const loadedImages = this._loadedImages;
  111. for (let i = 0; i < loadedImages.length; i++) {
  112. const loadedImage = loadedImages[i];
  113. createTexture(this, loadedImage, context);
  114. }
  115. loadedImages.length = 0;
  116. };
  117. /**
  118. * Returns true if this object was destroyed; otherwise, false.
  119. * <br /><br />
  120. * If this object was destroyed, it should not be used; calling any function other than
  121. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
  122. *
  123. * @returns {Boolean} True if this object was destroyed; otherwise, false.
  124. *
  125. * @see TextureManager#destroy
  126. * @private
  127. */
  128. TextureManager.prototype.isDestroyed = function () {
  129. return false;
  130. };
  131. /**
  132. * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
  133. * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
  134. * <br /><br />
  135. * Once an object is destroyed, it should not be used; calling any function other than
  136. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
  137. * assign the return value (<code>undefined</code>) to the object as done in the example.
  138. *
  139. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  140. *
  141. * @example
  142. * textureManager = textureManager && textureManager.destroy();
  143. *
  144. * @see TextureManager#isDestroyed
  145. * @private
  146. */
  147. TextureManager.prototype.destroy = function () {
  148. const textures = this._textures;
  149. for (const texture in textures) {
  150. if (textures.hasOwnProperty(texture)) {
  151. const instance = textures[texture];
  152. if (instance !== this._defaultTexture) {
  153. instance.destroy();
  154. }
  155. }
  156. }
  157. return destroyObject(this);
  158. };