Производительность холста HTML5 - вычисление циклов/кадров в секунду

Я знаю, что было задано несколько вопросов, подобных этому, например: Проверить FPS в JS? - что в некоторой степени помогло мне чтобы узнать, как долго выполнялся цикл.

То, что я ищу, является чем-то более читаемым и контролируемым. Я хочу иметь возможность установить частоту обновления для счетчика FPS, чтобы он был медленным, поэтому он доступен для чтения человеком или так же быстро, как приложение может работать, поэтому я могу использовать его на каком-то спидометре.

Во всяком случае, вот код, который у меня есть прямо сейчас:

var lastLoop = new Date().getTime();

function updateStage()
{   
    clearCanvas();
    updateStageObjects();
    drawStageObjects();     

    var thisLoop = new Date().getTime(); 
    var fps = (thisLoop - lastLoop);

    $('#details').html(fps);

    lastLoop = thisLoop;
    iteration = setTimeout(updateStage, 1);
}
  • Можно ли настроить функцию setTimeout на скорость 1 миллисекунду? Я думал, что это просто запустит его так быстро, как это возможно.

  • Должен ли я подсчитывать каждые 100 кадров или около того, узнать, сколько миллисекунд потребовалось для запуска 100 кадров, затем выполните расчет, чтобы узнать, сколько кадров он сделал бы, если миллисекунды были 1000? Каким будет этот расчет?

  • Чтобы сделать результат более точным, я предполагаю, что мне нужно отображать средние значения, поскольку один кадр может варьировать значительную величину, как мне это сделать?

Приветствуются любые советы.

Спасибо.

Ответы

Ответ 1

  • Обратите внимание, что чем быстрее вы обновите свой результат, тем больше вы повлияете на ваши измерения. Несмотря на минимальный уровень, я пытаюсь обновить свой fps-выход один раз в секунду или меньше, если не нужно идти быстрее.

  • Мне нравится иметь фильтр низких частот по моим результатам, чтобы временная икота не слишком сильно влияла на значения. Это легче вычислять и писать, чем скользящее среднее, и не имеет проблемы с общим показателем, когда ваши текущие показания зависят от общей производительности на протяжении всего прогона (например, аномальные показания во время запуска).

Объединяем, вот как я обычно измеряю FPS:

var fps = 0, now, lastUpdate = (new Date)*1;

// The higher this value, the less the FPS will be affected by quick changes
// Setting this to 1 will show you the FPS of the last sampled frame only
var fpsFilter = 50;

function drawFrame(){
  // ... draw the frame ...

  var thisFrameFPS = 1000 / ((now=new Date) - lastUpdate);
  if (now!=lastUpdate){
    fps += (thisFrameFPS - fps) / fpsFilter;
    lastUpdate = now;
  }

  setTimeout( drawFrame, 1 );
}

var fpsOut = document.getElementById('fps');
setInterval(function(){
  fpsOut.innerHTML = fps.toFixed(1) + "fps";
}, 1000); 

Ответ 2

Я что-то пробовал,

Если вы измените

lastUpdate = now

к

lastUpdate = now * 1 - 1;

Ваша проблема с NaN решена! Это также используется, когда определяется lastUpdate. Вероятно, потому что он не может преобразовать дату в временную метку unix.

Новый результат будет:

var fps = 0, now, lastUpdate = (new Date)*1 - 1;

// The higher this value, the less the FPS will be affected by quick changes
// Setting this to 1 will show you the FPS of the last sampled frame only
var fpsFilter = 50;

function drawFrame(){
  // ... draw the frame ...

  var thisFrameFPS = 1000 / ((now=new Date) - lastUpdate);
  fps += (thisFrameFPS - fps) / fpsFilter;
  lastUpdate = now * 1 - 1;

  setTimeout( drawFrame, 1 );
}

var fpsOut = document.getElementById('fps');
setInterval(function(){
  fpsOut.innerHTML = fps.toFixed(1) + "fps";
}, 1000); 

Ответ 3

Я опубликовал это решение и немного улучшил его. Посмотрите здесь - http://jsfiddle.net/ync3S/

  • Я исправил ошибку NaN с помощью Date.now() вместо того, чтобы каждый раз создавать новый объект даты и пытаться ссылаться на него. Это также предотвращает необходимость сбора мусора.
  • Я немного обновил имена переменных и функций и добавил некоторые дополнительные комментарии - не обязательно, но приятно иметь.
  • Я включил некоторый код чертежа для тестирования.
  • Я добавил fpsDesired как тест var для цикла двигателя.
  • Я начал fpsAverage в fpsDesired, поэтому с fpsFilter он не работает от 0 до реального FPS, а начинается с желаемого FPS и настраивается оттуда.
  • Теперь чертеж блокирует блокировку, которую он уже рисовал, и это можно использовать для приостановки и других функций управления.

Основной блок выглядит следующим образом:

var fpsFilter = 1; // the low pass filter to apply to the FPS average
var fpsDesired = 25; // your desired FPS, also works as a max
var fpsAverage = fpsDesired;
var timeCurrent, timeLast = Date.now();
var drawing = false;

function fpsUpdate() {
    fpsOutput.innerHTML = fpsAverage.toFixed(2);
}

function frameDraw() {
    if(drawing) { return; } else { drawing = true; }

    timeCurrent = Date.now();
    var fpsThisFrame = 1000 / (timeCurrent - timeLast);
    if(timeCurrent > timeLast) {
        fpsAverage += (fpsThisFrame - fpsAverage) / fpsFilter;
        timeLast = timeCurrent;
    }

    drawing = false;
}

setInterval(fpsUpdate, 1000);
fpsUpdate();

setInterval(frameDraw, 1000 / fpsDesired);
frameDraw();

Пойдем, чтобы иметь возиться и посмотреть, могу ли я придумать что-то более плавное, так как этот поток находится в верхней части результатов Google.

Посмотрим, что мы можем все придумать как команда, и я думаю, что всегда аккуратно не использовать сторонние библиотеки, делая код переносимым для всех:)

-Platima

Ответ 4

Просто установите интервал, который каждую секунду перезагружает счетчик fps.

var fpsOut, fpsCount;

var draw = function () {

    fpsCount++;

    ..Draw To Canvas..


    ..Get the fps value: fpsOut

    requestAnimationFrame(draw);

};
setInterval(function () {

    fpsOut = fpsCount;
    fpsCount = 0;

}, 1000);

draw();