The `last_presentation_time` is usually a little in the past (although sometimes in the future depending on the driver). When it's over 2ms (`sync_delay`) in the past that would trigger the while loop to count up so that the next `update_time` is in the future. The problem with that is for common values of `last_presentation_time` which are only a few milliseconds ago, incrementing `update_time` by `refresh_interval` also means counting past the next physical frame that we haven't rendered yet. And so mutter would skip that frame. **Example** Given: ``` last_presentation_time = now - 3ms sync_delay = 2ms refresh_interval = 16ms next_presentation_time = last_presentation_time + refresh_interval = now + 13ms -3ms now +13ms +29ms +45ms ----|--+------------|---------------|---------------|---- : : last_presentation_time next_presentation_time ``` Old algorithm: ``` update_time = last_presentation_time + sync_delay = now - 1ms while (update_time < now) (now - 1ms < now) update_time = now - 1ms + 16ms update_time = now + 15ms next_presentation_time = now + 13ms available_render_time = next_presentation_time - max(now, update_time) = (now + 13ms) - (now + 15ms) = -2ms so the next frame will be skipped. -3ms now +13ms +29ms +45ms ----|--+------------|-+-------------|---------------|---- : : : : : update_time (too late) : : last_presentation_time next_presentation_time (a missed frame) ``` New algorithm: ``` min_render_time_allowed = refresh_interval / 2 = 8ms max_render_time_allowed = refresh_interval - sync_delay = 14ms target_presentation_time = last_presentation_time + refresh_interval = now - 3ms + 16ms = now + 13ms while (target_presentation_time - min_render_time_allowed < now) (now + 13ms - 8ms < now) (5ms < 0ms) # loop is never entered update_time = target_presentation_time - max_render_time_allowed = now + 13ms - 14ms = now - 1ms next_presentation_time = now + 13ms available_render_time = next_presentation_time - max(now, update_time) = (now + 13ms) - now = 13ms which is plenty of render time. -3ms now +13ms +29ms +45ms ----|-++------------|---------------|---------------|---- : : : : update_time : : : last_presentation_time next_presentation_time ``` The reason nobody noticed these missed frames very often was because mutter has some accidental workarounds built-in: * Prior to 3.32, the offending code was only reachable in Xorg sessions. It was never reached in Wayland sessions because it hadn't been implemented yet (till e9e4b2b72). * Even though Wayland support is now implemented the native backend provides a `last_presentation_time` much faster than Xorg sessions (being in the same process) and so is less likely to spuriously enter the while loop to miss a frame. * For Xorg sessions we are accidentally triple buffering (#334). This is a good way to avoid the missed frames, but is also an accident. * `sync_delay` is presently just high enough (2ms by coincidence is very close to common values of `now - last_presentation_time`) to push the `update_time` into the future in some cases, which avoids entering the while loop. This is why the same missed frames problem was also noticed when experimenting with `sync_delay = 0`. v2: adjust variable names and code style. Fixes: https://bugzilla.gnome.org/show_bug.cgi?id=789186 and most of https://gitlab.gnome.org/GNOME/mutter/issues/571 https://gitlab.gnome.org/GNOME/mutter/merge_requests/520
Mutter
Mutter is a Wayland display server and X11 window manager and compositor library.
When used as a Wayland display server, it runs on top of KMS and libinput. It implements the compositor side of the Wayland core protocol as well as various protocol extensions. It also has functionality related to running X11 applications using Xwayland.
When used on top of Xorg it acts as a X11 window manager and compositing manager.
It contains functionality related to, among other things, window management, window compositing, focus tracking, workspace management, keybindings and monitor configuration.
Internally it uses a fork of Cogl, a hardware acceleration abstraction library used to simplify usage of OpenGL pipelines, as well as a fork af Clutter, a scene graph and user interface toolkit.
Mutter is used by, for example, GNOME Shell, the GNOME core user interface, and by Gala, elementary OS's window manager. It can also be run standalone, using the command "mutter", but just running plain mutter is only intended for debugging purposes.
Contributing
To contribute, open merge requests at https://gitlab.gnome.org/GNOME/mutter.
The coding style used is primarily the GNU flavor of the GNOME coding
style
with some minor additions such as preferring stdint.h
types over GLib
fundamental types, and a soft 80 character line limit. However, in general,
look at the file you're editing for inspiration.
Commit messages should follow the GNOME commit message guidelines. We require an URL to either an issue or a merge request in each commit.
License
Mutter is distributed under the terms of the GNU General Public License, version 2 or later. See the COPYING file for detalis.