import { useRef, useCallback } from 'react'; interface PerformanceMetrics { frameRate: number; memoryUsage?: number; renderTime: number; dropCount: number; } export function usePerformanceMonitor() { const frameCount = useRef(0); const lastTime = useRef(performance.now()); const dropCount = useRef(0); const renderTimes = useRef([]); const maxRenderTimeSamples = 60; // Keep last 60 frames const recordFrame = useCallback(() => { const now = performance.now(); const deltaTime = now - lastTime.current; frameCount.current++; // Track render time renderTimes.current.push(deltaTime); if (renderTimes.current.length > maxRenderTimeSamples) { renderTimes.current.shift(); } // Detect frame drops (should be ~16.67ms for 60fps) if (deltaTime > 20) { // Allow some tolerance dropCount.current++; } lastTime.current = now; // Calculate metrics every second if (frameCount.current % 60 === 0) { const avgRenderTime = renderTimes.current.reduce((a, b) => a + b, 0) / renderTimes.current.length; const fps = 1000 / avgRenderTime; const metrics: PerformanceMetrics = { frameRate: fps, memoryUsage: (performance as any).memory ? Math.round((performance as any).memory.usedJSHeapSize / (1024 * 1024)) : undefined, renderTime: avgRenderTime, dropCount: dropCount.current }; // Log performance warnings if (fps < 30) { console.warn(`⚠️ Low FPS: ${fps.toFixed(1)}, Drops: ${dropCount.current}, Memory: ${metrics.memoryUsage}MB`); } if (metrics.memoryUsage && metrics.memoryUsage > 2000) { // > 2GB console.warn(`⚠️ High memory usage: ${metrics.memoryUsage}MB, FPS: ${fps.toFixed(1)}`); } return metrics; } return null; }, []); const reset = useCallback(() => { frameCount.current = 0; lastTime.current = performance.now(); dropCount.current = 0; renderTimes.current = []; }, []); const getCurrentMetrics = useCallback((): PerformanceMetrics => { const avgRenderTime = renderTimes.current.length > 0 ? renderTimes.current.reduce((a, b) => a + b, 0) / renderTimes.current.length : 16.67; // Default for 60fps return { frameRate: 1000 / avgRenderTime, memoryUsage: (performance as any).memory ? Math.round((performance as any).memory.usedJSHeapSize / (1024 * 1024)) : undefined, renderTime: avgRenderTime, dropCount: dropCount.current }; }, []); return { recordFrame, reset, getCurrentMetrics }; }