Я не могу понять, почему этот счетчик кадров в секунду неточен

Я пытаюсь отслеживать кадры в секунду в своей игре. Я не хочу, чтобы фпс показывался как средний. Я хочу посмотреть, как влияет на частоту кадров, когда я нажимаю клавиши, добавляю модели и т. д. Поэтому я использую переменную для хранения текущего и предыдущего времени, и когда они отличаются на 1 секунду, я обновляю частоту кадров.

Моя проблема в том, что он показывает около 33 кадров в секунду, но когда я очень быстро перемещаю мышь, частота кадров подскакивает до 49 кадров в секунду. В других случаях, если я изменю простую строку кода в другом месте, не связанном со счетчиком кадров, или закрою проект и открою его позже, частота кадров будет около 60. Vsync включен, поэтому я не могу сказать, работает ли мышь. кадры в секунду.

Вот мой код, который находится в функции обновления, которая происходит каждый кадр:

FrameCount++;
currentTime = timeGetTime ();
static unsigned long prevTime = currentTime;
TimeDelta = (currentTime - prevTime) / 1000;
if (TimeDelta > 1.0f)
{
 fps = FrameCount / TimeDelta;
 prevTime = currentTime;
 FrameCount = 0;
    TimeDelta = 0;
}

Вот объявления переменных:

int FrameCount;
double fps, currentTime, prevTime, TimeDelta, TimeElapsed;

Пожалуйста, дайте мне знать, что здесь не так и как это исправить, или если у вас есть лучший способ подсчета кадров в секунду. Спасибо!!!!!!

Кстати, я использую DirectX 9, но сомневаюсь, что это актуально, и я использую PeekMessage. Должен ли я вместо этого использовать оператор if else? Вот мой цикл обработки сообщений:

MSG msg;
ZeroMemory (&msg, sizeof (MSG));

while (msg.message != WM_QUIT)
{
    if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
    {
        TranslateMessage (&msg);
        DispatchMessage (&msg);
    }

    Update ();
    RenderFrame ();
}

person rmetzger    schedule 14.02.2010    source источник
comment
Ответ на этот вопрос будет зависеть от того, как вы генерируете свои кадры и где этот код находится в вашем цикле рендеринга.   -  person ChrisF    schedule 14.02.2010


Ответы (5)


timeGetTime() неточен. Вместо этого используйте высокопроизводительный счетчик.

Пример кода находится в этом другом ответе.

person bobobobo    schedule 16.02.2010

Это признак того, что ваш цикл обработки сообщений блокируется, а не заглядывает или опрашивается, поскольку частота кадров увеличивается по мере того, как вы получаете больше сообщений мыши. Вам следует рассмотреть возможность использования PeekMessage вместо GetMessage.

РЕДАКТИРОВАТЬ: Кроме того, если вам хочется перегрузить ЦП, вы также можете добавить PM_NOYIELD, чтобы система не позволяла другим потокам выполняться во время PeekMessage. Из документации PeekMessage:

При желании вы можете комбинировать значение PM_NOYIELD либо с PM_NOREMOVE, либо с PM_REMOVE. Этот флаг не позволяет системе освобождать любой поток, ожидающий бездействия вызывающего объекта (см. WaitForInputIdle).

person MSN    schedule 14.02.2010
comment
Я хотел бы опубликовать свой цикл обработки сообщений. Но я не могу получить код для форматирования в этих полях комментариев. Должен ли я форматировать его иначе, чем в полях вопросов? - person rmetzger; 14.02.2010
comment
@rmetzger, просто добавьте его в исходный вопрос. - person MSN; 14.02.2010

Скорее примечание, чем ответ на вашу проблему, но вы говорите, что не хотите усреднять частоту кадров, однако вы фактически усредняете ее за секунду, вычисляя ее таким образом. Если вы хотите, чтобы частота кадров обновлялась каждый кадр, вы можете попробовать что-то вроде:

currentTime = timeGetTime();
fps = 1000.0f / (currentTime - prevTime);
prevTime = currentTime;

Хотя на этом этапе timeGetTime будет немного неточным, так как числа, вероятно, будут небольшими. QueryPerformanceCounter может быть лучшим таймером для использования.

person James Sutherland    schedule 14.02.2010
comment
В среднем 1 секунда, поэтому он не обновляется на экране быстрее, чем я могу его прочитать. Я просто не хочу брать средний fps с момента запуска программы или умножать на какой-то коэффициент для масштабирования. Например, я видел это в другом треде: Вам нужно сглаженное среднее, проще всего взять текущий ответ (время отрисовки последнего кадра) и объединить его с предыдущим ответом. time = time * 0,9 + last_frame * 0,1 Если я не ошибаюсь, разве это не объединяет последние кадры в секунду со средним значением всех кадров в секунду? - person rmetzger; 14.02.2010
comment
Извините за форматирование выше. Я не знаю, почему он не позволяет мне делать блочные кавычки или форматировать код. - person rmetzger; 14.02.2010
comment
Среднее значение, которое вы описываете, является средневзвешенным, которое будет точно отслеживать последние тайминги, но также всегда будет зависеть от всплесков в начале процесса. Я бы не стал беспокоиться о вашем временном коде, так как он подходит для ваших целей. Возможно, увеличьте частоту обновления до двух раз в секунду или нарисуйте ее в виде гистограммы, чтобы было легче увидеть эффекты. - person Kylotan; 16.02.2010

Что касается вашего цикла обработки сообщений, да, вы должны использовать такой оператор if-else.

if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
    TranslateMessage (&msg);
    DispatchMessage (&msg);
} 
else
{
    Update ();
    RenderFrame ();
}

В противном случае будет обрабатываться только одно сообщение на кадр, что приведет к серьезной задержке ввода, если кадр будет отображаться долго.

Что касается ваших показаний FPS, то тут нечего сказать, на это может повлиять множество вещей. Код счетчика кадров в секунду выглядит так, как будто он должен выполнять свою задачу (но с некоторой потерей точности).

Мышь управляет камерой? Если да, то уверены ли вы, что именно ввод с помощью мыши вызывает увеличение частоты кадров, а не в каком направлении вы смотрите?

Вы абсолютно уверены, что ваш код fps запускается только один раз за кадр?

Вы где-то обрабатываете ввод? Если да, то что произойдет, если вы отключите этот код и быстро переместите мышь? Все еще такой же?

person Daniel Dimovski    schedule 16.02.2010
comment
Ну, теперь я использую оператор if-else. Я уверен, что обновляю счетчик кадров только один раз за кадр. Раньше я использовал клавиши со стрелками для перемещения камеры. Я закомментировал код обработки ключа и фпс не изменился. Я нигде не получаю и не обрабатываю ввод мыши в своей программе. Мой счетчик кадров в секунду показывает от 31 до 34 кадров в секунду. Тем не менее, когда я двигаю мышь, она все равно подскакивает до 48-50. Я не уверен, связано ли это, но когда я рисую модель, мой fps находится между 40-48 и очень непостоянен. И когда я двигаю мышь, fps снова достигает 48-50. - person rmetzger; 17.02.2010

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

Псевдо:

логический рендерфрейм = истина; if (PeekMessage(...)) { // Обработать. рендерфрейм = ложь; }

if (renderFrame) визуализировать ваш кадр

в противном случае выйдите из функции цикла сообщений (и введите ее снова или решите ее через некоторое время, это немного зависит от того, как вы структурировали остальную часть вашего основного цикла

person nielsj    schedule 23.05.2010