mesa: Add new MESA_multithread_makecurrent extension.

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.
This commit is contained in:
Eric Anholt 2011-02-03 17:26:02 -08:00
parent 132dc0b6d2
commit 49d7e48b33
5 changed files with 173 additions and 25 deletions

View file

@ -0,0 +1,135 @@
Name
MESA_multithread_makecurrent
Name Strings
GLX_MESA_multithread_makecurrent
Contact
Eric Anholt (eric@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.

View file

@ -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];
};

View file

@ -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) {
__glXUnlock();
return True;
}
if (oldGC == gc &&
gc->currentDrawable == draw && gc->currentReadable == read)
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);

View file

@ -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

View file

@ -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,