* Copyright (c) 2025 Huawei Technologies Co., Ltd.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
* @param {number[]} frameTimesInSec
*/
function createFpsStats(frameTimesInSec) {
const sampleSize = frameTimesInSec.length;
if (sampleSize === 0) {
return null;
}
const filteredFrameTimesInSec = frameTimesInSec.filter(
frameTimeInSec => frameTimeInSec < 1,
);
return {
mean: calculateAverageFps(filteredFrameTimesInSec),
standardDeviationInFps: calculateStandardDeviation(
filteredFrameTimesInSec.map(ft => 1 / ft),
),
frameTime95thPercentileInFps: convertFrameTimeToFPS(
calculateFrameTimesPercentile(filteredFrameTimesInSec, 95),
),
frameTime99thPercentileInFps: convertFrameTimeToFPS(
calculateFrameTimesPercentile(filteredFrameTimesInSec, 99),
),
};
}
* @param {number[]} frameTimesInSec
*/
function calculateAverageFps(frameTimesInSec) {
return convertFrameTimeToFPS(sum(frameTimesInSec) / frameTimesInSec.length);
}
* @param {number[]} values
*/
function calculateStandardDeviation(values) {
const n = values.length;
const mean = values.reduce((a, b) => a + b) / n;
return Math.sqrt(
values.map(x => Math.pow(x - mean, 2)).reduce((a, b) => a + b) / n,
);
}
* @param {Object} fpsStats
* @param {number} fpsStats.mean
* @param {number} fpsStats.standardDeviationInFps
* @param {number} fpsStats.frameTime95thPercentileInFps
* @param {number} fpsStats.frameTime99thPercentileInFps
*/
function generateFPSStatsConsoleOutput(fpsStats) {
return (
'\n' +
'========= FPS stats =========\n' +
'mean : ' +
fpsStats.mean.toFixed(1) +
' FPS\n' +
'standard deviation : ' +
fpsStats.standardDeviationInFps.toFixed(1) +
' FPS\n' +
'95th frame time percentile: ' +
fpsStats.frameTime95thPercentileInFps.toFixed(1) +
' FPS\n' +
'99th frame time percentile: ' +
fpsStats.frameTime99thPercentileInFps.toFixed(1) +
' FPS\n'
);
}
* @param {number[]} values
*/
function sum(values) {
return values.reduce((acc, value) => acc + value, 0);
}
* @param {number} frameTimeInSec
*/
function convertFrameTimeToFPS(frameTimeInSec) {
return 1 / frameTimeInSec;
}
* @param{number[]} frameTimesInSec
* @param{number} percentile
*/
function calculateFrameTimesPercentile(frameTimesInSec, percentile) {
const sortedFrameTimesInSec = [...frameTimesInSec].sort((a, b) => a - b);
const totalTimeInSec = sum(frameTimesInSec);
let currentFrameTimesInSecSum = 0;
let currentIdx = 0;
while (
currentFrameTimesInSecSum < totalTimeInSec * (percentile / 100) &&
currentIdx < sortedFrameTimesInSec.length - 1
) {
currentFrameTimesInSecSum += sortedFrameTimesInSec[currentIdx];
currentIdx++;
}
return sortedFrameTimesInSec[currentIdx];
}
module.exports = {createFpsStats, generateFPSStatsConsoleOutput};