The Mesa 3-D Graphics Library

A White Paper

Brian Paul

Second Edition, April 1997



Abstract

Mesa is a free 3-D graphics library which uses the OpenGL API and semantics. It works on most modern computers allowing people without OpenGL to write and use OpenGL-style applications. This paper gives an overview of Mesa and describes a bit of its implementation.


Contents



1. Introduction

Mesa began as an experiment in writing a 3-D graphics library. After about a year of "spare time" development it was released on the Internet. It has since evolved with the help of many contributors to the point where it is a viable and popular alternative to OpenGL.

In the spirit of free software, Mesa is distributed under the terms of the GNU library copyright.

The Mesa distribution includes implementations of the core OpenGL library functions, the GLU utility functions, the aux and tk toolkits, Xt/Motif widgets, drivers for X11, Microsoft Windows '95/NT and DOS, NeXTStep, and many demonstration programs. Macintosh and Amiga drivers are available separately.

Mesa compiles easily, requiring only an ANSI C compiler and standard development headers and libraries.

From the application programmer's point of view, Mesa is a nearly seemless replacement for OpenGL. The Mesa header files are named the same as OpenGL's (GL/gl.h, GL/glu.h, GL/glx.h, etc) and contain equivalent datatypes, constants and function prototypes. The Mesa library files may be renamed to match the typical OpenGL library names and locations. On some operating systems Mesa may be built as a shared library.

After Mesa has been installed most OpenGL applications should compile and execute without modification.

Since version 2.0 of Mesa the OpenGL 1.1 API is implemented.



2. Mesa vs. OpenGL

While Mesa uses the OpenGL API and follows the OpenGL specification very closely, it is important to understand that Mesa is not a true implementation of OpenGL. Official OpenGL products are licensed and must completely implement the OpenGL specification and pass a suite of conformance tests. Mesa meets none of these requirements.

At first, Mesa may seem to be a competitor to official OpenGL products. Actually, Mesa has helped to promote the OpenGL API by expanding the range of computers which may execute OpenGL programs. There are many systems which are not supported by OpenGL vendors but can run Mesa instead. People who are curious about OpenGL may try Mesa at no cost and later purchase an OpenGL implementation which perhaps utilizes 3-D graphics hardware. Mesa has been very popular in computer graphics courses. Many students and colleges without the resources to obtain commercial OpenGL implementations successfully use Mesa instead.

Mesa does not implement the full OpenGL specification. For example, antialiasing, trimmed NURBS, and a few glGet* functions are not yet implemented. The GLX interface is only an emulation; it does not generate GLX protocol. It is expected that these features will eventually be implemented.

Mesa doesn't typically perform as well as commercial OpenGL implementations for several reasons. First, portability to a wide range of computers is considered more important than optimizing for a particular architecture. Second, the features of the underlying hardware can't be directly accessed since Mesa exists as a software library above the operating system and window system programming interfaces. And finally, Mesa's development is not supported by any sort of development team. Only so much can be accomplished by people working in their spare time.

In other respects Mesa has some advantages over OpenGL.



3. Implementation

Mesa is written in ANSI C. The core library contains no operating system or window system dependent code which makes it extremely portable. A special device driver interface insulates the core Mesa library from the underlying operating/window system.

3.1 Library State

OpenGL is designed around the concept of a state machine. In Mesa this state is stored in a large C structure. Much of the state is stored in substructures which directly correspond to the attribute groups such as the polygon group, lighting group and texture group. Pushing and popping of attribute groups is just a matter of copying C structs to and from a stack.

Many API functions simply modify state values and produce no output. Before rendering functions are invoked it is often necessary to evaluate the current state to compute derived state values and setup pointers to specific instances of rendering functions. Lazy evaluation is used to updated the state.

For example, Mesa has many instances of specialized polygon drawing functions. The function to use depends on the state of smooth vs flat shading, dithering, depth testing, texturing, etc. When any of these state values are changed the new state flag is set. When glBegin is called the new state flag is tested and if set, the state is evaluated to select the specialized polygon function and the flag is cleared.

3.2 Point, Line and Polygon Rendering

Arguably the most important feature of Mesa is efficient point, line and polygon rendering. The two major components of this are vertex transformation and rasterization.

Vertices specified between glBegin and glEnd are accumulated in a vertex buffer. When the buffer is full or glEnd is called the buffer is processed. Processing the vertex buffer includes transforming vertices from object coordinates to eye coordinates, lighting, transforming eye coordinates to clip coordinates, clip testing, and mapping clip coordinates to window coordinates.

Each transformation and clip test stage is implemented in a tight loop which compilers can unroll for efficient executution. The size of the vertex buffer was chosen so that all vertex data touched in the transformation loops will fit in a 16KB CPU data cache.

Several optimization are used during transformation. The modelview and projection matrices often have particular elements with values of zero or one. These elements are tested to determine if simplified vector/matrix multiplications can be used. Depending on the current lighting parameters, either a full-featured or specialized, optimized lighting function is used. Lookup tables are used to compute the exponential spotlight and material shininess functions.

After a vertex buffer has been processed it is rendered as a set of points, lines or polygons as specified by glBegin.

Arrays of points are rendered by either calling a specialized device driver function or by falling back to a core Mesa drawing function. Points whose clip flag is set are discarded.

Line segments are clipped if either endpoint's clip flag is set. Then, the line is rasterized by calling either a specialized device driver function or a fallback Mesa line drawing function. Different line drawing functions are called for flat or smooth shading, RGB or color index mode, texturing, etc.

Polygons are clipped with the Sutherland-Hodgman algorithm if any of the vertex clip flags are set. Next, the equation of the plane containing the polygon is computed. The coefficients of the plane equation ax+by+cz=d are used for determining front/back orientation and implementing the polygon offset feature.

Polygons with more than three vertices are decomposed into triangles. Then, as with line segments, the triangle is rasterized either by a specialized device driver function or by a core fall-back function.

The specialized device driver functions for point, line and triangle rendering take vertices as input and directly modify the frame buffer. Alternatively, the fallback rendering functions in Mesa handle rendering of primitives with arbitrary raster operations. Point, line and bitmap functions generate fragments which are stored in a pixel buffer. The triangle rasterizers and glDrawPixels generate horizontal runs of pixels called spans. The pixel buffer and spans are subjected to fragment processing before being written to the frame buffer.

3.3 Fragment processing

Fragments are the pixels generated by rasterization augmented with auxiliary information such as color, depth (Z) and texture coordinates. OpenGL defines an extremely flexible fragment processing pipeline which includes texturing, fogging, clipping, scissoring, alpha testing, stenciling, depth testing, blending, dithering, bitwise logic operations, and masking.

Pixel buffer and span-based fragment processing are very similar, the only difference is that the pixel buffer stores fragments with arbitrary window coordinates while spans are continuous horizontal runs of fragments.

Since fragments may be culled during processing, each fragment has a write flag associated with it. Initially, all fragments have their write flags set to true. Clipping, scissoring, alpha testing, stenciling, and depth testing may set a flag to false to indicate that it should not be considered in further stages. In the end, only those fragments with their flags set are written to the color buffer.

Each stage of fragment processing is implemented in succession with code similar to:

if (stage is enabled) {
    for (each fragment in the buffer or span) {
        apply the fragment operation,
            possibly setting some write flags to false
    }
}
Finally, fragments are written to the color buffer by device driver functions similar to:

for (each fragment) {
    if (fragment flag is true) {
        write fragment color to color buffer
    }
}
The special cases of all write flags set to true or false are handled appropriately. Also, optimized code is used when all fragments have the same color.

The only fragment operation which must be handled below the device driver level is dithering. Depth testing, bitwise logic operators and masking may optionally be implemented by the device driver.

3.4 Device Driver Functions

A Mesa driver implements two things:
  1. A public OpenGL/window system API (the GLX API, for example)
  2. A set of priver driver functions (line and triangle drawing functions, for example)

The device driver interface is a set of function pointers which point to implementations specific to the window system. It includes functions for:

Some device driver functions are optional. If a particular function isn't implemented by the device driver then we fall back to an internal Mesa function.

The next section explains this in more detail for the X device driver.


3.5 The X Device Driver

The X device driver is the most mature of the Mesa device drivers so it is the example we elaborate upon.

3.5.1 GLX Emulation

Mesa's interface to the X Window System is defined by the X/Mesa interface. There are X/Mesa functions for creating rendering contexts, destroying contexts, binding contexts to windows and pixmaps, swapping color buffers and querying the current context. This interface is not intented for use by application programmers. It's purpose is to support Mesa's GLX emulation.

Mesa only emulates the GLX interface since a true implementation requires hooks into the X server. Mesa and its GLX can be though of as a translator which converts OpenGL API functions to Xlib commands. The nice side-effect of this is that Mesa can remotely render to any X server, even if the X server does not have the GLX server extension. Operating systems which support shared libraries can substitute Mesa for OpenGL at runtime, allowing OpenGL applications to be displayed on non-GLX capable X servers without recompiling.

Since it's an emulation, Mesa's GLX is not 100% compatible with OpenGL's GLX. In several ways is actually superior. For example, while OpenGL only supports RGB rendering into TrueColor or DirectColor X visuals, Mesa allows RGB rendering into virtually any type and depth of X visual. This is an important feature since many X servers don't offer TrueColor or DirectColor visuals. Other visuals are supported by dithering or converting RGB values to gray levels.

This introduces two potential incompatibilities with OpenGL's GLX.

The first problem is solved with a special Mesa extension to GLX. The second problem can usually be fixed by modifying the application's GLX code.

3.5.2 Pixmaps vs XImages

Images in X can be stored in one of two formats. Pixmaps are stored in the X server and cannot be directly addressed by an X client. XImages are stored in the client's address space and may be directly addressed.

When operating in single buffered mode, rendering is directed into an X window. When operating in double buffered mode, rendering is directed into either a Pixmap or XImage. A Pixmap can be accessed in the same way as a window (both are considered to be drawables). Whether a Pixmap or XImage gives best performance depends on a number of factors.

Using a Pixmap can be quite efficient for rendering plain, flat-shaded points, lines and polygons since the intrinsic X point, line and polygon drawing functions can be used. Performance is relatively good whether displaying locally or remotely. However, when using smooth shading or per-pixel fragment operations pixels must be drawn individually with XSetForeground and XDrawPoint calls. The amount of data transferred from the client to X server is directly proportional to the number of X calls made. For XSetForeground/XDrawPoint rendering this is usually unacceptably slow.

In most cases using an XImage yields best performance in double buffer mode. The reason is individual pixels can be directly "poked" into the image since it resides in the client's address space. Front/back buffer swapping is implemented by copying the XImage to the X window. The X Shared Memory extension is used when displaying on the local host to accelerate this operation. In the case of remote display, the amount of data transferred from the client to the X server is directly proportional to the window size and not the number of pixels generated during rendering.

Programmers should note that double buffering using an XImage can be faster than single buffering.

3.5.3 Pixel Processing

The most important factor in device driver performance is efficient access to the frame/image buffer for reading and writing fragments.

The code for writing RGB pixels to the color buffer could be expressed as:

for (each pixel i) {
    pixel_value = convert_rgb_to_pixel( red[i], green[i], blue[i] );
    put_pixel( x[i], y[i], pixel_value );
}
However, this would be very inefficient since the convert_rgb_to_pixel and put_pixel functions must cope with many types of X visuals and depths. The best method to convert RGB values to pixel values depends on the X visual. The best method to write pixels to the color buffer depends on whether the buffer is implemented as an X Pixmap or XImage. Therefore, almost all inner-loops in the X device driver are optimized for special pixel formats.

For example, there are specialized span and pixel-array writing functions for 24-bit TrueColor, 16-bit TrueColor, 8-bit PseudoColor, N-bit GrayScale, etc. Furthermore, there are many line and triangle rasterizer functions optimized for these pixels formats with popular combination of flat/smooth shading, depth-tested/non-depth-tested rasterization modes.

When the device driver's UpdateState state function is called the driver's pointers for span, line and triangle functions are updated to point to the appropriate optimized function. If no optimized function satisfies the current library state then a core Mesa fall-back function is used instead.

The device driver's point, line and triangle functions are also used for hardware acceleration. In this case the driver function will simply set hardware registers and trigger an interupt or DMA to make the hardware render the primitive.



4. Extensions

Mesa implements several popular OpenGL extensions and adds a few of its own.

4.1 OpenGL Extensions

Mesa has the following OpenGL extensions: Several, such as texture objects and vertex arrays, are also standard OpenGL 1.1 (Mesa 2.x) features. Implementing them both as standard features and as extensions is simply a portability convenience to programmers.


4.2 Mesa Extensions

Like OpenGL, Mesa can have extensions. At this time, Mesa has four unique extensions.

GL_MESA_window_pos

This extension adds the glWindowPos*MESA functions. These functions are convenient alternatives to glRasterPos* because they set the current raster position to a specific window coordinate, bypassing the usual modelview, projection and viewport transformations. This is especially useful for setting the position for glDrawPixels or glBitmap to a desired window coordinate.

For glWindowPosMESA4f(x,y,z,w) the x, y, z, and w parameters directly set the current raster position except that z is clamped to the range [0,1]. The current raster position valid flag is always set to true. The current raster distance is set to zero. The current raster color and texture coordinate are updated in the same manner as for glRasterPos. In selection mode a hit record is always generated.

Programs using OpenGL, not Mesa, may also use the glWindowPos*MESA functions since an implementation of it in terms of standard OpenGL functions is included with Mesa.

Perhaps the GL_MESA_window_pos extension may be incorporated into a future version of OpenGL since it is so convenient.


GL_MESA_resize_buffers

Mesa can't determine when a window is resized. When the on-screen window is resized the ancillary (depth, stencil, accumulation) buffers should be resized. The work-around is for Mesa to query the window size whenever glViewport is called. This is usually sufficient since glViewport is usually called soon after a window has been resized. When this isn't sufficent the programmer can include a call to glResizeBuffersMESA() which forces Mesa to query the current window size and resize the ancillary buffers if needed.


GLX_MESA_release_buffers

Mesa can't determine when an X window has been destroyed. When a window is destroyed the associated ancillary buffers should also be destroyed. As a work-around, Mesa maintains a list of known rendering windows and whenever glXCreateContext or glXDestroyContext are called checks if any of those windows as been recently destroyed. Since this isn't sufficient in all situations a programmer can explicitly tell Mesa to free the ancillary buffers by calling glXReleaseBuffersMESA just before calling XDestroyWindow.


GLX_MESA_pixmap_colormap

This extension adds the GLX function:

GLXPixmap glXCreateGLXPixmapMESA( Display *dpy, XVisualInfo *visual, Pixmap pixmap, Colormap cmap )

It is an alternative to the standard glXCreateGLXPixmap function. Since Mesa supports RGB rendering into any X visual, not just TrueColor or DirectColor, Mesa needs colormap information to convert RGB values into pixel values. An X window carries this information but a pixmap does not. This function associates a colormap to a GLX pixmap.

An application using GLX pixmaps should use the following code to associate a colormap with the GLX pixmap when using Mesa.

#ifdef GLX_MESA_pixmap_colormap
   glxpixmap = glXCreateGLXPixmapMESA( display, xvisualinfo,
                                       xpixmap, colormap );
#else
   glxpixmap = glXCreateGLXPixmap( display, xvisualinfo, xpixmap );
#endif



5. Future Plans

There are a number of things planned in the future for Mesa.

More optimization

Each Mesa release has usually been a bit faster then the previous one. Optimization is an on-going process. Most recently, optimization of vertex transformation, clipping and lighting has been the focus since the rasterization bottleneck is greatly reduced when 3-D hardware is used.

GLX protocol encoding

Steven Parker (sparker@taz.cs.utah.edu) of the University of Utah has written free GLX encoder/decoder software. By integrating the encoder into Mesa, an application linked with Mesa could send true GLX protocol data to a GLX-equipped X server or send ordinary Xlib protocol to non-GLX X servers.

If the GLX X server has 3-D acceleration hardware the Mesa-linked application would use it.

X server integration

Work is underway to integrate Mesa into the XFree86 X server. This implies implementing the GLX decoder and integrating Mesa so that GLX client applications could render to computers running the XFree86 X server.

Hardware acceleration

Recently, 3-D acceleration hardware for personal computers has become very common and affordable. There have been several efforts to support 3-D hardware with Mesa.

The first was a driver for the GLint chipset written by Ken Adams while at Clemson University. Development is now maintained by others at the university. Dr. Robert Geist (rmg@cs.clemson.edu) is the current contact.

The second was a driver for the Cirrus Logic CL5464 chipset written by Peter McDermott while at the University of Texas at Austin. Again, development continues at the university. Contact Adam Seligman (adams@cs.utexas.edu).

The most recent hardware support is for the 3Dfx VooDoo chipset written by David Bucciarelli (tech.hmw@plus.it). This driver is implemented on the 3Dfx GLide rasterization library.

More hardware acceleration projects will probably follow when Mesa has been integrated with XFree86.

Other possibilities

Other long term items for Mesa development include free versions of the GLS (GL Stream encoder/decoder) library, GLC (GL Character rendering) library and the OpenGL debugger. Work has not yet begun on these projects.



6. Summary

Mesa has turned out to be a very useful and popular 3-D library. Its success can be attributed to the fact that the library is free, full featured, reliable, portable and compatible with OpenGL. Many volunteers have contributed to this success.

Mesa has a bright future with many new features planned. No doubt, much of this work will be done by volunteers who share an enthusiasm for computer graphics and free software.


Appendix A

Obtaining Mesa

Mesa can be downloaded via the Mesa home page at http://www.ssec.wisc.edu/~brianp/Mesa.html.




Last edited on April 19, 1997 by Brian Paul.