wined3d: CreateFakeGLContext should also work when we have a context

Jan Zerebecki jan.wine at zerebecki.de
Thu Oct 19 13:07:37 CDT 2006


Per request this adds thread safety only to CreateFakeGLContext
and it's corresponding Release function. This addresses no thread
safety in the rest of wined3d, which AFAIk as a whole is still
not thread safe. When addressing thread safety in wined3d it must
also be taken care that a GL context is not deleted away by a
different thread when execution is between these two functions.

If this patch is rejected from inclusion, please tell me why, as
I would have to ask anyway.

From: Jan Zerebecki <jan.wine at zerebecki.de>
Changelog:
wined3d: CreateFakeGLContext should also work when we have a context
from somewhere else. Add thread safety. Fix all the calling
functions for the changed interface.
---

 dlls/wined3d/directx.c    |  305 +++++++++++++++++++++++----------------------
 include/wine/wined3d_gl.h |    8 -
 2 files changed, 159 insertions(+), 154 deletions(-)

diff --git a/dlls/wined3d/directx.c b/dlls/wined3d/directx.c
index 2e6d38e..3f90b2c 100644
--- a/dlls/wined3d/directx.c
+++ b/dlls/wined3d/directx.c
@@ -28,6 +28,7 @@
 
 
 #include "config.h"
+#include <assert.h>
 #include "wined3d_private.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(d3d);
@@ -72,90 +73,119 @@ DWORD minMipLookup[WINED3DTEXF_ANISOTROP
  * ie there is no GL Context - Get a default rendering context to enable the
  * function query some info from GL
  */
-static WineD3D_Context* WineD3D_CreateFakeGLContext(void) {
-    static WineD3D_Context ctx = { NULL, NULL, NULL, 0, 0 };
-    WineD3D_Context* ret = NULL;
-
-    if (glXGetCurrentContext() == NULL) {
-       BOOL         gotContext  = FALSE;
-       BOOL         created     = FALSE;
-       XVisualInfo  template;
-       HDC          device_context;
-       Visual*      visual;
-       BOOL         failed = FALSE;
-       int          num;
-       XWindowAttributes win_attr;
-       TRACE_(d3d_caps)("Creating Fake GL Context\n");
-
-       ctx.drawable = (Drawable) GetPropA(GetDesktopWindow(), "__wine_x11_whole_window");
-
-       /* Get the display */
-       device_context = GetDC(0);
-       ctx.display = get_display(device_context);
-       ReleaseDC(0, device_context);
-
-       /* Get the X visual */
-       ENTER_GL();
-       if (XGetWindowAttributes(ctx.display, ctx.drawable, &win_attr)) {
-           visual = win_attr.visual;
-       } else {
-           visual = DefaultVisual(ctx.display, DefaultScreen(ctx.display));
-       }
-       template.visualid = XVisualIDFromVisual(visual);
-       ctx.visInfo = XGetVisualInfo(ctx.display, VisualIDMask, &template, &num);
-       if (ctx.visInfo == NULL) {
-           LEAVE_GL();
-           WARN_(d3d_caps)("Error creating visual info for capabilities initialization\n");
-           failed = TRUE;
-       }
-
-       /* Create a GL context */
-       if (!failed) {
-           ctx.glCtx = glXCreateContext(ctx.display, ctx.visInfo, NULL, GL_TRUE);
-
-           if (ctx.glCtx == NULL) {
-               LEAVE_GL();
-               WARN_(d3d_caps)("Error creating default context for capabilities initialization\n");
-               failed = TRUE;
-           }
-       }
-
-       /* Make it the current GL context */
-       if (!failed && glXMakeCurrent(ctx.display, ctx.drawable, ctx.glCtx) == False) {
-           glXDestroyContext(ctx.display, ctx.glCtx);
-           LEAVE_GL();
-           WARN_(d3d_caps)("Error setting default context as current for capabilities initialization\n");
-           failed = TRUE;
-       }
-
-       /* It worked! Wow... */
-       if (!failed) {
-           gotContext = TRUE;
-           created = TRUE;
-           ret = &ctx;
-       } else {
-           ret = NULL;
-       }
-
-   } else {
-     if (ctx.ref > 0) ret = &ctx;
-   }
-
-   if (NULL != ret) InterlockedIncrement(&ret->ref);
-   return ret;
+
+static int             wined3d_fake_gl_context_ref = 0;
+static BOOL            wined3d_fake_gl_context_foreign;
+static BOOL            wined3d_fake_gl_context_available = FALSE;
+static Display*        wined3d_fake_gl_context_display = NULL;
+
+static CRITICAL_SECTION wined3d_fake_gl_context_cs;
+static CRITICAL_SECTION_DEBUG wined3d_fake_gl_context_cs_debug =
+{
+    0, 0, &wined3d_fake_gl_context_cs,
+    { &wined3d_fake_gl_context_cs_debug.ProcessLocksList,
+      &wined3d_fake_gl_context_cs_debug.ProcessLocksList },
+    0, 0, { (DWORD_PTR)(__FILE__ ": wined3d_fake_gl_context_cs") }
+};
+static CRITICAL_SECTION wined3d_fake_gl_context_cs = { &wined3d_fake_gl_context_cs_debug, -1, 0, 0, 0, 0 };
+
+static void WineD3D_ReleaseFakeGLContext(void) {
+    EnterCriticalSection(&wined3d_fake_gl_context_cs);
+
+    if(!wined3d_fake_gl_context_available) {
+        TRACE_(d3d_caps)("no context available to release\n");
+        LeaveCriticalSection(&wined3d_fake_gl_context_cs);
+        return;
+    }
+
+    GLXContext glCtx = glXGetCurrentContext();
+
+    TRACE_(d3d_caps)("decrementing ref from %i\n", wined3d_fake_gl_context_ref);
+    if (0 == (++wined3d_fake_gl_context_ref) ) {
+        if(!wined3d_fake_gl_context_foreign && glCtx) {
+            TRACE_(d3d_caps)("destroying fake GL context\n");
+            glXMakeCurrent(wined3d_fake_gl_context_display, None, NULL);
+            glXDestroyContext(wined3d_fake_gl_context_display, glCtx);
+        }
+        LEAVE_GL();
+        wined3d_fake_gl_context_available = FALSE;
+    }
+    assert(wined3d_fake_gl_context_ref >= 0);
+
+    LeaveCriticalSection(&wined3d_fake_gl_context_cs);
 }
 
-static void WineD3D_ReleaseFakeGLContext(WineD3D_Context* ctx) {
-    /* If we created a dummy context, throw it away */
-    if (NULL != ctx) {
-        if (0 == InterlockedDecrement(&ctx->ref)) {
-            glXMakeCurrent(ctx->display, None, NULL);
-            glXDestroyContext(ctx->display, ctx->glCtx);
-            ctx->display = NULL;
-            ctx->glCtx = NULL;
-            LEAVE_GL();
+static void WineD3D_CreateFakeGLContext(void) {
+    EnterCriticalSection(&wined3d_fake_gl_context_cs);
+
+    TRACE_(d3d_caps)("getting context...\n");
+    if(wined3d_fake_gl_context_ref > 0) goto ret;
+    assert(0 == wined3d_fake_gl_context_ref);
+
+    wined3d_fake_gl_context_foreign = TRUE;
+
+    if(!wined3d_fake_gl_context_display) {
+        HDC        device_context = GetDC(0);
+
+        wined3d_fake_gl_context_display = get_display(device_context);
+        ReleaseDC(0, device_context);
+    }
+
+    XVisualInfo* visInfo = NULL;
+    GLXContext   glCtx = glXGetCurrentContext();
+
+    ENTER_GL();
+
+    if (!glCtx) {
+        wined3d_fake_gl_context_foreign = FALSE;
+        Drawable     drawable = (Drawable) GetPropA(GetDesktopWindow(), "__wine_x11_whole_window");;
+
+        XVisualInfo  template;
+        Visual*      visual;
+        int          num;
+        XWindowAttributes win_attr;
+
+        TRACE_(d3d_caps)("Creating Fake GL Context\n");
+
+        /* Get the X visual */
+        if (XGetWindowAttributes(wined3d_fake_gl_context_display, drawable, &win_attr)) {
+            visual = win_attr.visual;
+        } else {
+            visual = DefaultVisual(wined3d_fake_gl_context_display, DefaultScreen(wined3d_fake_gl_context_display));
+        }
+        template.visualid = XVisualIDFromVisual(visual);
+        visInfo = XGetVisualInfo(wined3d_fake_gl_context_display, VisualIDMask, &template, &num);
+        if (!visInfo) {
+            WARN_(d3d_caps)("Error creating visual info for capabilities initialization\n");
+            goto fail;
         }
+
+        /* Create a GL context */
+        glCtx = glXCreateContext(wined3d_fake_gl_context_display, visInfo, NULL, GL_TRUE);
+        if (!glCtx) {
+            WARN_(d3d_caps)("Error creating default context for capabilities initialization\n");
+            goto fail;
+        }
+
+        /* Make it the current GL context */
+        if (!glXMakeCurrent(wined3d_fake_gl_context_display, drawable, glCtx)) {
+            WARN_(d3d_caps)("Error setting default context as current for capabilities initialization\n");
+            goto fail;
+        }
+
+        XFree(visInfo);
+
     }
+
+  ret:
+    wined3d_fake_gl_context_ref++;
+    wined3d_fake_gl_context_available = TRUE;
+    LeaveCriticalSection(&wined3d_fake_gl_context_cs);
+    return;
+  fail:
+    if(visInfo) XFree(visInfo);
+    if(glCtx) glXDestroyContext(wined3d_fake_gl_context_display, glCtx);
+    LeaveCriticalSection(&wined3d_fake_gl_context_cs);
 }
 
 /**********************************************************
@@ -295,18 +325,12 @@ BOOL IWineD3DImpl_FillGLCaps(IWineD3D *i
     GLfloat     gl_floatv[2];
     Bool        test = 0;
     int         major, minor;
-    WineD3D_Context *fake_ctx = NULL;
-    BOOL        gotContext    = FALSE;
+    BOOL        ret = TRUE;
     int         i;
 
     /* Make sure that we've got a context */
-    if (glXGetCurrentContext() == NULL) {
-        /* TODO: CreateFakeGLContext should really take a display as a parameter  */
-        fake_ctx = WineD3D_CreateFakeGLContext();
-        if (NULL != fake_ctx) gotContext = TRUE;
-    } else {
-        gotContext = TRUE;
-    }
+    /* TODO: CreateFakeGLContext should really take a display as a parameter  */
+    WineD3D_CreateFakeGLContext();
 
     TRACE_(d3d_caps)("(%p, %p)\n", gl_info, display);
 
@@ -1000,15 +1024,12 @@ #undef USE_GL_FUNC
         }
     }
 
-    /* If we created a dummy context, throw it away */
-    if (NULL != fake_ctx) WineD3D_ReleaseFakeGLContext(fake_ctx);
 
     /* Only save the values obtained when a display is provided */
-    if (fake_ctx == NULL) {
-        return TRUE;
-    } else {
-        return FALSE;
-    }
+    if (wined3d_fake_gl_context_foreign)
+        ret = FALSE;
+    WineD3D_ReleaseFakeGLContext();
+    return ret;
 }
 
 /**********************************************************
@@ -1279,11 +1300,8 @@ static HRESULT WINAPI IWineD3DImpl_GetAd
            reuse the values once we have a context which is valid. Values from
            a temporary context may differ from the final ones                 */
         if (!isGLInfoValid) {
-            WineD3D_Context *fake_ctx = NULL;
-            if (glXGetCurrentContext() == NULL) fake_ctx = WineD3D_CreateFakeGLContext();
             /* If we don't know the device settings, go query them now */
             isGLInfoValid = IWineD3DImpl_FillGLCaps(iface, IWineD3DImpl_GetAdapterDisplay(iface, Adapter));
-            if (fake_ctx != NULL) WineD3D_ReleaseFakeGLContext(fake_ctx);
         }
 
         /* If it worked, return the information requested */
@@ -1333,17 +1351,18 @@ static HRESULT WINAPI IWineD3DImpl_GetAd
     return WINED3D_OK;
 }
 
-static BOOL IWineD3DImpl_IsGLXFBConfigCompatibleWithRenderFmt(WineD3D_Context* ctx, GLXFBConfig cfgs, WINED3DFORMAT Format) {
+static BOOL IWineD3DImpl_IsGLXFBConfigCompatibleWithRenderFmt(GLXFBConfig cfgs, WINED3DFORMAT Format) {
 #if 0 /* This code performs a strict test between the format and the current X11  buffer depth, which may give the best performance */
   int gl_test;
   int rb, gb, bb, ab, type, buf_sz;
 
-  gl_test = glXGetFBConfigAttrib(ctx->display, cfgs, GLX_RED_SIZE,   &rb);
-  gl_test = glXGetFBConfigAttrib(ctx->display, cfgs, GLX_GREEN_SIZE, &gb);
-  gl_test = glXGetFBConfigAttrib(ctx->display, cfgs, GLX_BLUE_SIZE,  &bb);
-  gl_test = glXGetFBConfigAttrib(ctx->display, cfgs, GLX_ALPHA_SIZE, &ab);
-  gl_test = glXGetFBConfigAttrib(ctx->display, cfgs, GLX_RENDER_TYPE, &type);
-  gl_test = glXGetFBConfigAttrib(ctx->display, cfgs, GLX_BUFFER_SIZE, &buf_sz);
+  assert(wined3d_fake_gl_context_available);
+  gl_test = glXGetFBConfigAttrib(wined3d_fake_gl_context_display, cfgs, GLX_RED_SIZE,   &rb);
+  gl_test = glXGetFBConfigAttrib(wined3d_fake_gl_context_display, cfgs, GLX_GREEN_SIZE, &gb);
+  gl_test = glXGetFBConfigAttrib(wined3d_fake_gl_context_display, cfgs, GLX_BLUE_SIZE,  &bb);
+  gl_test = glXGetFBConfigAttrib(wined3d_fake_gl_context_display, cfgs, GLX_ALPHA_SIZE, &ab);
+  gl_test = glXGetFBConfigAttrib(wined3d_fake_gl_context_display, cfgs, GLX_RENDER_TYPE, &type);
+  gl_test = glXGetFBConfigAttrib(wined3d_fake_gl_context_display, cfgs, GLX_BUFFER_SIZE, &buf_sz);
 
   switch (Format) {
   case WINED3DFMT_X8R8G8B8:
@@ -1403,13 +1422,14 @@ return FALSE;
 #endif
 }
 
-static BOOL IWineD3DImpl_IsGLXFBConfigCompatibleWithDepthFmt(WineD3D_Context* ctx, GLXFBConfig cfgs, WINED3DFORMAT Format) {
+static BOOL IWineD3DImpl_IsGLXFBConfigCompatibleWithDepthFmt(GLXFBConfig cfgs, WINED3DFORMAT Format) {
 #if 0/* This code performs a strict test between the format and the current X11  buffer depth, which may give the best performance */
   int gl_test;
   int db, sb;
 
-  gl_test = glXGetFBConfigAttrib(ctx->display, cfgs, GLX_DEPTH_SIZE, &db);
-  gl_test = glXGetFBConfigAttrib(ctx->display, cfgs, GLX_STENCIL_SIZE, &sb);
+  assert(wined3d_fake_gl_context_available);
+  gl_test = glXGetFBConfigAttrib(wined3d_fake_gl_context_display, cfgs, GLX_DEPTH_SIZE, &db);
+  gl_test = glXGetFBConfigAttrib(wined3d_fake_gl_context_display, cfgs, GLX_STENCIL_SIZE, &sb);
 
   switch (Format) {
   case WINED3DFMT_D16:
@@ -1468,7 +1488,6 @@ static HRESULT WINAPI IWineD3DImpl_Check
                                                    WINED3DFORMAT DepthStencilFormat) {
     IWineD3DImpl *This = (IWineD3DImpl *)iface;
     HRESULT hr = WINED3DERR_NOTAVAILABLE;
-    WineD3D_Context* ctx = NULL;
     GLXFBConfig* cfgs = NULL;
     int nCfgs = 0;
     int it;
@@ -1484,32 +1503,25 @@ static HRESULT WINAPI IWineD3DImpl_Check
         TRACE("(%p) Failed: Atapter (%u) higher than supported adapters (%u) returning WINED3DERR_INVALIDCALL\n", This, Adapter, IWineD3D_GetAdapterCount(iface));
         return WINED3DERR_INVALIDCALL;
     }
-    /* TODO: use the real context if it's available */
-    ctx = WineD3D_CreateFakeGLContext();
-    if(NULL !=  ctx) {
-        cfgs = glXGetFBConfigs(ctx->display, DefaultScreen(ctx->display), &nCfgs);
-    } else {
-        TRACE_(d3d_caps)("(%p) : Unable to create a fake context at this time (there may already be an active context)\n", This);
-    }
 
-    if (NULL != cfgs) {
+    WineD3D_CreateFakeGLContext();
+    if(wined3d_fake_gl_context_available)
+        cfgs = glXGetFBConfigs(wined3d_fake_gl_context_display, DefaultScreen(wined3d_fake_gl_context_display), &nCfgs);
+
+    if (cfgs) {
         for (it = 0; it < nCfgs; ++it) {
-            if (IWineD3DImpl_IsGLXFBConfigCompatibleWithRenderFmt(ctx, cfgs[it], RenderTargetFormat)) {
-                if (IWineD3DImpl_IsGLXFBConfigCompatibleWithDepthFmt(ctx, cfgs[it], DepthStencilFormat)) {
+            if (IWineD3DImpl_IsGLXFBConfigCompatibleWithRenderFmt(cfgs[it], RenderTargetFormat)) {
+                if (IWineD3DImpl_IsGLXFBConfigCompatibleWithDepthFmt(cfgs[it], DepthStencilFormat)) {
                     hr = WINED3D_OK;
                     break ;
                 }
             }
         }
         XFree(cfgs);
-        cfgs = NULL;
-    } else {
-        /* If there's a current context then we cannot create a fake one so pass everything */
+    } else
         hr = WINED3D_OK;
-    }
 
-    if (ctx != NULL)
-        WineD3D_ReleaseFakeGLContext(ctx);
+    WineD3D_ReleaseFakeGLContext();
 
     if (hr != WINED3D_OK)
         TRACE_(d3d_caps)("Failed to match stencil format to device\n");
@@ -1553,6 +1565,11 @@ static HRESULT WINAPI IWineD3DImpl_Check
                                             WINED3DFORMAT DisplayFormat, WINED3DFORMAT BackBufferFormat, BOOL Windowed) {
 
     IWineD3DImpl *This = (IWineD3DImpl *)iface;
+    GLXFBConfig* cfgs = NULL;
+    int nCfgs = 0;
+    int it;
+    HRESULT hr = WINED3DERR_NOTAVAILABLE;
+
     TRACE_(d3d_caps)("(%p)-> (STUB) (Adptr:%d, CheckType:(%x,%s), DispFmt:(%x,%s), BackBuf:(%x,%s), Win?%d): stub\n",
           This,
           Adapter,
@@ -1562,32 +1579,28 @@ static HRESULT WINAPI IWineD3DImpl_Check
           Windowed);
 
     if (Adapter >= IWineD3D_GetAdapterCount(iface)) {
+        WARN_(d3d_caps)("Adapter >= IWineD3D_GetAdapterCount(iface), returning WINED3DERR_INVALIDCALL\n");
         return WINED3DERR_INVALIDCALL;
     }
 
-    {
-      GLXFBConfig* cfgs = NULL;
-      int nCfgs = 0;
-      int it;
-      HRESULT hr = WINED3DERR_NOTAVAILABLE;
-
-      WineD3D_Context* ctx = WineD3D_CreateFakeGLContext();
-      if (NULL != ctx) {
-        cfgs = glXGetFBConfigs(ctx->display, DefaultScreen(ctx->display), &nCfgs);
-        for (it = 0; it < nCfgs; ++it) {
-            if (IWineD3DImpl_IsGLXFBConfigCompatibleWithRenderFmt(ctx, cfgs[it], DisplayFormat)) {
-                hr = WINED3D_OK;
-                break ;
-            }
-        }
-        XFree(cfgs);
-
-        WineD3D_ReleaseFakeGLContext(ctx);
-        return hr;
+    WineD3D_CreateFakeGLContext();
+    if (wined3d_fake_gl_context_available) {
+      cfgs = glXGetFBConfigs(wined3d_fake_gl_context_display, DefaultScreen(wined3d_fake_gl_context_display), &nCfgs);
+      for (it = 0; it < nCfgs; ++it) {
+          if (IWineD3DImpl_IsGLXFBConfigCompatibleWithRenderFmt(cfgs[it], DisplayFormat)) {
+              hr = WINED3D_OK;
+              TRACE_(d3d_caps)("OK\n");
+              break ;
+          }
       }
+      if(cfgs) XFree(cfgs);
+      WineD3D_ReleaseFakeGLContext();
     }
 
-    return WINED3DERR_NOTAVAILABLE;
+    if(hr != WINED3D_OK)
+        TRACE_(d3d_caps)("returning WINED3DERR_NOTAVAILABLE\n");
+
+    return hr;
 }
 
 static HRESULT WINAPI IWineD3DImpl_CheckDeviceFormat(IWineD3D *iface, UINT Adapter, WINED3DDEVTYPE DeviceType, 
diff --git a/include/wine/wined3d_gl.h b/include/wine/wined3d_gl.h
index 1cc6409..dbe5214 100644
--- a/include/wine/wined3d_gl.h
+++ b/include/wine/wined3d_gl.h
@@ -1795,14 +1795,6 @@ typedef struct _WineD3D_GL_Info {
 } WineD3D_GL_Info;
 #undef USE_GL_FUNC
 
-typedef struct _WineD3D_GLContext {
-  GLXContext   glCtx; 
-  XVisualInfo* visInfo;
-  Display*     display;
-  Drawable     drawable;
-  LONG         ref;
-} WineD3D_Context;
-
 #endif /* HAVE_OPENGL */
 
 #endif /* __WINE_WINED3D_GL */



More information about the wine-patches mailing list