Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
10 changes: 1 addition & 9 deletions examples/src/examples/gaussian-splatting/flipbook.example.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -76,16 +76,8 @@ assetListLoader.load(() => {
roomEntity.setLocalScale(30, 30, 30);
app.root.addChild(roomEntity);

// Mini-Stats: add VRAM on top of default stats
// Mini-Stats with default options
const msOptions = pc.MiniStats.getDefaultOptions();
msOptions.stats.push({
name: 'VRAM',
stats: ['vram.tex'],
decimalPlaces: 1,
multiplier: 1 / (1024 * 1024),
unitsName: 'MB',
watermark: 1024
});
const miniStats = new pc.MiniStats(app, msOptions); // eslint-disable-line no-unused-vars

// Create an Entity with a camera component
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,16 +119,8 @@ assetListLoader.load(() => {
app.scene.skyboxMip = 1;
app.scene.exposure = 1.5;

// Mini-Stats: add VRAM and gsplats on top of default stats
// Mini-Stats: add gsplats on top of default stats
const msOptions = pc.MiniStats.getDefaultOptions();
msOptions.stats.push({
name: 'VRAM',
stats: ['vram.tex'],
decimalPlaces: 1,
multiplier: 1 / (1024 * 1024),
unitsName: 'MB',
watermark: 1024
});
msOptions.stats.push({
name: 'GSplats',
stats: ['frame.gsplats'],
Expand Down
4 changes: 2 additions & 2 deletions examples/src/examples/misc/mini-stats.example.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,10 @@ options.stats = [
unitsName: 'ms'
},

// used VRAM, displayed using 2 colors - red for textures, green for geometry
// used VRAM in MB
{
name: 'VRAM',
stats: ['vram.tex', 'vram.geom'],
stats: ['vram.totalUsed'],
decimalPlaces: 1,
multiplier: 1 / (1024 * 1024),
unitsName: 'MB',
Expand Down
13 changes: 5 additions & 8 deletions src/extras/mini-stats/graph.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class Graph {
update(ms) {
const timings = this.timer.timings;

// calculate stacked total
// calculate total
const total = timings.reduce((a, v) => a + v, 0);

// update averages and max
Expand All @@ -63,14 +63,11 @@ class Graph {
}

if (this.enabled) {
// update timings
let value = 0;
// update total timing sample
const range = 1.5 * this.watermark;
for (let i = 0; i < timings.length; ++i) {
// scale the value into the range
value += Math.floor(timings[i] / range * 255);
this.sample[i] = value;
}
this.sample[0] = Math.floor(total / range * 255);
this.sample[1] = 0;
this.sample[2] = 0;

// .a store watermark
this.sample[3] = this.watermark / range * 255;
Expand Down
91 changes: 68 additions & 23 deletions src/extras/mini-stats/mini-stats.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ const delayedStartStats = new Set([
* graphs. Defaults to 1.
* @property {number} [cpuTimingMinSize] - Minimum size index at which to show CPU sub-timing
* graphs (script, anim, physics, render). Defaults to 1.
* @property {number} [vramTimingMinSize] - Minimum size index at which to show VRAM subcategory
* graphs. Defaults to 1.
*/

/**
Expand Down Expand Up @@ -118,10 +120,11 @@ class MiniStats {
this.wordAtlas = new WordAtlas(device, words);
this._activeSizeIndex = options.startSizeIndex;

// if GPU pass tracking or CPU timing is enabled, use the last width for medium/large sizes
// if GPU pass tracking, CPU timing or VRAM detail is enabled, use the last width for medium/large sizes
const gpuTimingMinSize = options.gpuTimingMinSize ?? 1;
const cpuTimingMinSize = options.cpuTimingMinSize ?? 1;
if (gpuTimingMinSize < this.sizes.length || cpuTimingMinSize < this.sizes.length) {
const vramTimingMinSize = options.vramTimingMinSize ?? 1;
if (gpuTimingMinSize < this.sizes.length || cpuTimingMinSize < this.sizes.length || vramTimingMinSize < this.sizes.length) {
const lastWidth = this.sizes[this.sizes.length - 1].width;
for (let i = 1; i < this.sizes.length - 1; i++) {
this.sizes[i].width = lastWidth;
Expand Down Expand Up @@ -177,6 +180,10 @@ class MiniStats {
this.cpuTimingMinSize = cpuTimingMinSize;
this.cpuGraphs = new Map(); // Map<statName, { graph, lastNonZeroFrame }>

// VRAM subcategory tracking
this.vramTimingMinSize = vramTimingMinSize;
this.vramGraphs = new Map(); // Map<statName, { graph, lastNonZeroFrame }>

this.frameIndex = 0;
this.textRefreshRate = options.textRefreshRate;

Expand All @@ -197,6 +204,8 @@ class MiniStats {

this.graphs.forEach(graph => graph.destroy());
this.gpuPassGraphs.clear();
this.cpuGraphs.clear();
this.vramGraphs.clear();
this.wordAtlas.destroy();
this.texture.destroy();
this.div.remove();
Expand All @@ -210,6 +219,7 @@ class MiniStats {
* - GPU utilization
* - Overall frame time
* - Draw call count
* - Total VRAM usage
*
* @returns {object} The default options for MiniStats.
* @example
Expand Down Expand Up @@ -267,14 +277,27 @@ class MiniStats {
name: 'DrawCalls',
stats: ['drawCalls.total'],
watermark: 1000
},

// used VRAM in MB
{
name: 'VRAM',
stats: ['vram.totalUsed'],
decimalPlaces: 1,
multiplier: 1 / (1024 * 1024),
unitsName: 'MB',
watermark: 1024
}
],

// minimum size index to show GPU pass timing graphs
gpuTimingMinSize: 1,

// minimum size index to show CPU sub-timing graphs
cpuTimingMinSize: 1
cpuTimingMinSize: 1,

// minimum size index to show VRAM subcategory graphs
vramTimingMinSize: 1
};
}

Expand Down Expand Up @@ -306,9 +329,9 @@ class MiniStats {
}
this.gpuPassGraphs.clear();

// reset main GPU graph to default background color
// keep main GPU graph in GPU color group
const gpuGraph = this.graphs.find(g => g.name === 'GPU');
if (gpuGraph) gpuGraph.graphType = 0.0;
if (gpuGraph) gpuGraph.graphType = 0.33;
}

// delete CPU sub-timing graphs when switching below threshold
Expand All @@ -323,9 +346,9 @@ class MiniStats {
}
this.cpuGraphs.clear();

// reset main CPU graph to default background color
// keep main CPU graph in CPU color group
const cpuGraph = this.graphs.find(g => g.name === 'CPU');
if (cpuGraph) cpuGraph.graphType = 0.0;
if (cpuGraph) cpuGraph.graphType = 0.66;
}
}

Expand Down Expand Up @@ -407,20 +430,37 @@ class MiniStats {
initGraphs(app, device, options) {
this.graphs = [];

// Add VRAM first so it appears at the bottom in the compact stacked view.
// Graphs are rendered bottom-to-top.
if (options.stats) {
options.stats.forEach((entry) => {
if (entry.name === 'VRAM') {
const timer = new StatsTimer(app, entry.stats, entry.decimalPlaces, entry.unitsName, entry.multiplier);
const graph = new Graph(entry.name, app, entry.watermark, options.textRefreshRate, timer);
this.graphs.push(graph);
}
});
}

if (options.cpu.enabled) {
const timer = new CpuTimer(app);
const graph = new Graph('CPU', app, options.cpu.watermark, options.textRefreshRate, timer);
graph.graphType = 0.66;
this.graphs.push(graph);
}

if (options.gpu.enabled) {
const timer = new GpuTimer(device);
const graph = new Graph('GPU', app, options.gpu.watermark, options.textRefreshRate, timer);
graph.graphType = 0.33;
this.graphs.push(graph);
}

if (options.stats) {
options.stats.forEach((entry) => {
if (entry.name === 'VRAM') {
return;
}
const timer = new StatsTimer(app, entry.stats, entry.decimalPlaces, entry.unitsName, entry.multiplier);
const graph = new Graph(entry.name, app, entry.watermark, options.textRefreshRate, timer);
this.graphs.push(graph);
Expand Down Expand Up @@ -590,6 +630,7 @@ class MiniStats {

// scan for new sub-stats
const statsEntries = (stats instanceof Map) ? stats : Object.entries(stats);
const mainGraph = this.graphs.find(g => g.name === mainGraphName);
for (const [statName, timing] of statsEntries) {
if (!subGraphs.has(statName)) {
// Skip creating graph for auto-hide stats with zero timing
Expand All @@ -607,11 +648,15 @@ class MiniStats {
}
const graphName = ` ${displayName}`; // indent with 2 spaces

// initial watermark (will be synced to main graph)
const watermark = 10.0;
// use main graph watermark when available
const watermark = mainGraph?.watermark ?? 10.0;

const decimalPlaces = 1;
const unitsName = statPathPrefix === 'vram' ? 'MB' : 'ms';
const multiplier = statPathPrefix === 'vram' ? 1 / (1024 * 1024) : 1;

const statPath = `${statPathPrefix}.${statName}`;
const timer = new StatsTimer(this.app, [statPath], 1, 'ms', 1);
const timer = new StatsTimer(this.app, [statPath], decimalPlaces, unitsName, multiplier);
const graph = new Graph(graphName, this.app, watermark, this.textRefreshRate, timer);

// Set graph type for background tinting
Expand Down Expand Up @@ -656,23 +701,10 @@ class MiniStats {
}

// sync all sub-stat watermarks to match main graph
const mainGraph = this.graphs.find(g => g.name === mainGraphName);
if (mainGraph) {
for (const statData of subGraphs.values()) {
statData.graph.watermark = mainGraph.watermark;
}

// set main graph background color to match sub-graphs when they exist
if (subGraphs.size > 0) {
if (statPathPrefix === 'gpu') {
mainGraph.graphType = 0.33; // Match GPU sub-graphs
} else if (statPathPrefix === 'frame') {
mainGraph.graphType = 0.66; // Match CPU sub-graphs
}
} else {
// reset to default background when no sub-graphs
mainGraph.graphType = 0.0;
}
}
}

Expand Down Expand Up @@ -757,6 +789,19 @@ class MiniStats {
};
this.updateSubStats(this.cpuGraphs, 'CPU', cpuStats, 'frame', 240);
}

// Update VRAM subcategory graphs when size index meets threshold
if (this._activeSizeIndex >= this.vramTimingMinSize) {
const vram = this.app.stats.vram;
const vramStats = {
tex: vram.tex,
geom: vram.geom
};
if (this.device.isWebGPU) {
vramStats.buffers = vram.buffers;
}
this.updateSubStats(this.vramGraphs, 'VRAM', vramStats, 'vram', 0);
}
}

this.frameIndex++;
Expand Down
35 changes: 20 additions & 15 deletions src/extras/mini-stats/render2d.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ import { VertexFormat } from '../../platform/graphics/vertex-format.js';
import { ShaderMaterial } from '../../scene/materials/shader-material.js';

// Graph colors for MiniStats
const graphColorRed = '1.0, 0.412, 0.380'; // Pastel Red
const graphColorGreen = '0.467, 0.867, 0.467'; // Pastel Green
const graphColorBlue = '0.424, 0.627, 0.863'; // Little Boy Blue
const graphColorDefault = '1.0, 0.412, 0.380'; // Pastel Red
const graphColorGpu = '0.467, 0.867, 0.467'; // Pastel Green
const graphColorCpu = '0.424, 0.627, 0.863'; // Little Boy Blue

// Background colors for MiniStats graphs
const mainBackgroundColor = '0.0, 0.0, 0.0';
Expand Down Expand Up @@ -62,8 +62,7 @@ const vertexShaderWGSL = /* wgsl */ `

// this fragment shader renders the bits required for text and graphs. The text is identified
// in the texture by white color. The graph data is specified as a single row of pixels
// where the R channel denotes the height of the 1st graph and the G channel the height
// of the second graph and B channel the height of the last graph
// where the R channel denotes the graph height
const fragmentShaderGLSL = /* glsl */ `
varying vec4 uv0;
varying float wordFlag;
Expand All @@ -73,15 +72,18 @@ const fragmentShaderGLSL = /* glsl */ `
uniform sampler2D wordsTex;

void main (void) {
vec3 graphColor = vec3(${graphColorDefault});
if (wordFlag > 0.5) {
graphColor = vec3(${graphColorCpu});
} else if (wordFlag > 0.2) {
graphColor = vec3(${graphColorGpu});
}

vec4 graphSample = texture2D(graphTex, uv0.xy);

vec4 graph;
if (uv0.w < graphSample.r)
graph = vec4(${graphColorRed}, 1.0);
else if (uv0.w < graphSample.g)
graph = vec4(${graphColorGreen}, 1.0);
else if (uv0.w < graphSample.b)
graph = vec4(${graphColorBlue}, 1.0);
graph = vec4(graphColor, 1.0);
else {
vec3 bgColor = vec3(${mainBackgroundColor});
if (wordFlag > 0.5) {
Expand Down Expand Up @@ -117,15 +119,18 @@ const fragmentShaderWGSL = /* wgsl */ `

@fragment fn fragmentMain(input : FragmentInput) -> FragmentOutput {
var uv0: vec4f = input.uv0;
var graphColor: vec3f = vec3f(${graphColorDefault});
if (input.wordFlag > 0.5) {
graphColor = vec3f(${graphColorCpu});
} else if (input.wordFlag > 0.2) {
graphColor = vec3f(${graphColorGpu});
}

var graphSample: vec4f = textureSample(graphTex, graphTex_sampler, uv0.xy);

var graph: vec4f;
if (uv0.w < graphSample.r) {
graph = vec4f(${graphColorRed}, 1.0);
} else if (uv0.w < graphSample.g) {
graph = vec4f(${graphColorGreen}, 1.0);
} else if (uv0.w < graphSample.b) {
graph = vec4f(${graphColorBlue}, 1.0);
graph = vec4f(graphColor, 1.0);
} else {
var bgColor: vec3f = vec3f(${mainBackgroundColor});
if (input.wordFlag > 0.5) {
Expand Down
5 changes: 1 addition & 4 deletions src/extras/mini-stats/stats-timer.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,8 @@ class StatsTimer {
this.app = app;
this.values = [];

// supporting up to 3 stats
// support one or more stats and accumulate them in the graph total
this.statNames = statNames;
if (this.statNames.length > 3) {
this.statNames.length = 3;
}

this.unitsName = unitsName;
this.decimalPlaces = decimalPlaces;
Expand Down
8 changes: 7 additions & 1 deletion src/framework/stats.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ class ApplicationStats {

Object.defineProperty(this.vram, 'totalUsed', {
get: function () {
return this.tex + this.vb + this.ib;
return this.tex + this.vb + this.ib + this.ub + this.sb;
}
});

Expand All @@ -97,6 +97,12 @@ class ApplicationStats {
return this.vb + this.ib;
}
});

Object.defineProperty(this.vram, 'buffers', {
get: function () {
return this.ub + this.sb;
}
});
}

get scene() {
Expand Down