gl: Make code safe for multithreaded access

The code callss the gl device's acquire/release in
cairo_device_acquire/release(). This way, external APIs can use these
functions to prepare for rendering GL.

Also adds code to unset the glx context if it wasn't set when acquiring
the device. This allows multithreaded apps to work fine with just using
cairo APIs, but might introduce penalties in single-threaded
applications.
This commit is contained in:
Benjamin Otte 2010-03-26 15:50:10 +01:00
parent 8f2e82cea3
commit 712919223d
4 changed files with 60 additions and 4 deletions

View file

@ -104,6 +104,8 @@ cairo_egl_device_create (EGLDisplay dpy, EGLContext egl)
ctx->display = dpy;
ctx->context = egl;
ctx->base.acquire = NULL; /* FIXME */
ctx->base.release = NULL; /* FIXME */
ctx->base.make_current = _egl_make_current;
ctx->base.swap_buffers = _egl_swap_buffers;
ctx->base.destroy = _egl_destroy;

View file

@ -136,7 +136,10 @@ typedef struct _cairo_gl_context {
cairo_gl_glyph_cache_t glyph_cache[2];
cairo_list_t fonts;
void (*make_current)(void *ctx, cairo_gl_surface_t *surface);
void (*acquire) (void *ctx);
void (*release) (void *ctx);
void (*make_current) (void *ctx, cairo_gl_surface_t *surface);
void (*swap_buffers)(void *ctx, cairo_gl_surface_t *surface);
void (*destroy) (void *ctx);
} cairo_gl_context_t;

View file

@ -67,6 +67,23 @@ static cairo_bool_t _cairo_surface_is_gl (cairo_surface_t *surface)
return surface->backend == &_cairo_gl_surface_backend;
}
static void
_gl_lock (void *device)
{
cairo_gl_context_t *ctx = (cairo_gl_context_t *) device;
ctx->acquire (ctx);
}
static void
_gl_unlock (void *device)
{
cairo_gl_context_t *ctx = (cairo_gl_context_t *) device;
ctx->release (ctx);
ctx->current_target = NULL;
}
static void
_gl_destroy (void *device)
{
@ -94,7 +111,8 @@ _gl_destroy (void *device)
static const cairo_device_backend_t _cairo_gl_device_backend = {
CAIRO_DEVICE_TYPE_GL,
NULL, NULL, /* lock, unlock */
_gl_lock,
_gl_unlock,
NULL, /* flush */
NULL, /* finish */

View file

@ -52,6 +52,9 @@ typedef struct _cairo_glx_context {
Display *display;
Window dummy_window;
GLXContext context;
GLXContext prev_context;
GLXDrawable prev_drawable;
} cairo_glx_context_t;
typedef struct _cairo_glx_surface {
@ -61,8 +64,20 @@ typedef struct _cairo_glx_surface {
} cairo_glx_surface_t;
static void
_glx_make_current (void *abstract_ctx,
cairo_gl_surface_t *abstract_surface)
_glx_acquire (void *abstract_ctx)
{
cairo_glx_context_t *ctx = abstract_ctx;
ctx->prev_context = glXGetCurrentContext ();
ctx->prev_drawable = glXGetCurrentDrawable ();
/* XXX: This is necessary with current code, but shouldn't be */
if (ctx->prev_context != ctx->context)
glXMakeCurrent (ctx->display, ctx->dummy_window, ctx->context);
}
static void
_glx_make_current (void *abstract_ctx, cairo_gl_surface_t *abstract_surface)
{
cairo_glx_context_t *ctx = abstract_ctx;
cairo_glx_surface_t *surface = (cairo_glx_surface_t *) abstract_surface;
@ -71,6 +86,18 @@ _glx_make_current (void *abstract_ctx,
glXMakeCurrent (ctx->display, surface->win, ctx->context);
}
static void
_glx_release (void *abstract_ctx)
{
cairo_glx_context_t *ctx = abstract_ctx;
if (ctx->prev_context != glXGetCurrentContext () ||
ctx->prev_drawable != glXGetCurrentDrawable ())
glXMakeCurrent (ctx->display,
ctx->prev_drawable,
ctx->prev_context);
}
static void
_glx_swap_buffers (void *abstract_ctx,
cairo_gl_surface_t *abstract_surface)
@ -162,7 +189,11 @@ cairo_glx_device_create (Display *dpy, GLXContext gl_ctx)
ctx->display = dpy;
ctx->dummy_window = dummy;
ctx->context = gl_ctx;
ctx->prev_context = NULL;
ctx->prev_drawable = None;
ctx->base.acquire = _glx_acquire;
ctx->base.release = _glx_release;
ctx->base.make_current = _glx_make_current;
ctx->base.swap_buffers = _glx_swap_buffers;
ctx->base.destroy = _glx_destroy;
@ -173,6 +204,8 @@ cairo_glx_device_create (Display *dpy, GLXContext gl_ctx)
return _cairo_gl_context_create_in_error (status);
}
ctx->base.release (ctx);
return &ctx->base.base;
}