it-swarm.com.ru

Steady_Clock пропуск между обновлениями в основном игровом цикле

В процессе разработки прочного игрового цикла в SFML я столкнулся с этой проблемой, которую, похоже, не могу понять. Мне удалось удалить весь код SFML и все еще видеть проблему с clock() в time.h. Затем я пошел дальше и все еще вижу проблему, используя std::chrono::steady_clock.

Проблема: Несколько последовательно я вижу пропуски в объеме работы, которую можно выполнить между обновлениями. Каждое обновление должно занимать 1/60 секунды, а остальное время тратится в функции Draw(), чтобы сделать как можно больше рисунков Иногда количество ничьих уменьшается до 0 или 1 без видимой причины. Это пузыри до фактического применения в виде заметного заикания. Помимо «пропусков», количество проведенных розыгрышей очень стабильно.

Вот изображение (обратите внимание на скачок времени обновления и падение числа розыгрышей): Вывод на консоль проблемы

Некоторый код:

#include <iostream>
#include <time.h>
#include <chrono>

using namespace std;
using namespace std::chrono;

void Draw()
{
    //for (int i = 0; i < 1000000; i++);
}

int main()
{
    steady_clock::time_point update_time;
    steady_clock::time_point update_next;
    int update_rate = 16666666; // 60 times a second (nanosecs)
    int updates;
    int max_updates = 5;
    int draws = 0;
    update_next = steady_clock::now();

    while (true)
    {
        updates = 0;
        update_time = steady_clock::now();
        while (duration_cast<nanoseconds>(update_time - update_next) > nanoseconds(update_rate) && updates++ < max_updates)
        {
            if (draws <= 1) {
                cout << "!!!!!!!!!!!!!ERROR!!!!!!!!!!!!!" << endl;
            }
            cout << "UPDATE - ";
            cout << "Draws: " << draws 
                 << " - UT - UN: " << duration_cast<nanoseconds>(update_time - update_next).count()
                 << endl;

            draws = 0;
            update_next += nanoseconds(update_rate);
        }
        draws++;
        Draw();
    }

    return 0;
}
  • Возможно, есть что-то, чего я не понимаю в типичных приложениях? Нужно ли Windows периодически перезагружать циклы процессора?
  • Я видел эту проблему с stable_clock, clock и в усовершенствованном SFML-приложении, где работа выполняется во время Update и Draw
  • Я предполагаю, что часы SFML, вероятно, используют часы time.h
  • Из моего тестирования проверки max_updates не имеют ничего общего с этой проблемой (я не думаю, что они вызывают проблему)

Тот факт, что я видел это с несколькими разными таймерами, заставляет меня поверить, что что-то не так с моей реализацией или моей системой. Этот пример был запущен в VS, но я видел его также в отдельном выпуске exe. Игра с частотой обновления или объемом работы, выполненной в розыгрыше, может помочь вам показать это.


После тестирования моих фоновых процессов я заметил странную корреляцию. Эта проблема пропуска возникает только тогда, когда веб-плеер Spotify открыт в Chrome и возникает раз в секунду или около того.

Я нашел этот пост, который может быть связан: https://community.spotify.com/t5/Other-Partners-Web-Player-etc/Web-Player-on-Chrome-causes-lag-stutter/ тд-п/4587103

11
S. Turnage

Возможно, есть что-то, чего я не понимаю в типичных приложениях? Нужно ли Windows периодически перезагружать циклы процессора?

Да, конечно. Windows одновременно запускает множество процессов. Теперь ваше приложение приходит и выполняет то, что по сути является занятым циклом вращения. В какой-то момент ОС, скорее всего, будет расставлять приоритеты дольше, чем вы ожидаете, потому что это просто выглядит как долгий расчет, и ОС должна предоставлять другим процессам достаточную долю процессорного времени.

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

Однако у меня есть опыт работы с аудио в реальном времени (и видео в этом отношении), работающим в циклах, которые превышают 1000 обновлений в секунду. Вы можете улучшить свою долю времени игрового цикла, установив приоритет потока в THREAD_PRIORITY_HIGHEST или THREAD_PRIORITY_TIME_CRITICAL (см. SetThreadPriority ).

Для того, чтобы это было эффективным, вы также должны быть хорошим приложением и периодически выполнять какое-то ожидание. Ожидание позволяет ОС выполнить необходимое переключение задач для обслуживания других процессов (некоторые из которых также будут иметь высокий приоритет и часто будут выше, чем вы сможете использовать в качестве приложения пользовательского пространства).

Очевидное место для ожидания - до вашего следующего цикла розыгрыша. Вместо того, чтобы крутить таймер со 100% использованием ядра, просто посчитайте, как долго вы готовы ждать и позвоните std::this_thread::sleep_for . Помните, что единственной гарантией является то, что сон будет для как минимум указанной вами суммы. Это абсолютно может и будет больше, чем это. Но я рекомендую вам начать с экспериментов.

8
paddy

В дополнение к ответу @ paddy я рекомендую вам посмотреть фиксированные временные шаги . Если это не стоит того, чтобы его реализовать, вы также должны заметить, что SFML имеет Window.setFramerateLimit() . Это не очень точно, но большинству простых игр не нужна значительная точность.

3
bruglesco

Я использовал вращающуюся петлю плюс выход для 1 KHz контрольных петель с хорошими результатами, но ожидаю некоторого пропуска по срокам (один раз в тысячи циклов также длительное время сна).

1
PeppeDx

Изначально это выглядело так, как будто проблема была вызвана моим антивирусом, но я думаю, что на самом деле я сузил его, чтобы из-за того, что веб-плеер был открыт и вызывал всплеск производительности 1/сек. Я не уверен, почему это будет.

0
S. Turnage