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 { ConfigUI } from './config-ui.js';
|
||||
import { StageLightBars } from './stage-light-bars.js';
|
||||
import { FPSCounter } from './fps-counter.js';
|
||||
// Scene Features ^^^
|
||||
|
||||
// --- Scene Modeling Function ---
|
||||
|
||||
Loading…
Reference in New Issue
Block a user