xbmc/xbmc — pull request #4856 by ossman (2014-06-06 21:37:30+00:00)
Title: RenderCapture causes double vsync

This is somewhat long, but bare with me as it has serious consequences. :smiley:

First the symptoms; Enabling RenderCapture (e.g. using the boblight plugin) results in the frame rate being limited to half the refresh rate. This is of course bad, but gets unusable when you set XBMC to adapt refresh rate to the frame rate. At 24 fps film will then play at 12 fps.

I confirmed that the limit was clearly following the refresh rate, so it was obviously an extra vblank wait that was happening. I started by reporting a bug against the driver, and doing investigation on that end:

https://bugs.freedesktop.org/show_bug.cgi?id=79223

The gist of it is that some drivers (in my case the r600 gallium driver) will wait for pending swap buffer requests on some rendering commands. This wreaks havoc with how XBMC schedules rendering and buffer swaps. In the normal case, XBMC will do:

1. Render the frame. This causes an unexpected wait for vsync.
2. Wait for the correct time to swap buffers. At this point the time is -0.5 frames. I.e. it won't wait.
3. Schedule a buffer swap.

By dumb luck, this happens to work. But once you enable RenderCapture, it goes south:

1. Render the frame. No delay is seen here.
2. Wait for the correct time to swap buffers. Request time is 0.5 frames, but results in waiting for the next vblank.
3. Schedule a buffer swap.
4. Render the frame again for RenderCapture. This causes another wait for vblank.
5. Notice the next frame is late and drop it.

Hence, since we have two waits for vblank, we get a too restricted frame rate.

Unfortunately it cannot be easily fixed in that end, so I started looking at making xbmc play nice with less capable drivers instead. The commits in this PR are the result of that. There are three fixes:

1. We're waiting too long before swapping buffers. The code is written to expect a delay of 0.5 frames. But the code waits for the optimal time, then the vblank after that. And a swap scheduled just after the vblank won't actually occur until the next vblank. Meaning that we get 1.5 frames of delay instead. The commit makes sure we break one vblank earlier so that we can present the frame with the intended delay.

2. The code for glXWaitVideoSyncSGI() did not work well when glXSwapBuffers() waits for vblank. In that case you'd get first a pointless wait, then the scheduled swap, and then another wait for the swap to occur.

3. Always make use of glXWaitVideoSyncSGI() when available so that the buffer swaps are synchronous. Without this you'll have some variation in XBMC's frame scheduling that isn't desirable.

PR is against master, but it was tested with Gotham.