Feature: FPS counter and memory tracker for performance investigations
This commit is contained in:
parent
4cd869791f
commit
7c18a3db11
205
party-stage/src/scene/fps-counter.js
Normal file
205
party-stage/src/scene/fps-counter.js
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
import { SceneFeature } from './SceneFeature.js';
|
||||||
|
import sceneFeatureManager from './SceneFeatureManager.js';
|
||||||
|
|
||||||
|
export class FPSCounter extends SceneFeature {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.frames = 0;
|
||||||
|
this.timeAccumulator = 0;
|
||||||
|
this.fpsElement = null;
|
||||||
|
this.textElement = null;
|
||||||
|
this.canvas = null;
|
||||||
|
this.ctx = null;
|
||||||
|
this.history = [];
|
||||||
|
this.memTextElement = null;
|
||||||
|
this.memCanvas = null;
|
||||||
|
this.memCtx = null;
|
||||||
|
this.memHistory = [];
|
||||||
|
sceneFeatureManager.register(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
// Create the FPS display container
|
||||||
|
this.fpsElement = document.createElement('div');
|
||||||
|
Object.assign(this.fpsElement.style, {
|
||||||
|
position: 'absolute',
|
||||||
|
top: '0',
|
||||||
|
right: '0',
|
||||||
|
zIndex: '10000',
|
||||||
|
padding: '5px',
|
||||||
|
background: 'rgba(0, 0, 0, 0.5)',
|
||||||
|
pointerEvents: 'none',
|
||||||
|
userSelect: 'none',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'flex-end'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Text Display
|
||||||
|
this.textElement = document.createElement('div');
|
||||||
|
Object.assign(this.textElement.style, {
|
||||||
|
color: '#00ff00',
|
||||||
|
fontFamily: 'monospace',
|
||||||
|
fontSize: '16px',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
marginBottom: '2px'
|
||||||
|
});
|
||||||
|
this.textElement.innerText = 'FPS: --';
|
||||||
|
this.fpsElement.appendChild(this.textElement);
|
||||||
|
|
||||||
|
// Graph Canvas
|
||||||
|
this.canvas = document.createElement('canvas');
|
||||||
|
this.canvas.width = 100;
|
||||||
|
this.canvas.height = 40;
|
||||||
|
Object.assign(this.canvas.style, {
|
||||||
|
width: '100px',
|
||||||
|
height: '40px',
|
||||||
|
background: '#222',
|
||||||
|
border: '1px solid #444'
|
||||||
|
});
|
||||||
|
this.ctx = this.canvas.getContext('2d');
|
||||||
|
this.fpsElement.appendChild(this.canvas);
|
||||||
|
|
||||||
|
// Memory Text Display
|
||||||
|
this.memTextElement = document.createElement('div');
|
||||||
|
Object.assign(this.memTextElement.style, {
|
||||||
|
color: '#00ffff',
|
||||||
|
fontFamily: 'monospace',
|
||||||
|
fontSize: '16px',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
marginBottom: '2px',
|
||||||
|
marginTop: '5px'
|
||||||
|
});
|
||||||
|
this.memTextElement.innerText = 'MEM: --';
|
||||||
|
this.fpsElement.appendChild(this.memTextElement);
|
||||||
|
|
||||||
|
// Memory Graph Canvas
|
||||||
|
this.memCanvas = document.createElement('canvas');
|
||||||
|
this.memCanvas.width = 100;
|
||||||
|
this.memCanvas.height = 40;
|
||||||
|
Object.assign(this.memCanvas.style, {
|
||||||
|
width: '100px',
|
||||||
|
height: '40px',
|
||||||
|
background: '#222',
|
||||||
|
border: '1px solid #444'
|
||||||
|
});
|
||||||
|
this.memCtx = this.memCanvas.getContext('2d');
|
||||||
|
this.fpsElement.appendChild(this.memCanvas);
|
||||||
|
|
||||||
|
document.body.appendChild(this.fpsElement);
|
||||||
|
|
||||||
|
this.history = new Array(this.canvas.width).fill(0);
|
||||||
|
this.memHistory = new Array(this.memCanvas.width).fill(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
update(deltaTime) {
|
||||||
|
this.frames++;
|
||||||
|
this.timeAccumulator += deltaTime;
|
||||||
|
|
||||||
|
const segment = 0.25;
|
||||||
|
if (this.timeAccumulator >= segment) {
|
||||||
|
const fps = Math.round(this.frames / this.timeAccumulator);
|
||||||
|
if (this.textElement) {
|
||||||
|
this.textElement.innerText = `FPS: ${fps}`;
|
||||||
|
if (fps >= 58) {
|
||||||
|
this.textElement.style.color = '#00ff00';
|
||||||
|
} else if (fps >= 55) {
|
||||||
|
this.textElement.style.color = '#ffff00';
|
||||||
|
} else {
|
||||||
|
this.textElement.style.color = '#ff0000';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update Graph
|
||||||
|
this.history.push(fps);
|
||||||
|
if (this.history.length > this.canvas.width) {
|
||||||
|
this.history.shift();
|
||||||
|
}
|
||||||
|
this.drawGraph();
|
||||||
|
|
||||||
|
// Update Memory
|
||||||
|
if (performance && performance.memory) {
|
||||||
|
const mem = performance.memory.usedJSHeapSize / 1048576; // MB
|
||||||
|
if (this.memTextElement) this.memTextElement.innerText = `MEM: ${Math.round(mem)} MB`;
|
||||||
|
|
||||||
|
this.memHistory.push(mem);
|
||||||
|
if (this.memHistory.length > this.memCanvas.width) {
|
||||||
|
this.memHistory.shift();
|
||||||
|
}
|
||||||
|
this.drawMemGraph();
|
||||||
|
} else {
|
||||||
|
if (this.memTextElement) this.memTextElement.style.display = 'none';
|
||||||
|
if (this.memCanvas) this.memCanvas.style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
this.frames = 0;
|
||||||
|
this.timeAccumulator = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drawGraph() {
|
||||||
|
if (!this.ctx) return;
|
||||||
|
const ctx = this.ctx;
|
||||||
|
const w = this.canvas.width;
|
||||||
|
const h = this.canvas.height;
|
||||||
|
const maxFps = 70;
|
||||||
|
|
||||||
|
ctx.clearRect(0, 0, w, h);
|
||||||
|
|
||||||
|
// Draw 60 FPS reference line
|
||||||
|
const y60 = h - (60 / maxFps) * h;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.strokeStyle = '#555';
|
||||||
|
ctx.moveTo(0, y60);
|
||||||
|
ctx.lineTo(w, y60);
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
// Draw Graph
|
||||||
|
ctx.lineWidth = 1.5;
|
||||||
|
|
||||||
|
for (let i = 0; i < this.history.length - 1; i++) {
|
||||||
|
const val = this.history[i + 1];
|
||||||
|
const x1 = i;
|
||||||
|
const y1 = h - (this.history[i] / maxFps) * h;
|
||||||
|
const x2 = i + 1;
|
||||||
|
const y2 = h - (val / maxFps) * h;
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(x1, y1);
|
||||||
|
ctx.lineTo(x2, y2);
|
||||||
|
|
||||||
|
if (val >= 58) ctx.strokeStyle = '#00ff00';
|
||||||
|
else if (val >= 55) ctx.strokeStyle = '#ffff00';
|
||||||
|
else ctx.strokeStyle = '#ff0000';
|
||||||
|
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drawMemGraph() {
|
||||||
|
if (!this.memCtx) return;
|
||||||
|
const ctx = this.memCtx;
|
||||||
|
const w = this.memCanvas.width;
|
||||||
|
const h = this.memCanvas.height;
|
||||||
|
// Auto-scale max memory
|
||||||
|
const maxMem = Math.max(...this.memHistory, 100) * 1.1;
|
||||||
|
|
||||||
|
ctx.clearRect(0, 0, w, h);
|
||||||
|
|
||||||
|
// Draw Graph
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.strokeStyle = '#00ffff';
|
||||||
|
ctx.lineWidth = 1.5;
|
||||||
|
|
||||||
|
for (let i = 0; i < this.memHistory.length; i++) {
|
||||||
|
const val = this.memHistory[i];
|
||||||
|
const x = i;
|
||||||
|
const y = h - (val / maxMem) * h;
|
||||||
|
if (i === 0) ctx.moveTo(x, y);
|
||||||
|
else ctx.lineTo(x, y);
|
||||||
|
}
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
new FPSCounter();
|
||||||
@ -20,6 +20,7 @@ import { ProjectionScreen } from './projection-screen.js';
|
|||||||
import { StageLasers } from './stage-lasers.js';
|
import { StageLasers } from './stage-lasers.js';
|
||||||
import { ConfigUI } from './config-ui.js';
|
import { ConfigUI } from './config-ui.js';
|
||||||
import { StageLightBars } from './stage-light-bars.js';
|
import { StageLightBars } from './stage-light-bars.js';
|
||||||
|
import { FPSCounter } from './fps-counter.js';
|
||||||
// Scene Features ^^^
|
// Scene Features ^^^
|
||||||
|
|
||||||
// --- Scene Modeling Function ---
|
// --- Scene Modeling Function ---
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user