-
Notifications
You must be signed in to change notification settings - Fork 3.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Enforce memory limit for Cesium3DTilesetCache
#11310
Merged
Merged
Changes from 13 commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
0fa7440
Adjust tileset screen space error if out of memory
c93423a
Sort tileset processing queue when hitting memory limit
860dfb3
Add cachedBytes and maximumCachedBytes tileset options
92d7493
Fix failing tests and types
703a39f
Use cacheHeadroomBytes to specify memory limit for tileset cache
a8ddd97
Tweak memory-adjusted screen space error functions
4e8006e
Use larger cache size in createGooglePhotorealistic3DTileset
558fcdb
Require cacheHeadroomBytes to be at least 10 MB
9c3e810
Rename cacheHeadroomBytes to maximumCacheOverflowBytes
57fe792
First round PR feedback
4087cd4
Merge branch 'main' into tileset-cache
9eff548
Update PointCloudStylingPipelineStageSpec
4e99c91
Allow maximumCacheOverflowBytes == 0, and warn if too small
f1baff4
Second round PR feedback
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Reminder to add some unit tests |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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} [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. | ||
* @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,7 +223,33 @@ function Cesium3DTileset(options) { | |
options.maximumScreenSpaceError, | ||
16 | ||
); | ||
this._maximumMemoryUsage = defaultValue(options.maximumMemoryUsage, 512); | ||
this._memoryAdjustedScreenSpaceError = this._maximumScreenSpaceError; | ||
|
||
let defaultCacheBytes = 512 * 1024 * 1024; | ||
if (defined(options.maximumMemoryUsage)) { | ||
deprecationWarning( | ||
"Cesium3DTileset.maximumMemoryUsage", | ||
"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; | ||
} | ||
this._cacheBytes = defaultValue(options.cacheBytes, defaultCacheBytes); | ||
ggetz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
//>>includeStart('debug', pragmas.debug); | ||
Check.typeOf.number.greaterThanOrEquals("cacheBytes", this._cacheBytes, 0); | ||
//>>includeEnd('debug'); | ||
|
||
const maximumCacheOverflowBytes = defaultValue( | ||
options.maximumCacheOverflowBytes, | ||
512 * 1024 * 1024 | ||
); | ||
//>>includeStart('debug', pragmas.debug); | ||
Check.typeOf.number.greaterThanOrEquals( | ||
"maximumCacheOverflowBytes", | ||
maximumCacheOverflowBytes, | ||
0 | ||
); | ||
//>>includeEnd('debug'); | ||
this._maximumCacheOverflowBytes = maximumCacheOverflowBytes; | ||
|
||
this._styleEngine = new Cesium3DTileStyleEngine(); | ||
this._styleApplied = false; | ||
|
@@ -590,7 +618,7 @@ function Cesium3DTileset(options) { | |
* console.log('A tile was unloaded from the cache.'); | ||
* }); | ||
* | ||
* @see Cesium3DTileset#maximumMemoryUsage | ||
* @see Cesium3DTileset#cacheBytes | ||
* @see Cesium3DTileset#trimLoadedTiles | ||
*/ | ||
this.tileUnload = new Event(); | ||
|
@@ -1516,6 +1544,7 @@ Object.defineProperties(Cesium3DTileset.prototype, { | |
//>>includeEnd('debug'); | ||
|
||
this._maximumScreenSpaceError = value; | ||
this._memoryAdjustedScreenSpaceError = value; | ||
}, | ||
}, | ||
|
||
|
@@ -1545,17 +1574,122 @@ Object.defineProperties(Cesium3DTileset.prototype, { | |
* | ||
* @exception {DeveloperError} <code>maximumMemoryUsage</code> must be greater than or equal to zero. | ||
* @see Cesium3DTileset#totalMemoryUsageInBytes | ||
* | ||
* @deprecated | ||
*/ | ||
maximumMemoryUsage: { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Several tests should be updated to use |
||
get: function () { | ||
deprecationWarning( | ||
"Cesium3DTileset.maximumMemoryUsage", | ||
"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.107. It will be removed in CesiumJS 1.110. Use Cesium3DTileset.cacheBytes instead." | ||
); | ||
//>>includeStart('debug', pragmas.debug); | ||
Check.typeOf.number.greaterThanOrEquals("value", value, 0); | ||
//>>includeEnd('debug'); | ||
|
||
this._maximumMemoryUsage = value; | ||
this._cacheBytes = value * 1024 * 1024; | ||
}, | ||
}, | ||
|
||
/** | ||
* 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. | ||
* <p> | ||
* Tiles not in view are unloaded to enforce this. | ||
* </p> | ||
* <p> | ||
* If decreasing this value results in unloading tiles, the tiles are unloaded the next frame. | ||
* </p> | ||
* <p> | ||
* If tiles sized more than <code>cacheBytes</code> 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 | ||
* <code>cacheBytes</code> by up to <code>maximumCacheOverflowBytes</code>. | ||
* For example, if <code>cacheBytes</code> is 500000, but 600000 bytes | ||
* of tiles are needed to meet the screen space error, then 600000 bytes of tiles | ||
* may be loaded (if <code>maximumCacheOverflowBytes</code> is at least 100000). | ||
* When these tiles go out of view, they will be unloaded. | ||
* </p> | ||
* | ||
* @memberof Cesium3DTileset.prototype | ||
* | ||
* @type {number} | ||
* @default 536870912 | ||
* | ||
* @exception {DeveloperError} <code>cacheBytes</code> must be typeof 'number' and greater than or equal to 0 | ||
* @see Cesium3DTileset#totalMemoryUsageInBytes | ||
*/ | ||
cacheBytes: { | ||
get: function () { | ||
return this._cacheBytes; | ||
}, | ||
set: function (value) { | ||
//>>includeStart('debug', pragmas.debug); | ||
Check.typeOf.number.greaterThanOrEquals("value", value, 0); | ||
//>>includeEnd('debug'); | ||
|
||
this._cacheBytes = value; | ||
}, | ||
}, | ||
|
||
/** | ||
* The maximum additional amount of GPU memory (in bytes) that will be used to cache tiles. | ||
* <p> | ||
* If tiles sized more than <code>cacheBytes</code> plus <code>maximumCacheOverflowBytes</code> | ||
* 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 <code>cacheBytes</code> plus <code>maximumCacheOverflowBytes</code>. | ||
* </p> | ||
* | ||
* @memberof Cesium3DTileset.prototype | ||
* | ||
* @type {number} | ||
* @default 536870912 | ||
* | ||
* @exception {DeveloperError} <code>maximumCacheOverflowBytes</code> must be typeof 'number' and greater than or equal to 0 | ||
* @see Cesium3DTileset#totalMemoryUsageInBytes | ||
*/ | ||
maximumCacheOverflowBytes: { | ||
get: function () { | ||
return this._maximumCacheOverflowBytes; | ||
}, | ||
set: function (value) { | ||
//>>includeStart('debug', pragmas.debug); | ||
Check.typeOf.number.greaterThanOrEquals("value", value, 0); | ||
//>>includeEnd('debug'); | ||
|
||
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#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 | ||
* | ||
* @memberof Cesium3DTileset.prototype | ||
* | ||
* @type {number} | ||
* @readonly | ||
* | ||
* @private | ||
*/ | ||
memoryAdjustedScreenSpaceError: { | ||
get: function () { | ||
return this._memoryAdjustedScreenSpaceError; | ||
}, | ||
}, | ||
|
||
|
@@ -1665,7 +1799,7 @@ Object.defineProperties(Cesium3DTileset.prototype, { | |
* @type {number} | ||
* @readonly | ||
* | ||
* @see Cesium3DTileset#maximumMemoryUsage | ||
* @see Cesium3DTileset#cacheBytes | ||
*/ | ||
totalMemoryUsageInBytes: { | ||
get: function () { | ||
|
@@ -2524,7 +2658,7 @@ function requestContent(tileset, tile) { | |
tileset._requestedTilesInFlight.push(tile); | ||
} | ||
|
||
function sortRequestByPriority(a, b) { | ||
function sortTilesByPriority(a, b) { | ||
return a._priority - b._priority; | ||
} | ||
|
||
|
@@ -2627,7 +2761,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]); | ||
} | ||
|
@@ -2694,10 +2828,18 @@ function filterProcessingQueue(tileset) { | |
function processTiles(tileset, frameState) { | ||
filterProcessingQueue(tileset); | ||
const tiles = tileset._processingQueue; | ||
const statistics = tileset._statistics; | ||
let tile; | ||
|
||
const { cacheBytes, maximumCacheOverflowBytes, statistics } = tileset; | ||
const cacheByteLimit = cacheBytes + maximumCacheOverflowBytes; | ||
|
||
let memoryExceeded = false; | ||
for (let i = 0; i < tiles.length; ++i) { | ||
tile = tiles[i]; | ||
if (tileset.totalMemoryUsageInBytes > cacheByteLimit) { | ||
memoryExceeded = true; | ||
break; | ||
} | ||
|
||
const tile = tiles[i]; | ||
try { | ||
tile.process(tileset, frameState); | ||
|
||
|
@@ -2710,6 +2852,37 @@ function processTiles(tileset, frameState) { | |
handleTileFailure(error, tileset, tile); | ||
} | ||
} | ||
|
||
if (tileset.totalMemoryUsageInBytes < cacheBytes) { | ||
decreaseScreenSpaceError(tileset); | ||
} else if (memoryExceeded && tiles.length > 0) { | ||
increaseScreenSpaceError(tileset); | ||
} | ||
} | ||
|
||
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) { | ||
tiles[i].updatePriority(); | ||
} | ||
tiles.sort(sortTilesByPriority); | ||
} | ||
|
||
function decreaseScreenSpaceError(tileset) { | ||
tileset._memoryAdjustedScreenSpaceError = Math.max( | ||
tileset.memoryAdjustedScreenSpaceError / 1.02, | ||
tileset.maximumScreenSpaceError | ||
); | ||
} | ||
|
||
const scratchCartesian = new Cartesian3(); | ||
|
@@ -3057,7 +3230,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#cacheBytes}. | ||
* <p> | ||
* Tile unloads occur at the next frame to keep all the WebGL delete calls | ||
* within the render loop. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And convert MB to bytes, correct?