C# 如何在非完美60Hz(59.92Hz)显示器上安装60fps环路?

C# 如何在非完美60Hz(59.92Hz)显示器上安装60fps环路?,c#,game-loop,vsync,C#,Game Loop,Vsync,我刚开始学习游戏编程,我选择使用固定的timestep游戏循环,因为它简单易懂 基本上是这样的: while(游戏正在运行){ 输入(); 更新(); render(); } 启用VSync时,它以我的显示速度运行,但我想限制它以60 FPS的速度运行,即使在刷新率更高的监视器上也是如此,因此我添加了帧跳过和FPS限制器: static void Main() { long frame_count = 0; long tick_frame_end; Stopwatch

我刚开始学习游戏编程,我选择使用固定的timestep游戏循环,因为它简单易懂

基本上是这样的:

while(游戏正在运行){
输入();
更新();
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
}
static void Main()
{
长帧计数=0;
长勾选框结束;
秒表秒表=新秒表();
WaitForVSync();
stopwatch.Start();//希望与显示器同步
同时(游戏正在运行){
帧计数++;
tick\u frame\u end=GetEndTick(frame\u count);
如果(stopwatch.ElapsedTicks>勾选帧结束){//落后
continue;//跳过此帧
}
输入();
更新(勾选框结束);
//render();//启用VSync时在此处渲染
while(stopwatch.ElapsedTicks
当VSync关闭时,它可以以稳定的30、60、120(或两者之间的任何值)FPS运行。我对结果很满意,但当我重新打开VSync(并将FPS设置为60)时,我注意到有很多帧跳转

当VSync关闭时,我可以稳定地运行120FPS,没有一个帧丢失,但是当运行60FPS VSync时,我有一个显著的帧丢失数。我花了一段时间才弄清原因:

我的显示器我以为是以60Hz的频率运行,但实际上是以59.920Hz的频率运行

随着时间的推移,我的内部帧时间和监视器帧时间将变得越来越不同步,导致许多意外的帧丢失。这完全破坏了我的
GetEndTick()
函数,它基本上破坏了一切:跳帧逻辑、fps限制器,最重要的是
update(tick\u frame\u end)
被破坏为

因此,问题是:

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

在我上面的代码中,结束时间是通过假设每秒有60个偶数帧来计算的。如果这是真的,我的内部帧时间可能不会完全与显示帧时间对齐,但仍然会同步,这是非常有用的

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

我尝试在循环开始时(或在最后一帧渲染之后)记录时间,并向循环中添加1帧时间-不起作用,因为记录的时间由于系统负载、后台进程和许多其他因素而不可靠。我还需要一个新的方法来确定哪一帧是当前帧

2。如果(1.)不可能,如何修复游戏循环?


我已经读过臭名昭著的修正你的时间步!格伦·费德勒,但它是关于游戏物理而非图形的,并没有真正为我提供第一个问题的答案。

这是错误的方法,
Thread.Sleep(1)
将永远不会有您想要的分辨率,如果您使用的是游戏引擎,它将已经内置了这个,并且将只是一个setting@TheGeneral我意识到线程。睡眠可能需要更长的时间。在我使用Spinwait而不是Thread的实际代码中,这不是原因(否则我一开始就不会让120FPS工作)。睡眠,我会编辑问题以避免混淆。可能是59.94,这是一个古老的电视标准(60*1000/1001)。启用vsync时,您需要跳过繁忙的while等待循环。现在,您希望它实际上延迟很少,但是如果render()比预期的慢一点,或者计时有点偏离,那么它将延迟整个帧时间,您将不可避免地落后。您不需要它,因为render()已经提供了延迟,这要感谢它等待vsync。