1

I have a GUI written using wxPython that contains two GLCanvases, a 'display' canvas and a 'preview' canvas onto which I am drawing some very simple geometry using PyOpenGL. The 'preview' and 'display' canvases display the contents of the same framebuffer:

enter image description here

I need both of these canvases to be updated synchronously at a consistent framerate with no tearing.

So far I have just been calling

self.SetCurrent()
# draw stuff...
self.SwapBuffers()

for both the preview and display canvases within my rendering loop. This works reasonably well provided that I disable vsync in my driver settings.

However, if I enable vsync my framerate drops to ~30Hz, half the vertical refresh rate of my display. Presumably this is because the execution of the rendering loop stalls at each SwapBuffers() call waiting for the next vertical refresh - if I comment out one of the two SwapBuffers() calls, my framerate goes back up to 60Hz, but then of course only one of my canvases gets updated.

What I'd really like is for both of my canvases to be updated synchronously, once per vsync interval. Is there any way that I can achieve this using the wx.glcanvas.GLCanvas class?

ali_m
  • 71,714
  • 23
  • 223
  • 298
  • 1
    I explained this in another similar question recently. You can do multi-threaded rendering with each canvas having its own thread, but this adds a lot of complexity. Alternatively, you can use VSYNC on one canvas, swap buffers and then immediately afterwards swap buffers on the other one (without VSYNC). The *vast* majority of the time you will not see any tearing on the other canvas, but it might have a 1 frame latency. – Andon M. Coleman Sep 11 '13 at 18:26
  • That sounds like it would work, but a big part of the problem that I'm having is that the `SwapBuffers()` method of a `wx.glcanvas.GLCanvas` object seems to _implicitly_ block execution until the next vsync. I'm not currently aware of any way that I can call `SwapBuffers` without also waiting for the next vertical refresh, aside from just disabling vsync entirely in my driver settings. – ali_m Sep 11 '13 at 18:32
  • 2
    @ali_m: What Andon implies here is, that you use `{wgl|glX}SwapInterval` to set the swap interval for the first, synching SwapBuffers to 1, and then right after the synching SwapBuffers returns set the swap interval to 0 and do the other SwapBuffers. – datenwolf Sep 11 '13 at 18:42
  • @AndonM.Coleman: I recently dealt with a similar problem, once again. As it turns out, according to the specification, the SwapBuffers call should never block on VSync. Instead the next (implicit) OpenGL synchronization point after a synched SwapBuffers shall be stalled until the VSync happened. In your typical rendering loop this means, that every, but the first SwapBuffers call may effectively stall. Here's a small experiment: Measure the actual time the execution of SwapBuffers takes. For bonus points measure the time from start of rendering to SwapBuffer return time. – datenwolf Sep 11 '13 at 18:46
  • @AndonM.Coleman Unfortunately I can't find anything in the wxPython documentation relating to swap intervals. I'll look into whether it's possible to go down the multithreaded rendering route. – ali_m Sep 11 '13 at 18:52
  • 1
    @ali_m: I think wxPython is probably just a thin wrapper around OpenGL. If you know the underlying window system, you can use `{wgl|glX}SwapInterval` like datenwolf said. I just figured wx was managing your OpenGL context for you so it would have a more portable way of doing the same thing. – Andon M. Coleman Sep 11 '13 at 18:58
  • @datenwolf it took me a little while to figure out how to implement it in PyOpenGL, but your original suggestion to use `glXSwapInterval` to reset the swap interval on the fly works beautifully. If you write it up as an answer I'd be happy to accept. – ali_m Sep 11 '13 at 21:04

1 Answers1

0

What Andon implied in his comment is, that you use {wgl|glX}SwapInterval to set the swap interval for the first, synching SwapBuffers to 1, and then right after the synching SwapBuffers returns set the swap interval to 0 and do the other SwapBuffers.

datenwolf
  • 159,371
  • 13
  • 185
  • 298
  • For anyone else who might want to do this using PyOpenGL, the call is `from OpenGL import GLX; GLX.glXSwapInterval{SGI|MESA}(0)` (the SGI function worked for me with a GeForce GT 630) – ali_m Sep 11 '13 at 22:24
  • 1
    Just keep in mind that GLX is specific to X11/GLX. If you're running on Windows it's something along the line of `from OpenGL import wgl; wgl.wglSwapInterval…` – datenwolf Sep 11 '13 at 22:30