Before you start#
Read the userspace graphics path and libdrm and dynamic loading. This chapter uses Mesa 24.2.0 as a separately pinned historical source baseline and Linux v6.12 only as a kernel bridge. These are not a validated compatibility matrix or current security recommendation. The lab inspects local metadata without opening a GPU or changing the display.
Learning goals#
Trace one Mesa EGL driver-load branch and a Gallium context initialization, distinguish screen, context, winsys and state-tracker ownership, and compare hardware, software and translation paths without asserting a universal driver stack.
Source walkthrough#
- EGL / DRI loading
- A loader locates a compatible driver and display platform.
- Gallium path
- State tracker, pipe screen, context and winsys have separate lifetimes.
- Software or Vulkan-based paths
- llvmpipe and Zink differ from native hardware drivers.
- Shader compilation
- Userspace compiler and driver process IR before device execution.
Mesa is a collection of userspace graphics API implementations, compiler infrastructure and drivers. An application can call an OpenGL or EGL entry point through a vendor dispatch arrangement such as GLVND when the distribution uses it. That dispatch selects a vendor implementation; it is not itself the GPU command processor. Mesa's EGL implementation negotiates a display platform and driver interfaces, while a DRI driver and its loader cooperate through versioned extension structures. DRI is a family of userspace interfaces and integration mechanisms, not one single kernel driver. Availability, selected platform, environment, GPU and packaging decide the path.
In Mesa 24.2.0, dri2_load_driver_common calls dri2_open_driver and checks the result. The file carries an MIT-style permissive license; this excerpt is from that pinned file:
extensions = dri2_open_driver(disp);
if (!extensions) {
The first line asks the loader for the driver's extension list. A null result enters the failure branch and returns EGL_FALSE after the applicable diagnostic; it is not a usable extension table. The following code calls loader_bind_extensions and returns failure if required extensions cannot be bound. A library file existing on disk does not guarantee a compatible driver, a usable device fd or a successfully initialized screen. The surrounding platform initialization owns its cleanup; this short branch alone does not describe every failure label.
For a Gallium-oriented path, a pipe_screen describes screen/device capabilities and operations, a pipe_context represents a rendering context with command/state operations, and a winsys connects the driver to the window system or lower device interface. Mesa's state tracker translates a higher-level API's state into Gallium operations. These objects have different owners and lifetimes. A screen can serve contexts and resources; a context refers to its screen but should not outlive it. A winsys can manage device and buffer interactions without being the kernel DRM driver. See pipe_loader.c and st_context.c.
The pinned st_create_context, MIT-style, allocates a GL context and initializes it. Its failure branch is:
if (!_mesa_initialize_context(ctx, api, no_error, visual, shareCtx, &funcs)) {
align_free(ctx);
return NULL;
}
If initialization fails, the function frees the allocation it owns and reports failure; callers must not treat the returned null as a context. Earlier statements assign ctx->pipe and ctx->screen from the supplied pipe context. That relationship requires the caller's object-lifetime contract, not an assumption that freeing this GL context always destroys the entire screen. Later creation stages have additional failure and teardown paths that must be read in full.
One conditional path and alternatives#
For a hypothetical Linux application using EGL on a supported platform, an EGL display is initialized, the implementation chooses or opens an appropriate device/platform path, and dri2_load_driver_common obtains driver extensions. On a Gallium-based OpenGL driver, the state tracker can create a GL context around a pipe_context and screen. The driver translates operations into its own command and resource handling; a winsys and kernel interface can submit work to hardware where applicable. The application then presents using a platform-specific surface/compositor path. This is a conditional map connecting real boundaries, not proof that every EGL initialization calls exactly these functions or that the lab created a context.
- EGL platform init
chooses a display platform and device path; depends on environment and build
- dri2_open_driver
returns the extension list; null takes the failure branch
- loader_bind_extensions
binds required extensions; failure returns EGL_FALSE
- pipe_loader_create_screen
Gallium loader path; calls the device create_screen with sw_vk false
- st_create_context
allocates the GL context; frees it if _mesa_initialize_context fails
- Driver execution
translated state becomes driver commands; hardware or CPU route is configuration dependent
If the driver library is missing, dri2_open_driver can fail before a screen is usable. If context initialization fails, the shown function frees its allocation and returns null. If a display or device disappears after successful creation, later operations can still fail; holding an EGL display or DRM fd does not make hardware immortal. An application must destroy contexts, surfaces and other objects in the order required by the API and its own threads. A context is generally current to a thread under API rules; concurrent access to driver objects requires the relevant synchronization and is not made safe merely by sharing a screen.
llvmpipe is a software renderer and uses CPU execution with its own winsys and LLVM-based compilation; it can be useful without a supported hardware 3D driver, subject to build and platform availability. Zink implements OpenGL over Vulkan and has a distinct downstream path through a Vulkan implementation. RADV and ANV are Mesa Vulkan drivers for different hardware families, not Gallium state trackers that must sit beneath every OpenGL application. Hardware Gallium drivers, llvmpipe, Zink, RADV and ANV are alternatives or layers in particular configurations, not a single universal chain. A renderer string or library name is evidence about one process and environment, not every process on the machine.
Shaders, IR, and cache lifetime#
A shader is a program describing a graphics or compute stage. A userspace compiler translates API-level shader representation through intermediate representations (IR), optimization and driver-specific lowering before device execution. Mesa's NIR is an IR used by many Mesa compiler paths, with instructions, values and control-flow structures whose invariants must hold across passes. It is not a claim that the Linux kernel compiles GLSL into GPU instructions. Some drivers or firmware have additional compilation or validation responsibilities; the exact pipeline depends on the chosen API, driver and hardware. See src/compiler/nir/nir.c and the relevant driver source.
Imagine a vertex shader that computes position = input * scale. A frontend produces IR representing an input, multiplication and output. An optimizer may fold a constant scale or remove dead work, provided observable semantics are preserved. A hardware backend lowers the remaining operations for a particular device, while llvmpipe can use a CPU-oriented route. This is an authored conceptual IR example, not an excerpt or output from Mesa. A cached compiled shader can avoid repeated work only when the cache key captures relevant driver, device and configuration inputs; a stale or incompatible entry must not be assumed valid merely because a filename exists.
Resource and compilation lifetimes overlap. A context can reference shaders and buffers submitted to an asynchronous GPU queue after the application has issued its calls. The API and driver retain or transfer the required references until work completes; deleting an application handle is not a license for a driver to reuse memory while hardware still reads it. On error, creation and compilation can return failure, and partial objects need their corresponding cleanup. A shader cache is a performance optimization, not an authoritative source of program correctness or security policy.
Loader search and version boundaries#
Mesa's loader_get_driver_for_fd and EGL DRI code implement particular discovery paths. A process can encounter a render node, software fallback, platform extension mismatch or dynamic-library loading error depending on its build and environment. Loader search paths and environment variables can affect which binary is selected; do not set them globally on a host to make a lesson pass. A package's Mesa version, the loaded driver build, kernel DRM driver and GPU firmware are separate facts. Pinning the source archive establishes what was read for this lesson, not what the local machine runs.
The generic loader's dynamic library handle and the returned extension pointers have connected lifetimes. Unloading a library while callbacks or driver objects still use its code would be unsafe; Mesa's actual teardown path must be followed for the selected platform. Likewise, a failed loader_bind_extensions means the requested interface was not established even if dlopen succeeded. Versioned DRI extension structures let components negotiate capabilities, but a mismatch or missing entry must be handled rather than dereferencing a nonexistent function pointer.
Driver discovery is not device initialization#
Mesa's loader code can derive a driver name from an fd and search configured library paths for a suitable module. The path can fail because the device is inaccessible, the expected driver was not built, the library cannot be opened, or required extensions do not match. These failures are not equivalent: a missing shared object is a packaging/search problem, whereas a failed device ioctl or unsupported extension can occur after a library is found. Preserve the first useful diagnostic and clean up each object that was actually acquired. Do not copy a path from one distribution and assume it identifies the active driver everywhere.
In dri2_load_driver_common, the returned extension array belongs to the loaded driver's interface lifetime. The function attempts to bind the required extensions and records the list only after the binding check succeeds. A caller must follow the display's teardown path before releasing a library that contains still-referenced function pointers. In a multithreaded application, one thread destroying an EGL display while another is creating a context requires the API and implementation's synchronization rules; the display pointer is not an eternal reference just because a manifest existed at startup.
The Gallium pipe loader's pipe_loader_create_screen delegates to its Vulkan-aware helper with its sw_vk argument set to false. The helper builds a configuration from driver and option state and calls the device's create_screen operation. That function pointer is supplied by the selected pipe-loader device. This is a particular Gallium loader interface, not the entry point used by every Mesa Vulkan driver. A null screen result must be handled before creating a context. A screen can encapsulate device-wide capabilities, whereas a context holds command and rendering state that can be destroyed separately under its owner contract.
In pipe_loader.c, pipe_loader_create_screen, Mesa 24.2.0, MIT-style permission notice; the _vk helper it calls is defined earlier in the same file:
pipe_loader_create_screen(struct pipe_loader_device *dev, bool driver_name_is_inferred)
{
return pipe_loader_create_screen_vk(dev, false, driver_name_is_inferred);
}
The wrapper fixes sw_vk to false and returns whatever the device's create_screen callback produces, so the caller must still check for a null screen. These four lines do not show how the device was selected or how the screen is destroyed.
From API state to a driver operation#
In an OpenGL/Gallium example, the application creates a context with a visual/configuration compatible with its surface. Mesa initializes a gl_context and a state tracker around a pipe_context. When the application changes texture or blend state, the state tracker validates and translates relevant state into Gallium structures and driver callbacks. A driver can defer hardware command emission or batch work; the timing of one GL call is not necessarily the timing of the corresponding GPU execution. A fence or explicit API synchronization is needed when the application must know that asynchronous work completed.
- Application
- EGL or GL calls on a thread where the context is current
- Mesa state tracker
- gl_context and state validation; translates API state to Gallium
- pipe_context
- command and rendering state for one context; must not outlive its screen
- pipe_screen
- device-wide capabilities and resource operations shared by contexts
- winsys
- platform buffer and device services; not the Wayland compositor
- Kernel DRM
- validates and executes the kernel side of device requests
The st_create_context failure branch frees its own aligned GL context if _mesa_initialize_context fails. The pipe_context was supplied by the caller; a reviewer should inspect the caller's failure path before saying that this function destroyed it. A successful return establishes a context object under the implementation's ownership rules, but later shader compilation or resource allocation can still fail. Teardown must prevent another thread from using a destroyed context, and objects shared between contexts have their own references. API context-current rules do not make a raw application pointer safe after destruction.
A winsys handles platform-facing operations such as buffer allocation/import, display integration or device submission in a particular driver family. It is not simply another spelling for the Wayland compositor, and a software winsys need not issue hardware commands. The kernel DRM driver validates and executes its side of the interface; Mesa's userspace driver and compiler are separate code. A device fd can outlive an application-visible context, while closing it too early can invalidate later device operations unless another reference is held. Inspect the actual winsys and driver before attributing a specific ioctl to a generic Gallium call.
Shader IR and failure evidence#
The conceptual multiply example has an input value, a constant scale and an output. If the scale is known to be one, a compiler pass may replace a multiply by the input, provided precision, side effects and observable semantics allow it. A real NIR instruction has typed metadata, SSA values and control-flow constraints; that sentence is not a literal dump of a particular compiler run. A lowering pass can transform an operation into forms supported by a chosen backend, and validation or compilation can fail for unsupported capabilities or resource limits. A source-only reader can reason about the IR boundary without asserting which assembly a specific GPU receives.
A disk shader cache can speed up later compilations, but a cache hit must be keyed to the relevant driver/compiler/device inputs and validated by the implementation. Clearing a host cache is unnecessary for this lesson and could disrupt another application. A local JSON manifest count and a successful source search are not evidence of a cache hit, a selected driver or a rendered frame. To make those claims, a separately controlled process would need to record its loaded libraries, device, API, driver messages and output, with permissions and cleanup appropriate to that environment.
What a software fallback does not establish#
A software fallback can permit an API call to execute on the CPU, but the result still depends on the built driver, compiler, platform integration and requested API features. A process might render offscreen without a compositor, or render into a buffer that later needs copying for display. The distinction matters when interpreting performance: a correct image does not by itself prove that a discrete GPU executed the shader. Conversely, an inaccessible render node does not prove that no software renderer can run.
The source-only example stops at driver and context creation because no shader was compiled in this exercise. It is legitimate to inspect how a NIR object is allocated and freed, but the actual NIR for a shader depends on its frontend, options and optimization passes. A reader should label any hand-drawn sequence as conceptual and use a controlled build and captured compiler output before asserting exact instructions or a cache hit. This limitation preserves the distinction between the upstream implementation and the authored teaching model.
Practice#
Source-only route: in the extracted Mesa 24.2.0 archive, find dri2_load_driver_common in src/egl/drivers/dri2/egl_dri2.c and st_create_context in src/mesa/state_tracker/st_context.c. For the supplied hypothetical initialization, mark the extension-list failure, context-allocation failure and _mesa_initialize_context cleanup. Describe which object exists after each success; do not claim that a GPU was initialized.
A safe local metadata observation needs no graphics device access:
python3 - <<'PY'
from pathlib import Path
for root in ('/usr/share/glvnd/egl_vendor.d', '/usr/share/vulkan/icd.d'):
p = Path(root)
try: print(root, 'JSON files:', len(list(p.glob('*.json'))) if p.is_dir() else 0)
except OSError as e: print(root, 'unavailable:', e.strerror)
PY
Zero files, permission restrictions or a different distribution path are valid observations. A manifest file is not proof that its library can be loaded, that a device exists, or that Mesa is the active renderer. No environment variable, GPU node or display is modified. Deliver the loader/context failure timeline and a distinction between the conceptual shader IR and actual compiler output.
Knowledge check#
- What does
dri2_load_driver_commondo when driver extensions cannot be opened? - What does
st_create_contextrelease when_mesa_initialize_contextfails? - How do a Gallium screen, context and winsys differ?
- Why are llvmpipe, Zink and RADV not one mandatory serial chain?
- Does a cached shader or a manifest prove that a GPU executed work?
Answers#
- It returns
EGL_FALSEthrough the failure branch rather than using a null extension list. - The shown branch frees the allocated GL context and returns null; other objects remain governed by their own caller contracts.
- The screen provides device capabilities/operations, the context carries rendering state and commands, and the winsys connects the driver to platform/device services.
- They represent distinct software, translation and Vulkan-driver paths selected under different configurations.
- No. A cache or manifest is a piece of userspace state, not a captured execution or proof of loaded hardware support.
Checkpoint#
Save the source failure timeline, renderer-path comparison and metadata result. Pass when you can name each object's owner and lifetime, distinguish NIR from kernel execution, and explain why a manifest or successful library open is not enough. Re-read the two functions if needed. This is practice, not certification.