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;
}
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);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With