防止OpenGL缓冲帧。

5
我正在编写一个需要极低延迟纹理到屏幕流的程序(小于10毫秒),我已经使用 GL_ARB_buffer_storage 实现了这个功能,它非常适合流媒体,同时使用垂直同步来防止撕裂。

然而,我发现在调用 swap buffers 之前,NVidia 管道会缓冲 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,但没有禁用垂直同步。编辑2:也许应该以稍微不同的方式实现呈现时间函数:
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
OpenGL维基有一篇有趣的文章关于这个问题:https://www.khronos.org/opengl/wiki/Swap_Interval - bernie
1个回答

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

例如:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接