Course 24: OpenGL and Window System Integration

OpenGL/Mesa Off-screen Rendering


1. Introduction

Normally, OpenGL is used for rendering into a window which is displayed on your computer's screen. But sometimes it's useful to render into an image buffer which is not displayed. This is called off-screen rendering.

Some uses of off-screen rendering include:

Generaly, off-screen rendering is not a core part of OpenGL; it's provided by an OpenGL window system interface such as GLX or WGL. Some systems have more than one facility for off-screen rendering, each with its own advantages and disadvantages.

The following sections describe the off-screen rendering facilities for WGL, GLX and Mesa with an emphasis on portablity and performance trade-offs.

2. Microsoft OpenGL Off-Screen Rendering

OpenGL for Windows supports off-screen rendering into Windows device-independent bitmaps.


Cons: Basically, a bitmap is created with CreateDIBSection. A pixel format with the PFD_DRAW_TO_BITMAP, PFD_SUPPORT_OPENGL, PFD_SUPPORT_GDI flags must be chosen. After creating a WGL context and binding it, OpenGL rendering can proceed.

3. GLX Pixmaps

A GLX pixmap is an X Pixmap augmented with a set of ancillary buffers such as a depth buffer, stencil buffer or accumulation buffer.


Cons: The basic steps to create a GLX pixmap are:
  1. Call XOpenDisplay to open an X display connection.
  2. Select an X visual with glXChooseVisual.
  3. Create an X pixmap with XCreatePixmap specifying the depth of the X visual.
  4. Create the GLX pixmap with glXCreateGLXPixmap.
The GLXPixmap handle returned by glXCreateGLXPixmap may be passed to glXMakeCurrent to bind an OpenGL rendering context to the GLX pixmap. Rendering into the GLX pixmap may then begin.

The contents of a GLX pixmap may be read back with glReadPixels or XGetImage.

4. SGI pbuffers

Pbuffers are an OpenGL extension available on recent SGI systems. It is an experimental extension- it may be changed in the future. The purpose of pbuffers is to allow hardware accelerated rendering to an off-screen buffer, possibly with pixel formats which aren't normally supported by the X display.


Cons: If you are using an SGI system and need accelerated off-screen rendering then pbuffers should be considered. Otherwise, GLX pixmaps are a more attractive off-screen rendering solution.

With that in mind let us consider pbuffers in more detail.

The pbuffers extension name is GLX_SGIX_pbuffers. Prerequisite to the pbuffers extension is the exerimental fbconfig extension (GLX_SGIX_fbconfig).

The fbconfig extension was introduced for several reasons:

For more information about the fbconfig extension see the fbconfig.txt file.

Pbuffer applications must test for both the GLX_SGIX_pbuffers and GLX_SGIX_fbconfig extensions. See the Using OpenGL Extensions document for details on extension testing. If either extension is not available the application should fall back to using GLX pixmaps.

The basic steps for creating a pbuffer are:

  1. Call XOpenDisplay to open an X display connection.
  2. Get a GLXFBConfigSGIX handle by calling glXChooseFBConfigSGIX
  3. Create a pbuffer by calling glXCreateGLXPbuffer
Several difficulties may arise during these seemingly simple steps: These difficulties basically boil down to the fact that pbuffers are allocated from the frame buffer which is, in general, of fixed size. Also, the fbconfigs may be staticly configured- a particular combination of buffer attributes may not be supported.

As an example, suppose you need a single-buffered RGB pbuffer with a depth buffer. glXChooseFBConfigSGIX may return a list of several fbconfig candidates. However, there may not be enough memory available in the frame buffer for some or any of those fbconfigs. There may be enough memory for the color buffer but not the depth buffer, for example. Or, it may not be possible to allocate a single buffered pbuffer; only double buffered pbuffers may exist.

The best approach is a nested loop:

    let fbAttribs = list of fbconfig attribute lists
    foreach fbAttrib in fbAttribs do
        let fbConfigs = list returned by glXChooseFBConfigSGIX(fbAttrib)
        foreach fbConfig in fbConfigs do
	    let pBuffer = glXCreateGLXPbufferSGIX(fbConfig)
            if pBuffer then
The course notes CD-ROM includes sample pbuffer code in the pbuffer.trz file. The pbdemo.c program illustrates this approach. See the MakePbuffer function.

The pbutil.c file contains several pbuffer utility functions. The CreatePbuffer handles the X protocol error problem.

The pbinfo.c program is similar to glxinfo. It prints a list of fbconfigs available on your system and whether or not a pbuffer of that config can be created.

System Configuration

Some SGI systems require reconfiguring the display / X server to enable pbuffers (or at least useful pbuffer configurations).

On SGI Impact systems, for example, if you look in the /usr/gfx/ucode/MGRAS/vof/ directory you will find a list of video output formats supported by the Impact architecture. Look for ones with the _pbuf suffix. Use the setmon -x utility to configure your X server to use a pbuffer-enabled video format.

5. Auxiliary Buffers

The OpenGL specification includes auxillary buffers. These are buffers intended for off-screen rendering. They are addressed via the glDrawBuffer and glReadBuffer functions. Up to four auxiliary buffers named GL_AUX0, GL_AUX1, GL_AUX2, and GL_AUX3 are available. The actual number of auxiliary buffers available can be queried with glGetIntegerv(GL_AUX_BUFFERS, numBuffers).



6. Mesa

Mesa includes a special off-screen rendering interface called OSMesa. It's unique in that the interface has no dependencies on any operating system or window system.


Cons: Mesa's off-screen rendering interface is quite simple. Documentation for it may be found in the Mesa README file and there is an example program in the Mesa distribution (demos/osdemo.c).

7. Tiled Rendering

Tiled rendering is a technique in which a large image is produced by tiling together smaller, individually rendered images. It's useful for generating images which are larger than what OpenGL would normally permit.

OpenGL and/or window systems limit the size of rendered imagery in several ways:

The basic technique of tiled rendering is to draw your entire scene for each tile, adjusting the projection and viewport parameters such that when the tiles are assembled there are no seams. Unfortunately, this is easier said than done. To make tiled rendering easier I have developed a tile rendering utility library for this course.

Here is a modified excerpt of the trdemo1.c example program which demonstrates how to use the tr (tile rendering) library:

static void Display(void)
   GLubyte *image;
   TRcontext *tr;

   /* allocate final image buffer */
   image = malloc(WindowWidth * WindowHeight * 4 * sizeof(GLubyte));
   if (!image) {
      printf("Malloc failed!\n");

   /* Setup tiled rendering.  Each tile is TILESIZE x TILESIZE pixels. */
   tr = trNew();
   trTileSize(tr, TILESIZE, TILESIZE);
   trImageSize(tr, WindowWidth, WindowHeight);
   trImageBuffer(tr, GL_RGBA, GL_UNSIGNED_BYTE, image);
   if (Perspective)
      trFrustum(tr, -1.0, 1.0, -1.0, 1.0, 5.0, 25.0);
      trOrtho(tr, -3.0, 3.0, -3.0, 3.0, -3.0, 3.0);

   /* Draw tiles */
   do {
   } while (trEndTile(tr));


   /* 'image' buffer now contains the final image.
    * You could now print it, write it to a file, etc.
The basic steps are:
  1. Allocate memory for the final image.
  2. Create a tile rendering context with trNew.
  3. Call trTileSize to specify the tile size.
  4. Call trImageSize to specify the final image size.
  5. Call trImageBuffer to specify where the final image is to be stored.
  6. Setup a perspective or orthographic projection with trFrustum or trOrtho.
  7. Call the trBeginTile and trEndTile functions inside a loop which surrounds your scene drawing function until trEndTile returns zero.
  8. Free the tile rendering context with trDelete.
The final image is typically written to a file or sent to a printer.

There is one caveat to this utility library: glRasterPos, glDrawPixels and glBitmap may be troublesome. The problem is that if glRasterPos specifies a coordinate which falls outside the current viewport, the current raster position becomes invalid. If the current raster position is invalid subsequent calls to glDrawPixels or glBitmap will have no consequence.

The solution to this problem is the trRasterPos3f function. It works just like glRasterPos3f but doesn't suffer from the invalid raster position problem. See the trdemo1.c program for example usage.

The trdemo2.c example demonstrates how to generate very large image files without allocating a full-size image buffer.

Last edited on April 29, 1997 by Brian Paul.