问题描述
我有一个简单的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;
}
```