Files
personal_website/src/hooks/usePerformanceMonitor.ts
T
jory b42ff297eb Fix Tetris 2P preview block synchronization issue
- Prevent duplicate piece shapes when generating next pieces
- Enhanced randomTetromino() with excludeShape parameter
- Update piece placement logic to avoid visual duplicates
- Improve initial state generation for both players
- Add P1 and P2 next piece previews in 2P mode
2026-01-06 21:54:11 +01:00

90 lines
2.6 KiB
TypeScript

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<number[]>([]);
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
};
}