====== Ubuntu 22.04 + Nvidia Window Movement Stutter ====== //Updated on 2023-10-26 to change to mutter 42.9// There's a known issue within [[https://gitlab.gnome.org/GNOME/mutter|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 [[https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2652/diffs|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 [[https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2652/diffs|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: {{ :os:linux:ubuntu:mutter.patch.gz |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)