Steady_Clock在主游戏循环中的更新之间跳过

时间:2018-12-12 02:58:08

标签: c++ game-engine clock game-development game-loop

在尝试在SFML中建立可靠的游戏循环的过程中,我遇到了这个似乎无法弄清的问题。我能够剥离所有SFML代码,并且仍然在time.h中看到clock()的问题。然后我走得更远,仍然使用std::chrono::steady_clock看到问题。

问题: 在某种程度上,我一直看到更新之间能够完成的工作量有所跳过。每次更新应花费1/60秒的时间,其余时间则花在Draw()上以尽可能多地完成绘制。 有时,平局的金额下降到0或1没有明显的原因。这以明显的口吃的形式上升到实际应用中。除了“跳过”以外,完成的抽签次数非常一致。

这是一张图片(注意更新时间的增加和绘制次数的下降): Console output of the issue

某些代码:

#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是否需要经常劫持CPU周期吗?
  • 我已经在使用稳定的时钟,时钟以及充实的SFML应用程序中看到了此问题,该应用程序在更新和绘制期间完成了工作
  • 我认为SFML时钟可能使用time.h时钟
  • 根据我的测试,max_updates检查与该问题无关(我认为它们不是引起问题的原因)

我用几个不同的计时器看到了这一事实,这使我相信我的实现或系统有问题。该示例在VS中运行,但我在独立发行版exe中也看到了。发挥更新率或抽奖中完成的工作量可能会帮助您显示出来。


测试完我的后台程序后,我发现一个奇怪的相关性。仅当在chrome中打开Spotify网络播放器并且大约每秒发生一次时,才会发生此跳过问题。

我发现这则帖子可能与以下内容有关: https://community.spotify.com/t5/Other-Partners-Web-Player-etc/Web-Player-on-Chrome-causes-lag-stutter/td-p/4587103

4 个答案:

答案 0 :(得分:8)

  

关于典型的应用程序,也许我不了解吗? Windows是否需要经常劫持CPU周期吗?

是的,绝对如此。 Windows一次运行很多进程。现在,您的应用程序出现并执行本质上是繁忙的自旋循环。在某个时候,操作系统可能会取消优先级,这比您预期的要长,因为它看起来像是很长的计算,并且操作系统需要给其他进程相当的CPU时间份额。

通常,您不应该依赖于每秒调用绘制例程的确切次数,并且游戏的主时钟应该能够应对跳帧。我对SFML不熟悉,因此无法对此发表评论。

但是,我确实有实时音频(和相关视频)以每秒超过1000次更新的循环运行的经验。您可以通过将线程优先级设置为THREAD_PRIORITY_HIGHEST或THREAD_PRIORITY_TIME_CRITICAL(请参见SetThreadPriority)来改善游戏循环时间份额。

要使此方法有效,您还应该成为行为良好的应用程序,并定期执行某种等待。等待使操作系统可以执行必要的任务切换,以服务其他进程(其中几个优先级也很高,并且通常比您作为用户空间应用程序所能承受的要高)。

一个明显的等待位置是在下一个绘制循环之前。无需计算具有100%内核利用率的计时器,只需简单地计算您准备等待并调用std::this_thread::sleep_for的时间。请记住,唯一的保证就是睡眠时间至少您指定的时间。它绝对可以而且将不止于此。但我建议您从那里开始并做一些实验。

答案 1 :(得分:3)

除了@paddy的答案外,我建议您调查fixed timesteps。如果这样做不值得麻烦,那么您还应该注意SFML具有Window.setFramerateLimit()。它不是很精确,但是大多数简单的游戏并不需要很高的精度。

答案 2 :(得分:1)

我在1 KHz控制环路上使用了自旋环路加良率,但效果很好,但是可能会错过最后期限(数千个周期一次,还需要很长的睡眠时间)。

答案 3 :(得分:0)

最初看似问题是由我的防病毒软件引起的,但我认为我实际上已经缩小了范围,以发现Web播放器处于打开状态并导致性能峰值提高1 /秒。我不确定为什么会这样。