Last active
March 22, 2025 09:22
-
-
Save regenrek/0076f7ff32765edcc11aba6770343d2b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { CSSProperties, useEffect, useState } from 'react'; | |
import { useFrame } from '@react-three/fiber'; | |
import { useWorld } from 'koota/react'; | |
// Define the Chrome performance memory interface | |
interface PerformanceMemory { | |
usedJSHeapSize: number; | |
jsHeapSizeLimit: number; | |
totalJSHeapSize: number; | |
} | |
interface ExtendedPerformance extends Performance { | |
memory?: PerformanceMemory; | |
} | |
export function DebugMetricsOverlay() { | |
const world = useWorld(); | |
const [isActive, setIsActive] = useState(true); | |
const [metrics, setMetrics] = useState({ | |
fps: 0, | |
memoryMB: 0, | |
entityCount: 0, | |
drawCalls: 0, | |
triangles: 0, | |
}); | |
const [frameSamples, setFrameSamples] = useState<number[]>([]); | |
const maxSamples = 60; | |
// F3 Toggle handler | |
useEffect(() => { | |
const handleKeyDown = (e: KeyboardEvent) => { | |
if (e.key === 'F3' || e.key === 'f3') { | |
setIsActive((prev) => !prev); | |
} | |
}; | |
window.addEventListener('keydown', handleKeyDown); | |
return () => window.removeEventListener('keydown', handleKeyDown); | |
}, []); | |
// Update metrics every frame - must be at the top level | |
useFrame(({ gl }, delta) => { | |
if (!isActive) return; | |
// FPS calculation | |
if (delta > 0) { | |
const fpsThisFrame = 1 / delta; | |
const newSamples = [...frameSamples, fpsThisFrame]; | |
if (newSamples.length > maxSamples) { | |
newSamples.shift(); | |
} | |
setFrameSamples(newSamples); | |
const sum = newSamples.reduce((a, b) => a + b, 0); | |
const avgFps = sum / newSamples.length; | |
// Memory usage (if available) | |
const perf = performance as ExtendedPerformance; | |
const memory = perf.memory?.usedJSHeapSize ? perf.memory.usedJSHeapSize / (1024 * 1024) : 0; | |
// Entity count from Koota | |
const entityCount = world.entities.length; | |
// Renderer stats | |
const drawCalls = gl.info.render?.calls || 0; | |
const triangles = gl.info.render?.triangles || 0; | |
setMetrics({ | |
fps: avgFps, | |
memoryMB: memory, | |
entityCount, | |
drawCalls, | |
triangles, | |
}); | |
} | |
}); | |
// If not active, render nothing | |
if (!isActive) return null; | |
const { fps, memoryMB, entityCount, drawCalls, triangles } = metrics; | |
// Color thresholds | |
const fpsColor = fps < 30 ? 'red' : fps < 55 ? 'yellow' : 'green'; | |
const memColor = memoryMB > 500 ? 'red' : memoryMB > 200 ? 'yellow' : 'green'; | |
const dcColor = drawCalls > 500 ? 'red' : drawCalls > 100 ? 'yellow' : 'green'; | |
const triColor = triangles > 500000 ? 'red' : triangles > 100000 ? 'yellow' : 'green'; | |
const containerStyle: CSSProperties = { | |
position: 'absolute', | |
top: 0, | |
right: 0, | |
padding: 40, | |
backgroundColor: 'rgba(20, 20, 20, 0.6)', | |
color: 'white', | |
fontSize: '1rem', | |
fontFamily: 'monospace', | |
zIndex: 5, | |
borderRadius: '4px', | |
lineHeight: 1.4, | |
}; | |
return ( | |
<div style={containerStyle}> | |
<div> | |
FPS: <span style={{ color: fpsColor }}>{fps.toFixed(1)}</span> | |
</div> | |
<div> | |
MEM: <span style={{ color: memColor }}>{memoryMB.toFixed(1)} MB</span> | |
</div> | |
<div>Entities: {entityCount}</div> | |
<div> | |
DrawCalls: <span style={{ color: dcColor }}>{drawCalls}</span> | |
</div> | |
<div> | |
Triangles: <span style={{ color: triColor }}>{triangles}</span> | |
</div> | |
</div> | |
); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment