@itwin/frontend-devtools
Version:
Debug menu and supporting UI widgets
255 lines • 9.5 kB
JavaScript
"use strict";
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.MemoryTracker = void 0;
exports.formatMemory = formatMemory;
/** @packageDocumentation
* @module Widgets
*/
const core_bentley_1 = require("@itwin/core-bentley");
const core_frontend_1 = require("@itwin/core-frontend");
const ComboBox_1 = require("../ui/ComboBox");
function collectTileTreeMemory(stats, owner) {
const tree = owner.tileTree;
if (undefined !== tree)
tree.collectStatistics(stats);
}
// Includes None (-1) at index 0
const memLabels = [
"None",
"Viewed Tile Trees",
"Selected Tiles",
"All Tile Trees",
"Render Target",
"Viewport",
"System",
"All",
];
function collectStatisticsForViewedTileTrees(vp, stats) {
vp.collectStatistics(stats);
const trees = new core_frontend_1.DisclosedTileTreeSet();
vp.discloseTileTrees(trees);
return trees.size;
}
function collectStatisticsForSelectedTiles(vp, stats) {
const trees = new Set();
const selectedTiles = core_frontend_1.IModelApp.tileAdmin.getTilesForUser(vp)?.selected;
if (selectedTiles) {
for (const tile of selectedTiles) {
trees.add(tile.tree);
tile.collectStatistics(stats);
}
}
return trees.size;
}
function collectStatisticsForAllTileTrees(vp, stats) {
let numTrees = 0;
vp.view.iModel.tiles.forEachTreeOwner((owner) => {
collectTileTreeMemory(stats, owner);
if (undefined !== owner.tileTree)
++numTrees;
});
return numTrees;
}
const calcMem = [
(stats, vp) => collectStatisticsForViewedTileTrees(vp, stats),
(stats, vp) => collectStatisticsForSelectedTiles(vp, stats),
(stats, vp) => collectStatisticsForAllTileTrees(vp, stats),
(stats, vp) => {
vp.target.collectStatistics(stats);
return 0;
},
(stats, vp) => {
vp.target.collectStatistics(stats);
return collectStatisticsForViewedTileTrees(vp, stats);
},
(stats, vp) => {
vp.target.renderSystem.collectStatistics(stats);
return 0;
},
(stats, vp) => {
vp.target.renderSystem.collectStatistics(stats);
for (const x of core_frontend_1.IModelApp.viewManager)
x.target.collectStatistics(stats);
return collectStatisticsForAllTileTrees(vp, stats);
},
];
// ###TODO...
const purgeMem = [
undefined,
(olderThan) => core_frontend_1.IModelApp.viewManager.purgeTileTrees(olderThan ? olderThan : core_bentley_1.BeTimePoint.now()),
];
/** @internal */
function formatMemory(numBytes) {
let suffix = "b";
if (numBytes >= 1024) {
numBytes /= 1024;
suffix = "kb";
if (numBytes >= 1024) {
numBytes /= 1024;
suffix = "mb";
}
}
return numBytes.toFixed(2) + suffix;
}
class MemoryPanel {
_label;
_labels;
_elems = [];
_div;
_header;
constructor(parent, label, labels) {
this._label = label;
this._labels = labels;
this._div = document.createElement("div");
this._header = document.createElement("label");
this._header.style.fontWeight = "bold";
this._div.appendChild(this._header);
const numElems = labels.length;
for (let i = 0; i < numElems; i++) {
const elem = document.createElement("label");
this._elems.push(elem);
this._div.appendChild(elem);
}
parent.appendChild(this._div);
}
update(stats, total) {
(0, core_bentley_1.assert)(this._labels.length === stats.length);
(0, core_bentley_1.assert)(this._labels.length === this._elems.length);
this._header.innerHTML = `${this._label}: ${formatMemory(total)}`;
for (let i = 0; i < this._labels.length; i++) {
const elem = this._elems[i];
const stat = stats[i];
if (0 === stat.totalBytes) {
elem.style.display = "none";
continue;
}
elem.style.display = "block";
elem.innerHTML = `${this._labels[i]} (${stat.count}): ${formatMemory(stat.totalBytes)}`; // + "\n(max: " + formatMemory(stat.maxBytes) + ")";
}
}
}
/** Displays GPU memory allocated to tile trees - either all tile trees in the system, or only those associated with a specific Viewport.
* @beta
*/
class MemoryTracker {
_stats = new core_frontend_1.RenderMemory.Statistics();
_vp;
_div;
_curIntervalId;
_memIndex = -1 /* MemIndex.None */;
_totalElem;
_totalTreesElem;
_purgeButton;
_textures;
_buffers;
constructor(parent, vp) {
this._vp = vp;
this._div = document.createElement("div");
this._div.style.display = "none";
this._div.style.textAlign = "right";
this.addSelector(parent);
const table = document.createElement("table");
table.style.width = "100%";
table.setAttribute("border", "1");
this._div.appendChild(table);
const row0 = document.createElement("tr");
const cell00 = document.createElement("td");
const cell01 = document.createElement("td");
cell00.style.width = cell01.style.width = "50%";
row0.appendChild(cell00);
row0.appendChild(cell01);
table.appendChild(row0);
const row1 = document.createElement("tr");
const cell10 = document.createElement("td");
const cell11 = document.createElement("td");
cell10.style.width = cell11.style.width = "50%";
row1.appendChild(cell10);
row1.appendChild(cell11);
table.appendChild(row1);
this._textures = new MemoryPanel(cell00, "Textures", ["Surface Textures", "Vertex Tables", "Edge Tables", "Feature Tables", "Feature Overrides", "Clip Volumes", "Planar Classifiers", "Shadow Maps", "Texture Attachments", "Thematic Textures"]);
this._buffers = new MemoryPanel(cell01, "Buffers", ["Surfaces", "Visible Edges", "Silhouettes", "Polyline Edges", "Indexed Edges", "Polylines", "Point Strings", "Point Clouds", "Instances", "Terrain", "Reality Mesh"]);
this._totalElem = this.addStatistics(cell10);
this._totalTreesElem = this.addStatistics(cell11);
this._purgeButton = this.addPurgeButton(this._div);
parent.appendChild(this._div);
}
[Symbol.dispose]() {
this.clearInterval();
}
addSelector(parent) {
const entries = [];
for (let i = -1 /* MemIndex.None */; i < 7 /* MemIndex.COUNT */; i++)
entries.push({ name: memLabels[i + 1], value: i });
(0, ComboBox_1.createComboBox)({
parent,
name: "Track Memory: ",
id: "memTracker_type",
value: -1 /* MemIndex.None */,
handler: (select) => this.change(Number.parseInt(select.value, 10)),
entries,
});
}
addPurgeButton(parent) {
const div = document.createElement("div");
div.style.textAlign = "center";
const button = document.createElement("button");
button.innerText = "Purge";
button.addEventListener("click", () => this.purge());
div.appendChild(button);
parent.appendChild(div);
return button;
}
addStatistics(parent) {
const div = document.createElement("div");
const text = document.createElement("text");
text.style.fontWeight = "bold";
div.appendChild(text);
parent.appendChild(div);
return text;
}
clearInterval() {
if (undefined !== this._curIntervalId) {
window.clearInterval(this._curIntervalId);
this._curIntervalId = undefined;
}
}
change(newIndex) {
if (newIndex === this._memIndex)
return;
this._memIndex = newIndex;
if (-1 /* MemIndex.None */ === newIndex) {
this.clearInterval();
this._div.style.display = "none";
return;
}
if (undefined === this._curIntervalId) {
this._curIntervalId = window.setInterval(() => this.update(), 1000);
this._div.style.display = "block";
}
this._purgeButton.disabled = undefined === purgeMem[this._memIndex];
this.update();
}
update() {
const calc = calcMem[this._memIndex];
this._stats.clear();
const numTrees = calc(this._stats, this._vp);
this._totalElem.innerText = `Total: ${formatMemory(this._stats.totalBytes)}`;
this._totalTreesElem.innerText = `Total Tile Trees: ${numTrees}`;
this._textures.update(this._stats.consumers, this._stats.totalBytes - this._stats.buffers.totalBytes);
this._buffers.update(this._stats.buffers.consumers, this._stats.buffers.totalBytes);
}
purge() {
const purge = purgeMem[this._memIndex];
if (undefined !== purge) {
purge();
this._vp.invalidateScene(); // to trigger reloading of tiles we actually do want to continue drawing
this.update();
}
}
}
exports.MemoryTracker = MemoryTracker;
//# sourceMappingURL=MemoryTracker.js.map