Mesa Implementation Notes
This document is an overview of the internal structure of Mesa and is
meant for those who are interested in modifying or enhancing Mesa, or
just curious.
NOTE: As of version 2.0 of Mesa, some of this information is
out of date.
Library State and Contexts
OpenGL uses the notion of a state machine. Mesa encapsulates the state
in one large struct: gl_context, as seen in context.h. The global
variable CC is is the current state, or "Current Context." Contexts are
allocated dynamically with malloc. When a context is made current its
struct is copied to CC. When a context is made non-current the CC is
copied back to the context pointer. CC is not a pointer because I
believe it's faster to address a global variable's field rather than
dereference a pointer. This is important because CC is referenced a lot.
The gl_context struct actually contains a number of sub structs which
exactly correspond to OpenGL's attribute groups. This organization
made glPushAttrib and glPopAttrib trivial to implement and proved to be
a good way of organizing the state variables.
Vertex buffer
The vertex buffer (see vb.[ch]) is used to accumulate glVertex and
associated data (normals, colors, etc) between glBegin/glEnd pairs.
When the buffer is filled or glEnd is called the buffer is flushed (see
draw.c). Flushing consists of transforming vertices and normals by the
current matrices, computing lighting, clip testing, fogging, etc.
Originally, Mesa didn't accumulate vertices in this way. Instead, glVertex
transformed and lit then buffered each vertex as it was received. When
enough vertices to draw the primitive (1 for points, 2 for lines, >2 for
polygons) were accumulated the primitive was drawn and the buffer
cleared.
The new approach of buffering many vertices and then transforming, lighting
and clip testing is faster because it's done in a "vectorized" manner. See
gl_transform_points() in xform.c for an example. Also, vertices shared
between primitives (i.e. GL_LINE_STRIP) are only transformed once.
The only complication is clipping. If no vertices in the vertex buffer
have their clip flag set, the rasterization functions can be applied
directly to the vertex buffer. Otherwise, a clipping function is called
before rasterizing each primitive. If clipping introduces new vertices
they will be stored at the end of the vertex buffer.
For best performance Mesa clients should try to maximize the number of
vertices between glBegin/End pairs and used connected primitives when
possible.
Rasterization
The point, line and polygon rasterizers are called via the CC.PointFunc,
CC.LineFunc, and CC.PolygonFunc function pointers. Whenever the library
state is changed in a significant way, the CC.NewState flag is raised.
When glBegin is called CC.NewState is checked. If the flag is set we
re-evaluate the state to determine what rasterizers to use. Special
purpose rasterizers are selected according to the status of certain state
variables such as flat vs smooth shading, depth-buffered vs. non-depth-
buffered, etc. The gl_set_point|line|polygon_function() functions do
this analysis. They in turn query the device driver for "accelerated"
rasterizers. More on that later.
In general, typical states (depth-buffered & smooth-shading) result in
optimized rasterizers being selected. Non-typical states (stenciling,
blending, stippling) result in slower, general purpose rasterizers being
selected.
For best performance, Mesa clients should group state changes (glEnable,
glDisable, glShadeModel, glLight*, glMaterial) together to minimize the
number of times the rasterizer selectors are called.
Pixel (fragment) buffer
The general purpose point, line and bitmap rasterizers accumulate
fragments (pixels plus color, depth, texture coords) in the PB (Pixel
Buffer) struct seen in pb.[ch]. When the pixel buffer is full or glEnd
is called the pixel buffer is flushed. This includes clipping the frag-
ments against the window, depth testing, stenciling, blending, stippling,
etc. Finally, the pixel buffer's pixels are drawn by calling one of
device driver functions.
Pixel spans
The polygon, glDrawPixels, and glCopyPixels functions generate horizontal
runs of pixels called spans. Spans are processed in span.c. Processing
includes window clipping, depth testing, stenciling, texturing, etc.)
After processing the span is written to the frame buffer by calling a
device driver function.
Device Driver
The struct dd_function_table seen in dd.h, defines the device driver
functions. By using a table of pointers, the device driver can be
changed dynamically at runtime. For example, the X/Mesa and OS/Mesa
(Off-Screen rendering) device drivers can co-exist in one library and
be selected at runtime. The DD variable instantiates the struct
dd_function_table.
In addition to the device driver table functions, each Mesa driver
has its own set of unique interface functions. For example, the X/Mesa
driver has the XMesaCreateContext, XMesaBindWindow, and XMesaSwapBuffers
functions while the Windows/Mesa interface has WMesaCreateContext,
WMesaPaletteChange and WMesaSwapBuffers. New Mesa drivers need to both
implement the dd_function_table functions and define a set of unique
window system or operating system-specific interface functions.
The device driver functions can roughly be divided into four groups:
-
pixel span functions which read or write horizontal runs of RGB or
color-index pixels. Each function takes an array of mask flags
which indicate whether or not to plot each pixel in the span.
-
pixel array functions which are very similar to the pixel span
functions except that they're used to read or write arrays of pixels
at random locations rather than horizontal runs.
-
miscellaneous functions for window clearing, setting the current
drawing color, enabling/disabling dithering, returning the current
frame buffer size, specifying the window clear color, synchroniz-
ation, etc. Most of these functions directly correspond to higher
level OpenGL functions.
-
if your graphics hardware or operating system provides accelerated
point, line and polygon rendering operations, they can be utilized
through the get_points_func, get_line_func, and get_polygon_func
functions. Mesa will call these functions to "ask" the device
driver for accelerated functions. If the device driver can provide
an appropriate renderer, given the current Mesa state, then a
pointer to that function can be returned. Otherwise the
get_points_func, get_line_func, and get_polygon_func functions can
just return NULL.
Even if hardware accelerated renderers aren't available, the device
driver may implement tuned, special purpose code for common kinds
of points, lines or polygons. The X/Mesa device driver does this
for a number of lines and polygons. See the xmesa3.c file.
Overall Organization
The overall relation of the core Mesa library, X device driver/interface,
toolkits and application programs is shown in this diagram:
+-----------------------------------------------------------+
| |
| Application Programs |
| |
| +- glu.h -+- aux.h tk.h glut.h -+ |
| | | | |
| | GLU | aux, tk or GLUT | |
| | | toolkits | |
| | | | |
+---------- gl.h ------------+-------- glx.h ----+ |
| | | |
| Mesa core | GLX functions | |
| | | |
+---------- dd.h ------------+------------- xmesa.h --------+
| |
| XMesa* and device driver functions |
| |
+-----------------------------------------------------------+
| Hardware/OS/Window System |
+-----------------------------------------------------------+
Last updated on Jan 18, 1996 by
brianp@ssec.wisc.edu.