SDL / OpenGL游戏在144Hz屏幕上运行得太快;不能使用vsync

时间:2016-11-28 22:54:23

标签: c++ sdl game-engine vsync

这就是我处理游戏循环的方式:

while (running) {
    diff = duration_cast<milliseconds>(end - start).count();
    start = clock::now();

    dt = diff / (16.0);

    handleInput(); // get input
    update(dt); // game logic
    render(); // render game

    SDL_GL_SwapWindow(window); // swap frame buffer

    end = clock::now();
}

它是一个固定的时间步骤游戏锁定到60FPS(它是一个重新制作的SNES游戏模拟)但​​它在我的144hz屏幕上运行144个时间步,使它太快。 Vsync无法解决这个问题,那么可以做什么呢?

2 个答案:

答案 0 :(得分:2)

以下是如何实施游戏循环的快速示例:

int32_t tickInteval = 1000/FPS; // frequency in Hz to period in ms
uint32_t lastUpdateTime = 0;
int32_t deltaTime = 0;
while (running) { // running condition
    uint32_t currentTime = SDL_GetTicks();
    deltaTime = currentTime - lastUpdateTime;

    int32_t timeToSleep = tickInteval - deltaTime;
    if(timeToSleep > 0)
    {
        SDL_Delay(timeToSleep); // energy saving
    }

    update(deltaTime); // game logic
    lastUpdateTime = currentTime;
}

我建议仔细研究这个话题。

UPD。
有人可能会担心uint32_t溢出。是的,它会溢出。经过近两个月的不间断运行(确切地说是49.7天)。那会发生什么? currentTime将是一个非常小的正整数,lastUpdateTime将是一个非常大的正整数。但无论如何,减去两个都不会溢出。此外,如果差异不适合int32_t,它将被包裹在UINT_MAX + 1的模数周围,从而产生一个小的正整数,这将是这两个值不同的滴答的确切数量(关于无符号溢出一个)。

答案 1 :(得分:1)

SDL_Delay()SDL_GetTicks()解决方案由于其不精确性而不太理想。


我们可以进行热循环:

while (1)
  if stopwatch.time() < 16666 // in usec
    break
  stopwatch.reset()
  input()
  update()
  render()

但这是不希望的,因为它会耗尽可能放在其他地方或节省能源的CPU周期。


我们只能动作偶数帧

while (1)
  if frameCount = 1
    frameCount = 0
  else
    frameCount++
    input()
    update()
    render()
  waitForVSync // aka SDL_GL_SwapWindow(window)

但这只能使我们达到72 fps。


如果我们需要尽可能接近60 FPS,则最好使用以上两种方法的混合:在偶数帧上进行热循环,直到达到16.666 ms。

while (1)
  if frameCount = 1
    frameCount = 0
  else
    while stopWatch.time() < 16666 // in usec
      break
    stopwatch.reset()
    frameCount++
    input()
    update()
    render()
  waitForVSync // aka SDL_GL_SwapWindow(window)

这有一个好处:您不必跳过奇数帧,可以将它们用于各种用途。也许更新奇数帧,渲染偶数?在奇数帧上应用视频滤镜?很多可能性。

注意:您可能应该整体看一下帧时间。仅当帧速率至少为120 fps时,此解决方案才有效。当帧速率降到20秒以下,并且您想要比OS线程提供的精度更高时,热循环是您的最佳选择。


另一种选择是利用OS调度,这往往比OS线程休眠更准确。但是,仍然不能保证所有系统的准确性。