如何在不完美的60Hz(59.92Hz)显示屏上修复60 fps循环?

我刚开始学习游戏编程,我选择使用固定时间步的游戏循环,因为它简单明了且易于理解。

基本上是这样的:

    while (game_is_running) {
        input();
        update();
        render();
    }

启用VSync后,它以我的显示速度运行,但我想限制它甚至在更高刷新率的显示器上也能以60 FPS的速度运行,因此我添加了跳帧和fps限制器:

static void Main()
{
    long frame_count = 0;
    long tick_frame_end;

    Stopwatch stopwatch = new Stopwatch();
    WaitForVSync();
    stopwatch.Start(); // hoping to get in sync with display

    while (game_is_running) {
        frame_count++;
        tick_frame_end = GetEndTick(frame_count);

        if (stopwatch.ElapsedTicks > tick_frame_end) { // falling behind
            continue; // skip this frame
        }

        input();
        update(tick_frame_end);
        // render(); // render here when VSync is enabled

        while (stopwatch.ElapsedTicks < tick_frame_end) {
            // Busy waiting,in real code I used spinwait to avoid 100% cpu usage
        }
        render(); // I moved it here because I turned off VSync and I want render to happen at a constant rate
    }
}

static long GetEndTick(long frame_count) {
    return (long)((double)frame_count * Stopwatch.Frequency / 60); // Target FPS: 60
}

关闭VSync后,它可以以稳定的30、60、120(或其间的任何值)FPS运行。我对结果感到满意,但是当我重新打开VSync(并将FPS设置为60)时,我注意到跳帧很多。

在关闭VSync且没有丢掉一帧的情况下,我可以稳定地运行120FPS,但是在运行60FPS VSync时,我丢掉了很多的帧。我花了一段时间才找出原因:

我以为以60Hz运行的显示器实际上以大约59.920Hz 运行(已通过https://www.displayhz.com/测试)

随着时间的流逝,我的内部帧时间和监视器帧时间将变得越来越不同步,从而导致许多意外的帧丢失。这完全破坏了我的GetEndTick()函数,该函数基本上破坏了一切:跳帧逻辑,fps限制器,最重要的是update(tick_frame_end)被破坏了。

所以问题是:

1。如何获得当前帧的结束时间?

在我上面的代码中,结束时间的计算是假设每秒有60个偶数帧。如果是这样,我的内部帧时间可能无法与显示帧时间完美对齐,但仍将保持同步,这很有用。

(所有内容都是基于帧结束的时间戳计算的,其想法是我根据帧结束的时间生成图像,该时间恰好是下一帧显示在屏幕上的那一刻。)

我试图记录循环开始时的时间(或紧接在最后一帧渲染之后),并为其添加一帧时间-不起作用,因为记录的时间不正确由于系统负载,后台进程和许多其他原因,因此非常可靠。另外,我还需要一种新方法来确定哪个帧是当前帧。

2。如果(1.)不可能,该如何解决我的游戏循环?

我已经读过Glenn Fiedler臭名昭著的 Fix Your Timestep!,但这是关于游戏物理而非图形的,并没有真正为我回答第一个问题。

hyqgxnxu 回答:如何在不完美的60Hz(59.92Hz)显示屏上修复60 fps循环?

暂时没有好的解决方案,如果你有好的解决方案,请发邮件至:iooj@foxmail.com
本文链接:https://www.f2er.com/3169308.html

大家都在问