Below is a patch which fixes the visuals for me, it simply uses glXWaitForSbcOML, falling back to glFinish if that's not supported.
Partway through coding this up, I was thinking that maybe listening for damage events would be more natural. One reason: the current code is probably imperfect for single-buffered contexts. The window will get redrawn any time glFlush or glFinish was called (flush_gl_drawable is called inside both), but really apps might expect that a single-buffered window get updated whenever rendering finishes. Damage is naturally asynchronous (which might avoid a performance hit). I also wonder whether vsync is easier to make work with that kind of flow (how do compositing window managers accomplish this? they've got the same problem).
All the same, that might be a more invasive thing (i.e. can you do it asynchronously? hook into the event loop and intercept damage events).
diff --git a/dlls/winex11.drv/opengl.c b/dlls/winex11.drv/opengl.c index e8d0f89..c541044 100644 --- a/dlls/winex11.drv/opengl.c +++ b/dlls/winex11.drv/opengl.c @@ -256,6 +256,9 @@ MAKE_FUNCPTR(glXGetCurrentReadDrawable) static GLXContext (*pglXCreateContextAttribsARB)(Display *dpy, GLXFBConfig config, GLXContext share_context, Bool direct, const int *attrib_list); static void* (*pglXGetProcAddressARB)(const GLubyte *); static int (*pglXSwapIntervalSGI)(int); +static int (*pglXSwapIntervalSGI)(int); +static BOOL (*pglXWaitForSbcOML)(Display *dpy, GLXDrawable drawable, int64_t target_sbc, int64_t *ust, int64_t *msc, int64_t *sbc); +static BOOL (*pglXGetSyncValuesOML)(Display *dpy, GLXDrawable drawable, int64_t *ust, int64_t *msc, int64_t *sbc);
/* ATI GLX Extensions */ static BOOL (*pglXBindTexImageATI)(Display *dpy, GLXPbuffer pbuffer, int buffer); @@ -602,6 +605,11 @@ static BOOL has_opengl(void) pglXCopySubBufferMESA = pglXGetProcAddressARB((const GLubyte *) "glXCopySubBufferMESA"); }
+ if(glxRequireExtension("GLX_OML_sync_control")) { + pglXWaitForSbcOML = pglXGetProcAddressARB((const GLubyte *) "glXWaitForSbcOML"); + pglXGetSyncValuesOML = pglXGetProcAddressARB((const GLubyte *) "glXGetSyncValuesOML"); + } + X11DRV_WineGL_LoadExtensions();
wine_tsx11_unlock(); @@ -2281,6 +2289,11 @@ static void WINAPI X11DRV_wglGetIntegerv(GLenum pname, GLint* params) wine_tsx11_unlock(); }
+/* For offscreen-rendered (child) windows, this copies the window + * content into to the onscreen window. The caller must have + * performed any OpenGL flushing to ensure the gl_drawable is + * up-to-date + */ void flush_gl_drawable(X11DRV_PDEVICE *physDev) { int w, h; @@ -2295,10 +2308,7 @@ void flush_gl_drawable(X11DRV_PDEVICE *physDev) Drawable src = physDev->pixmap; if(!src) src = physDev->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 */ wine_tsx11_lock(); - XFlush(gdi_display); XSetFunction(gdi_display, physDev->gc, GXcopy); XCopyArea(gdi_display, src, physDev->drawable, physDev->gc, 0, 0, w, h, physDev->dc_rect.left, physDev->dc_rect.top); @@ -3857,6 +3867,8 @@ BOOL X11DRV_SwapBuffers(PHYSDEV dev) wine_tsx11_lock(); sync_context(ctx); if(physDev->pixmap) { + /* Child window w/ pixmap rendering + * (composite extension unavailable) */ if(pglXCopySubBufferMESA) { int w = physDev->dc_rect.right - physDev->dc_rect.left; int h = physDev->dc_rect.bottom - physDev->dc_rect.top; @@ -3867,14 +3879,33 @@ BOOL X11DRV_SwapBuffers(PHYSDEV dev) pglFlush(); if(w > 0 && h > 0) pglXCopySubBufferMESA(gdi_display, drawable, 0, 0, w, h); + } else { + pglXSwapBuffers(gdi_display, drawable); } - else + flush_gl_drawable(physDev); + } else if(physDev->gl_drawable) { + /* Composite-redirected child window. We must wait for the buffer swap + * since flush_gl_drawable will be copying out the window contents. */ + if(pglXWaitForSbcOML) { + /* Wait for swap buffer with GLX_OML_sync_control extension, + * if it's supported. Note that DRI-based renderers like r600g + * support this extension and wait for swap on glFinish(). */ + int64_t ust, msc, sbc; + pglXGetSyncValuesOML(gdi_display, drawable, &ust, &msc, &sbc); pglXSwapBuffers(gdi_display, drawable); - } - else + pglXWaitForSbcOML(gdi_display, drawable, sbc+1, &ust, &msc, &sbc); + } else { + /* No GLX_OML_swap_method support, just assume that glFinish() will + * wait for the swap. This works on nv & fglrx. */ + pglXSwapBuffers(gdi_display, drawable); + pglFinish(); + } + flush_gl_drawable(physDev); + } else { + /* normal fullscreen window */ pglXSwapBuffers(gdi_display, drawable); + }
- flush_gl_drawable(physDev); wine_tsx11_unlock();
/* FPS support */