Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prevent OpenGL from Buffering Frames

I am writing a program that requires extremely low latency texture to screen streaming (under 10ms), I have implemented this using GL_ARB_buffer_storage which works great for the streaming, and vsync to prevent tearing.

However I have discovered that the NVidia pipeline will buffer from 2 to 8 frames when calling swap buffers before it blocks, I need to prevent this.

What I have done is the following:

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;
}

Then in the draw thread I do the following:

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();
  }
}

This solution works great on NVidia hardware but is reported to not calculate a proper present time on AMD GPUs.

Is there a better way to do this? I know that glFinish normally shouldn't be used in an application except for profiling, but I can't find another way to ensure the GPU pipeline is not buffering frames.

Edit: For those interested this effectively emulates FastSync under Linux, but without disabling vsync.

Edit2: Perhaps the present time function should be implemented a bit different:

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;
}
like image 450
Geoffrey Avatar asked Oct 29 '22 22:10

Geoffrey


1 Answers

I found the answer, there is a little known OpenGL extension called SGI_video_sync, using this it is possible to wait for the next frame.

ie:

glFlush();
uint remainder;
glXWaitVideoSyncSGI(1, 0, &remainder);
like image 143
Geoffrey Avatar answered Nov 01 '22 16:11

Geoffrey