Skip to content
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

Tiles: Memory adjusted screen space error #2851

Merged
merged 4 commits into from
Jan 11, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 44 additions & 2 deletions modules/tiles/src/tileset/tileset-3d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// loaders.gl
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors

Expand Down Expand Up @@ -53,6 +52,7 @@ export type Tileset3DProps = {
loadTiles?: boolean;
basePath?: string;
maximumMemoryUsage?: number;
memoryCacheOverflow?: number;
maximumTilesSelected?: number;
ibgreen marked this conversation as resolved.
Show resolved Hide resolved
debounceTime?: number;

Expand All @@ -66,6 +66,7 @@ export type Tileset3DProps = {

// Traversal
maximumScreenSpaceError?: number;
memoryAdjustedScreenSpaceError?: boolean;
viewportTraversersMap?: any;
ibgreen marked this conversation as resolved.
Show resolved Hide resolved
updateTransforms?: boolean;
viewDistanceScale?: number;
Expand All @@ -87,7 +88,10 @@ type Props = {
throttleRequests: boolean;
/** Number of simultaneous requsts, if throttleRequests is true */
maxRequests: number;
/* Maximum amount of GPU memory (in MB) that may be used to cache tiles. */
maximumMemoryUsage: number;
/* The maximum additional memory (in MB) to allow for cache headroom before adjusting the screen spacer error */
memoryCacheOverflow: number;
/** Maximum number limit of tiles selected for show. 0 means no limit */
maximumTilesSelected: number;
/** Delay time before the tileset traversal. It prevents traversal requests spam.*/
Expand All @@ -102,6 +106,8 @@ type Props = {
onTraversalComplete: (selectedTiles: Tile3D[]) => Tile3D[];
/** The maximum screen space error used to drive level of detail refinement. */
maximumScreenSpaceError: number;
/** Whether to adjust the maximum screen space error to comply with the maximum memory limitation */
memoryAdjustedScreenSpaceError: boolean;
viewportTraversersMap: Record<string, any> | null;
attributions: string[];
loadTiles: boolean;
Expand All @@ -123,6 +129,7 @@ const DEFAULT_PROPS: Props = {
throttleRequests: true,
maxRequests: 64,
maximumMemoryUsage: 32,
memoryCacheOverflow: 1,
maximumTilesSelected: 0,
ibgreen marked this conversation as resolved.
Show resolved Hide resolved
debounceTime: 0,
onTileLoad: () => {},
Expand All @@ -132,6 +139,7 @@ const DEFAULT_PROPS: Props = {
contentLoader: undefined,
viewDistanceScale: 1.0,
maximumScreenSpaceError: 8,
memoryAdjustedScreenSpaceError: false,
loadTiles: true,
updateTransforms: true,
viewportTraversersMap: null,
Expand All @@ -152,6 +160,7 @@ const TILES_UNLOADED = 'Tiles Unloaded';
const TILES_LOAD_FAILED = 'Failed Tile Loads';
const POINTS_COUNT = 'Points/Vertices';
const TILES_GPU_MEMORY = 'Tile Memory Use';
const MAXIMUM_SSE = 'Maximum Screen Space Error';

/**
* The Tileset loading and rendering flow is as below,
Expand Down Expand Up @@ -239,6 +248,17 @@ export class Tileset3D {
/** The total amount of GPU memory in bytes used by the tileset. */
gpuMemoryUsageInBytes: number = 0;

/**
* If loading the level of detail required by maximumScreenSpaceError
* results in the memory usage exceeding maximumMemoryUsage (GPU), level of detail refinement
* will instead use this (larger) adjusted screen space error to achieve the
* best possible visual quality within the available memory.
*/
memoryAdjustedScreenSpaceError: number = 0.0;

private _cacheBytes: number = 0;
private _cacheOverflowBytes: number = 0;

/** Update tracker. increase in each update cycle. */
_frameNumber: number = 0;
private _queryParams: Record<string, string> = {};
Expand Down Expand Up @@ -301,6 +321,10 @@ export class Tileset3D {
maxRequests: this.options.maxRequests
});

this.memoryAdjustedScreenSpaceError = this.options.maximumScreenSpaceError;
this._cacheBytes = this.options.maximumMemoryUsage * 1024 * 1024;
this._cacheOverflowBytes = this.options.memoryCacheOverflow * 1024 * 1024;

// METRICS
// The total amount of GPU memory in bytes used by the tileset.
this.stats = new Stats({id: this.url});
Expand Down Expand Up @@ -407,6 +431,16 @@ export class Tileset3D {
return this.updatePromise;
}

adjustScreenSpaceError(): void {
if (this.gpuMemoryUsageInBytes < this._cacheBytes) {
this.memoryAdjustedScreenSpaceError = Math.max(
this.memoryAdjustedScreenSpaceError / 1.02,
this.options.maximumScreenSpaceError
);
} else if (this.gpuMemoryUsageInBytes > this._cacheBytes + this._cacheOverflowBytes) {
this.memoryAdjustedScreenSpaceError *= 1.02;
}
}
/**
* Update visible tiles relying on a list of viewports
* @param viewports viewports
Expand Down Expand Up @@ -571,6 +605,7 @@ export class Tileset3D {
this.stats.get(TILES_IN_VIEW).count = this.selectedTiles.length;
this.stats.get(TILES_RENDERABLE).count = tilesRenderable;
this.stats.get(POINTS_COUNT).count = pointsRenderable;
this.stats.get(MAXIMUM_SSE).count = this.memoryAdjustedScreenSpaceError;
}

async _initializeTileSet(tilesetJson: TilesetJSON): Promise<void> {
Expand Down Expand Up @@ -668,6 +703,7 @@ export class Tileset3D {
this.stats.get(TILES_LOAD_FAILED);
this.stats.get(POINTS_COUNT);
this.stats.get(TILES_GPU_MEMORY, 'memory');
this.stats.get(MAXIMUM_SSE);
}

// Installs the main tileset JSON file or a tileset JSON file referenced from a tile.
Expand Down Expand Up @@ -836,9 +872,15 @@ export class Tileset3D {
this.stats.get(TILES_LOADED).incrementCount();
this.stats.get(TILES_IN_MEMORY).incrementCount();

// Good enough? Just use the raw binary ArrayBuffer's byte length.
// TODO: Calculate GPU memory usage statistics for a tile.
this.gpuMemoryUsageInBytes += tile.gpuMemoryUsageInBytes || 0;

this.stats.get(TILES_GPU_MEMORY).count = this.gpuMemoryUsageInBytes;

// Adjust SSE based on cache limits
if (this.options.memoryAdjustedScreenSpaceError) {
this.adjustScreenSpaceError();
}
}

_unloadTile(tile) {
Expand Down
4 changes: 1 addition & 3 deletions modules/tiles/src/tileset/tileset-traverser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ export type TilesetTraverserProps = {
loadSiblings?: boolean;
skipLevelOfDetail?: boolean;
updateTransforms?: boolean;
maximumScreenSpaceError?: number;
onTraversalEnd?: (frameState) => any;
viewportTraversersMap?: Record<string, any>;
basePath?: string;
Expand All @@ -20,7 +19,6 @@ export type TilesetTraverserProps = {
export const DEFAULT_PROPS: Required<TilesetTraverserProps> = {
loadSiblings: false,
skipLevelOfDetail: false,
maximumScreenSpaceError: 2,
updateTransforms: true,
onTraversalEnd: () => {},
viewportTraversersMap: {},
Expand Down Expand Up @@ -295,7 +293,7 @@ export class TilesetTraverser {
screenSpaceError = tile.getScreenSpaceError(frameState, true);
}

return screenSpaceError > this.options.maximumScreenSpaceError;
return screenSpaceError > tile.tileset.memoryAdjustedScreenSpaceError;
}

updateTileVisibility(tile: Tile3D, frameState: FrameState): void {
Expand Down
Loading