Skip to content

Commit

Permalink
Merge pull request #11310 from CesiumGS/tileset-cache
Browse files Browse the repository at this point in the history
Enforce memory limit for `Cesium3DTilesetCache`
  • Loading branch information
ggetz authored Jun 7, 2023
2 parents 26adf79 + f1baff4 commit 5de34c2
Show file tree
Hide file tree
Showing 11 changed files with 343 additions and 37 deletions.
12 changes: 12 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -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, convert `maximumMemoryUsage` from MB to bytes, assign the value 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.1 - 2023-06-02

This is an npm-only release to fix a dependency issue published in 1.106
Expand Down
9 changes: 4 additions & 5 deletions packages/engine/Source/Scene/Cesium3DTile.js
Original file line number Diff line number Diff line change
Expand Up @@ -870,15 +870,15 @@ function isPriorityDeferred(tile, frameState) {
);
const sseRelaxation = tileset.foveatedInterpolationCallback(
tileset.foveatedMinimumScreenSpaceErrorRelaxation,
tileset.maximumScreenSpaceError,
tileset.memoryAdjustedScreenSpaceError,
normalizedFoveatedFactor
);
const sse =
tile._screenSpaceError === 0.0 && defined(tile.parent)
? tile.parent._screenSpaceError * 0.5
: tile._screenSpaceError;

return tileset.maximumScreenSpaceError - sseRelaxation <= sse;
return tileset.memoryAdjustedScreenSpaceError - sseRelaxation <= sse;
}

const scratchJulianDate = new JulianDate();
Expand Down Expand Up @@ -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 =
Expand Down
193 changes: 183 additions & 10 deletions packages/engine/Source/Scene/Cesium3DTileset.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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);
//>>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;
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -1516,6 +1544,7 @@ Object.defineProperties(Cesium3DTileset.prototype, {
//>>includeEnd('debug');

this._maximumScreenSpaceError = value;
this._memoryAdjustedScreenSpaceError = value;
},
},

Expand Down Expand Up @@ -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: {
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;
},
},

Expand Down Expand Up @@ -1665,7 +1799,7 @@ Object.defineProperties(Cesium3DTileset.prototype, {
* @type {number}
* @readonly
*
* @see Cesium3DTileset#maximumMemoryUsage
* @see Cesium3DTileset#cacheBytes
*/
totalMemoryUsageInBytes: {
get: function () {
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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]);
}
Expand Down Expand Up @@ -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);

Expand All @@ -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();
Expand Down Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ Cesium3DTilesetBaseTraversal.selectTiles = function (tileset, frameState) {

if (
root.getScreenSpaceError(frameState, true) <=
tileset._maximumScreenSpaceError
tileset.memoryAdjustedScreenSpaceError
) {
return;
}
Expand Down
4 changes: 1 addition & 3 deletions packages/engine/Source/Scene/Cesium3DTilesetCache.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
//
Expand All @@ -68,7 +66,7 @@ Cesium3DTilesetCache.prototype.unloadTiles = function (
let node = list.head;
while (
node !== sentinel &&
(tileset.totalMemoryUsageInBytes > maximumMemoryUsageInBytes || trimTiles)
(tileset.totalMemoryUsageInBytes > tileset.cacheBytes || trimTiles)
) {
const tile = node.item;
node = node.next;
Expand Down
7 changes: 5 additions & 2 deletions packages/engine/Source/Scene/Cesium3DTilesetSkipTraversal.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ Cesium3DTilesetSkipTraversal.selectTiles = function (tileset, frameState) {

if (
root.getScreenSpaceError(frameState, true) <=
tileset._maximumScreenSpaceError
tileset.memoryAdjustedScreenSpaceError
) {
return;
}
Expand Down Expand Up @@ -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,
Expand Down
4 changes: 2 additions & 2 deletions packages/engine/Source/Scene/Cesium3DTilesetTraversal.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};

/**
Expand Down Expand Up @@ -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
);
}

Expand Down
Loading

0 comments on commit 5de34c2

Please sign in to comment.