在循环 C++ 中限制 fps

Limit fps in loop c++

我有一个简单的 fps 限制器,它可以工作:
Clock.h

#include <chrono>
using namespace std::chrono; 

class Clock 
{ 
    high_resolution_clock::time_point startTime;
public:
    Clock(); 
    duration<double> restart();
};

Clock.cpp


#include "Clock.h"

Clock::Clock()
: startTime(high_resolution_clock::now())
{

}

duration<double> Clock::restart()
{
    high_resolution_clock::time_point now = high_resolution_clock::now();
    duration<double> elapsed = now - startTime;
    startTime = now;

    return elapsed;
}

main.cpp

    duration<double> stepInterval(1.f / 60.f);
    duration<double> deltaTime(0);

    Clock clock;
    while(isRunning)
    {
        deltaTime += clock.restart();
        if(deltaTime > stepInterval)
        {
            std::cout << deltaTime.count() * 1000 << std::endl;
            deltaTime = seconds(0);
        }
    }

但在这种情况下 CPU 负载很高。我用 std::this_thread::sleep_for 尝试了另一种解决方案,但它们不适用于小间隔(如 16 毫秒)。关于实施 std::this_thread::sleep_for 有什么想法吗?

此代码可能对您有所帮助。它对我来说效果很好(精度高):


#include <iostream>
#include <thread>
#include <chrono>
    
int main()
{
    std::chrono::high_resolution_clock::time_point prev = 
        std::chrono::high_resolution_clock::now();
    std::chrono::high_resolution_clock::time_point current = 
        std::chrono::high_resolution_clock::now();
    
    int i = 0; 
    
    while (true) {
    
        current = std::chrono::high_resolution_clock::now();
    
        if (std::chrono::duration_cast<std::chrono::microseconds>(current - prev).count() >= 16000) {
            prev = current;
            i++;
            std::cout << i << std::endl;
        } else {
            std::this_thread::sleep_for(std::chrono::microseconds(1));
        }  
    
    }  
    
    return 0; 
}
```    ‍‍‍‍‍‍‍
  • 不要使用 sleep_for。请改用 sleep_until。这将使您从所需帧速率的漂移平均为零。这是通过休眠 直到 当前帧的开始加上所需的帧持续时间来完成的。

  • 使用 system_clock 可以让您的帧速率在很长一段时间内保持准确。没有时钟是完美的。但是 system_clock 偶尔会被少量调整到正确的时间。这将影响您的帧速率在电影长度内符合所需的速率。如果您担心具有管理员权限的进程按总量调整系统时钟,请不要遵循此建议。在这种情况下,请改用 steady_clock

  • 创建一个 1/60 秒的持续时间类型以消除浮点舍入误差。

  • 消除代码中的转换常量,例如 1000,以减少出现逻辑错误的可能性。

  • 在你的 sleep_until 中,早点起床然后 busy-loop 直到所需的时间。你必须提前试验多少。太早你会消耗太多 CPU(和电池)。太少了,而且sleep_until偶尔会在CPU高负载的时候醒得太晚。

总的来说,这可能看起来像:

template <class Clock, class Duration>
void
sleep_until(std::chrono::time_point<Clock, Duration> tp)
{
    using namespace std::chrono;
    std::this_thread::sleep_until(tp - 10us);
    while (tp >= Clock::now())
        ;
}

int
main()
{
    using namespace std;
    using namespace std::chrono;

    using framerate = duration<int, ratio<1, 60>>;
    auto tp = system_clock::now() + framerate{1};

    while (isRunning)
    {
        // do drawing
        ::sleep_until(tp);
        tp += framerate{1};
    }
}

这里有一个更详细的想法草图,其中包括“绘制”每帧的当前帧速率和平均帧速率。它使用 C++17 的 chrono::round 函数和这个 free, open-source C++20 chrono preview library 用于“绘图”。

#include "date/date.h"
#include <chrono>
#include <iostream>
#include <thread>

template <class Clock, class Duration>
void
sleep_until(std::chrono::time_point<Clock, Duration> tp)
{
    using namespace std::chrono;
    std::this_thread::sleep_until(tp - 10us);
    while (tp >= Clock::now())
        ;
}

int
main()
{
    using namespace std;
    using namespace std::chrono;

    using framerate = duration<int, ratio<1, 60>>;
    auto prev = system_clock::now();
    auto next = prev + framerate{1};
    int N = 0;
    system_clock::duration sum{0};

    while (true)
    {
        ::sleep_until(next);
        next += framerate{1};

        // do drawing
        using namespace date;
        auto now = system_clock::now();
        sum += now - prev;
        ++N;
        cerr << "This frame: " << round<milliseconds>(now-prev)
             << "  average: " << round<milliseconds>(sum/N) << '\n';
        prev = now;
    }
}

我的输出:

...
This frame: 16ms  average: 17ms
This frame: 15ms  average: 17ms
This frame: 19ms  average: 17ms
This frame: 16ms  average: 17ms
This frame: 17ms  average: 17ms
This frame: 16ms  average: 17ms
This frame: 17ms  average: 17ms
This frame: 17ms  average: 17ms
This frame: 16ms  average: 17ms
This frame: 16ms  average: 17ms
This frame: 16ms  average: 17ms
This frame: 18ms  average: 17ms
This frame: 16ms  average: 17ms