Intro
Before you dive into this post, I highly recommend checking out the previous chapter of my little GPU saga — where I accidentally ordered the wrong NVLink bridge and decided to turn my misfortune into a full-blown experiment. That story sets the stage for everything that follows here, as this post picks up right where the last one left off — with two GPUs, one impatient tinkerer, and a quest to squeeze every bit of performance out of silicon (with or without the bridge).
And so this chapter begins — a deep dive into how OpenGL behaves on a dual-GPU system without NVLink, without PRIME, and without Optimus. I wanted to know:
- How does Ubuntu 25.10 handle OpenGL workloads across two discrete GPUs?
- Which GPU actually does the rendering when one is headless?
- Does separating rendering from display introduce bottlenecks?
- And most importantly, what configuration squeezes out the absolute best performance and image quality?
Current setup by nvidia-smi is 2 RTX 2080 Ti, NVIDIA driver 580 (open):
nvidia-smi
Thu Oct 30 21:55:45 2025
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 580.95.05 Driver Version: 580.95.05 CUDA Version: 13.0 |
+-----------------------------------------+------------------------+----------------------+
| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |
| | | MIG M. |
|=========================================+========================+======================|
| 0 NVIDIA GeForce RTX 2080 Ti Off | 00000000:03:00.0 Off | N/A |
| 18% 38C P8 21W / 250W | 330MiB / 11264MiB | 0% Default |
| | | N/A |
+-----------------------------------------+------------------------+----------------------+
| 1 NVIDIA GeForce RTX 2080 Ti Off | 00000000:04:00.0 On | N/A |
| 18% 39C P8 15W / 250W | 166MiB / 11264MiB | 0% Default |
| | | N/A |
+-----------------------------------------+------------------------+----------------------+1. Overview
This guide focuses exclusively on OpenGL configuration and performance testing for systems equipped with two NVIDIA RTX 2080 Ti GPUs running Ubuntu 25.10 with the nvidia-driver-580-open package.
Where are the three packages on NVIDIA drivers:
nvidia-driver-580: This is (traditionally) the full NVIDIA proprietary driver package — includes the kernel modules, Xorg/Wayland 2D/3D libraries, 32-bit compatibility libs, CUDA support, etc.nvidia-driver-580-open: This is a variant that uses the open-kernel-modules (or NVIDIA’s “open kernel modules”) rather than the fully closed proprietary kernel module. It is part of NVIDIA’s shift toward open-source kernel components for Linux.nvidia-driver-580-server: This edition is optimized for data centers, compute nodes, and headless environments rather than desktop graphics. It includes the same core driver stack, but with configurations and defaults tuned for stability, long uptimes, and CUDA workloads.
Key Feature Differences & Implications:
| Feature | nvidia-driver-580 (proprietary kernel module) |
nvidia-driver-580-open (open kernel module variant) |
|---|---|---|
| Kernel module nature | Fully closed-source NVIDIA kernel module | Kernel module built (or open) so portions of the driver stack are open source |
| Compatibility | Mature, widely used for gaming and desktops | Newer approach, may have gaps or additional configuration needed for some hardware or features |
| Feature support | All legacy features, SLI/NVLink, full 32-bit libs, gaming optimizations | Should aim to match feature set, but given the open-module transition, there might be missing legacy support or some performance differences |
| Updates / future-proofing | Stable for current generation; post-580 NVIDIA indicates future driver series may require open modules/GSP firmware. (NVIDIA Developer Forums) | Likely the more future-oriented path, especially for newer GPUs and architecture changes (e.g., requiring open modules) |
| Gaming suitability | Excellent, proven for Steam + Proton + OpenGL/Vulkan in Linux gaming setups | Also good, but depending on your exact GPU model and configuration, you may encounter minor differences; you should check if your hardware is fully supported |
| Package variants & multi-arch (32-bit libs) | Full support for gaming scenarios (desktop + 32-bit libs) | May sometimes lag in bundling some legacy 32-bit components, depending on packaging; always verify i386 compatibility if you game |
In short:
-
nvidia-driver-580→ Best for desktops and gaming. -
nvidia-driver-580-open→ Same as above but with open-source kernel modules. nvidia-driver-580-server→ Tuned for compute and headless environments (e.g., AI, CUDA, rendering farms).
We’ll verify that both GPUs are recognized, confirm that OpenGL rendering runs on the expected GPU, and perform controlled tests with glxgears and glmark2.
This setup does not use PRIME or Optimus — both GPUs are discrete, with:
- GPU 1 → connected to the display (primary GPU)
- GPU 0 → headless (secondary GPU, no display output)
2. Understanding OpenGL on Linux
OpenGL on Linux sits atop your GPU’s driver stack. For NVIDIA cards, the key components are:
| Layer | Component | Description |
|---|---|---|
| Kernel module | nvidia |
Interfaces with hardware and memory |
| User-space library | libGL.so / libnvidia-glcore.so |
Implements the OpenGL API |
| GLX extension | libGLX_nvidia.so |
Connects Xorg to the OpenGL stack |
| Tools | glxinfo, glxgears |
Used to query and test rendering capabilities |
If OpenGL is properly configured, it will render via the NVIDIA driver — not via Mesa’s software renderer (llvmpipe) as I did in the previous post.
3. Preliminary Setup
Step 1 — Install the Necessary Packages
sudo apt update
sudo apt install mesa-utils mesa-utils-extra nvidia-driver-580-open
This installs:
glxinfoandglxgears— OpenGL test tools- The latest NVIDIA Open driver for Ubuntu 25.10, it should be the 580.
Reboot to activate the kernel modules:
sudo reboot
4. Verifying GPU Detection
Run:
nvidia-smi
Expected output (example from my actual system):
Tue Oct 28 17:02:20 2025
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 580.95.05 Driver Version: 580.95.05 CUDA Version: 13.0 |
+-----------------------------------------+------------------------+----------------------+
| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |
| | | MIG M. |
|=========================================+========================+======================|
| 0 NVIDIA GeForce RTX 2080 Ti Off | 00000000:03:00.0 Off | N/A |
| 18% 43C P8 21W / 250W | 594MiB / 11264MiB | 3% Default |
| | | N/A |
+-----------------------------------------+------------------------+----------------------+
| 1 NVIDIA GeForce RTX 2080 Ti Off | 00000000:04:00.0 On | N/A |
| 18% 53C P8 17W / 250W | 165MiB / 11264MiB | 3% Default |
| | | N/A |
+-----------------------------------------+------------------------+----------------------+- GPU 1 (
Disp.A = On) drives the display. - GPU 0 (
Disp.A = Off) is headless.
5. Confirm OpenGL Renderer
5.1 Checking OpenGL Information
Run the following command to see the current OpenGL information:
glxinfo | grep "OpenGL"
This command displays details about your GPU, driver, and renderer in use. For multi-GPU systems, this helps verify which GPU is actually doing the work (discrete vs. integrated).
You’ll see output like:
OpenGL vendor string: NVIDIA Corporation
OpenGL renderer string: GeForce RTX 2080 Ti/PCIe/SSE2
OpenGL version string: 4.6.0 NVIDIA 560.35.03
Use this command to get the OpenGL renderer:
glxinfo | grep "OpenGL renderer"
Expected output:
OpenGL renderer string: NVIDIA GeForce RTX 2080 Ti/PCIe/SSE2
If you see:
OpenGL renderer string: llvmpipe (LLVM x.y, x bits)
It means hardware acceleration is not active — reinstall the NVIDIA driver.
6. Running glxgears
6.1 Launch the Test
glxgears
You’ll see a window with three rotating gears and FPS output like:
301 frames in 5.0 seconds = 60.015 FPS
Note:
As I told you before,glxgears is not a benchmark — it’s a sanity test to confirm that OpenGL rendering is working correctly.
6.2 Bind to a Specific GPU
If you want to explicitly render on the headless GPU 0, you can use NVIDIA’s GPU display device selection.
First, identify available devices:
xrandr --listproviders
The output:
Providers: number : 0Then use the __GLX_VENDOR_LIBRARY_NAME variable to target NVIDIA explicitly:
__GLX_VENDOR_LIBRARY_NAME=nvidia glxgears
However, since GPU 0 has no connected display, you’ll need a virtual display (X server) or an off-screen rendering context (EGL headless) to run OpenGL there (explained below).
The Results:
15132 frames in 5.0 seconds = 3026.306 FPS
15455 frames in 5.0 seconds = 3090.827 FPS
15513 frames in 5.0 seconds = 3101.881 FPS
15361 frames in 5.0 seconds = 3072.163 FPS
15368 frames in 5.0 seconds = 3073.485 FPS
15400 frames in 5.0 seconds = 3079.922 FPS
15331 frames in 5.0 seconds = 3066.059 FPS
15465 frames in 5.0 seconds = 3092.986 FPS
15396 frames in 5.0 seconds = 3079.095 FPS
15421 frames in 5.0 seconds = 3084.134 FPS7. Benchmarking OpenGL Performance
While glxgears only checks functionality, for actual performance use glmark2.
7.1 Install glmark2
sudo apt install glmark2
7.2 Run Full OpenGL Benchmark
glmark2
Typical output:
vblank_mode=0 __GL_SYNC_TO_VBLANK=0 glmark2
=======================================================
glmark2 2023.01
=======================================================
OpenGL Information
GL_VENDOR: NVIDIA Corporation
GL_RENDERER: NVIDIA GeForce RTX 2080 Ti/PCIe/SSE2
GL_VERSION: 4.6.0 NVIDIA 580.95.05
Surface Config: buf=32 r=8 g=8 b=8 a=8 depth=24 stencil=0 samples=0
Surface Size: 800x600 windowed
=======================================================
[build] use-vbo=false: FPS: 2479 FrameTime: 0.403 ms
[build] use-vbo=true: FPS: 3036 FrameTime: 0.329 ms
[texture] texture-filter=nearest: FPS: 3027 FrameTime: 0.330 ms
[texture] texture-filter=linear: FPS: 3027 FrameTime: 0.330 ms
[texture] texture-filter=mipmap: FPS: 3004 FrameTime: 0.333 ms
[shading] shading=gouraud: FPS: 2969 FrameTime: 0.337 ms
...
FrameTime: 0.333 ms
[loop] fragment-steps=5:fragment-uniform=false:vertex-steps=5: FPS: 3007 FrameTime: 0.333 ms
[loop] fragment-steps=5:fragment-uniform=true:vertex-steps=5: FPS: 3002 FrameTime: 0.333 ms
=======================================================
glmark2 Score: 2662
=======================================================8. Advanced: Testing Image Quality & Synchronization
8.1 Check Vertical Sync (VSync)
VSync limits the FPS to your display’s refresh rate (usually 60 Hz).
Run:
vblank_mode=0 glxgears
or with the NVIDIA version of this parameter:
__GL_SYNC_TO_VBLANK=0 glxgearsto disable VSync and measure raw OpenGL throughput.
If FPS jumps from 60 → 1000+, hardware acceleration is confirmed, and you finally not binded to monitor frequency.
8.2 Test Multi-GPU Workload Distribution
If both GPUs are active, run one benchmark per GPU:
DISPLAY=:0 glmark2 &
DISPLAY=:1 glmark2 &
Then check with:
nvidia-smi -l 1
You should see both GPUs engaged simultaneously — perfect for heavy multitasking or concurrent rendering setups.
9. Tuning for Maximum OpenGL Performance
| Setting | Command | Description |
|---|---|---|
| Persistent mode | sudo nvidia-smi -pm 1 |
Keeps driver loaded to reduce initialization delay |
| Max performance | sudo nvidia-settings -a |
Prevents down-clocking during load |
| Coolbits | Option "Coolbits" "28" in xorg.conf |
Enables overclock and fan control |
| Triple buffering | Enable in driver settings | Smooths frame pacing |
| Disable compositing | Use a lightweight window manager (e.g., i3, XFCE) |
Reduces frame latency |
10. Best Practices for Dual-GPU OpenGL
| Use Case | Recommended GPU | Notes |
|---|---|---|
| Gaming (display rendering) | GPU 1 (display-connected) | Best performance and lowest latency |
| Headless rendering / compute | GPU 0 (headless) | Use X :1 or EGL for testing |
| SLI/NVLink | Optional | Works only in select titles; usually unnecessary |
| OBS / Encoding | GPU 0 | Ideal for NVENC workloads |
11. Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
llvmpipe renderer |
Mesa software fallback | Reinstall NVIDIA driver, reboot |
glxgears capped at 60 FPS |
VSync active | Run vblank_mode=0 glxgears |
glmark2 fails on headless GPU |
No display context | Start virtual X server on GPU 0 |
No GPU activity in nvidia-smi |
Wrong display variable | Use DISPLAY=:0 or :1 explicitly |
12. Summary: Ideal Setup for Quality & Performance
| Component | Configuration | Purpose |
|---|---|---|
| Display GPU | GPU 1 (connected) | Primary OpenGL renderer |
| Headless GPU | GPU 0 | Secondary offload / background tasks |
| Driver | nvidia-driver-580-open |
Latest stable for Ubuntu 25.04 |
| Rendering Context | GLX (X11) or EGL (headless) | Chosen per-test |
| Tools | glxgears, glmark2, glxinfo, nvidia-smi |
Verification and benchmarking |
| VSync | Disabled (vblank_mode=0) |
For peak FPS benchmarking |
13 My Results and Reflections on OpenGL Performance
The score is really low compared to the modern systems, only 2662 points on glmark2 and only around 3200 FPS on glxgears. At the same time, the load is very odd; it looks like the not connected to the monitor GPU is working harder than the one companion, but both GPUs are working under the load.



Conclusion
My dual RTX 2080 Ti system under Ubuntu 25.10 is fully capable of delivering high-quality, low-latency OpenGL rendering without PRIME or Optimus.
For best results:
- Connect your monitor to the GPU you’re rendering on.
- Use
glxgearsto confirm functionality andglmark2to measure performance. - Use headless EGL contexts or virtual X servers for testing the secondary GPU.
- Keep VSync off for benchmarks and on for gameplay smoothness.
- Fine-tune PowerMizer and Coolbits settings for stable, maximum clock speeds.
With this setup, you’ll achieve the best possible OpenGL performance — ready for serious benchmarking or high-frame-rate Linux gaming.