C++ 如何设置精确的帧率限制?

C++ How to make precise frame rate limit?

本文关键字:帧率 何设置 设置 C++      更新时间:2023-10-16

我正在尝试使用C++创建游戏,我想为 fps 创建限制,但我总是得到比我想要的更多或更少的 fps。当我查看具有 fps 限制的游戏时,它总是精确的帧速率。已尝试使用Sleep() std::this_thread::sleep_for(sleep_until).例如,Sleep(0.01-deltaTime)获得 100 fps,但最终得到 +-90fps。 当任何睡眠不精确时,这些游戏如何如此精确地处理 fps? 我知道我可以使用无限循环来检查时间是否过去,但它正在使用 CPU 的全部功能,但我想在没有 VSync 的情况下通过此限制降低 CPU 使用率。

您可以在开始时记录时间点,为其添加固定持续时间并休眠,直到计算的时间点出现在每个循环的结束(或开始(处。例:

#include <chrono>
#include <iostream>
#include <ratio>
#include <thread>
template<std::intmax_t FPS>
class frame_rater {
public:
frame_rater() :                 // initialize the object keeping the pace
time_between_frames{1},     // std::ratio<1, FPS> seconds
tp{std::chrono::steady_clock::now()}
{}
void sleep() {
// add to time point
tp += time_between_frames;
// and sleep until that time point
std::this_thread::sleep_until(tp);
}
private:
// a duration with a length of 1/FPS seconds
std::chrono::duration<double, std::ratio<1, FPS>> time_between_frames;
// the time point we'll add to in every loop
std::chrono::time_point<std::chrono::steady_clock, decltype(time_between_frames)> tp;
};
// this should print ~10 times per second pretty accurately
int main() {
frame_rater<10> fr; // 10 FPS
while(true) {
std::cout << "Hello worldn";
fr.sleep();                   // let it sleep any time remaining
}
}

是的,睡眠通常是不准确的。这就是为什么你的睡眠时间少于完成帧所需的实际时间。例如,如果还需要 5 毫秒才能完成帧,则睡眠 4 毫秒。睡眠后,只需对帧的其余部分进行旋转锁定即可。类似的东西

float TimeRemaining = NextFrameTime - GetCurrentTime();
Sleep(ConvertToMilliseconds(TimeRemaining) - 1);
while (GetCurrentTime() < NextFrameTime) {};

编辑:如另一个答案所述,应该调用timeBeginPeriod((以提高Sleep((的准确性。另外,根据我所读到的内容,当您的进程退出时,如果您在此之前没有退出,Windows将自动调用timeEndPeriod((。

接受的答案听起来真的很糟糕。它不会准确,它会烧毁 CPU!

Thread.Sleep 不准确,因为您必须告诉它是准确的(默认情况下大约 15 毫秒准确 - 这意味着如果你告诉它睡眠 1 毫秒,它可以睡眠 15 毫秒(。

您可以使用Win32 API调用timeBeginPeriod和timeEndPeriod函数来执行此操作。

查看 MSDN 以获取更多详细信息 -> https://learn.microsoft.com/en-us/windows/win32/api/timeapi/nf-timeapi-timebeginperiod

(我会评论接受的答案,但仍然没有 50 的声誉(

在实现任何基于调度程序睡眠的等待时要非常小心。

大多数操作系统计划程序具有更高的延迟周转时间,等待时间没有明确定义的间隔或信号,无法将线程恢复到准备运行状态。

睡眠本身并不是不准确的,你只是在处理问题时完全错了。如果您可以访问 DXGI 的可等待交换链之类的内容,则可以同步到 DWM 的当前队列并获得真正可靠的低延迟计时。

您无需忙于等待即可获得准确的计时,可等待计时器将为您提供一个同步对象来重新安排线程。

无论您做什么,都不要在生产代码中使用当前接受的答案。这里有一个你想要避免的边缘情况,其中睡眠 (0( 不会将 CPU 时间让给更高优先级的线程。我见过很多游戏开发者尝试睡眠(0(,它会给你带来重大问题。

使用计时器。
某些操作系统可以提供特殊功能。例如,对于Windows,您可以使用SetTimer 并处理其WM_TIMER消息。

然后计算计时器的频率。 100 fps 表示计时器必须每 0.01 秒触发一个事件。

在此计时器事件的事件处理程序中,您可以进行渲染。

如果渲染速度慢于所需频率,请使用 syncro 标志 OpenGL sync,如果之前的渲染未完成,则丢弃计时器事件。

您可以将const fps 变量设置为所需的帧速率,然后如果上次更新后的经过时间等于或大于 1/desired_fps,则可以更新游戏。 这可能会起作用。 例:

const /*or constexpr*/ int fps{60};
// then at update loop.
while(running)
{
// update the game timer.
timer->update();
// check for any events.
if(timer->ElapsedTime() >= 1 / fps)
{
// do your updates and THEN renderer.
}
}