[cairo] Performance OpenGL backend

Ralf mock at cipsoft.com
Tue Feb 23 03:19:48 PST 2010

Of course, here is how I did it:

First I took the boilerplate code for creating a gl surface, and 
modified it to use
my own GLXContext, my own Display*, and my own Window*.
All of these are created when Ogre3D initilises, so these are the 
get....Someting calls in the code below.
Also, so far I did not make any effort in cleaning things up correctly 
(or error handling), so don't check the code for this.
All changes I made to cairo code are in cairo-gl-surface.c (and in the 
header of course)

You will notice the last parameter TextureID in the signature of the 
function: this is actually the fbo texture
handle created completely by Ogre (as a rendertarget).
You will also notice the call to cairo_gl_surface_create inside the 
function, which also passes this
TextureID to cairo. I made a little adjustment to the original cairo 
code to actually use this TextureID, when given.
(see also code below).

Secondly, whenever I want to start calling cairo drawing functions, I 
have to save the whole rendering context of Ogre,
and select the cairo fbo target again into the gl context.
I wrote a new small function for cairo to do this. (see also code below).
As soon as cairo finishes its drawings, I further use this texture 
inside Ogre for following render passes.

Hope this sheds some light on how this seems to work with Ogre (or any 
other render engine, I guess).
This might as well be not a really elegant way to integrate this into 
Ogre, 'cause there are many expensive
graphics card context switches happening at the moment. But so far I did 
not find anything better ;-)

If you need more info, please let me know.


This is the function to create the surface (with all original code 
commented out):

static cairo_surface_t * _cairo_boilerplate_gl_create_surface (const 
char     *name,
     cairo_content_t     content,
     double        width,
     double        height,
     double        max_width,
     double        max_height,
//               cairo_boilerplate_mode_t    mode,
     int       id,
     void      **closure,
     unsigned int TextureID)
   int rgba_attribs[] = { GLX_RGBA,
     GLX_RED_SIZE, 1,
     GLX_BLUE_SIZE, 1,
     None };
     int rgb_attribs[] = { GLX_RGBA,
       GLX_RED_SIZE, 1,
       GLX_GREEN_SIZE, 1,
       GLX_BLUE_SIZE, 1,
       None };

       XVisualInfo *visinfo;
       GLXContext ctx;
       gl_target_closure_t *gltc;
       cairo_surface_t *surface;
       Display *dpy;

       gltc = (gl_target_closure_t *) calloc (1, sizeof 
       *closure = gltc;

       if (width == 0)
         width = 1;
       if (height == 0)
         height = 1;

//       dpy = XOpenDisplay (NULL);
//       gltc->dpy = dpy;
//       if (!gltc->dpy) {
//         fprintf (stderr, "Failed to open display: %s\n", 
//         free (gltc);
//         return NULL;
//       }
//       if (content == CAIRO_CONTENT_COLOR)
//         visinfo = glXChooseVisual (dpy, DefaultScreen (dpy), 
//       else
//         visinfo = glXChooseVisual (dpy, DefaultScreen (dpy), 
//       if (visinfo == NULL) {
//         fprintf (stderr, "Failed to create RGB, double-buffered 
//         XCloseDisplay (dpy);
//         free (gltc);
//         return NULL;
//       }

      // ctx = glXCreateContext (dpy, visinfo, NULL, True);

       // get these variables from Ogre
      ctx = (GLXContext)g_RenderSystem->getMainGLContext();
      dpy = (Display*)g_RenderSystem->getMainGLDisplay();
      Window* win = (Window*)g_pRenderWindow->getXWindow();
       PRINT(3, "----------->GLXContext is %x.", ctx);

//       XFree (visinfo);

       gltc->ctx = ctx;
       gltc->device = cairo_glx_device_create (dpy, ctx, win);

       gltc->surface = surface = cairo_gl_surface_create (gltc->device,
                                                         ceil (width),
                                                         ceil (height),

//     if (cairo_surface_status (surface))
//       _cairo_boilerplate_gl_cleanup (gltc);

       return surface;

This is how I call it:

     ///// test of the new cairo opengl backend
     mObject = NULL;
     void* closure;
     m_pSurface =
         m_TexWidth, m_TexHeight,
         m_TexWidth + 1, m_TexHeight + 1,
         m_TexturePtr->getGLID());    // m_TexturePtr is a smart pointer 
to an Ogre texture

This is the change in _cairo_gl_surface_create_scratch to use the given 

     /* Create the texture used to store the surface's data. */
     if (TextureID == 0) {
       glGenTextures (1, &surface->tex);
       printf("_cairo_gl_surface_create_scratch creating own texture, 
texture id %d \n", surface->tex);
     } else {
       surface->tex = TextureID;
       printf("_cairo_gl_surface_create_scratch using texture id %d \n", 

This is the little function I write for cairo to set the cairo texture 

cairo_gl_set_surface_active (cairo_surface_t *abstract_surface)
     cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface;

     glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, surface->fb);
     glDrawBuffer (GL_COLOR_ATTACHMENT0_EXT);
     glReadBuffer (GL_COLOR_ATTACHMENT0_EXT);

     cairo_status_t status;
     return status;    // would be: check for OpenGL errors
//     return _cairo_gl_surface_clear(surface);

On 02/22/2010 05:39 PM, Eric Anholt wrote:
> On Wed, 17 Feb 2010 17:17:02 +0000, Chris Wilson<chris at chris-wilson.co.uk>  wrote:
>> On Wed, 17 Feb 2010 12:23:48 +0100, Ralf<mock at cipsoft.com>  wrote:
>>> So can that actually be the case where the opengl backend is slower,
>>> when rendering many curves which
>>> only fill a few pixels in the final picture or am I using the backend in
>>> the wrong way ?
>> No, just the common delusion that OpenGL is a panacea for all ills. In
>> this case we currently create scan-lines on the CPU and then emit a vertex
>> buffer containing all the opacities to be composited. Depending on the
>> overhead and speed of the GPU, it is easily conceivable that simply using
>> the CPU on local memory will be faster than the offload. Of course, we
>> wish to improve our algorithms for the more advanced GPUs, so please note
>> that the OpenGL backend is a work in progress.
>> If you can share your experience on how you integrated cairo-gl with
>> Ogre3D, that will be great - and help us reflect on the gl backend api
>> before it is frozen. Similarly, if you can profile the bottlenecks on the
>> -image and -gl (and other backends) that will be very useful as well. One
>> technique you can use is to record an application trace using cairo-trace,
>> and submit it for inclusion in cairo-traces so that we can tune cairo for
>> your workload.
>> I hope you are having fun with Cairo, and together we can make it better!
>> :)
> Second on "we're interested in how you did the integration" -- it's the
> big thing I haven't figured out how to handle yet.  Any experience you
> can offer from your experiments would be useful.

Ralf Mock - Senior Programmer
CipSoft GmbH, Gabelsbergerstraße 11, 93047 Regensburg, Germany
Fon: +49 941 630828-0, Fax: +49 941 630828-20
Management: Stephan Börzsönyi, Guido Lübke, Ulrich Schlott, Stephan Vogler
Register: Amtsgericht Regensburg, HRB 8295
USt-IdNr: DE216262082

More information about the cairo mailing list