From 180070ae2797db29768ac2c4d7b131ef68017564 Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Mar 30 2021 14:27:42 +0000 Subject: import mutter-3.32.2-55.el8 --- diff --git a/SOURCES/0001-Add-support-for-quad-buffer-stereo.patch b/SOURCES/0001-Add-support-for-quad-buffer-stereo.patch index 38a1213..1130e5d 100644 --- a/SOURCES/0001-Add-support-for-quad-buffer-stereo.patch +++ b/SOURCES/0001-Add-support-for-quad-buffer-stereo.patch @@ -1,4 +1,4 @@ -From 8d7356fd7439f94f163438d55f2b2d3d918de96d Mon Sep 17 00:00:00 2001 +From fd67e75df470b50510b68ccf0f52b0b98d05c63f Mon Sep 17 00:00:00 2001 From: "Owen W. Taylor" Date: Thu, 8 May 2014 18:44:15 -0400 Subject: [PATCH] Add support for quad-buffer stereo @@ -21,7 +21,7 @@ texture_from_pixmap. src/compositor/compositor-private.h | 9 ++ src/compositor/compositor.c | 125 +++++++++++++++ src/compositor/meta-shaped-texture-private.h | 5 +- - src/compositor/meta-shaped-texture.c | 84 +++++++++- + src/compositor/meta-shaped-texture.c | 85 +++++++++- src/compositor/meta-surface-actor-wayland.c | 2 +- src/compositor/meta-surface-actor-x11.c | 54 ++++++- src/compositor/meta-surface-actor-x11.h | 5 + @@ -32,7 +32,7 @@ texture_from_pixmap. src/core/stereo.h | 28 ++++ src/meson.build | 2 + src/wayland/meta-wayland-surface.c | 2 +- - 14 files changed, 481 insertions(+), 20 deletions(-) + 14 files changed, 482 insertions(+), 20 deletions(-) create mode 100644 src/core/stereo.c create mode 100644 src/core/stereo.h @@ -265,7 +265,7 @@ index a86a2bff0..d0efdd4dc 100644 gboolean is_y_inverted); void meta_shaped_texture_set_snippet (MetaShapedTexture *stex, diff --git a/src/compositor/meta-shaped-texture.c b/src/compositor/meta-shaped-texture.c -index d64e214e5..e77a32109 100644 +index d64e214e5..332b4c814 100644 --- a/src/compositor/meta-shaped-texture.c +++ b/src/compositor/meta-shaped-texture.c @@ -88,8 +88,10 @@ struct _MetaShapedTexture @@ -287,7 +287,7 @@ index d64e214e5..e77a32109 100644 stex->texture = NULL; stex->mask_texture = NULL; -@@ -297,6 +300,9 @@ meta_shaped_texture_dispose (GObject *object) +@@ -297,7 +300,11 @@ meta_shaped_texture_dispose (GObject *object) meta_texture_tower_free (stex->paint_tower); stex->paint_tower = NULL; @@ -295,9 +295,11 @@ index d64e214e5..e77a32109 100644 + g_clear_pointer (&stex->paint_tower_right, meta_texture_tower_free); + g_clear_pointer (&stex->texture, cogl_object_unref); ++ g_clear_pointer (&stex->texture_right, cogl_object_unref); g_clear_pointer (&stex->opaque_region, cairo_region_destroy); -@@ -507,8 +513,9 @@ paint_clipped_rectangle (MetaShapedTexture *stex, + meta_shaped_texture_set_mask_texture (stex, NULL); +@@ -507,8 +514,9 @@ paint_clipped_rectangle (MetaShapedTexture *stex, } static void @@ -309,7 +311,7 @@ index d64e214e5..e77a32109 100644 { int width, height; -@@ -516,8 +523,11 @@ set_cogl_texture (MetaShapedTexture *stex, +@@ -516,8 +524,11 @@ set_cogl_texture (MetaShapedTexture *stex, if (stex->texture) cogl_object_unref (stex->texture); @@ -321,7 +323,7 @@ index d64e214e5..e77a32109 100644 if (cogl_tex != NULL) { -@@ -531,6 +541,9 @@ set_cogl_texture (MetaShapedTexture *stex, +@@ -531,6 +542,9 @@ set_cogl_texture (MetaShapedTexture *stex, height = 0; } @@ -331,7 +333,7 @@ index d64e214e5..e77a32109 100644 if (stex->tex_width != width || stex->tex_height != height) { -@@ -544,8 +557,23 @@ set_cogl_texture (MetaShapedTexture *stex, +@@ -544,8 +558,23 @@ set_cogl_texture (MetaShapedTexture *stex, * previous buffer. We only queue a redraw in response to surface * damage. */ @@ -356,7 +358,7 @@ index d64e214e5..e77a32109 100644 } static gboolean -@@ -779,7 +807,9 @@ meta_shaped_texture_paint (ClutterActor *actor) +@@ -779,7 +808,9 @@ meta_shaped_texture_paint (ClutterActor *actor) { MetaShapedTexture *stex = META_SHAPED_TEXTURE (actor); CoglTexture *paint_tex; @@ -366,7 +368,7 @@ index d64e214e5..e77a32109 100644 if (!stex->texture) return; -@@ -841,7 +871,32 @@ meta_shaped_texture_paint (ClutterActor *actor) +@@ -841,7 +872,32 @@ meta_shaped_texture_paint (ClutterActor *actor) return; fb = cogl_get_draw_framebuffer (); @@ -400,7 +402,7 @@ index d64e214e5..e77a32109 100644 } static void -@@ -915,6 +970,12 @@ meta_shaped_texture_set_create_mipmaps (MetaShapedTexture *stex, +@@ -915,6 +971,12 @@ meta_shaped_texture_set_create_mipmaps (MetaShapedTexture *stex, stex->create_mipmaps = create_mipmaps; base_texture = create_mipmaps ? stex->texture : NULL; meta_texture_tower_set_base_texture (stex->paint_tower, base_texture); @@ -413,7 +415,7 @@ index d64e214e5..e77a32109 100644 } } -@@ -1046,6 +1107,12 @@ meta_shaped_texture_update_area (MetaShapedTexture *stex, +@@ -1046,6 +1108,12 @@ meta_shaped_texture_update_area (MetaShapedTexture *stex, clip.y, clip.width, clip.height); @@ -426,7 +428,7 @@ index d64e214e5..e77a32109 100644 stex->prev_invalidation = stex->last_invalidation; stex->last_invalidation = g_get_monotonic_time (); -@@ -1092,17 +1159,18 @@ meta_shaped_texture_update_area (MetaShapedTexture *stex, +@@ -1092,17 +1160,18 @@ meta_shaped_texture_update_area (MetaShapedTexture *stex, } /** @@ -901,5 +903,5 @@ index da0acfcbb..ddad1a45c 100644 meta_shaped_texture_set_is_y_inverted (stex, is_y_inverted); g_clear_pointer (&snippet, cogl_object_unref); -- -2.25.1 +2.28.0 diff --git a/SOURCES/0001-clutter-stage-view-Hide-double-buffered-shadowfb-beh.patch b/SOURCES/0001-clutter-stage-view-Hide-double-buffered-shadowfb-beh.patch new file mode 100644 index 0000000..6d9cffd --- /dev/null +++ b/SOURCES/0001-clutter-stage-view-Hide-double-buffered-shadowfb-beh.patch @@ -0,0 +1,46 @@ +From 7bcc274dbc6cb75814cce3e5c2e7f45cf25b0538 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Tue, 9 Feb 2021 17:59:08 +0100 +Subject: [PATCH 1/2] clutter/stage-view: Hide double buffered shadowfb behind + envvar + +It still results in worse performance than a single FBO based shadowfb, +so don't use it. It will need a new EGL extension for zero copy CPU +memory based FBO to be feasable. +--- + clutter/clutter/clutter-stage-view.c | 12 ++++++++++-- + 1 file changed, 10 insertions(+), 2 deletions(-) + +diff --git a/clutter/clutter/clutter-stage-view.c b/clutter/clutter/clutter-stage-view.c +index 5e5966d06..ec18db7b8 100644 +--- a/clutter/clutter/clutter-stage-view.c ++++ b/clutter/clutter/clutter-stage-view.c +@@ -282,6 +282,14 @@ init_dma_buf_shadowfbs (ClutterStageView *view, + CoglRenderer *cogl_renderer = cogl_context_get_renderer (cogl_context); + CoglFramebuffer *initial_shadowfb; + ++ if (g_strcmp0 (g_getenv ("MUTTER_DEBUG_ENABLE_DOUBLE_BUFFER_SHADOWFB"), ++ "1") != 0) ++ { ++ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, ++ "Double buffered shadowfb not enabled"); ++ return FALSE; ++ } ++ + if (!cogl_clutter_winsys_has_feature (COGL_WINSYS_FEATURE_BUFFER_AGE)) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, +@@ -390,8 +398,8 @@ init_shadowfb (ClutterStageView *view) + return; + } + +- g_warning ("Failed to initialize double buffered shadow fb for %s: %s", +- priv->name, error->message); ++ g_debug ("Failed to initialize double buffered shadow fb for %s: %s", ++ priv->name, error->message); + g_clear_error (&error); + + if (!init_fallback_shadowfb (view, cogl_context, width, height, &error)) +-- +2.29.2 + diff --git a/SOURCES/0001-display-Make-check-alive-timeout-configureable.patch b/SOURCES/0001-display-Make-check-alive-timeout-configureable.patch new file mode 100644 index 0000000..e0dfabe --- /dev/null +++ b/SOURCES/0001-display-Make-check-alive-timeout-configureable.patch @@ -0,0 +1,248 @@ +From 7f6f326a1bb96aad0b7aea9c4d7e257bf53c026c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Fri, 21 Feb 2020 21:03:16 +0100 +Subject: [PATCH] display: Make check-alive timeout configureable + +The check-alive feature is there for the user to be able to terminate +frozen applications more easily. However, sometimes applications are +implemented in a way where they fail to be reply to ping requests in a +timely manner, resulting in that, to the compositor, they are +indistinguishable from clients that have frozen indefinitely. + +When using an application that has these issues, the GUI showed in +response to the failure to respond to ping requests can become annoying, +as it disrupts the visual presentation of the application. + +To allow users to work-around these issues, add a setting allowing them +to configure the timeout waited until an application is considered +frozen, or disabling the check completely. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/1080 +--- + data/org.gnome.mutter.gschema.xml.in | 10 ++++ + src/core/display.c | 18 ++++---- + src/core/prefs.c | 68 ++++++++++++++++++++++++++++ + src/meta/prefs.h | 3 ++ + 4 files changed, 90 insertions(+), 9 deletions(-) + +diff --git a/data/org.gnome.mutter.gschema.xml.in b/data/org.gnome.mutter.gschema.xml.in +index 6cbd9c1b5..4d37b1488 100644 +--- a/data/org.gnome.mutter.gschema.xml.in ++++ b/data/org.gnome.mutter.gschema.xml.in +@@ -123,6 +123,16 @@ + + + ++ ++ 5000 ++ Timeout for check-alive ping ++ ++ Number of milliseconds a client has to respond to a ping request in ++ order to not be detected as frozen. Using 0 will disable the alive check ++ completely. ++ ++ ++ + + + +diff --git a/src/core/display.c b/src/core/display.c +index eb7dc43b6..c30a03385 100644 +--- a/src/core/display.c ++++ b/src/core/display.c +@@ -1923,12 +1923,6 @@ meta_set_syncing (gboolean setting) + } + } + +-/* +- * How long, in milliseconds, we should wait after pinging a window +- * before deciding it's not going to get back to us. +- */ +-#define PING_TIMEOUT_DELAY 5000 +- + /** + * meta_display_ping_timeout: + * @data: All the information about this ping. It is a #MetaPingData +@@ -1986,6 +1980,11 @@ meta_display_ping_window (MetaWindow *window, + { + MetaDisplay *display = window->display; + MetaPingData *ping_data; ++ unsigned int check_alive_timeout; ++ ++ check_alive_timeout = meta_prefs_get_check_alive_timeout (); ++ if (check_alive_timeout == 0) ++ return; + + if (serial == 0) + { +@@ -1999,9 +1998,10 @@ meta_display_ping_window (MetaWindow *window, + ping_data = g_new (MetaPingData, 1); + ping_data->window = window; + ping_data->serial = serial; +- ping_data->ping_timeout_id = g_timeout_add (PING_TIMEOUT_DELAY, +- meta_display_ping_timeout, +- ping_data); ++ ping_data->ping_timeout_id = ++ g_timeout_add (check_alive_timeout, ++ meta_display_ping_timeout, ++ ping_data); + g_source_set_name_by_id (ping_data->ping_timeout_id, "[mutter] meta_display_ping_timeout"); + + display->pending_pings = g_slist_prepend (display->pending_pings, ping_data); +diff --git a/src/core/prefs.c b/src/core/prefs.c +index 3f0db8afc..4892406ce 100644 +--- a/src/core/prefs.c ++++ b/src/core/prefs.c +@@ -99,6 +99,7 @@ static gboolean bell_is_visible = FALSE; + static gboolean bell_is_audible = TRUE; + static gboolean gnome_accessibility = FALSE; + static gboolean gnome_animations = TRUE; ++static unsigned int check_alive_timeout = 5000; + static char *cursor_theme = NULL; + /* cursor_size will, when running as an X11 compositing window manager, be the + * actual cursor size, multiplied with the global window scaling factor. On +@@ -213,6 +214,12 @@ typedef struct + gint *target; + } MetaIntPreference; + ++typedef struct ++{ ++ MetaBasePreference base; ++ unsigned int *target; ++} MetaUintPreference; ++ + + /* All preferences that are not keybindings must be listed here, + * plus in the GSettings schemas and the MetaPreference enum. +@@ -491,6 +498,18 @@ static MetaIntPreference preferences_int[] = + { { NULL, 0, 0 }, NULL }, + }; + ++static MetaUintPreference preferences_uint[] = ++ { ++ { ++ { "check-alive-timeout", ++ SCHEMA_MUTTER, ++ META_PREF_CHECK_ALIVE_TIMEOUT, ++ }, ++ &check_alive_timeout, ++ }, ++ { { NULL, 0, 0 }, NULL }, ++ }; ++ + static void + handle_preference_init_enum (void) + { +@@ -613,6 +632,21 @@ handle_preference_init_int (void) + } + } + ++static void ++handle_preference_init_uint (void) ++{ ++ MetaUintPreference *cursor = preferences_uint; ++ ++ while (cursor->base.key != NULL) ++ { ++ if (cursor->target) ++ *cursor->target = g_settings_get_uint (SETTINGS (cursor->base.schema), ++ cursor->base.key); ++ ++ ++cursor; ++ } ++} ++ + static void + handle_preference_update_enum (GSettings *settings, + gchar *key) +@@ -788,6 +822,28 @@ handle_preference_update_int (GSettings *settings, + } + } + ++static void ++handle_preference_update_uint (GSettings *settings, ++ char *key) ++{ ++ MetaUintPreference *cursor = preferences_uint; ++ unsigned int new_value; ++ ++ while (cursor->base.key && strcmp (key, cursor->base.key) != 0) ++ ++cursor; ++ ++ if (!cursor->base.key || !cursor->target) ++ return; ++ ++ new_value = g_settings_get_uint (SETTINGS (cursor->base.schema), key); ++ ++ if (*cursor->target != new_value) ++ { ++ *cursor->target = new_value; ++ queue_changed (cursor->base.pref); ++ } ++} ++ + + /****************************************************************************/ + /* Listeners. */ +@@ -964,6 +1020,7 @@ meta_prefs_init (void) + handle_preference_init_string (); + handle_preference_init_string_array (); + handle_preference_init_int (); ++ handle_preference_init_uint (); + + init_bindings (); + } +@@ -1017,6 +1074,8 @@ settings_changed (GSettings *settings, + handle_preference_update_bool (settings, key); + else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT32)) + handle_preference_update_int (settings, key); ++ else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT32)) ++ handle_preference_update_uint (settings, key); + else if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING_ARRAY)) + handle_preference_update_string_array (settings, key); + else if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING)) +@@ -1640,6 +1699,9 @@ meta_preference_to_string (MetaPreference pref) + + case META_PREF_AUTO_MAXIMIZE: + return "AUTO_MAXIMIZE"; ++ ++ case META_PREF_CHECK_ALIVE_TIMEOUT: ++ return "CHECK_ALIVE_TIMEOUT"; + } + + return "(unknown)"; +@@ -1966,6 +2028,12 @@ meta_prefs_get_overlay_binding (MetaKeyCombo *combo) + *combo = overlay_key_combo; + } + ++unsigned int ++meta_prefs_get_check_alive_timeout (void) ++{ ++ return check_alive_timeout; ++} ++ + const char * + meta_prefs_get_iso_next_group_option (void) + { +diff --git a/src/meta/prefs.h b/src/meta/prefs.h +index 9664b5c07..f42d1c63c 100644 +--- a/src/meta/prefs.h ++++ b/src/meta/prefs.h +@@ -103,6 +103,7 @@ typedef enum + META_PREF_AUTO_MAXIMIZE, + META_PREF_CENTER_NEW_WINDOWS, + META_PREF_DRAG_THRESHOLD, ++ META_PREF_CHECK_ALIVE_TIMEOUT, + } MetaPreference; + + typedef void (* MetaPrefsChangedFunc) (MetaPreference pref, +@@ -475,4 +476,6 @@ gboolean meta_prefs_bell_is_audible (void); + META_EXPORT + GDesktopVisualBellType meta_prefs_get_visual_bell_type (void); + ++unsigned int meta_prefs_get_check_alive_timeout (void); ++ + #endif +-- +2.28.0 + diff --git a/SOURCES/0001-monitor-config-manager-Handle-multiple-builtin-panel.patch b/SOURCES/0001-monitor-config-manager-Handle-multiple-builtin-panel.patch new file mode 100644 index 0000000..73fcb7b --- /dev/null +++ b/SOURCES/0001-monitor-config-manager-Handle-multiple-builtin-panel.patch @@ -0,0 +1,211 @@ +From 19024a5b2eff02b22cdb3fc90142f522dd361996 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Fri, 27 Nov 2020 09:03:38 +0100 +Subject: [PATCH] monitor-config-manager: Handle multiple builtin panels + gracefully + +While multiple built-in panels isn't actually supported in any +meaningful manner, if we would ever end up with such a situation, e.g. +due to kernel bugs[0], we shouldn't crash when trying to set an +'external only' without any external monitors. + +While we could handle this with more degraded functionality (e.g. don't +support the 'switch' method of monitor configuration at all), handle it +by simply not trying to switch to external-only when there are no, +according to the kernel, external monitors available. This would e.g. +still allow betwene 'mirror-all', and 'linear' switches. + +The crash itself was disguised as an arbitrary X11 BadValue error, due +to mutter trying to resize the root window to 0x0, as the monitor +configuration that was applied consisted of zero logical monitors, thus +was effectively empty. + +[0] https://bugzilla.redhat.com/show_bug.cgi?id=1896904 + +Related: https://bugzilla.redhat.com/show_bug.cgi?id=1899260 +Part-of: +--- + src/backends/meta-monitor-config-manager.c | 3 + + src/tests/monitor-unit-tests.c | 145 +++++++++++++++++++++ + 2 files changed, 148 insertions(+) + +diff --git a/src/backends/meta-monitor-config-manager.c b/src/backends/meta-monitor-config-manager.c +index bc1a39db8..d62bad52d 100644 +--- a/src/backends/meta-monitor-config-manager.c ++++ b/src/backends/meta-monitor-config-manager.c +@@ -1157,6 +1157,9 @@ create_for_switch_config_external (MetaMonitorConfigManager *config_manager) + x += logical_monitor_config->layout.width; + } + ++ if (!logical_monitor_configs) ++ return NULL; ++ + return meta_monitors_config_new (monitor_manager, + logical_monitor_configs, + layout_mode, +diff --git a/src/tests/monitor-unit-tests.c b/src/tests/monitor-unit-tests.c +index f47544b03..725f84173 100644 +--- a/src/tests/monitor-unit-tests.c ++++ b/src/tests/monitor-unit-tests.c +@@ -3175,6 +3175,149 @@ meta_test_monitor_non_upright_panel (void) + check_monitor_configuration (&test_case); + } + ++static void ++meta_test_monitor_switch_external_without_external (void) ++{ ++ MonitorTestCase test_case = { ++ .setup = { ++ .modes = { ++ { ++ .width = 1024, ++ .height = 768, ++ .refresh_rate = 60.0 ++ } ++ }, ++ .n_modes = 1, ++ .outputs = { ++ { ++ .crtc = 0, ++ .modes = { 0 }, ++ .n_modes = 1, ++ .preferred_mode = 0, ++ .possible_crtcs = { 0 }, ++ .n_possible_crtcs = 1, ++ .width_mm = 222, ++ .height_mm = 125, ++ .is_laptop_panel = TRUE ++ }, ++ { ++ .crtc = 1, ++ .modes = { 0 }, ++ .n_modes = 1, ++ .preferred_mode = 0, ++ .possible_crtcs = { 1 }, ++ .n_possible_crtcs = 1, ++ .width_mm = 222, ++ .height_mm = 125, ++ .is_laptop_panel = TRUE ++ } ++ }, ++ .n_outputs = 2, ++ .crtcs = { ++ { ++ .current_mode = 0 ++ }, ++ { ++ .current_mode = 0 ++ } ++ }, ++ .n_crtcs = 2 ++ }, ++ ++ .expect = { ++ .monitors = { ++ { ++ .outputs = { 0 }, ++ .n_outputs = 1, ++ .modes = { ++ { ++ .width = 1024, ++ .height = 768, ++ .refresh_rate = 60.0, ++ .crtc_modes = { ++ { ++ .output = 0, ++ .crtc_mode = 0 ++ } ++ } ++ } ++ }, ++ .n_modes = 1, ++ .current_mode = 0, ++ .width_mm = 222, ++ .height_mm = 125 ++ }, ++ { ++ .outputs = { 1 }, ++ .n_outputs = 1, ++ .modes = { ++ { ++ .width = 1024, ++ .height = 768, ++ .refresh_rate = 60.0, ++ .crtc_modes = { ++ { ++ .output = 1, ++ .crtc_mode = 0 ++ } ++ } ++ } ++ }, ++ .n_modes = 1, ++ .current_mode = 0, ++ .width_mm = 222, ++ .height_mm = 125 ++ } ++ }, ++ .n_monitors = 2, ++ .logical_monitors = { ++ { ++ .monitors = { 0 }, ++ .n_monitors = 1, ++ .layout = { .x = 0, .y = 0, .width = 1024, .height = 768 }, ++ .scale = 1 ++ }, ++ { ++ .monitors = { 1 }, ++ .n_monitors = 1, ++ .layout = { .x = 1024, .y = 0, .width = 1024, .height = 768 }, ++ .scale = 1 ++ } ++ }, ++ .n_logical_monitors = 2, ++ .primary_logical_monitor = 0, ++ .n_outputs = 2, ++ .crtcs = { ++ { ++ .current_mode = 0, ++ }, ++ { ++ .current_mode = 0, ++ }, ++ }, ++ .n_crtcs = 2, ++ .n_tiled_monitors = 0, ++ .screen_width = 2048, ++ .screen_height = 768 ++ } ++ }; ++ MetaMonitorTestSetup *test_setup; ++ MetaBackend *backend = meta_get_backend (); ++ MetaMonitorManager *monitor_manager = ++ meta_backend_get_monitor_manager (backend); ++ ++ test_setup = create_monitor_test_setup (&test_case.setup, ++ MONITOR_TEST_FLAG_NO_STORED); ++ emulate_hotplug (test_setup); ++ check_monitor_configuration (&test_case); ++ ++ meta_monitor_manager_switch_config (monitor_manager, ++ META_MONITOR_SWITCH_CONFIG_EXTERNAL); ++ check_monitor_configuration (&test_case); ++ ++ check_monitor_test_clients_state (); ++} ++ + static void + meta_test_monitor_custom_vertical_config (void) + { +@@ -5969,6 +6112,8 @@ init_monitor_tests (void) + meta_test_monitor_preferred_non_first_mode); + add_monitor_test ("/backends/monitor/non-upright-panel", + meta_test_monitor_non_upright_panel); ++ add_monitor_test ("/backends/monitor/switch-external-without-external", ++ meta_test_monitor_switch_external_without_external); + + add_monitor_test ("/backends/monitor/custom/vertical-config", + meta_test_monitor_custom_vertical_config); +-- +2.29.2 + diff --git a/SOURCES/0001-monitor-manager-kms-Trigger-hotplug-processing-on-gp.patch b/SOURCES/0001-monitor-manager-kms-Trigger-hotplug-processing-on-gp.patch new file mode 100644 index 0000000..427bb17 --- /dev/null +++ b/SOURCES/0001-monitor-manager-kms-Trigger-hotplug-processing-on-gp.patch @@ -0,0 +1,41 @@ +From 9f8564ce066aeb704341d6f926daec0045243b70 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Thu, 25 Jun 2020 10:06:38 +0200 +Subject: [PATCH 1/2] monitor-manager-kms: Trigger hotplug processing on gpu + removal + +--- + src/backends/native/meta-monitor-manager-kms.c | 16 +++++++++++----- + 1 file changed, 11 insertions(+), 5 deletions(-) + +diff --git a/src/backends/native/meta-monitor-manager-kms.c b/src/backends/native/meta-monitor-manager-kms.c +index 9a0364441a..2819881576 100644 +--- a/src/backends/native/meta-monitor-manager-kms.c ++++ b/src/backends/native/meta-monitor-manager-kms.c +@@ -470,12 +470,18 @@ on_uevent (GUdevClient *client, + + if (!g_strcmp0 (seat_id, device_seat)) + handle_gpu_hotplug (manager_kms, device); +- } +- +- if (!g_udev_device_get_property_as_boolean (device, "HOTPLUG")) +- return; + +- handle_hotplug_event (manager); ++ handle_hotplug_event (manager); ++ } ++ else if (g_str_equal (action, "remove") && ++ g_udev_device_get_device_file (device) != NULL) ++ { ++ handle_hotplug_event (manager); ++ } ++ else if (g_udev_device_get_property_as_boolean (device, "HOTPLUG")) ++ { ++ handle_hotplug_event (manager); ++ } + } + + static void +-- +2.26.2 + diff --git a/SOURCES/0001-xwayland-Don-t-spew-warnings-when-looking-for-X11-di.patch b/SOURCES/0001-xwayland-Don-t-spew-warnings-when-looking-for-X11-di.patch new file mode 100644 index 0000000..b1bcbbe --- /dev/null +++ b/SOURCES/0001-xwayland-Don-t-spew-warnings-when-looking-for-X11-di.patch @@ -0,0 +1,304 @@ +From d366b2bc4e89ed5807f0221afc25e66cb3d289ed Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Wed, 9 Dec 2020 11:23:37 +0100 +Subject: [PATCH 1/2] xwayland: Don't spew warnings when looking for X11 + displays + +It's not important, so only show it when doing MUTTER_DEBUG=wayland. +Instead report what display numbers were eventually found. +--- + src/wayland/meta-xwayland.c | 123 +++++++++++++++++++++++++++--------- + 1 file changed, 92 insertions(+), 31 deletions(-) + +diff --git a/src/wayland/meta-xwayland.c b/src/wayland/meta-xwayland.c +index 15c85df69..699d5561c 100644 +--- a/src/wayland/meta-xwayland.c ++++ b/src/wayland/meta-xwayland.c +@@ -146,9 +146,10 @@ meta_xwayland_is_xwayland_surface (MetaWaylandSurface *surface) + } + + static gboolean +-try_display (int display, +- char **filename_out, +- int *fd_out) ++try_display (int display, ++ char **filename_out, ++ int *fd_out, ++ GError **error) + { + gboolean ret = FALSE; + char *filename; +@@ -164,11 +165,32 @@ try_display (int display, + char pid[11]; + char *end; + pid_t other; ++ int read_bytes; + + fd = open (filename, O_CLOEXEC, O_RDONLY); +- if (fd < 0 || read (fd, pid, 11) != 11) ++ if (fd < 0) + { +- g_warning ("can't read lock file %s: %m", filename); ++ g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), ++ "Failed to open lock file %s: %s", ++ filename, g_strerror (errno)); ++ goto out; ++ } ++ ++ read_bytes = read (fd, pid, 11); ++ if (read_bytes != 11) ++ { ++ if (read_bytes < 0) ++ { ++ g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), ++ "Failed to read lock file %s: %s", ++ filename, g_strerror (errno)); ++ } ++ else ++ { ++ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, ++ "Failed to read lock file %s", ++ filename); ++ } + goto out; + } + close (fd); +@@ -178,7 +200,8 @@ try_display (int display, + other = strtol (pid, &end, 0); + if (end != pid + 10) + { +- g_warning ("can't parse lock file %s", filename); ++ g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, ++ "Can't parse lock file %s", filename); + goto out; + } + +@@ -187,18 +210,23 @@ try_display (int display, + /* Process is dead. Try unlinking the lock file and trying again. */ + if (unlink (filename) < 0) + { +- g_warning ("failed to unlink stale lock file %s: %m", filename); ++ g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), ++ "Failed to unlink stale lock file %s: %m", filename); + goto out; + } + + goto again; + } + ++ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, ++ "Lock file %s already occupied", filename); + goto out; + } + else if (fd < 0) + { +- g_warning ("failed to create lock file %s: %m", filename); ++ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, ++ "Failed to create lock file %s: %s", ++ filename, g_strerror (errno)); + goto out; + } + +@@ -223,24 +251,34 @@ try_display (int display, + } + + static char * +-create_lock_file (int display, int *display_out) ++create_lock_file (int display, ++ int *display_out, ++ GError **error) + { ++ g_autoptr (GError) local_error = NULL; + char *filename; + int fd; +- + char pid[12]; + int size; + int number_of_tries = 0; + +- while (!try_display (display, &filename, &fd)) ++ while (!try_display (display, &filename, &fd, &local_error)) + { ++ meta_verbose ("Failed to open display %d: %s\n", ++ display, local_error->message); ++ g_clear_error (&local_error); ++ + display++; + number_of_tries++; + + /* If we can't get a display after 50 times, then something's wrong. Just + * abort in this case. */ + if (number_of_tries >= 50) +- return NULL; ++ { ++ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, ++ "Tried to bind 50 display numbers, giving up"); ++ return NULL; ++ } + } + + /* Subtle detail: we use the pid of the wayland compositor, not the xserver +@@ -248,11 +286,22 @@ create_lock_file (int display, int *display_out) + * it _would've_ written without either the NUL or the size clamping, hence + * the disparity in size. */ + size = snprintf (pid, 12, "%10d\n", getpid ()); ++ errno = 0; + if (size != 11 || write (fd, pid, 11) != 11) + { ++ if (errno != 0) ++ { ++ g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), ++ "Failed to write pid to lock file: %s", ++ g_strerror (errno)); ++ } ++ else ++ { ++ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, ++ "Failed to write pid to lock file"); ++ } + unlink (filename); + close (fd); +- g_warning ("failed to write pid to lock file %s", filename); + g_free (filename); + return NULL; + } +@@ -264,8 +313,8 @@ create_lock_file (int display, int *display_out) + } + + static int +-bind_to_abstract_socket (int display, +- gboolean *fatal) ++bind_to_abstract_socket (int display, ++ GError **error) + { + struct sockaddr_un addr; + socklen_t size, name_size; +@@ -274,8 +323,8 @@ bind_to_abstract_socket (int display, + fd = socket (PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (fd < 0) + { +- *fatal = TRUE; +- g_warning ("Failed to create socket: %m"); ++ g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), ++ "Failed to create socket: %s", g_strerror (errno)); + return -1; + } + +@@ -285,17 +334,18 @@ bind_to_abstract_socket (int display, + size = offsetof (struct sockaddr_un, sun_path) + name_size; + if (bind (fd, (struct sockaddr *) &addr, size) < 0) + { +- *fatal = errno != EADDRINUSE; +- g_warning ("failed to bind to @%s: %m", addr.sun_path + 1); ++ g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), ++ "Failed to bind to @%s: %s", ++ addr.sun_path + 1, g_strerror (errno)); + close (fd); + return -1; + } + + if (listen (fd, 1) < 0) + { +- *fatal = errno != EADDRINUSE; +- g_warning ("Failed to listen on abstract socket @%s: %m", +- addr.sun_path + 1); ++ g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), ++ "Failed to listen on abstract socket @%s: %s", ++ addr.sun_path + 1, g_strerror (errno)); + close (fd); + return -1; + } +@@ -304,7 +354,8 @@ bind_to_abstract_socket (int display, + } + + static int +-bind_to_unix_socket (int display) ++bind_to_unix_socket (int display, ++ GError **error) + { + struct sockaddr_un addr; + socklen_t size, name_size; +@@ -321,13 +372,18 @@ bind_to_unix_socket (int display) + unlink (addr.sun_path); + if (bind (fd, (struct sockaddr *) &addr, size) < 0) + { +- g_warning ("failed to bind to %s: %m\n", addr.sun_path); ++ g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), ++ "Failed to bind to %s: %s", ++ addr.sun_path, g_strerror (errno)); + close (fd); + return -1; + } + + if (listen (fd, 1) < 0) + { ++ g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), ++ "Failed to listen on %s: %s", ++ addr.sun_path, g_strerror (errno)); + unlink (addr.sun_path); + close (fd); + return -1; +@@ -385,7 +441,6 @@ choose_xdisplay (MetaXWaylandManager *manager) + { + int display = 0; + char *lock_file = NULL; +- gboolean fatal = FALSE; + + if (display_number_override != -1) + display = display_number_override; +@@ -394,33 +449,37 @@ choose_xdisplay (MetaXWaylandManager *manager) + + do + { +- lock_file = create_lock_file (display, &display); ++ g_autoptr (GError) error = NULL; ++ ++ lock_file = create_lock_file (display, &display, &error); + if (!lock_file) + { +- g_warning ("Failed to create an X lock file"); ++ g_warning ("Failed to create an X lock file: %s", error->message); + return FALSE; + } + +- manager->abstract_fd = bind_to_abstract_socket (display, &fatal); ++ manager->abstract_fd = bind_to_abstract_socket (display, &error); + if (manager->abstract_fd < 0) + { + unlink (lock_file); + +- if (!fatal) ++ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_ADDRESS_IN_USE)) + { ++ meta_verbose ("Failed to bind abstract socket: %s\n", error->message); + display++; + continue; + } + else + { +- g_warning ("Failed to bind abstract socket"); ++ g_warning ("Failed to bind abstract socket: %s", error->message); + return FALSE; + } + } + +- manager->unix_fd = bind_to_unix_socket (display); ++ manager->unix_fd = bind_to_unix_socket (display, &error); + if (manager->unix_fd < 0) + { ++ meta_verbose ("Failed to bind unix socket: %s\n", error->message); + unlink (lock_file); + close (manager->abstract_fd); + display++; +@@ -435,6 +494,8 @@ choose_xdisplay (MetaXWaylandManager *manager) + manager->display_name = g_strdup_printf (":%d", manager->display_index); + manager->lock_file = lock_file; + ++ g_message ("Using X11 display %s for Xwayland", manager->display_name); ++ + return TRUE; + } + +-- +2.29.2 + diff --git a/SOURCES/0002-cogl-gpu-info-Fix-software-acceleration-detection.patch b/SOURCES/0002-cogl-gpu-info-Fix-software-acceleration-detection.patch new file mode 100644 index 0000000..106ebaa --- /dev/null +++ b/SOURCES/0002-cogl-gpu-info-Fix-software-acceleration-detection.patch @@ -0,0 +1,27 @@ +From 03c30b76bae4c2e3f51a6689ebb7c0c60bd7b29a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Tue, 9 Feb 2021 18:00:26 +0100 +Subject: [PATCH 2/2] cogl/gpu-info: Fix software acceleration detection + +The string used to match mesa changed; update to fix software rendering +detection. +--- + cogl/cogl/cogl-gpu-info.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/cogl/cogl/cogl-gpu-info.c b/cogl/cogl/cogl-gpu-info.c +index f44319e96..c1817b3b0 100644 +--- a/cogl/cogl/cogl-gpu-info.c ++++ b/cogl/cogl/cogl-gpu-info.c +@@ -192,6 +192,8 @@ check_mesa_vendor (const CoglGpuInfoStrings *strings) + return TRUE; + else if (strcmp (strings->vendor_string, "Mesa Project") == 0) + return TRUE; ++ else if (strcmp (strings->vendor_string, "Mesa/X.org") == 0) ++ return TRUE; + + return FALSE; + } +-- +2.29.2 + diff --git a/SOURCES/0002-gpu-kms-Reset-CRTC-mode-and-output-list-if-no-resour.patch b/SOURCES/0002-gpu-kms-Reset-CRTC-mode-and-output-list-if-no-resour.patch new file mode 100644 index 0000000..5a1c1ff --- /dev/null +++ b/SOURCES/0002-gpu-kms-Reset-CRTC-mode-and-output-list-if-no-resour.patch @@ -0,0 +1,28 @@ +From a192b9abd77aa14ae794850e41d210472f86b9b0 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Thu, 25 Jun 2020 10:09:48 +0200 +Subject: [PATCH 2/2] gpu-kms: Reset CRTC, mode and output list if no resources + +On device removal, the next resource retrieval will fail; handle this by +just clearing the CRTC, mode and outputs. +--- + src/backends/native/meta-gpu-kms.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/src/backends/native/meta-gpu-kms.c b/src/backends/native/meta-gpu-kms.c +index 93e509def5..dc93abb7b1 100644 +--- a/src/backends/native/meta-gpu-kms.c ++++ b/src/backends/native/meta-gpu-kms.c +@@ -871,6 +871,9 @@ meta_gpu_kms_read_current (MetaGpu *gpu, + local_error->message); + gpu_kms->resources_init_failed_before = TRUE; + } ++ meta_gpu_take_outputs (gpu, NULL); ++ meta_gpu_take_modes (gpu, NULL); ++ meta_gpu_take_crtcs (gpu, NULL); + return TRUE; + } + +-- +2.26.2 + diff --git a/SOURCES/0002-xwayland-Make-sure-tmp-.X11-unix-exists.patch b/SOURCES/0002-xwayland-Make-sure-tmp-.X11-unix-exists.patch new file mode 100644 index 0000000..15d829c --- /dev/null +++ b/SOURCES/0002-xwayland-Make-sure-tmp-.X11-unix-exists.patch @@ -0,0 +1,90 @@ +From 56c2e4efdcef14531dcf752e89117d22a21ec8ad Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Wed, 9 Dec 2020 15:18:29 +0100 +Subject: [PATCH 2/2] xwayland: Make sure /tmp/.X11-unix/ exists + +When we're running under a polyinstantiated SELinux environment, we'll +likely start with an isolated and empty /tmp, meannig no /tmp/.X11-unix +directory to add things to. To make it possible to still function in +this kind of setup, make sure said directory exists. +--- + src/wayland/meta-xwayland.c | 30 ++++++++++++++++++++++++++++-- + 1 file changed, 28 insertions(+), 2 deletions(-) + +diff --git a/src/wayland/meta-xwayland.c b/src/wayland/meta-xwayland.c +index 699d5561c..f3df9766e 100644 +--- a/src/wayland/meta-xwayland.c ++++ b/src/wayland/meta-xwayland.c +@@ -30,6 +30,7 @@ + #include + #include + #include ++#include + #include + + #include "compositor/meta-surface-actor-wayland.h" +@@ -436,9 +437,27 @@ meta_xwayland_override_display_number (int number) + display_number_override = number; + } + ++static gboolean ++ensure_x11_unix_dir (GError **error) ++{ ++ if (mkdir ("/tmp/.X11-unix", 01777) != 0) ++ { ++ if (errno == EEXIST) ++ return TRUE; ++ ++ g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), ++ "Failed to create directory \"/tmp/.X11-unix\": %s", ++ g_strerror (errno)); ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ + static gboolean + choose_xdisplay (MetaXWaylandManager *manager) + { ++ g_autoptr (GError) error = NULL; + int display = 0; + char *lock_file = NULL; + +@@ -447,10 +466,15 @@ choose_xdisplay (MetaXWaylandManager *manager) + else if (g_getenv ("RUNNING_UNDER_GDM")) + display = 1024; + +- do ++ if (!ensure_x11_unix_dir (&error)) + { +- g_autoptr (GError) error = NULL; ++ g_warning ("Failed to ensure X11 socket directory: %s", ++ error->message); ++ return FALSE; ++ } + ++ do ++ { + lock_file = create_lock_file (display, &display, &error); + if (!lock_file) + { +@@ -466,6 +490,7 @@ choose_xdisplay (MetaXWaylandManager *manager) + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_ADDRESS_IN_USE)) + { + meta_verbose ("Failed to bind abstract socket: %s\n", error->message); ++ g_clear_error (&error); + display++; + continue; + } +@@ -480,6 +505,7 @@ choose_xdisplay (MetaXWaylandManager *manager) + if (manager->unix_fd < 0) + { + meta_verbose ("Failed to bind unix socket: %s\n", error->message); ++ g_clear_error (&error); + unlink (lock_file); + close (manager->abstract_fd); + display++; +-- +2.29.2 + diff --git a/SOURCES/cursor-move-only-screen-cast-fixes.patch b/SOURCES/cursor-move-only-screen-cast-fixes.patch new file mode 100644 index 0000000..03d9814 --- /dev/null +++ b/SOURCES/cursor-move-only-screen-cast-fixes.patch @@ -0,0 +1,1115 @@ +From 30caca0cb389dcbbab3d7ba72b92fce8e243b30b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Fri, 3 Jul 2020 16:42:45 +0200 +Subject: [PATCH 1/9] screen-cast-src: Make the two record vfuncs more + similarly named + +Both do more or less the same but with different methods - one puts +pixels into a buffer using the CPU, the other puts pixels into a buffer +using the GPU. + +However, they are behaving slightly different, which they shouldn't. +Lets first address the misleading disconnect in naming, and later we'll +make them behave more similarly. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1351 +(cherry picked from commit d07335cd4ca094d790eac75e75cff01a28fda827) +--- + .../meta-screen-cast-monitor-stream-src.c | 15 ++++++++------- + src/backends/meta-screen-cast-stream-src.c | 17 +++++++++-------- + src/backends/meta-screen-cast-stream-src.h | 8 ++++---- + .../meta-screen-cast-window-stream-src.c | 15 ++++++++------- + 4 files changed, 29 insertions(+), 26 deletions(-) + +diff --git a/src/backends/meta-screen-cast-monitor-stream-src.c b/src/backends/meta-screen-cast-monitor-stream-src.c +index 655b682610..a1a98eb05b 100644 +--- a/src/backends/meta-screen-cast-monitor-stream-src.c ++++ b/src/backends/meta-screen-cast-monitor-stream-src.c +@@ -347,8 +347,8 @@ meta_screen_cast_monitor_stream_src_disable (MetaScreenCastStreamSrc *src) + } + + static gboolean +-meta_screen_cast_monitor_stream_src_record_frame (MetaScreenCastStreamSrc *src, +- uint8_t *data) ++meta_screen_cast_monitor_stream_src_record_to_buffer (MetaScreenCastStreamSrc *src, ++ uint8_t *data) + { + MetaScreenCastMonitorStreamSrc *monitor_src = + META_SCREEN_CAST_MONITOR_STREAM_SRC (src); +@@ -368,8 +368,8 @@ meta_screen_cast_monitor_stream_src_record_frame (MetaScreenCastStreamSrc *src, + } + + static gboolean +-meta_screen_cast_monitor_stream_src_blit_to_framebuffer (MetaScreenCastStreamSrc *src, +- CoglFramebuffer *framebuffer) ++meta_screen_cast_monitor_stream_src_record_to_framebuffer (MetaScreenCastStreamSrc *src, ++ CoglFramebuffer *framebuffer) + { + MetaScreenCastMonitorStreamSrc *monitor_src = + META_SCREEN_CAST_MONITOR_STREAM_SRC (src); +@@ -551,9 +551,10 @@ meta_screen_cast_monitor_stream_src_class_init (MetaScreenCastMonitorStreamSrcCl + src_class->get_specs = meta_screen_cast_monitor_stream_src_get_specs; + src_class->enable = meta_screen_cast_monitor_stream_src_enable; + src_class->disable = meta_screen_cast_monitor_stream_src_disable; +- src_class->record_frame = meta_screen_cast_monitor_stream_src_record_frame; +- src_class->blit_to_framebuffer = +- meta_screen_cast_monitor_stream_src_blit_to_framebuffer; ++ src_class->record_to_buffer = ++ meta_screen_cast_monitor_stream_src_record_to_buffer; ++ src_class->record_to_framebuffer = ++ meta_screen_cast_monitor_stream_src_record_to_framebuffer; + src_class->set_cursor_metadata = + meta_screen_cast_monitor_stream_src_set_cursor_metadata; + } +diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c +index b77186415f..bafb82388d 100644 +--- a/src/backends/meta-screen-cast-stream-src.c ++++ b/src/backends/meta-screen-cast-stream-src.c +@@ -133,23 +133,23 @@ meta_screen_cast_stream_src_get_videocrop (MetaScreenCastStreamSrc *src, + } + + static gboolean +-meta_screen_cast_stream_src_record_frame (MetaScreenCastStreamSrc *src, +- uint8_t *data) ++meta_screen_cast_stream_src_record_to_buffer (MetaScreenCastStreamSrc *src, ++ uint8_t *data) + { + MetaScreenCastStreamSrcClass *klass = + META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src); + +- return klass->record_frame (src, data); ++ return klass->record_to_buffer (src, data); + } + + static gboolean +-meta_screen_cast_stream_src_blit_to_framebuffer (MetaScreenCastStreamSrc *src, +- CoglFramebuffer *framebuffer) ++meta_screen_cast_stream_src_record_to_framebuffer (MetaScreenCastStreamSrc *src, ++ CoglFramebuffer *framebuffer) + { + MetaScreenCastStreamSrcClass *klass = + META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src); + +- return klass->blit_to_framebuffer (src, framebuffer); ++ return klass->record_to_framebuffer (src, framebuffer); + } + + static void +@@ -417,7 +417,7 @@ do_record_frame (MetaScreenCastStreamSrc *src, + if (spa_buffer->datas[0].data || + spa_buffer->datas[0].type == SPA_DATA_MemFd) + { +- return meta_screen_cast_stream_src_record_frame (src, data); ++ return meta_screen_cast_stream_src_record_to_buffer (src, data); + } + else if (spa_buffer->datas[0].type == SPA_DATA_DmaBuf) + { +@@ -427,7 +427,8 @@ do_record_frame (MetaScreenCastStreamSrc *src, + CoglFramebuffer *dmabuf_fbo = + cogl_dma_buf_handle_get_framebuffer (dmabuf_handle); + +- return meta_screen_cast_stream_src_blit_to_framebuffer (src, dmabuf_fbo); ++ return meta_screen_cast_stream_src_record_to_framebuffer (src, ++ dmabuf_fbo); + } + + return FALSE; +diff --git a/src/backends/meta-screen-cast-stream-src.h b/src/backends/meta-screen-cast-stream-src.h +index 3f6a1af2bb..0eda02f717 100644 +--- a/src/backends/meta-screen-cast-stream-src.h ++++ b/src/backends/meta-screen-cast-stream-src.h +@@ -53,10 +53,10 @@ struct _MetaScreenCastStreamSrcClass + float *frame_rate); + void (* enable) (MetaScreenCastStreamSrc *src); + void (* disable) (MetaScreenCastStreamSrc *src); +- gboolean (* record_frame) (MetaScreenCastStreamSrc *src, +- uint8_t *data); +- gboolean (* blit_to_framebuffer) (MetaScreenCastStreamSrc *src, +- CoglFramebuffer *framebuffer); ++ gboolean (* record_to_buffer) (MetaScreenCastStreamSrc *src, ++ uint8_t *data); ++ gboolean (* record_to_framebuffer) (MetaScreenCastStreamSrc *src, ++ CoglFramebuffer *framebuffer); + gboolean (* get_videocrop) (MetaScreenCastStreamSrc *src, + MetaRectangle *crop_rect); + void (* set_cursor_metadata) (MetaScreenCastStreamSrc *src, +diff --git a/src/backends/meta-screen-cast-window-stream-src.c b/src/backends/meta-screen-cast-window-stream-src.c +index c252b4356b..281df5e7b2 100644 +--- a/src/backends/meta-screen-cast-window-stream-src.c ++++ b/src/backends/meta-screen-cast-window-stream-src.c +@@ -462,8 +462,8 @@ meta_screen_cast_window_stream_src_disable (MetaScreenCastStreamSrc *src) + } + + static gboolean +-meta_screen_cast_window_stream_src_record_frame (MetaScreenCastStreamSrc *src, +- uint8_t *data) ++meta_screen_cast_window_stream_src_record_to_buffer (MetaScreenCastStreamSrc *src, ++ uint8_t *data) + { + MetaScreenCastWindowStreamSrc *window_src = + META_SCREEN_CAST_WINDOW_STREAM_SRC (src); +@@ -474,8 +474,8 @@ meta_screen_cast_window_stream_src_record_frame (MetaScreenCastStreamSrc *src, + } + + static gboolean +-meta_screen_cast_window_stream_src_blit_to_framebuffer (MetaScreenCastStreamSrc *src, +- CoglFramebuffer *framebuffer) ++meta_screen_cast_window_stream_src_record_to_framebuffer (MetaScreenCastStreamSrc *src, ++ CoglFramebuffer *framebuffer) + { + MetaScreenCastWindowStreamSrc *window_src = + META_SCREEN_CAST_WINDOW_STREAM_SRC (src); +@@ -591,9 +591,10 @@ meta_screen_cast_window_stream_src_class_init (MetaScreenCastWindowStreamSrcClas + src_class->get_specs = meta_screen_cast_window_stream_src_get_specs; + src_class->enable = meta_screen_cast_window_stream_src_enable; + src_class->disable = meta_screen_cast_window_stream_src_disable; +- src_class->record_frame = meta_screen_cast_window_stream_src_record_frame; +- src_class->blit_to_framebuffer = +- meta_screen_cast_window_stream_src_blit_to_framebuffer; ++ src_class->record_to_buffer = ++ meta_screen_cast_window_stream_src_record_to_buffer; ++ src_class->record_to_framebuffer = ++ meta_screen_cast_window_stream_src_record_to_framebuffer; + src_class->get_videocrop = meta_screen_cast_window_stream_src_get_videocrop; + src_class->set_cursor_metadata = meta_screen_cast_window_stream_src_set_cursor_metadata; + } +-- +2.26.2 + + +From ddc2094222fb55143922364cd4887cb18f856628 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Fri, 3 Jul 2020 16:46:44 +0200 +Subject: [PATCH 2/9] screen-cast/window-stream-src: Fix indentation + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1351 +(cherry picked from commit b1d45820efc5c9136f12d8a3b97a573a2eede9e7) +--- + src/backends/meta-screen-cast-window-stream-src.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/backends/meta-screen-cast-window-stream-src.c b/src/backends/meta-screen-cast-window-stream-src.c +index 281df5e7b2..abdc791575 100644 +--- a/src/backends/meta-screen-cast-window-stream-src.c ++++ b/src/backends/meta-screen-cast-window-stream-src.c +@@ -488,8 +488,8 @@ meta_screen_cast_window_stream_src_record_to_framebuffer (MetaScreenCastStreamSr + stream_rect.height = get_stream_height (window_src); + + if (!meta_screen_cast_window_blit_to_framebuffer (window_src->screen_cast_window, +- &stream_rect, +- framebuffer)) ++ &stream_rect, ++ framebuffer)) + return FALSE; + + stream = meta_screen_cast_stream_src_get_stream (src); +-- +2.26.2 + + +From 59382848840aeb5c6491412742f474a3fb61639e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Fri, 3 Jul 2020 16:48:52 +0200 +Subject: [PATCH 3/9] screen-cast/src: Add flag to maybe_record() + +Will later be used to make recording avoid recording actual pixel +content if e.g. only the cursor moved. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1351 +(cherry picked from commit 92db8902d9c3229a13d104bba71dd74f14d6dfac) +--- + src/backends/meta-screen-cast-monitor-stream-src.c | 8 ++++++-- + src/backends/meta-screen-cast-stream-src.c | 3 ++- + src/backends/meta-screen-cast-stream-src.h | 8 +++++++- + src/backends/meta-screen-cast-window-stream-src.c | 12 +++++++++--- + 4 files changed, 24 insertions(+), 7 deletions(-) + +diff --git a/src/backends/meta-screen-cast-monitor-stream-src.c b/src/backends/meta-screen-cast-monitor-stream-src.c +index a1a98eb05b..8d57fafc0f 100644 +--- a/src/backends/meta-screen-cast-monitor-stream-src.c ++++ b/src/backends/meta-screen-cast-monitor-stream-src.c +@@ -120,8 +120,10 @@ stage_painted (MetaStage *stage, + gpointer user_data) + { + MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (user_data); ++ MetaScreenCastRecordFlag flags; + +- meta_screen_cast_stream_src_maybe_record_frame (src); ++ flags = META_SCREEN_CAST_RECORD_FLAG_NONE; ++ meta_screen_cast_stream_src_maybe_record_frame (src, flags); + } + + static MetaBackend * +@@ -180,6 +182,7 @@ sync_cursor_state (MetaScreenCastMonitorStreamSrc *monitor_src) + { + MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (monitor_src); + ClutterStage *stage = get_stage (monitor_src); ++ MetaScreenCastRecordFlag flags; + + if (!is_cursor_in_stream (monitor_src)) + return; +@@ -187,7 +190,8 @@ sync_cursor_state (MetaScreenCastMonitorStreamSrc *monitor_src) + if (clutter_stage_is_redraw_queued (stage)) + return; + +- meta_screen_cast_stream_src_maybe_record_frame (src); ++ flags = META_SCREEN_CAST_RECORD_FLAG_NONE; ++ meta_screen_cast_stream_src_maybe_record_frame (src, flags); + } + + static void +diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c +index bafb82388d..303c030be7 100644 +--- a/src/backends/meta-screen-cast-stream-src.c ++++ b/src/backends/meta-screen-cast-stream-src.c +@@ -435,7 +435,8 @@ do_record_frame (MetaScreenCastStreamSrc *src, + } + + void +-meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src) ++meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src, ++ MetaScreenCastRecordFlag flags) + { + MetaScreenCastStreamSrcPrivate *priv = + meta_screen_cast_stream_src_get_instance_private (src); +diff --git a/src/backends/meta-screen-cast-stream-src.h b/src/backends/meta-screen-cast-stream-src.h +index 0eda02f717..6c73d05c1d 100644 +--- a/src/backends/meta-screen-cast-stream-src.h ++++ b/src/backends/meta-screen-cast-stream-src.h +@@ -37,6 +37,11 @@ + + typedef struct _MetaScreenCastStream MetaScreenCastStream; + ++typedef enum _MetaScreenCastRecordFlag ++{ ++ META_SCREEN_CAST_RECORD_FLAG_NONE = 0, ++} MetaScreenCastRecordFlag; ++ + #define META_TYPE_SCREEN_CAST_STREAM_SRC (meta_screen_cast_stream_src_get_type ()) + G_DECLARE_DERIVABLE_TYPE (MetaScreenCastStreamSrc, + meta_screen_cast_stream_src, +@@ -63,7 +68,8 @@ struct _MetaScreenCastStreamSrcClass + struct spa_meta_cursor *spa_meta_cursor); + }; + +-void meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src); ++void meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src, ++ MetaScreenCastRecordFlag flags); + + MetaScreenCastStream * meta_screen_cast_stream_src_get_stream (MetaScreenCastStreamSrc *src); + +diff --git a/src/backends/meta-screen-cast-window-stream-src.c b/src/backends/meta-screen-cast-window-stream-src.c +index abdc791575..f64d00860a 100644 +--- a/src/backends/meta-screen-cast-window-stream-src.c ++++ b/src/backends/meta-screen-cast-window-stream-src.c +@@ -338,8 +338,10 @@ screen_cast_window_damaged (MetaWindowActor *actor, + MetaScreenCastWindowStreamSrc *window_src) + { + MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (window_src); ++ MetaScreenCastRecordFlag flags; + +- meta_screen_cast_stream_src_maybe_record_frame (src); ++ flags = META_SCREEN_CAST_RECORD_FLAG_NONE; ++ meta_screen_cast_stream_src_maybe_record_frame (src, flags); + } + + static void +@@ -376,6 +378,7 @@ static void + sync_cursor_state (MetaScreenCastWindowStreamSrc *window_src) + { + MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (window_src); ++ MetaScreenCastRecordFlag flags; + + if (!is_cursor_in_stream (window_src)) + return; +@@ -383,7 +386,8 @@ sync_cursor_state (MetaScreenCastWindowStreamSrc *window_src) + if (meta_screen_cast_window_has_damage (window_src->screen_cast_window)) + return; + +- meta_screen_cast_stream_src_maybe_record_frame (src); ++ flags = META_SCREEN_CAST_RECORD_FLAG_NONE; ++ meta_screen_cast_stream_src_maybe_record_frame (src, flags); + } + + static void +@@ -412,6 +416,7 @@ meta_screen_cast_window_stream_src_enable (MetaScreenCastStreamSrc *src) + MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend); + MetaWindowActor *window_actor; + MetaScreenCastStream *stream; ++ MetaScreenCastRecordFlag flags; + + window_actor = meta_window_actor_from_window (get_window (window_src)); + if (!window_actor) +@@ -449,7 +454,8 @@ meta_screen_cast_window_stream_src_enable (MetaScreenCastStreamSrc *src) + break; + } + +- meta_screen_cast_stream_src_maybe_record_frame (src); ++ flags = META_SCREEN_CAST_RECORD_FLAG_NONE; ++ meta_screen_cast_stream_src_maybe_record_frame (src, flags); + } + + static void +-- +2.26.2 + + +From b8d76f2ded6a0c8b88403d97d4ea2c84993c0263 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Fri, 3 Jul 2020 16:52:43 +0200 +Subject: [PATCH 4/9] screen-cast: Let the reason for recording determine what + to record + +E.g. we'll have pointer movement that, if no painting is already +scheduled, should only send new cursor metadata without any new pixel +buffer. When this happens, tell next step to not record the pixels if +this was the case, instead of having it rediscover this itself. + +Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/1323 +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1351 +(cherry picked from commit cf88d648822eb6b7d412c08d4038c657d415bfff) +--- + .../meta-screen-cast-monitor-stream-src.c | 5 +- + src/backends/meta-screen-cast-stream-src.c | 51 +++++++++++-------- + src/backends/meta-screen-cast-stream-src.h | 1 + + .../meta-screen-cast-window-stream-src.c | 4 +- + 4 files changed, 33 insertions(+), 28 deletions(-) + +diff --git a/src/backends/meta-screen-cast-monitor-stream-src.c b/src/backends/meta-screen-cast-monitor-stream-src.c +index 8d57fafc0f..2352c3b3d8 100644 +--- a/src/backends/meta-screen-cast-monitor-stream-src.c ++++ b/src/backends/meta-screen-cast-monitor-stream-src.c +@@ -190,7 +190,7 @@ sync_cursor_state (MetaScreenCastMonitorStreamSrc *monitor_src) + if (clutter_stage_is_redraw_queued (stage)) + return; + +- flags = META_SCREEN_CAST_RECORD_FLAG_NONE; ++ flags = META_SCREEN_CAST_RECORD_FLAG_CURSOR_ONLY; + meta_screen_cast_stream_src_maybe_record_frame (src, flags); + } + +@@ -361,9 +361,6 @@ meta_screen_cast_monitor_stream_src_record_to_buffer (MetaScreenCastStreamSrc *s + MetaLogicalMonitor *logical_monitor; + + stage = get_stage (monitor_src); +- if (!clutter_stage_is_redraw_queued (stage)) +- return FALSE; +- + monitor = get_monitor (monitor_src); + logical_monitor = meta_monitor_get_logical_monitor (monitor); + clutter_stage_capture_into (stage, FALSE, &logical_monitor->rect, data); +diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c +index 303c030be7..aa4b03b180 100644 +--- a/src/backends/meta-screen-cast-stream-src.c ++++ b/src/backends/meta-screen-cast-stream-src.c +@@ -470,34 +470,41 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src, + return; + } + +- if (do_record_frame (src, spa_buffer, data)) ++ if (!(flags & META_SCREEN_CAST_RECORD_FLAG_CURSOR_ONLY)) + { +- struct spa_meta_region *spa_meta_video_crop; ++ if (do_record_frame (src, spa_buffer, data)) ++ { ++ struct spa_meta_region *spa_meta_video_crop; + +- spa_buffer->datas[0].chunk->size = spa_buffer->datas[0].maxsize; +- spa_buffer->datas[0].chunk->stride = priv->video_stride; ++ spa_buffer->datas[0].chunk->size = spa_buffer->datas[0].maxsize; ++ spa_buffer->datas[0].chunk->stride = priv->video_stride; + +- /* Update VideoCrop if needed */ +- spa_meta_video_crop = +- spa_buffer_find_meta_data (spa_buffer, SPA_META_VideoCrop, +- sizeof (*spa_meta_video_crop)); +- if (spa_meta_video_crop) +- { +- if (meta_screen_cast_stream_src_get_videocrop (src, &crop_rect)) +- { +- spa_meta_video_crop->region.position.x = crop_rect.x; +- spa_meta_video_crop->region.position.y = crop_rect.y; +- spa_meta_video_crop->region.size.width = crop_rect.width; +- spa_meta_video_crop->region.size.height = crop_rect.height; +- } +- else ++ /* Update VideoCrop if needed */ ++ spa_meta_video_crop = ++ spa_buffer_find_meta_data (spa_buffer, SPA_META_VideoCrop, ++ sizeof (*spa_meta_video_crop)); ++ if (spa_meta_video_crop) + { +- spa_meta_video_crop->region.position.x = 0; +- spa_meta_video_crop->region.position.y = 0; +- spa_meta_video_crop->region.size.width = priv->stream_width; +- spa_meta_video_crop->region.size.height = priv->stream_height; ++ if (meta_screen_cast_stream_src_get_videocrop (src, &crop_rect)) ++ { ++ spa_meta_video_crop->region.position.x = crop_rect.x; ++ spa_meta_video_crop->region.position.y = crop_rect.y; ++ spa_meta_video_crop->region.size.width = crop_rect.width; ++ spa_meta_video_crop->region.size.height = crop_rect.height; ++ } ++ else ++ { ++ spa_meta_video_crop->region.position.x = 0; ++ spa_meta_video_crop->region.position.y = 0; ++ spa_meta_video_crop->region.size.width = priv->stream_width; ++ spa_meta_video_crop->region.size.height = priv->stream_height; ++ } + } + } ++ else ++ { ++ spa_buffer->datas[0].chunk->size = 0; ++ } + } + else + { +diff --git a/src/backends/meta-screen-cast-stream-src.h b/src/backends/meta-screen-cast-stream-src.h +index 6c73d05c1d..87054eedf5 100644 +--- a/src/backends/meta-screen-cast-stream-src.h ++++ b/src/backends/meta-screen-cast-stream-src.h +@@ -40,6 +40,7 @@ typedef struct _MetaScreenCastStream MetaScreenCastStream; + typedef enum _MetaScreenCastRecordFlag + { + META_SCREEN_CAST_RECORD_FLAG_NONE = 0, ++ META_SCREEN_CAST_RECORD_FLAG_CURSOR_ONLY = 1 << 0, + } MetaScreenCastRecordFlag; + + #define META_TYPE_SCREEN_CAST_STREAM_SRC (meta_screen_cast_stream_src_get_type ()) +diff --git a/src/backends/meta-screen-cast-window-stream-src.c b/src/backends/meta-screen-cast-window-stream-src.c +index f64d00860a..63c3429df0 100644 +--- a/src/backends/meta-screen-cast-window-stream-src.c ++++ b/src/backends/meta-screen-cast-window-stream-src.c +@@ -340,7 +340,7 @@ screen_cast_window_damaged (MetaWindowActor *actor, + MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (window_src); + MetaScreenCastRecordFlag flags; + +- flags = META_SCREEN_CAST_RECORD_FLAG_NONE; ++ flags = META_SCREEN_CAST_RECORD_FLAG_CURSOR_ONLY; + meta_screen_cast_stream_src_maybe_record_frame (src, flags); + } + +@@ -386,7 +386,7 @@ sync_cursor_state (MetaScreenCastWindowStreamSrc *window_src) + if (meta_screen_cast_window_has_damage (window_src->screen_cast_window)) + return; + +- flags = META_SCREEN_CAST_RECORD_FLAG_NONE; ++ flags = META_SCREEN_CAST_RECORD_FLAG_CURSOR_ONLY; + meta_screen_cast_stream_src_maybe_record_frame (src, flags); + } + +-- +2.26.2 + + +From 5c925cf4de91c9fdd44cb1c13748b2f4d6187dd9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Fri, 3 Jul 2020 16:57:01 +0200 +Subject: [PATCH 5/9] screen-cast/src: Make record functions return an error + when failing + +Now that we don't use the record function to early out depending on +implicit state (don't record pixels if only cursor moved for example), +let it simply report an error when it fails, as we should no longer ever +return without pixels if nothing failed. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1351 +(cherry picked from commit 2d899596e21d43ab241d0ba37c0a9f90c2e610be) +--- + .../meta-screen-cast-monitor-stream-src.c | 19 +++++------ + src/backends/meta-screen-cast-stream-src.c | 32 ++++++++++++------- + src/backends/meta-screen-cast-stream-src.h | 10 +++--- + .../meta-screen-cast-window-stream-src.c | 16 +++++++--- + 4 files changed, 45 insertions(+), 32 deletions(-) + +diff --git a/src/backends/meta-screen-cast-monitor-stream-src.c b/src/backends/meta-screen-cast-monitor-stream-src.c +index 2352c3b3d8..27b3ea37d8 100644 +--- a/src/backends/meta-screen-cast-monitor-stream-src.c ++++ b/src/backends/meta-screen-cast-monitor-stream-src.c +@@ -351,8 +351,9 @@ meta_screen_cast_monitor_stream_src_disable (MetaScreenCastStreamSrc *src) + } + + static gboolean +-meta_screen_cast_monitor_stream_src_record_to_buffer (MetaScreenCastStreamSrc *src, +- uint8_t *data) ++meta_screen_cast_monitor_stream_src_record_to_buffer (MetaScreenCastStreamSrc *src, ++ uint8_t *data, ++ GError **error) + { + MetaScreenCastMonitorStreamSrc *monitor_src = + META_SCREEN_CAST_MONITOR_STREAM_SRC (src); +@@ -369,8 +370,9 @@ meta_screen_cast_monitor_stream_src_record_to_buffer (MetaScreenCastStreamSrc *s + } + + static gboolean +-meta_screen_cast_monitor_stream_src_record_to_framebuffer (MetaScreenCastStreamSrc *src, +- CoglFramebuffer *framebuffer) ++meta_screen_cast_monitor_stream_src_record_to_framebuffer (MetaScreenCastStreamSrc *src, ++ CoglFramebuffer *framebuffer, ++ GError **error) + { + MetaScreenCastMonitorStreamSrc *monitor_src = + META_SCREEN_CAST_MONITOR_STREAM_SRC (src); +@@ -394,7 +396,6 @@ meta_screen_cast_monitor_stream_src_record_to_framebuffer (MetaScreenCastStreamS + for (l = _clutter_stage_peek_stage_views (stage); l; l = l->next) + { + ClutterStageView *view = CLUTTER_STAGE_VIEW (l->data); +- g_autoptr (GError) error = NULL; + CoglFramebuffer *view_framebuffer; + MetaRectangle view_layout; + int x, y; +@@ -415,12 +416,8 @@ meta_screen_cast_monitor_stream_src_record_to_framebuffer (MetaScreenCastStreamS + x, y, + cogl_framebuffer_get_width (view_framebuffer), + cogl_framebuffer_get_height (view_framebuffer), +- &error)) +- { +- g_warning ("Error blitting view into DMABuf framebuffer: %s", +- error->message); +- return FALSE; +- } ++ error)) ++ return FALSE; + } + + cogl_framebuffer_finish (framebuffer); +diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c +index aa4b03b180..b930d5e7c0 100644 +--- a/src/backends/meta-screen-cast-stream-src.c ++++ b/src/backends/meta-screen-cast-stream-src.c +@@ -133,23 +133,25 @@ meta_screen_cast_stream_src_get_videocrop (MetaScreenCastStreamSrc *src, + } + + static gboolean +-meta_screen_cast_stream_src_record_to_buffer (MetaScreenCastStreamSrc *src, +- uint8_t *data) ++meta_screen_cast_stream_src_record_to_buffer (MetaScreenCastStreamSrc *src, ++ uint8_t *data, ++ GError **error) + { + MetaScreenCastStreamSrcClass *klass = + META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src); + +- return klass->record_to_buffer (src, data); ++ return klass->record_to_buffer (src, data, error); + } + + static gboolean +-meta_screen_cast_stream_src_record_to_framebuffer (MetaScreenCastStreamSrc *src, +- CoglFramebuffer *framebuffer) ++meta_screen_cast_stream_src_record_to_framebuffer (MetaScreenCastStreamSrc *src, ++ CoglFramebuffer *framebuffer, ++ GError **error) + { + MetaScreenCastStreamSrcClass *klass = + META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src); + +- return klass->record_to_framebuffer (src, framebuffer); ++ return klass->record_to_framebuffer (src, framebuffer, error); + } + + static void +@@ -407,9 +409,10 @@ maybe_record_cursor (MetaScreenCastStreamSrc *src, + } + + static gboolean +-do_record_frame (MetaScreenCastStreamSrc *src, +- struct spa_buffer *spa_buffer, +- uint8_t *data) ++do_record_frame (MetaScreenCastStreamSrc *src, ++ struct spa_buffer *spa_buffer, ++ uint8_t *data, ++ GError **error) + { + MetaScreenCastStreamSrcPrivate *priv = + meta_screen_cast_stream_src_get_instance_private (src); +@@ -417,7 +420,7 @@ do_record_frame (MetaScreenCastStreamSrc *src, + if (spa_buffer->datas[0].data || + spa_buffer->datas[0].type == SPA_DATA_MemFd) + { +- return meta_screen_cast_stream_src_record_to_buffer (src, data); ++ return meta_screen_cast_stream_src_record_to_buffer (src, data, error); + } + else if (spa_buffer->datas[0].type == SPA_DATA_DmaBuf) + { +@@ -428,9 +431,12 @@ do_record_frame (MetaScreenCastStreamSrc *src, + cogl_dma_buf_handle_get_framebuffer (dmabuf_handle); + + return meta_screen_cast_stream_src_record_to_framebuffer (src, +- dmabuf_fbo); ++ dmabuf_fbo, ++ error); + } + ++ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, ++ "Unknown SPA buffer type %u", spa_buffer->datas[0].type); + return FALSE; + } + +@@ -445,6 +451,7 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src, + struct spa_buffer *spa_buffer; + uint8_t *data = NULL; + uint64_t now_us; ++ g_autoptr (GError) error = NULL; + + now_us = g_get_monotonic_time (); + if (priv->video_format.max_framerate.num > 0 && +@@ -472,7 +479,7 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src, + + if (!(flags & META_SCREEN_CAST_RECORD_FLAG_CURSOR_ONLY)) + { +- if (do_record_frame (src, spa_buffer, data)) ++ if (do_record_frame (src, spa_buffer, data, &error)) + { + struct spa_meta_region *spa_meta_video_crop; + +@@ -503,6 +510,7 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src, + } + else + { ++ g_warning ("Failed to record screen cast frame: %s", error->message); + spa_buffer->datas[0].chunk->size = 0; + } + } +diff --git a/src/backends/meta-screen-cast-stream-src.h b/src/backends/meta-screen-cast-stream-src.h +index 87054eedf5..152790ecfb 100644 +--- a/src/backends/meta-screen-cast-stream-src.h ++++ b/src/backends/meta-screen-cast-stream-src.h +@@ -59,10 +59,12 @@ struct _MetaScreenCastStreamSrcClass + float *frame_rate); + void (* enable) (MetaScreenCastStreamSrc *src); + void (* disable) (MetaScreenCastStreamSrc *src); +- gboolean (* record_to_buffer) (MetaScreenCastStreamSrc *src, +- uint8_t *data); +- gboolean (* record_to_framebuffer) (MetaScreenCastStreamSrc *src, +- CoglFramebuffer *framebuffer); ++ gboolean (* record_to_buffer) (MetaScreenCastStreamSrc *src, ++ uint8_t *data, ++ GError **error); ++ gboolean (* record_to_framebuffer) (MetaScreenCastStreamSrc *src, ++ CoglFramebuffer *framebuffer, ++ GError **error); + gboolean (* get_videocrop) (MetaScreenCastStreamSrc *src, + MetaRectangle *crop_rect); + void (* set_cursor_metadata) (MetaScreenCastStreamSrc *src, +diff --git a/src/backends/meta-screen-cast-window-stream-src.c b/src/backends/meta-screen-cast-window-stream-src.c +index 63c3429df0..70e868997e 100644 +--- a/src/backends/meta-screen-cast-window-stream-src.c ++++ b/src/backends/meta-screen-cast-window-stream-src.c +@@ -468,8 +468,9 @@ meta_screen_cast_window_stream_src_disable (MetaScreenCastStreamSrc *src) + } + + static gboolean +-meta_screen_cast_window_stream_src_record_to_buffer (MetaScreenCastStreamSrc *src, +- uint8_t *data) ++meta_screen_cast_window_stream_src_record_to_buffer (MetaScreenCastStreamSrc *src, ++ uint8_t *data, ++ GError **error) + { + MetaScreenCastWindowStreamSrc *window_src = + META_SCREEN_CAST_WINDOW_STREAM_SRC (src); +@@ -480,8 +481,9 @@ meta_screen_cast_window_stream_src_record_to_buffer (MetaScreenCastStreamSrc *sr + } + + static gboolean +-meta_screen_cast_window_stream_src_record_to_framebuffer (MetaScreenCastStreamSrc *src, +- CoglFramebuffer *framebuffer) ++meta_screen_cast_window_stream_src_record_to_framebuffer (MetaScreenCastStreamSrc *src, ++ CoglFramebuffer *framebuffer, ++ GError **error) + { + MetaScreenCastWindowStreamSrc *window_src = + META_SCREEN_CAST_WINDOW_STREAM_SRC (src); +@@ -496,7 +498,11 @@ meta_screen_cast_window_stream_src_record_to_framebuffer (MetaScreenCastStreamSr + if (!meta_screen_cast_window_blit_to_framebuffer (window_src->screen_cast_window, + &stream_rect, + framebuffer)) +- return FALSE; ++ { ++ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, ++ "Failed to blit window content to framebuffer"); ++ return FALSE; ++ } + + stream = meta_screen_cast_stream_src_get_stream (src); + switch (meta_screen_cast_stream_get_cursor_mode (stream)) +-- +2.26.2 + + +From 47b03793413aad449f021f64e66c68ca95be9a0f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Fri, 3 Jul 2020 23:50:28 +0200 +Subject: [PATCH 6/9] screen-cast/src: Fix signedness of timestamp field + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1351 +(cherry picked from commit 449fa7bf81fe0bee63f497d896cbeffe84dca82d) +--- + src/backends/meta-screen-cast-stream-src.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c +index b930d5e7c0..f6f66daaa3 100644 +--- a/src/backends/meta-screen-cast-stream-src.c ++++ b/src/backends/meta-screen-cast-stream-src.c +@@ -89,7 +89,7 @@ typedef struct _MetaScreenCastStreamSrcPrivate + struct spa_video_info_raw video_format; + int video_stride; + +- uint64_t last_frame_timestamp_us; ++ int64_t last_frame_timestamp_us; + + GHashTable *dmabuf_handles; + +-- +2.26.2 + + +From d8c8ea23e700f85a55a5cc0b79151fbed75ab191 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Fri, 3 Jul 2020 23:57:31 +0200 +Subject: [PATCH 7/9] screen-cast/src: Record follow up frame after timeout + +During animation or other things that cause multiple frames in a row +being painted, we might skip recording frames if the max framerate is +reached. + +Doing so means we might end up skipping the last frame in a series, +ending with the last frame we sent was not the last one, making things +appear to get stuck sometimes. + +Handle this by creating a timeout if we ever throttle, and at the time +the timeout callback is triggered, make sure we eventually send an up to +date frame. + +This is handle differently depending on the source type. A monitor +source type reports 1x1 pixel damage on each view its monitor overlaps, +while a window source type simply records a frame from the surface +directly, except without recording a timestamp, so that timestamps +always refer to when damage actually happened. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1351 +(cherry picked from commit e8052f169b957a502bf86ca65071582692039b9c) +--- + .../meta-screen-cast-monitor-stream-src.c | 43 +++++++++++ + src/backends/meta-screen-cast-stream-src.c | 77 +++++++++++++++++-- + src/backends/meta-screen-cast-stream-src.h | 4 + + .../meta-screen-cast-window-stream-src.c | 11 +++ + 4 files changed, 130 insertions(+), 5 deletions(-) + +diff --git a/src/backends/meta-screen-cast-monitor-stream-src.c b/src/backends/meta-screen-cast-monitor-stream-src.c +index 27b3ea37d8..3079578d8d 100644 +--- a/src/backends/meta-screen-cast-monitor-stream-src.c ++++ b/src/backends/meta-screen-cast-monitor-stream-src.c +@@ -190,6 +190,9 @@ sync_cursor_state (MetaScreenCastMonitorStreamSrc *monitor_src) + if (clutter_stage_is_redraw_queued (stage)) + return; + ++ if (meta_screen_cast_stream_src_pending_follow_up_frame (src)) ++ return; ++ + flags = META_SCREEN_CAST_RECORD_FLAG_CURSOR_ONLY; + meta_screen_cast_stream_src_maybe_record_frame (src, flags); + } +@@ -425,6 +428,44 @@ meta_screen_cast_monitor_stream_src_record_to_framebuffer (MetaScreenCastStreamS + return TRUE; + } + ++static void ++meta_screen_cast_monitor_stream_record_follow_up (MetaScreenCastStreamSrc *src) ++{ ++ MetaScreenCastMonitorStreamSrc *monitor_src = ++ META_SCREEN_CAST_MONITOR_STREAM_SRC (src); ++ MetaBackend *backend = get_backend (monitor_src); ++ MetaRenderer *renderer = meta_backend_get_renderer (backend); ++ ClutterStage *stage = get_stage (monitor_src); ++ MetaMonitor *monitor; ++ MetaLogicalMonitor *logical_monitor; ++ MetaRectangle logical_monitor_layout; ++ GList *l; ++ ++ monitor = get_monitor (monitor_src); ++ logical_monitor = meta_monitor_get_logical_monitor (monitor); ++ logical_monitor_layout = meta_logical_monitor_get_layout (logical_monitor); ++ ++ for (l = meta_renderer_get_views (renderer); l; l = l->next) ++ { ++ MetaRendererView *view = l->data; ++ MetaRectangle view_layout; ++ MetaRectangle damage; ++ ++ clutter_stage_view_get_layout (CLUTTER_STAGE_VIEW (view), &view_layout); ++ ++ if (!meta_rectangle_overlap (&logical_monitor_layout, &view_layout)) ++ continue; ++ ++ damage = (cairo_rectangle_int_t) { ++ .x = view_layout.x, ++ .y = view_layout.y, ++ .width = 1, ++ .height = 1, ++ }; ++ clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (stage), &damage); ++ } ++} ++ + static void + meta_screen_cast_monitor_stream_src_set_cursor_metadata (MetaScreenCastStreamSrc *src, + struct spa_meta_cursor *spa_meta_cursor) +@@ -553,6 +594,8 @@ meta_screen_cast_monitor_stream_src_class_init (MetaScreenCastMonitorStreamSrcCl + meta_screen_cast_monitor_stream_src_record_to_buffer; + src_class->record_to_framebuffer = + meta_screen_cast_monitor_stream_src_record_to_framebuffer; ++ src_class->record_follow_up = ++ meta_screen_cast_monitor_stream_record_follow_up; + src_class->set_cursor_metadata = + meta_screen_cast_monitor_stream_src_set_cursor_metadata; + } +diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c +index f6f66daaa3..55af56f8b9 100644 +--- a/src/backends/meta-screen-cast-stream-src.c ++++ b/src/backends/meta-screen-cast-stream-src.c +@@ -90,6 +90,7 @@ typedef struct _MetaScreenCastStreamSrcPrivate + int video_stride; + + int64_t last_frame_timestamp_us; ++ guint follow_up_frame_source_id; + + GHashTable *dmabuf_handles; + +@@ -107,6 +108,12 @@ G_DEFINE_TYPE_WITH_CODE (MetaScreenCastStreamSrc, + meta_screen_cast_stream_src_init_initable_iface) + G_ADD_PRIVATE (MetaScreenCastStreamSrc)) + ++static inline uint32_t ++us2ms (uint64_t us) ++{ ++ return (uint32_t) (us / 1000); ++} ++ + static void + meta_screen_cast_stream_src_get_specs (MetaScreenCastStreamSrc *src, + int *width, +@@ -154,6 +161,15 @@ meta_screen_cast_stream_src_record_to_framebuffer (MetaScreenCastStreamSrc *src + return klass->record_to_framebuffer (src, framebuffer, error); + } + ++static void ++meta_screen_cast_stream_src_record_follow_up (MetaScreenCastStreamSrc *src) ++{ ++ MetaScreenCastStreamSrcClass *klass = ++ META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src); ++ ++ klass->record_follow_up (src); ++} ++ + static void + meta_screen_cast_stream_src_set_cursor_metadata (MetaScreenCastStreamSrc *src, + struct spa_meta_cursor *spa_meta_cursor) +@@ -440,6 +456,43 @@ do_record_frame (MetaScreenCastStreamSrc *src, + return FALSE; + } + ++gboolean ++meta_screen_cast_stream_src_pending_follow_up_frame (MetaScreenCastStreamSrc *src) ++{ ++ MetaScreenCastStreamSrcPrivate *priv = ++ meta_screen_cast_stream_src_get_instance_private (src); ++ ++ return priv->follow_up_frame_source_id != 0; ++} ++ ++static gboolean ++follow_up_frame_cb (gpointer user_data) ++{ ++ MetaScreenCastStreamSrc *src = user_data; ++ MetaScreenCastStreamSrcPrivate *priv = ++ meta_screen_cast_stream_src_get_instance_private (src); ++ ++ priv->follow_up_frame_source_id = 0; ++ meta_screen_cast_stream_src_record_follow_up (src); ++ ++ return G_SOURCE_REMOVE; ++} ++ ++static void ++maybe_schedule_follow_up_frame (MetaScreenCastStreamSrc *src, ++ int64_t timeout_us) ++{ ++ MetaScreenCastStreamSrcPrivate *priv = ++ meta_screen_cast_stream_src_get_instance_private (src); ++ ++ if (priv->follow_up_frame_source_id) ++ return; ++ ++ priv->follow_up_frame_source_id = g_timeout_add (us2ms (timeout_us), ++ follow_up_frame_cb, ++ src); ++} ++ + void + meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src, + MetaScreenCastRecordFlag flags) +@@ -455,11 +508,24 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src, + + now_us = g_get_monotonic_time (); + if (priv->video_format.max_framerate.num > 0 && +- priv->last_frame_timestamp_us != 0 && +- (now_us - priv->last_frame_timestamp_us < +- ((1000000 * priv->video_format.max_framerate.denom) / +- priv->video_format.max_framerate.num))) +- return; ++ priv->last_frame_timestamp_us != 0) ++ { ++ int64_t min_interval_us; ++ int64_t time_since_last_frame_us; ++ ++ min_interval_us = ((1000000 * priv->video_format.max_framerate.denom) / ++ priv->video_format.max_framerate.num); ++ ++ time_since_last_frame_us = now_us - priv->last_frame_timestamp_us; ++ if (time_since_last_frame_us < min_interval_us) ++ { ++ int64_t timeout_us; ++ ++ timeout_us = min_interval_us - time_since_last_frame_us; ++ maybe_schedule_follow_up_frame (src, timeout_us); ++ return; ++ } ++ } + + if (!priv->pipewire_stream) + return; +@@ -479,6 +545,7 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src, + + if (!(flags & META_SCREEN_CAST_RECORD_FLAG_CURSOR_ONLY)) + { ++ g_clear_handle_id (&priv->follow_up_frame_source_id, g_source_remove); + if (do_record_frame (src, spa_buffer, data, &error)) + { + struct spa_meta_region *spa_meta_video_crop; +diff --git a/src/backends/meta-screen-cast-stream-src.h b/src/backends/meta-screen-cast-stream-src.h +index 152790ecfb..81ea20b173 100644 +--- a/src/backends/meta-screen-cast-stream-src.h ++++ b/src/backends/meta-screen-cast-stream-src.h +@@ -65,6 +65,8 @@ struct _MetaScreenCastStreamSrcClass + gboolean (* record_to_framebuffer) (MetaScreenCastStreamSrc *src, + CoglFramebuffer *framebuffer, + GError **error); ++ void (* record_follow_up) (MetaScreenCastStreamSrc *src); ++ + gboolean (* get_videocrop) (MetaScreenCastStreamSrc *src, + MetaRectangle *crop_rect); + void (* set_cursor_metadata) (MetaScreenCastStreamSrc *src, +@@ -74,6 +76,8 @@ struct _MetaScreenCastStreamSrcClass + void meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src, + MetaScreenCastRecordFlag flags); + ++gboolean meta_screen_cast_stream_src_pending_follow_up_frame (MetaScreenCastStreamSrc *src); ++ + MetaScreenCastStream * meta_screen_cast_stream_src_get_stream (MetaScreenCastStreamSrc *src); + + gboolean meta_screen_cast_stream_src_draw_cursor_into (MetaScreenCastStreamSrc *src, +diff --git a/src/backends/meta-screen-cast-window-stream-src.c b/src/backends/meta-screen-cast-window-stream-src.c +index 70e868997e..7026ec3b4f 100644 +--- a/src/backends/meta-screen-cast-window-stream-src.c ++++ b/src/backends/meta-screen-cast-window-stream-src.c +@@ -520,6 +520,15 @@ meta_screen_cast_window_stream_src_record_to_framebuffer (MetaScreenCastStreamSr + return TRUE; + } + ++static void ++meta_screen_cast_window_stream_record_follow_up (MetaScreenCastStreamSrc *src) ++{ ++ MetaScreenCastRecordFlag flags; ++ ++ flags = META_SCREEN_CAST_RECORD_FLAG_NONE; ++ meta_screen_cast_stream_src_maybe_record_frame (src, flags); ++} ++ + static void + meta_screen_cast_window_stream_src_set_cursor_metadata (MetaScreenCastStreamSrc *src, + struct spa_meta_cursor *spa_meta_cursor) +@@ -607,6 +616,8 @@ meta_screen_cast_window_stream_src_class_init (MetaScreenCastWindowStreamSrcClas + meta_screen_cast_window_stream_src_record_to_buffer; + src_class->record_to_framebuffer = + meta_screen_cast_window_stream_src_record_to_framebuffer; ++ src_class->record_follow_up = ++ meta_screen_cast_window_stream_record_follow_up; + src_class->get_videocrop = meta_screen_cast_window_stream_src_get_videocrop; + src_class->set_cursor_metadata = meta_screen_cast_window_stream_src_set_cursor_metadata; + } +-- +2.26.2 + + +From f2babf5129df9e948f471e3d464162888a99201d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Wed, 8 Jul 2020 15:08:23 +0200 +Subject: [PATCH 8/9] screen-cast/src: Use G_USEC_PER_SEC instead of 1000000 + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1351 +(cherry picked from commit 0c6ac287e6da91ba76bf3958befef4bec6ed28f6) +--- + src/backends/meta-screen-cast-stream-src.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c +index 55af56f8b9..1d6c2b9d08 100644 +--- a/src/backends/meta-screen-cast-stream-src.c ++++ b/src/backends/meta-screen-cast-stream-src.c +@@ -513,8 +513,9 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src, + int64_t min_interval_us; + int64_t time_since_last_frame_us; + +- min_interval_us = ((1000000 * priv->video_format.max_framerate.denom) / +- priv->video_format.max_framerate.num); ++ min_interval_us = ++ ((G_USEC_PER_SEC * priv->video_format.max_framerate.denom) / ++ priv->video_format.max_framerate.num); + + time_since_last_frame_us = now_us - priv->last_frame_timestamp_us; + if (time_since_last_frame_us < min_interval_us) +-- +2.26.2 + + +From 950b3ea51391ffcb434f8f5380459174ba4c4853 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Fri, 10 Jul 2020 07:06:33 +0000 +Subject: [PATCH 9/9] screen-cast/src: Remove follow up timeout source on + disable + +We failed to remove the timeout source when disabling, meaning that if a +follow up was scheduled, and shortly after we disabled the source, the +timeout would be invoked after the source was freed causing +use-after-free bugs. + +Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/1337 + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1365 + +(cherry picked from commit d67ba3ea65717ceab3e0c91267191c6ed2aac2c2) +(cherry picked from commit 1fd53c480f9bb58bd4ac0efc2bbce17dfda8645b) +--- + src/backends/meta-screen-cast-stream-src.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c +index 1d6c2b9d08..f39d348baa 100644 +--- a/src/backends/meta-screen-cast-stream-src.c ++++ b/src/backends/meta-screen-cast-stream-src.c +@@ -622,6 +622,8 @@ meta_screen_cast_stream_src_disable (MetaScreenCastStreamSrc *src) + + META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src)->disable (src); + ++ g_clear_handle_id (&priv->follow_up_frame_source_id, g_source_remove); ++ + priv->is_enabled = FALSE; + } + +-- +2.26.2 + diff --git a/SOURCES/mutter-bump-screencast-api-version.patch b/SOURCES/mutter-bump-screencast-api-version.patch new file mode 100644 index 0000000..37be67e --- /dev/null +++ b/SOURCES/mutter-bump-screencast-api-version.patch @@ -0,0 +1,13 @@ +diff --git a/src/backends/meta-screen-cast.c b/src/backends/meta-screen-cast.c +index 268155e..18fc779 100644 +--- a/src/backends/meta-screen-cast.c ++++ b/src/backends/meta-screen-cast.c +@@ -32,7 +32,7 @@ + + #define META_SCREEN_CAST_DBUS_SERVICE "org.gnome.Mutter.ScreenCast" + #define META_SCREEN_CAST_DBUS_PATH "/org/gnome/Mutter/ScreenCast" +-#define META_SCREEN_CAST_API_VERSION 2 ++#define META_SCREEN_CAST_API_VERSION 3 + + struct _MetaScreenCast + { diff --git a/SOURCES/shadow-buffer-tile-damage.patch b/SOURCES/shadow-buffer-tile-damage.patch new file mode 100644 index 0000000..ad19f8e --- /dev/null +++ b/SOURCES/shadow-buffer-tile-damage.patch @@ -0,0 +1,3248 @@ +From e42c4e83283787062fb446a2aa698f227fe2db5f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Wed, 29 Apr 2020 16:26:52 +0200 +Subject: [PATCH 01/20] cogl/dma-buf-handle: Pass more metadata to handle + constructor + +Could be useful would one want to mmap the dmabuf and deal with its +content manually in CPU space. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1237 +(cherry picked from commit e656d0caf01d8b012d2b458676e5658c540525dc) +--- + cogl/cogl/cogl-dma-buf-handle.c | 45 +++++++++++++++++++++ + cogl/cogl/cogl-dma-buf-handle.h | 46 +++++++++++++++++++++- + src/backends/native/meta-renderer-native.c | 14 +++++-- + 3 files changed, 101 insertions(+), 4 deletions(-) + +diff --git a/cogl/cogl/cogl-dma-buf-handle.c b/cogl/cogl/cogl-dma-buf-handle.c +index 4a8f709f2c..d8b4e57c55 100644 +--- a/cogl/cogl/cogl-dma-buf-handle.c ++++ b/cogl/cogl/cogl-dma-buf-handle.c +@@ -40,6 +40,11 @@ struct _CoglDmaBufHandle + { + CoglFramebuffer *framebuffer; + int dmabuf_fd; ++ int width; ++ int height; ++ int stride; ++ int offset; ++ int bpp; + gpointer user_data; + GDestroyNotify destroy_func; + }; +@@ -47,6 +52,11 @@ struct _CoglDmaBufHandle + CoglDmaBufHandle * + cogl_dma_buf_handle_new (CoglFramebuffer *framebuffer, + int dmabuf_fd, ++ int width, ++ int height, ++ int stride, ++ int offset, ++ int bpp, + gpointer user_data, + GDestroyNotify destroy_func) + { +@@ -61,6 +71,12 @@ cogl_dma_buf_handle_new (CoglFramebuffer *framebuffer, + dmabuf_handle->user_data = user_data; + dmabuf_handle->destroy_func = destroy_func; + ++ dmabuf_handle->width = width; ++ dmabuf_handle->height = height; ++ dmabuf_handle->stride = stride; ++ dmabuf_handle->offset = offset; ++ dmabuf_handle->bpp = bpp; ++ + return dmabuf_handle; + } + +@@ -92,3 +108,32 @@ cogl_dma_buf_handle_get_fd (CoglDmaBufHandle *dmabuf_handle) + return dmabuf_handle->dmabuf_fd; + } + ++int ++cogl_dma_buf_handle_get_width (CoglDmaBufHandle *dmabuf_handle) ++{ ++ return dmabuf_handle->width; ++} ++ ++int ++cogl_dma_buf_handle_get_height (CoglDmaBufHandle *dmabuf_handle) ++{ ++ return dmabuf_handle->height; ++} ++ ++int ++cogl_dma_buf_handle_get_stride (CoglDmaBufHandle *dmabuf_handle) ++{ ++ return dmabuf_handle->stride; ++} ++ ++int ++cogl_dma_buf_handle_get_offset (CoglDmaBufHandle *dmabuf_handle) ++{ ++ return dmabuf_handle->offset; ++} ++ ++int ++cogl_dma_buf_handle_get_bpp (CoglDmaBufHandle *dmabuf_handle) ++{ ++ return dmabuf_handle->bpp; ++} +diff --git a/cogl/cogl/cogl-dma-buf-handle.h b/cogl/cogl/cogl-dma-buf-handle.h +index 25b9b0ccb5..f64a20678d 100644 +--- a/cogl/cogl/cogl-dma-buf-handle.h ++++ b/cogl/cogl/cogl-dma-buf-handle.h +@@ -46,7 +46,12 @@ + CoglDmaBufHandle * + cogl_dma_buf_handle_new (CoglFramebuffer *framebuffer, + int dmabuf_fd, +- gpointer data, ++ int width, ++ int height, ++ int stride, ++ int offset, ++ int bpp, ++ gpointer user_data, + GDestroyNotify destroy_func); + + /** +@@ -79,5 +84,44 @@ cogl_dma_buf_handle_get_framebuffer (CoglDmaBufHandle *dmabuf_handle); + int + cogl_dma_buf_handle_get_fd (CoglDmaBufHandle *dmabuf_handle); + ++/** ++ * cogl_dmabuf_handle_get_width: (skip) ++ * ++ * Returns: the buffer width ++ */ ++int ++cogl_dma_buf_handle_get_width (CoglDmaBufHandle *dmabuf_handle); ++ ++/** ++ * cogl_dmabuf_handle_get_height: (skip) ++ * ++ * Returns: the buffer height ++ */ ++int ++cogl_dma_buf_handle_get_height (CoglDmaBufHandle *dmabuf_handle); ++ ++/** ++ * cogl_dmabuf_handle_get_stride: (skip) ++ * ++ * Returns: the buffer stride ++ */ ++int ++cogl_dma_buf_handle_get_stride (CoglDmaBufHandle *dmabuf_handle); ++ ++/** ++ * cogl_dmabuf_handle_get_offset: (skip) ++ * ++ * Returns: the buffer offset ++ */ ++int ++cogl_dma_buf_handle_get_offset (CoglDmaBufHandle *dmabuf_handle); ++ ++/** ++ * cogl_dmabuf_handle_get_bpp: (skip) ++ * ++ * Returns: the number of bytes per pixel ++ */ ++int ++cogl_dma_buf_handle_get_bpp (CoglDmaBufHandle *dmabuf_handle); + + #endif /* __COGL_DMA_BUF_HANDLE_H__ */ +diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c +index 25833b6cf6..c14cb5acda 100644 +--- a/src/backends/native/meta-renderer-native.c ++++ b/src/backends/native/meta-renderer-native.c +@@ -2641,6 +2641,9 @@ meta_renderer_native_create_dma_buf (CoglRenderer *cogl_renderer, + CoglFramebuffer *dmabuf_fb; + CoglDmaBufHandle *dmabuf_handle; + struct gbm_bo *new_bo; ++ int stride; ++ int offset; ++ int bpp; + int dmabuf_fd = -1; + + new_bo = gbm_bo_create (renderer_gpu_data->gbm.device, +@@ -2664,11 +2667,14 @@ meta_renderer_native_create_dma_buf (CoglRenderer *cogl_renderer, + return NULL; + } + ++ stride = gbm_bo_get_stride (new_bo); ++ offset = gbm_bo_get_offset (new_bo, 0); ++ bpp = 4; + dmabuf_fb = create_dma_buf_framebuffer (renderer_native, + dmabuf_fd, + width, height, +- gbm_bo_get_stride (new_bo), +- gbm_bo_get_offset (new_bo, 0), ++ stride, ++ offset, + DRM_FORMAT_MOD_LINEAR, + DRM_FORMAT_XRGB8888, + error); +@@ -2677,7 +2683,9 @@ meta_renderer_native_create_dma_buf (CoglRenderer *cogl_renderer, + return NULL; + + dmabuf_handle = +- cogl_dma_buf_handle_new (dmabuf_fb, dmabuf_fd, new_bo, ++ cogl_dma_buf_handle_new (dmabuf_fb, dmabuf_fd, ++ width, height, stride, offset, bpp, ++ new_bo, + (GDestroyNotify) gbm_bo_destroy); + cogl_object_unref (dmabuf_fb); + return dmabuf_handle; +-- +2.28.0 + + +From 2270f6dcf7b1e70386f5b4242f92bf5735bb88ba Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Thu, 30 Apr 2020 10:42:30 +0200 +Subject: [PATCH 02/20] clutter/stage-view: Add name property + +Will be used for logging to identify what view a log entry concerns. For +the native and nested backend this is the name of the output the CRTC is +assigned to drive; for X11 it's just "X11 screen", and for the legacy +"X11 screen" emulation mode of the nested backend it's called "legacy +nested". + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1237 +(cherry picked from commit c86367199febdee10ecd7ba24c69b6dda52cb896) +--- + clutter/clutter/clutter-stage-view.c | 18 ++++++++++++++++++ + src/backends/native/meta-renderer-native.c | 3 +++ + .../x11/nested/meta-renderer-x11-nested.c | 4 ++++ + 3 files changed, 25 insertions(+) + +diff --git a/clutter/clutter/clutter-stage-view.c b/clutter/clutter/clutter-stage-view.c +index 0fad6fc446..6b543b5d51 100644 +--- a/clutter/clutter/clutter-stage-view.c ++++ b/clutter/clutter/clutter-stage-view.c +@@ -27,6 +27,7 @@ enum + { + PROP_0, + ++ PROP_NAME, + PROP_LAYOUT, + PROP_FRAMEBUFFER, + PROP_OFFSCREEN, +@@ -40,6 +41,8 @@ static GParamSpec *obj_props[PROP_LAST]; + + typedef struct _ClutterStageViewPrivate + { ++ char *name; ++ + cairo_rectangle_int_t layout; + float scale; + CoglFramebuffer *framebuffer; +@@ -339,6 +342,9 @@ clutter_stage_view_get_property (GObject *object, + + switch (prop_id) + { ++ case PROP_NAME: ++ g_value_set_string (value, priv->name); ++ break; + case PROP_LAYOUT: + g_value_set_boxed (value, &priv->layout); + break; +@@ -372,6 +378,9 @@ clutter_stage_view_set_property (GObject *object, + + switch (prop_id) + { ++ case PROP_NAME: ++ priv->name = g_value_dup_string (value); ++ break; + case PROP_LAYOUT: + layout = g_value_get_boxed (value); + priv->layout = *layout; +@@ -414,6 +423,7 @@ clutter_stage_view_dispose (GObject *object) + ClutterStageViewPrivate *priv = + clutter_stage_view_get_instance_private (view); + ++ g_clear_pointer (&priv->name, g_free); + g_clear_pointer (&priv->framebuffer, cogl_object_unref); + g_clear_pointer (&priv->shadowfb, cogl_object_unref); + g_clear_pointer (&priv->offscreen, cogl_object_unref); +@@ -446,6 +456,14 @@ clutter_stage_view_class_init (ClutterStageViewClass *klass) + object_class->set_property = clutter_stage_view_set_property; + object_class->dispose = clutter_stage_view_dispose; + ++ obj_props[PROP_NAME] = ++ g_param_spec_string ("name", ++ "Name", ++ "Name of view", ++ NULL, ++ G_PARAM_READWRITE | ++ G_PARAM_CONSTRUCT_ONLY | ++ G_PARAM_STATIC_STRINGS); + obj_props[PROP_LAYOUT] = + g_param_spec_boxed ("layout", + "View layout", +diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c +index c14cb5acda..d3fb5b3c55 100644 +--- a/src/backends/native/meta-renderer-native.c ++++ b/src/backends/native/meta-renderer-native.c +@@ -3679,6 +3679,7 @@ meta_renderer_native_create_view (MetaRenderer *renderer, + CoglContext *cogl_context = + cogl_context_from_renderer_native (renderer_native); + CoglDisplay *cogl_display = cogl_context_get_display (cogl_context); ++ MetaMonitor *monitor; + CoglDisplayEGL *cogl_display_egl; + CoglOnscreenEGL *onscreen_egl; + MetaMonitorTransform view_transform; +@@ -3742,7 +3743,9 @@ meta_renderer_native_create_view (MetaRenderer *renderer, + g_error ("Failed to allocate shadow buffer texture: %s", error->message); + } + ++ monitor = meta_logical_monitor_get_monitors (logical_monitor)->data; + view = g_object_new (META_TYPE_RENDERER_VIEW, ++ "name", meta_monitor_get_connector (monitor), + "layout", &logical_monitor->rect, + "scale", scale, + "framebuffer", onscreen, +diff --git a/src/backends/x11/nested/meta-renderer-x11-nested.c b/src/backends/x11/nested/meta-renderer-x11-nested.c +index 5000bf3579..f3a5547dbb 100644 +--- a/src/backends/x11/nested/meta-renderer-x11-nested.c ++++ b/src/backends/x11/nested/meta-renderer-x11-nested.c +@@ -163,6 +163,7 @@ meta_renderer_x11_nested_ensure_legacy_view (MetaRendererX11Nested *renderer_x11 + .height = height + }; + legacy_view = g_object_new (META_TYPE_RENDERER_VIEW, ++ "name", "legacy nested", + "layout", &view_layout, + "framebuffer", COGL_FRAMEBUFFER (fake_onscreen), + NULL); +@@ -179,6 +180,7 @@ meta_renderer_x11_nested_create_view (MetaRenderer *renderer, + meta_backend_get_monitor_manager (backend); + ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); + CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend); ++ MetaMonitor *monitor; + MetaMonitorTransform view_transform; + float view_scale; + int width, height; +@@ -212,7 +214,9 @@ meta_renderer_x11_nested_create_view (MetaRenderer *renderer, + else + offscreen = NULL; + ++ monitor = meta_logical_monitor_get_monitors (logical_monitor)->data; + return g_object_new (META_TYPE_RENDERER_VIEW, ++ "name", meta_monitor_get_connector (monitor), + "layout", &logical_monitor->rect, + "framebuffer", COGL_FRAMEBUFFER (fake_onscreen), + "offscreen", COGL_FRAMEBUFFER (offscreen), +-- +2.28.0 + + +From 6716fde14c5b1a00a02a80b46db67d3f236a87c1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Thu, 30 Apr 2020 17:53:30 +0200 +Subject: [PATCH 03/20] renderer-native: Move shadow fb construction to the + stage view + +The stage view will need a more involved approach to shadow buffers, in +order to implement things such double buffered shadow buffers and damage +detection. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1237 +(cherry picked from commit 3ab89be574f0e02dc67e1b1f538bb24f94612bcf) +--- + clutter/clutter/clutter-stage-view.c | 115 ++++++++++++++++++--- + src/backends/native/meta-renderer-native.c | 25 +---- + 2 files changed, 106 insertions(+), 34 deletions(-) + +diff --git a/clutter/clutter/clutter-stage-view.c b/clutter/clutter/clutter-stage-view.c +index 6b543b5d51..db0067297c 100644 +--- a/clutter/clutter/clutter-stage-view.c ++++ b/clutter/clutter/clutter-stage-view.c +@@ -31,7 +31,7 @@ enum + PROP_LAYOUT, + PROP_FRAMEBUFFER, + PROP_OFFSCREEN, +- PROP_SHADOWFB, ++ PROP_USE_SHADOWFB, + PROP_SCALE, + + PROP_LAST +@@ -50,6 +50,7 @@ typedef struct _ClutterStageViewPrivate + CoglOffscreen *offscreen; + CoglPipeline *offscreen_pipeline; + ++ gboolean use_shadowfb; + CoglOffscreen *shadowfb; + CoglPipeline *shadowfb_pipeline; + +@@ -206,6 +207,80 @@ clutter_stage_view_copy_to_framebuffer (ClutterStageView *view, + cogl_framebuffer_pop_matrix (dst_framebuffer); + } + ++static CoglOffscreen * ++create_offscreen_framebuffer (CoglContext *context, ++ int width, ++ int height, ++ GError **error) ++{ ++ CoglOffscreen *framebuffer; ++ CoglTexture2D *texture; ++ ++ texture = cogl_texture_2d_new_with_size (context, width, height); ++ cogl_primitive_texture_set_auto_mipmap (COGL_PRIMITIVE_TEXTURE (texture), ++ FALSE); ++ ++ if (!cogl_texture_allocate (COGL_TEXTURE (texture), error)) ++ { ++ cogl_object_unref (texture); ++ return FALSE; ++ } ++ ++ framebuffer = cogl_offscreen_new_with_texture (COGL_TEXTURE (texture)); ++ cogl_object_unref (texture); ++ if (!cogl_framebuffer_allocate (COGL_FRAMEBUFFER (framebuffer), error)) ++ { ++ cogl_object_unref (framebuffer); ++ return FALSE; ++ } ++ ++ return framebuffer; ++} ++ ++static gboolean ++init_offscreen_shadowfb (ClutterStageView *view, ++ CoglContext *cogl_context, ++ int width, ++ int height, ++ GError **error) ++{ ++ ClutterStageViewPrivate *priv = ++ clutter_stage_view_get_instance_private (view); ++ CoglOffscreen *offscreen; ++ ++ offscreen = create_offscreen_framebuffer (cogl_context, width, height, error); ++ if (!offscreen) ++ return FALSE; ++ ++ priv->shadowfb = offscreen; ++ return TRUE; ++} ++ ++static void ++init_shadowfb (ClutterStageView *view) ++{ ++ ClutterStageViewPrivate *priv = ++ clutter_stage_view_get_instance_private (view); ++ g_autoptr (GError) error = NULL; ++ int width; ++ int height; ++ CoglContext *cogl_context; ++ ++ width = cogl_framebuffer_get_width (priv->framebuffer); ++ height = cogl_framebuffer_get_height (priv->framebuffer); ++ cogl_context = cogl_framebuffer_get_context (priv->framebuffer); ++ ++ if (!init_offscreen_shadowfb (view, cogl_context, width, height, &error)) ++ { ++ g_warning ("Failed to initialize single buffered shadow fb for %s: %s", ++ priv->name, error->message); ++ } ++ else ++ { ++ g_message ("Initialized single buffered shadow fb for %s", priv->name); ++ } ++} ++ + void + clutter_stage_view_blit_offscreen (ClutterStageView *view, + const cairo_rectangle_int_t *rect) +@@ -354,8 +429,8 @@ clutter_stage_view_get_property (GObject *object, + case PROP_OFFSCREEN: + g_value_set_boxed (value, priv->offscreen); + break; +- case PROP_SHADOWFB: +- g_value_set_boxed (value, priv->shadowfb); ++ case PROP_USE_SHADOWFB: ++ g_value_set_boolean (value, priv->use_shadowfb); + break; + case PROP_SCALE: + g_value_set_float (value, priv->scale); +@@ -405,8 +480,8 @@ clutter_stage_view_set_property (GObject *object, + case PROP_OFFSCREEN: + priv->offscreen = g_value_dup_boxed (value); + break; +- case PROP_SHADOWFB: +- priv->shadowfb = g_value_dup_boxed (value); ++ case PROP_USE_SHADOWFB: ++ priv->use_shadowfb = g_value_get_boolean (value); + break; + case PROP_SCALE: + priv->scale = g_value_get_float (value); +@@ -416,6 +491,19 @@ clutter_stage_view_set_property (GObject *object, + } + } + ++static void ++clutter_stage_view_constructed (GObject *object) ++{ ++ ClutterStageView *view = CLUTTER_STAGE_VIEW (object); ++ ClutterStageViewPrivate *priv = ++ clutter_stage_view_get_instance_private (view); ++ ++ if (priv->use_shadowfb) ++ init_shadowfb (view); ++ ++ G_OBJECT_CLASS (clutter_stage_view_parent_class)->constructed (object); ++} ++ + static void + clutter_stage_view_dispose (GObject *object) + { +@@ -454,6 +542,7 @@ clutter_stage_view_class_init (ClutterStageViewClass *klass) + + object_class->get_property = clutter_stage_view_get_property; + object_class->set_property = clutter_stage_view_set_property; ++ object_class->constructed = clutter_stage_view_constructed; + object_class->dispose = clutter_stage_view_dispose; + + obj_props[PROP_NAME] = +@@ -491,14 +580,14 @@ clutter_stage_view_class_init (ClutterStageViewClass *klass) + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + +- obj_props[PROP_SHADOWFB] = +- g_param_spec_boxed ("shadowfb", +- "Shadow framebuffer", +- "Framebuffer used as intermediate shadow buffer", +- COGL_TYPE_HANDLE, +- G_PARAM_READWRITE | +- G_PARAM_CONSTRUCT_ONLY | +- G_PARAM_STATIC_STRINGS); ++ obj_props[PROP_USE_SHADOWFB] = ++ g_param_spec_boolean ("use-shadowfb", ++ "Use shadowfb", ++ "Whether to use one or more shadow framebuffers", ++ FALSE, ++ G_PARAM_READWRITE | ++ G_PARAM_CONSTRUCT_ONLY | ++ G_PARAM_STATIC_STRINGS); + + obj_props[PROP_SCALE] = + g_param_spec_float ("scale", +diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c +index d3fb5b3c55..463dddd3a7 100644 +--- a/src/backends/native/meta-renderer-native.c ++++ b/src/backends/native/meta-renderer-native.c +@@ -3685,7 +3685,7 @@ meta_renderer_native_create_view (MetaRenderer *renderer, + MetaMonitorTransform view_transform; + CoglOnscreen *onscreen = NULL; + CoglOffscreen *offscreen = NULL; +- CoglOffscreen *shadowfb = NULL; ++ gboolean use_shadowfb; + float scale; + int width, height; + MetaRendererView *view; +@@ -3724,24 +3724,8 @@ meta_renderer_native_create_view (MetaRenderer *renderer, + + } + +- if (should_force_shadow_fb (renderer_native, +- renderer_native->primary_gpu_kms)) +- { +- int shadow_width; +- int shadow_height; +- +- /* The shadowfb must be the same size as the on-screen framebuffer */ +- shadow_width = cogl_framebuffer_get_width (COGL_FRAMEBUFFER (onscreen)); +- shadow_height = cogl_framebuffer_get_height (COGL_FRAMEBUFFER (onscreen)); +- +- shadowfb = meta_renderer_native_create_offscreen (renderer_native, +- cogl_context, +- shadow_width, +- shadow_height, +- &error); +- if (!shadowfb) +- g_error ("Failed to allocate shadow buffer texture: %s", error->message); +- } ++ use_shadowfb = should_force_shadow_fb (renderer_native, ++ renderer_native->primary_gpu_kms); + + monitor = meta_logical_monitor_get_monitors (logical_monitor)->data; + view = g_object_new (META_TYPE_RENDERER_VIEW, +@@ -3750,12 +3734,11 @@ meta_renderer_native_create_view (MetaRenderer *renderer, + "scale", scale, + "framebuffer", onscreen, + "offscreen", offscreen, +- "shadowfb", shadowfb, ++ "use-shadowfb", use_shadowfb, + "logical-monitor", logical_monitor, + "transform", view_transform, + NULL); + g_clear_pointer (&offscreen, cogl_object_unref); +- g_clear_pointer (&shadowfb, cogl_object_unref); + + meta_onscreen_native_set_view (onscreen, view); + +-- +2.28.0 + + +From f79b37583e575d34edb4b7965cb0e48eb2736749 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Thu, 30 Apr 2020 18:19:30 +0200 +Subject: [PATCH 04/20] clutter/stage-view: Move shadowfb struct fields into + anonymous struct + +With the aim to collect shadow buffer related things in one place, place +them in an anonymous struct. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1237 +(cherry picked from commit 310ca695d90b48074a06327e87bd7e924f49cb7f) +--- + clutter/clutter/clutter-stage-view.c | 32 +++++++++++++++------------- + 1 file changed, 17 insertions(+), 15 deletions(-) + +diff --git a/clutter/clutter/clutter-stage-view.c b/clutter/clutter/clutter-stage-view.c +index db0067297c..9bbe158f36 100644 +--- a/clutter/clutter/clutter-stage-view.c ++++ b/clutter/clutter/clutter-stage-view.c +@@ -51,8 +51,10 @@ typedef struct _ClutterStageViewPrivate + CoglPipeline *offscreen_pipeline; + + gboolean use_shadowfb; +- CoglOffscreen *shadowfb; +- CoglPipeline *shadowfb_pipeline; ++ struct { ++ CoglOffscreen *framebuffer; ++ CoglPipeline *pipeline; ++ } shadow; + + guint dirty_viewport : 1; + guint dirty_projection : 1; +@@ -86,8 +88,8 @@ clutter_stage_view_get_framebuffer (ClutterStageView *view) + + if (priv->offscreen) + return priv->offscreen; +- else if (priv->shadowfb) +- return priv->shadowfb; ++ else if (priv->shadow.framebuffer) ++ return priv->shadow.framebuffer; + else + return priv->framebuffer; + } +@@ -153,11 +155,11 @@ clutter_stage_view_ensure_shadowfb_blit_pipeline (ClutterStageView *view) + ClutterStageViewPrivate *priv = + clutter_stage_view_get_instance_private (view); + +- if (priv->shadowfb_pipeline) ++ if (priv->shadow.pipeline) + return; + +- priv->shadowfb_pipeline = +- clutter_stage_view_create_framebuffer_pipeline (priv->shadowfb); ++ priv->shadow.pipeline = ++ clutter_stage_view_create_framebuffer_pipeline (priv->shadow.framebuffer); + } + + void +@@ -252,7 +254,7 @@ init_offscreen_shadowfb (ClutterStageView *view, + if (!offscreen) + return FALSE; + +- priv->shadowfb = offscreen; ++ priv->shadow.framebuffer = offscreen; + return TRUE; + } + +@@ -297,13 +299,13 @@ clutter_stage_view_blit_offscreen (ClutterStageView *view, + clutter_stage_view_get_offscreen_transformation_matrix (view, &matrix); + can_blit = cogl_matrix_is_identity (&matrix); + +- if (priv->shadowfb) ++ if (priv->shadow.framebuffer) + { + clutter_stage_view_copy_to_framebuffer (view, + rect, + priv->offscreen_pipeline, + priv->offscreen, +- priv->shadowfb, ++ priv->shadow.framebuffer, + can_blit); + } + else +@@ -317,13 +319,13 @@ clutter_stage_view_blit_offscreen (ClutterStageView *view, + } + } + +- if (priv->shadowfb) ++ if (priv->shadow.framebuffer) + { + clutter_stage_view_ensure_shadowfb_blit_pipeline (view); + clutter_stage_view_copy_to_framebuffer (view, + rect, +- priv->shadowfb_pipeline, +- priv->shadowfb, ++ priv->shadow.pipeline, ++ priv->shadow.framebuffer, + priv->framebuffer, + TRUE); + } +@@ -513,10 +515,10 @@ clutter_stage_view_dispose (GObject *object) + + g_clear_pointer (&priv->name, g_free); + g_clear_pointer (&priv->framebuffer, cogl_object_unref); +- g_clear_pointer (&priv->shadowfb, cogl_object_unref); ++ g_clear_pointer (&priv->shadow.framebuffer, cogl_object_unref); ++ g_clear_pointer (&priv->shadow.pipeline, cogl_object_unref); + g_clear_pointer (&priv->offscreen, cogl_object_unref); + g_clear_pointer (&priv->offscreen_pipeline, cogl_object_unref); +- g_clear_pointer (&priv->shadowfb_pipeline, cogl_object_unref); + + G_OBJECT_CLASS (clutter_stage_view_parent_class)->dispose (object); + } +-- +2.28.0 + + +From 7bf71e7b5f39fcf34c4a636640636f9452b4b06c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Thu, 30 Apr 2020 21:51:10 +0200 +Subject: [PATCH 05/20] clutter/stage-view: Move fb viewport and projection + setting to here + +The stage would fetch the front framebuffer and set the viewport and +projection matrix, but if we are going to more than one front buffer, +that won't work, so let the stage just pass the viewport and projection +matrix to the view and have the view deal with the framebuffer(s). + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1237 +(cherry picked from commit c79bcf0d7e35cf9e85864cf72ea53659a6b8d8a7) +--- + clutter/clutter/clutter-stage-view-private.h | 8 ++++++ + clutter/clutter/clutter-stage-view.c | 29 ++++++++++++++++++++ + clutter/clutter/clutter-stage.c | 16 ++++------- + 3 files changed, 42 insertions(+), 11 deletions(-) + +diff --git a/clutter/clutter/clutter-stage-view-private.h b/clutter/clutter/clutter-stage-view-private.h +index 89c42599fc..78aa37c9f4 100644 +--- a/clutter/clutter/clutter-stage-view-private.h ++++ b/clutter/clutter/clutter-stage-view-private.h +@@ -28,10 +28,18 @@ gboolean clutter_stage_view_is_dirty_viewport (ClutterStageView *view); + void clutter_stage_view_set_dirty_viewport (ClutterStageView *view, + gboolean dirty); + ++void clutter_stage_view_set_viewport (ClutterStageView *view, ++ float x, ++ float y, ++ float width, ++ float height); ++ + gboolean clutter_stage_view_is_dirty_projection (ClutterStageView *view); + + void clutter_stage_view_set_dirty_projection (ClutterStageView *view, + gboolean dirty); + ++void clutter_stage_view_set_projection (ClutterStageView *view, ++ const CoglMatrix *matrix); + + #endif /* __CLUTTER_STAGE_VIEW_PRIVATE_H__ */ +diff --git a/clutter/clutter/clutter-stage-view.c b/clutter/clutter/clutter-stage-view.c +index 9bbe158f36..4d8bbddc9d 100644 +--- a/clutter/clutter/clutter-stage-view.c ++++ b/clutter/clutter/clutter-stage-view.c +@@ -359,6 +359,22 @@ clutter_stage_view_set_dirty_viewport (ClutterStageView *view, + priv->dirty_viewport = dirty; + } + ++void ++clutter_stage_view_set_viewport (ClutterStageView *view, ++ float x, ++ float y, ++ float width, ++ float height) ++{ ++ ClutterStageViewPrivate *priv = ++ clutter_stage_view_get_instance_private (view); ++ CoglFramebuffer *framebuffer; ++ ++ priv->dirty_viewport = FALSE; ++ framebuffer = clutter_stage_view_get_framebuffer (view); ++ cogl_framebuffer_set_viewport (framebuffer, x, y, width, height); ++} ++ + gboolean + clutter_stage_view_is_dirty_projection (ClutterStageView *view) + { +@@ -378,6 +394,19 @@ clutter_stage_view_set_dirty_projection (ClutterStageView *view, + priv->dirty_projection = dirty; + } + ++void ++clutter_stage_view_set_projection (ClutterStageView *view, ++ const CoglMatrix *matrix) ++{ ++ ClutterStageViewPrivate *priv = ++ clutter_stage_view_get_instance_private (view); ++ CoglFramebuffer *framebuffer; ++ ++ priv->dirty_projection = FALSE; ++ framebuffer = clutter_stage_view_get_framebuffer (view); ++ cogl_framebuffer_set_projection_matrix (framebuffer, matrix); ++} ++ + void + clutter_stage_view_get_offscreen_transformation_matrix (ClutterStageView *view, + CoglMatrix *matrix) +diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c +index 34c4e0119a..4bde234dbf 100644 +--- a/clutter/clutter/clutter-stage.c ++++ b/clutter/clutter/clutter-stage.c +@@ -3687,7 +3687,6 @@ _clutter_stage_maybe_setup_viewport (ClutterStage *stage, + ClutterStageView *view) + { + ClutterStagePrivate *priv = stage->priv; +- CoglFramebuffer *fb = clutter_stage_view_get_framebuffer (view); + + if (clutter_stage_view_is_dirty_viewport (view)) + { +@@ -3716,9 +3715,10 @@ _clutter_stage_maybe_setup_viewport (ClutterStage *stage, + viewport_y = roundf (priv->viewport[1] * fb_scale - viewport_offset_y); + viewport_width = roundf (priv->viewport[2] * fb_scale); + viewport_height = roundf (priv->viewport[3] * fb_scale); +- cogl_framebuffer_set_viewport (fb, +- viewport_x, viewport_y, +- viewport_width, viewport_height); ++ ++ clutter_stage_view_set_viewport (view, ++ viewport_x, viewport_y, ++ viewport_width, viewport_height); + + perspective = priv->perspective; + +@@ -3751,16 +3751,10 @@ _clutter_stage_maybe_setup_viewport (ClutterStage *stage, + z_2d, + priv->viewport[2], + priv->viewport[3]); +- +- clutter_stage_view_set_dirty_viewport (view, FALSE); + } + + if (clutter_stage_view_is_dirty_projection (view)) +- { +- cogl_framebuffer_set_projection_matrix (fb, &priv->projection); +- +- clutter_stage_view_set_dirty_projection (view, FALSE); +- } ++ clutter_stage_view_set_projection (view, &priv->projection); + } + + #undef _DEG_TO_RAD +-- +2.28.0 + + +From 0b345dc3a108f12ebc00e831692b43291c84cd07 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Thu, 30 Apr 2020 21:59:49 +0200 +Subject: [PATCH 06/20] clutter/stage-view: Change set_dirty..() API to + invalidate..() + +The manual "cleaning" of the viewport and projection state is removed, +and we only ever try to invalidate the state so that it'll be updated +next time. Change the API used to reflect this. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1237 +(cherry picked from commit 3080ee672a366a3a52d9f43523c40e3afd08e874) +--- + clutter/clutter/clutter-stage-view-private.h | 6 ++---- + clutter/clutter/clutter-stage-view.c | 10 ++++------ + clutter/clutter/clutter-stage.c | 4 ++-- + 3 files changed, 8 insertions(+), 12 deletions(-) + +diff --git a/clutter/clutter/clutter-stage-view-private.h b/clutter/clutter/clutter-stage-view-private.h +index 78aa37c9f4..e27f140b8a 100644 +--- a/clutter/clutter/clutter-stage-view-private.h ++++ b/clutter/clutter/clutter-stage-view-private.h +@@ -25,8 +25,7 @@ void clutter_stage_view_blit_offscreen (ClutterStageView *view, + + gboolean clutter_stage_view_is_dirty_viewport (ClutterStageView *view); + +-void clutter_stage_view_set_dirty_viewport (ClutterStageView *view, +- gboolean dirty); ++void clutter_stage_view_invalidate_viewport (ClutterStageView *view); + + void clutter_stage_view_set_viewport (ClutterStageView *view, + float x, +@@ -36,8 +35,7 @@ void clutter_stage_view_set_viewport (ClutterStageView *view, + + gboolean clutter_stage_view_is_dirty_projection (ClutterStageView *view); + +-void clutter_stage_view_set_dirty_projection (ClutterStageView *view, +- gboolean dirty); ++void clutter_stage_view_invalidate_projection (ClutterStageView *view); + + void clutter_stage_view_set_projection (ClutterStageView *view, + const CoglMatrix *matrix); +diff --git a/clutter/clutter/clutter-stage-view.c b/clutter/clutter/clutter-stage-view.c +index 4d8bbddc9d..40edfad6e1 100644 +--- a/clutter/clutter/clutter-stage-view.c ++++ b/clutter/clutter/clutter-stage-view.c +@@ -350,13 +350,12 @@ clutter_stage_view_is_dirty_viewport (ClutterStageView *view) + } + + void +-clutter_stage_view_set_dirty_viewport (ClutterStageView *view, +- gboolean dirty) ++clutter_stage_view_invalidate_viewport (ClutterStageView *view) + { + ClutterStageViewPrivate *priv = + clutter_stage_view_get_instance_private (view); + +- priv->dirty_viewport = dirty; ++ priv->dirty_viewport = TRUE; + } + + void +@@ -385,13 +384,12 @@ clutter_stage_view_is_dirty_projection (ClutterStageView *view) + } + + void +-clutter_stage_view_set_dirty_projection (ClutterStageView *view, +- gboolean dirty) ++clutter_stage_view_invalidate_projection (ClutterStageView *view) + { + ClutterStageViewPrivate *priv = + clutter_stage_view_get_instance_private (view); + +- priv->dirty_projection = dirty; ++ priv->dirty_projection = TRUE; + } + + void +diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c +index 4bde234dbf..aaa77d9ede 100644 +--- a/clutter/clutter/clutter-stage.c ++++ b/clutter/clutter/clutter-stage.c +@@ -2636,7 +2636,7 @@ _clutter_stage_dirty_projection (ClutterStage *stage) + { + ClutterStageView *view = l->data; + +- clutter_stage_view_set_dirty_projection (view, TRUE); ++ clutter_stage_view_invalidate_projection (view); + } + } + +@@ -2725,7 +2725,7 @@ _clutter_stage_dirty_viewport (ClutterStage *stage) + { + ClutterStageView *view = l->data; + +- clutter_stage_view_set_dirty_viewport (view, TRUE); ++ clutter_stage_view_invalidate_viewport (view); + } + } + +-- +2.28.0 + + +From 32da7b5c31277c56089e4b3b8ccf43bc552e8974 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Tue, 5 May 2020 17:05:36 +0200 +Subject: [PATCH 07/20] cogl: Make private BLIT_FRAMEBUFFER feature public + +Will be a requirement for enabling shadow buffers. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1237 +(cherry picked from commit b3153760bf81af07f5328ba07b0ff3009bd8305b) +--- + cogl/cogl/cogl-blit.c | 2 +- + cogl/cogl/cogl-context.h | 3 +++ + cogl/cogl/cogl-framebuffer.c | 2 +- + cogl/cogl/cogl-framebuffer.h | 2 +- + cogl/cogl/cogl-private.h | 1 - + cogl/cogl/driver/gl/cogl-framebuffer-gl.c | 4 ++-- + cogl/cogl/driver/gl/gl/cogl-driver-gl.c | 4 ++-- + cogl/cogl/driver/gl/gles/cogl-driver-gles.c | 4 ++-- + 8 files changed, 12 insertions(+), 10 deletions(-) + +diff --git a/cogl/cogl/cogl-blit.c b/cogl/cogl/cogl-blit.c +index ae5a8a345d..dd5fffff37 100644 +--- a/cogl/cogl/cogl-blit.c ++++ b/cogl/cogl/cogl-blit.c +@@ -158,7 +158,7 @@ _cogl_blit_framebuffer_begin (CoglBlitData *data) + supported. */ + if ((_cogl_texture_get_format (data->src_tex) & COGL_PREMULT_BIT) != + (_cogl_texture_get_format (data->dst_tex) & COGL_PREMULT_BIT) || +- !_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_BLIT_FRAMEBUFFER)) ++ !cogl_has_feature (ctx, COGL_FEATURE_ID_BLIT_FRAMEBUFFER)) + return FALSE; + + dst_offscreen = _cogl_offscreen_new_with_texture_full +diff --git a/cogl/cogl/cogl-context.h b/cogl/cogl/cogl-context.h +index d4104625e6..ec90491e94 100644 +--- a/cogl/cogl/cogl-context.h ++++ b/cogl/cogl/cogl-context.h +@@ -227,6 +227,8 @@ cogl_is_context (void *object); + * the depth buffer to a texture. + * @COGL_FEATURE_ID_PRESENTATION_TIME: Whether frame presentation + * time stamps will be recorded in #CoglFrameInfo objects. ++ * @COGL_FEATURE_ID_BLIT_FRAMEBUFFER: Whether blitting using ++ * cogl_blit_framebuffer() is supported. + * + * All the capabilities that can vary between different GPUs supported + * by Cogl. Applications that depend on any of these features should explicitly +@@ -261,6 +263,7 @@ typedef enum _CoglFeatureID + COGL_FEATURE_ID_TEXTURE_RG, + COGL_FEATURE_ID_BUFFER_AGE, + COGL_FEATURE_ID_TEXTURE_EGL_IMAGE_EXTERNAL, ++ COGL_FEATURE_ID_BLIT_FRAMEBUFFER, + + /*< private >*/ + _COGL_N_FEATURE_IDS /*< skip >*/ +diff --git a/cogl/cogl/cogl-framebuffer.c b/cogl/cogl/cogl-framebuffer.c +index d64fc89fb6..fffac3f685 100644 +--- a/cogl/cogl/cogl-framebuffer.c ++++ b/cogl/cogl/cogl-framebuffer.c +@@ -1464,7 +1464,7 @@ cogl_blit_framebuffer (CoglFramebuffer *src, + int src_x1, src_y1, src_x2, src_y2; + int dst_x1, dst_y1, dst_x2, dst_y2; + +- if (!_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_BLIT_FRAMEBUFFER)) ++ if (!cogl_has_feature (ctx, COGL_FEATURE_ID_BLIT_FRAMEBUFFER)) + { + g_set_error_literal (error, COGL_SYSTEM_ERROR, + COGL_SYSTEM_ERROR_UNSUPPORTED, +diff --git a/cogl/cogl/cogl-framebuffer.h b/cogl/cogl/cogl-framebuffer.h +index 38ada9feb7..c347076919 100644 +--- a/cogl/cogl/cogl-framebuffer.h ++++ b/cogl/cogl/cogl-framebuffer.h +@@ -1863,7 +1863,7 @@ cogl_is_framebuffer (void *object); + * + * This blits a region of the color buffer of the source buffer + * to the destination buffer. This function should only be +- * called if the COGL_PRIVATE_FEATURE_BLIT_FRAMEBUFFER feature is ++ * called if the COGL_FEATURE_ID_BLIT_FRAMEBUFFER feature is + * advertised. + * + * The source and destination rectangles are defined in offscreen +diff --git a/cogl/cogl/cogl-private.h b/cogl/cogl/cogl-private.h +index d9fbe68c76..07ac7eb2d8 100644 +--- a/cogl/cogl/cogl-private.h ++++ b/cogl/cogl/cogl-private.h +@@ -42,7 +42,6 @@ typedef enum + { + COGL_PRIVATE_FEATURE_TEXTURE_2D_FROM_EGL_IMAGE, + COGL_PRIVATE_FEATURE_MESA_PACK_INVERT, +- COGL_PRIVATE_FEATURE_BLIT_FRAMEBUFFER, + COGL_PRIVATE_FEATURE_FOUR_CLIP_PLANES, + COGL_PRIVATE_FEATURE_PBOS, + COGL_PRIVATE_FEATURE_VBOS, +diff --git a/cogl/cogl/driver/gl/cogl-framebuffer-gl.c b/cogl/cogl/driver/gl/cogl-framebuffer-gl.c +index 6466fd6bcf..2c0613462f 100644 +--- a/cogl/cogl/driver/gl/cogl-framebuffer-gl.c ++++ b/cogl/cogl/driver/gl/cogl-framebuffer-gl.c +@@ -401,8 +401,8 @@ _cogl_framebuffer_gl_flush_state (CoglFramebuffer *draw_buffer, + { + /* NB: Currently we only take advantage of binding separate + * read/write buffers for framebuffer blit purposes. */ +- _COGL_RETURN_IF_FAIL (_cogl_has_private_feature +- (ctx, COGL_PRIVATE_FEATURE_BLIT_FRAMEBUFFER)); ++ _COGL_RETURN_IF_FAIL (cogl_has_feature ++ (ctx, COGL_FEATURE_ID_BLIT_FRAMEBUFFER)); + + _cogl_framebuffer_gl_bind (draw_buffer, GL_DRAW_FRAMEBUFFER); + _cogl_framebuffer_gl_bind (read_buffer, GL_READ_FRAMEBUFFER); +diff --git a/cogl/cogl/driver/gl/gl/cogl-driver-gl.c b/cogl/cogl/driver/gl/gl/cogl-driver-gl.c +index 716617b54b..f905267c53 100644 +--- a/cogl/cogl/driver/gl/gl/cogl-driver-gl.c ++++ b/cogl/cogl/driver/gl/gl/cogl-driver-gl.c +@@ -466,8 +466,8 @@ _cogl_driver_update_features (CoglContext *ctx, + } + + if (ctx->glBlitFramebuffer) +- COGL_FLAGS_SET (private_features, +- COGL_PRIVATE_FEATURE_BLIT_FRAMEBUFFER, TRUE); ++ COGL_FLAGS_SET (ctx->features, ++ COGL_FEATURE_ID_BLIT_FRAMEBUFFER, TRUE); + + if (ctx->glRenderbufferStorageMultisampleIMG) + { +diff --git a/cogl/cogl/driver/gl/gles/cogl-driver-gles.c b/cogl/cogl/driver/gl/gles/cogl-driver-gles.c +index 902bd0bd3a..e55bb302c4 100644 +--- a/cogl/cogl/driver/gl/gles/cogl-driver-gles.c ++++ b/cogl/cogl/driver/gl/gles/cogl-driver-gles.c +@@ -325,8 +325,8 @@ _cogl_driver_update_features (CoglContext *context, + } + + if (context->glBlitFramebuffer) +- COGL_FLAGS_SET (private_features, +- COGL_PRIVATE_FEATURE_BLIT_FRAMEBUFFER, TRUE); ++ COGL_FLAGS_SET (context->features, ++ COGL_FEATURE_ID_BLIT_FRAMEBUFFER, TRUE); + + if (_cogl_check_extension ("GL_OES_element_index_uint", gl_extensions)) + { +-- +2.28.0 + + +From 32aa92e50a12a5fd9652866937750a3c86c4845f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Tue, 5 May 2020 17:06:35 +0200 +Subject: [PATCH 08/20] renderer/native: Only enable shadowfbs if we can blit + +There is no point in enabling shadow buffers if we can't as that'd be +even slower than not having them at all. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1237 +(cherry picked from commit f191c3b74f572547707fcb6522db76a88689eae2) +--- + src/backends/native/meta-renderer-native.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c +index 463dddd3a7..62ca4bcbd4 100644 +--- a/src/backends/native/meta-renderer-native.c ++++ b/src/backends/native/meta-renderer-native.c +@@ -3649,6 +3649,9 @@ should_force_shadow_fb (MetaRendererNative *renderer_native, + break; + } + ++ if (!cogl_has_feature (cogl_context, COGL_FEATURE_ID_BLIT_FRAMEBUFFER)) ++ return FALSE; ++ + kms_fd = meta_gpu_kms_get_fd (primary_gpu); + if (drmGetCap (kms_fd, DRM_CAP_DUMB_PREFER_SHADOW, &prefer_shadow) == 0) + { +-- +2.28.0 + + +From 5f247503e261f5bbb6baedc40c737c96b8144218 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Tue, 5 May 2020 18:55:03 +0200 +Subject: [PATCH 09/20] clutter/stage-view: Always use cogl_blit_framebuffer() + from shadowfb + +It should only be used when direct blitting is supported, so there is no +reason we should have to deal with pipelines etc when blitting from the +shadow buffer to the onscreen. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1237 +(cherry picked from commit 130f696f303a01d6d666ac967c53b4b5dc372f08) +--- + clutter/clutter/clutter-stage-view.c | 37 +++++++++++----------------- + 1 file changed, 15 insertions(+), 22 deletions(-) + +diff --git a/clutter/clutter/clutter-stage-view.c b/clutter/clutter/clutter-stage-view.c +index 40edfad6e1..e7e33963a6 100644 +--- a/clutter/clutter/clutter-stage-view.c ++++ b/clutter/clutter/clutter-stage-view.c +@@ -53,7 +53,6 @@ typedef struct _ClutterStageViewPrivate + gboolean use_shadowfb; + struct { + CoglOffscreen *framebuffer; +- CoglPipeline *pipeline; + } shadow; + + guint dirty_viewport : 1; +@@ -149,19 +148,6 @@ clutter_stage_view_ensure_offscreen_blit_pipeline (ClutterStageView *view) + view_class->setup_offscreen_blit_pipeline (view, priv->offscreen_pipeline); + } + +-static void +-clutter_stage_view_ensure_shadowfb_blit_pipeline (ClutterStageView *view) +-{ +- ClutterStageViewPrivate *priv = +- clutter_stage_view_get_instance_private (view); +- +- if (priv->shadow.pipeline) +- return; +- +- priv->shadow.pipeline = +- clutter_stage_view_create_framebuffer_pipeline (priv->shadow.framebuffer); +-} +- + void + clutter_stage_view_invalidate_offscreen_blit_pipeline (ClutterStageView *view) + { +@@ -321,13 +307,21 @@ clutter_stage_view_blit_offscreen (ClutterStageView *view, + + if (priv->shadow.framebuffer) + { +- clutter_stage_view_ensure_shadowfb_blit_pipeline (view); +- clutter_stage_view_copy_to_framebuffer (view, +- rect, +- priv->shadow.pipeline, +- priv->shadow.framebuffer, +- priv->framebuffer, +- TRUE); ++ int width, height; ++ g_autoptr (GError) error = NULL; ++ ++ width = cogl_framebuffer_get_width (priv->framebuffer); ++ height = cogl_framebuffer_get_height (priv->framebuffer); ++ if (!cogl_blit_framebuffer (priv->shadow.framebuffer, ++ priv->framebuffer, ++ 0, 0, ++ 0, 0, ++ width, height, ++ &error)) ++ { ++ g_warning ("Failed to blit shadow buffer: %s", error->message); ++ return; ++ } + } + } + +@@ -543,7 +537,6 @@ clutter_stage_view_dispose (GObject *object) + g_clear_pointer (&priv->name, g_free); + g_clear_pointer (&priv->framebuffer, cogl_object_unref); + g_clear_pointer (&priv->shadow.framebuffer, cogl_object_unref); +- g_clear_pointer (&priv->shadow.pipeline, cogl_object_unref); + g_clear_pointer (&priv->offscreen, cogl_object_unref); + g_clear_pointer (&priv->offscreen_pipeline, cogl_object_unref); + +-- +2.28.0 + + +From d20008aa8630c87d8607e64ff77188fc67b3d22a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Tue, 5 May 2020 18:59:32 +0200 +Subject: [PATCH 10/20] clutter/stage-view: Simplify painting of offscreen + slightly + +We will only ever have an "offscreen" if we're painting transformed in +some way, so the 'can_blit' checking is unnecessary. Remove it. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1237 +(cherry picked from commit 32d5e7d3d77c7ba29b8a7da45731aa31bd486056) +--- + clutter/clutter/clutter-stage-view.c | 49 +++++++--------------------- + 1 file changed, 12 insertions(+), 37 deletions(-) + +diff --git a/clutter/clutter/clutter-stage-view.c b/clutter/clutter/clutter-stage-view.c +index e7e33963a6..64fb20cb00 100644 +--- a/clutter/clutter/clutter-stage-view.c ++++ b/clutter/clutter/clutter-stage-view.c +@@ -158,29 +158,13 @@ clutter_stage_view_invalidate_offscreen_blit_pipeline (ClutterStageView *view) + } + + static void +-clutter_stage_view_copy_to_framebuffer (ClutterStageView *view, +- const cairo_rectangle_int_t *rect, +- CoglPipeline *pipeline, +- CoglFramebuffer *src_framebuffer, +- CoglFramebuffer *dst_framebuffer, +- gboolean can_blit) ++paint_transformed_framebuffer (ClutterStageView *view, ++ CoglPipeline *pipeline, ++ CoglFramebuffer *src_framebuffer, ++ CoglFramebuffer *dst_framebuffer) + { + CoglMatrix matrix; + +- /* First, try with blit */ +- if (can_blit) +- { +- if (cogl_blit_framebuffer (src_framebuffer, +- dst_framebuffer, +- 0, 0, +- 0, 0, +- cogl_framebuffer_get_width (dst_framebuffer), +- cogl_framebuffer_get_height (dst_framebuffer), +- NULL)) +- return; +- } +- +- /* If blit fails, fallback to the slower painting method */ + cogl_framebuffer_push_matrix (dst_framebuffer); + + cogl_matrix_init_identity (&matrix); +@@ -278,30 +262,21 @@ clutter_stage_view_blit_offscreen (ClutterStageView *view, + + if (priv->offscreen) + { +- gboolean can_blit; +- CoglMatrix matrix; +- + clutter_stage_view_ensure_offscreen_blit_pipeline (view); +- clutter_stage_view_get_offscreen_transformation_matrix (view, &matrix); +- can_blit = cogl_matrix_is_identity (&matrix); + + if (priv->shadow.framebuffer) + { +- clutter_stage_view_copy_to_framebuffer (view, +- rect, +- priv->offscreen_pipeline, +- priv->offscreen, +- priv->shadow.framebuffer, +- can_blit); ++ paint_transformed_framebuffer (view, ++ priv->offscreen_pipeline, ++ priv->offscreen, ++ priv->shadow.framebuffer); + } + else + { +- clutter_stage_view_copy_to_framebuffer (view, +- rect, +- priv->offscreen_pipeline, +- priv->offscreen, +- priv->framebuffer, +- can_blit); ++ paint_transformed_framebuffer (view, ++ priv->offscreen_pipeline, ++ priv->offscreen, ++ priv->framebuffer); + } + } + +-- +2.28.0 + + +From 8fca65cc3ff989529bf08a47f20b80691f91f95f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Tue, 5 May 2020 19:08:03 +0200 +Subject: [PATCH 11/20] region-utils: Make transform util const correct + +The input should be const, as it will not be altered. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1237 +(cherry picked from commit 761bc64cdd4746389625173454b8861cf211cd79) +--- + src/compositor/region-utils.c | 2 +- + src/compositor/region-utils.h | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/compositor/region-utils.c b/src/compositor/region-utils.c +index 752af85c5c..8edb89322c 100644 +--- a/src/compositor/region-utils.c ++++ b/src/compositor/region-utils.c +@@ -376,7 +376,7 @@ meta_make_border_region (cairo_region_t *region, + } + + cairo_region_t * +-meta_region_transform (cairo_region_t *region, ++meta_region_transform (const cairo_region_t *region, + MetaMonitorTransform transform, + int width, + int height) +diff --git a/src/compositor/region-utils.h b/src/compositor/region-utils.h +index 84e4d83bc2..ca1b8b7b45 100644 +--- a/src/compositor/region-utils.h ++++ b/src/compositor/region-utils.h +@@ -106,7 +106,7 @@ cairo_region_t * meta_make_border_region (cairo_region_t *region, + int y_amount, + gboolean flip); + +-cairo_region_t * meta_region_transform (cairo_region_t *region, ++cairo_region_t * meta_region_transform (const cairo_region_t *region, + MetaMonitorTransform transform, + int width, + int height); +-- +2.28.0 + + +From 58331ff2f10aad87f537e3ebdaa5707c13c9e41b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Tue, 5 May 2020 19:05:36 +0200 +Subject: [PATCH 12/20] clutter/stage-cogl: Use buffer age when view monitor is + rotated + +We failed to use the buffer age when monitors were rotated, as when they +are, we first composite to an offscreen framebuffer, then later again to +the onscreen. The buffer age checking happened on the offscreen, and an +offscreen being single buffered, they can't possible support buffer +ages. + +Instead, move the buffer age check to check the actual onscreen +framebuffer. The offscreen to onscreen painting is still always full +frame, but that will be fixed in a later commit. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1237 +(cherry picked from commit 41c2c2c7d72a0bc8ac1970d35183345424642cf1) +--- + clutter/clutter/clutter-stage-view-private.h | 6 +++ + clutter/clutter/clutter-stage-view.c | 29 ++++++----- + clutter/clutter/clutter-stage-view.h | 11 ++-- + clutter/clutter/cogl/clutter-stage-cogl.c | 54 +++++++------------- + src/backends/meta-renderer-view.c | 22 ++++++++ + 5 files changed, 68 insertions(+), 54 deletions(-) + +diff --git a/clutter/clutter/clutter-stage-view-private.h b/clutter/clutter/clutter-stage-view-private.h +index e27f140b8a..10f9847b70 100644 +--- a/clutter/clutter/clutter-stage-view-private.h ++++ b/clutter/clutter/clutter-stage-view-private.h +@@ -40,4 +40,10 @@ void clutter_stage_view_invalidate_projection (ClutterStageView *view); + void clutter_stage_view_set_projection (ClutterStageView *view, + const CoglMatrix *matrix); + ++void clutter_stage_view_transform_rect_to_onscreen (ClutterStageView *view, ++ const cairo_rectangle_int_t *src_rect, ++ int dst_width, ++ int dst_height, ++ cairo_rectangle_int_t *dst_rect); ++ + #endif /* __CLUTTER_STAGE_VIEW_PRIVATE_H__ */ +diff --git a/clutter/clutter/clutter-stage-view.c b/clutter/clutter/clutter-stage-view.c +index 64fb20cb00..080bfd6669 100644 +--- a/clutter/clutter/clutter-stage-view.c ++++ b/clutter/clutter/clutter-stage-view.c +@@ -157,6 +157,22 @@ clutter_stage_view_invalidate_offscreen_blit_pipeline (ClutterStageView *view) + g_clear_pointer (&priv->offscreen_pipeline, cogl_object_unref); + } + ++void ++clutter_stage_view_transform_rect_to_onscreen (ClutterStageView *view, ++ const cairo_rectangle_int_t *src_rect, ++ int dst_width, ++ int dst_height, ++ cairo_rectangle_int_t *dst_rect) ++{ ++ ClutterStageViewClass *view_class = CLUTTER_STAGE_VIEW_GET_CLASS (view); ++ ++ return view_class->transform_rect_to_onscreen (view, ++ src_rect, ++ dst_width, ++ dst_height, ++ dst_rect); ++} ++ + static void + paint_transformed_framebuffer (ClutterStageView *view, + CoglPipeline *pipeline, +@@ -383,19 +399,6 @@ clutter_stage_view_get_offscreen_transformation_matrix (ClutterStageView *view, + view_class->get_offscreen_transformation_matrix (view, matrix); + } + +-void +-clutter_stage_view_transform_to_onscreen (ClutterStageView *view, +- gfloat *x, +- gfloat *y) +-{ +- gfloat z = 0, w = 1; +- CoglMatrix matrix; +- +- clutter_stage_view_get_offscreen_transformation_matrix (view, &matrix); +- cogl_matrix_get_inverse (&matrix, &matrix); +- cogl_matrix_transform_point (&matrix, x, y, &z, &w); +-} +- + static void + clutter_stage_default_get_offscreen_transformation_matrix (ClutterStageView *view, + CoglMatrix *matrix) +diff --git a/clutter/clutter/clutter-stage-view.h b/clutter/clutter/clutter-stage-view.h +index 26bf10e798..eb0184e9ab 100644 +--- a/clutter/clutter/clutter-stage-view.h ++++ b/clutter/clutter/clutter-stage-view.h +@@ -43,6 +43,12 @@ struct _ClutterStageViewClass + + void (* get_offscreen_transformation_matrix) (ClutterStageView *view, + CoglMatrix *matrix); ++ ++ void (* transform_rect_to_onscreen) (ClutterStageView *view, ++ const cairo_rectangle_int_t *src_rect, ++ int dst_width, ++ int dst_height, ++ cairo_rectangle_int_t *dst_rect); + }; + + CLUTTER_EXPORT +@@ -56,11 +62,6 @@ CoglFramebuffer *clutter_stage_view_get_onscreen (ClutterStageView *view); + CLUTTER_EXPORT + void clutter_stage_view_invalidate_offscreen_blit_pipeline (ClutterStageView *view); + +-CLUTTER_EXPORT +-void clutter_stage_view_transform_to_onscreen (ClutterStageView *view, +- gfloat *x, +- gfloat *y); +- + CLUTTER_EXPORT + float clutter_stage_view_get_scale (ClutterStageView *view); + +diff --git a/clutter/clutter/cogl/clutter-stage-cogl.c b/clutter/clutter/cogl/clutter-stage-cogl.c +index 005c6f6922..821f78ee7c 100644 +--- a/clutter/clutter/cogl/clutter-stage-cogl.c ++++ b/clutter/clutter/cogl/clutter-stage-cogl.c +@@ -509,36 +509,17 @@ static void + transform_swap_region_to_onscreen (ClutterStageView *view, + cairo_rectangle_int_t *swap_region) + { +- CoglFramebuffer *framebuffer; +- cairo_rectangle_int_t layout; +- gfloat x1, y1, x2, y2; +- gint width, height; +- +- framebuffer = clutter_stage_view_get_onscreen (view); +- clutter_stage_view_get_layout (view, &layout); +- +- x1 = (float) swap_region->x / layout.width; +- y1 = (float) swap_region->y / layout.height; +- x2 = (float) (swap_region->x + swap_region->width) / layout.width; +- y2 = (float) (swap_region->y + swap_region->height) / layout.height; +- +- clutter_stage_view_transform_to_onscreen (view, &x1, &y1); +- clutter_stage_view_transform_to_onscreen (view, &x2, &y2); +- +- width = cogl_framebuffer_get_width (framebuffer); +- height = cogl_framebuffer_get_height (framebuffer); +- +- x1 = floor (x1 * width); +- y1 = floor (height - (y1 * height)); +- x2 = ceil (x2 * width); +- y2 = ceil (height - (y2 * height)); +- +- *swap_region = (cairo_rectangle_int_t) { +- .x = x1, +- .y = y1, +- .width = x2 - x1, +- .height = y2 - y1 +- }; ++ CoglFramebuffer *onscreen = clutter_stage_view_get_onscreen (view); ++ int width, height; ++ ++ width = cogl_framebuffer_get_width (onscreen); ++ height = cogl_framebuffer_get_height (onscreen); ++ ++ clutter_stage_view_transform_rect_to_onscreen (view, ++ swap_region, ++ width, ++ height, ++ swap_region); + } + + static void +@@ -593,6 +574,7 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window, + ClutterStageViewCoglPrivate *view_priv = + clutter_stage_view_cogl_get_instance_private (view_cogl); + CoglFramebuffer *fb = clutter_stage_view_get_framebuffer (view); ++ CoglFramebuffer *onscreen = clutter_stage_view_get_onscreen (view); + cairo_rectangle_int_t view_rect; + gboolean have_clip; + gboolean may_use_clipped_redraw; +@@ -618,10 +600,10 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window, + fb_height = cogl_framebuffer_get_height (fb); + + can_blit_sub_buffer = +- cogl_is_onscreen (fb) && ++ cogl_is_onscreen (onscreen) && + cogl_clutter_winsys_has_feature (COGL_WINSYS_FEATURE_SWAP_REGION); + +- has_buffer_age = cogl_is_onscreen (fb) && is_buffer_age_enabled (); ++ has_buffer_age = cogl_is_onscreen (onscreen) && is_buffer_age_enabled (); + + /* NB: a zero width redraw clip == full stage redraw */ + if (stage_cogl->bounding_redraw_clip.width == 0) +@@ -645,7 +627,7 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window, + have_clip && + /* some drivers struggle to get going and produce some junk + * frames when starting up... */ +- cogl_onscreen_get_frame_counter (COGL_ONSCREEN (fb)) > 3) ++ cogl_onscreen_get_frame_counter (COGL_ONSCREEN (onscreen)) > 3) + { + ClutterRect rect; + +@@ -686,7 +668,7 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window, + cairo_rectangle_int_t *current_fb_damage = + &view_priv->damage_history[DAMAGE_HISTORY (view_priv->damage_index++)]; + +- age = cogl_onscreen_get_buffer_age (COGL_ONSCREEN (fb)); ++ age = cogl_onscreen_get_buffer_age (COGL_ONSCREEN (onscreen)); + + if (valid_buffer_age (view_cogl, age)) + { +@@ -961,9 +943,9 @@ clutter_stage_cogl_get_dirty_pixel (ClutterStageWindow *stage_window, + int *x, + int *y) + { +- CoglFramebuffer *framebuffer = clutter_stage_view_get_framebuffer (view); ++ CoglFramebuffer *onscreen = clutter_stage_view_get_onscreen (view); + gboolean has_buffer_age = +- cogl_is_onscreen (framebuffer) && ++ cogl_is_onscreen (onscreen) && + is_buffer_age_enabled (); + float fb_scale; + gboolean scale_is_fractional; +diff --git a/src/backends/meta-renderer-view.c b/src/backends/meta-renderer-view.c +index cab1f5f483..4e45f2ef02 100644 +--- a/src/backends/meta-renderer-view.c ++++ b/src/backends/meta-renderer-view.c +@@ -34,6 +34,7 @@ + + #include "backends/meta-renderer.h" + #include "clutter/clutter-mutter.h" ++#include "compositor/region-utils.h" + + enum + { +@@ -125,6 +126,25 @@ meta_renderer_view_setup_offscreen_blit_pipeline (ClutterStageView *view, + cogl_pipeline_set_layer_matrix (pipeline, 0, &matrix); + } + ++static void ++meta_renderer_view_transform_rect_to_onscreen (ClutterStageView *view, ++ const cairo_rectangle_int_t *src_rect, ++ int dst_width, ++ int dst_height, ++ cairo_rectangle_int_t *dst_rect) ++{ ++ MetaRendererView *renderer_view = META_RENDERER_VIEW (view); ++ MetaMonitorTransform inverted_transform; ++ ++ inverted_transform = ++ meta_monitor_transform_invert (renderer_view->transform); ++ return meta_rectangle_transform (src_rect, ++ inverted_transform, ++ dst_width, ++ dst_height, ++ dst_rect); ++} ++ + static void + meta_renderer_view_set_transform (MetaRendererView *view, + MetaMonitorTransform transform) +@@ -195,6 +215,8 @@ meta_renderer_view_class_init (MetaRendererViewClass *klass) + meta_renderer_view_setup_offscreen_blit_pipeline; + view_class->get_offscreen_transformation_matrix = + meta_renderer_view_get_offscreen_transformation_matrix; ++ view_class->transform_rect_to_onscreen = ++ meta_renderer_view_transform_rect_to_onscreen; + + object_class->get_property = meta_renderer_view_get_property; + object_class->set_property = meta_renderer_view_set_property; +-- +2.28.0 + + +From 6fc1da9dd3ac2753771bb68adb780d1d55494cba Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Tue, 5 May 2020 19:22:10 +0200 +Subject: [PATCH 13/20] clutter/stage-view: Only paint redraw clip from + offscreen + +The rest didn't change, so only actually paint the part of the offscreen +that was composited as part of the stage painting. In practice, this +means that, unless a shadow buffer is used, we now only paint the +damaged part of the stage, and copy the damage part of the offscreen to +the onscreen. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1237 +(cherry picked from commit acf6b79e3a5b9d8d285886c471961e8c0bec48ce) +--- + clutter/clutter/clutter-stage-view.c | 85 ++++++++++++++++++++++++---- + 1 file changed, 73 insertions(+), 12 deletions(-) + +diff --git a/clutter/clutter/clutter-stage-view.c b/clutter/clutter/clutter-stage-view.c +index 080bfd6669..b686272db0 100644 +--- a/clutter/clutter/clutter-stage-view.c ++++ b/clutter/clutter/clutter-stage-view.c +@@ -19,6 +19,7 @@ + + #include "clutter/clutter-stage-view.h" + #include "clutter/clutter-stage-view-private.h" ++#include "clutter/clutter-private.h" + + #include + #include +@@ -174,23 +175,81 @@ clutter_stage_view_transform_rect_to_onscreen (ClutterStageView *view + } + + static void +-paint_transformed_framebuffer (ClutterStageView *view, +- CoglPipeline *pipeline, +- CoglFramebuffer *src_framebuffer, +- CoglFramebuffer *dst_framebuffer) ++paint_transformed_framebuffer (ClutterStageView *view, ++ CoglPipeline *pipeline, ++ CoglFramebuffer *src_framebuffer, ++ CoglFramebuffer *dst_framebuffer, ++ const cairo_rectangle_int_t *redraw_clip) + { + CoglMatrix matrix; ++ int dst_width, dst_height; ++ cairo_rectangle_int_t view_layout; ++ cairo_rectangle_int_t onscreen_layout; ++ float view_scale; ++ float *coordinates; ++ cairo_rectangle_int_t src_rect; ++ cairo_rectangle_int_t dst_rect; ++ ++ dst_width = cogl_framebuffer_get_width (dst_framebuffer); ++ dst_height = cogl_framebuffer_get_height (dst_framebuffer); ++ clutter_stage_view_get_layout (view, &view_layout); ++ clutter_stage_view_transform_rect_to_onscreen (view, ++ &(cairo_rectangle_int_t) { ++ .width = view_layout.width, ++ .height = view_layout.height, ++ }, ++ view_layout.width, ++ view_layout.height, ++ &onscreen_layout); ++ view_scale = clutter_stage_view_get_scale (view); + + cogl_framebuffer_push_matrix (dst_framebuffer); + + cogl_matrix_init_identity (&matrix); +- cogl_matrix_translate (&matrix, -1, 1, 0); +- cogl_matrix_scale (&matrix, 2, -2, 0); ++ cogl_matrix_scale (&matrix, ++ 1.0 / (dst_width / 2.0), ++ -1.0 / (dst_height / 2.0), 0); ++ cogl_matrix_translate (&matrix, ++ -(dst_width / 2.0), ++ -(dst_height / 2.0), 0); + cogl_framebuffer_set_projection_matrix (dst_framebuffer, &matrix); +- +- cogl_framebuffer_draw_rectangle (dst_framebuffer, +- pipeline, +- 0, 0, 1, 1); ++ cogl_framebuffer_set_viewport (dst_framebuffer, ++ 0, 0, dst_width, dst_height); ++ ++ coordinates = g_newa (float, 2 * 4); ++ ++ src_rect = *redraw_clip; ++ _clutter_util_rectangle_offset (&src_rect, ++ -view_layout.x, ++ -view_layout.y, ++ &src_rect); ++ ++ clutter_stage_view_transform_rect_to_onscreen (view, ++ &src_rect, ++ onscreen_layout.width, ++ onscreen_layout.height, ++ &dst_rect); ++ ++ coordinates[0] = (float) dst_rect.x * view_scale; ++ coordinates[1] = (float) dst_rect.y * view_scale; ++ coordinates[2] = ((float) (dst_rect.x + dst_rect.width) * ++ view_scale); ++ coordinates[3] = ((float) (dst_rect.y + dst_rect.height) * ++ view_scale); ++ ++ coordinates[4] = (((float) dst_rect.x / (float) dst_width) * ++ view_scale); ++ coordinates[5] = (((float) dst_rect.y / (float) dst_height) * ++ view_scale); ++ coordinates[6] = ((float) (dst_rect.x + dst_rect.width) / ++ (float) dst_width) * view_scale; ++ coordinates[7] = ((float) (dst_rect.y + dst_rect.height) / ++ (float) dst_height) * view_scale; ++ ++ cogl_framebuffer_draw_textured_rectangles (dst_framebuffer, ++ pipeline, ++ coordinates, ++ 1); + + cogl_framebuffer_pop_matrix (dst_framebuffer); + } +@@ -285,14 +344,16 @@ clutter_stage_view_blit_offscreen (ClutterStageView *view, + paint_transformed_framebuffer (view, + priv->offscreen_pipeline, + priv->offscreen, +- priv->shadow.framebuffer); ++ priv->shadow.framebuffer, ++ rect); + } + else + { + paint_transformed_framebuffer (view, + priv->offscreen_pipeline, + priv->offscreen, +- priv->framebuffer); ++ priv->framebuffer, ++ rect); + } + } + +-- +2.28.0 + + +From ff3164440e6bbb3e845a1d4a23843a5792afc16f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Wed, 13 May 2020 17:18:50 +0200 +Subject: [PATCH 14/20] clutter/stage-cogl: Only construct damage array if + it'll be used + +It's only used when we actually swap buffers, which we only do if the +target framebuffer is an onscreen. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1237 +(cherry picked from commit 95a80c442b6300ce5b41b4b3975a372f1eabd166) +--- + clutter/clutter/cogl/clutter-stage-cogl.c | 22 +++++++++++----------- + 1 file changed, 11 insertions(+), 11 deletions(-) + +diff --git a/clutter/clutter/cogl/clutter-stage-cogl.c b/clutter/clutter/cogl/clutter-stage-cogl.c +index 821f78ee7c..fc6d0d031d 100644 +--- a/clutter/clutter/cogl/clutter-stage-cogl.c ++++ b/clutter/clutter/cogl/clutter-stage-cogl.c +@@ -413,17 +413,6 @@ swap_framebuffer (ClutterStageWindow *stage_window, + gboolean swap_with_damage) + { + CoglFramebuffer *framebuffer = clutter_stage_view_get_onscreen (view); +- int damage[4], ndamage; +- +- damage[0] = swap_region->x; +- damage[1] = swap_region->y; +- damage[2] = swap_region->width; +- damage[3] = swap_region->height; +- +- if (swap_region->width != 0) +- ndamage = 1; +- else +- ndamage = 0; + + if (G_UNLIKELY ((clutter_paint_debug_flags & CLUTTER_DEBUG_PAINT_DAMAGE_REGION))) + paint_damage_region (stage_window, view, swap_region); +@@ -431,6 +420,17 @@ swap_framebuffer (ClutterStageWindow *stage_window, + if (cogl_is_onscreen (framebuffer)) + { + CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer); ++ int damage[4], ndamage; ++ ++ damage[0] = swap_region->x; ++ damage[1] = swap_region->y; ++ damage[2] = swap_region->width; ++ damage[3] = swap_region->height; ++ ++ if (swap_region->width != 0) ++ ndamage = 1; ++ else ++ ndamage = 0; + + /* push on the screen */ + if (ndamage == 1 && !swap_with_damage) +-- +2.28.0 + + +From f946746f5938e7d6c48b688827fb991f22dc1364 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Tue, 5 May 2020 19:25:23 +0200 +Subject: [PATCH 15/20] clutter/stage-view: Only blit the damage part of the + shadow buffer + +This fixes the last "copy everything" paths when clutter doesn't +directly paint onto the onscreen framebuffer. It adds a new hook into +the stage view called before the swap buffer, as at this point, we have +the swap buffer damag regions ready, which corresponds to the regions we +must blit according to the damage reported to clutter. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1237 +(cherry picked from commit 851e7727ec6f3719139ab562ac2524cdc1bd64ae) +--- + clutter/clutter/clutter-stage-view-private.h | 3 +++ + clutter/clutter/clutter-stage-view.c | 25 ++++++++++++++++++-- + clutter/clutter/cogl/clutter-stage-cogl.c | 2 ++ + 3 files changed, 28 insertions(+), 2 deletions(-) + +diff --git a/clutter/clutter/clutter-stage-view-private.h b/clutter/clutter/clutter-stage-view-private.h +index 10f9847b70..bddc38ded6 100644 +--- a/clutter/clutter/clutter-stage-view-private.h ++++ b/clutter/clutter/clutter-stage-view-private.h +@@ -23,6 +23,9 @@ + void clutter_stage_view_blit_offscreen (ClutterStageView *view, + const cairo_rectangle_int_t *clip); + ++void clutter_stage_view_before_swap_buffer (ClutterStageView *view, ++ const cairo_rectangle_int_t *swap_region); ++ + gboolean clutter_stage_view_is_dirty_viewport (ClutterStageView *view); + + void clutter_stage_view_invalidate_viewport (ClutterStageView *view); +diff --git a/clutter/clutter/clutter-stage-view.c b/clutter/clutter/clutter-stage-view.c +index b686272db0..21ab02c97b 100644 +--- a/clutter/clutter/clutter-stage-view.c ++++ b/clutter/clutter/clutter-stage-view.c +@@ -356,11 +356,22 @@ clutter_stage_view_blit_offscreen (ClutterStageView *view, + rect); + } + } ++} ++ ++void ++clutter_stage_view_before_swap_buffer (ClutterStageView *view, ++ const cairo_rectangle_int_t *swap_region) ++{ ++ ClutterStageViewPrivate *priv = ++ clutter_stage_view_get_instance_private (view); ++ g_autoptr (GError) error = NULL; + +- if (priv->shadow.framebuffer) ++ if (!priv->shadow.framebuffer) ++ return; ++ ++ if (swap_region->width == 0 || swap_region->height == 0) + { + int width, height; +- g_autoptr (GError) error = NULL; + + width = cogl_framebuffer_get_width (priv->framebuffer); + height = cogl_framebuffer_get_height (priv->framebuffer); +@@ -370,6 +381,16 @@ clutter_stage_view_blit_offscreen (ClutterStageView *view, + 0, 0, + width, height, + &error)) ++ g_warning ("Failed to blit shadow buffer: %s", error->message); ++ } ++ else ++ { ++ if (!cogl_blit_framebuffer (priv->shadow.framebuffer, ++ priv->framebuffer, ++ swap_region->x, swap_region->y, ++ swap_region->x, swap_region->y, ++ swap_region->width, swap_region->height, ++ &error)) + { + g_warning ("Failed to blit shadow buffer: %s", error->message); + return; +diff --git a/clutter/clutter/cogl/clutter-stage-cogl.c b/clutter/clutter/cogl/clutter-stage-cogl.c +index fc6d0d031d..884819ebd3 100644 +--- a/clutter/clutter/cogl/clutter-stage-cogl.c ++++ b/clutter/clutter/cogl/clutter-stage-cogl.c +@@ -417,6 +417,8 @@ swap_framebuffer (ClutterStageWindow *stage_window, + if (G_UNLIKELY ((clutter_paint_debug_flags & CLUTTER_DEBUG_PAINT_DAMAGE_REGION))) + paint_damage_region (stage_window, view, swap_region); + ++ clutter_stage_view_before_swap_buffer (view, swap_region); ++ + if (cogl_is_onscreen (framebuffer)) + { + CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer); +-- +2.28.0 + + +From 757dd09dc9b76a7654f087679db1c7f005b7653c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Wed, 6 May 2020 09:11:34 +0200 +Subject: [PATCH 16/20] clutter/stage-cogl: Extract damage history logic + +Move the damage history tracking to a new ClutterDamageHistory helper +type. The aim is to be able to track damage history elsewhere without +reimplementing the data structure and tracking logic. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1237 +(cherry picked from commit 09271bcfef8889022f15a3b2949843e55f3df9da) +--- + clutter/clutter/clutter-damage-history.c | 89 +++++++++++++++++ + clutter/clutter/clutter-damage-history.h | 42 ++++++++ + clutter/clutter/cogl/clutter-stage-cogl.c | 116 ++++++++++++---------- + clutter/clutter/meson.build | 2 + + 4 files changed, 195 insertions(+), 54 deletions(-) + create mode 100644 clutter/clutter/clutter-damage-history.c + create mode 100644 clutter/clutter/clutter-damage-history.h + +diff --git a/clutter/clutter/clutter-damage-history.c b/clutter/clutter/clutter-damage-history.c +new file mode 100644 +index 0000000000..78ab0f7b5e +--- /dev/null ++++ b/clutter/clutter/clutter-damage-history.c +@@ -0,0 +1,89 @@ ++/* ++ * Copyright (C) 2007,2008,2009,2010,2011 Intel Corporation. ++ * Copyright (C) 2020 Red Hat Inc ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library. If not, see . ++ */ ++ ++#include "clutter-build-config.h" ++ ++#include "clutter-damage-history.h" ++ ++#define DAMAGE_HISTORY_LENGTH 0x10 ++ ++struct _ClutterDamageHistory ++{ ++ cairo_rectangle_int_t damages[DAMAGE_HISTORY_LENGTH]; ++ int index; ++}; ++ ++ClutterDamageHistory * ++clutter_damage_history_new (void) ++{ ++ ClutterDamageHistory *history; ++ ++ history = g_new0 (ClutterDamageHistory, 1); ++ ++ return history; ++} ++ ++void ++clutter_damage_history_free (ClutterDamageHistory *history) ++{ ++ g_free (history); ++} ++ ++gboolean ++clutter_damage_history_is_age_valid (ClutterDamageHistory *history, ++ int age) ++{ ++ const cairo_rectangle_int_t *damage; ++ ++ if (age >= DAMAGE_HISTORY_LENGTH || ++ age < 1) ++ return FALSE; ++ ++ damage = clutter_damage_history_lookup (history, age); ++ if (damage->width == 0 || damage->height == 0) ++ return FALSE; ++ ++ return TRUE; ++} ++ ++void ++clutter_damage_history_record (ClutterDamageHistory *history, ++ const cairo_rectangle_int_t *damage) ++{ ++ history->damages[history->index] = *damage; ++} ++ ++static inline int ++step_damage_index (int current, ++ int diff) ++{ ++ return (current + diff) & (DAMAGE_HISTORY_LENGTH - 1); ++} ++ ++void ++clutter_damage_history_step (ClutterDamageHistory *history) ++{ ++ history->index = step_damage_index (history->index, 1); ++} ++ ++const cairo_rectangle_int_t * ++clutter_damage_history_lookup (ClutterDamageHistory *history, ++ int age) ++{ ++ return &history->damages[step_damage_index (history->index, -age)]; ++} +diff --git a/clutter/clutter/clutter-damage-history.h b/clutter/clutter/clutter-damage-history.h +new file mode 100644 +index 0000000000..6c483acab7 +--- /dev/null ++++ b/clutter/clutter/clutter-damage-history.h +@@ -0,0 +1,42 @@ ++/* ++ * Copyright (C) 2007,2008,2009,2010,2011 Intel Corporation. ++ * Copyright (C) 2020 Red Hat Inc ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library. If not, see . ++ */ ++ ++#ifndef CLUTTER_DAMAGE_HISTORY_H ++#define CLUTTER_DAMAGE_HISTORY_H ++ ++#include ++#include ++ ++typedef struct _ClutterDamageHistory ClutterDamageHistory; ++ ++ClutterDamageHistory * clutter_damage_history_new (void); ++ ++void clutter_damage_history_free (ClutterDamageHistory *history); ++ ++gboolean clutter_damage_history_is_age_valid (ClutterDamageHistory *history, ++ int age); ++ ++void clutter_damage_history_record (ClutterDamageHistory *history, ++ const cairo_rectangle_int_t *damage); ++ ++void clutter_damage_history_step (ClutterDamageHistory *history); ++ ++const cairo_rectangle_int_t * clutter_damage_history_lookup (ClutterDamageHistory *history, ++ int age); ++ ++#endif /* CLUTTER_DAMAGE_HISTORY_H */ +diff --git a/clutter/clutter/cogl/clutter-stage-cogl.c b/clutter/clutter/cogl/clutter-stage-cogl.c +index 884819ebd3..11273ec894 100644 +--- a/clutter/clutter/cogl/clutter-stage-cogl.c ++++ b/clutter/clutter/cogl/clutter-stage-cogl.c +@@ -38,6 +38,7 @@ + + #include "clutter-actor-private.h" + #include "clutter-backend-private.h" ++#include "clutter-damage-history.h" + #include "clutter-debug.h" + #include "clutter-event.h" + #include "clutter-enum-types.h" +@@ -49,13 +50,9 @@ + + typedef struct _ClutterStageViewCoglPrivate + { +- /* +- * List of previous damaged areas in stage view framebuffer coordinate space. ++ /* Damage history, in stage view render target framebuffer coordinate space. + */ +-#define DAMAGE_HISTORY_MAX 16 +-#define DAMAGE_HISTORY(x) ((x) & (DAMAGE_HISTORY_MAX - 1)) +- cairo_rectangle_int_t damage_history[DAMAGE_HISTORY_MAX]; +- unsigned int damage_index; ++ ClutterDamageHistory *damage_history; + } ClutterStageViewCoglPrivate; + + G_DEFINE_TYPE_WITH_PRIVATE (ClutterStageViewCogl, clutter_stage_view_cogl, +@@ -348,10 +345,7 @@ valid_buffer_age (ClutterStageViewCogl *view_cogl, + ClutterStageViewCoglPrivate *view_priv = + clutter_stage_view_cogl_get_instance_private (view_cogl); + +- if (age <= 0) +- return FALSE; +- +- return age < MIN (view_priv->damage_index, DAMAGE_HISTORY_MAX); ++ return clutter_damage_history_is_age_valid (view_priv->damage_history, age); + } + + static void +@@ -483,30 +477,6 @@ paint_stage (ClutterStageCogl *stage_cogl, + clutter_stage_view_blit_offscreen (view, clip); + } + +-static void +-fill_current_damage_history_and_step (ClutterStageView *view) +-{ +- ClutterStageViewCogl *view_cogl = CLUTTER_STAGE_VIEW_COGL (view); +- ClutterStageViewCoglPrivate *view_priv = +- clutter_stage_view_cogl_get_instance_private (view_cogl); +- cairo_rectangle_int_t view_rect; +- float fb_scale; +- cairo_rectangle_int_t *current_fb_damage; +- +- current_fb_damage = +- &view_priv->damage_history[DAMAGE_HISTORY (view_priv->damage_index)]; +- clutter_stage_view_get_layout (view, &view_rect); +- fb_scale = clutter_stage_view_get_scale (view); +- +- *current_fb_damage = (cairo_rectangle_int_t) { +- .x = 0, +- .y = 0, +- .width = view_rect.width * fb_scale, +- .height = view_rect.height * fb_scale +- }; +- view_priv->damage_index++; +-} +- + static void + transform_swap_region_to_onscreen (ClutterStageView *view, + cairo_rectangle_int_t *swap_region) +@@ -567,6 +537,24 @@ scale_and_clamp_rect (const ClutterRect *rect, + _clutter_util_rectangle_int_extents (&tmp, dest); + } + ++static void ++record_full_damage (ClutterStageView *view) ++{ ++ ClutterStageViewCogl *view_cogl = CLUTTER_STAGE_VIEW_COGL (view); ++ ClutterStageViewCoglPrivate *view_priv = ++ clutter_stage_view_cogl_get_instance_private (view_cogl); ++ CoglFramebuffer *fb = clutter_stage_view_get_framebuffer (view); ++ int fb_width, fb_height; ++ ++ fb_width = cogl_framebuffer_get_width (fb); ++ fb_height = cogl_framebuffer_get_height (fb); ++ clutter_damage_history_record (view_priv->damage_history, ++ &(cairo_rectangle_int_t) { ++ .width = fb_width, ++ .height = fb_height ++ }); ++} ++ + static gboolean + clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window, + ClutterStageView *view) +@@ -666,9 +654,7 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window, + { + if (use_clipped_redraw && !clip_region_empty) + { +- int age, i; +- cairo_rectangle_int_t *current_fb_damage = +- &view_priv->damage_history[DAMAGE_HISTORY (view_priv->damage_index++)]; ++ int age; + + age = cogl_onscreen_get_buffer_age (COGL_ONSCREEN (onscreen)); + +@@ -676,16 +662,20 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window, + { + ClutterRect rect; + cairo_rectangle_int_t damage_region; ++ int i; + +- *current_fb_damage = fb_clip_region; ++ clutter_damage_history_record (view_priv->damage_history, ++ &fb_clip_region); + + for (i = 1; i <= age; i++) + { +- cairo_rectangle_int_t *fb_damage = +- &view_priv->damage_history[DAMAGE_HISTORY (view_priv->damage_index - i - 1)]; ++ const cairo_rectangle_int_t *old_damage; ++ ++ old_damage = ++ clutter_damage_history_lookup (view_priv->damage_history, i); + + _clutter_util_rectangle_union (&fb_clip_region, +- fb_damage, ++ old_damage, + &fb_clip_region); + } + +@@ -713,18 +703,15 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window, + { + CLUTTER_NOTE (CLIPPING, "Invalid back buffer(age=%d): forcing full redraw\n", age); + use_clipped_redraw = FALSE; +- *current_fb_damage = (cairo_rectangle_int_t) { +- .x = 0, +- .y = 0, +- .width = view_rect.width * fb_scale, +- .height = view_rect.height * fb_scale +- }; ++ record_full_damage (view); + } + } + else if (!use_clipped_redraw) + { +- fill_current_damage_history_and_step (view); ++ record_full_damage (view); + } ++ ++ clutter_damage_history_step (view_priv->damage_history); + } + + cogl_push_framebuffer (fb); +@@ -946,6 +933,9 @@ clutter_stage_cogl_get_dirty_pixel (ClutterStageWindow *stage_window, + int *y) + { + CoglFramebuffer *onscreen = clutter_stage_view_get_onscreen (view); ++ ClutterStageViewCogl *view_cogl = CLUTTER_STAGE_VIEW_COGL (view); ++ ClutterStageViewCoglPrivate *view_priv = ++ clutter_stage_view_cogl_get_instance_private (view_cogl); + gboolean has_buffer_age = + cogl_is_onscreen (onscreen) && + is_buffer_age_enabled (); +@@ -967,22 +957,21 @@ clutter_stage_cogl_get_dirty_pixel (ClutterStageWindow *stage_window, + * For now, always use the (0, 0) pixel for picking when using fractional + * framebuffer scaling. + */ +- if (!has_buffer_age || scale_is_fractional) ++ if (!has_buffer_age || ++ scale_is_fractional || ++ !clutter_damage_history_is_age_valid (view_priv->damage_history, 0)) + { + *x = 0; + *y = 0; + } + else + { +- ClutterStageViewCogl *view_cogl = CLUTTER_STAGE_VIEW_COGL (view); +- ClutterStageViewCoglPrivate *view_priv = +- clutter_stage_view_cogl_get_instance_private (view_cogl); + cairo_rectangle_int_t view_layout; +- cairo_rectangle_int_t *fb_damage; ++ const cairo_rectangle_int_t *fb_damage; + + clutter_stage_view_get_layout (view, &view_layout); + +- fb_damage = &view_priv->damage_history[DAMAGE_HISTORY (view_priv->damage_index - 1)]; ++ fb_damage = clutter_damage_history_lookup (view_priv->damage_history, 0); + *x = fb_damage->x / fb_scale; + *y = fb_damage->y / fb_scale; + } +@@ -1052,12 +1041,31 @@ _clutter_stage_cogl_init (ClutterStageCogl *stage) + stage->update_time = -1; + } + ++static void ++clutter_stage_view_cogl_finalize (GObject *object) ++{ ++ ClutterStageViewCogl *view_cogl = CLUTTER_STAGE_VIEW_COGL (object); ++ ClutterStageViewCoglPrivate *view_priv = ++ clutter_stage_view_cogl_get_instance_private (view_cogl); ++ ++ clutter_damage_history_free (view_priv->damage_history); ++ ++ G_OBJECT_CLASS (clutter_stage_view_cogl_parent_class)->finalize (object); ++} ++ + static void + clutter_stage_view_cogl_init (ClutterStageViewCogl *view_cogl) + { ++ ClutterStageViewCoglPrivate *view_priv = ++ clutter_stage_view_cogl_get_instance_private (view_cogl); ++ ++ view_priv->damage_history = clutter_damage_history_new (); + } + + static void + clutter_stage_view_cogl_class_init (ClutterStageViewCoglClass *klass) + { ++ GObjectClass *object_class = G_OBJECT_CLASS (klass); ++ ++ object_class->finalize = clutter_stage_view_cogl_finalize; + } +diff --git a/clutter/clutter/meson.build b/clutter/clutter/meson.build +index 8e0484453d..c9eab96d29 100644 +--- a/clutter/clutter/meson.build ++++ b/clutter/clutter/meson.build +@@ -116,6 +116,7 @@ clutter_sources = [ + 'clutter-constraint.c', + 'clutter-container.c', + 'clutter-content.c', ++ 'clutter-damage-history.c', + 'clutter-deform-effect.c', + 'clutter-desaturate-effect.c', + 'clutter-device-manager.c', +@@ -186,6 +187,7 @@ clutter_private_headers = [ + 'clutter-bezier.h', + 'clutter-constraint-private.h', + 'clutter-content-private.h', ++ 'clutter-damage-history.h', + 'clutter-debug.h', + 'clutter-device-manager-private.h', + 'clutter-easing.h', +-- +2.28.0 + + +From 5da1c8083784a351a7763a0c9a9ce4c8359522a4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Wed, 6 May 2020 21:40:40 +0200 +Subject: [PATCH 17/20] cogl/dma-buf: Add API to synchronize reading + +Used before and after accessing DMA buffer content using mmap(). + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1237 +(cherry picked from commit 2d972fc761b9e39f78e66dd84eab57309cdc8658) +--- + cogl/cogl/cogl-dma-buf-handle.c | 51 +++++++++++++++++++++++++++++++++ + cogl/cogl/cogl-dma-buf-handle.h | 8 ++++++ + cogl/meson.build | 1 + + 3 files changed, 60 insertions(+) + +diff --git a/cogl/cogl/cogl-dma-buf-handle.c b/cogl/cogl/cogl-dma-buf-handle.c +index d8b4e57c55..7e86e2267b 100644 +--- a/cogl/cogl/cogl-dma-buf-handle.c ++++ b/cogl/cogl/cogl-dma-buf-handle.c +@@ -34,6 +34,10 @@ + #include "cogl-dma-buf-handle.h" + #include "cogl-object.h" + ++#include ++#include ++#include ++#include + #include + + struct _CoglDmaBufHandle +@@ -96,6 +100,53 @@ cogl_dma_buf_handle_free (CoglDmaBufHandle *dmabuf_handle) + g_free (dmabuf_handle); + } + ++static gboolean ++sync_read (CoglDmaBufHandle *dmabuf_handle, ++ uint64_t start_or_end, ++ GError **error) ++{ ++ struct dma_buf_sync sync = { 0 }; ++ ++ sync.flags = start_or_end | DMA_BUF_SYNC_READ; ++ ++ while (TRUE) ++ { ++ int ret; ++ ++ ret = ioctl (dmabuf_handle->dmabuf_fd, DMA_BUF_IOCTL_SYNC, &sync); ++ if (ret == -1 && errno == EINTR) ++ { ++ continue; ++ } ++ else if (ret == -1) ++ { ++ g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), ++ "ioctl: %s", g_strerror (errno)); ++ return FALSE; ++ } ++ else ++ { ++ break; ++ } ++ } ++ ++ return TRUE; ++} ++ ++gboolean ++cogl_dma_buf_handle_sync_read_start (CoglDmaBufHandle *dmabuf_handle, ++ GError **error) ++{ ++ return sync_read (dmabuf_handle, DMA_BUF_SYNC_START, error); ++} ++ ++gboolean ++cogl_dma_buf_handle_sync_read_end (CoglDmaBufHandle *dmabuf_handle, ++ GError **error) ++{ ++ return sync_read (dmabuf_handle, DMA_BUF_SYNC_END, error); ++} ++ + CoglFramebuffer * + cogl_dma_buf_handle_get_framebuffer (CoglDmaBufHandle *dmabuf_handle) + { +diff --git a/cogl/cogl/cogl-dma-buf-handle.h b/cogl/cogl/cogl-dma-buf-handle.h +index f64a20678d..63c5bab7b7 100644 +--- a/cogl/cogl/cogl-dma-buf-handle.h ++++ b/cogl/cogl/cogl-dma-buf-handle.h +@@ -63,6 +63,14 @@ cogl_dma_buf_handle_new (CoglFramebuffer *framebuffer, + void + cogl_dma_buf_handle_free (CoglDmaBufHandle *dmabuf_handle); + ++gboolean ++cogl_dma_buf_handle_sync_read_start (CoglDmaBufHandle *dmabuf_handle, ++ GError **error); ++ ++gboolean ++cogl_dma_buf_handle_sync_read_end (CoglDmaBufHandle *dmabuf_handle, ++ GError **error); ++ + /** + * cogl_dma_buf_handle_get_framebuffer: (skip) + * +diff --git a/cogl/meson.build b/cogl/meson.build +index 356d596f56..47e6a3e0da 100644 +--- a/cogl/meson.build ++++ b/cogl/meson.build +@@ -23,6 +23,7 @@ cogl_mutter_config_h = configure_file( + + cogl_pkg_deps = [ + glib_dep, ++ gio_dep, + gobject_dep, + ] + +-- +2.28.0 + + +From 360a397c19046c6a914ee27e3e5104da3ad0c1c6 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Wed, 6 May 2020 22:12:46 +0200 +Subject: [PATCH 18/20] cogl/dma-buf: Add mmap/munmap helpers + +Avoids dealing directly with mmap() and munmap(). + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1237 +(cherry picked from commit e05a1a6c0b2526146c85ec9c381bb2b49d19b4b2) +--- + cogl/cogl/cogl-dma-buf-handle.c | 41 +++++++++++++++++++++++++++++++++ + cogl/cogl/cogl-dma-buf-handle.h | 9 ++++++++ + 2 files changed, 50 insertions(+) + +diff --git a/cogl/cogl/cogl-dma-buf-handle.c b/cogl/cogl/cogl-dma-buf-handle.c +index 7e86e2267b..9724ac9c95 100644 +--- a/cogl/cogl/cogl-dma-buf-handle.c ++++ b/cogl/cogl/cogl-dma-buf-handle.c +@@ -38,6 +38,7 @@ + #include + #include + #include ++#include + #include + + struct _CoglDmaBufHandle +@@ -147,6 +148,46 @@ cogl_dma_buf_handle_sync_read_end (CoglDmaBufHandle *dmabuf_handle, + return sync_read (dmabuf_handle, DMA_BUF_SYNC_END, error); + } + ++gpointer ++cogl_dma_buf_handle_mmap (CoglDmaBufHandle *dmabuf_handle, ++ GError **error) ++{ ++ size_t size; ++ gpointer data; ++ ++ size = dmabuf_handle->height * dmabuf_handle->stride; ++ ++ data = mmap (NULL, size, PROT_READ, MAP_PRIVATE, ++ dmabuf_handle->dmabuf_fd, ++ dmabuf_handle->offset); ++ if (data == MAP_FAILED) ++ { ++ g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), ++ "mmap failed: %s", g_strerror (errno)); ++ return NULL; ++ } ++ ++ return data; ++} ++ ++gboolean ++cogl_dma_buf_handle_munmap (CoglDmaBufHandle *dmabuf_handle, ++ gpointer data, ++ GError **error) ++{ ++ size_t size; ++ ++ size = dmabuf_handle->height * dmabuf_handle->stride; ++ if (munmap (data, size) != 0) ++ { ++ g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), ++ "munmap failed: %s", g_strerror (errno)); ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ + CoglFramebuffer * + cogl_dma_buf_handle_get_framebuffer (CoglDmaBufHandle *dmabuf_handle) + { +diff --git a/cogl/cogl/cogl-dma-buf-handle.h b/cogl/cogl/cogl-dma-buf-handle.h +index 63c5bab7b7..08f307c1db 100644 +--- a/cogl/cogl/cogl-dma-buf-handle.h ++++ b/cogl/cogl/cogl-dma-buf-handle.h +@@ -71,6 +71,15 @@ gboolean + cogl_dma_buf_handle_sync_read_end (CoglDmaBufHandle *dmabuf_handle, + GError **error); + ++gpointer ++cogl_dma_buf_handle_mmap (CoglDmaBufHandle *dmabuf_handle, ++ GError **error); ++ ++gboolean ++cogl_dma_buf_handle_munmap (CoglDmaBufHandle *dmabuf_handle, ++ gpointer data, ++ GError **error); ++ + /** + * cogl_dma_buf_handle_get_framebuffer: (skip) + * +-- +2.28.0 + + +From ff8a80137047a91ed27d90467b004d691428bac4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Wed, 6 May 2020 22:14:17 +0200 +Subject: [PATCH 19/20] clutter/stage-view: Add tile based shadow damage + detection + +Compare, tile by tile, whether actual damage actually changed any +pixels. While this requires mmap():ing DMA buffers and comparing their +content, we should only ever use shadow buffers when we're using the +software renderer, meaning mmap() is cheap as it doesn't involve any +downloading. + +This works by making the shadow framebuffer double buffered, while +keeping track of damage history. When we're about to swap the onscreen +buffer, we compare what part of the posted damage actually changed, +records that into a damage history, then given the onscreen buffer age, +collect all actual damage for that age. The intersection of these tiles, +and the actual damage, is then used when blitting the shadow buffer to +the onscreen framebuffer. + +Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/1157 + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1237 +(cherry picked from commit 068385df3a0cf545e5110378b59db56cbd1bdef3) +--- + clutter/clutter/clutter-private.h | 3 + + clutter/clutter/clutter-stage-view.c | 472 +++++++++++++++++++++++++-- + clutter/clutter/clutter-util.c | 22 ++ + 3 files changed, 465 insertions(+), 32 deletions(-) + +diff --git a/clutter/clutter/clutter-private.h b/clutter/clutter/clutter-private.h +index a5cd1fa197..5a0fed85c9 100644 +--- a/clutter/clutter/clutter-private.h ++++ b/clutter/clutter/clutter-private.h +@@ -265,6 +265,9 @@ gboolean _clutter_util_rectangle_intersection (const cairo_rectangle_int_t *src1 + const cairo_rectangle_int_t *src2, + cairo_rectangle_int_t *dest); + ++gboolean _clutter_util_rectangle_contains (const cairo_rectangle_int_t *src1, ++ const cairo_rectangle_int_t *src2); ++ + + struct _ClutterVertex4 + { +diff --git a/clutter/clutter/clutter-stage-view.c b/clutter/clutter/clutter-stage-view.c +index 21ab02c97b..5e5966d06e 100644 +--- a/clutter/clutter/clutter-stage-view.c ++++ b/clutter/clutter/clutter-stage-view.c +@@ -17,6 +17,7 @@ + + #include "clutter-build-config.h" + ++#include "clutter/clutter-damage-history.h" + #include "clutter/clutter-stage-view.h" + #include "clutter/clutter-stage-view-private.h" + #include "clutter/clutter-private.h" +@@ -53,6 +54,12 @@ typedef struct _ClutterStageViewPrivate + + gboolean use_shadowfb; + struct { ++ struct { ++ CoglDmaBufHandle *handles[2]; ++ int current_idx; ++ ClutterDamageHistory *damage_history; ++ } dma_buf; ++ + CoglOffscreen *framebuffer; + } shadow; + +@@ -254,6 +261,66 @@ paint_transformed_framebuffer (ClutterStageView *view, + cogl_framebuffer_pop_matrix (dst_framebuffer); + } + ++static gboolean ++is_shadowfb_double_buffered (ClutterStageView *view) ++{ ++ ClutterStageViewPrivate *priv = ++ clutter_stage_view_get_instance_private (view); ++ ++ return priv->shadow.dma_buf.handles[0] && priv->shadow.dma_buf.handles[1]; ++} ++ ++static gboolean ++init_dma_buf_shadowfbs (ClutterStageView *view, ++ CoglContext *cogl_context, ++ int width, ++ int height, ++ GError **error) ++{ ++ ClutterStageViewPrivate *priv = ++ clutter_stage_view_get_instance_private (view); ++ CoglRenderer *cogl_renderer = cogl_context_get_renderer (cogl_context); ++ CoglFramebuffer *initial_shadowfb; ++ ++ if (!cogl_clutter_winsys_has_feature (COGL_WINSYS_FEATURE_BUFFER_AGE)) ++ { ++ g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, ++ "Buffer age not supported"); ++ return FALSE; ++ } ++ ++ if (!cogl_is_onscreen (priv->framebuffer)) ++ { ++ g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, ++ "Tried to use shadow buffer without onscreen"); ++ return FALSE; ++ } ++ ++ priv->shadow.dma_buf.handles[0] = cogl_renderer_create_dma_buf (cogl_renderer, ++ width, height, ++ error); ++ if (!priv->shadow.dma_buf.handles[0]) ++ return FALSE; ++ ++ priv->shadow.dma_buf.handles[1] = cogl_renderer_create_dma_buf (cogl_renderer, ++ width, height, ++ error); ++ if (!priv->shadow.dma_buf.handles[1]) ++ { ++ g_clear_pointer (&priv->shadow.dma_buf.handles[0], ++ cogl_dma_buf_handle_free); ++ return FALSE; ++ } ++ ++ priv->shadow.dma_buf.damage_history = clutter_damage_history_new (); ++ ++ initial_shadowfb = ++ cogl_dma_buf_handle_get_framebuffer (priv->shadow.dma_buf.handles[0]); ++ priv->shadow.framebuffer = cogl_object_ref (initial_shadowfb); ++ ++ return TRUE; ++} ++ + static CoglOffscreen * + create_offscreen_framebuffer (CoglContext *context, + int width, +@@ -285,11 +352,11 @@ create_offscreen_framebuffer (CoglContext *context, + } + + static gboolean +-init_offscreen_shadowfb (ClutterStageView *view, +- CoglContext *cogl_context, +- int width, +- int height, +- GError **error) ++init_fallback_shadowfb (ClutterStageView *view, ++ CoglContext *cogl_context, ++ int width, ++ int height, ++ GError **error) + { + ClutterStageViewPrivate *priv = + clutter_stage_view_get_instance_private (view); +@@ -317,7 +384,17 @@ init_shadowfb (ClutterStageView *view) + height = cogl_framebuffer_get_height (priv->framebuffer); + cogl_context = cogl_framebuffer_get_context (priv->framebuffer); + +- if (!init_offscreen_shadowfb (view, cogl_context, width, height, &error)) ++ if (init_dma_buf_shadowfbs (view, cogl_context, width, height, &error)) ++ { ++ g_message ("Initialized double buffered shadow fb for %s", priv->name); ++ return; ++ } ++ ++ g_warning ("Failed to initialize double buffered shadow fb for %s: %s", ++ priv->name, error->message); ++ g_clear_error (&error); ++ ++ if (!init_fallback_shadowfb (view, cogl_context, width, height, &error)) + { + g_warning ("Failed to initialize single buffered shadow fb for %s: %s", + priv->name, error->message); +@@ -358,44 +435,298 @@ clutter_stage_view_blit_offscreen (ClutterStageView *view, + } + } + +-void +-clutter_stage_view_before_swap_buffer (ClutterStageView *view, +- const cairo_rectangle_int_t *swap_region) ++static gboolean ++is_tile_dirty (cairo_rectangle_int_t *tile, ++ uint8_t *current_data, ++ uint8_t *prev_data, ++ int bpp, ++ int stride) ++{ ++ int y; ++ ++ for (y = tile->y; y < tile->y + tile->height; y++) ++ { ++ if (memcmp (prev_data + y * stride + tile->x * bpp, ++ current_data + y * stride + tile->x * bpp, ++ tile->width * bpp) != 0) ++ return TRUE; ++ } ++ ++ return FALSE; ++} ++ ++static int ++flip_dma_buf_idx (int idx) ++{ ++ return (idx + 1) % 2; ++} ++ ++static cairo_region_t * ++find_damaged_tiles (ClutterStageView *view, ++ const cairo_region_t *damage_region, ++ GError **error) + { + ClutterStageViewPrivate *priv = + clutter_stage_view_get_instance_private (view); +- g_autoptr (GError) error = NULL; ++ cairo_region_t *tile_damage_region; ++ cairo_rectangle_int_t damage_extents; ++ cairo_rectangle_int_t fb_rect; ++ int prev_dma_buf_idx; ++ CoglDmaBufHandle *prev_dma_buf_handle; ++ uint8_t *prev_data; ++ int current_dma_buf_idx; ++ CoglDmaBufHandle *current_dma_buf_handle; ++ uint8_t *current_data; ++ int width, height, stride, bpp; ++ int tile_x_min, tile_x_max; ++ int tile_y_min, tile_y_max; ++ int tile_x, tile_y; ++ const int tile_size = 16; ++ ++ prev_dma_buf_idx = flip_dma_buf_idx (priv->shadow.dma_buf.current_idx); ++ prev_dma_buf_handle = priv->shadow.dma_buf.handles[prev_dma_buf_idx]; ++ ++ current_dma_buf_idx = priv->shadow.dma_buf.current_idx; ++ current_dma_buf_handle = priv->shadow.dma_buf.handles[current_dma_buf_idx]; ++ ++ width = cogl_dma_buf_handle_get_width (current_dma_buf_handle); ++ height = cogl_dma_buf_handle_get_height (current_dma_buf_handle); ++ stride = cogl_dma_buf_handle_get_stride (current_dma_buf_handle); ++ bpp = cogl_dma_buf_handle_get_bpp (current_dma_buf_handle); ++ ++ cogl_framebuffer_finish (priv->shadow.framebuffer); ++ ++ if (!cogl_dma_buf_handle_sync_read_start (prev_dma_buf_handle, error)) ++ return NULL; ++ ++ if (!cogl_dma_buf_handle_sync_read_start (current_dma_buf_handle, error)) ++ goto err_sync_read_current; ++ ++ prev_data = cogl_dma_buf_handle_mmap (prev_dma_buf_handle, error); ++ if (!prev_data) ++ goto err_mmap_prev; ++ current_data = cogl_dma_buf_handle_mmap (current_dma_buf_handle, error); ++ if (!current_data) ++ goto err_mmap_current; ++ ++ fb_rect = (cairo_rectangle_int_t) { ++ .width = width, ++ .height = height, ++ }; ++ ++ cairo_region_get_extents (damage_region, &damage_extents); ++ ++ tile_x_min = damage_extents.x / tile_size; ++ tile_x_max = ((damage_extents.x + damage_extents.width + tile_size - 1) / ++ tile_size); ++ tile_y_min = damage_extents.y / tile_size; ++ tile_y_max = ((damage_extents.y + damage_extents.height + tile_size - 1) / ++ tile_size); ++ ++ tile_damage_region = cairo_region_create (); ++ ++ for (tile_y = tile_y_min; tile_y <= tile_y_max; tile_y++) ++ { ++ for (tile_x = tile_x_min; tile_x <= tile_x_max; tile_x++) ++ { ++ cairo_rectangle_int_t tile = { ++ .x = tile_x * tile_size, ++ .y = tile_y * tile_size, ++ .width = tile_size, ++ .height = tile_size, ++ }; + +- if (!priv->shadow.framebuffer) +- return; ++ if (cairo_region_contains_rectangle (damage_region, &tile) == ++ CAIRO_REGION_OVERLAP_OUT) ++ continue; + +- if (swap_region->width == 0 || swap_region->height == 0) ++ _clutter_util_rectangle_intersection (&tile, &fb_rect, &tile); ++ ++ if (is_tile_dirty (&tile, current_data, prev_data, bpp, stride)) ++ cairo_region_union_rectangle (tile_damage_region, &tile); ++ } ++ } ++ ++ if (!cogl_dma_buf_handle_sync_read_end (prev_dma_buf_handle, error)) + { +- int width, height; ++ g_warning ("Failed to end DMA buffer read synchronization: %s", ++ (*error)->message); ++ g_clear_error (error); ++ } + +- width = cogl_framebuffer_get_width (priv->framebuffer); +- height = cogl_framebuffer_get_height (priv->framebuffer); +- if (!cogl_blit_framebuffer (priv->shadow.framebuffer, +- priv->framebuffer, +- 0, 0, +- 0, 0, +- width, height, +- &error)) +- g_warning ("Failed to blit shadow buffer: %s", error->message); ++ if (!cogl_dma_buf_handle_sync_read_end (current_dma_buf_handle, error)) ++ { ++ g_warning ("Failed to end DMA buffer read synchronization: %s", ++ (*error)->message); ++ g_clear_error (error); ++ } ++ ++ cogl_dma_buf_handle_munmap (prev_dma_buf_handle, prev_data, NULL); ++ cogl_dma_buf_handle_munmap (current_dma_buf_handle, current_data, NULL); ++ ++ cairo_region_intersect (tile_damage_region, damage_region); ++ ++ return tile_damage_region; ++ ++err_mmap_current: ++ cogl_dma_buf_handle_munmap (prev_dma_buf_handle, prev_data, NULL); ++ ++err_mmap_prev: ++ cogl_dma_buf_handle_sync_read_end (current_dma_buf_handle, NULL); ++ ++err_sync_read_current: ++ cogl_dma_buf_handle_sync_read_end (prev_dma_buf_handle, NULL); ++ ++ return NULL; ++} ++ ++static void ++swap_dma_buf_framebuffer (ClutterStageView *view) ++{ ++ ClutterStageViewPrivate *priv = ++ clutter_stage_view_get_instance_private (view); ++ int next_idx; ++ CoglDmaBufHandle *next_dma_buf_handle; ++ CoglOffscreen *next_framebuffer; ++ ++ next_idx = ((priv->shadow.dma_buf.current_idx + 1) % ++ G_N_ELEMENTS (priv->shadow.dma_buf.handles)); ++ priv->shadow.dma_buf.current_idx = next_idx; ++ ++ next_dma_buf_handle = priv->shadow.dma_buf.handles[next_idx]; ++ next_framebuffer = ++ cogl_dma_buf_handle_get_framebuffer (next_dma_buf_handle); ++ cogl_clear_object (&priv->shadow.framebuffer); ++ priv->shadow.framebuffer = cogl_object_ref (next_framebuffer); ++} ++ ++static void ++copy_shadowfb_to_onscreen (ClutterStageView *view, ++ const cairo_rectangle_int_t *swap_region) ++{ ++ ClutterStageViewPrivate *priv = ++ clutter_stage_view_get_instance_private (view); ++ ClutterDamageHistory *damage_history = priv->shadow.dma_buf.damage_history; ++ cairo_region_t *damage_region; ++ int age; ++ int i; ++ ++ if (swap_region->width == 0 || swap_region->height == 0) ++ { ++ cairo_rectangle_int_t full_damage = { ++ .width = cogl_framebuffer_get_width (priv->framebuffer), ++ .height = cogl_framebuffer_get_height (priv->framebuffer), ++ }; ++ damage_region = cairo_region_create_rectangle (&full_damage); + } + else + { ++ damage_region = cairo_region_create_rectangle (swap_region); ++ } ++ ++ if (is_shadowfb_double_buffered (view)) ++ { ++ CoglOnscreen *onscreen = COGL_ONSCREEN (priv->framebuffer); ++ cairo_region_t *changed_region; ++ ++ if (cogl_onscreen_get_frame_counter (onscreen) >= 1) ++ { ++ g_autoptr (GError) error = NULL; ++ ++ changed_region = find_damaged_tiles (view, damage_region, &error); ++ if (!changed_region) ++ { ++ int other_dma_buf_idx; ++ ++ g_warning ("Disabling actual damage detection: %s", ++ error->message); ++ ++ other_dma_buf_idx = ++ flip_dma_buf_idx (priv->shadow.dma_buf.current_idx); ++ g_clear_pointer (&priv->shadow.dma_buf.handles[other_dma_buf_idx], ++ cogl_dma_buf_handle_free); ++ } ++ } ++ else ++ { ++ changed_region = cairo_region_copy (damage_region); ++ } ++ ++ if (changed_region) ++ { ++ cairo_rectangle_int_t changed_extents; ++ int buffer_age; ++ ++ cairo_region_get_extents (changed_region, &changed_extents); ++ clutter_damage_history_record (damage_history, &changed_extents); ++ ++ buffer_age = cogl_onscreen_get_buffer_age (onscreen); ++ if (clutter_damage_history_is_age_valid (damage_history, buffer_age)) ++ { ++ for (age = 1; age <= buffer_age; age++) ++ { ++ const cairo_rectangle_int_t *old_damage; ++ ++ old_damage = clutter_damage_history_lookup (damage_history, age); ++ cairo_region_union_rectangle (changed_region, old_damage); ++ } ++ ++ cairo_region_destroy (damage_region); ++ damage_region = g_steal_pointer (&changed_region); ++ } ++ else ++ { ++ cairo_region_destroy (changed_region); ++ } ++ ++ clutter_damage_history_step (damage_history); ++ } ++ } ++ ++ if (0) ++ { ++ CoglColor clear_color; ++ ++ cogl_color_init_from_4ub (&clear_color, ++ 0, 0, 0, 0); ++ cogl_framebuffer_clear (priv->framebuffer, COGL_BUFFER_BIT_COLOR, &clear_color); ++ } ++ ++ for (i = 0; i < cairo_region_num_rectangles (damage_region); i++) ++ { ++ g_autoptr (GError) error = NULL; ++ cairo_rectangle_int_t rect; ++ ++ cairo_region_get_rectangle (damage_region, i, &rect); ++ + if (!cogl_blit_framebuffer (priv->shadow.framebuffer, + priv->framebuffer, +- swap_region->x, swap_region->y, +- swap_region->x, swap_region->y, +- swap_region->width, swap_region->height, ++ rect.x, rect.y, ++ rect.x, rect.y, ++ rect.width, rect.height, + &error)) + { + g_warning ("Failed to blit shadow buffer: %s", error->message); ++ cairo_region_destroy (damage_region); + return; + } + } ++ ++ cairo_region_destroy (damage_region); ++ ++ if (is_shadowfb_double_buffered (view)) ++ swap_dma_buf_framebuffer (view); ++} ++ ++void ++clutter_stage_view_before_swap_buffer (ClutterStageView *view, ++ const cairo_rectangle_int_t *swap_region) ++{ ++ ClutterStageViewPrivate *priv = ++ clutter_stage_view_get_instance_private (view); ++ ++ if (priv->shadow.framebuffer) ++ copy_shadowfb_to_onscreen (view, swap_region); + } + + float +@@ -407,6 +738,47 @@ clutter_stage_view_get_scale (ClutterStageView *view) + return priv->scale; + } + ++typedef void (*FrontBufferCallback) (CoglFramebuffer *framebuffer, ++ gconstpointer user_data); ++ ++static void ++clutter_stage_view_foreach_front_buffer (ClutterStageView *view, ++ FrontBufferCallback callback, ++ gconstpointer user_data) ++{ ++ ClutterStageViewPrivate *priv = ++ clutter_stage_view_get_instance_private (view); ++ ++ if (priv->offscreen) ++ { ++ callback (priv->offscreen, user_data); ++ } ++ else if (priv->shadow.framebuffer) ++ { ++ if (is_shadowfb_double_buffered (view)) ++ { ++ int i; ++ ++ for (i = 0; i < G_N_ELEMENTS (priv->shadow.dma_buf.handles); i++) ++ { ++ CoglDmaBufHandle *handle = priv->shadow.dma_buf.handles[i]; ++ CoglFramebuffer *framebuffer = ++ cogl_dma_buf_handle_get_framebuffer (handle); ++ ++ callback (framebuffer, user_data); ++ } ++ } ++ else ++ { ++ callback (priv->shadow.framebuffer, user_data); ++ } ++ } ++ else ++ { ++ callback (priv->framebuffer, user_data); ++ } ++} ++ + gboolean + clutter_stage_view_is_dirty_viewport (ClutterStageView *view) + { +@@ -425,6 +797,19 @@ clutter_stage_view_invalidate_viewport (ClutterStageView *view) + priv->dirty_viewport = TRUE; + } + ++static void ++set_framebuffer_viewport (CoglFramebuffer *framebuffer, ++ gconstpointer user_data) ++{ ++ const ClutterRect *rect = user_data; ++ ++ cogl_framebuffer_set_viewport (framebuffer, ++ rect->origin.x, ++ rect->origin.y, ++ rect->size.width, ++ rect->size.height); ++} ++ + void + clutter_stage_view_set_viewport (ClutterStageView *view, + float x, +@@ -434,11 +819,17 @@ clutter_stage_view_set_viewport (ClutterStageView *view, + { + ClutterStageViewPrivate *priv = + clutter_stage_view_get_instance_private (view); +- CoglFramebuffer *framebuffer; ++ ClutterRect rect; + + priv->dirty_viewport = FALSE; +- framebuffer = clutter_stage_view_get_framebuffer (view); +- cogl_framebuffer_set_viewport (framebuffer, x, y, width, height); ++ ++ rect = (ClutterRect) { ++ .origin = { .x = x, .y = y }, ++ .size = { .width = width, .height = height }, ++ }; ++ clutter_stage_view_foreach_front_buffer (view, ++ set_framebuffer_viewport, ++ &rect); + } + + gboolean +@@ -450,6 +841,13 @@ clutter_stage_view_is_dirty_projection (ClutterStageView *view) + return priv->dirty_projection; + } + ++static void ++set_framebuffer_projection_matrix (CoglFramebuffer *framebuffer, ++ gconstpointer user_data) ++{ ++ cogl_framebuffer_set_projection_matrix (framebuffer, user_data); ++} ++ + void + clutter_stage_view_invalidate_projection (ClutterStageView *view) + { +@@ -465,11 +863,11 @@ clutter_stage_view_set_projection (ClutterStageView *view, + { + ClutterStageViewPrivate *priv = + clutter_stage_view_get_instance_private (view); +- CoglFramebuffer *framebuffer; + + priv->dirty_projection = FALSE; +- framebuffer = clutter_stage_view_get_framebuffer (view); +- cogl_framebuffer_set_projection_matrix (framebuffer, matrix); ++ clutter_stage_view_foreach_front_buffer (view, ++ set_framebuffer_projection_matrix, ++ matrix); + } + + void +@@ -593,10 +991,20 @@ clutter_stage_view_dispose (GObject *object) + ClutterStageView *view = CLUTTER_STAGE_VIEW (object); + ClutterStageViewPrivate *priv = + clutter_stage_view_get_instance_private (view); ++ int i; + + g_clear_pointer (&priv->name, g_free); + g_clear_pointer (&priv->framebuffer, cogl_object_unref); ++ + g_clear_pointer (&priv->shadow.framebuffer, cogl_object_unref); ++ for (i = 0; i < G_N_ELEMENTS (priv->shadow.dma_buf.handles); i++) ++ { ++ g_clear_pointer (&priv->shadow.dma_buf.handles[i], ++ cogl_dma_buf_handle_free); ++ } ++ g_clear_pointer (&priv->shadow.dma_buf.damage_history, ++ clutter_damage_history_free); ++ + g_clear_pointer (&priv->offscreen, cogl_object_unref); + g_clear_pointer (&priv->offscreen_pipeline, cogl_object_unref); + +diff --git a/clutter/clutter/clutter-util.c b/clutter/clutter/clutter-util.c +index ed52b69774..834adae39a 100644 +--- a/clutter/clutter/clutter-util.c ++++ b/clutter/clutter/clutter-util.c +@@ -210,6 +210,28 @@ _clutter_util_rectangle_intersection (const cairo_rectangle_int_t *src1, + } + } + ++gboolean ++_clutter_util_rectangle_contains (const cairo_rectangle_int_t *src1, ++ const cairo_rectangle_int_t *src2) ++{ ++ int x1, y1, x2, y2; ++ ++ x1 = MAX (src1->x, src2->x); ++ y1 = MAX (src1->y, src2->y); ++ ++ x2 = MIN (src1->x + (int) src1->width, src2->x + (int) src2->width); ++ y2 = MIN (src1->y + (int) src1->height, src2->y + (int) src2->height); ++ ++ if (x1 >= x2 || y1 >= y2) ++ { ++ return FALSE; ++ } ++ else ++ { ++ return TRUE; ++ } ++} ++ + float + _clutter_util_matrix_determinant (const ClutterMatrix *matrix) + { +-- +2.28.0 + + +From 9968d4aeefc2c47a63e12f977dad031672a63abe Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= +Date: Sat, 7 Mar 2020 20:29:09 +0100 +Subject: [PATCH 20/20] clutter/stage-cogl: Use view fb instead of onscreen fb + for debug-drawing + +We need to use the framebuffer of the view instead of the onscreen +framebuffer when painting the damage region, otherwise the redraw clips +on rotated monitors won't be shown correctly. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1237 +(cherry picked from commit 8e1bd64e05c3098fcce4f916f9e4468decb8f30c) +--- + clutter/clutter/cogl/clutter-stage-cogl.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/clutter/clutter/cogl/clutter-stage-cogl.c b/clutter/clutter/cogl/clutter-stage-cogl.c +index 11273ec894..3f1f609c4e 100644 +--- a/clutter/clutter/cogl/clutter-stage-cogl.c ++++ b/clutter/clutter/cogl/clutter-stage-cogl.c +@@ -353,7 +353,7 @@ paint_damage_region (ClutterStageWindow *stage_window, + ClutterStageView *view, + cairo_rectangle_int_t *swap_region) + { +- CoglFramebuffer *framebuffer = clutter_stage_view_get_onscreen (view); ++ CoglFramebuffer *framebuffer = clutter_stage_view_get_framebuffer (view); + CoglContext *ctx = cogl_framebuffer_get_context (framebuffer); + static CoglPipeline *overlay_blue = NULL; + ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window); +-- +2.28.0 + diff --git a/SPECS/mutter.spec b/SPECS/mutter.spec index e168884..45e87b0 100644 --- a/SPECS/mutter.spec +++ b/SPECS/mutter.spec @@ -8,7 +8,7 @@ Name: mutter Version: 3.32.2 -Release: 44%{?dist} +Release: 55%{?dist} Summary: Window and compositing manager based on Clutter License: GPLv2+ @@ -144,6 +144,10 @@ Patch404: 0001-backend-Add-getter-for-MetaScreenCast.patch Patch405: 0002-renderer-native-Add-API-to-get-primary-GPU.patch Patch406: 0003-screen-cast-Move-DMA-buffer-allocation-to-MetaScreen.patch Patch407: 0004-screen-cast-Disable-DMA-buffer-based-screen-casting-.patch +# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1351 +# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1365 +Patch408: cursor-move-only-screen-cast-fixes.patch +Patch409: mutter-bump-screencast-api-version.patch # Only treat WM_PROTOCOLS messages as WM_PROTOCOL messages (#1847203) Patch500: 0001-stage-x11-Check-that-message-is-WM_PROTOCOLS-before-.patch @@ -151,6 +155,28 @@ Patch500: 0001-stage-x11-Check-that-message-is-WM_PROTOCOLS-before-.patch # Don't show widow actor until explictly shown (#1719937) Patch501: 0001-window-actor-Don-t-show-actor-until-meta_window_acto.patch +# Handle GPU unplug gracefully (#1846191) +Patch502: 0001-monitor-manager-kms-Trigger-hotplug-processing-on-gp.patch +Patch503: 0002-gpu-kms-Reset-CRTC-mode-and-output-list-if-no-resour.patch + +# Add tile based shadow buffer damage tracking (#1670273) +Patch504: shadow-buffer-tile-damage.patch + +# Add PING_TIMEOUT_DELAY to mutter MetaPreferences (#1886034) +Patch505: 0001-display-Make-check-alive-timeout-configureable.patch + +# Polyinstantiation (#1861769) +Patch506: 0001-xwayland-Don-t-spew-warnings-when-looking-for-X11-di.patch +Patch507: 0002-xwayland-Make-sure-tmp-.X11-unix-exists.patch + +# Mitigate nouveau misidentifying connectors (#1786496) +Patch508: 0001-monitor-config-manager-Handle-multiple-builtin-panel.patch + +# Don't ever enable double buffered shadowfb and fix software rendering +# detection (#1921151) +Patch509: 0001-clutter-stage-view-Hide-double-buffered-shadowfb-beh.patch +Patch510: 0002-cogl-gpu-info-Fix-software-acceleration-detection.patch + BuildRequires: chrpath BuildRequires: pango-devel BuildRequires: startup-notification-devel @@ -292,6 +318,48 @@ desktop-file-validate %{buildroot}/%{_datadir}/applications/%{name}.desktop %{_datadir}/mutter-%{mutter_api_version}/tests %changelog +* Tue Feb 09 2021 Jonas Ådahl - 3.32.2-55 +- Fix slow nouveau with llvmpipe + Resolves: #1921151 + +* Tue Jan 12 2021 Jonas Ådahl - 3.32.2-54 +- Fix polyinstantiation patch backport + Resolves: #1861769 + +* Thu Dec 17 2020 Jonas Ådahl - 3.32.2-53 +- Fix test case backport + Related: #1786496 + +* Thu Dec 17 2020 Jonas Ådahl - 3.32.2-52 +- Support polyinstantiation + Resolves: #1861769 +- Mitigate nouveau misidentifying connectors + Resolves: #1786496 + +* Mon Dec 07 2020 Jonas Ådahl - 3.32.2-51 +- Add PING_TIMEOUT_DELAY to mutter MetaPreferences + Resolves: #1886034 + +* Thu Nov 26 2020 Jonas Ådahl - 3.32.2-50 +- Fix GLX stereo buffer rebase error + Resolves: #1889528 + +* Tue Nov 10 2020 Jonas Ådahl - 3.32.2-49 +- Add tile based shadow buffer damage tracking + Resolves: #1670273 + +* Thu Sep 03 2020 Florian Müllner - 3.32.2-47 +- Fix screen sharing on wayland + Resolves: #1873963 + +* Wed Jul 15 2020 Jonas Ådahl - 3.32.2-46 +- Handle cursor only screen cast frames better + Related: #1837381 + +* Thu Jul 02 2020 Jonas Ådahl - 3.32.2-45 +- Handle GPU unplug gracefully + Resolves: #1846191 + * Thu Jun 25 2020 Jonas Ådahl - 3.32.2-44 - Don't show widow actor until explictly shown Resolves: #1719937