Hi all,
I could use a little help fixing a window lag and screen corruption issue in SketchUp: http://bugs.winehq.org/show_bug.cgi?id=25912 Long story short, I think the implementation of OpenGL child rendering needs a small update.
As a reminder, this is how an OpenGL child window is setup:
static BOOL set_win_format( HWND hwnd, XID fbconfig_id ) { gl_drawable = XCreateWindow(...); // offscreen window XCompositeRedirectWindow(..., gl_drawable, CompositeRedirectManual); }
Here XComposite extension is render into an offscreen window. When it's time to display, the contents of that window are copied into the visible onscreen parent window (physDev is the onscreen window, gl_drawable is the offscreen window):
BOOL X11DRV_SwapBuffers(PHYSDEV dev) { ... glXSwapBuffers(gdi_display, gl_drawable);
... /* The GL drawable may be lagged behind if we don't flush first, so * flush the display make sure we copy up-to-date data */ XFlush(gdi_display); XCopyArea(gdi_display, gl_drawable, physDev...); }
The XFlush() is the problem; the buffer swap must happen before the copy, and accelerated renderers wouldn't guarantee to do that on an X queue flush.
So how do you wait for a buffer swap? I wrote a test program, can you say "undefined behavior"? I tested on a few drivers:
1) ATI fglrx binary driver: glXSwapBuffers is instantaneous, existing code works fine.
2) NVidia binary driver: glXSwapBuffers is asynchronous, neither XFlush() nor XSync() is enough. Calling glXWaitGL or glFinish works.
3) Mesa DRI driver (Intel i915): glXSwapBuffers is asynchronous. None of XFlush/XSync/glXWaitX/glXWaitGL is enough. glFinish works.
4) r600g (Open Source accelerated ATI) driver: glXSwapBuffers is asynchronous. None of the flush calls wait for it (XFlush/XSync/glXWaitGL/glXWaitX/glFinish).
The last one is the tricky part -- this driver is also based on the DRI, so I'm guessing it may appear on other open-source accelerated drivers (looking at http://www.x.org/releases/current/doc/dri2proto/dri2proto.txt suggests that this is new behavior as of 1.99.2)
So how to come up with a common fix? I could think of 3 possibilities:
1) Use the GLX_OML_sync_control extension, it has an explicit call to wait for a buffer swap. Not supported by NV or fglrx, but we could fallback to glXWaitGL on those drivers. Probably simplest.
2) Use the X Damage extension, wait for an XDamageNotify event before copying. This should work everywhere, all drivers probably implement damage so that compositing window managers can use it.
3) Talk to the DRI people, maybe glXWaitGL should't return when there's a pending a buffer swap. The GLX spec seems to say it should wait, but it's a grey area and the Composite extension was designed much later. Not a quick fix.
Any ideas or thoughts? If I hear nothing, I can code up a patch that does (1), but it'd be great to hear from some people who know this area well.
Thanks, Brian Bloniarz
Il 08 febbraio 2012 18:19, Brian Bloniarz brian.bloniarz@gmail.com ha scritto:
Hi all,
I could use a little help fixing a window lag and screen corruption issue in SketchUp: http://bugs.winehq.org/show_bug.cgi?id=25912 Long story short, I think the implementation of OpenGL child rendering needs a small update.
As a reminder, this is how an OpenGL child window is setup:
static BOOL set_win_format( HWND hwnd, XID fbconfig_id ) { gl_drawable = XCreateWindow(...); // offscreen window XCompositeRedirectWindow(..., gl_drawable, CompositeRedirectManual); }
Here XComposite extension is render into an offscreen window. When it's time to display, the contents of that window are copied into the visible onscreen parent window (physDev is the onscreen window, gl_drawable is the offscreen window):
BOOL X11DRV_SwapBuffers(PHYSDEV dev) { ... glXSwapBuffers(gdi_display, gl_drawable);
... /* The GL drawable may be lagged behind if we don't flush first, so * flush the display make sure we copy up-to-date data */ XFlush(gdi_display); XCopyArea(gdi_display, gl_drawable, physDev...); }
The XFlush() is the problem; the buffer swap must happen before the copy, and accelerated renderers wouldn't guarantee to do that on an X queue flush.
So how do you wait for a buffer swap? I wrote a test program, can you say "undefined behavior"? I tested on a few drivers:
- ATI fglrx binary driver:
glXSwapBuffers is instantaneous, existing code works fine.
- NVidia binary driver:
glXSwapBuffers is asynchronous, neither XFlush() nor XSync() is enough. Calling glXWaitGL or glFinish works.
- Mesa DRI driver (Intel i915):
glXSwapBuffers is asynchronous. None of XFlush/XSync/glXWaitX/glXWaitGL is enough. glFinish works.
- r600g (Open Source accelerated ATI) driver:
glXSwapBuffers is asynchronous. None of the flush calls wait for it (XFlush/XSync/glXWaitGL/glXWaitX/glFinish).
The last one is the tricky part -- this driver is also based on the DRI, so I'm guessing it may appear on other open-source accelerated drivers (looking at http://www.x.org/releases/current/doc/dri2proto/dri2proto.txt suggests that this is new behavior as of 1.99.2)
So how to come up with a common fix? I could think of 3 possibilities:
- Use the GLX_OML_sync_control extension, it has an explicit
call to wait for a buffer swap. Not supported by NV or fglrx, but we could fallback to glXWaitGL on those drivers. Probably simplest.
- Use the X Damage extension, wait for an XDamageNotify
event before copying. This should work everywhere, all drivers probably implement damage so that compositing window managers can use it.
- Talk to the DRI people, maybe glXWaitGL should't return
when there's a pending a buffer swap. The GLX spec seems to say it should wait, but it's a grey area and the Composite extension was designed much later. Not a quick fix.
Any ideas or thoughts? If I hear nothing, I can code up a patch that does (1), but it'd be great to hear from some people who know this area well.
Thanks, Brian Bloniarz
Nice analysis. I'm not really such an expert in the area, but I think both (1) and (2) make sense. I'm not sure in (1) case about the glXWaitGL call, as technically glXSwapBuffers is not a GL call, but given that it works for you (while XSync doesn't) on Nvidia, I guess it's fine... Make sure it works correctly on all the drivers you mentioned, or even that e.g. it doesn't cause excessive slowdowns.
On Wed, Feb 8, 2012 at 6:31 PM, Matteo Bruni matteo.mystral@gmail.com wrote:
Il 08 febbraio 2012 18:19, Brian Bloniarz brian.bloniarz@gmail.com ha scritto:
Hi all,
I could use a little help fixing a window lag and screen corruption issue in SketchUp: http://bugs.winehq.org/show_bug.cgi?id=25912 Long story short, I think the implementation of OpenGL child rendering needs a small update.
As a reminder, this is how an OpenGL child window is setup:
static BOOL set_win_format( HWND hwnd, XID fbconfig_id ) { gl_drawable = XCreateWindow(...); // offscreen window XCompositeRedirectWindow(..., gl_drawable, CompositeRedirectManual); }
Here XComposite extension is render into an offscreen window. When it's time to display, the contents of that window are copied into the visible onscreen parent window (physDev is the onscreen window, gl_drawable is the offscreen window):
BOOL X11DRV_SwapBuffers(PHYSDEV dev) { ... glXSwapBuffers(gdi_display, gl_drawable);
... /* The GL drawable may be lagged behind if we don't flush first, so * flush the display make sure we copy up-to-date data */ XFlush(gdi_display); XCopyArea(gdi_display, gl_drawable, physDev...); }
The XFlush() is the problem; the buffer swap must happen before the copy, and accelerated renderers wouldn't guarantee to do that on an X queue flush.
So how do you wait for a buffer swap? I wrote a test program, can you say "undefined behavior"? I tested on a few drivers:
- ATI fglrx binary driver:
glXSwapBuffers is instantaneous, existing code works fine.
- NVidia binary driver:
glXSwapBuffers is asynchronous, neither XFlush() nor XSync() is enough. Calling glXWaitGL or glFinish works.
- Mesa DRI driver (Intel i915):
glXSwapBuffers is asynchronous. None of XFlush/XSync/glXWaitX/glXWaitGL is enough. glFinish works.
- r600g (Open Source accelerated ATI) driver:
glXSwapBuffers is asynchronous. None of the flush calls wait for it (XFlush/XSync/glXWaitGL/glXWaitX/glFinish).
The last one is the tricky part -- this driver is also based on the DRI, so I'm guessing it may appear on other open-source accelerated drivers (looking at http://www.x.org/releases/current/doc/dri2proto/dri2proto.txt suggests that this is new behavior as of 1.99.2)
So how to come up with a common fix? I could think of 3 possibilities:
- Use the GLX_OML_sync_control extension, it has an explicit
call to wait for a buffer swap. Not supported by NV or fglrx, but we could fallback to glXWaitGL on those drivers. Probably simplest.
- Use the X Damage extension, wait for an XDamageNotify
event before copying. This should work everywhere, all drivers probably implement damage so that compositing window managers can use it.
- Talk to the DRI people, maybe glXWaitGL should't return
when there's a pending a buffer swap. The GLX spec seems to say it should wait, but it's a grey area and the Composite extension was designed much later. Not a quick fix.
Any ideas or thoughts? If I hear nothing, I can code up a patch that does (1), but it'd be great to hear from some people who know this area well.
Thanks, Brian Bloniarz
Nice analysis. I'm not really such an expert in the area, but I think both (1) and (2) make sense. I'm not sure in (1) case about the glXWaitGL call, as technically glXSwapBuffers is not a GL call, but given that it works for you (while XSync doesn't) on Nvidia, I guess it's fine... Make sure it works correctly on all the drivers you mentioned, or even that e.g. it doesn't cause excessive slowdowns.
The offscreen X11 window code is a bit questionable and we knew that when added it. I have to think a bit and need to look closely at the GLX specs again. My gut feeling is that vsync related functions may not work for our purpose since we are well in undocumented territory by using offscreen Windows (pbuffers also don't have vsync defined).
glXWaitGL and friends were intended for syncing GLX and X, so this may be the most reliable way. As you pointed out there may be bugs in DRI, but this needs a careful read of the specs (will try to look at it). As you said the damage extension can be used as well, but I think I would lean to using the classic glX/X synchronization features first.
I hope to have some time tonight to look at this.
Roderick