opengl C++如何进行精确的帧率限制?

2skhul33  于 2022-11-23  发布在  其他
关注(0)|答案(6)|浏览(309)

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

trnvg8h3

trnvg8h31#

是的,睡眠通常是不准确的。这就是为什么你睡眠的时间比完成一帧所需的实际时间要短。例如,如果你需要5毫秒来完成一帧,那么睡眠4毫秒。睡眠后,只需对帧的其余部分进行旋转锁定。类似于

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

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

rur96b6h

rur96b6h2#

您可以在开始时记录时间点,添加一个固定的持续时间,然后休眠,直到计算出的时间点出现在每个循环的结束(或开始)。

#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 world\n";
        fr.sleep();                   // let it sleep any time remaining
    }
}
lawou6xi

lawou6xi3#

被接受的答案听起来真的很糟糕。它不会准确,而且会烧掉CPU!
Thread.Sleep是不准确的,因为你必须告诉它是准确的(默认情况下大约是15 ms准确-意味着如果你告诉它休眠1 ms,它可以休眠15 ms)。
您可以通过Win32 API调用timeBeginPeriod和timeEndPeriod函数来完成此操作。
有关详细信息,请查看MSDN-〉https://learn.microsoft.com/en-us/windows/win32/api/timeapi/nf-timeapi-timebeginperiod
(我会对已接受的答案进行评论,但仍没有50点声望)

nhn9ugyo

nhn9ugyo4#

在实现任何基于调度程序休眠的等待时,请务必小心。
大多数操作系统调度程序都有较高的等待时间,没有明确定义的时间间隔或信号来使线程返回到准备运行状态。
睡眠本身并不是不准确的,你只是在错误地处理问题。如果你可以访问像DXGI的Waitable Swapchain这样的东西,你就可以同步到DWM的当前队列,并获得真正可靠的低延迟定时。
你不需要忙等待来获得准确的计时,一个可等待的计时器会给予你一个同步对象来重新安排你的线程。
无论你做什么,都不要在产品代码中使用当前接受的答案。这里有一个你要避免的边缘情况,即Sleep(0)不会为更高优先级的线程提供CPU时间。我见过很多游戏开发人员尝试Sleep(0),它会给你带来很大的问题。

7kqas0il

7kqas0il5#

使用计时器。
某些操作系统可以提供特殊功能。例如,对于Windows,您可以使用SetTimer并处理其WM_TIMER消息。
然后计算计时器的频率。100 fps表示计时器必须每0.01秒触发一个事件。
您可以在这个计时器事件的事件行程常式中进行呈现。
如果渲染比所需频率慢,则使用同步标志OpenGL sync,并且如果之前的渲染未完成,则丢弃定时器事件。

drnojrws

drnojrws6#

您可以将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.
    }
}

相关问题