[Mesa-dev] [PATCH 1/4] mesa: Add new MESA_multithread_makecurrent extension.

Eric Anholt eric at anholt.net
Mon Feb 21 14:41:06 PST 2011


This extension allows a client to bind one context in multiple threads
simultaneously.  It is then up to the client to manage synchronization of
access to the GL, just as normal multithreaded GL from multiple contexts
requires synchronization management to shared objects.
---
 docs/MESA_multithread_makecurrent.spec |  135 ++++++++++++++++++++++++++++++++
 src/glx/glxclient.h                    |    4 +-
 src/glx/glxcurrent.c                   |   55 ++++++++-----
 src/glx/glxextensions.c                |    1 +
 src/glx/glxextensions.h                |    1 +
 5 files changed, 172 insertions(+), 24 deletions(-)
 create mode 100644 docs/MESA_multithread_makecurrent.spec

diff --git a/docs/MESA_multithread_makecurrent.spec b/docs/MESA_multithread_makecurrent.spec
new file mode 100644
index 0000000..68ade76
--- /dev/null
+++ b/docs/MESA_multithread_makecurrent.spec
@@ -0,0 +1,135 @@
+Name
+
+    MESA_multithread_makecurrent
+
+Name Strings
+
+    GLX_MESA_multithread_makecurrent
+
+Contact
+
+    Eric Anholt (eric at anholt.net)
+
+Status
+
+    Not shipping.
+
+Version
+
+    Last Modified Date:  21 February 2011
+
+Number
+
+    TBD
+
+Dependencies
+
+    OpenGL 1.0 or later is required.
+    GLX 1.3 or later is required.
+
+Overview
+
+    The GLX context setup encourages multithreaded applications to
+    create a context per thread which each operate on their own
+    objects in parallel, and leaves synchronization for write access
+    to shared objects up to the application.
+
+    For some applications, maintaining per-thread contexts and
+    ensuring that the glFlush happens in one thread before another
+    thread starts working on that object is difficult.  For them,
+    using the same context across multiple threads and protecting its
+    usage with a mutex is both higher performance and easier to
+    implement.  This extension gives those applications that option by
+    relaxing the context binding requirements.
+
+    This new behavior matches the requirements of AGL, while providing
+    a feature not specified in WGL.
+
+IP Status
+
+    Open-source; freely implementable.
+
+Issues
+
+    None.
+
+New Procedures and Functions
+
+    None.
+
+New Tokens
+
+    None.
+
+Changes to Chapter 3 of the GLX 1.3 Specification (Functions and Errors)
+
+    Remove the following sentence from section 3.3.7 Rendering Contexts:
+	If ctx is current to some other thread, then
+	glXMakeContextCurrent will generate a BadAccess error.
+
+    Remove the following sentence from section 3.5 Rendering Contexts:
+	If ctx is current to some other thread, then
+	glXMakeCurrent will generate a BadAccess error.
+
+GLX Protocol
+
+    None.  The GLX extension is client-side.
+
+Errors
+
+    None.
+
+New State
+
+    None.
+
+Issues
+
+    (1) What happens if the app binds a context/drawable in multiple
+	threads, then binds a different context/thread in one of them?
+
+    As with binding a new context from the current thread, the old
+    context's refcount is reduced and the new context's refcount is
+    increased.
+
+    (2) What happens if the app binds a context/drawable in multiple
+	threads, then binds None/None in one of them?
+
+    The GLX context is unreferenced from that thread, and the other
+    threads retain their GLX context binding.
+
+    (3) What happens if the app binds a context/drawable in 7 threads,
+	then destroys the context in one of them?
+
+    As with GLX context destruction previously, the XID is destroyed
+    but the context remains usable by threads that have the context
+    current.
+
+    (4) What happens if the app binds a new drawable/readable with
+        glXMakeCurrent() when it is already bound to another thread?
+
+    The context becomes bound to the new drawable/readable, and
+    further rendering in either thread will use the new
+    drawable/readable.
+
+    (5) What requirements should be placed on the user managing contexts
+        from multiple threads?
+
+    The intention is to allow multithreaded access to the GL at the
+    minimal performance cost, so requiring that the GL do general
+    synchronization (beyond that already required by context sharing)
+    is not an option, and synchronizing of GL's access to the GL
+    context between multiple threads is left to the application to do
+    across GL calls.  However, it would be unfortunate for a library
+    doing multithread_makecurrent to require that other libraries
+    share in synchronization for binding of their own contexts, so the
+    refcounting of the contexts is required to be threadsafe.
+
+Revision History
+
+    20 November 2009 Eric Anholt - initial specification
+    22 November 2009 Eric Anholt - added issues from Ian Romanick.
+    3 February 2011 Eric Anholt - updated with resolution to issues 1-3
+    3 February 2011 Eric Anholt - added issue 4, 5
+    21 February 2011 Eric Anholt - Include glXMakeCurrent() sentence
+    along with glXMakeContextCurrent() for removal.
diff --git a/src/glx/glxclient.h b/src/glx/glxclient.h
index fdcef80..2b6966f 100644
--- a/src/glx/glxclient.h
+++ b/src/glx/glxclient.h
@@ -419,9 +419,9 @@ struct glx_context
    /*@} */
 
    /**
-    * Thread ID we're currently current in. Zero if none.
+    * Number of threads we're currently current in.
     */
-   unsigned long thread_id;
+   unsigned long thread_refcount;
 
    char gl_extension_bits[__GL_EXT_BYTES];
 };
diff --git a/src/glx/glxcurrent.c b/src/glx/glxcurrent.c
index 3631738..9a64990 100644
--- a/src/glx/glxcurrent.c
+++ b/src/glx/glxcurrent.c
@@ -216,6 +216,16 @@ MakeContextCurrent(Display * dpy, GLXDrawable draw,
    struct glx_context *oldGC = __glXGetCurrentContext();
    int ret = Success;
 
+   /* XXX: If this is left out, then libGL ends up not having this
+    * symbol, and drivers using it fail to load.  Compare the
+    * implementation of this symbol to _glapi_noop_enable_warnings(),
+    * though, which gets into the library despite no callers, the same
+    * prototypes, and the same compile flags to the files containing
+    * them.  Moving the definition to glapi_nop.c gets it into the
+    * library, though.
+    */
+   (void)_glthread_GetID();
+
    /* Make sure that the new context has a nonzero ID.  In the request,
     * a zero context ID is used only to mean that we bind to no current
     * context.
@@ -236,41 +246,42 @@ MakeContextCurrent(Display * dpy, GLXDrawable draw,
 
    _glapi_check_multithread();
 
-   if (gc != NULL && gc->thread_id != 0 && gc->thread_id != _glthread_GetID()) {
-      __glXGenerateError(dpy, gc, gc->xid,
-                         BadAccess, X_GLXMakeContextCurrent);
-      return False;
-   }
-
+   __glXLock();
    if (oldGC == gc &&
-       gc->currentDrawable == draw && gc->currentReadable == read)
+       gc->currentDrawable == draw && gc->currentReadable == read) {
+      __glXUnlock();
       return True;
+   }
 
    if (oldGC != &dummyContext) {
-      oldGC->vtable->unbind(oldGC, gc);
-      oldGC->currentDpy = 0;
-      oldGC->currentDrawable = None;
-      oldGC->currentReadable = None;
-      oldGC->thread_id = 0;
+      if (--oldGC->thread_refcount == 0) {
+	 oldGC->vtable->unbind(oldGC, gc);
+	 oldGC->currentDpy = 0;
+	 oldGC->currentDrawable = None;
+	 oldGC->currentReadable = None;
+
+	 if (oldGC->xid == None && oldGC != gc) {
+	    /* We are switching away from a context that was
+	     * previously destroyed, so we need to free the memory
+	     * for the old handle. */
+	    oldGC->vtable->destroy(oldGC);
+	 }
+      }
    }
 
    if (gc) {
-      gc->currentDpy = dpy;
-      gc->currentDrawable = draw;
-      gc->currentReadable = read;
-      gc->thread_id = _glthread_GetID();
+      if (gc->thread_refcount++ == 0) {
+	 gc->currentDpy = dpy;
+	 gc->currentDrawable = draw;
+	 gc->currentReadable = read;
+      }
       __glXSetCurrentContext(gc);
       ret = gc->vtable->bind(gc, oldGC, draw, read);
    } else {
       __glXSetCurrentContextNull();
    }
 
-   if (oldGC != &dummyContext && oldGC->xid == None && oldGC != gc) {
-      /* We are switching away from a context that was
-       * previously destroyed, so we need to free the memory
-       * for the old handle. */
-      oldGC->vtable->destroy(oldGC);
-   }
+   __glXUnlock();
 
    if (ret) {
       __glXGenerateError(dpy, gc, None, ret, X_GLXMakeContextCurrent);
diff --git a/src/glx/glxextensions.c b/src/glx/glxextensions.c
index 3a0e64c..ffd4664 100644
--- a/src/glx/glxextensions.c
+++ b/src/glx/glxextensions.c
@@ -90,6 +90,7 @@ static const struct extension_info known_glx_extensions[] = {
    { GLX(MESA_agp_offset),             VER(0,0), N, N, N, Y }, /* Deprecated */
    { GLX(MESA_copy_sub_buffer),        VER(0,0), Y, N, N, N },
 #endif
+   { GLX(MESA_multithread_makecurrent),VER(0,0), Y, N, Y, N },
    { GLX(MESA_pixmap_colormap),        VER(0,0), N, N, N, N }, /* Deprecated */
    { GLX(MESA_release_buffers),        VER(0,0), N, N, N, N }, /* Deprecated */
 #ifdef GLX_USE_APPLEGL
diff --git a/src/glx/glxextensions.h b/src/glx/glxextensions.h
index 7877661..333b3f9 100644
--- a/src/glx/glxextensions.h
+++ b/src/glx/glxextensions.h
@@ -43,6 +43,7 @@ enum
    MESA_agp_offset_bit,
    MESA_copy_sub_buffer_bit,
    MESA_depth_float_bit,
+   MESA_multithread_makecurrent_bit,
    MESA_pixmap_colormap_bit,
    MESA_release_buffers_bit,
    MESA_swap_control_bit,
-- 
1.7.4.1



More information about the mesa-dev mailing list