PostProcessStageTextureCache.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. import Color from '../Core/Color.js';
  2. import defined from '../Core/defined.js';
  3. import destroyObject from '../Core/destroyObject.js';
  4. import CesiumMath from '../Core/Math.js';
  5. import ClearCommand from '../Renderer/ClearCommand.js';
  6. import Framebuffer from '../Renderer/Framebuffer.js';
  7. import Texture from '../Renderer/Texture.js';
  8. /**
  9. * Creates a minimal amount of textures and framebuffers.
  10. *
  11. * @alias PostProcessStageTextureCache
  12. * @constructor
  13. *
  14. * @param {PostProcessStageCollection} postProcessStageCollection The post process collection.
  15. *
  16. * @private
  17. */
  18. function PostProcessStageTextureCache(postProcessStageCollection) {
  19. this._collection = postProcessStageCollection;
  20. this._framebuffers = [];
  21. this._stageNameToFramebuffer = {};
  22. this._width = undefined;
  23. this._height = undefined;
  24. this._updateDependencies = false;
  25. }
  26. function getLastStageName(stage) {
  27. while (defined(stage.length)) {
  28. stage = stage.get(stage.length - 1);
  29. }
  30. return stage.name;
  31. }
  32. function getStageDependencies(collection, context, dependencies, stage, previousName) {
  33. if (!stage.enabled || !stage._isSupported(context)) {
  34. return previousName;
  35. }
  36. var stageDependencies = dependencies[stage.name] = {};
  37. if (defined(previousName)) {
  38. var previous = collection.getStageByName(previousName);
  39. stageDependencies[getLastStageName(previous)] = true;
  40. }
  41. var uniforms = stage.uniforms;
  42. if (defined(uniforms)) {
  43. var uniformNames = Object.getOwnPropertyNames(uniforms);
  44. var uniformNamesLength = uniformNames.length;
  45. for (var i = 0; i < uniformNamesLength; ++i) {
  46. var value = uniforms[uniformNames[i]];
  47. if (typeof value === 'string') {
  48. var dependent = collection.getStageByName(value);
  49. if (defined(dependent)) {
  50. stageDependencies[getLastStageName(dependent)] = true;
  51. }
  52. }
  53. }
  54. }
  55. return stage.name;
  56. }
  57. function getCompositeDependencies(collection, context, dependencies, composite, previousName) {
  58. if ((defined(composite.enabled) && !composite.enabled) || (defined(composite._isSupported) && !composite._isSupported(context))) {
  59. return previousName;
  60. }
  61. var originalDependency = previousName;
  62. var inSeries = !defined(composite.inputPreviousStageTexture) || composite.inputPreviousStageTexture;
  63. var currentName = previousName;
  64. var length = composite.length;
  65. for (var i = 0; i < length; ++i) {
  66. var stage = composite.get(i);
  67. if (defined(stage.length)) {
  68. currentName = getCompositeDependencies(collection, context, dependencies, stage, previousName);
  69. } else {
  70. currentName = getStageDependencies(collection, context, dependencies, stage, previousName);
  71. }
  72. // Stages in a series only depend on the previous stage
  73. if (inSeries) {
  74. previousName = currentName;
  75. }
  76. }
  77. // Stages not in a series depend on every stage executed before it since it could reference it as a uniform.
  78. // This prevents looking at the dependencies of each stage in the composite, but might create more framebuffers than necessary.
  79. // In practice, there are only 2-3 stages in these composites.
  80. var j;
  81. var name;
  82. if (!inSeries) {
  83. for (j = 1; j < length; ++j) {
  84. name = getLastStageName(composite.get(j));
  85. var currentDependencies = dependencies[name];
  86. for (var k = 0; k < j; ++k) {
  87. currentDependencies[getLastStageName(composite.get(k))] = true;
  88. }
  89. }
  90. } else {
  91. for (j = 1; j < length; ++j) {
  92. name = getLastStageName(composite.get(j));
  93. if (!defined(dependencies[name])) {
  94. dependencies[name] = {};
  95. }
  96. dependencies[name][originalDependency] = true;
  97. }
  98. }
  99. return currentName;
  100. }
  101. function getDependencies(collection, context) {
  102. var dependencies = {};
  103. if (defined(collection.ambientOcclusion)) {
  104. var ao = collection.ambientOcclusion;
  105. var bloom = collection.bloom;
  106. var tonemapping = collection._tonemapping;
  107. var fxaa = collection.fxaa;
  108. var previousName = getCompositeDependencies(collection, context, dependencies, ao, undefined);
  109. previousName = getCompositeDependencies(collection, context, dependencies, bloom, previousName);
  110. previousName = getStageDependencies(collection, context, dependencies, tonemapping, previousName);
  111. previousName = getCompositeDependencies(collection, context, dependencies, collection, previousName);
  112. getStageDependencies(collection, context, dependencies, fxaa, previousName);
  113. } else {
  114. getCompositeDependencies(collection, context, dependencies, collection, undefined);
  115. }
  116. return dependencies;
  117. }
  118. function getFramebuffer(cache, stageName, dependencies) {
  119. var collection = cache._collection;
  120. var stage = collection.getStageByName(stageName);
  121. var textureScale = stage._textureScale;
  122. var forcePowerOfTwo = stage._forcePowerOfTwo;
  123. var pixelFormat = stage._pixelFormat;
  124. var pixelDatatype = stage._pixelDatatype;
  125. var clearColor = stage._clearColor;
  126. var i;
  127. var framebuffer;
  128. var framebuffers = cache._framebuffers;
  129. var length = framebuffers.length;
  130. for (i = 0; i < length; ++i) {
  131. framebuffer = framebuffers[i];
  132. if (textureScale !== framebuffer.textureScale || forcePowerOfTwo !== framebuffer.forcePowerOfTwo ||
  133. pixelFormat !== framebuffer.pixelFormat || pixelDatatype !== framebuffer.pixelDatatype ||
  134. !Color.equals(clearColor, framebuffer.clearColor)) {
  135. continue;
  136. }
  137. var stageNames = framebuffer.stages;
  138. var stagesLength = stageNames.length;
  139. var foundConflict = false;
  140. for (var j = 0; j < stagesLength; ++j) {
  141. if (dependencies[stageNames[j]]) {
  142. foundConflict = true;
  143. break;
  144. }
  145. }
  146. if (!foundConflict) {
  147. break;
  148. }
  149. }
  150. if (defined(framebuffer) && i < length) {
  151. framebuffer.stages.push(stageName);
  152. return framebuffer;
  153. }
  154. framebuffer = {
  155. textureScale : textureScale,
  156. forcePowerOfTwo : forcePowerOfTwo,
  157. pixelFormat : pixelFormat,
  158. pixelDatatype : pixelDatatype,
  159. clearColor : clearColor,
  160. stages : [stageName],
  161. buffer : undefined,
  162. clear : undefined
  163. };
  164. framebuffers.push(framebuffer);
  165. return framebuffer;
  166. }
  167. function createFramebuffers(cache, context) {
  168. var dependencies = getDependencies(cache._collection, context);
  169. for (var stageName in dependencies) {
  170. if (dependencies.hasOwnProperty(stageName)) {
  171. cache._stageNameToFramebuffer[stageName] = getFramebuffer(cache, stageName, dependencies[stageName]);
  172. }
  173. }
  174. }
  175. function releaseResources(cache) {
  176. var framebuffers = cache._framebuffers;
  177. var length = framebuffers.length;
  178. for (var i = 0; i < length; ++i) {
  179. var framebuffer = framebuffers[i];
  180. framebuffer.buffer = framebuffer.buffer && framebuffer.buffer.destroy();
  181. framebuffer.buffer = undefined;
  182. }
  183. }
  184. function updateFramebuffers(cache, context) {
  185. var width = cache._width;
  186. var height = cache._height;
  187. var framebuffers = cache._framebuffers;
  188. var length = framebuffers.length;
  189. for (var i = 0; i < length; ++i) {
  190. var framebuffer = framebuffers[i];
  191. var scale = framebuffer.textureScale;
  192. var textureWidth = Math.ceil(width * scale);
  193. var textureHeight = Math.ceil(height * scale);
  194. var size = Math.min(textureWidth, textureHeight);
  195. if (framebuffer.forcePowerOfTwo) {
  196. if (!CesiumMath.isPowerOfTwo(size)) {
  197. size = CesiumMath.nextPowerOfTwo(size);
  198. }
  199. textureWidth = size;
  200. textureHeight = size;
  201. }
  202. framebuffer.buffer = new Framebuffer({
  203. context : context,
  204. colorTextures : [new Texture({
  205. context : context,
  206. width : textureWidth,
  207. height : textureHeight,
  208. pixelFormat : framebuffer.pixelFormat,
  209. pixelDatatype : framebuffer.pixelDatatype
  210. })]
  211. });
  212. framebuffer.clear = new ClearCommand({
  213. color : framebuffer.clearColor,
  214. framebuffer : framebuffer.buffer
  215. });
  216. }
  217. }
  218. PostProcessStageTextureCache.prototype.updateDependencies = function() {
  219. this._updateDependencies = true;
  220. };
  221. /**
  222. * Called before the stages in the collection are executed. Creates the minimum amount of framebuffers for a post-process collection.
  223. *
  224. * @param {Context} context The context.
  225. */
  226. PostProcessStageTextureCache.prototype.update = function(context) {
  227. var collection = this._collection;
  228. var updateDependencies = this._updateDependencies;
  229. var aoEnabled = defined(collection.ambientOcclusion) && collection.ambientOcclusion.enabled && collection.ambientOcclusion._isSupported(context);
  230. var bloomEnabled = defined(collection.bloom) && collection.bloom.enabled && collection.bloom._isSupported(context);
  231. var tonemappingEnabled = defined(collection._tonemapping) && collection._tonemapping.enabled && collection._tonemapping._isSupported(context);
  232. var fxaaEnabled = defined(collection.fxaa) && collection.fxaa.enabled && collection.fxaa._isSupported(context);
  233. var needsCheckDimensionsUpdate = !defined(collection._activeStages) || collection._activeStages.length > 0 || aoEnabled || bloomEnabled || tonemappingEnabled || fxaaEnabled;
  234. if (updateDependencies || (!needsCheckDimensionsUpdate && this._framebuffers.length > 0)) {
  235. releaseResources(this);
  236. this._framebuffers.length = 0;
  237. this._stageNameToFramebuffer = {};
  238. this._width = undefined;
  239. this._height = undefined;
  240. }
  241. if (!updateDependencies && !needsCheckDimensionsUpdate) {
  242. return;
  243. }
  244. if (this._framebuffers.length === 0) {
  245. createFramebuffers(this, context);
  246. }
  247. var width = context.drawingBufferWidth;
  248. var height = context.drawingBufferHeight;
  249. var dimensionsChanged = this._width !== width || this._height !== height;
  250. if (!updateDependencies && !dimensionsChanged) {
  251. return;
  252. }
  253. this._width = width;
  254. this._height = height;
  255. this._updateDependencies = false;
  256. releaseResources(this);
  257. updateFramebuffers(this, context);
  258. };
  259. /**
  260. * Clears all of the framebuffers.
  261. *
  262. * @param {Context} context The context.
  263. */
  264. PostProcessStageTextureCache.prototype.clear = function(context) {
  265. var framebuffers = this._framebuffers;
  266. for (var i = 0; i < framebuffers.length; ++i) {
  267. framebuffers[i].clear.execute(context);
  268. }
  269. };
  270. /**
  271. * Gets the stage with the given name.
  272. * @param {String} name The name of the stage.
  273. * @return {PostProcessStage|PostProcessStageComposite}
  274. */
  275. PostProcessStageTextureCache.prototype.getStageByName = function(name) {
  276. return this._collection.getStageByName(name);
  277. };
  278. /**
  279. * Gets the output texture for a stage with the given name.
  280. * @param {String} name The name of the stage.
  281. * @return {Texture|undefined} The output texture of the stage with the given name.
  282. */
  283. PostProcessStageTextureCache.prototype.getOutputTexture = function(name) {
  284. return this._collection.getOutputTexture(name);
  285. };
  286. /**
  287. * Gets the framebuffer for a stage with the given name.
  288. *
  289. * @param {String} name The name of the stage.
  290. * @return {Framebuffer|undefined} The framebuffer for the stage with the given name.
  291. */
  292. PostProcessStageTextureCache.prototype.getFramebuffer = function(name) {
  293. var framebuffer = this._stageNameToFramebuffer[name];
  294. if (!defined(framebuffer)) {
  295. return undefined;
  296. }
  297. return framebuffer.buffer;
  298. };
  299. /**
  300. * Returns true if this object was destroyed; otherwise, false.
  301. * <p>
  302. * If this object was destroyed, it should not be used; calling any function other than
  303. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
  304. * </p>
  305. *
  306. * @returns {Boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>.
  307. *
  308. * @see PostProcessStageTextureCache#destroy
  309. */
  310. PostProcessStageTextureCache.prototype.isDestroyed = function() {
  311. return false;
  312. };
  313. /**
  314. * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
  315. * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
  316. * <p>
  317. * Once an object is destroyed, it should not be used; calling any function other than
  318. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
  319. * assign the return value (<code>undefined</code>) to the object as done in the example.
  320. * </p>
  321. *
  322. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  323. *
  324. * @see PostProcessStageTextureCache#isDestroyed
  325. */
  326. PostProcessStageTextureCache.prototype.destroy = function() {
  327. releaseResources(this);
  328. return destroyObject(this);
  329. };
  330. export default PostProcessStageTextureCache;