@itwin/core-frontend
Version:
iTwin.js frontend components
157 lines • 7.27 kB
JavaScript
;
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
* @module Tiles
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.IModelTileRequestChannels = void 0;
const core_bentley_1 = require("@itwin/core-bentley");
const IModelApp_1 = require("../../IModelApp");
const IpcApp_1 = require("../../IpcApp");
const internal_1 = require("../../tile/internal");
/** Handles requests to the cloud storage tile cache, if one is configured. If a tile's content is not found in the cache, subsequent requests for the same tile will
* use the IModelTileChannel instead.
*/
class CloudStorageCacheChannel extends internal_1.TileRequestChannel {
async requestContent(tile) {
(0, core_bentley_1.assert)(tile instanceof internal_1.IModelTile);
return IModelApp_1.IModelApp.tileAdmin.requestCachedTileContent(tile);
}
onNoContent(request) {
(0, core_bentley_1.assert)(request.tile instanceof internal_1.IModelTile);
request.tile.requestChannel = IModelApp_1.IModelApp.tileAdmin.channels.iModelChannels.rpc;
++this._statistics.totalCacheMisses;
return true;
}
}
/** For an [[IpcApp]], allows backend tile generation requests in progress to be canceled. */
class IModelTileChannel extends internal_1.TileRequestChannel {
_canceled = new Map();
onActiveRequestCanceled(request) {
const tree = request.tile.tree;
let entry = this._canceled.get(tree.iModel);
if (!entry)
this._canceled.set(tree.iModel, entry = new Map());
let ids = entry.get(tree.id);
if (!ids)
entry.set(tree.id, ids = new Set());
ids.add(request.tile.contentId);
}
processCancellations() {
for (const [imodel, entries] of this._canceled) {
const treeContentIds = [];
for (const [treeId, tileIds] of entries) {
const contentIds = Array.from(tileIds);
treeContentIds.push({ treeId, contentIds });
this._statistics.totalAbortedRequests += contentIds.length;
}
// eslint-disable-next-line @typescript-eslint/no-floating-promises
IpcApp_1.IpcApp.appFunctionIpc.cancelTileContentRequests(imodel.getRpcProps(), treeContentIds);
}
this._canceled.clear();
}
onIModelClosed(imodel) {
this._canceled.delete(imodel);
}
}
/** If TileAdmin.Props.cacheTileMetadata is true, then this is the first channel through which we request content for an IModelTile.
* It serves a niche purpose: a tile pre-generation agent that wants to ensure that every tile selected during interaction with the application
* has its tile generated and cached in cloud storage. This agent might request thousands of tiles in sequence, causing a given tile to be discarded
* and reloaded many times. To avoid pointlessly reloading tiles whose contents have already been generated, this channel caches the metadata for each tile;
* on subsequent requests for the same tile, it produces the metadata and an empty RenderGraphic.
*/
class IModelTileMetadataCacheChannel extends internal_1.TileRequestChannel {
_cacheByIModel = new Map();
constructor() {
super("itwinjs-imodel-metadata-cache", 100);
}
onNoContent(request) {
(0, core_bentley_1.assert)(request.tile instanceof internal_1.IModelTile);
const channels = IModelApp_1.IModelApp.tileAdmin.channels.iModelChannels;
request.tile.requestChannel = channels.cloudStorage ?? channels.rpc;
return true;
}
async requestContent(tile) {
(0, core_bentley_1.assert)(tile instanceof internal_1.IModelTile);
const content = this.getCachedContent(tile);
return content ? { content } : undefined;
}
getCachedContent(tile) {
const cached = this._cacheByIModel.get(tile.iModel)?.get(tile.tree)?.findEquivalent((x) => (0, core_bentley_1.compareStrings)(x.contentId, tile.contentId));
if (!cached)
return undefined;
const content = {
...cached,
graphic: cached.hasGraphic ? IModelApp_1.IModelApp.renderSystem.createGraphicList([]) : undefined,
contentRange: cached.contentRange?.clone(),
};
return content;
}
onIModelClosed(imodel) {
this._cacheByIModel.delete(imodel);
}
registerChannel(channel) {
channel.contentCallback = (tile, content) => this.cache(tile, content);
}
cache(tile, content) {
(0, core_bentley_1.assert)(tile instanceof internal_1.IModelTile);
let trees = this._cacheByIModel.get(tile.iModel);
if (!trees)
this._cacheByIModel.set(tile.iModel, trees = new Map());
let list = trees.get(tile.tree);
if (!list)
trees.set(tile.tree, list = new core_bentley_1.SortedArray((lhs, rhs) => (0, core_bentley_1.compareStrings)(lhs.contentId, rhs.contentId)));
(0, core_bentley_1.assert)(undefined === list.findEquivalent((x) => (0, core_bentley_1.compareStrings)(x.contentId, tile.contentId)));
list.insert({
contentId: tile.contentId,
hasGraphic: undefined !== content.graphic,
contentRange: content.contentRange?.clone(),
isLeaf: content.isLeaf,
sizeMultiplier: content.sizeMultiplier,
emptySubRangeMask: content.emptySubRangeMask,
});
}
}
/** TileRequestChannels used for requesting content for IModelTiles.
*/
class IModelTileRequestChannels {
_cloudStorage;
_contentCache;
rpc;
constructor(args) {
const channelName = "itwinjs-tile-rpc";
this.rpc = args.usesHttp ? new internal_1.TileRequestChannel(channelName, args.concurrency) : new IModelTileChannel(channelName, args.concurrency);
if (args.cacheMetadata) {
this._contentCache = new IModelTileMetadataCacheChannel();
this._contentCache.registerChannel(this.rpc);
}
this._cloudStorage = new CloudStorageCacheChannel("itwinjs-cloud-cache", args.cacheConcurrency);
this._contentCache?.registerChannel(this._cloudStorage);
}
get cloudStorage() {
return this._cloudStorage;
}
[Symbol.iterator]() {
const channels = [this.rpc];
if (this._cloudStorage)
channels.push(this._cloudStorage);
if (this._contentCache)
channels.push(this._contentCache);
return channels[Symbol.iterator]();
}
setRpcConcurrency(concurrency) {
this.rpc.concurrency = concurrency;
}
getChannelForTile(tile) {
return tile.requestChannel || this._contentCache || this._cloudStorage || this.rpc;
}
/** Strictly for tests. */
getCachedContent(tile) {
return this._contentCache?.getCachedContent(tile);
}
}
exports.IModelTileRequestChannels = IModelTileRequestChannels;
//# sourceMappingURL=IModelTileRequestChannels.js.map