OpenGL child windows (SketchUp)

Brian Bloniarz brian.bloniarz at gmail.com
Thu Feb 16 00:45:27 CST 2012


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 */



More information about the wine-devel mailing list