Old Mesa 3.4.x 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: Based on the original Mesa Implementation Notes by Brian Paul.
Library State and Contexts
OpenGL uses the notion of a state machine. Mesa encapsulates the state in one large structure: gl_context, as seen in types.h.
The gl_context structure actually contains a number of sub structures 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.
The vertices between glBegin and glEnd are accumulated in the vertex buffer (see vb.h and vb.c ). When either the vertex buffer becomes filled or a state change outside the glBegin/glEnd is made, we must flush the buffer. That is, we apply the vertex transformations, compute lighting, fog, texture coordinates etc. Then, we can render the vertices as points, lines or polygons by calling the gl_render_vb() function in render.c.
When we're outside of a glBegin/glEnd pair the information in this structure is retained pending either of the flushing events described above.
Note: 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/glEnd pairs and used connected primitives when possible.
The point, line and polygon rasterizers are called via the, , and function pointers in the dd_function_table driver function pointer table. Whenever the library state is changed in a significant way, the context flag is raised. When glBegin is called 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.
Pixel (fragment) buffer
The general purpose point, line and bitmap rasterizers accumulate fragments (pixels plus color, depth, texture coordinates) in the PB (Pixel Buffer) structure seen in . pb.h and pb.c. When the pixel buffer is full or glEnd is called the pixel buffer is flushed. This includes clipping the fragments against the window, depth testing, stenciling, blending, stippling, etc. Finally, the pixel buffer's pixels are drawn to the display buffer by calling one of device driver functions. The goal is to maximize the number of pixels processed inside loops and to minimize the number of function calls.
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.
- There are three Mesa data types which are meant to be used by device drivers:
- this contains the Mesa rendering state
- this describes the color buffer (RGB vs. CI), whether or not there's a depth buffer, stencil buffer, etc.
- contains pointers to the depth buffer, stencil buffer, accum buffer and alpha buffers.
In OOP terms, GLcontext, GLvisual, and GLframebuffer are base classes which the device driver must derive from.
The structure 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.
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, synchronization, 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 xmesa3.c file. , , and functions. Mesa will call these functions to "ask" the device driver for accelerated functions through the . 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 , , and functions pointers can just be set to 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
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 -+------ glut.h -------+ | | | | | | | | GLU | GLUT | | | | | toolkits | | | | | | | +---------- gl.h ------------+-------- glx.h ----+ | | | | | | Mesa core | GLX functions | | | | | | +---------- dd.h ------------+------------- xmesa.h --------+ | | | XMesa* and device driver functions | | | +-----------------------------------------------------------+ | Hardware/OS/Window System | +-----------------------------------------------------------+