在循环C ++中限制fps

问题描述

我有一个简单的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 ms)不能工作。关于实现std :: this_thread :: sleep_for的任何想法?

解决方法

  • 请勿使用sleep_for。请改用sleep_until。这将平均使您从所需的帧频漂移到零。这是通过使直到休眠当前的开始加上所需的帧持续时间来完成的。

  • 使用system_clock,以便长时间保持帧速率准确。没有时钟是完美的。但是system_clock有时会被少量调整为正确的时间。这会影响您的帧速率在电影的整个长度上都符合所需的帧速率。如果您担心具有管理员特权的进程会通过总量调整系统时钟,请不要遵循此建议。在这种情况下,请使用steady_clock

  • 创建1/60 s持续时间类型以消除浮点舍入错误。

  • 在代码中消除诸如1000之类的转换常量,以减少发生逻辑错误的可能性。

  • 在您的sleep_until中,提早醒来,然后忙个循环直到所需的时间。您必须尝试多少时间。太早了,您消耗了太多的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,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
,

此代码可以为您提供帮助。对我来说效果很好(精度很高):


#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; 
}
```    ‍‍‍‍‍‍‍

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...