Files
fabrikanabytok/apps/fabrikanabytok/lib/three/performance.ts
2025-11-28 20:48:15 +01:00

140 lines
3.5 KiB
TypeScript

/**
* Performance Monitoring
*/
import * as THREE from 'three'
export interface PerformanceMetrics {
fps: number
frameTime: number
drawCalls: number
triangles: number
geometries: number
textures: number
programs: number
memory: {
geometries: number
textures: number
total: number
}
renderTime: number
gpuTime: number
}
export class PerformanceMonitor {
private static instance: PerformanceMonitor
private metrics: PerformanceMetrics = {
fps: 60,
frameTime: 16.67,
drawCalls: 0,
triangles: 0,
geometries: 0,
textures: 0,
programs: 0,
memory: { geometries: 0, textures: 0, total: 0 },
renderTime: 0,
gpuTime: 0
}
private fpsHistory: number[] = []
private lastTime: number = performance.now()
private frames: number = 0
private callbacks: Set<(metrics: PerformanceMetrics) => void> = new Set()
private constructor() {
setInterval(() => {
this.updateMetrics()
this.notifyCallbacks()
}, 1000)
}
static getInstance(): PerformanceMonitor {
if (!PerformanceMonitor.instance) {
PerformanceMonitor.instance = new PerformanceMonitor()
}
return PerformanceMonitor.instance
}
private updateMetrics(): void {
const currentTime = performance.now()
const deltaTime = currentTime - this.lastTime
if (deltaTime >= 1000) {
this.metrics.fps = Math.round((this.frames * 1000) / deltaTime)
this.metrics.frameTime = deltaTime / this.frames
this.fpsHistory.push(this.metrics.fps)
if (this.fpsHistory.length > 60) this.fpsHistory.shift()
this.frames = 0
this.lastTime = currentTime
}
}
updateRendererMetrics(renderer: THREE.WebGLRenderer): void {
const info = renderer.info
this.metrics.drawCalls = info.render.calls
this.metrics.triangles = info.render.triangles
this.metrics.geometries = info.memory.geometries
this.metrics.textures = info.memory.textures
this.metrics.programs = info.programs?.length || 0
this.frames++
}
getMetrics(): PerformanceMetrics {
return { ...this.metrics }
}
getAverageFPS(): number {
if (this.fpsHistory.length === 0) return 0
return this.fpsHistory.reduce((a, b) => a + b, 0) / this.fpsHistory.length
}
getFPSVariance(): number {
if (this.fpsHistory.length === 0) return 0
const avg = this.getAverageFPS()
const variance = this.fpsHistory.reduce((sum, fps) => sum + Math.pow(fps - avg, 2), 0) / this.fpsHistory.length
return Math.sqrt(variance)
}
getPerformanceGrade(): 'A' | 'B' | 'C' | 'D' | 'F' {
const avgFPS = this.getAverageFPS()
if (avgFPS >= 60) return 'A'
if (avgFPS >= 45) return 'B'
if (avgFPS >= 30) return 'C'
if (avgFPS >= 20) return 'D'
return 'F'
}
generateReport() {
return {
grade: this.getPerformanceGrade(),
avgFPS: this.getAverageFPS(),
variance: this.getFPSVariance(),
recommendations: []
}
}
subscribe(callback: (metrics: PerformanceMetrics) => void): () => void {
this.callbacks.add(callback)
return () => this.callbacks.delete(callback)
}
private notifyCallbacks(): void {
this.callbacks.forEach(cb => cb(this.metrics))
}
reset(): void {
this.fpsHistory = []
this.frames = 0
this.lastTime = performance.now()
}
getCurrentPreset() {
return {
name: this.metrics.fps >= 60 ? 'ultra' : this.metrics.fps >= 45 ? 'high' : this.metrics.fps >= 30 ? 'medium' : 'low',
minFPS: this.metrics.fps
}
}
}