From 0fa7440b0463e4b87ebca23d93b4b60a27d1b30e Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Thu, 18 May 2023 18:02:32 -0400 Subject: [PATCH 01/13] Adjust tileset screen space error if out of memory --- packages/engine/Source/Scene/Cesium3DTile.js | 9 +- .../engine/Source/Scene/Cesium3DTileset.js | 89 +++++++++++++------ .../Scene/Cesium3DTilesetBaseTraversal.js | 2 +- .../Source/Scene/Cesium3DTilesetCache.js | 3 +- .../Scene/Cesium3DTilesetSkipTraversal.js | 2 +- .../Source/Scene/Cesium3DTilesetTraversal.js | 4 +- 6 files changed, 70 insertions(+), 39 deletions(-) diff --git a/packages/engine/Source/Scene/Cesium3DTile.js b/packages/engine/Source/Scene/Cesium3DTile.js index 0993a95629bd..9388f5660870 100644 --- a/packages/engine/Source/Scene/Cesium3DTile.js +++ b/packages/engine/Source/Scene/Cesium3DTile.js @@ -870,7 +870,7 @@ function isPriorityDeferred(tile, frameState) { ); const sseRelaxation = tileset.foveatedInterpolationCallback( tileset.foveatedMinimumScreenSpaceErrorRelaxation, - tileset.maximumScreenSpaceError, + tileset.memoryAdjustedScreenSpaceError, normalizedFoveatedFactor ); const sse = @@ -878,7 +878,7 @@ function isPriorityDeferred(tile, frameState) { ? tile.parent._screenSpaceError * 0.5 : tile._screenSpaceError; - return tileset.maximumScreenSpaceError - sseRelaxation <= sse; + return tileset.memoryAdjustedScreenSpaceError - sseRelaxation <= sse; } const scratchJulianDate = new JulianDate(); @@ -957,12 +957,11 @@ function isPriorityProgressiveResolution(tileset, tile) { return false; } + const maximumScreenSpaceError = tileset.memoryAdjustedScreenSpaceError; let isProgressiveResolutionTile = - tile._screenSpaceErrorProgressiveResolution > - tileset._maximumScreenSpaceError; // Mark non-SSE leaves + tile._screenSpaceErrorProgressiveResolution > maximumScreenSpaceError; // Mark non-SSE leaves tile._priorityProgressiveResolutionScreenSpaceErrorLeaf = false; // Needed for skipLOD const parent = tile.parent; - const maximumScreenSpaceError = tileset._maximumScreenSpaceError; const tilePasses = tile._screenSpaceErrorProgressiveResolution <= maximumScreenSpaceError; const parentFails = diff --git a/packages/engine/Source/Scene/Cesium3DTileset.js b/packages/engine/Source/Scene/Cesium3DTileset.js index 7d7466bf4b6f..177326f509d7 100644 --- a/packages/engine/Source/Scene/Cesium3DTileset.js +++ b/packages/engine/Source/Scene/Cesium3DTileset.js @@ -222,6 +222,7 @@ function Cesium3DTileset(options) { 16 ); this._maximumMemoryUsage = defaultValue(options.maximumMemoryUsage, 512); + this._memoryAdjustedScreenSpaceError = this._maximumScreenSpaceError; this._styleEngine = new Cesium3DTileStyleEngine(); this._styleApplied = false; @@ -1559,6 +1560,25 @@ Object.defineProperties(Cesium3DTileset.prototype, { }, }, + /** + * If loading the level of detail required by @{link Cesium3DTileset#maximumScreenSpaceError} + * results in the memory usage exceeding maximumMemoryUsage, level of detail refinement will + * instead use this (larger) adjusted screen space error to achieve the best possible visual + * quality within the available memory + * + * @memberof Cesium3DTileset.prototype + * + * @type {number} + * @readonly + * + * @private + */ + memoryAdjustedScreenSpaceError: { + get: function () { + return this._memoryAdjustedScreenSpaceError; + }, + }, + /** * Options for controlling point size based on geometric error and eye dome lighting. * @@ -2661,30 +2681,6 @@ function handleTileFailure(error, tileset, tile) { } } -/** - * @private - * @param {Cesium3DTileset} tileset - */ -function filterProcessingQueue(tileset) { - const tiles = tileset._processingQueue; - - let removeCount = 0; - for (let i = 0; i < tiles.length; ++i) { - const tile = tiles[i]; - if ( - tile.isDestroyed() || - tile._contentState !== Cesium3DTileContentState.PROCESSING - ) { - ++removeCount; - continue; - } - if (removeCount > 0) { - tiles[i - removeCount] = tile; - } - } - tiles.length -= removeCount; -} - /** * Process tiles in the PROCESSING state so they will eventually move to the READY state. * @private @@ -2692,12 +2688,19 @@ function filterProcessingQueue(tileset) { * @param {Cesium3DTile} tile */ function processTiles(tileset, frameState) { - filterProcessingQueue(tileset); - const tiles = tileset._processingQueue; - const statistics = tileset._statistics; - let tile; + const tiles = tileset._processingQueue.filter( + (tile) => + !tile.isDestroyed() && + tile._contentState === Cesium3DTileContentState.PROCESSING + ); + tileset._processingQueue = tiles; + + const { maximumMemoryUsage, statistics } = tileset; + const maximumMemoryUsageInBytes = maximumMemoryUsage * 1024 * 1024; + + let memoryExceeded = false; for (let i = 0; i < tiles.length; ++i) { - tile = tiles[i]; + const tile = tiles[i]; try { tile.process(tileset, frameState); @@ -2709,9 +2712,37 @@ function processTiles(tileset, frameState) { --statistics.numberOfTilesProcessing; handleTileFailure(error, tileset, tile); } + + if (tileset.totalMemoryUsageInBytes > maximumMemoryUsageInBytes) { + memoryExceeded = true; + break; + } + } + + if (tileset.totalMemoryUsageInBytes < maximumMemoryUsageInBytes * 0.75) { + decreaseScreenSpaceError(tileset); + } else if (memoryExceeded && tiles.length > 1) { + increaseScreenSpaceError(tileset); } } +function increaseScreenSpaceError(tileset) { + tileset._memoryAdjustedScreenSpaceError *= 1.01; + console.log( + `tileset.totalMemoryUsageInBytes = ${tileset.totalMemoryUsageInBytes}. memoryAdjustedScreenSpaceError increased to ${tileset.memoryAdjustedScreenSpaceError}` + ); +} + +function decreaseScreenSpaceError(tileset) { + tileset._memoryAdjustedScreenSpaceError = Math.max( + tileset.memoryAdjustedScreenSpaceError / 1.01, + tileset.maximumScreenSpaceError + ); + console.log( + `memoryAdjustedScreenSpaceError decreased to ${tileset.memoryAdjustedScreenSpaceError}` + ); +} + const scratchCartesian = new Cartesian3(); const stringOptions = { diff --git a/packages/engine/Source/Scene/Cesium3DTilesetBaseTraversal.js b/packages/engine/Source/Scene/Cesium3DTilesetBaseTraversal.js index a85ddcc4013c..df773afbcc11 100644 --- a/packages/engine/Source/Scene/Cesium3DTilesetBaseTraversal.js +++ b/packages/engine/Source/Scene/Cesium3DTilesetBaseTraversal.js @@ -53,7 +53,7 @@ Cesium3DTilesetBaseTraversal.selectTiles = function (tileset, frameState) { if ( root.getScreenSpaceError(frameState, true) <= - tileset._maximumScreenSpaceError + tileset.memoryAdjustedScreenSpaceError ) { return; } diff --git a/packages/engine/Source/Scene/Cesium3DTilesetCache.js b/packages/engine/Source/Scene/Cesium3DTilesetCache.js index 6605a26f47dd..12186e1c63eb 100644 --- a/packages/engine/Source/Scene/Cesium3DTilesetCache.js +++ b/packages/engine/Source/Scene/Cesium3DTilesetCache.js @@ -68,7 +68,8 @@ Cesium3DTilesetCache.prototype.unloadTiles = function ( let node = list.head; while ( node !== sentinel && - (tileset.totalMemoryUsageInBytes > maximumMemoryUsageInBytes || trimTiles) + (tileset.totalMemoryUsageInBytes > maximumMemoryUsageInBytes * 0.7 || + trimTiles) ) { const tile = node.item; node = node.next; diff --git a/packages/engine/Source/Scene/Cesium3DTilesetSkipTraversal.js b/packages/engine/Source/Scene/Cesium3DTilesetSkipTraversal.js index aea34eee3fcb..bbfea15a6788 100644 --- a/packages/engine/Source/Scene/Cesium3DTilesetSkipTraversal.js +++ b/packages/engine/Source/Scene/Cesium3DTilesetSkipTraversal.js @@ -61,7 +61,7 @@ Cesium3DTilesetSkipTraversal.selectTiles = function (tileset, frameState) { if ( root.getScreenSpaceError(frameState, true) <= - tileset._maximumScreenSpaceError + tileset.memoryAdjustedScreenSpaceError ) { return; } diff --git a/packages/engine/Source/Scene/Cesium3DTilesetTraversal.js b/packages/engine/Source/Scene/Cesium3DTilesetTraversal.js index fc30a1eca829..12561ba541eb 100644 --- a/packages/engine/Source/Scene/Cesium3DTilesetTraversal.js +++ b/packages/engine/Source/Scene/Cesium3DTilesetTraversal.js @@ -64,7 +64,7 @@ Cesium3DTilesetTraversal.canTraverse = function (tile) { // Don't traverse if the subtree is expired because it will be destroyed return !tile.contentExpired; } - return tile._screenSpaceError > tile.tileset._maximumScreenSpaceError; + return tile._screenSpaceError > tile.tileset.memoryAdjustedScreenSpaceError; }; /** @@ -260,7 +260,7 @@ function meetsScreenSpaceErrorEarly(tile, frameState) { // Use parent's geometric error with child's box to see if the tile already meet the SSE return ( tile.getScreenSpaceError(frameState, true) <= - tileset._maximumScreenSpaceError + tileset.memoryAdjustedScreenSpaceError ); } From c93423aebbab43c3de7a0a0c038b5275999e1a3c Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Thu, 18 May 2023 18:42:35 -0400 Subject: [PATCH 02/13] Sort tileset processing queue when hitting memory limit --- packages/engine/Source/Scene/Cesium3DTileset.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/engine/Source/Scene/Cesium3DTileset.js b/packages/engine/Source/Scene/Cesium3DTileset.js index 177326f509d7..84cf3d441bc9 100644 --- a/packages/engine/Source/Scene/Cesium3DTileset.js +++ b/packages/engine/Source/Scene/Cesium3DTileset.js @@ -2544,7 +2544,7 @@ function requestContent(tileset, tile) { tileset._requestedTilesInFlight.push(tile); } -function sortRequestByPriority(a, b) { +function sortTilesByPriority(a, b) { return a._priority - b._priority; } @@ -2647,7 +2647,7 @@ function cancelOutOfViewRequests(tileset, frameState) { */ function requestTiles(tileset) { const requestedTiles = tileset._requestedTiles; - requestedTiles.sort(sortRequestByPriority); + requestedTiles.sort(sortTilesByPriority); for (let i = 0; i < requestedTiles.length; ++i) { requestContent(tileset, requestedTiles[i]); } @@ -2731,6 +2731,11 @@ function increaseScreenSpaceError(tileset) { console.log( `tileset.totalMemoryUsageInBytes = ${tileset.totalMemoryUsageInBytes}. memoryAdjustedScreenSpaceError increased to ${tileset.memoryAdjustedScreenSpaceError}` ); + const tiles = tileset._processingQueue; + for (let i = 0; i < tiles.length; ++i) { + tiles[i].updatePriority(); + } + tiles.sort(sortTilesByPriority); } function decreaseScreenSpaceError(tileset) { From 860dfb38b1be4dbb99e31accbd5e95aa472fbec1 Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Fri, 19 May 2023 16:34:14 -0400 Subject: [PATCH 03/13] Add cachedBytes and maximumCachedBytes tileset options --- .../engine/Source/Scene/Cesium3DTileset.js | 115 ++++++++++++++++-- .../Source/Scene/Cesium3DTilesetCache.js | 5 +- 2 files changed, 104 insertions(+), 16 deletions(-) diff --git a/packages/engine/Source/Scene/Cesium3DTileset.js b/packages/engine/Source/Scene/Cesium3DTileset.js index 84cf3d441bc9..44ce107d9900 100644 --- a/packages/engine/Source/Scene/Cesium3DTileset.js +++ b/packages/engine/Source/Scene/Cesium3DTileset.js @@ -68,7 +68,9 @@ import Cesium3DTilesetSkipTraversal from "./Cesium3DTilesetSkipTraversal.js"; * @property {Axis} [modelForwardAxis=Axis.X] Which axis is considered forward when loading models for tile contents. * @property {ShadowMode} [shadows=ShadowMode.ENABLED] Determines whether the tileset casts or receives shadows from light sources. * @property {number} [maximumScreenSpaceError=16] The maximum screen space error used to drive level of detail refinement. - * @property {number} [maximumMemoryUsage=512] The maximum amount of memory in MB that can be used by the tileset. + * @property {number} [maximumMemoryUsage=512] The maximum amount of memory in MB that can be used by the tileset. Deprecated. + * @property {number} [cachedBytes=536870912] The size (in bytes) to which the tile cache will be trimmed + * @property {number} [maximumCachedBytes=1073741824] The maximum amount of memory in bytes to use for the cache. Must be larger than {@link Cesium3DTileset#cachedBytes} * @property {boolean} [cullWithChildrenBounds=true] Optimization option. Whether to cull tiles using the union of their children bounding volumes. * @property {boolean} [cullRequestsWhileMoving=true] Optimization option. Don't request tiles that will likely be unused when they come back because of the camera's movement. This optimization only applies to stationary tilesets. * @property {number} [cullRequestsWhileMovingMultiplier=60.0] Optimization option. Multiplier used in culling requests while moving. Larger is more aggressive culling, smaller less aggressive culling. @@ -221,9 +223,22 @@ function Cesium3DTileset(options) { options.maximumScreenSpaceError, 16 ); - this._maximumMemoryUsage = defaultValue(options.maximumMemoryUsage, 512); this._memoryAdjustedScreenSpaceError = this._maximumScreenSpaceError; + let defaultCachedBytes = 512 * 1024 * 1024; + if (defined(options.maximumMemoryUsage)) { + deprecationWarning( + "Cesium3DTileset.maximumMemoryUsage", + "Cesium3DTileset.maximumMemoryUsage was deprecated in CesiumJS 1.106. It will be removed in CesiumJS 1.109. Use Cesium3DTileset.cachedBytes instead." + ); + defaultCachedBytes = options.maximumMemoryUsage * 1024 * 1024; + } + this._cachedBytes = defaultValue(options.cachedBytes, defaultCachedBytes); + this._maximumCachedBytes = defaultValue( + options.maximumCachedBytes, + this._cachedBytes * 2.0 + ); + this._styleEngine = new Cesium3DTileStyleEngine(); this._styleApplied = false; @@ -591,7 +606,7 @@ function Cesium3DTileset(options) { * console.log('A tile was unloaded from the cache.'); * }); * - * @see Cesium3DTileset#maximumMemoryUsage + * @see Cesium3DTileset#cachedBytes * @see Cesium3DTileset#trimLoadedTiles */ this.tileUnload = new Event(); @@ -1546,12 +1561,22 @@ Object.defineProperties(Cesium3DTileset.prototype, { * * @exception {DeveloperError} maximumMemoryUsage must be greater than or equal to zero. * @see Cesium3DTileset#totalMemoryUsageInBytes + * + * @deprecated */ maximumMemoryUsage: { get: function () { + deprecationWarning( + "Cesium3DTileset.maximumMemoryUsage", + "Cesium3DTileset.maximumMemoryUsage was deprecated in CesiumJS 1.106. It will be removed in CesiumJS 1.109. Use Cesium3DTileset.cachedBytes instead." + ); return this._maximumMemoryUsage; }, set: function (value) { + deprecationWarning( + "Cesium3DTileset.maximumMemoryUsage", + "Cesium3DTileset.maximumMemoryUsage was deprecated in CesiumJS 1.106. It will be removed in CesiumJS 1.109. Use Cesium3DTileset.cachedBytes instead." + ); //>>includeStart('debug', pragmas.debug); Check.typeOf.number.greaterThanOrEquals("value", value, 0); //>>includeEnd('debug'); @@ -1560,11 +1585,78 @@ Object.defineProperties(Cesium3DTileset.prototype, { }, }, + /** + * The amount of GPU memory (in MB) used to cache tiles. This memory usage is estimated from + * geometry, textures, and batch table textures of loaded tiles. For point clouds, this value also + * includes per-point metadata. + *

+ * Tiles not in view are unloaded to enforce this. + *

+ *

+ * If decreasing this value results in unloading tiles, the tiles are unloaded the next frame. + *

+ *

+ * If tiles sized more than cachedBytes are needed to meet the + * desired screen space error, determined by {@link Cesium3DTileset#maximumScreenSpaceError}, + * for the current view, then the memory usage of the tiles loaded will exceed + * cachedBytes. For example, if the maximum is 256 MB, but 300 MB + * of tiles are needed to meet the screen space error, then 300 MB of tiles may + * be loaded. When these tiles go out of view, they will be unloaded. + *

+ * + * @memberof Cesium3DTileset.prototype + * + * @type {number} + * @default 536870912 + * + * @exception {DeveloperError} cachedBytes must be typeof 'number' and greater than or equal to 0 + * @see Cesium3DTileset#totalMemoryUsageInBytes + */ + cachedBytes: { + get: function () { + return this._cachedBytes; + }, + set: function (value) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.number.greaterThanOrEquals("value", value, 0); + //>>includeEnd('debug'); + + this._cachedBytes = value; + this._maximumCachedBytes = Math.max( + this._maximumCachedBytes, + value * 1.1 + ); + }, + }, + + /** + * The maximum amount of GPU memory (in MB) that will be used to cache tiles. + *

+ * If tiles sized more than maximumCachedBytes are needed to meet the + * desired screen space error, determined by {@link Cesium3DTileset#maximumScreenSpaceError}, + * for the current view, then {@link Cesium3DTileset#memoryAdjustedScreenSpaceError} + * will be adjusted until the tiles required to meet the adjusted screen space error + * are sized less than maximumCachedBytes. + *

+ * + * @memberof Cesium3DTileset.prototype + * + * @type {number} + * @readonly + * + * @see Cesium3DTileset#totalMemoryUsageInBytes + */ + maximumCachedBytes: { + get: function () { + return this._maximumCachedBytes; + }, + }, + /** * If loading the level of detail required by @{link Cesium3DTileset#maximumScreenSpaceError} - * results in the memory usage exceeding maximumMemoryUsage, level of detail refinement will - * instead use this (larger) adjusted screen space error to achieve the best possible visual - * quality within the available memory + * results in the memory usage exceeding @{link Cesium3DTileset#maximumCachedBytes}, + * level of detail refinement will instead use this (larger) adjusted screen space error + * to achieve the best possible visual quality within the available memory * * @memberof Cesium3DTileset.prototype * @@ -1685,7 +1777,7 @@ Object.defineProperties(Cesium3DTileset.prototype, { * @type {number} * @readonly * - * @see Cesium3DTileset#maximumMemoryUsage + * @see Cesium3DTileset#cachedBytes */ totalMemoryUsageInBytes: { get: function () { @@ -2695,8 +2787,7 @@ function processTiles(tileset, frameState) { ); tileset._processingQueue = tiles; - const { maximumMemoryUsage, statistics } = tileset; - const maximumMemoryUsageInBytes = maximumMemoryUsage * 1024 * 1024; + const { cachedBytes, maximumCachedBytes, statistics } = tileset; let memoryExceeded = false; for (let i = 0; i < tiles.length; ++i) { @@ -2713,13 +2804,13 @@ function processTiles(tileset, frameState) { handleTileFailure(error, tileset, tile); } - if (tileset.totalMemoryUsageInBytes > maximumMemoryUsageInBytes) { + if (tileset.totalMemoryUsageInBytes > maximumCachedBytes) { memoryExceeded = true; break; } } - if (tileset.totalMemoryUsageInBytes < maximumMemoryUsageInBytes * 0.75) { + if (tileset.totalMemoryUsageInBytes < cachedBytes) { decreaseScreenSpaceError(tileset); } else if (memoryExceeded && tiles.length > 1) { increaseScreenSpaceError(tileset); @@ -3093,7 +3184,7 @@ function destroyTile(tileset, tile) { /** * Unloads all tiles that weren't selected the previous frame. This can be used to * explicitly manage the tile cache and reduce the total number of tiles loaded below - * {@link Cesium3DTileset#maximumMemoryUsage}. + * {@link Cesium3DTileset#cachedBytes}. *

* Tile unloads occur at the next frame to keep all the WebGL delete calls * within the render loop. diff --git a/packages/engine/Source/Scene/Cesium3DTilesetCache.js b/packages/engine/Source/Scene/Cesium3DTilesetCache.js index 12186e1c63eb..6e0df2a470f8 100644 --- a/packages/engine/Source/Scene/Cesium3DTilesetCache.js +++ b/packages/engine/Source/Scene/Cesium3DTilesetCache.js @@ -58,8 +58,6 @@ Cesium3DTilesetCache.prototype.unloadTiles = function ( const list = this._list; - const maximumMemoryUsageInBytes = tileset.maximumMemoryUsage * 1024 * 1024; - // Traverse the list only to the sentinel since tiles/nodes to the // right of the sentinel were used this frame. // @@ -68,8 +66,7 @@ Cesium3DTilesetCache.prototype.unloadTiles = function ( let node = list.head; while ( node !== sentinel && - (tileset.totalMemoryUsageInBytes > maximumMemoryUsageInBytes * 0.7 || - trimTiles) + (tileset.totalMemoryUsageInBytes > tileset.cachedBytes || trimTiles) ) { const tile = node.item; node = node.next; From 92d7493132964f2e1edae10a6856dcfe69120557 Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Fri, 19 May 2023 18:48:15 -0400 Subject: [PATCH 04/13] Fix failing tests and types Remove console.logs --- packages/engine/Source/Scene/Cesium3DTileset.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/engine/Source/Scene/Cesium3DTileset.js b/packages/engine/Source/Scene/Cesium3DTileset.js index 44ce107d9900..d5f74ac6e96a 100644 --- a/packages/engine/Source/Scene/Cesium3DTileset.js +++ b/packages/engine/Source/Scene/Cesium3DTileset.js @@ -1532,6 +1532,7 @@ Object.defineProperties(Cesium3DTileset.prototype, { //>>includeEnd('debug'); this._maximumScreenSpaceError = value; + this._memoryAdjustedScreenSpaceError = value; }, }, @@ -1582,6 +1583,7 @@ Object.defineProperties(Cesium3DTileset.prototype, { //>>includeEnd('debug'); this._maximumMemoryUsage = value; + this._cachedBytes = value * 1024 * 1024; }, }, @@ -2819,9 +2821,6 @@ function processTiles(tileset, frameState) { function increaseScreenSpaceError(tileset) { tileset._memoryAdjustedScreenSpaceError *= 1.01; - console.log( - `tileset.totalMemoryUsageInBytes = ${tileset.totalMemoryUsageInBytes}. memoryAdjustedScreenSpaceError increased to ${tileset.memoryAdjustedScreenSpaceError}` - ); const tiles = tileset._processingQueue; for (let i = 0; i < tiles.length; ++i) { tiles[i].updatePriority(); @@ -2834,9 +2833,6 @@ function decreaseScreenSpaceError(tileset) { tileset.memoryAdjustedScreenSpaceError / 1.01, tileset.maximumScreenSpaceError ); - console.log( - `memoryAdjustedScreenSpaceError decreased to ${tileset.memoryAdjustedScreenSpaceError}` - ); } const scratchCartesian = new Cartesian3(); From 703a39f82ad7cd7241578d609085a5ac4538a324 Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Sun, 21 May 2023 21:00:10 -0400 Subject: [PATCH 05/13] Use cacheHeadroomBytes to specify memory limit for tileset cache --- .../engine/Source/Scene/Cesium3DTileset.js | 85 ++++++++++--------- .../Source/Scene/Cesium3DTilesetCache.js | 2 +- 2 files changed, 47 insertions(+), 40 deletions(-) diff --git a/packages/engine/Source/Scene/Cesium3DTileset.js b/packages/engine/Source/Scene/Cesium3DTileset.js index d5f74ac6e96a..c36f10c83109 100644 --- a/packages/engine/Source/Scene/Cesium3DTileset.js +++ b/packages/engine/Source/Scene/Cesium3DTileset.js @@ -69,8 +69,8 @@ import Cesium3DTilesetSkipTraversal from "./Cesium3DTilesetSkipTraversal.js"; * @property {ShadowMode} [shadows=ShadowMode.ENABLED] Determines whether the tileset casts or receives shadows from light sources. * @property {number} [maximumScreenSpaceError=16] The maximum screen space error used to drive level of detail refinement. * @property {number} [maximumMemoryUsage=512] The maximum amount of memory in MB that can be used by the tileset. Deprecated. - * @property {number} [cachedBytes=536870912] The size (in bytes) to which the tile cache will be trimmed - * @property {number} [maximumCachedBytes=1073741824] The maximum amount of memory in bytes to use for the cache. Must be larger than {@link Cesium3DTileset#cachedBytes} + * @property {number} [cacheBytes=536870912] The size (in bytes) to which the tile cache will be trimmed, if the cache contains tiles not needed for the current view + * @property {number} [cacheHeadroomBytes=536870912] The maximum additional memory (in bytes) to allow for cache headroom, if more than {@link Cesium3DTileset#cacheBytes} are needed for the current view * @property {boolean} [cullWithChildrenBounds=true] Optimization option. Whether to cull tiles using the union of their children bounding volumes. * @property {boolean} [cullRequestsWhileMoving=true] Optimization option. Don't request tiles that will likely be unused when they come back because of the camera's movement. This optimization only applies to stationary tilesets. * @property {number} [cullRequestsWhileMovingMultiplier=60.0] Optimization option. Multiplier used in culling requests while moving. Larger is more aggressive culling, smaller less aggressive culling. @@ -229,14 +229,14 @@ function Cesium3DTileset(options) { if (defined(options.maximumMemoryUsage)) { deprecationWarning( "Cesium3DTileset.maximumMemoryUsage", - "Cesium3DTileset.maximumMemoryUsage was deprecated in CesiumJS 1.106. It will be removed in CesiumJS 1.109. Use Cesium3DTileset.cachedBytes instead." + "Cesium3DTileset.maximumMemoryUsage was deprecated in CesiumJS 1.106. It will be removed in CesiumJS 1.109. Use Cesium3DTileset.cacheBytes instead." ); defaultCachedBytes = options.maximumMemoryUsage * 1024 * 1024; } - this._cachedBytes = defaultValue(options.cachedBytes, defaultCachedBytes); - this._maximumCachedBytes = defaultValue( - options.maximumCachedBytes, - this._cachedBytes * 2.0 + this._cacheBytes = defaultValue(options.cacheBytes, defaultCachedBytes); + this._cacheHeadroomBytes = defaultValue( + options.cacheHeadroomBytes, + defaultCachedBytes ); this._styleEngine = new Cesium3DTileStyleEngine(); @@ -606,7 +606,7 @@ function Cesium3DTileset(options) { * console.log('A tile was unloaded from the cache.'); * }); * - * @see Cesium3DTileset#cachedBytes + * @see Cesium3DTileset#cacheBytes * @see Cesium3DTileset#trimLoadedTiles */ this.tileUnload = new Event(); @@ -1569,26 +1569,26 @@ Object.defineProperties(Cesium3DTileset.prototype, { get: function () { deprecationWarning( "Cesium3DTileset.maximumMemoryUsage", - "Cesium3DTileset.maximumMemoryUsage was deprecated in CesiumJS 1.106. It will be removed in CesiumJS 1.109. Use Cesium3DTileset.cachedBytes instead." + "Cesium3DTileset.maximumMemoryUsage was deprecated in CesiumJS 1.106. It will be removed in CesiumJS 1.109. Use Cesium3DTileset.cacheBytes instead." ); return this._maximumMemoryUsage; }, set: function (value) { deprecationWarning( "Cesium3DTileset.maximumMemoryUsage", - "Cesium3DTileset.maximumMemoryUsage was deprecated in CesiumJS 1.106. It will be removed in CesiumJS 1.109. Use Cesium3DTileset.cachedBytes instead." + "Cesium3DTileset.maximumMemoryUsage was deprecated in CesiumJS 1.106. It will be removed in CesiumJS 1.109. Use Cesium3DTileset.cacheBytes instead." ); //>>includeStart('debug', pragmas.debug); Check.typeOf.number.greaterThanOrEquals("value", value, 0); //>>includeEnd('debug'); this._maximumMemoryUsage = value; - this._cachedBytes = value * 1024 * 1024; + this._cacheBytes = value * 1024 * 1024; }, }, /** - * The amount of GPU memory (in MB) used to cache tiles. This memory usage is estimated from + * The amount of GPU memory (in bytes) used to cache tiles. This memory usage is estimated from * geometry, textures, and batch table textures of loaded tiles. For point clouds, this value also * includes per-point metadata. *

@@ -1598,10 +1598,10 @@ Object.defineProperties(Cesium3DTileset.prototype, { * If decreasing this value results in unloading tiles, the tiles are unloaded the next frame. *

*

- * If tiles sized more than cachedBytes are needed to meet the + * If tiles sized more than cacheBytes are needed to meet the * desired screen space error, determined by {@link Cesium3DTileset#maximumScreenSpaceError}, * for the current view, then the memory usage of the tiles loaded will exceed - * cachedBytes. For example, if the maximum is 256 MB, but 300 MB + * cacheBytes. For example, if the maximum is 256 MB, but 300 MB * of tiles are needed to meet the screen space error, then 300 MB of tiles may * be loaded. When these tiles go out of view, they will be unloaded. *

@@ -1611,54 +1611,60 @@ Object.defineProperties(Cesium3DTileset.prototype, { * @type {number} * @default 536870912 * - * @exception {DeveloperError} cachedBytes must be typeof 'number' and greater than or equal to 0 + * @exception {DeveloperError} cacheBytes must be typeof 'number' and greater than or equal to 0 * @see Cesium3DTileset#totalMemoryUsageInBytes */ - cachedBytes: { + cacheBytes: { get: function () { - return this._cachedBytes; + return this._cacheBytes; }, set: function (value) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number.greaterThanOrEquals("value", value, 0); //>>includeEnd('debug'); - this._cachedBytes = value; - this._maximumCachedBytes = Math.max( - this._maximumCachedBytes, - value * 1.1 - ); + this._cacheBytes = value; }, }, /** - * The maximum amount of GPU memory (in MB) that will be used to cache tiles. + * The maximum additional amount of GPU memory (in bytes) that will be used to cache tiles. *

- * If tiles sized more than maximumCachedBytes are needed to meet the - * desired screen space error, determined by {@link Cesium3DTileset#maximumScreenSpaceError}, - * for the current view, then {@link Cesium3DTileset#memoryAdjustedScreenSpaceError} - * will be adjusted until the tiles required to meet the adjusted screen space error - * are sized less than maximumCachedBytes. + * If tiles sized more than cacheBytes plus cacheHeadroomBytes + * are needed to meet the desired screen space error, determined by + * {@link Cesium3DTileset#maximumScreenSpaceError} for the current view, then + * {@link Cesium3DTileset#memoryAdjustedScreenSpaceError} will be adjusted + * until the tiles required to meet the adjusted screen space error use less + * than cacheBytes plus cacheHeadroomBytes. *

* * @memberof Cesium3DTileset.prototype * * @type {number} - * @readonly + * @default 536870912 * + * @exception {DeveloperError} cacheHeadroomBytes must be typeof 'number' and greater than or equal to 0 * @see Cesium3DTileset#totalMemoryUsageInBytes */ - maximumCachedBytes: { + cacheHeadroomBytes: { get: function () { - return this._maximumCachedBytes; + return this._cacheHeadroomBytes; + }, + set: function (value) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.number.greaterThanOrEquals("value", value, 0); + //>>includeEnd('debug'); + + this._cacheHeadroomBytes = value; }, }, /** * If loading the level of detail required by @{link Cesium3DTileset#maximumScreenSpaceError} - * results in the memory usage exceeding @{link Cesium3DTileset#maximumCachedBytes}, - * level of detail refinement will instead use this (larger) adjusted screen space error - * to achieve the best possible visual quality within the available memory + * results in the memory usage exceeding @{link Cesium3DTileset#cacheBytes} + * plus @{link Cesium3DTileset#cacheHeadroomBytes}, level of detail refinement + * will instead use this (larger) adjusted screen space error to achieve the + * best possible visual quality within the available memory * * @memberof Cesium3DTileset.prototype * @@ -1779,7 +1785,7 @@ Object.defineProperties(Cesium3DTileset.prototype, { * @type {number} * @readonly * - * @see Cesium3DTileset#cachedBytes + * @see Cesium3DTileset#cacheBytes */ totalMemoryUsageInBytes: { get: function () { @@ -2789,7 +2795,8 @@ function processTiles(tileset, frameState) { ); tileset._processingQueue = tiles; - const { cachedBytes, maximumCachedBytes, statistics } = tileset; + const { cacheBytes, cacheHeadroomBytes, statistics } = tileset; + const cacheByteLimit = cacheBytes + cacheHeadroomBytes; let memoryExceeded = false; for (let i = 0; i < tiles.length; ++i) { @@ -2806,13 +2813,13 @@ function processTiles(tileset, frameState) { handleTileFailure(error, tileset, tile); } - if (tileset.totalMemoryUsageInBytes > maximumCachedBytes) { + if (tileset.totalMemoryUsageInBytes > cacheByteLimit) { memoryExceeded = true; break; } } - if (tileset.totalMemoryUsageInBytes < cachedBytes) { + if (tileset.totalMemoryUsageInBytes < cacheBytes) { decreaseScreenSpaceError(tileset); } else if (memoryExceeded && tiles.length > 1) { increaseScreenSpaceError(tileset); @@ -3180,7 +3187,7 @@ function destroyTile(tileset, tile) { /** * Unloads all tiles that weren't selected the previous frame. This can be used to * explicitly manage the tile cache and reduce the total number of tiles loaded below - * {@link Cesium3DTileset#cachedBytes}. + * {@link Cesium3DTileset#cacheBytes}. *

* Tile unloads occur at the next frame to keep all the WebGL delete calls * within the render loop. diff --git a/packages/engine/Source/Scene/Cesium3DTilesetCache.js b/packages/engine/Source/Scene/Cesium3DTilesetCache.js index 6e0df2a470f8..d4a8a5015159 100644 --- a/packages/engine/Source/Scene/Cesium3DTilesetCache.js +++ b/packages/engine/Source/Scene/Cesium3DTilesetCache.js @@ -66,7 +66,7 @@ Cesium3DTilesetCache.prototype.unloadTiles = function ( let node = list.head; while ( node !== sentinel && - (tileset.totalMemoryUsageInBytes > tileset.cachedBytes || trimTiles) + (tileset.totalMemoryUsageInBytes > tileset.cacheBytes || trimTiles) ) { const tile = node.item; node = node.next; From a8ddd9749cd7e5a79c4a9c42d7e329ffab060faf Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Thu, 25 May 2023 01:58:21 -0400 Subject: [PATCH 06/13] Tweak memory-adjusted screen space error functions --- packages/engine/Source/Scene/Cesium3DTileset.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/engine/Source/Scene/Cesium3DTileset.js b/packages/engine/Source/Scene/Cesium3DTileset.js index c36f10c83109..d7bdf8b927eb 100644 --- a/packages/engine/Source/Scene/Cesium3DTileset.js +++ b/packages/engine/Source/Scene/Cesium3DTileset.js @@ -2821,13 +2821,13 @@ function processTiles(tileset, frameState) { if (tileset.totalMemoryUsageInBytes < cacheBytes) { decreaseScreenSpaceError(tileset); - } else if (memoryExceeded && tiles.length > 1) { + } else if (memoryExceeded && tiles.length > 0) { increaseScreenSpaceError(tileset); } } function increaseScreenSpaceError(tileset) { - tileset._memoryAdjustedScreenSpaceError *= 1.01; + tileset._memoryAdjustedScreenSpaceError *= 1.02; const tiles = tileset._processingQueue; for (let i = 0; i < tiles.length; ++i) { tiles[i].updatePriority(); @@ -2837,7 +2837,7 @@ function increaseScreenSpaceError(tileset) { function decreaseScreenSpaceError(tileset) { tileset._memoryAdjustedScreenSpaceError = Math.max( - tileset.memoryAdjustedScreenSpaceError / 1.01, + tileset.memoryAdjustedScreenSpaceError / 1.02, tileset.maximumScreenSpaceError ); } From 4e8006e30dba3d07229bd4fba09e9c0f7a7e6fba Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Thu, 25 May 2023 15:01:26 -0400 Subject: [PATCH 07/13] Use larger cache size in createGooglePhotorealistic3DTileset --- packages/engine/Source/Scene/Cesium3DTileset.js | 8 ++++---- .../Source/Scene/createGooglePhotorealistic3DTileset.js | 5 +++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/engine/Source/Scene/Cesium3DTileset.js b/packages/engine/Source/Scene/Cesium3DTileset.js index d7bdf8b927eb..548dcb171f34 100644 --- a/packages/engine/Source/Scene/Cesium3DTileset.js +++ b/packages/engine/Source/Scene/Cesium3DTileset.js @@ -225,18 +225,18 @@ function Cesium3DTileset(options) { ); this._memoryAdjustedScreenSpaceError = this._maximumScreenSpaceError; - let defaultCachedBytes = 512 * 1024 * 1024; + let defaultCacheBytes = 512 * 1024 * 1024; if (defined(options.maximumMemoryUsage)) { deprecationWarning( "Cesium3DTileset.maximumMemoryUsage", "Cesium3DTileset.maximumMemoryUsage was deprecated in CesiumJS 1.106. It will be removed in CesiumJS 1.109. Use Cesium3DTileset.cacheBytes instead." ); - defaultCachedBytes = options.maximumMemoryUsage * 1024 * 1024; + defaultCacheBytes = options.maximumMemoryUsage * 1024 * 1024; } - this._cacheBytes = defaultValue(options.cacheBytes, defaultCachedBytes); + this._cacheBytes = defaultValue(options.cacheBytes, defaultCacheBytes); this._cacheHeadroomBytes = defaultValue( options.cacheHeadroomBytes, - defaultCachedBytes + defaultCacheBytes ); this._styleEngine = new Cesium3DTileStyleEngine(); diff --git a/packages/engine/Source/Scene/createGooglePhotorealistic3DTileset.js b/packages/engine/Source/Scene/createGooglePhotorealistic3DTileset.js index d644f3988eff..24a5942689bc 100644 --- a/packages/engine/Source/Scene/createGooglePhotorealistic3DTileset.js +++ b/packages/engine/Source/Scene/createGooglePhotorealistic3DTileset.js @@ -38,6 +38,11 @@ async function createGooglePhotorealistic3DTileset(key, options) { options = defaultValue(options, {}); options.showCreditsOnScreen = true; + options.cacheBytes = defaultValue(options.cacheBytes, 1536 * 1024 * 1024); + options.cacheHeadroomBytes = defaultValue( + options.cacheHeadroomBytes, + 1024 * 1024 * 1024 + ); const resource = new Resource({ url: `${GoogleMaps.mapTilesApiEndpoint}3dtiles/root.json`, From 558fcdb79373920d76ba3758a5f20d9cef0bd71b Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Thu, 25 May 2023 15:14:48 -0400 Subject: [PATCH 08/13] Require cacheHeadroomBytes to be at least 10 MB --- packages/engine/Source/Scene/Cesium3DTileset.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/engine/Source/Scene/Cesium3DTileset.js b/packages/engine/Source/Scene/Cesium3DTileset.js index 548dcb171f34..e67911fb15ae 100644 --- a/packages/engine/Source/Scene/Cesium3DTileset.js +++ b/packages/engine/Source/Scene/Cesium3DTileset.js @@ -69,8 +69,8 @@ import Cesium3DTilesetSkipTraversal from "./Cesium3DTilesetSkipTraversal.js"; * @property {ShadowMode} [shadows=ShadowMode.ENABLED] Determines whether the tileset casts or receives shadows from light sources. * @property {number} [maximumScreenSpaceError=16] The maximum screen space error used to drive level of detail refinement. * @property {number} [maximumMemoryUsage=512] The maximum amount of memory in MB that can be used by the tileset. Deprecated. - * @property {number} [cacheBytes=536870912] The size (in bytes) to which the tile cache will be trimmed, if the cache contains tiles not needed for the current view - * @property {number} [cacheHeadroomBytes=536870912] The maximum additional memory (in bytes) to allow for cache headroom, if more than {@link Cesium3DTileset#cacheBytes} are needed for the current view + * @property {number} [cacheBytes=536870912] The size (in bytes) to which the tile cache will be trimmed, if the cache contains tiles not needed for the current view. + * @property {number} [cacheHeadroomBytes=536870912] The maximum additional memory (in bytes) to allow for cache headroom, if more than {@link Cesium3DTileset#cacheBytes} are needed for the current view. Must be at least 10 * 1024 * 1024. * @property {boolean} [cullWithChildrenBounds=true] Optimization option. Whether to cull tiles using the union of their children bounding volumes. * @property {boolean} [cullRequestsWhileMoving=true] Optimization option. Don't request tiles that will likely be unused when they come back because of the camera's movement. This optimization only applies to stationary tilesets. * @property {number} [cullRequestsWhileMovingMultiplier=60.0] Optimization option. Multiplier used in culling requests while moving. Larger is more aggressive culling, smaller less aggressive culling. @@ -234,9 +234,9 @@ function Cesium3DTileset(options) { defaultCacheBytes = options.maximumMemoryUsage * 1024 * 1024; } this._cacheBytes = defaultValue(options.cacheBytes, defaultCacheBytes); - this._cacheHeadroomBytes = defaultValue( - options.cacheHeadroomBytes, - defaultCacheBytes + this._cacheHeadroomBytes = Math.max( + 10 * 1024 * 1024, + defaultValue(options.cacheHeadroomBytes, defaultCacheBytes) ); this._styleEngine = new Cesium3DTileStyleEngine(); @@ -1637,6 +1637,9 @@ Object.defineProperties(Cesium3DTileset.prototype, { * until the tiles required to meet the adjusted screen space error use less * than cacheBytes plus cacheHeadroomBytes. *

+ *

+ * Must be at least 10 * 1024 * 1024. + *

* * @memberof Cesium3DTileset.prototype * @@ -1655,7 +1658,7 @@ Object.defineProperties(Cesium3DTileset.prototype, { Check.typeOf.number.greaterThanOrEquals("value", value, 0); //>>includeEnd('debug'); - this._cacheHeadroomBytes = value; + this._cacheHeadroomBytes = Math.max(10 * 1024 * 1024, value); }, }, From 9c3e8105656875cc9a2f408ae93539ef85705786 Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Thu, 25 May 2023 21:22:31 -0400 Subject: [PATCH 09/13] Rename cacheHeadroomBytes to maximumCacheOverflowBytes --- .../engine/Source/Scene/Cesium3DTileset.js | 37 ++++++++++++------- .../createGooglePhotorealistic3DTileset.js | 4 +- 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/packages/engine/Source/Scene/Cesium3DTileset.js b/packages/engine/Source/Scene/Cesium3DTileset.js index e67911fb15ae..043655e51bb3 100644 --- a/packages/engine/Source/Scene/Cesium3DTileset.js +++ b/packages/engine/Source/Scene/Cesium3DTileset.js @@ -70,7 +70,7 @@ import Cesium3DTilesetSkipTraversal from "./Cesium3DTilesetSkipTraversal.js"; * @property {number} [maximumScreenSpaceError=16] The maximum screen space error used to drive level of detail refinement. * @property {number} [maximumMemoryUsage=512] The maximum amount of memory in MB that can be used by the tileset. Deprecated. * @property {number} [cacheBytes=536870912] The size (in bytes) to which the tile cache will be trimmed, if the cache contains tiles not needed for the current view. - * @property {number} [cacheHeadroomBytes=536870912] The maximum additional memory (in bytes) to allow for cache headroom, if more than {@link Cesium3DTileset#cacheBytes} are needed for the current view. Must be at least 10 * 1024 * 1024. + * @property {number} [maximumCacheOverflowBytes=536870912] The maximum additional memory (in bytes) to allow for cache headroom, if more than {@link Cesium3DTileset#cacheBytes} are needed for the current view. Must be at least 10 * 1024 * 1024. * @property {boolean} [cullWithChildrenBounds=true] Optimization option. Whether to cull tiles using the union of their children bounding volumes. * @property {boolean} [cullRequestsWhileMoving=true] Optimization option. Don't request tiles that will likely be unused when they come back because of the camera's movement. This optimization only applies to stationary tilesets. * @property {number} [cullRequestsWhileMovingMultiplier=60.0] Optimization option. Multiplier used in culling requests while moving. Larger is more aggressive culling, smaller less aggressive culling. @@ -234,10 +234,19 @@ function Cesium3DTileset(options) { defaultCacheBytes = options.maximumMemoryUsage * 1024 * 1024; } this._cacheBytes = defaultValue(options.cacheBytes, defaultCacheBytes); - this._cacheHeadroomBytes = Math.max( - 10 * 1024 * 1024, - defaultValue(options.cacheHeadroomBytes, defaultCacheBytes) + + const maximumCacheOverflowBytes = defaultValue( + options.maximumCacheOverflowBytes, + 512 * 1024 * 1024 + ); + //>>includeStart('debug', pragmas.debug); + Check.typeOf.number.greaterThanOrEquals( + "maximumCacheOverflowBytes", + maximumCacheOverflowBytes, + 10 * 1024 * 1024 ); + //>>includeEnd('debug'); + this._maximumCacheOverflowBytes = maximumCacheOverflowBytes; this._styleEngine = new Cesium3DTileStyleEngine(); this._styleApplied = false; @@ -1630,12 +1639,12 @@ Object.defineProperties(Cesium3DTileset.prototype, { /** * The maximum additional amount of GPU memory (in bytes) that will be used to cache tiles. *

- * If tiles sized more than cacheBytes plus cacheHeadroomBytes + * If tiles sized more than cacheBytes plus maximumCacheOverflowBytes * are needed to meet the desired screen space error, determined by * {@link Cesium3DTileset#maximumScreenSpaceError} for the current view, then * {@link Cesium3DTileset#memoryAdjustedScreenSpaceError} will be adjusted * until the tiles required to meet the adjusted screen space error use less - * than cacheBytes plus cacheHeadroomBytes. + * than cacheBytes plus maximumCacheOverflowBytes. *

*

* Must be at least 10 * 1024 * 1024. @@ -1646,26 +1655,26 @@ Object.defineProperties(Cesium3DTileset.prototype, { * @type {number} * @default 536870912 * - * @exception {DeveloperError} cacheHeadroomBytes must be typeof 'number' and greater than or equal to 0 + * @exception {DeveloperError} maximumCacheOverflowBytes must be typeof 'number' and greater than or equal to 0 * @see Cesium3DTileset#totalMemoryUsageInBytes */ - cacheHeadroomBytes: { + maximumCacheOverflowBytes: { get: function () { - return this._cacheHeadroomBytes; + return this._maximumCacheOverflowBytes; }, set: function (value) { //>>includeStart('debug', pragmas.debug); - Check.typeOf.number.greaterThanOrEquals("value", value, 0); + Check.typeOf.number.greaterThanOrEquals("value", value, 10 * 1024 * 1024); //>>includeEnd('debug'); - this._cacheHeadroomBytes = Math.max(10 * 1024 * 1024, value); + this._maximumCacheOverflowBytes = value; }, }, /** * If loading the level of detail required by @{link Cesium3DTileset#maximumScreenSpaceError} * results in the memory usage exceeding @{link Cesium3DTileset#cacheBytes} - * plus @{link Cesium3DTileset#cacheHeadroomBytes}, level of detail refinement + * plus @{link Cesium3DTileset#maximumCacheOverflowBytes}, level of detail refinement * will instead use this (larger) adjusted screen space error to achieve the * best possible visual quality within the available memory * @@ -2798,8 +2807,8 @@ function processTiles(tileset, frameState) { ); tileset._processingQueue = tiles; - const { cacheBytes, cacheHeadroomBytes, statistics } = tileset; - const cacheByteLimit = cacheBytes + cacheHeadroomBytes; + const { cacheBytes, maximumCacheOverflowBytes, statistics } = tileset; + const cacheByteLimit = cacheBytes + maximumCacheOverflowBytes; let memoryExceeded = false; for (let i = 0; i < tiles.length; ++i) { diff --git a/packages/engine/Source/Scene/createGooglePhotorealistic3DTileset.js b/packages/engine/Source/Scene/createGooglePhotorealistic3DTileset.js index 24a5942689bc..b526bfd8a322 100644 --- a/packages/engine/Source/Scene/createGooglePhotorealistic3DTileset.js +++ b/packages/engine/Source/Scene/createGooglePhotorealistic3DTileset.js @@ -39,8 +39,8 @@ async function createGooglePhotorealistic3DTileset(key, options) { options = defaultValue(options, {}); options.showCreditsOnScreen = true; options.cacheBytes = defaultValue(options.cacheBytes, 1536 * 1024 * 1024); - options.cacheHeadroomBytes = defaultValue( - options.cacheHeadroomBytes, + options.maximumCacheOverflowBytes = defaultValue( + options.maximumCacheOverflowBytes, 1024 * 1024 * 1024 ); From 57fe7926a1d630d6042bc56c226907350fcf2519 Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Mon, 5 Jun 2023 19:01:41 -0400 Subject: [PATCH 10/13] First round PR feedback --- CHANGES.md | 12 +++ .../engine/Source/Scene/Cesium3DTileset.js | 56 +++++++++---- .../Scene/Cesium3DTilesetSkipTraversal.js | 5 +- .../Model/PointCloudStylingPipelineStage.js | 2 +- .../engine/Specs/Scene/Cesium3DTilesetSpec.js | 78 +++++++++++++++---- 5 files changed, 117 insertions(+), 36 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 6e8e01a1515c..b7067dfe462f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,17 @@ # Change Log +### 1.107 - 2023-07-01 + +##### Additions :tada: + +- Added `Cesium3DTileset.cacheBytes` and `Cesium3DTileset.maximumCacheOverflowBytes` to better control memory usage. To replicate previous behavior, change `maximumMemoryUsage` to `cacheBytes`, and set `maximumCacheOverflowBytes = Number.MAX_VALUE` + +##### Fixes :wrench: + +##### Deprecated :hourglass_flowing_sand: + +- `Cesium3DTileset.maximumMemoryUsage` has been deprecated in CesiumJS 1.107. It will be removed in 1.110. Use `Cesium3DTileset.cacheBytes` and `Cesium3DTileset.maximumCacheOverflowBytes` instead. [#11310](https://github.com/CesiumGS/cesium/pull/11310) + ### 1.106 - 2023-06-01 #### @cesium/engine diff --git a/packages/engine/Source/Scene/Cesium3DTileset.js b/packages/engine/Source/Scene/Cesium3DTileset.js index 043655e51bb3..408794e70912 100644 --- a/packages/engine/Source/Scene/Cesium3DTileset.js +++ b/packages/engine/Source/Scene/Cesium3DTileset.js @@ -229,7 +229,7 @@ function Cesium3DTileset(options) { if (defined(options.maximumMemoryUsage)) { deprecationWarning( "Cesium3DTileset.maximumMemoryUsage", - "Cesium3DTileset.maximumMemoryUsage was deprecated in CesiumJS 1.106. It will be removed in CesiumJS 1.109. Use Cesium3DTileset.cacheBytes instead." + "Cesium3DTileset.maximumMemoryUsage was deprecated in CesiumJS 1.107. It will be removed in CesiumJS 1.110. Use Cesium3DTileset.cacheBytes instead." ); defaultCacheBytes = options.maximumMemoryUsage * 1024 * 1024; } @@ -1578,14 +1578,14 @@ Object.defineProperties(Cesium3DTileset.prototype, { get: function () { deprecationWarning( "Cesium3DTileset.maximumMemoryUsage", - "Cesium3DTileset.maximumMemoryUsage was deprecated in CesiumJS 1.106. It will be removed in CesiumJS 1.109. Use Cesium3DTileset.cacheBytes instead." + "Cesium3DTileset.maximumMemoryUsage was deprecated in CesiumJS 1.107. It will be removed in CesiumJS 1.110. Use Cesium3DTileset.cacheBytes instead." ); return this._maximumMemoryUsage; }, set: function (value) { deprecationWarning( "Cesium3DTileset.maximumMemoryUsage", - "Cesium3DTileset.maximumMemoryUsage was deprecated in CesiumJS 1.106. It will be removed in CesiumJS 1.109. Use Cesium3DTileset.cacheBytes instead." + "Cesium3DTileset.maximumMemoryUsage was deprecated in CesiumJS 1.107. It will be removed in CesiumJS 1.110. Use Cesium3DTileset.cacheBytes instead." ); //>>includeStart('debug', pragmas.debug); Check.typeOf.number.greaterThanOrEquals("value", value, 0); @@ -1610,9 +1610,11 @@ Object.defineProperties(Cesium3DTileset.prototype, { * If tiles sized more than cacheBytes are needed to meet the * desired screen space error, determined by {@link Cesium3DTileset#maximumScreenSpaceError}, * for the current view, then the memory usage of the tiles loaded will exceed - * cacheBytes. For example, if the maximum is 256 MB, but 300 MB - * of tiles are needed to meet the screen space error, then 300 MB of tiles may - * be loaded. When these tiles go out of view, they will be unloaded. + * cacheBytes by up to maximumCacheOverflowBytes. + * For example, if cacheBytes is 500000, but 600000 bytes + * of tiles are needed to meet the screen space error, then 600000 bytes of tiles + * may be loaded (if maximumCacheOverflowBytes is at least 100000). + * When these tiles go out of view, they will be unloaded. *

* * @memberof Cesium3DTileset.prototype @@ -2793,6 +2795,30 @@ function handleTileFailure(error, tileset, tile) { } } +/** + * @private + * @param {Cesium3DTileset} tileset + */ +function filterProcessingQueue(tileset) { + const tiles = tileset._processingQueue; + + let removeCount = 0; + for (let i = 0; i < tiles.length; ++i) { + const tile = tiles[i]; + if ( + tile.isDestroyed() || + tile._contentState !== Cesium3DTileContentState.PROCESSING + ) { + ++removeCount; + continue; + } + if (removeCount > 0) { + tiles[i - removeCount] = tile; + } + } + tiles.length -= removeCount; +} + /** * Process tiles in the PROCESSING state so they will eventually move to the READY state. * @private @@ -2800,18 +2826,19 @@ function handleTileFailure(error, tileset, tile) { * @param {Cesium3DTile} tile */ function processTiles(tileset, frameState) { - const tiles = tileset._processingQueue.filter( - (tile) => - !tile.isDestroyed() && - tile._contentState === Cesium3DTileContentState.PROCESSING - ); - tileset._processingQueue = tiles; + filterProcessingQueue(tileset); + const tiles = tileset._processingQueue; const { cacheBytes, maximumCacheOverflowBytes, statistics } = tileset; const cacheByteLimit = cacheBytes + maximumCacheOverflowBytes; let memoryExceeded = false; for (let i = 0; i < tiles.length; ++i) { + if (tileset.totalMemoryUsageInBytes > cacheByteLimit) { + memoryExceeded = true; + break; + } + const tile = tiles[i]; try { tile.process(tileset, frameState); @@ -2824,11 +2851,6 @@ function processTiles(tileset, frameState) { --statistics.numberOfTilesProcessing; handleTileFailure(error, tileset, tile); } - - if (tileset.totalMemoryUsageInBytes > cacheByteLimit) { - memoryExceeded = true; - break; - } } if (tileset.totalMemoryUsageInBytes < cacheBytes) { diff --git a/packages/engine/Source/Scene/Cesium3DTilesetSkipTraversal.js b/packages/engine/Source/Scene/Cesium3DTilesetSkipTraversal.js index bbfea15a6788..81e18c41e78c 100644 --- a/packages/engine/Source/Scene/Cesium3DTilesetSkipTraversal.js +++ b/packages/engine/Source/Scene/Cesium3DTilesetSkipTraversal.js @@ -267,7 +267,10 @@ function executeTraversal(root, frameState) { const { tileset } = root; const baseScreenSpaceError = tileset.immediatelyLoadDesiredLevelOfDetail ? Number.MAX_VALUE - : Math.max(tileset.baseScreenSpaceError, tileset.maximumScreenSpaceError); + : Math.max( + tileset.baseScreenSpaceError, + tileset.memoryAdjustedScreenSpaceError + ); const { canTraverse, loadTile, diff --git a/packages/engine/Source/Scene/Model/PointCloudStylingPipelineStage.js b/packages/engine/Source/Scene/Model/PointCloudStylingPipelineStage.js index cb8e8af2cfd6..9abb174126d3 100644 --- a/packages/engine/Source/Scene/Model/PointCloudStylingPipelineStage.js +++ b/packages/engine/Source/Scene/Model/PointCloudStylingPipelineStage.js @@ -161,7 +161,7 @@ PointCloudStylingPipelineStage.process = function ( if (is3DTiles) { defaultPointSize = usesAddRefinement ? 5.0 - : content.tileset.maximumScreenSpaceError; + : content.tileset.memoryAdjustedScreenSpaceError; } vec4.x = defaultValue( pointCloudShading.maximumAttenuation, diff --git a/packages/engine/Specs/Scene/Cesium3DTilesetSpec.js b/packages/engine/Specs/Scene/Cesium3DTilesetSpec.js index d0535302cc31..e7facff644be 100644 --- a/packages/engine/Specs/Scene/Cesium3DTilesetSpec.js +++ b/packages/engine/Specs/Scene/Cesium3DTilesetSpec.js @@ -2493,7 +2493,7 @@ describe( ) { const spyUpdate = jasmine.createSpy("listener"); tileset.tileLoad.addEventListener(spyUpdate); - tileset.maximumMemoryUsage = 0; + tileset.cacheBytes = 0; viewRootOnly(); return Cesium3DTilesTester.waitForTilesLoaded(scene, tileset).then( function () { @@ -2539,7 +2539,7 @@ describe( } ); tileset.tileFailed.addEventListener(spyUpdate); - tileset.maximumMemoryUsage = 0; + tileset.cacheBytes = 0; viewRootOnly(); return Cesium3DTilesTester.waitForTilesLoaded(scene, tileset); }) @@ -3434,11 +3434,11 @@ describe( /////////////////////////////////////////////////////////////////////////// // Cache replacement tests - it("Unload all cached tiles not required to meet SSE using maximumMemoryUsage", function () { + it("Unload all cached tiles not required to meet SSE using cacheBytes", function () { return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function ( tileset ) { - tileset.maximumMemoryUsage = 0; + tileset.cacheBytes = 0; // Render parent and four children (using additive refinement) viewAllTiles(); @@ -3471,11 +3471,11 @@ describe( }); }); - it("Unload some cached tiles not required to meet SSE using maximumMemoryUsage", function () { + it("Unload some cached tiles not required to meet SSE using cacheBytes", function () { return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function ( tileset ) { - tileset.maximumMemoryUsage = 0.025; // Just enough memory to allow 3 tiles to remain + tileset.cacheBytes = 0.025 * 1024 * 1024; // Just enough memory to allow 3 tiles to remain // Render parent and four children (using additive refinement) viewAllTiles(); scene.renderForSpecs(); @@ -3505,11 +3505,41 @@ describe( }); }); - it("Unloads cached tiles outside of the view frustum using maximumMemoryUsage", function () { + it("Restrict tileset memory usage with maximumCacheOverflowBytes", function () { return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function ( tileset ) { - tileset.maximumMemoryUsage = 0; + tileset.cacheBytes = 0.025 * 1024 * 1024; // Just enough memory to allow 3 tiles to remain + tileset._maximumCacheOverflowBytes = 0; + expect(tileset.memoryAdjustedScreenSpaceError).toEqual(16); + + // Zoom out so only root tile is needed to meet SSE. + viewRootOnly(); + scene.renderForSpecs(); + const statistics = tileset._statistics; + expect(statistics.numberOfCommands).toEqual(1); + expect(statistics.numberOfTilesWithContentReady).toEqual(3); + + // Zoom back in and attempt to render all tiles + viewAllTiles(); + + return Cesium3DTilesTester.waitForTilesLoaded(scene, tileset).then( + function () { + // Only 3 tiles should have been actually loaded + expect(statistics.numberOfCommands).toEqual(3); + expect(statistics.numberOfTilesWithContentReady).toEqual(3); // Three loaded tiles + // SSE should have been adjusted higher + expect(tileset.memoryAdjustedScreenSpaceError).toBeGreaterThan(16); + } + ); + }); + }); + + it("Unloads cached tiles outside of the view frustum using cacheBytes", function () { + return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function ( + tileset + ) { + tileset.cacheBytes = 0; scene.renderForSpecs(); const statistics = tileset._statistics; @@ -3535,13 +3565,13 @@ describe( }); }); - it("Unloads cached tiles in a tileset with external tileset JSON file using maximumMemoryUsage", function () { + it("Unloads cached tiles in a tileset with external tileset JSON file using cacheBytes", function () { return Cesium3DTilesTester.loadTileset(scene, tilesetOfTilesetsUrl).then( function (tileset) { const statistics = tileset._statistics; const cacheList = tileset._cache._list; - tileset.maximumMemoryUsage = 0.02; + tileset.cacheBytes = 0.02 * 1024 * 1024; scene.renderForSpecs(); expect(statistics.numberOfCommands).toEqual(5); @@ -3572,12 +3602,12 @@ describe( ); }); - it("Unloads cached tiles in a tileset with empty tiles using maximumMemoryUsage", function () { + it("Unloads cached tiles in a tileset with empty tiles using cacheBytes", function () { return Cesium3DTilesTester.loadTileset(scene, tilesetEmptyRootUrl).then( function (tileset) { const statistics = tileset._statistics; - tileset.maximumMemoryUsage = 0.02; + tileset.cacheBytes = 0.02 * 1024 * 1024; scene.renderForSpecs(); expect(statistics.numberOfCommands).toEqual(4); @@ -3603,7 +3633,7 @@ describe( ); }); - it("Unload cached tiles when a tileset uses replacement refinement using maximumMemoryUsage", function () { + it("Unload cached tiles when a tileset uses replacement refinement using cacheBytes", function () { // No children have content, but all grandchildren have content // // C @@ -3614,7 +3644,7 @@ describe( scene, tilesetReplacement1Url ).then(function (tileset) { - tileset.maximumMemoryUsage = 0; // Only root needs to be visible + tileset.cacheBytes = 0; // Only root needs to be visible // Render parent and four children (using additive refinement) viewAllTiles(); @@ -3648,7 +3678,7 @@ describe( return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function ( tileset ) { - tileset.maximumMemoryUsage = 0.05; + tileset.cacheBytes = 0.05 * 1024 * 1024; // Render parent and four children (using additive refinement) viewAllTiles(); @@ -3678,7 +3708,7 @@ describe( return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function ( tileset ) { - tileset.maximumMemoryUsage = 0; + tileset.cacheBytes = 0; // Render parent and four children (using additive refinement) viewAllTiles(); @@ -3716,6 +3746,20 @@ describe( }).toThrowDeveloperError(); }); + it("cacheBytes throws when negative", async function () { + const tileset = await Cesium3DTileset.fromUrl(tilesetUrl, options); + expect(function () { + tileset.cacheBytes = -1; + }).toThrowDeveloperError(); + }); + + it("maximumCacheOverflowBytes throws if less than 10 MB", async function () { + const tileset = await Cesium3DTileset.fromUrl(tilesetUrl, options); + expect(function () { + tileset.maximumCacheOverflowBytes = 10000000; + }).toThrowDeveloperError(); + }); + it("maximumScreenSpaceError throws when negative", async function () { const tileset = await Cesium3DTileset.fromUrl(tilesetUrl, options); expect(function () { @@ -4856,7 +4900,7 @@ describe( const cartographics = [centerCartographic]; return Cesium3DTilesTester.loadTileset(scene, tilesetUniform).then( function (tileset) { - tileset.maximumMemoryUsage = 0; + tileset.cacheBytes = 0; return sampleHeightMostDetailed(cartographics).then(function () { expect(centerCartographic.height).toEqualEpsilon( 2.47, From 9eff54842a953a2302629fb9288e1a81f5f6c7ae Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Mon, 5 Jun 2023 19:20:29 -0400 Subject: [PATCH 11/13] Update PointCloudStylingPipelineStageSpec --- .../Specs/Scene/Model/PointCloudStylingPipelineStageSpec.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/engine/Specs/Scene/Model/PointCloudStylingPipelineStageSpec.js b/packages/engine/Specs/Scene/Model/PointCloudStylingPipelineStageSpec.js index 38639d9da85e..d2fb924c6212 100644 --- a/packages/engine/Specs/Scene/Model/PointCloudStylingPipelineStageSpec.js +++ b/packages/engine/Specs/Scene/Model/PointCloudStylingPipelineStageSpec.js @@ -764,6 +764,7 @@ describe( }, tileset: { maximumScreenSpaceError: 16, + memoryAdjustedScreenSpaceError: 16, }, }, }); From 4e99c9151179851ad39aaa7d8c3d88795dca493f Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Wed, 7 Jun 2023 11:31:22 -0400 Subject: [PATCH 12/13] Allow maximumCacheOverflowBytes == 0, and warn if too small --- .../engine/Source/Scene/Cesium3DTileset.js | 21 +++++++++++++------ .../engine/Specs/Scene/Cesium3DTilesetSpec.js | 6 +++--- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/packages/engine/Source/Scene/Cesium3DTileset.js b/packages/engine/Source/Scene/Cesium3DTileset.js index 408794e70912..9a81f98a033a 100644 --- a/packages/engine/Source/Scene/Cesium3DTileset.js +++ b/packages/engine/Source/Scene/Cesium3DTileset.js @@ -70,7 +70,7 @@ import Cesium3DTilesetSkipTraversal from "./Cesium3DTilesetSkipTraversal.js"; * @property {number} [maximumScreenSpaceError=16] The maximum screen space error used to drive level of detail refinement. * @property {number} [maximumMemoryUsage=512] The maximum amount of memory in MB that can be used by the tileset. Deprecated. * @property {number} [cacheBytes=536870912] The size (in bytes) to which the tile cache will be trimmed, if the cache contains tiles not needed for the current view. - * @property {number} [maximumCacheOverflowBytes=536870912] The maximum additional memory (in bytes) to allow for cache headroom, if more than {@link Cesium3DTileset#cacheBytes} are needed for the current view. Must be at least 10 * 1024 * 1024. + * @property {number} [maximumCacheOverflowBytes=536870912] The maximum additional memory (in bytes) to allow for cache headroom, if more than {@link Cesium3DTileset#cacheBytes} are needed for the current view. * @property {boolean} [cullWithChildrenBounds=true] Optimization option. Whether to cull tiles using the union of their children bounding volumes. * @property {boolean} [cullRequestsWhileMoving=true] Optimization option. Don't request tiles that will likely be unused when they come back because of the camera's movement. This optimization only applies to stationary tilesets. * @property {number} [cullRequestsWhileMovingMultiplier=60.0] Optimization option. Multiplier used in culling requests while moving. Larger is more aggressive culling, smaller less aggressive culling. @@ -234,6 +234,9 @@ function Cesium3DTileset(options) { defaultCacheBytes = options.maximumMemoryUsage * 1024 * 1024; } this._cacheBytes = defaultValue(options.cacheBytes, defaultCacheBytes); + //>>includeStart('debug', pragmas.debug); + Check.typeOf.number.greaterThanOrEquals("cacheBytes", this._cacheBytes, 0); + //>>includeEnd('debug'); const maximumCacheOverflowBytes = defaultValue( options.maximumCacheOverflowBytes, @@ -243,7 +246,7 @@ function Cesium3DTileset(options) { Check.typeOf.number.greaterThanOrEquals( "maximumCacheOverflowBytes", maximumCacheOverflowBytes, - 10 * 1024 * 1024 + 0 ); //>>includeEnd('debug'); this._maximumCacheOverflowBytes = maximumCacheOverflowBytes; @@ -1648,9 +1651,6 @@ Object.defineProperties(Cesium3DTileset.prototype, { * until the tiles required to meet the adjusted screen space error use less * than cacheBytes plus maximumCacheOverflowBytes. *

- *

- * Must be at least 10 * 1024 * 1024. - *

* * @memberof Cesium3DTileset.prototype * @@ -1666,7 +1666,7 @@ Object.defineProperties(Cesium3DTileset.prototype, { }, set: function (value) { //>>includeStart('debug', pragmas.debug); - Check.typeOf.number.greaterThanOrEquals("value", value, 10 * 1024 * 1024); + Check.typeOf.number.greaterThanOrEquals("value", value, 0); //>>includeEnd('debug'); this._maximumCacheOverflowBytes = value; @@ -2861,6 +2861,15 @@ function processTiles(tileset, frameState) { } function increaseScreenSpaceError(tileset) { + //>>includeStart('debug', pragmas.debug); + oneTimeWarning( + "increase-screenSpaceError", + `The tiles needed to meet maximumScreenSpaceError would use more memory than allocated for this tileset. + The tileset will be rendered with a larger screen space error (see memoryAdjustedScreenSpaceError). + Consider using larger values for cacheBytes and maximumCacheOverflowBytes.` + ); + //>>includeEnd('debug'); + tileset._memoryAdjustedScreenSpaceError *= 1.02; const tiles = tileset._processingQueue; for (let i = 0; i < tiles.length; ++i) { diff --git a/packages/engine/Specs/Scene/Cesium3DTilesetSpec.js b/packages/engine/Specs/Scene/Cesium3DTilesetSpec.js index e7facff644be..e0da5d6a92dd 100644 --- a/packages/engine/Specs/Scene/Cesium3DTilesetSpec.js +++ b/packages/engine/Specs/Scene/Cesium3DTilesetSpec.js @@ -3510,7 +3510,7 @@ describe( tileset ) { tileset.cacheBytes = 0.025 * 1024 * 1024; // Just enough memory to allow 3 tiles to remain - tileset._maximumCacheOverflowBytes = 0; + tileset.maximumCacheOverflowBytes = 0; expect(tileset.memoryAdjustedScreenSpaceError).toEqual(16); // Zoom out so only root tile is needed to meet SSE. @@ -3753,10 +3753,10 @@ describe( }).toThrowDeveloperError(); }); - it("maximumCacheOverflowBytes throws if less than 10 MB", async function () { + it("maximumCacheOverflowBytes throws when negative", async function () { const tileset = await Cesium3DTileset.fromUrl(tilesetUrl, options); expect(function () { - tileset.maximumCacheOverflowBytes = 10000000; + tileset.maximumCacheOverflowBytes = -1; }).toThrowDeveloperError(); }); From f1baff4e30cb9830a2603f8fc61bf5932848d3c2 Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Wed, 7 Jun 2023 14:27:02 -0400 Subject: [PATCH 13/13] Second round PR feedback --- CHANGES.md | 2 +- .../engine/Specs/Scene/Cesium3DTilesetSpec.js | 71 +++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index f817528a246f..97fcd49f305c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,7 +4,7 @@ ##### Additions :tada: -- Added `Cesium3DTileset.cacheBytes` and `Cesium3DTileset.maximumCacheOverflowBytes` to better control memory usage. To replicate previous behavior, change `maximumMemoryUsage` to `cacheBytes`, and set `maximumCacheOverflowBytes = Number.MAX_VALUE` +- Added `Cesium3DTileset.cacheBytes` and `Cesium3DTileset.maximumCacheOverflowBytes` to better control memory usage. To replicate previous behavior, convert `maximumMemoryUsage` from MB to bytes, assign the value to `cacheBytes`, and set `maximumCacheOverflowBytes = Number.MAX_VALUE` ##### Fixes :wrench: diff --git a/packages/engine/Specs/Scene/Cesium3DTilesetSpec.js b/packages/engine/Specs/Scene/Cesium3DTilesetSpec.js index e0da5d6a92dd..ed51505b8d44 100644 --- a/packages/engine/Specs/Scene/Cesium3DTilesetSpec.js +++ b/packages/engine/Specs/Scene/Cesium3DTilesetSpec.js @@ -3434,6 +3434,77 @@ describe( /////////////////////////////////////////////////////////////////////////// // Cache replacement tests + it("Unload all cached tiles not required to meet SSE using maximumMemoryUsage", function () { + return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function ( + tileset + ) { + tileset.maximumMemoryUsage = 0; + + // Render parent and four children (using additive refinement) + viewAllTiles(); + scene.renderForSpecs(); + + const statistics = tileset._statistics; + expect(statistics.numberOfCommands).toEqual(5); + expect(statistics.numberOfTilesWithContentReady).toEqual(5); // Five loaded tiles + expect(tileset.totalMemoryUsageInBytes).toEqual(37200); // Specific to this tileset + + // Zoom out so only root tile is needed to meet SSE. This unloads + // the four children since the maximum memory usage is zero. + viewRootOnly(); + scene.renderForSpecs(); + + expect(statistics.numberOfCommands).toEqual(1); + expect(statistics.numberOfTilesWithContentReady).toEqual(1); + expect(tileset.totalMemoryUsageInBytes).toEqual(7440); // Specific to this tileset + + // Zoom back in so all four children are re-requested. + viewAllTiles(); + + return Cesium3DTilesTester.waitForTilesLoaded(scene, tileset).then( + function () { + expect(statistics.numberOfCommands).toEqual(5); + expect(statistics.numberOfTilesWithContentReady).toEqual(5); // Five loaded tiles + expect(tileset.totalMemoryUsageInBytes).toEqual(37200); // Specific to this tileset + } + ); + }); + }); + + it("Unload some cached tiles not required to meet SSE using maximumMemoryUsage", function () { + return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function ( + tileset + ) { + tileset.maximumMemoryUsage = 0.025; // Just enough memory to allow 3 tiles to remain + // Render parent and four children (using additive refinement) + viewAllTiles(); + scene.renderForSpecs(); + + const statistics = tileset._statistics; + expect(statistics.numberOfCommands).toEqual(5); + expect(statistics.numberOfTilesWithContentReady).toEqual(5); // Five loaded tiles + + // Zoom out so only root tile is needed to meet SSE. This unloads + // two of the four children so three tiles are still loaded (the + // root and two children) since the maximum memory usage is sufficient. + viewRootOnly(); + scene.renderForSpecs(); + + expect(statistics.numberOfCommands).toEqual(1); + expect(statistics.numberOfTilesWithContentReady).toEqual(3); + + // Zoom back in so the two children are re-requested. + viewAllTiles(); + + return Cesium3DTilesTester.waitForTilesLoaded(scene, tileset).then( + function () { + expect(statistics.numberOfCommands).toEqual(5); + expect(statistics.numberOfTilesWithContentReady).toEqual(5); // Five loaded tiles + } + ); + }); + }); + it("Unload all cached tiles not required to meet SSE using cacheBytes", function () { return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function ( tileset