If you’ve upgraded to Ubuntu 22.04 then you probably noticed how smooth the GNOME experience is. If you haven’t noticed then try comparing it to an older Ubuntu release or even the latest Fedora. The main enhancement responsible for this is the introduction of triple buffering in Ubuntu.
Before describing what triple buffering is, consider double buffering:
- Wait for the monitor to display the last frame.
- Wait a little bit more.
- Prepare the next frame behind the scenes.
- Offer the next frame to the monitor.
- Goto 1.
Monitor refresh | * * * * * Prepare next frame | A B A B A Display next frame | A B A B frame rate = 100%, latency < 1 frame
There are always two images in this loop: the one you see on screen and the next one that will follow it. That’s double buffering.
A problem occurs however when preparing (rendering) the next frame takes too long:
Monitor refresh | * * * * * Prepare next frame | A.............. B.............. A... Display next frame | A B frame rate = 50%, latency < 2 frames
The above diagram shows double buffering only achieving half the frame rate of the monitor. You see this as stutter, although some people use the word “lag”.
The long render times shown above are often due to the GPU running at its lowest frequency. But the GPU is not very smart and it doesn’t know that you probably wanted a higher frame rate. The blank space between frames is when the GPU is completely idle and as such it thinks it is appropriate to stay at the same low frequency.
But what if we reduce the gaps? Ensure the GPU is not allowed to idle until it at least gives us the full frame rate? This means we have to pre-render two frames instead of one:
Monitor refresh | * * * * * * Prepare next frame | A..............B....C.... A..............B... Display next frame | A B C A frame rate = 100%, latency < 2 frames
So now using three different buffers we are able to achieve full frame rate. A is rendered slowly at the default low frequency but when we don’t stop for a break the GPU knows (well the graphics driver knows) it needs to speed up. That’s why B and C have shorter render times. It is only by trying to pre-render two frames that we make it likely at least one frame has been pre-rendered in time.
The above diagram is illustrative of Intel GPUs that typically start at 30% speed. It is for illustrative purposes only and should not be seen as an accurate representation of what is happening for all GPUs all the time.
The end result is that GNOME sessions in Ubuntu 22.04 will use your full hardware’s ability to first reach full frame rate, and only after that is achieved will performance scale down to more power efficient settings.
So all I needed was a higher GPU frequency?
Not exactly. The .......
can represent not only a long render time but also unexpected events that delay rendering from starting. This is especially an issue in a single threaded event loop like in gnome-shell. The same benefits still apply - as we pre-render an extra frame we are able to cope with and recover from such hiccups without stuttering being seen on screen. An improvement to smoothness is therefore seen with triple buffering even on systems without frequency scaling.
Will my games run faster?
Probably not. Games with an unthrottled frame rate (or just vsync “off”) already convince the GPU to run as fast as it can. Games that are throttled to the refresh rate (vsync “on”) will benefit if they are run in a window but usually not in full screen. We turn off triple buffering in full screen direct scanout mode because (a) it was too much work at the time (development took almost 2 years already); and (b) the compositor render time is zero there so isn’t a metric that needs improving. For extra smoothness in future, perhaps.
More buffers means more latency right?
No, not in this case. Our triple buffering implementation dynamically switches between double and triple buffering as required. When it is required, double buffering would only provide half the ideal frame rate (or worse). So you’re comparing two frames of latency with two frames of latency. Triple buffering just doubles the frame rate. If the rendering is simple then we switch to double buffering and latency drops below one frame.
Source code
The source code for triple buffering in GNOME is available to everyone as a patch for mutter 43 or as a patch for mutter 42. It is already included in Ubuntu 22.04 and later.
But I want more performance
While triple buffering in Ubuntu 22.04 provides a significant improvement out of the box, it’s always possible to go faster. Here are some suggestions that won’t void the warranty:
Extensions
Ubuntu’s three default gnome-shell extensions are great, but they do have a measurable performance impact. We’re working to fix this but in the meantime you might consider disabling any that you don’t need. If you don’t already have the Extensions app icon installed, you can just run:
gjs /usr/share/gnome-shell/org.gnome.Extensions
or
sudo apt install gnome-shell-extension-prefs
to install the Extensions app icon.
Mouse movement
You might get a feeling that Wayland sessions have a slightly laggy, slightly sloppy mouse response. You’re not imagining things. This is a feature of Linux’s atomic KMS architecture where all on screen changes are deferred to occur at exactly the same time. If you don’t want that to include the mouse pointer then consider reverting to traditional KMS mode by editing /etc/environment
and adding:
Ubuntu 22.04:
MUTTER_DEBUG_ENABLE_ATOMIC_KMS=0
Ubuntu 22.10:
MUTTER_DEBUG_FORCE_KMS_MODE=simple
and remember to reboot afterwards. This ensures the mouse pointer location is allowed to update even sooner than the rest of the screen.
Web browsers
Neither of the major web browsers enable native Wayland support by default yet, but they do support it. If you want the fastest, smoothest browsing experience (and more precise touchpad scrolling) then you can enable native Wayland rendering in:
Firefox
Add this to /etc/environment
:
MOZ_ENABLE_WAYLAND=1
and reboot.
Chrome
Open address:
chrome://flags/#ozone-platform-hint
and change the setting “Preferred Ozone platform” to “Wayland”, then click Relaunch.
Phase shifting
Mutter contains an optimization that dynamically adjusts the render start time to try and minimize latency to the screen. This improves latency by a fraction of a frame interval, so a few milliseconds. The downside is that it can make frame skips appear.
If you are willing to sacrifice a few milliseconds latency in order to gain a smoother frame rate then you can add this to your /etc/environment
:
CLUTTER_PAINT=disable-dynamic-max-render-time
What if I want to prioritise low power instead of frame rate?
You probably don’t need to. As soon as the screen stops changing your CPU and GPU are going to change to a low power state. Even with triple buffering.
If you really wanted to minimize power usage by knowingly reducing frame rate then you could:
- Set your monitor to a lower refresh rate; or
- Limit your Intel GPU to a low clock speed (write a lower value to
/sys/class/drm/card0/gt_max_freq_mhz
); or - Use the NVIDIA Settings app to control the power profile of the NVIDIA driver, if installed.