The following is a work in progress introduction to the Mesa project. Feedback on how to improve this information, or what else to cover is appreciated.

Introduction to Mesa through example

by Timothy Arceri

Last updated: 12 Feb 2016

Expected prior knowledge

A basic understanding of programming is expected to get the most out of this guide. Also some basic knowledge of tools such as git, and make would also be helpful. An understanding of how to resolve and install missing build dependencies on your distro is also required as I will not be going into to much detail on this, with package names often differing on distros an attempt to do so would just be a waste of time.

A basic understanding of OpenGL would also be helpful but not necessary.

What this guide is not

I want to make it clear from the start this an introductory guide to Mesa using the implementation of the KHR_debug extension as an example to provide an overview of the Mesa codebase. The KHR_debug extension is implemented in core Mesa and this guide will not jump into the specifics of device driver implementation (I will define what I mean by core Mesa in a following section).

This guide will also not cover the implementation of 3D graphics functions, any attempt to cover these topics would require a lot more time than the two weeks I have allocated to both implementing the KHR_debug extension and creating this introductory guide.

Introduction to the Mesa code base

Once you have cloned the Mesa repository the next step for any budding developer is to begin to understand the layout of the source tree. I suggest the first thing you do is take a look at the sourcetree webpage on the Mesa3d website:

This webpage is also available in the /docs directory in the sourcetree and provides an excellent overview of what lives where and what the code in these directories does.

The most important directory to take note of for implementing the KHR_debug extension is /src/mesa/main from the sourcetree webpage the description is “The core Mesa code (mainly state management) ”. As this extension basically does all its work in on the state for example keeping track of message logs this is the code that we are interested in. I will expand a little on what is meant by state in the understanding an OpenGL specification section of this document but if you are already familiar with OpenGL this concept should be nothing new.

One interesting file to take a look at in this directory is mtypes.h the description in the file says “Main Mesa data structures” basically this is the place to define the data structures to be used in the state (core Mesa). We will later see how we use mtypes.h to define the data structures used in KHR_debug.

The next directory that is important to implementing any extension is /src/mapi/glapi/gen in this directory is code and definitions used to generate some infrastructure code for Mesa such as the dispatch table which basically allows the OpenGL functions to be called by applications.

There is some (unfinished) information on the Mesa website about the dispatch table if you are interested in further details:

The file we are mainly interested in is gl_API.xml this xml file contains the definitions for extensions which are used to auto generate Mesa code. We will explore in more detail the code that is generated when we look at the patches for implementing the KHR_debug extension.

Another thing that may be useful pointing out about the codebase is the build tools that are used to build Mesa. Such as:

Building and Using Mesa from git

The following is a quick guide to building and running Mesa. I focus on building the Intel drivers on Fedora because that's the system I'm working on but the general idea can be applied to all drivers/distros. The radeon drivers have an excellent guide here: much of my information was taken from this guide.

Building Intel drivers on Fedora:

First you may need to install some build dependencies. I needed to install these packages:

$ sudo dnf builddep mesa

Rather than overwrite your distros Mesa install it is recommended you create a new directory. The one we will be using is /opt/xorg this way you can switch back to a stable version of Mesa.

Configure and build Mesa

$ ./ --prefix=/opt/xorg --with-dri-drivers="i965" --enable-gles1 --enable-gles2 --enable-shared-glapi --with-gallium-drivers= --with-egl-platforms=x11,drm --enable-gbm --enable-glx-tls --enable-dri3

$ make

Configuring system to load mesa and libdrm from /opt/xorg

(This is just a direct copy from the Radeon freedesktop wiki)

Now comes the tricky part because you have to configure your system to load correct libraries when any application tries to load them. You have to configure ld to load first from /opt/xorg/lib so custom mesa libraries are loaded. Another tricky part is to load dri driver which requires environment variable set to point to /opt/xorg/lib/dri.

/etc/ (new file) or add to begin of /etc/ if directory doesn't exists.


and to /etc/environment you need to add new environment variable that tells libgl where to look for dri drivers (example


After changes to the files you have to run ldconfig as root to update the linker cache.

You can now test new dri driver in a terminal if you export LIBGL_DRIVERS_PATH. If everything works you can now restart to load new settings for all applications.

Problem with this configuration is that AIGLX doesn't respect it. This means that compiz doesn't get advantage of new driver.

It is possible to get the new libGL and dri drivers used without setting LIBGL_DRIVERS_PATH:

you need to build mesa with --with-dri-driverdir=/opt/xorg/lib/dri/ (or --with-dri-driverdir=/opt/xorg/lib/dri/ for 32-bit)

check that /opt/xorg takes precedence:

$ ldconfig -p | grep (libc6,x86-64, OS ABI: Linux 2.4.20) => /opt/xorg/lib/ (libc6,x86-64, OS ABI: Linux 2.4.20) => /usr/lib/ (libc6,x86-64, OS ABI: Linux 2.4.20) => /opt/xorg/lib/

if system's libGL takes precedence it might be due to OS ABI, in that case build mesa with --enable-glx-tls

Resolving issues with new dri driver not loading:

If you get a message something like this:

libGL error: failed to load driver: i965
libGL error: Try again with LIBGL_DEBUG=verbose for more details.
libGL error: failed to load driver: swrast
libGL error: Try again with LIBGL_DEBUG=verbose for more details.

You can try running this to get more information on why the driver is not loading:

$ LIBGL_DEBUG=verbose glxinfo | grep direct

Regression Testing (Piglit)

An important part of any project is testing. For Mesa we have a test suite called piglit to help us with this. From the piglit readme file:

“Piglit is a collection of automated tests for OpenGL and OpenCL implementations.

The goal of Piglit is to help improve the quality of open source OpenGL and OpenCL drivers by providing developers with a simple means to perform regression tests.”

Piglit is available here:

Installing dependencies

On Fedora I had to install the following:

$ sudo dnf install freeglut-devel python-numpy waffle-devel cmake

The piglit readme provides the following for Ubuntu:

$ sudo apt-get install cmake g++ mesa-common-dev libgl1-mesa-dev python-numpy python-mako freeglut3-dev x11proto-gl-dev libxrender-dev

Building piglit

$ ccmake .

- Set "CMAKE_BUILD_TYPE" to "Debug"

Then press c

$ c

Then 'g' to generate the build system.

$ g

Now you can build everything with make

$ make

If you get an error building:

/usr/lib/gcc/x86_64-redhat-linux/4.8.1/../../../../lib64/ undefined reference to `_glapi_tls_Dispatch'
collect2: error: ld returned 1 exit status

You may need to point your system back to the default GL library rather than the one you have built. Do this by deleting /etc/ and running ldconfig as root once you have built piglit you can always switch back once again.

Running Piglit Tests

Running Piglit tests can be done using the -t parameter or you run the test executable directly from the bin directory.

Something useful to note is that its easy to run only the tests you need to.

For example:

$ ./ -t glsl-1.40 all results

Will run all glsl-1.40 tests

$ ./ -t glsl-1.40@compiler all results

Will run only the glsl-1.40 compiler tests

$ ./ -t glsl-1.40@compiler@gl_F all results

Will run only compiler tests that start with gl_F

$ ./ -t glsl-1.40@compiler@gl_FragCoord.frag all results

Will only run gl_FragCoord.frag

Understanding an OpenGL extensions specification

Obviously if you are going to implement an OpenGL extension in Mesa you need to understand the specification. Specifications can be found on the OpenGL website, the following is a link to the KHR_debug extension:

Rather than spend to much time reinventing the wheel I find its more productive to point you to more well written existing documentation where possible. I suggest you first have a skim over the KHR_debug extension above then follow the link below which does an excellent job explaining the various sections of the specification. Please note that while the link is outdated it still contains a lot of useful and relevant documentation.

The sections we are particularly interested in, in regards to KHR_debug are New Procedures and Functions, New Tokens and New State.

All About OpenGL Extensions:

The other advice I have is to re-read the specification a couple of times it may seem daunting at first. One long page of unformatted text but once text but once you actually read it things should start to piece together nicely in your mind.

Another tip in regard to the functions themselves it can be useful to look at api documentation if its available to give you an alternative explanation on how the functions work. Api documentation for OpenGL 4 can be found here:

Note: If anyone has come across a more recent and better document describing the OpenGL extension specifications please let me know and I'll update this document.

Setting Up the infrastructure code for the KHR_debug extension.

As mentioned earlier Mesa generates some infrastructure code based on the definitions in gl_API.xml so this is where we start setting up the KHR_debug extension.

Basically we are just taking the New Procedures and Functions, New Types, and New Tokens from the spec and putting this information into gl_API.xml its really that simple. I have added a copy of the patch to the Appendix for reference.

Now that we have defined the extension and the auto generate code can be created by the mesa build scripts the next step is to advertise this extension as being available to applications.

This is done by adding an entry to the extension struct in src/mesa/main/extensions.c here is the code:

+ /* KHR extensions */
+ { "GL_KHR_debug", o(dummy_true), GL, 2012 },

Basically we are setting the extension to advertise itself as available always by using dummy_true this is because it is implemented in core Mesa and should always be available on all drivers. If we were not able to set this to always true then we would have to add an entry to the gl_extensions struct in src/mesa/main/mtypes.h which would be set to true/false by the individual driver.

The next step needed for mesa to be able to compile is to create the definitions for the new functions we added to gl_API.xml. This is needed because the auto generated code will be looking for these functions, the code the is expecting these definitions as can be seen in src/mesa/main/api_exec.c This file is generated by the script (obviously you need to try build mesa to create this file). Basically api_exec.c is mapping these new functions to the dispatch table. An snippet of the code generated for our extension can be seen below:

SET_DebugMessageCallback(exec, _mesa_DebugMessageCallback);
SET_DebugMessageControl(exec, _mesa_DebugMessageControl);
SET_DebugMessageInsert(exec, _mesa_DebugMessageInsert);

On the last line in the above snippet you can see a function named _mesa_DebugMessageInsert is being set to DebugMessageInsert in the dispatch table. This means that the format of the function name you use to implement DebugMessageInsert must match this. So basically its always just the gl functions name prefixed with _mesa_ you can see more on function naming here: under the heading Coding Style.

So to corresponding definitions to the above snippet (which we would add to errors.h) are:

_mesa_DebugMessageCallback(GLDEBUGPROC callback, const void *userParam);

_mesa_DebugMessageControl(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids,GLboolean enabled);

_mesa_DebugMessageInsert(GLenum source, GLenum type, GLuint id, GLenum severity, GLint length, const GLchar* buf);

I have added a full copy of the patches that add these definitions to core Mesa to the Appendix for reference. One further thing I would like to point out is that for the Object Label functions we created a new file to hold these definitions called objectlabel.h so we need to add this file to the script so that it knows to add this to api_exec.c

See src/mapi/glapi/gen/

#include "main/multisample.h"
+#include "main/objectlabel.h"
#include "main/pixel.h"

The final step of setting up the infrastructure code for KHR_debug is adding support for the new tokens.

First we setup two new constants in src/mesa/main/config.h for now I have just set these to the minimum vales as defined in the spec under the heading New Implementation Dependent State.

+/** For GL_KHR_debug */
+#define MAX_LABEL_LENGTH 256

We also add some of the new tokens to src/mesa/main/ which generates the code for functions like GetIntegerv so that is can return the values associated with these tokens.

+# GL_KHR_debug (GL 4.3)/ GL_ARB_debug_output

Looking at the last line form the snippet above you can see for DEBUG_GROUP_STACK_DEPTH the value is retrieved from the context struct Debug.GroupStackDepth.

As GroupStackDepth is new to KHR_debug (i.e wasn't part of the existing ARB_debug_ouptut extension) we need to add this to this struct used by the context.

This struct is defined in src/mesa/main/mtypes.h

struct gl_debug_state
const void *CallbackData;
GLboolean SyncOutput;
struct gl_debug_namespace Namespaces[MESA_DEBUG_SOURCE_COUNT][MESA_DEBUG_TYPE_COUNT];
struct gl_debug_msg Log[MAX_DEBUG_LOGGED_MESSAGES];
+ GLint GroupStackDepth;
GLint NumMessages;
GLint NextMsg;
GLint NextMsgLength; /* redundant, but copied here from Log[NextMsg].length

You can also see we change GLDEBUGPROCARB to GLDEBUGPROC both of these defined in gl_api.xml and are actually exactly the same. The only difference is that once an extension becomes a core extension the ARB suffix is dropped.

OpenGL state (Core Mesa)

Previous patch sets that might help in understanding this extension

Link to patches implementing ARB_debug_output:

Link to patches implementing Intel support for ARB_debug_output

Sending a patch to the Mesa mailing list for review - For but still very useful.

Helpful links and further reading


Patch References

Add KHR_debug definition to gl_API.xml

<type name="void" size="1"/>

<type name="DEBUGPROCARB" size="4" pointer="true"/>
+ <type name="DEBUGPROC" size="4" pointer="true"/>

<function name="NewList" offset="0" deprecated="3.1">

<param name="list" type="GLuint"/>

@@ -8309,7 +8310,153 @@

<xi:include href="ARB_texture_storage.xml" xmlns:xi=""/>

-<!-- ARB extensions #118...#126 -->
+<!-- ARB extension #118 -->

+<category name="GL_KHR_debug" number="119">
+ <enum name="DEBUG_OUTPUT" value="0x92E0"/>
+ <enum name="DEBUG_OUTPUT_SYNCHRONOUS" value="0x8242"/>

+ <enum name="CONTEXT_FLAG_DEBUG_BIT" value="0x00000002"/>
+ <enum name="MAX_DEBUG_MESSAGE_LENGTH" count="1" value="0x9143">
+ <size name="Get" mode="get"/>
+ </enum>
+ <enum name="MAX_DEBUG_LOGGED_MESSAGES" count="1" value="0x9144">
+ <size name="Get" mode="get"/>
+ </enum>
+ <enum name="DEBUG_LOGGED_MESSAGES" count="1" value="0x9145">
+ <size name="Get" mode="get"/>

+ </enum>

+ <enum name="DEBUG_NEXT_LOGGED_MESSAGE_LENGTH" count="1" value="0x8243">

+ <size name="Get" mode="get"/>

+ </enum>

+ <enum name="MAX_DEBUG_GROUP_STACK_DEPTH" count="1" value="0x826C">

+ <size name="Get" mode="get"/>

+ </enum>

+ <enum name="DEBUG_GROUP_STACK_DEPTH" count="1" value="0x826D">

+ <size name="Get" mode="get"/>

+ </enum>

+ <enum name="MAX_LABEL_LENGTH" count="1" value="0x82E8">

+ <size name="Get" mode="get"/>

+ </enum>


+ <enum name="DEBUG_CALLBACK_FUNCTION" count="1" value="0x8244">

+ <size name="GetPointerv" mode="get"/>

+ </enum>

+ <enum name="DEBUG_CALLBACK_USER_PARAM" count="1" value="0x8245">

+ <size name="GetPointerv" mode="get"/>

+ </enum>


+ <enum name="DEBUG_SOURCE_API" value="0x8246"/>

+ <enum name="DEBUG_SOURCE_WINDOW_SYSTEM" value="0x8247"/>

+ <enum name="DEBUG_SOURCE_SHADER_COMPILER" value="0x8248"/>

+ <enum name="DEBUG_SOURCE_THIRD_PARTY" value="0x8249"/>

+ <enum name="DEBUG_SOURCE_APPLICATION" value="0x824A"/>

+ <enum name="DEBUG_SOURCE_OTHER" value="0x824B"/>


+ <enum name="DEBUG_TYPE_ERROR" value="0x824C"/>

+ <enum name="DEBUG_TYPE_DEPRECATED_BEHAVIOR" value="0x824D"/>

+ <enum name="DEBUG_TYPE_UNDEFINED_BEHAVIOR" value="0x824E"/>

+ <enum name="DEBUG_TYPE_PORTABILITY" value="0x824F"/>

+ <enum name="DEBUG_TYPE_PERFORMANCE" value="0x8250"/>

+ <enum name="DEBUG_TYPE_OTHER" value="0x8251"/>

+ <enum name="DEBUG_TYPE_MARKER" value="0x8268"/>


+ <enum name="DEBUG_TYPE_PUSH_GROUP" value="0x8269"/>

+ <enum name="DEBUG_TYPE_POP_GROUP" value="0x826A"/>


+ <enum name="DEBUG_SEVERITY_HIGH" value="0x9146"/>

+ <enum name="DEBUG_SEVERITY_MEDIUM" value="0x9147"/>

+ <enum name="DEBUG_SEVERITY_LOW" value="0x9148"/>

+ <enum name="DEBUG_SEVERITY_NOTIFICATION" value="0x826B"/>


+ <enum name="STACK_UNDERFLOW" value="0x0504"/>

+ <enum name="STACK_OVERFLOW" value="0x0503"/>


+ <enum name="BUFFER" value="0x82E0"/>

+ <enum name="SHADER" value="0x82E1"/>

+ <enum name="PROGRAM" value="0x82E2"/>

+ <enum name="QUERY" value="0x82E3"/>

+ <enum name="PROGRAM_PIPELINE" value="0x82E4"/>

+ <enum name="SAMPLER" value="0x82E6"/>

+ <!-- Compatibility Profile -->

+ <enum name="DISPLAY_LIST" value="0x82E7"/>


+ <function name="DebugMessageControl" offset="assign">

+ <param name="source" type="GLenum"/>

+ <param name="type" type="GLenum"/>

+ <param name="severity" type="GLenum"/>

+ <param name="count" type="GLsizei" counter="true"/>

+ <param name="ids" type="const GLuint *" count="count"/>

+ <param name="enabled" type="GLboolean"/>

+ </function>


+ <function name="DebugMessageInsert" offset="assign">

+ <param name="source" type="GLenum"/>

+ <param name="type" type="GLenum"/>

+ <param name="id" type="GLuint"/>

+ <param name="severity" type="GLenum"/>

+ <param name="length" type="GLsizei"/>

+ <param name="buf" type="const GLchar *"/>

+ </function>


+ <function name="DebugMessageCallback" offset="assign">

+ <param name="callback" type="GLDEBUGPROC"/>

+ <param name="userParam" type="const GLvoid *"/>

+ </function>


+ <function name="GetDebugMessageLog" offset="assign">

+ <return type="GLuint"/>

+ <param name="count" type="GLuint"/>

+ <param name="bufsize" type="GLsizei"/>

+ <param name="sources" type="GLenum *" output="true"/>

+ <param name="types" type="GLenum *" output="true"/>

+ <param name="ids" type="GLuint *" output="true"/>

+ <param name="severities" type="GLenum *" output="true"/>

+ <param name="lengths" type="GLsizei *" output="true"/>

+ <param name="messageLog" type="GLchar *" output="true"/>

+ </function>


+ <function name="PushDebugGroup" offset="assign">

+ <param name="source" type="GLenum"/>

+ <param name="id" type="GLuint"/>

+ <param name="length" type="GLsizei"/>

+ <param name="message" type="const GLchar *"/>

+ </function>


+ <function name="PopDebugGroup" offset="assign"/>


+ <function name="ObjectLabel" offset="assign">

+ <param name="identifier" type="GLenum"/>

+ <param name="name" type="GLuint"/>

+ <param name="length" type="GLsizei"/>

+ <param name="label" type="const GLchar *"/>

+ </function>


+ <function name="GetObjectLabel" offset="assign">

+ <param name="identifier" type="GLenum"/>

+ <param name="name" type="GLuint"/>

+ <param name="bufSize" type="GLsizei"/>

+ <param name="length" type="GLsizei *"/>

+ <param name="label" type="GLchar *"/>

+ </function>


+ <function name="ObjectPtrLabel" offset="assign">

+ <param name="ptr" type="const GLvoid *"/>

+ <param name="length" type="GLsizei"/>

+ <param name="label" type="const GLchar *"/>

+ </function>


+ <function name="GetObjectPtrLabel" offset="assign">

+ <param name="ptr" type="const GLvoid *"/>

+ <param name="bufSize" type="GLsizei"/>

+ <param name="length" type="GLsizei *"/>

+ <param name="label" type="GLchar *"/>

+ </function>




+<!-- ARB extensions #120...#126 -->

Add KHR_debug function definitions to core Mesa



+_mesa_DebugMessageInsert(GLenum source, GLenum type, GLuint id,

+ GLenum severity, GLint length,

+ const GLchar* buf);


+_mesa_GetDebugMessageLog(GLuint count, GLsizei logSize, GLenum* sources,

+ GLenum* types, GLenum* ids, GLenum* severities,

+ GLsizei* lengths, GLchar* messageLog);


+_mesa_DebugMessageControl(GLenum source, GLenum type, GLenum severity,

+ GLsizei count, const GLuint *ids,

+ GLboolean enabled);


+_mesa_DebugMessageCallback(GLDEBUGPROC callback,

+ const void *userParam);


+_mesa_PushDebugGroup(GLenum source, GLuint id, GLsizei length,

+ const GLchar *message);


+_mesa_PopDebugGroup (void);





+#include "glheader.h"


+#ifdef __cplusplus

+extern "C" {




+_mesa_ObjectLabel(GLenum identifier, GLuint name, GLsizei length,

+ const GLchar *label);


+_mesa_GetObjectLabel(GLenum identifier, GLuint name, GLsizei bufSize,

+ GLsizei *length, GLchar *label);


+_mesa_ObjectPtrLabel(const void *ptr, GLsizei length, const GLchar *label);


+_mesa_GetObjectPtrLabel(const void *ptr, GLsizei bufSize, GLsizei *length,

+ GLchar *label);


+#ifdef __cplusplus




+#endif /* OBJECTLABEL_H */