简体   繁体   English

使用 std::chrono 限制 fps

[英]Limiting fps with std::chrono

std::chrono::system_clock::time_point m_BeginFrame = std::chrono::system_clock::now();
std::chrono::system_clock::time_point m_EndFrame = std::chrono::system_clock::now();
std::chrono::nanoseconds m_WorkTime = std::chrono::nanoseconds::zero();
std::chrono::nanoseconds m_WaitTime = std::chrono::nanoseconds::zero();
auto invFpsLimit = std::chrono::nanoseconds(1e9 / fpsLimit());

// main loop
while (!glfwWindowShouldClose(m_pScreen->glfwWindow()))
{
    m_WaitTime = m_BeginFrame - m_EndFrame;
    m_EndFrame = std::chrono::system_clock::now();
    m_WorkTime = m_EndFrame - m_BeginFrame;

    // if need sleep
    if (m_WorkTime < invFpsLimit)
    {
        std::this_thread::sleep_until(m_BeginFrame + invFpsLimit);
        m_DeltaTime = invFpsLimit;
    }
    else
    {
       m_DeltaTime = m_WorkTime;
    }

    m_BeginFrame = std::chrono::system_clock::now();

    TRACE("   %f   %u   %u   %u",
            ((float)1e9 / (float)(m_WaitTime.count() + m_WorkTime.count())),
            m_WorkTime.count(),
            m_WaitTime.count(),
            invFpsLimit.count());


    // think and opengl stuff...
}

I'm trying to limit fps with this code, but Fraps showes me about 56-57 value我正在尝试使用此代码限制 fps,但 Fraps 向我显示了大约 56-57 的值

58.848454   3175500   13817300   16666666
55.259304   3314200   14782300   16666666
58.923698   2321700   14649400   16666666
56.200928   2167400   15625900   16666666
52.774071   3188200   15760500   16666666
50.600887   4899700   14862800   16666666
65.011055   2347500   13034500   16666666
54.966499   2611700   15581200   16666666
55.605911   2653400   15330300   16666666
56.476437   2386100   15320400   16666666
55.280689   2581400   15508100   16666666
56.437550   2355400   15363300   16666666
47.170700   5535900   15663700   16666666
64.713608   3039700   12413000   16666666
56.136570   2941100   14872600   16666666
53.444496   3624200   15086800   16666666
60.074493   2397500   14248500   16666666
51.737339   3777100   15551300   16666666
59.369026   3665800   13178000   16666666
59.355282   3543900   13303800   16666666
54.243759   3961000   14474300   16666666
45.764706   7823500   14027400   16666666
61.242237   6239400   10089200   16666666
73.396652   2013800   11610800   16666666
50.531849   3824000   15965500   16666666
62.895454   2796000   13103400   16666666
60.611572   2360500   14138000   16666666
56.074242   2241700   15591800   16666666
55.143208   2454800   15679800   16666666

I have tried to change clock to system_clock, use sleep_for instead of sleep_untill, nothing works我试图将时钟更改为 system_clock,使用 sleep_for 而不是 sleep_untill,没有任何效果
Is there some specific thing I don't khow about std clocks or working code I can't see?有什么我不知道的关于标准时钟或我看不到的工作代码的具体事情吗?
What is stealing my fps?什么偷了我的fps?

One can do better by updating m_BeginFrame and m_EndFrame by invFpsLimit instead of by system_clock::now() .通过使用invFpsLimit而不是system_clock::now()更新m_BeginFramem_EndFrame可以做得更好。 This might look something like:这可能类似于:

#include <iostream>
#include <thread>

int fpsLimit() {return 60;}

int
main()
{
    using namespace std::chrono;
    using dsec = duration<double>;
    auto invFpsLimit = duration_cast<system_clock::duration>(dsec{1./fpsLimit()});
    auto m_BeginFrame = system_clock::now();
    auto m_EndFrame = m_BeginFrame + invFpsLimit;
    unsigned frame_count_per_second = 0;
    auto prev_time_in_seconds = time_point_cast<seconds>(m_BeginFrame);
    while (true)
    {
        // Do drawing work ...

        // This part is just measuring if we're keeping the frame rate.
        // It is not necessary to keep the frame rate.
        auto time_in_seconds = time_point_cast<seconds>(system_clock::now());
        ++frame_count_per_second;
        if (time_in_seconds > prev_time_in_seconds)
        {
            std::cerr << frame_count_per_second << " frames per second\n";
            frame_count_per_second = 0;
            prev_time_in_seconds = time_in_seconds;
        }

        // This part keeps the frame rate.
        std::this_thread::sleep_until(m_EndFrame);
        m_BeginFrame = m_EndFrame;
        m_EndFrame = m_BeginFrame + invFpsLimit;
    }
}

In C++17 and forward, I recommend constructing invFpsLimit with round instead of duration_cast for slightly better accuracy:在 C++17 及以后的版本中,我建议使用round而不是duration_cast构造invFpsLimit以获得稍微更好的准确性:

auto invFpsLimit = round<system_clock::duration>(dsec{1./fpsLimit()});

要获得增量,您可以执行以下操作:

unsgined deltaTime = 1000.0f / frame_count_per_fps; // In milliseconds

The documentation of sleep_until says: sleep_until文档说:

The function also may block for longer than until after sleep_time has been reached due to scheduling or resource contention delays.由于调度或资源争用延迟,该函数也可能会阻塞更长的时间,直到到达 sleep_time 之后

If you really need exact fps use either vSync, or replace the if block in your code with the following loop which continually does nothing until the frame time is used up:如果您确实需要精确的 fps,请使用 vSync,或使用以下循环替换代码中的 if 块,该循环在帧时间用完之前会持续执行任何操作:

while (m_WorkTime < invFpsLimit)
{
    m_EndFrame = std::chrono::system_clock::now();
    m_WorkTime = m_EndFrame - m_BeginFrame;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM