代码之家  ›  专栏  ›  技术社区  ›  Geoffrey

防止OpenGL缓冲帧

  •  5
  • Geoffrey  · 技术社区  · 7 年前

    我正在编写一个程序,需要极低延迟纹理到屏幕流(低于10ms),我已经使用 GL_ARB_buffer_storage 这对流媒体非常有效,vsync可以防止撕裂。

    然而,我发现英伟达管道在阻塞前调用交换缓冲区时会缓冲2到8帧,我需要防止这种情况。

    我做了以下工作:

    uint64_t detectPresentTime()
    {
      // warm up first as the GPU driver may have multiple buffers
      for(int i = 0; i < 10; ++i)
        glxSwapBuffers(state.renderer);
    
      // time 10 iterations and compute the average
      const uint64_t start = microtime();
      for(int i = 0; i < 10; ++i)
        glxSwapBuffers(state.renderer);
      const uint64_t t = (microtime() - start) / 10; 
    
      // ensure all buffers are flushed
      glFinish();
    
      DEBUG_INFO("detected: %lu (%f Hz)", t, 1000000.0f / t); 
      return t;
    }
    

    然后在绘制线程中,我执行以下操作:

    uint64_t presentTime = detectPresentTime();
    if (presentTime > 1000)
      presentTime -= 1000;
    
    while(running)
    {
      const uint64_t start = microtime();
      glClear();
    
      // copy the texture to the screen
    
      glxSwapBuffers();
    
      const uint64_t delta = microtime() - start;
      if (delta < presentTime)
      {
        glFlush();
        usleep(delta);
        glFinish();
      }
    }
    

    该解决方案在NVidia硬件上运行良好,但据报道无法在AMD GPU上计算正确的当前时间。

    有更好的方法吗?我知道 glFinish 通常情况下,除了分析之外,不应该在应用程序中使用,但我找不到其他方法来确保GPU管道不缓冲帧。

    编辑:对于那些感兴趣的人来说,这有效地模拟了Linux下的FastSync,但没有禁用vsync。

    Edit2:可能当前时间函数的实现应该有所不同:

    uint64_t detectPresentTime()
    {
      glFinish();
    
      // time 10 iterations and compute the average
      const uint64_t start = microtime();
      for(int i = 0; i < 10; ++i)
      {
        glxSwapBuffers(state.renderer);
        glFinish();
      }
      const uint64_t t = (microtime() - start) / 10; 
    
      DEBUG_INFO("detected: %lu (%f Hz)", t, 1000000.0f / t); 
      return t;
    }
    
    1 回复  |  直到 7 年前
        1
  •  2
  •   Geoffrey    7 年前

    我找到了答案,有一个鲜为人知的OpenGL扩展名为 SGI_video_sync ,使用它可以等待下一帧。

    ie:

    glFlush();
    uint remainder;
    glXWaitVideoSyncSGI(1, 0, &remainder);