b42ff297eb
- 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
90 lines
2.6 KiB
TypeScript
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
|
|
};
|
|
} |