[英]Limit fps in loop c++
我有一個簡單的 fps 限制器,它可以工作:
時鍾.h
#include <chrono>
using namespace std::chrono;
class Clock
{
high_resolution_clock::time_point startTime;
public:
Clock();
duration<double> restart();
};
時鍾.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;
}
主文件
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 的任何想法?
不要使用sleep_for
。 請改用sleep_until
。 這將使您從所需幀速率的漂移平均為零。 這是通過休眠直到電流開始加上所需的幀持續時間來完成的。
使用system_clock
以便您的幀速率在很長一段時間內保持准確。 沒有時鍾是完美的。 但是system_clock
偶爾會被少量調整到正確的時間。 這將影響您的幀速率在電影長度上與所需速率一致。 如果您擔心具有管理員權限的進程會按總金額調整系統時鍾,請不要遵循此建議。 在這種情況下,請改用steady_clock
。
創建 1/60 s 持續時間類型以消除浮點舍入誤差。
消除代碼中的轉換常數,如1000
,以減少出現邏輯錯誤的可能性。
在你的sleep_until
中,早點醒來,然后忙循環直到所需的時間。 您將不得不嘗試提前多長時間。 太早了,你會燒掉太多的 CPU(和電池)。 太少了,而且當 CPU 處於高負載下時, sleep_until
偶爾會喚醒太晚。
總而言之,這可能看起來像:
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 chrono::round
function 和這個免費的開源 C++20 chrono 預覽庫用於“繪圖”。
#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;
}
}
Output 對我來說:
...
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;
}
```
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.