User Tools

Site Tools


os:linux:ubuntu:nvidiastutter

Ubuntu 22.04 + Nvidia Window Movement Stutter

Updated on 2023-10-26 to change to mutter 42.9

There's a known issue within Mutter (Gnome library) where, if you fire up some sort GL app (could be glxgears) and try and move windows, you will hit some pretty severe lag/stuttering. There is a patch for this here, but it's a patch for the main branch, which means you won't be able to compile on Ubuntu 22.04 without running into later version dependency issues.

I spent some time today manually patching and building mutter to replace the system version on my machine and got it working. I'm documenting this for my own reference as well as anyone else who comes across this.

If you find anything that's wrong/typoed/doesn't work, feel free to ping me at admin@splitstreams.com and I'll fix whatever is busted.

Dependencies

There are a number of dependencies (dev libraries, aka headers) that have to be installed to build mutter, so we'll start there.

sudo apt install ninja-build meson libgraphene-1.0-dev gsettings-desktop-schemas-dev libjson-glib-dev \
    libcolord-dev liblcms2-dev libxkbfile-dev libxkbcommon-x11-dev libx11-xcb-dev \
    libxcb-randr0-dev libxcb-res0-dev libgnome-desktop-3-dev libcanberra-dev libkf5wayland-dev \
    libgudev-1.0-dev libdrm-dev libgbm-dev libinput-dev libstartup-notification0-dev \
    libpipewire-0.3-dev gnome-settings-daemon-dev libmirwayland-dev libnvidia-egl-wayland-dev \
    libwayland-dev gettext xvfb

I already had a bunch of core build tools installed, so hopefully that's all that needs to be installed for your system.

Mutter-42.9

This is the version of libmutter already installed on your 22.04 machine, so we're going to use that so we don't run into dependencies on later versions of items (I tried and failed to build off the tip of the main branch).

You can download that here: https://gitlab.gnome.org/GNOME/mutter/-/archive/42.9/mutter-42.9.tar.gz.

Once you get that downloaded, you need to move to a dir to unpack it.

tar -xvzf mutter-42.9.tar.gz

Patching

Next up, we need to apply the patch for 42.9, which is basically what I linked to above, but with some slight changes for the older version of mutter.

The patch is this:

diff -ru mutter-42.9.orig/src/core/display.c mutter-42.9/src/core/display.c
--- mutter-42.9.orig/src/core/display.c	2023-03-19 15:24:00.000000000 -0700
+++ mutter-42.9/src/core/display.c	2023-10-26 10:23:26.687211442 -0700
@@ -880,7 +880,6 @@
 
   display->current_time = META_CURRENT_TIME;
 
-  display->grab_resize_timeout_id = 0;
   display->grab_have_keyboard = FALSE;
 
   display->grab_op = META_GRAB_OP_NONE;
@@ -1962,13 +1961,12 @@
   display->grab_anchor_root_y = root_y;
   display->grab_latest_motion_x = root_x;
   display->grab_latest_motion_y = root_y;
-  display->grab_last_moveresize_time = 0;
   display->grab_last_edge_resistance_flags = META_EDGE_RESISTANCE_DEFAULT;
   display->grab_frame_action = frame_action;
 
   meta_display_update_cursor (display);
 
-  g_clear_handle_id (&display->grab_resize_timeout_id, g_source_remove);
+  meta_display_clear_grab_move_resize_later (display);
 
   meta_topic (META_DEBUG_WINDOW_OPS,
               "Grab op %u on window %s successful",
@@ -2054,13 +2052,12 @@
   display->grab_anchor_root_y = 0;
   display->grab_latest_motion_x = 0;
   display->grab_latest_motion_y = 0;
-  display->grab_last_moveresize_time = 0;
   display->grab_last_edge_resistance_flags = META_EDGE_RESISTANCE_DEFAULT;
   display->grab_frame_action = FALSE;
 
   meta_display_update_cursor (display);
 
-  g_clear_handle_id (&display->grab_resize_timeout_id, g_source_remove);
+  meta_display_clear_grab_move_resize_later (display);
 
   if (meta_is_wayland_compositor ())
     meta_display_sync_wayland_input_focus (display);
@@ -2758,6 +2755,21 @@
   return copy;
 }
 
+void
+meta_display_clear_grab_move_resize_later (MetaDisplay *display)
+{
+  if (display->grab_move_resize_later_id)
+    {
+      MetaCompositor *compositor;
+      MetaLaters *laters;
+
+      compositor = meta_display_get_compositor (display);
+      laters = meta_compositor_get_laters (compositor);
+      meta_laters_remove (laters, display->grab_move_resize_later_id);
+      display->grab_move_resize_later_id = 0;
+    }
+}
+
 static void
 prefs_changed_callback (MetaPreference pref,
                         void          *data)
diff -ru mutter-42.9.orig/src/core/display-private.h mutter-42.9/src/core/display-private.h
--- mutter-42.9.orig/src/core/display-private.h	2023-03-19 15:24:00.000000000 -0700
+++ mutter-42.9/src/core/display-private.h	2023-10-26 10:24:54.101094492 -0700
@@ -191,8 +191,7 @@
   MetaEdgeResistanceData *grab_edge_resistance_data;
   unsigned int grab_last_edge_resistance_flags;
 
-  int	      grab_resize_timeout_id;
-
+  unsigned int grab_move_resize_later_id;
   MetaKeyBindingManager key_binding_manager;
 
   /* Opening the display */
@@ -346,6 +345,7 @@
 gboolean meta_grab_op_is_mouse    (MetaGrabOp op);
 gboolean meta_grab_op_is_keyboard (MetaGrabOp op);
 
+void meta_display_clear_grab_move_resize_later (MetaDisplay *display);
 void meta_display_queue_autoraise_callback  (MetaDisplay *display,
                                              MetaWindow  *window);
 void meta_display_remove_autoraise_callback (MetaDisplay *display);
diff -ru mutter-42.9.orig/src/core/window.c mutter-42.9/src/core/window.c
--- mutter-42.9.orig/src/core/window.c	2023-03-19 15:24:00.000000000 -0700
+++ mutter-42.9/src/core/window.c	2023-10-26 10:38:40.578927863 -0700
@@ -132,15 +132,6 @@
 static void meta_window_unqueue (MetaWindow    *window,
                                  MetaQueueType  queuebits);
 
-static void     update_move           (MetaWindow              *window,
-                                       MetaEdgeResistanceFlags  flags,
-                                       int                      x,
-                                       int                      y);
-static void     update_resize         (MetaWindow              *window,
-                                       MetaEdgeResistanceFlags  flags,
-                                       int                      x,
-                                       int                      y,
-                                       gboolean                 force);
 static gboolean should_be_on_all_workspaces (MetaWindow *window);
 
 static void meta_window_flush_calc_showing   (MetaWindow *window);
@@ -5761,44 +5752,6 @@
   return is_onscreen;
 }
 
-static gboolean
-check_moveresize_frequency (MetaWindow *window,
-			    gdouble    *remaining)
-{
-  int64_t current_time;
-  const double max_resizes_per_second = 25.0;
-  const double ms_between_resizes = 1000.0 / max_resizes_per_second;
-  double elapsed;
-
-  current_time = g_get_real_time ();
-
-  /* If we are throttling via _NET_WM_SYNC_REQUEST, we don't need
-   * an artificial timeout-based throttled */
-  if (!window->disable_sync &&
-      window->sync_request_alarm != None)
-    return TRUE;
-
-  elapsed = (current_time - window->display->grab_last_moveresize_time) / 1000;
-
-  if (elapsed >= 0.0 && elapsed < ms_between_resizes)
-    {
-      meta_topic (META_DEBUG_RESIZING,
-                  "Delaying move/resize as only %g of %g ms elapsed",
-                  elapsed, ms_between_resizes);
-
-      if (remaining)
-        *remaining = (ms_between_resizes - elapsed);
-
-      return FALSE;
-    }
-
-  meta_topic (META_DEBUG_RESIZING,
-              " Checked moveresize freq, allowing move/resize now (%g of %g seconds elapsed)",
-              elapsed / 1000.0, 1.0 / max_resizes_per_second);
-
-  return TRUE;
-}
-
 static void
 update_move_maybe_tile (MetaWindow *window,
                         int         shake_threshold,
@@ -6039,34 +5992,59 @@
 }
 
 static gboolean
-update_resize_timeout (gpointer data)
+update_move_cb (gpointer data)
 {
-  MetaWindow *window = data;
+  MetaWindow *window = user_data;
+  window->display->grab_move_resize_later_id = 0;
 
-  update_resize (window,
-                 window->display->grab_last_edge_resistance_flags,
-                 window->display->grab_latest_motion_x,
-                 window->display->grab_latest_motion_y,
-                 TRUE);
-  return FALSE;
+  update_move (window,
+               window->display->grab_last_edge_resistance_flags,
+               window->display->grab_latest_motion_x,
+               window->display->grab_latest_motion_y);
+
+  return G_SOURCE_REMOVE;
+}
+
+static void
+queue_update_move (MetaWindow              *window,
+                   MetaEdgeResistanceFlags  flags,
+                   int                      x,
+                   int                      y)
+{
+  MetaCompositor *compositor;
+  MetaLaters *laters;
+
+  window->display->grab_latest_motion_x = x;
+  window->display->grab_latest_motion_y = y;
+
+  if (window->display->grab_move_resize_later_id)
+    return;
+
+  compositor = meta_display_get_compositor (window->display);
+  laters = meta_compositor_get_laters (compositor);
+  window->display->grab_move_resize_later_id =
+    meta_laters_add (laters,
+                     META_LATER_BEFORE_REDRAW,
+                     update_move_cb,
+                     window, NULL);
 }
 
 static void
 update_resize (MetaWindow              *window,
                MetaEdgeResistanceFlags  flags,
                int                      x,
-               int                      y,
-               gboolean                 force)
+               int                      y)
 {
   int dx, dy;
   MetaGravity gravity;
   MetaRectangle new_rect;
   MetaRectangle old_rect;
-  double remaining = 0;
 
   window->display->grab_latest_motion_x = x;
   window->display->grab_latest_motion_y = y;
 
+  meta_display_clear_grab_move_resize_later (window->display);
+
   dx = x - window->display->grab_anchor_root_x;
   dy = y - window->display->grab_anchor_root_y;
 
@@ -6128,27 +6106,6 @@
   if (window->sync_request_timeout_id != 0)
     return;
 
-  if (!check_moveresize_frequency (window, &remaining) && !force)
-    {
-      /* we are ignoring an event here, so we schedule a
-       * compensation event when we would otherwise not ignore
-       * an event. Otherwise we can become stuck if the user never
-       * generates another event.
-       */
-      if (!window->display->grab_resize_timeout_id)
-	{
-	  window->display->grab_resize_timeout_id =
-	    g_timeout_add ((int)remaining, update_resize_timeout, window);
-	  g_source_set_name_by_id (window->display->grab_resize_timeout_id,
-                                   "[mutter] update_resize_timeout");
-	}
-
-      return;
-    }
-
-  /* Remove any scheduled compensation events */
-  g_clear_handle_id (&window->display->grab_resize_timeout_id, g_source_remove);
-
   meta_window_get_frame_rect (window, &old_rect);
 
   /* One sided resizing ought to actually be one-sided, despite the fact that
@@ -6176,11 +6133,45 @@
   meta_window_resize_frame_with_gravity (window, TRUE,
                                          new_rect.width, new_rect.height,
                                          gravity);
+}
+
+static gboolean
+update_resize_cb (gpointer user_data)
+{
+  MetaWindow *window = user_data;
+
+  window->display->grab_move_resize_later_id = 0;
+
+  update_resize (window,
+                 window->display->grab_last_edge_resistance_flags,
+                 window->display->grab_latest_motion_x,
+                 window->display->grab_latest_motion_y);
+
+  return G_SOURCE_REMOVE;
+}
+
+static void
+queue_update_resize (MetaWindow              *window,
+                     MetaEdgeResistanceFlags  flags,
+                     int                      x,
+                     int                      y)
+{
+  MetaCompositor *compositor;
+  MetaLaters *laters;
+
+  window->display->grab_latest_motion_x = x;
+  window->display->grab_latest_motion_y = y;
+
+  if (window->display->grab_move_resize_later_id)
+    return;
 
-  /* Store the latest resize time, if we actually resized. */
-  if (window->rect.width != old_rect.width ||
-      window->rect.height != old_rect.height)
-    window->display->grab_last_moveresize_time = g_get_real_time ();
+  compositor = meta_display_get_compositor (window->display);
+  laters = meta_compositor_get_laters (compositor);
+  window->display->grab_move_resize_later_id =
+    meta_laters_add (laters,
+                     META_LATER_BEFORE_REDRAW,
+                     update_resize_cb,
+                     window, NULL);
 }
 
 static void
@@ -6204,10 +6195,9 @@
 void
 meta_window_update_resize (MetaWindow *window,
                            MetaEdgeResistanceFlags flags,
-                           int x, int y,
-                           gboolean force)
+                           int x, int y)
 {
-  update_resize (window, flags, x, y, force);
+  update_resize (window, flags, x, y);
 }
 
 static void
@@ -6251,7 +6241,7 @@
           if (window->tile_match != NULL)
             flags |= (META_EDGE_RESISTANCE_SNAP | META_EDGE_RESISTANCE_WINDOWS);
 
-          update_resize (window, flags, x, y, TRUE);
+          update_resize (window, flags, x, y);
           maybe_maximize_tiled_window (window);
         }
     }
@@ -6328,14 +6318,14 @@
       meta_display_check_threshold_reached (window->display, x, y);
       if (meta_grab_op_is_moving (window->display->grab_op))
         {
-          update_move (window, flags, x, y);
+          queue_update_move (window, flags, x, y);
         }
       else if (meta_grab_op_is_resizing (window->display->grab_op))
         {
           if (window->tile_match != NULL)
             flags |= (META_EDGE_RESISTANCE_SNAP | META_EDGE_RESISTANCE_WINDOWS);
 
-          update_resize (window, flags, x, y, FALSE);
+          queue_update_resize (window, flags, x, y);
         }
       return TRUE;
 
diff -ru mutter-42.9.orig/src/core/window-private.h mutter-42.9/src/core/window-private.h
--- mutter-42.9.orig/src/core/window-private.h	2023-03-19 15:24:00.000000000 -0700
+++ mutter-42.9/src/core/window-private.h	2023-10-26 10:39:43.863644170 -0700
@@ -869,8 +869,7 @@
 
 void meta_window_update_resize (MetaWindow *window,
                                 MetaEdgeResistanceFlags flags,
-                                int x, int y,
-                                gboolean force);
+                                int x, int y);
 
 void meta_window_move_resize_internal (MetaWindow          *window,
                                        MetaMoveResizeFlags  flags,
diff -ru mutter-42.9.orig/src/x11/window-x11.c mutter-42.9/src/x11/window-x11.c
--- mutter-42.9.orig/src/x11/window-x11.c	2023-03-19 15:24:00.000000000 -0700
+++ mutter-42.9/src/x11/window-x11.c	2023-10-26 10:40:36.721192953 -0700
@@ -1231,8 +1231,7 @@
       meta_window_update_resize (window,
                                  window->display->grab_last_edge_resistance_flags,
                                  window->display->grab_latest_motion_x,
-                                 window->display->grab_latest_motion_y,
-                                 TRUE);
+                                 window->display->grab_latest_motion_y);
     }
 
   return FALSE;
@@ -4153,8 +4152,7 @@
           meta_window_update_resize (window,
                                      window->display->grab_last_edge_resistance_flags,
                                      window->display->grab_latest_motion_x,
-                                     window->display->grab_latest_motion_y,
-                                     TRUE);
+                                     window->display->grab_latest_motion_y);
         }
     }
 

You can also download the file here: mutter.patch.gz . gunzip mutter.patch.gz if you go that route so you end up with a mutter.patch file.

Now, on to the actual patching.

cd /path/to/mutter-42.9  # wherever you unzipped the source code you got from gitlab
patch -p1 < /path/to/mutter.patch

You should get some output that looks like this (and hopefully no errors):

patching file src/core/display.c      
patching file src/core/display-private.h
patching file src/core/window.c                                                 
patching file src/core/window-private.h
patching file src/x11/window-x11.c

If everything looks good so far, let's head to the building of our new, patched, version of mutter!

Building and Installing Mutter

Believe it or not, the hard part is pretty much done at this point. First, we're going to use meson to configure our build.

cd /path/to/mutter-42.9  # Again, wherever you unzipped the source
meson build/

If everything goes well there, we're ready to actually do our compilation and installation. By default, the install prefix is /usr/local, which is likely what you'll want to keep, but if not you can append –prefix= to your meson build/ command above. e.g. meson build –prefix=/usr. It's worth noting that gnome-shell (which uses mutter as a library) will try to use /usr/local/lib first, so the default is preferred so you don't nuke your core system libs.

All right, let's compile and install! Note that you will get a prompt for your password here to install to /usr/local.

cd /path/to/mutter-42.9
ninja -C build/ install

You should end up with a bunch of output showing you where everything was installed.

Verify Your Changes

Now, we just need to double-check that gnome-shell is going to use our shiny new, custom-built libmutter.

Run the following command:

ldd $(which gnome-shell) | grep libmutter-10

You should see output similar to:

        libmutter-10.so.0 => /usr/local/lib/x86_64-linux-gnu/libmutter-10.so.0 (0x00007f938f542000)

The important bit there is that path prefix is /usr/local/lib and not /usr/lib. If that's the case, you should be ready to apply the changes.

Applying the Changes

The simplest way to apply the changes is a reboot. If you don't want to do a full reboot, and instead just want to restart gnome-shell, just run the following:

kill -3 $(pgrep -x gnome-shell)
os/linux/ubuntu/nvidiastutter.txt · Last modified: 2023/10/26 17:51 by jay