import React from 'react'; const trackers = {}; let tick = null; let fps = 0; let counter = 0; function startLoop() { if (tick) return; const frames = [performance.now(), 0]; tick = () => { // @ts-expect-error Object.values(trackers).forEach(tracker => tracker.update()); // Compute fps over last 60 frames counter++; const fpsN = 60; frames.unshift(performance.now()); if (frames.length > fpsN) frames.pop(); const l = frames.length; if (counter % 30 === 0) { fps = Math.round((l * 1000) / (frames[0] - frames[l - 1])); } requestAnimationFrame(tick); }; tick(); } export const createTracker = label => { if (!trackers[label]) { const N = 10; let value = 0; let valueAtFrameStart = value; const stats = []; const result: {countPerFrame?: number, maxPerFrame?: number, avPerFrame?: number} = {}; const update = () => { result.countPerFrame = value - valueAtFrameStart; valueAtFrameStart = value; stats.unshift(result.countPerFrame); if (stats.length > N) stats.pop(); result.maxPerFrame = Math.max(...stats); result.avPerFrame = stats.reduce((a, b) => a + b, 0) / N; }; const render = () => { if (!tick) startLoop(); return (
{`${label} per frame: ${result.countPerFrame}, max: ${result.maxPerFrame}, av: ${result.avPerFrame}`}
); }; const increment = () => { value++; }; const tracker = {update, render, increment}; trackers[label] = tracker; } return trackers[label]; }; const patchUpdateState = (tracker, LayerClass) => { const _oldUpdateState = LayerClass.prototype.updateState; LayerClass.prototype.updateState = function(params) { const { extensionsChanged, propsOrDataChanged, stateChanged, updateTriggersChanged } = params.changeFlags; if (extensionsChanged || propsOrDataChanged || stateChanged || updateTriggersChanged) { tracker.increment(); } _oldUpdateState.call(this, params); }; }; export const createLayerUpdateTracker = (label, layers) => { const tracker = createTracker('state updates'); layers.forEach(layer => patchUpdateState(tracker, layer)); return tracker; }; export const renderPerformanceStats = () => { if (!tick) startLoop(); return (
{/* @ts-expect-error */} {Object.values(trackers).map(tracker => tracker.render())} {`FPS: ${fps}`}
); };