diff --git a/SOURCES/0001-Add-support-for-quad-buffer-stereo.patch b/SOURCES/0001-Add-support-for-quad-buffer-stereo.patch index 7d150d2..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 6108e0175932f74733c46ebe13db06998f26d4ba 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 diff --git a/SOURCES/0001-clutter-Backport-of-touch-mode.patch b/SOURCES/0001-clutter-Backport-of-touch-mode.patch new file mode 100644 index 0000000..dff32fa --- /dev/null +++ b/SOURCES/0001-clutter-Backport-of-touch-mode.patch @@ -0,0 +1,357 @@ +From 2a2e870c139e2130b00d582546616269bca27458 Mon Sep 17 00:00:00 2001 +From: Carlos Garnacho +Date: Fri, 4 Sep 2020 17:11:36 +0200 +Subject: [PATCH] clutter: Backport of ::touch-mode + +In upstream/master this is a ClutterSeat readonly property. Add it to +ClutterDeviceManager here, the mechanism and triggering is the same +though. +--- + clutter/clutter/clutter-device-manager.c | 24 +++ + clutter/clutter/clutter-device-manager.h | 2 + + .../evdev/clutter-device-manager-evdev.c | 179 ++++++++++++++++++ + 3 files changed, 205 insertions(+) + +diff --git a/clutter/clutter/clutter-device-manager.c b/clutter/clutter/clutter-device-manager.c +index c676384..e1cc455 100644 +--- a/clutter/clutter/clutter-device-manager.c ++++ b/clutter/clutter/clutter-device-manager.c +@@ -62,6 +62,7 @@ enum + PROP_0, + + PROP_BACKEND, ++ PROP_TOUCH_MODE, + + PROP_LAST + }; +@@ -108,6 +109,7 @@ clutter_device_manager_set_property (GObject *gobject, + priv->backend = g_value_get_object (value); + break; + ++ case PROP_TOUCH_MODE: + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + } +@@ -127,6 +129,10 @@ clutter_device_manager_get_property (GObject *gobject, + g_value_set_object (value, priv->backend); + break; + ++ case PROP_TOUCH_MODE: ++ g_value_set_boolean (value, FALSE); ++ break; ++ + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + } +@@ -143,6 +149,12 @@ clutter_device_manager_class_init (ClutterDeviceManagerClass *klass) + P_("The ClutterBackend of the device manager"), + CLUTTER_TYPE_BACKEND, + CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); ++ obj_props[PROP_TOUCH_MODE] = ++ g_param_spec_boolean ("touch-mode", ++ P_("Touch mode"), ++ P_("Touch mode"), ++ FALSE, ++ CLUTTER_PARAM_READABLE); + + gobject_class->set_property = clutter_device_manager_set_property; + gobject_class->get_property = clutter_device_manager_get_property; +@@ -579,3 +591,15 @@ clutter_device_manager_get_kbd_a11y_settings (ClutterDeviceManager *device_man + + *settings = device_manager->priv->kbd_a11y_settings; + } ++ ++gboolean ++clutter_device_manager_get_touch_mode (ClutterDeviceManager *device_manager) ++{ ++ gboolean touch_mode; ++ ++ g_return_val_if_fail (CLUTTER_IS_DEVICE_MANAGER (device_manager), FALSE); ++ ++ g_object_get (G_OBJECT (device_manager), "touch-mode", &touch_mode, NULL); ++ ++ return touch_mode; ++} +diff --git a/clutter/clutter/clutter-device-manager.h b/clutter/clutter/clutter-device-manager.h +index 1cbf030..a4a6271 100644 +--- a/clutter/clutter/clutter-device-manager.h ++++ b/clutter/clutter/clutter-device-manager.h +@@ -155,6 +155,8 @@ void clutter_device_manager_set_kbd_a11y_settings (ClutterDeviceManager *devic + CLUTTER_EXPORT + void clutter_device_manager_get_kbd_a11y_settings (ClutterDeviceManager *device_manager, + ClutterKbdA11ySettings *settings); ++CLUTTER_EXPORT ++gboolean clutter_device_manager_get_touch_mode (ClutterDeviceManager *device_manager); + + G_END_DECLS + +diff --git a/clutter/clutter/evdev/clutter-device-manager-evdev.c b/clutter/clutter/evdev/clutter-device-manager-evdev.c +index 84b0aad..78b5b64 100644 +--- a/clutter/clutter/evdev/clutter-device-manager-evdev.c ++++ b/clutter/clutter/evdev/clutter-device-manager-evdev.c +@@ -108,6 +108,19 @@ struct _ClutterDeviceManagerEvdevPrivate + + gint device_id_next; + GList *free_device_ids; ++ ++ guint tablet_mode_switch_state : 1; ++ guint has_touchscreen : 1; ++ guint has_tablet_switch : 1; ++ guint has_pointer : 1; ++ guint touch_mode : 1; ++}; ++ ++enum ++{ ++ PROP_0, ++ PROP_TOUCH_MODE, ++ N_PROPS + }; + + static void clutter_device_manager_evdev_event_extender_init (ClutterEventExtenderInterface *iface); +@@ -765,6 +778,34 @@ clutter_event_source_free (ClutterEventSource *source) + g_source_unref (g_source); + } + ++static void ++update_touch_mode (ClutterDeviceManagerEvdev *manager_evdev) ++{ ++ ClutterDeviceManagerEvdevPrivate *priv = manager_evdev->priv; ++ gboolean touch_mode; ++ ++ /* No touch mode if we don't have a touchscreen, easy */ ++ if (!priv->has_touchscreen) ++ touch_mode = FALSE; ++ /* If we have a tablet mode switch, honor it being unset */ ++ else if (priv->has_tablet_switch && !priv->tablet_mode_switch_state) ++ touch_mode = FALSE; ++ /* If tablet mode is enabled, go for it */ ++ else if (priv->has_tablet_switch && priv->tablet_mode_switch_state) ++ touch_mode = TRUE; ++ /* If there is no tablet mode switch (eg. kiosk machines), ++ * assume touch-mode is mutually exclusive with pointers. ++ */ ++ else ++ touch_mode = !priv->has_pointer; ++ ++ if (priv->touch_mode != touch_mode) ++ { ++ priv->touch_mode = touch_mode; ++ g_object_notify (G_OBJECT (manager_evdev), "touch-mode"); ++ } ++} ++ + static void + evdev_add_device (ClutterDeviceManagerEvdev *manager_evdev, + struct libinput_device *libinput_device) +@@ -942,19 +983,81 @@ flush_event_queue (void) + } + } + ++static gboolean ++has_touchscreen (ClutterDeviceManagerEvdev *manager_evdev) ++{ ++ ClutterDeviceManagerEvdevPrivate *priv = manager_evdev->priv; ++ GSList *l; ++ ++ for (l = priv->devices; l; l = l->next) ++ { ++ ClutterInputDeviceType device_type; ++ ++ device_type = clutter_input_device_get_device_type (l->data); ++ ++ if (device_type == CLUTTER_TOUCHSCREEN_DEVICE) ++ return TRUE; ++ } ++ ++ return FALSE; ++} ++ ++static gboolean ++device_type_is_pointer (ClutterInputDeviceType device_type) ++{ ++ return (device_type == CLUTTER_POINTER_DEVICE || ++ device_type == CLUTTER_TOUCHPAD_DEVICE); ++} ++ ++static gboolean ++has_pointer (ClutterDeviceManagerEvdev *manager_evdev) ++{ ++ ClutterDeviceManagerEvdevPrivate *priv = manager_evdev->priv; ++ GSList *l; ++ ++ for (l = priv->devices; l; l = l->next) ++ { ++ ClutterInputDeviceType device_type; ++ ++ device_type = clutter_input_device_get_device_type (l->data); ++ ++ if (device_type_is_pointer (device_type)) ++ return TRUE; ++ } ++ ++ return FALSE; ++} ++ + static gboolean + process_base_event (ClutterDeviceManagerEvdev *manager_evdev, + struct libinput_event *event) + { ++ ClutterDeviceManagerEvdevPrivate *priv = manager_evdev->priv; + ClutterInputDevice *device; + struct libinput_device *libinput_device; + gboolean handled = TRUE; ++ gboolean check_touch_mode; + + switch (libinput_event_get_type (event)) + { + case LIBINPUT_EVENT_DEVICE_ADDED: + libinput_device = libinput_event_get_device (event); + ++ priv->has_touchscreen |= ++ libinput_device_has_capability (libinput_device, LIBINPUT_DEVICE_CAP_TOUCH); ++ priv->has_pointer |= ++ libinput_device_has_capability (libinput_device, LIBINPUT_DEVICE_CAP_POINTER); ++ check_touch_mode = priv->has_touchscreen | priv->has_pointer; ++ ++ if (libinput_device_has_capability (libinput_device, ++ LIBINPUT_DEVICE_CAP_SWITCH) && ++ libinput_device_switch_has_switch (libinput_device, ++ LIBINPUT_SWITCH_TABLET_MODE)) ++ { ++ priv->has_tablet_switch = TRUE; ++ check_touch_mode = TRUE; ++ } ++ + evdev_add_device (manager_evdev, libinput_device); + break; + +@@ -966,7 +1069,17 @@ process_base_event (ClutterDeviceManagerEvdev *manager_evdev, + + libinput_device = libinput_event_get_device (event); + ++ check_touch_mode = ++ libinput_device_has_capability (libinput_device, LIBINPUT_DEVICE_CAP_TOUCH); + device = libinput_device_get_user_data (libinput_device); ++ if (check_touch_mode) ++ priv->has_touchscreen = has_touchscreen (manager_evdev); ++ if (device_type_is_pointer (clutter_input_device_get_device_type (device))) ++ { ++ priv->has_pointer = has_pointer (manager_evdev); ++ check_touch_mode = TRUE; ++ } ++ + evdev_remove_device (manager_evdev, + CLUTTER_INPUT_DEVICE_EVDEV (device)); + break; +@@ -975,6 +1088,9 @@ process_base_event (ClutterDeviceManagerEvdev *manager_evdev, + handled = FALSE; + } + ++ if (check_touch_mode) ++ update_touch_mode (manager_evdev); ++ + return handled; + } + +@@ -1752,6 +1868,23 @@ process_device_event (ClutterDeviceManagerEvdev *manager_evdev, + notify_pad_ring (device, time, number, source, group, mode, angle); + break; + } ++ case LIBINPUT_EVENT_SWITCH_TOGGLE: ++ { ++ ClutterDeviceManagerEvdevPrivate *priv = manager_evdev->priv; ++ struct libinput_event_switch *switch_event = ++ libinput_event_get_switch_event (event); ++ enum libinput_switch sw = ++ libinput_event_switch_get_switch (switch_event); ++ enum libinput_switch_state state = ++ libinput_event_switch_get_switch_state (switch_event); ++ ++ if (sw == LIBINPUT_SWITCH_TABLET_MODE) ++ { ++ priv->tablet_mode_switch_state = (state == LIBINPUT_SWITCH_STATE_ON); ++ update_touch_mode (manager_evdev); ++ } ++ break; ++ } + default: + handled = FALSE; + } +@@ -1967,6 +2100,10 @@ clutter_device_manager_evdev_constructed (GObject *gobject) + + source = clutter_event_source_new (manager_evdev); + priv->event_source = source; ++ ++ priv->has_touchscreen = has_touchscreen (manager_evdev); ++ priv->has_pointer = has_pointer (manager_evdev); ++ update_touch_mode (manager_evdev); + } + + static void +@@ -2001,6 +2138,43 @@ clutter_device_manager_evdev_dispose (GObject *object) + G_OBJECT_CLASS (clutter_device_manager_evdev_parent_class)->dispose (object); + } + ++static void ++clutter_device_manager_evdev_set_property (GObject *object, ++ guint prop_id, ++ const GValue *value, ++ GParamSpec *pspec) ++{ ++ switch (prop_id) ++ { ++ case PROP_TOUCH_MODE: ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); ++ } ++} ++ ++static void ++clutter_device_manager_evdev_get_property (GObject *object, ++ guint prop_id, ++ GValue *value, ++ GParamSpec *pspec) ++{ ++ ClutterDeviceManagerEvdev *manager_evdev; ++ ClutterDeviceManagerEvdevPrivate *priv; ++ ++ manager_evdev = CLUTTER_DEVICE_MANAGER_EVDEV (object); ++ priv = manager_evdev->priv; ++ ++ switch (prop_id) ++ { ++ case PROP_TOUCH_MODE: ++ g_value_set_boolean (value, priv->touch_mode); ++ break; ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); ++ } ++} ++ ++ + static void + clutter_device_manager_evdev_finalize (GObject *object) + { +@@ -2036,6 +2210,8 @@ clutter_device_manager_evdev_class_init (ClutterDeviceManagerEvdevClass *klass) + gobject_class->constructed = clutter_device_manager_evdev_constructed; + gobject_class->finalize = clutter_device_manager_evdev_finalize; + gobject_class->dispose = clutter_device_manager_evdev_dispose; ++ gobject_class->set_property = clutter_device_manager_evdev_set_property; ++ gobject_class->get_property = clutter_device_manager_evdev_get_property; + + manager_class = CLUTTER_DEVICE_MANAGER_CLASS (klass); + manager_class->add_device = clutter_device_manager_evdev_add_device; +@@ -2047,6 +2223,9 @@ clutter_device_manager_evdev_class_init (ClutterDeviceManagerEvdevClass *klass) + manager_class->get_supported_virtual_device_types = clutter_device_manager_evdev_get_supported_virtual_device_types; + manager_class->compress_motion = clutter_device_manager_evdev_compress_motion; + manager_class->apply_kbd_a11y_settings = clutter_device_manager_evdev_apply_kbd_a11y_settings; ++ ++ g_object_class_override_property (gobject_class, PROP_TOUCH_MODE, ++ "touch-mode"); + } + + static void +-- +2.29.2 + 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-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-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/geometric-picking.patch b/SOURCES/geometric-picking.patch new file mode 100644 index 0000000..6892dca --- /dev/null +++ b/SOURCES/geometric-picking.patch @@ -0,0 +1,2091 @@ +From ac946bf95ce3e4dc900f72dcb4189dd49bdb3155 Mon Sep 17 00:00:00 2001 +From: Daniel van Vugt +Date: Thu, 18 Jul 2019 16:56:41 +0800 +Subject: [PATCH 1/4] clutter/point: Add ClutterPoint quarilateral testing API + +Add a function to check whether a point is inside a quadrilateral +by checking the cross product of vectors with the quadrilateral +points, and the point being checked. + +If the passed quadrilateral is zero-sized, no point is ever reported +to be inside it. + +This will be used by the next commit when comparing the transformed +actor vertices. + +[feaneron: add a commit message and remove unecessary code] + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/189 +--- + clutter/clutter/clutter-base-types.c | 62 ++++++++++++++++++++++++++++ + clutter/clutter/clutter-types.h | 3 ++ + 2 files changed, 65 insertions(+) + +diff --git a/clutter/clutter/clutter-base-types.c b/clutter/clutter/clutter-base-types.c +index aeb25c90ef..c84f9aa64b 100644 +--- a/clutter/clutter/clutter-base-types.c ++++ b/clutter/clutter/clutter-base-types.c +@@ -570,6 +570,68 @@ G_DEFINE_BOXED_TYPE_WITH_CODE (ClutterPoint, clutter_point, + clutter_point_free, + CLUTTER_REGISTER_INTERVAL_PROGRESS (clutter_point_progress)) + ++static int ++clutter_point_compare_line (const ClutterPoint *p, ++ const ClutterPoint *a, ++ const ClutterPoint *b) ++{ ++ float x1 = b->x - a->x; ++ float y1 = b->y - a->y; ++ float x2 = p->x - a->x; ++ float y2 = p->y - a->y; ++ float cross_z = x1 * y2 - y1 * x2; ++ ++ if (cross_z > 0.f) ++ return 1; ++ else if (cross_z < 0.f) ++ return -1; ++ else ++ return 0; ++} ++ ++/** ++ * clutter_point_inside_quadrilateral: ++ * @point: a #ClutterPoint to test ++ * @vertices: array of vertices of the quadrilateral, in clockwise order, ++ * from top-left to bottom-left ++ * ++ * Determines whether a point is inside the convex quadrilateral provided, ++ * and not on any of its edges or vertices. ++ * ++ * Returns: %TRUE if @point is inside the quadrilateral ++ */ ++gboolean ++clutter_point_inside_quadrilateral (const ClutterPoint *point, ++ const ClutterPoint *vertices) ++{ ++ unsigned int i; ++ int first_side; ++ ++ first_side = 0; ++ ++ for (i = 0; i < 4; i++) ++ { ++ int side; ++ ++ side = clutter_point_compare_line (point, ++ &vertices[i], ++ &vertices[(i + 1) % 4]); ++ ++ if (side) ++ { ++ if (first_side == 0) ++ first_side = side; ++ else if (side != first_side) ++ return FALSE; ++ } ++ } ++ ++ if (first_side == 0) ++ return FALSE; ++ ++ return TRUE; ++} ++ + + + /* +diff --git a/clutter/clutter/clutter-types.h b/clutter/clutter/clutter-types.h +index 0f0fb1c2ac..0508028273 100644 +--- a/clutter/clutter/clutter-types.h ++++ b/clutter/clutter/clutter-types.h +@@ -200,6 +200,9 @@ float clutter_point_distance (const ClutterPoint *a, + const ClutterPoint *b, + float *x_distance, + float *y_distance); ++CLUTTER_EXPORT ++gboolean clutter_point_inside_quadrilateral (const ClutterPoint *point, ++ const ClutterPoint *vertices); + + /** + * ClutterSize: +-- +2.29.2 + + +From 8abac81711cfef8317bb675349d6b5b0a79eb05d Mon Sep 17 00:00:00 2001 +From: Daniel van Vugt +Date: Thu, 2 Aug 2018 19:03:30 +0800 +Subject: [PATCH 2/4] clutter: Introduce geometric picking + +Currently, Clutter does picking by drawing with Cogl and reading +the pixel that's beneath the given point. Since Cogl has a journal +that records drawing operations, and has optimizations to read a +single pixel from a list of rectangle, it would be expected that +we would hit this fast path and not flush the journal while picking. + +However, that's not the case: dithering, clipping with scissors, etc, +can all flush the journal, issuing commands to the GPU and making +picking slow. On NVidia-based systems, this glReadPixels() call is +extremely costly. + +Introduce geometric picking, and avoid using the Cogl journal entirely. +Do this by introducing a stack of actors in ClutterStage. This stack +is cached, but for now, don't use the cache as much as possible. + +The picking routines are still tied to painting. + +When projecting the actor vertexes, do it manually and take the modelview +matrix of the framebuffer into account as well. + +CPU usage on an Intel i7-7700, tested with two different GPUs/drivers: + + | | Intel | Nvidia | + | ------: | --------: | -----: | + | Moving the mouse: | + | Before | 10% | 10% | + | After | 6% | 6% | + | Moving a window: | + | Before | 23% | 81% | + | After | 19% | 40% | + +Closes: https://gitlab.gnome.org/GNOME/mutter/issues/154, + https://gitlab.gnome.org/GNOME/mutter/issues/691 + +Helps significantly with: https://gitlab.gnome.org/GNOME/mutter/issues/283, + https://gitlab.gnome.org/GNOME/mutter/issues/590, + https://gitlab.gnome.org/GNOME/mutter/issues/700 + +v2: Fix code style issues + Simplify quadrilateral checks + Remove the 0.5f hack + Differentiate axis-aligned rectangles + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/189 +--- + clutter/clutter/clutter-actor-private.h | 2 - + clutter/clutter/clutter-actor.c | 232 ++++++---- + clutter/clutter/clutter-actor.h | 4 + + clutter/clutter/clutter-debug.h | 1 - + clutter/clutter/clutter-main.c | 120 ----- + clutter/clutter/clutter-private.h | 5 - + clutter/clutter/clutter-stage-private.h | 16 +- + clutter/clutter/clutter-stage-window.c | 18 - + clutter/clutter/clutter-stage-window.h | 8 - + clutter/clutter/clutter-stage.c | 459 +++++++++++-------- + clutter/clutter/cogl/clutter-stage-cogl.c | 52 --- + clutter/clutter/deprecated/clutter-texture.c | 93 +--- + clutter/tests/conform/actor-pick.c | 99 +--- + clutter/tests/conform/meson.build | 1 - + clutter/tests/conform/texture.c | 84 ---- + src/compositor/meta-surface-actor.c | 27 +- + 16 files changed, 439 insertions(+), 782 deletions(-) + delete mode 100644 clutter/tests/conform/texture.c + +diff --git a/clutter/clutter/clutter-actor-private.h b/clutter/clutter/clutter-actor-private.h +index c44f6342fd..9bf1a30493 100644 +--- a/clutter/clutter/clutter-actor-private.h ++++ b/clutter/clutter/clutter-actor-private.h +@@ -297,8 +297,6 @@ const gchar * _clutter_actor_get_debug_name + void _clutter_actor_push_clone_paint (void); + void _clutter_actor_pop_clone_paint (void); + +-guint32 _clutter_actor_get_pick_id (ClutterActor *self); +- + void _clutter_actor_shader_pre_paint (ClutterActor *actor, + gboolean repeat); + void _clutter_actor_shader_post_paint (ClutterActor *actor); +diff --git a/clutter/clutter/clutter-actor.c b/clutter/clutter/clutter-actor.c +index 43093fe79d..01ffa51caa 100644 +--- a/clutter/clutter/clutter-actor.c ++++ b/clutter/clutter/clutter-actor.c +@@ -730,8 +730,6 @@ struct _ClutterActorPrivate + + gchar *name; /* a non-unique name, used for debugging */ + +- gint32 pick_id; /* per-stage unique id, used for picking */ +- + /* a back-pointer to the Pango context that we can use + * to create pre-configured PangoLayout + */ +@@ -1281,6 +1279,105 @@ clutter_actor_verify_map_state (ClutterActor *self) + + #endif /* CLUTTER_ENABLE_DEBUG */ + ++static gboolean ++_clutter_actor_transform_local_box_to_stage (ClutterActor *self, ++ ClutterStage *stage, ++ const ClutterActorBox *box, ++ ClutterPoint vertices[4]) ++{ ++ CoglFramebuffer *fb = cogl_get_draw_framebuffer (); ++ CoglMatrix stage_transform, inv_stage_transform; ++ CoglMatrix modelview, transform_to_stage; ++ int v; ++ ++ clutter_actor_get_transform (CLUTTER_ACTOR (stage), &stage_transform); ++ if (!cogl_matrix_get_inverse (&stage_transform, &inv_stage_transform)) ++ return FALSE; ++ cogl_framebuffer_get_modelview_matrix (fb, &modelview); ++ cogl_matrix_multiply (&transform_to_stage, &inv_stage_transform, &modelview); ++ ++ vertices[0].x = box->x1; ++ vertices[0].y = box->y1; ++ ++ vertices[1].x = box->x2; ++ vertices[1].y = box->y1; ++ ++ vertices[2].x = box->x2; ++ vertices[2].y = box->y2; ++ ++ vertices[3].x = box->x1; ++ vertices[3].y = box->y2; ++ ++ for (v = 0; v < 4; v++) ++ { ++ float z = 0.f; ++ float w = 1.f; ++ ++ cogl_matrix_transform_point (&transform_to_stage, ++ &vertices[v].x, ++ &vertices[v].y, ++ &z, ++ &w); ++ } ++ ++ return TRUE; ++} ++ ++/** ++ * clutter_actor_pick_box: ++ * @self: The #ClutterActor being "pick" painted. ++ * @box: A rectangle in the actor's own local coordinates. ++ * ++ * Logs (does a virtual paint of) a rectangle for picking. Note that @box is ++ * in the actor's own local coordinates, so is usually {0,0,width,height} ++ * to include the whole actor. That is unless the actor has a shaped input ++ * region in which case you may wish to log the (multiple) smaller rectangles ++ * that make up the input region. ++ */ ++void ++clutter_actor_pick_box (ClutterActor *self, ++ const ClutterActorBox *box) ++{ ++ ClutterStage *stage; ++ ClutterPoint vertices[4]; ++ ++ g_return_if_fail (CLUTTER_IS_ACTOR (self)); ++ g_return_if_fail (box != NULL); ++ ++ if (box->x1 >= box->x2 || box->y1 >= box->y2) ++ return; ++ ++ stage = CLUTTER_STAGE (_clutter_actor_get_stage_internal (self)); ++ ++ if (_clutter_actor_transform_local_box_to_stage (self, stage, box, vertices)) ++ clutter_stage_log_pick (stage, vertices, self); ++} ++ ++static gboolean ++_clutter_actor_push_pick_clip (ClutterActor *self, ++ const ClutterActorBox *clip) ++{ ++ ClutterStage *stage; ++ ClutterPoint vertices[4]; ++ ++ stage = CLUTTER_STAGE (_clutter_actor_get_stage_internal (self)); ++ ++ if (!_clutter_actor_transform_local_box_to_stage (self, stage, clip, vertices)) ++ return FALSE; ++ ++ clutter_stage_push_pick_clip (stage, vertices); ++ return TRUE; ++} ++ ++static void ++_clutter_actor_pop_pick_clip (ClutterActor *self) ++{ ++ ClutterActor *stage; ++ ++ stage = _clutter_actor_get_stage_internal (self); ++ clutter_stage_pop_pick_clip (CLUTTER_STAGE (stage)); ++} ++ + static void + clutter_actor_set_mapped (ClutterActor *self, + gboolean mapped) +@@ -1509,8 +1606,7 @@ clutter_actor_update_map_state (ClutterActor *self, + static void + clutter_actor_real_map (ClutterActor *self) + { +- ClutterActorPrivate *priv = self->priv; +- ClutterActor *stage, *iter; ++ ClutterActor *iter; + + g_assert (!CLUTTER_ACTOR_IS_MAPPED (self)); + +@@ -1521,13 +1617,6 @@ clutter_actor_real_map (ClutterActor *self) + + self->priv->needs_paint_volume_update = TRUE; + +- stage = _clutter_actor_get_stage_internal (self); +- priv->pick_id = _clutter_stage_acquire_pick_id (CLUTTER_STAGE (stage), self); +- +- CLUTTER_NOTE (ACTOR, "Pick id '%d' for actor '%s'", +- priv->pick_id, +- _clutter_actor_get_debug_name (self)); +- + clutter_actor_ensure_resource_scale (self); + + /* notify on parent mapped before potentially mapping +@@ -1632,11 +1721,6 @@ clutter_actor_real_unmap (ClutterActor *self) + + stage = CLUTTER_STAGE (_clutter_actor_get_stage_internal (self)); + +- if (stage != NULL) +- _clutter_stage_release_pick_id (stage, priv->pick_id); +- +- priv->pick_id = -1; +- + if (stage != NULL && + clutter_stage_get_key_focus (stage) == self) + { +@@ -2255,46 +2339,16 @@ static void + clutter_actor_real_pick (ClutterActor *self, + const ClutterColor *color) + { +- CoglFramebuffer *framebuffer = cogl_get_draw_framebuffer (); +- +- /* the default implementation is just to paint a rectangle +- * with the same size of the actor using the passed color +- */ + if (clutter_actor_should_pick_paint (self)) + { +- static CoglPipeline *default_pick_pipeline = NULL; +- ClutterActorBox box = { 0, }; +- CoglPipeline *pick_pipeline; +- float width, height; +- +- if (G_UNLIKELY (default_pick_pipeline == NULL)) +- { +- CoglContext *ctx = +- clutter_backend_get_cogl_context (clutter_get_default_backend ()); +- +- default_pick_pipeline = cogl_pipeline_new (ctx); +- } +- +- g_assert (default_pick_pipeline != NULL); +- pick_pipeline = cogl_pipeline_copy (default_pick_pipeline); ++ ClutterActorBox box = { ++ .x1 = 0, ++ .y1 = 0, ++ .x2 = clutter_actor_get_width (self), ++ .y2 = clutter_actor_get_height (self), ++ }; + +- clutter_actor_get_allocation_box (self, &box); +- +- width = box.x2 - box.x1; +- height = box.y2 - box.y1; +- +- cogl_pipeline_set_color4ub (pick_pipeline, +- color->red, +- color->green, +- color->blue, +- color->alpha); +- +- cogl_framebuffer_draw_rectangle (framebuffer, +- pick_pipeline, +- 0, 0, +- width, height); +- +- cogl_object_unref (pick_pipeline); ++ clutter_actor_pick_box (self, &box); + } + + /* XXX - this thoroughly sucks, but we need to maintain compatibility +@@ -3585,15 +3639,6 @@ _clutter_actor_update_last_paint_volume (ClutterActor *self) + priv->last_paint_volume_valid = TRUE; + } + +-guint32 +-_clutter_actor_get_pick_id (ClutterActor *self) +-{ +- if (self->priv->pick_id < 0) +- return 0; +- +- return self->priv->pick_id; +-} +- + /* This is the same as clutter_actor_add_effect except that it doesn't + queue a redraw and it doesn't notify on the effect property */ + static void +@@ -3826,6 +3871,7 @@ clutter_actor_paint (ClutterActor *self) + ClutterActorPrivate *priv; + ClutterPickMode pick_mode; + gboolean culling_inhibited; ++ ClutterActorBox clip; + gboolean clip_set = FALSE; + ClutterStage *stage; + +@@ -3919,24 +3965,38 @@ clutter_actor_paint (ClutterActor *self) + + if (priv->has_clip) + { +- CoglFramebuffer *fb = _clutter_stage_get_active_framebuffer (stage); +- cogl_framebuffer_push_rectangle_clip (fb, +- priv->clip.origin.x, +- priv->clip.origin.y, +- priv->clip.origin.x + priv->clip.size.width, +- priv->clip.origin.y + priv->clip.size.height); ++ clip.x1 = priv->clip.origin.x; ++ clip.y1 = priv->clip.origin.y; ++ clip.x2 = priv->clip.origin.x + priv->clip.size.width; ++ clip.y2 = priv->clip.origin.y + priv->clip.size.height; + clip_set = TRUE; + } + else if (priv->clip_to_allocation) + { +- CoglFramebuffer *fb = _clutter_stage_get_active_framebuffer (stage); +- gfloat width, height; ++ clip.x1 = 0.f; ++ clip.y1 = 0.f; ++ clip.x2 = priv->allocation.x2 - priv->allocation.x1; ++ clip.y2 = priv->allocation.y2 - priv->allocation.y1; ++ clip_set = TRUE; ++ } + +- width = priv->allocation.x2 - priv->allocation.x1; +- height = priv->allocation.y2 - priv->allocation.y1; ++ if (clip_set) ++ { ++ if (pick_mode == CLUTTER_PICK_NONE) ++ { ++ CoglFramebuffer *fb = _clutter_stage_get_active_framebuffer (stage); + +- cogl_framebuffer_push_rectangle_clip (fb, 0, 0, width, height); +- clip_set = TRUE; ++ cogl_framebuffer_push_rectangle_clip (fb, ++ clip.x1, ++ clip.y1, ++ clip.x2, ++ clip.y2); ++ } ++ else ++ { ++ if (!_clutter_actor_push_pick_clip (self, &clip)) ++ clip_set = FALSE; ++ } + } + + if (pick_mode == CLUTTER_PICK_NONE) +@@ -4020,9 +4080,16 @@ clutter_actor_paint (ClutterActor *self) + done: + if (clip_set) + { +- CoglFramebuffer *fb = _clutter_stage_get_active_framebuffer (stage); ++ if (pick_mode == CLUTTER_PICK_NONE) ++ { ++ CoglFramebuffer *fb = _clutter_stage_get_active_framebuffer (stage); + +- cogl_framebuffer_pop_clip (fb); ++ cogl_framebuffer_pop_clip (fb); ++ } ++ else ++ { ++ _clutter_actor_pop_pick_clip (self); ++ } + } + + cogl_pop_matrix (); +@@ -4093,11 +4160,12 @@ clutter_actor_continue_paint (ClutterActor *self) + { + ClutterColor col = { 0, }; + +- _clutter_id_to_color (_clutter_actor_get_pick_id (self), &col); +- +- /* Actor will then paint silhouette of itself in supplied +- * color. See clutter_stage_get_actor_at_pos() for where +- * picking is enabled. ++ /* The actor will log a silhouette of itself to the stage pick log. ++ * Note that the picking color is no longer used as the "log" instead ++ * keeps a weak pointer to the actor itself. But we keep the color ++ * parameter for now so as to maintain ABI compatibility. The color ++ * parameter can be removed when someone feels like breaking the ABI ++ * along with gnome-shell. + * + * XXX:2.0 - Call the pick() virtual directly + */ +@@ -8603,8 +8671,6 @@ clutter_actor_init (ClutterActor *self) + + self->priv = priv = clutter_actor_get_instance_private (self); + +- priv->pick_id = -1; +- + priv->opacity = 0xff; + priv->show_on_set_parent = TRUE; + priv->resource_scale = -1.0f; +diff --git a/clutter/clutter/clutter-actor.h b/clutter/clutter/clutter-actor.h +index 3e7a59ac0c..16b438ba64 100644 +--- a/clutter/clutter/clutter-actor.h ++++ b/clutter/clutter/clutter-actor.h +@@ -910,6 +910,10 @@ void clutter_actor_bind_model_with_properties + const char *first_model_property, + ...); + ++CLUTTER_EXPORT ++void clutter_actor_pick_box (ClutterActor *self, ++ const ClutterActorBox *box); ++ + G_END_DECLS + + #endif /* __CLUTTER_ACTOR_H__ */ +diff --git a/clutter/clutter/clutter-debug.h b/clutter/clutter/clutter-debug.h +index 2462385f65..7d170d2d54 100644 +--- a/clutter/clutter/clutter-debug.h ++++ b/clutter/clutter/clutter-debug.h +@@ -30,7 +30,6 @@ typedef enum + typedef enum + { + CLUTTER_DEBUG_NOP_PICKING = 1 << 0, +- CLUTTER_DEBUG_DUMP_PICK_BUFFERS = 1 << 1 + } ClutterPickDebugFlag; + + typedef enum +diff --git a/clutter/clutter/clutter-main.c b/clutter/clutter/clutter-main.c +index 645c8bceb6..11c221a65b 100644 +--- a/clutter/clutter/clutter-main.c ++++ b/clutter/clutter/clutter-main.c +@@ -131,7 +131,6 @@ static const GDebugKey clutter_debug_keys[] = { + + static const GDebugKey clutter_pick_debug_keys[] = { + { "nop-picking", CLUTTER_DEBUG_NOP_PICKING }, +- { "dump-pick-buffers", CLUTTER_DEBUG_DUMP_PICK_BUFFERS }, + }; + + static const GDebugKey clutter_paint_debug_keys[] = { +@@ -533,125 +532,6 @@ clutter_get_motion_events_enabled (void) + return _clutter_context_get_motion_events_enabled (); + } + +-void +-_clutter_id_to_color (guint id_, +- ClutterColor *col) +-{ +- ClutterMainContext *ctx; +- gint red, green, blue; +- +- ctx = _clutter_context_get_default (); +- +- if (ctx->fb_g_mask == 0) +- { +- /* Figure out framebuffer masks used for pick */ +- cogl_get_bitmasks (&ctx->fb_r_mask, +- &ctx->fb_g_mask, +- &ctx->fb_b_mask, NULL); +- +- ctx->fb_r_mask_used = ctx->fb_r_mask; +- ctx->fb_g_mask_used = ctx->fb_g_mask; +- ctx->fb_b_mask_used = ctx->fb_b_mask; +- +- /* XXX - describe what "fuzzy picking" is */ +- if (clutter_use_fuzzy_picking) +- { +- ctx->fb_r_mask_used--; +- ctx->fb_g_mask_used--; +- ctx->fb_b_mask_used--; +- } +- } +- +- /* compute the numbers we'll store in the components */ +- red = (id_ >> (ctx->fb_g_mask_used+ctx->fb_b_mask_used)) +- & (0xff >> (8-ctx->fb_r_mask_used)); +- green = (id_ >> ctx->fb_b_mask_used) +- & (0xff >> (8-ctx->fb_g_mask_used)); +- blue = (id_) +- & (0xff >> (8-ctx->fb_b_mask_used)); +- +- /* shift left bits a bit and add one, this circumvents +- * at least some potential rounding errors in GL/GLES +- * driver / hw implementation. +- */ +- if (ctx->fb_r_mask_used != ctx->fb_r_mask) +- red = red * 2; +- if (ctx->fb_g_mask_used != ctx->fb_g_mask) +- green = green * 2; +- if (ctx->fb_b_mask_used != ctx->fb_b_mask) +- blue = blue * 2; +- +- /* shift up to be full 8bit values */ +- red = (red << (8 - ctx->fb_r_mask)) | (0x7f >> (ctx->fb_r_mask_used)); +- green = (green << (8 - ctx->fb_g_mask)) | (0x7f >> (ctx->fb_g_mask_used)); +- blue = (blue << (8 - ctx->fb_b_mask)) | (0x7f >> (ctx->fb_b_mask_used)); +- +- col->red = red; +- col->green = green; +- col->blue = blue; +- col->alpha = 0xff; +- +- /* XXX: We rotate the nibbles of the colors here so that there is a +- * visible variation between colors of sequential actor identifiers; +- * otherwise pick buffers dumped to an image will pretty much just look +- * black. +- */ +- if (G_UNLIKELY (clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS)) +- { +- col->red = (col->red << 4) | (col->red >> 4); +- col->green = (col->green << 4) | (col->green >> 4); +- col->blue = (col->blue << 4) | (col->blue >> 4); +- } +-} +- +-guint +-_clutter_pixel_to_id (guchar pixel[4]) +-{ +- ClutterMainContext *ctx; +- gint red, green, blue; +- guint retval; +- +- ctx = _clutter_context_get_default (); +- +- /* reduce the pixel components to the number of bits actually used of the +- * 8bits. +- */ +- if (G_UNLIKELY (clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS)) +- { +- guchar tmp; +- +- /* XXX: In _clutter_id_to_color we rotated the nibbles of the colors so +- * that there is a visible variation between colors of sequential actor +- * identifiers (otherwise pick buffers dumped to an image will pretty +- * much just look black.) Here we reverse that rotation. +- */ +- tmp = ((pixel[0] << 4) | (pixel[0] >> 4)); +- red = tmp >> (8 - ctx->fb_r_mask); +- tmp = ((pixel[1] << 4) | (pixel[1] >> 4)); +- green = tmp >> (8 - ctx->fb_g_mask); +- tmp = ((pixel[2] << 4) | (pixel[2] >> 4)); +- blue = tmp >> (8 - ctx->fb_b_mask); +- } +- else +- { +- red = pixel[0] >> (8 - ctx->fb_r_mask); +- green = pixel[1] >> (8 - ctx->fb_g_mask); +- blue = pixel[2] >> (8 - ctx->fb_b_mask); +- } +- +- /* divide potentially by two if 'fuzzy' */ +- red = red >> (ctx->fb_r_mask - ctx->fb_r_mask_used); +- green = green >> (ctx->fb_g_mask - ctx->fb_g_mask_used); +- blue = blue >> (ctx->fb_b_mask - ctx->fb_b_mask_used); +- +- /* combine the correct per component values into the final id */ +- retval = blue +- + (green << ctx->fb_b_mask_used) +- + (red << (ctx->fb_b_mask_used + ctx->fb_g_mask_used)); +- +- return retval; +-} +- + static CoglPangoFontMap * + clutter_context_get_pango_fontmap (void) + { +diff --git a/clutter/clutter/clutter-private.h b/clutter/clutter/clutter-private.h +index 5a0fed85c9..f2f870b014 100644 +--- a/clutter/clutter/clutter-private.h ++++ b/clutter/clutter/clutter-private.h +@@ -210,11 +210,6 @@ gboolean _clutter_feature_init (GError **error); + gboolean _clutter_diagnostic_enabled (void); + void _clutter_diagnostic_message (const char *fmt, ...) G_GNUC_PRINTF (1, 2); + +-/* Picking code */ +-guint _clutter_pixel_to_id (guchar pixel[4]); +-void _clutter_id_to_color (guint id, +- ClutterColor *col); +- + void _clutter_set_sync_to_vblank (gboolean sync_to_vblank); + + /* use this function as the accumulator if you have a signal with +diff --git a/clutter/clutter/clutter-stage-private.h b/clutter/clutter/clutter-stage-private.h +index 42474687ad..51ae47af1d 100644 +--- a/clutter/clutter/clutter-stage-private.h ++++ b/clutter/clutter/clutter-stage-private.h +@@ -75,6 +75,15 @@ gint64 _clutter_stage_get_update_time (ClutterStage *stage); + void _clutter_stage_clear_update_time (ClutterStage *stage); + gboolean _clutter_stage_has_full_redraw_queued (ClutterStage *stage); + ++void clutter_stage_log_pick (ClutterStage *stage, ++ const ClutterPoint *vertices, ++ ClutterActor *actor); ++ ++void clutter_stage_push_pick_clip (ClutterStage *stage, ++ const ClutterPoint *vertices); ++ ++void clutter_stage_pop_pick_clip (ClutterStage *stage); ++ + ClutterActor *_clutter_stage_do_pick (ClutterStage *stage, + gint x, + gint y, +@@ -93,13 +102,6 @@ void _clutter_stage_queue_redraw_entry_invalidate (Clut + + CoglFramebuffer *_clutter_stage_get_active_framebuffer (ClutterStage *stage); + +-gint32 _clutter_stage_acquire_pick_id (ClutterStage *stage, +- ClutterActor *actor); +-void _clutter_stage_release_pick_id (ClutterStage *stage, +- gint32 pick_id); +-ClutterActor * _clutter_stage_get_actor_by_pick_id (ClutterStage *stage, +- gint32 pick_id); +- + void _clutter_stage_add_pointer_drag_actor (ClutterStage *stage, + ClutterInputDevice *device, + ClutterActor *actor); +diff --git a/clutter/clutter/clutter-stage-window.c b/clutter/clutter/clutter-stage-window.c +index e8fa976a7d..4c4ef9d643 100644 +--- a/clutter/clutter/clutter-stage-window.c ++++ b/clutter/clutter/clutter-stage-window.c +@@ -293,24 +293,6 @@ _clutter_stage_window_redraw (ClutterStageWindow *window) + iface->redraw (window); + } + +- +-void +-_clutter_stage_window_get_dirty_pixel (ClutterStageWindow *window, +- ClutterStageView *view, +- int *x, int *y) +-{ +- ClutterStageWindowInterface *iface; +- +- *x = 0; +- *y = 0; +- +- g_return_if_fail (CLUTTER_IS_STAGE_WINDOW (window)); +- +- iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window); +- if (iface->get_dirty_pixel) +- iface->get_dirty_pixel (window, view, x, y); +-} +- + gboolean + _clutter_stage_window_can_clip_redraws (ClutterStageWindow *window) + { +diff --git a/clutter/clutter/clutter-stage-window.h b/clutter/clutter/clutter-stage-window.h +index 6c3601745f..aa0c5f71cc 100644 +--- a/clutter/clutter/clutter-stage-window.h ++++ b/clutter/clutter/clutter-stage-window.h +@@ -68,10 +68,6 @@ struct _ClutterStageWindowInterface + + void (* redraw) (ClutterStageWindow *stage_window); + +- void (* get_dirty_pixel) (ClutterStageWindow *stage_window, +- ClutterStageView *view, +- int *x, int *y); +- + gboolean (* can_clip_redraws) (ClutterStageWindow *stage_window); + + GList *(* get_views) (ClutterStageWindow *stage_window); +@@ -119,10 +115,6 @@ void _clutter_stage_window_set_accept_focus (ClutterStageWin + + void _clutter_stage_window_redraw (ClutterStageWindow *window); + +-void _clutter_stage_window_get_dirty_pixel (ClutterStageWindow *window, +- ClutterStageView *view, +- int *x, int *y); +- + gboolean _clutter_stage_window_can_clip_redraws (ClutterStageWindow *window); + + GList * _clutter_stage_window_get_views (ClutterStageWindow *window); +diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c +index aaa77d9ede..7d88d5752f 100644 +--- a/clutter/clutter/clutter-stage.c ++++ b/clutter/clutter/clutter-stage.c +@@ -97,6 +97,11 @@ typedef enum /*< prefix=CLUTTER_STAGE >*/ + + #define STAGE_NO_CLEAR_ON_PAINT(s) ((((ClutterStage *) (s))->priv->stage_hints & CLUTTER_STAGE_NO_CLEAR_ON_PAINT) != 0) + ++#ifndef G_APPROX_VALUE ++#define G_APPROX_VALUE(a, b, epsilon) \ ++ (((a) > (b) ? (a) - (b) : (b) - (a)) < (epsilon)) ++#endif ++ + struct _ClutterStageQueueRedrawEntry + { + ClutterActor *actor; +@@ -104,6 +109,19 @@ struct _ClutterStageQueueRedrawEntry + ClutterPaintVolume clip; + }; + ++typedef struct _PickRecord ++{ ++ ClutterPoint vertex[4]; ++ ClutterActor *actor; ++ int clip_stack_top; ++} PickRecord; ++ ++typedef struct _PickClipRecord ++{ ++ int prev; ++ ClutterPoint vertex[4]; ++} PickClipRecord; ++ + struct _ClutterStagePrivate + { + /* the stage implementation */ +@@ -137,7 +155,11 @@ struct _ClutterStagePrivate + GTimer *fps_timer; + gint32 timer_n_frames; + +- ClutterIDPool *pick_id_pool; ++ GArray *pick_stack; ++ GArray *pick_clip_stack; ++ int pick_clip_stack_top; ++ gboolean pick_stack_frozen; ++ ClutterPickMode cached_pick_mode; + + #ifdef CLUTTER_ENABLE_DEBUG + gulong redraw_count; +@@ -326,6 +348,211 @@ clutter_stage_get_preferred_height (ClutterActor *self, + *natural_height_p = geom.height; + } + ++static void ++add_pick_stack_weak_refs (ClutterStage *stage) ++{ ++ ClutterStagePrivate *priv = stage->priv; ++ int i; ++ ++ if (priv->pick_stack_frozen) ++ return; ++ ++ for (i = 0; i < priv->pick_stack->len; i++) ++ { ++ PickRecord *rec = &g_array_index (priv->pick_stack, PickRecord, i); ++ ++ if (rec->actor) ++ g_object_add_weak_pointer (G_OBJECT (rec->actor), ++ (gpointer) &rec->actor); ++ } ++ ++ priv->pick_stack_frozen = TRUE; ++} ++ ++static void ++remove_pick_stack_weak_refs (ClutterStage *stage) ++{ ++ ClutterStagePrivate *priv = stage->priv; ++ int i; ++ ++ if (!priv->pick_stack_frozen) ++ return; ++ ++ for (i = 0; i < priv->pick_stack->len; i++) ++ { ++ PickRecord *rec = &g_array_index (priv->pick_stack, PickRecord, i); ++ ++ if (rec->actor) ++ g_object_remove_weak_pointer (G_OBJECT (rec->actor), ++ (gpointer) &rec->actor); ++ } ++ ++ priv->pick_stack_frozen = FALSE; ++} ++ ++static void ++_clutter_stage_clear_pick_stack (ClutterStage *stage) ++{ ++ ClutterStagePrivate *priv = stage->priv; ++ ++ remove_pick_stack_weak_refs (stage); ++ g_array_set_size (priv->pick_stack, 0); ++ g_array_set_size (priv->pick_clip_stack, 0); ++ priv->pick_clip_stack_top = -1; ++ priv->cached_pick_mode = CLUTTER_PICK_NONE; ++} ++ ++void ++clutter_stage_log_pick (ClutterStage *stage, ++ const ClutterPoint *vertices, ++ ClutterActor *actor) ++{ ++ ClutterStagePrivate *priv; ++ PickRecord rec; ++ ++ g_return_if_fail (CLUTTER_IS_STAGE (stage)); ++ g_return_if_fail (actor != NULL); ++ ++ priv = stage->priv; ++ ++ g_assert (!priv->pick_stack_frozen); ++ ++ memcpy (rec.vertex, vertices, 4 * sizeof (ClutterPoint)); ++ rec.actor = actor; ++ rec.clip_stack_top = priv->pick_clip_stack_top; ++ ++ g_array_append_val (priv->pick_stack, rec); ++} ++ ++void ++clutter_stage_push_pick_clip (ClutterStage *stage, ++ const ClutterPoint *vertices) ++{ ++ ClutterStagePrivate *priv; ++ PickClipRecord clip; ++ ++ g_return_if_fail (CLUTTER_IS_STAGE (stage)); ++ ++ priv = stage->priv; ++ ++ g_assert (!priv->pick_stack_frozen); ++ ++ clip.prev = priv->pick_clip_stack_top; ++ memcpy (clip.vertex, vertices, 4 * sizeof (ClutterPoint)); ++ ++ g_array_append_val (priv->pick_clip_stack, clip); ++ priv->pick_clip_stack_top = priv->pick_clip_stack->len - 1; ++} ++ ++void ++clutter_stage_pop_pick_clip (ClutterStage *stage) ++{ ++ ClutterStagePrivate *priv; ++ const PickClipRecord *top; ++ ++ g_return_if_fail (CLUTTER_IS_STAGE (stage)); ++ ++ priv = stage->priv; ++ ++ g_assert (!priv->pick_stack_frozen); ++ g_assert (priv->pick_clip_stack_top >= 0); ++ ++ /* Individual elements of pick_clip_stack are not freed. This is so they ++ * can be shared as part of a tree of different stacks used by different ++ * actors in the pick_stack. The whole pick_clip_stack does however get ++ * freed later in _clutter_stage_clear_pick_stack. ++ */ ++ ++ top = &g_array_index (priv->pick_clip_stack, ++ PickClipRecord, ++ priv->pick_clip_stack_top); ++ ++ priv->pick_clip_stack_top = top->prev; ++} ++ ++static gboolean ++is_quadrilateral_axis_aligned_rectangle (const ClutterPoint *vertices) ++{ ++ int i; ++ ++ for (i = 0; i < 4; i++) ++ { ++ if (!G_APPROX_VALUE (vertices[i].x, ++ vertices[(i + 1) % 4].x, ++ FLT_EPSILON) && ++ !G_APPROX_VALUE (vertices[i].y, ++ vertices[(i + 1) % 4].y, ++ FLT_EPSILON)) ++ return FALSE; ++ } ++ return TRUE; ++} ++ ++static gboolean ++is_inside_axis_aligned_rectangle (const ClutterPoint *point, ++ const ClutterPoint *vertices) ++{ ++ float min_x = FLT_MAX; ++ float max_x = FLT_MIN; ++ float min_y = FLT_MAX; ++ float max_y = FLT_MIN; ++ int i; ++ ++ for (i = 0; i < 3; i++) ++ { ++ min_x = MIN (min_x, vertices[i].x); ++ min_y = MIN (min_y, vertices[i].y); ++ max_x = MAX (max_x, vertices[i].x); ++ max_y = MAX (max_y, vertices[i].y); ++ } ++ ++ return (point->x >= min_x && ++ point->y >= min_y && ++ point->x < max_x && ++ point->y < max_y); ++} ++ ++static gboolean ++is_inside_input_region (const ClutterPoint *point, ++ const ClutterPoint *vertices) ++{ ++ ++ if (is_quadrilateral_axis_aligned_rectangle (vertices)) ++ return is_inside_axis_aligned_rectangle (point, vertices); ++ else ++ return clutter_point_inside_quadrilateral (point, vertices); ++} ++ ++static gboolean ++pick_record_contains_pixel (ClutterStage *stage, ++ const PickRecord *rec, ++ int x, ++ int y) ++{ ++ const ClutterPoint point = CLUTTER_POINT_INIT (x, y); ++ ClutterStagePrivate *priv; ++ int clip_index; ++ ++ if (!is_inside_input_region (&point, rec->vertex)) ++ return FALSE; ++ ++ priv = stage->priv; ++ clip_index = rec->clip_stack_top; ++ while (clip_index >= 0) ++ { ++ const PickClipRecord *clip = &g_array_index (priv->pick_clip_stack, ++ PickClipRecord, ++ clip_index); ++ ++ if (!is_inside_input_region (&point, clip->vertex)) ++ return FALSE; ++ ++ clip_index = clip->prev; ++ } ++ ++ return TRUE; ++} ++ + static inline void + queue_full_redraw (ClutterStage *stage) + { +@@ -636,6 +863,12 @@ clutter_stage_do_paint_view (ClutterStage *stage, + float viewport[4]; + cairo_rectangle_int_t geom; + ++ /* Any mode of painting/picking invalidates the pick cache, unless we're ++ * in the middle of building it. So we reset the cached flag but don't ++ * completely clear the pick stack. ++ */ ++ priv->cached_pick_mode = CLUTTER_PICK_NONE; ++ + _clutter_stage_window_get_geometry (priv->impl, &geom); + + viewport[0] = priv->viewport[0]; +@@ -1414,40 +1647,6 @@ clutter_stage_get_redraw_clip_bounds (ClutterStage *stage, + } + } + +-static void +-read_pixels_to_file (CoglFramebuffer *fb, +- char *filename_stem, +- int x, +- int y, +- int width, +- int height) +-{ +- guint8 *data; +- cairo_surface_t *surface; +- static int read_count = 0; +- char *filename = g_strdup_printf ("%s-%05d.png", +- filename_stem, +- read_count); +- +- data = g_malloc (4 * width * height); +- cogl_framebuffer_read_pixels (fb, +- x, y, width, height, +- CLUTTER_CAIRO_FORMAT_ARGB32, +- data); +- +- surface = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_RGB24, +- width, height, +- width * 4); +- +- cairo_surface_write_to_png (surface, filename); +- cairo_surface_destroy (surface); +- +- g_free (data); +- g_free (filename); +- +- read_count++; +-} +- + static ClutterActor * + _clutter_stage_do_pick_on_view (ClutterStage *stage, + gint x, +@@ -1455,140 +1654,42 @@ _clutter_stage_do_pick_on_view (ClutterStage *stage, + ClutterPickMode mode, + ClutterStageView *view) + { +- ClutterActor *actor = CLUTTER_ACTOR (stage); ++ ClutterMainContext *context = _clutter_context_get_default (); + ClutterStagePrivate *priv = stage->priv; + CoglFramebuffer *fb = clutter_stage_view_get_framebuffer (view); +- cairo_rectangle_int_t view_layout; +- ClutterMainContext *context; +- guchar pixel[4] = { 0xff, 0xff, 0xff, 0xff }; +- CoglColor stage_pick_id; +- gboolean dither_enabled_save; +- ClutterActor *retval; +- gint dirty_x; +- gint dirty_y; +- gint read_x; +- gint read_y; +- float fb_width, fb_height; +- float fb_scale; +- float viewport_offset_x; +- float viewport_offset_y; +- +- priv = stage->priv; +- +- context = _clutter_context_get_default (); +- fb_scale = clutter_stage_view_get_scale (view); +- clutter_stage_view_get_layout (view, &view_layout); +- +- fb_width = view_layout.width * fb_scale; +- fb_height = view_layout.height * fb_scale; +- cogl_push_framebuffer (fb); +- +- /* needed for when a context switch happens */ +- _clutter_stage_maybe_setup_viewport (stage, view); +- +- /* FIXME: For some reason leaving the cogl clip stack empty causes the +- * picking to not work at all, so setting it the whole framebuffer content +- * for now. */ +- cogl_framebuffer_push_scissor_clip (fb, 0, 0, +- view_layout.width * fb_scale, +- view_layout.height * fb_scale); +- +- _clutter_stage_window_get_dirty_pixel (priv->impl, view, &dirty_x, &dirty_y); ++ int i; + +- if (G_LIKELY (!(clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS))) +- { +- CLUTTER_NOTE (PICK, "Pushing pick scissor clip x: %d, y: %d, 1x1", +- (int) (dirty_x * fb_scale), +- (int) (dirty_y * fb_scale)); +- cogl_framebuffer_push_scissor_clip (fb, dirty_x * fb_scale, dirty_y * fb_scale, 1, 1); +- } ++ g_assert (context->pick_mode == CLUTTER_PICK_NONE); + +- viewport_offset_x = x * fb_scale - dirty_x * fb_scale; +- viewport_offset_y = y * fb_scale - dirty_y * fb_scale; +- CLUTTER_NOTE (PICK, "Setting viewport to %f, %f, %f, %f", +- priv->viewport[0] * fb_scale - viewport_offset_x, +- priv->viewport[1] * fb_scale - viewport_offset_y, +- priv->viewport[2] * fb_scale, +- priv->viewport[3] * fb_scale); +- cogl_framebuffer_set_viewport (fb, +- priv->viewport[0] * fb_scale - viewport_offset_x, +- priv->viewport[1] * fb_scale - viewport_offset_y, +- priv->viewport[2] * fb_scale, +- priv->viewport[3] * fb_scale); +- +- read_x = dirty_x * fb_scale; +- read_y = dirty_y * fb_scale; +- +- CLUTTER_NOTE (PICK, "Performing pick at %i,%i on view %dx%d+%d+%d s: %f", +- x, y, +- view_layout.width, view_layout.height, +- view_layout.x, view_layout.y, fb_scale); +- +- cogl_color_init_from_4ub (&stage_pick_id, 255, 255, 255, 255); +- cogl_framebuffer_clear (fb, COGL_BUFFER_BIT_COLOR | COGL_BUFFER_BIT_DEPTH, &stage_pick_id); +- +- /* Disable dithering (if any) when doing the painting in pick mode */ +- dither_enabled_save = cogl_framebuffer_get_dither_enabled (fb); +- cogl_framebuffer_set_dither_enabled (fb, FALSE); +- +- /* Render the entire scence in pick mode - just single colored silhouette's +- * are drawn offscreen (as we never swap buffers) +- */ +- context->pick_mode = mode; +- +- clutter_stage_do_paint_view (stage, view, NULL); +- context->pick_mode = CLUTTER_PICK_NONE; +- +- /* Read the color of the screen co-ords pixel. RGBA_8888_PRE is used +- even though we don't care about the alpha component because under +- GLES this is the only format that is guaranteed to work so Cogl +- will end up having to do a conversion if any other format is +- used. The format is requested as pre-multiplied because Cogl +- assumes that all pixels in the framebuffer are premultiplied so +- it avoids a conversion. */ +- cogl_framebuffer_read_pixels (fb, +- read_x, read_y, 1, 1, +- COGL_PIXEL_FORMAT_RGBA_8888_PRE, +- pixel); +- +- if (G_UNLIKELY (clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS)) ++ if (mode != priv->cached_pick_mode) + { +- char *file_name = +- g_strdup_printf ("pick-buffer-%s-view-x-%d", +- _clutter_actor_get_debug_name (actor), +- view_layout.x); ++ _clutter_stage_clear_pick_stack (stage); + +- read_pixels_to_file (fb, file_name, 0, 0, fb_width, fb_height); ++ cogl_push_framebuffer (fb); + +- g_free (file_name); +- } +- +- /* Restore whether GL_DITHER was enabled */ +- cogl_framebuffer_set_dither_enabled (fb, dither_enabled_save); +- +- if (G_LIKELY (!(clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS))) +- cogl_framebuffer_pop_clip (fb); ++ context->pick_mode = mode; ++ clutter_stage_do_paint_view (stage, view, NULL); ++ context->pick_mode = CLUTTER_PICK_NONE; ++ priv->cached_pick_mode = mode; + +- cogl_framebuffer_pop_clip (fb); ++ cogl_pop_framebuffer (); + +- _clutter_stage_dirty_viewport (stage); ++ add_pick_stack_weak_refs (stage); ++ } + +- if (pixel[0] == 0xff && pixel[1] == 0xff && pixel[2] == 0xff) +- retval = actor; +- else ++ /* Search all "painted" pickable actors from front to back. A linear search ++ * is required, and also performs fine since there is typically only ++ * on the order of dozens of actors in the list (on screen) at a time. ++ */ ++ for (i = priv->pick_stack->len - 1; i >= 0; i--) + { +- guint32 id_ = _clutter_pixel_to_id (pixel); ++ const PickRecord *rec = &g_array_index (priv->pick_stack, PickRecord, i); + +- retval = _clutter_stage_get_actor_by_pick_id (stage, id_); +- CLUTTER_NOTE (PICK, "Picking actor %s with id %u (pixel: 0x%x%x%x%x", +- G_OBJECT_TYPE_NAME (retval), +- id_, +- pixel[0], pixel[1], pixel[2], pixel[3]); ++ if (rec->actor && pick_record_contains_pixel (stage, rec, x, y)) ++ return rec->actor; + } + +- cogl_pop_framebuffer (); +- +- return retval; ++ return CLUTTER_ACTOR (stage); + } + + static ClutterStageView * +@@ -1901,7 +2002,9 @@ clutter_stage_finalize (GObject *object) + + g_array_free (priv->paint_volume_stack, TRUE); + +- _clutter_id_pool_free (priv->pick_id_pool); ++ _clutter_stage_clear_pick_stack (stage); ++ g_array_free (priv->pick_clip_stack, TRUE); ++ g_array_free (priv->pick_stack, TRUE); + + if (priv->fps_timer != NULL) + g_timer_destroy (priv->fps_timer); +@@ -2435,7 +2538,10 @@ clutter_stage_init (ClutterStage *self) + priv->paint_volume_stack = + g_array_new (FALSE, FALSE, sizeof (ClutterPaintVolume)); + +- priv->pick_id_pool = _clutter_id_pool_new (256); ++ priv->pick_stack = g_array_new (FALSE, FALSE, sizeof (PickRecord)); ++ priv->pick_clip_stack = g_array_new (FALSE, FALSE, sizeof (PickClipRecord)); ++ priv->pick_clip_stack_top = -1; ++ priv->cached_pick_mode = CLUTTER_PICK_NONE; + } + + /** +@@ -4253,6 +4359,12 @@ _clutter_stage_queue_actor_redraw (ClutterStage *stage, + CLUTTER_NOTE (CLIPPING, "stage_queue_actor_redraw (actor=%s, clip=%p): ", + _clutter_actor_get_debug_name (actor), clip); + ++ /* Queuing a redraw or clip change invalidates the pick cache, unless we're ++ * in the middle of building it. So we reset the cached flag but don't ++ * completely clear the pick stack... ++ */ ++ priv->cached_pick_mode = CLUTTER_PICK_NONE; ++ + if (!priv->redraw_pending) + { + ClutterMasterClock *master_clock; +@@ -4513,39 +4625,6 @@ _clutter_stage_get_active_framebuffer (ClutterStage *stage) + return stage->priv->active_framebuffer; + } + +-gint32 +-_clutter_stage_acquire_pick_id (ClutterStage *stage, +- ClutterActor *actor) +-{ +- ClutterStagePrivate *priv = stage->priv; +- +- g_assert (priv->pick_id_pool != NULL); +- +- return _clutter_id_pool_add (priv->pick_id_pool, actor); +-} +- +-void +-_clutter_stage_release_pick_id (ClutterStage *stage, +- gint32 pick_id) +-{ +- ClutterStagePrivate *priv = stage->priv; +- +- g_assert (priv->pick_id_pool != NULL); +- +- _clutter_id_pool_remove (priv->pick_id_pool, pick_id); +-} +- +-ClutterActor * +-_clutter_stage_get_actor_by_pick_id (ClutterStage *stage, +- gint32 pick_id) +-{ +- ClutterStagePrivate *priv = stage->priv; +- +- g_assert (priv->pick_id_pool != NULL); +- +- return _clutter_id_pool_lookup (priv->pick_id_pool, pick_id); +-} +- + void + _clutter_stage_add_pointer_drag_actor (ClutterStage *stage, + ClutterInputDevice *device, +diff --git a/clutter/clutter/cogl/clutter-stage-cogl.c b/clutter/clutter/cogl/clutter-stage-cogl.c +index 3f1f609c4e..effed79759 100644 +--- a/clutter/clutter/cogl/clutter-stage-cogl.c ++++ b/clutter/clutter/cogl/clutter-stage-cogl.c +@@ -926,57 +926,6 @@ clutter_stage_cogl_redraw (ClutterStageWindow *stage_window) + stage_cogl->frame_count++; + } + +-static void +-clutter_stage_cogl_get_dirty_pixel (ClutterStageWindow *stage_window, +- ClutterStageView *view, +- int *x, +- 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 (); +- float fb_scale; +- gboolean scale_is_fractional; +- +- fb_scale = clutter_stage_view_get_scale (view); +- if (fb_scale != floorf (fb_scale)) +- scale_is_fractional = TRUE; +- else +- scale_is_fractional = FALSE; +- +- /* +- * Buffer damage is tracked in the framebuffer coordinate space +- * using the damage history. When fractional scaling is used, a +- * coordinate on the stage might not correspond to the exact position of any +- * physical pixel, which causes issues when painting using the pick mode. +- * +- * For now, always use the (0, 0) pixel for picking when using fractional +- * framebuffer scaling. +- */ +- if (!has_buffer_age || +- scale_is_fractional || +- !clutter_damage_history_is_age_valid (view_priv->damage_history, 0)) +- { +- *x = 0; +- *y = 0; +- } +- else +- { +- cairo_rectangle_int_t view_layout; +- const cairo_rectangle_int_t *fb_damage; +- +- clutter_stage_view_get_layout (view, &view_layout); +- +- fb_damage = clutter_damage_history_lookup (view_priv->damage_history, 0); +- *x = fb_damage->x / fb_scale; +- *y = fb_damage->y / fb_scale; +- } +-} +- + static void + clutter_stage_window_iface_init (ClutterStageWindowInterface *iface) + { +@@ -994,7 +943,6 @@ clutter_stage_window_iface_init (ClutterStageWindowInterface *iface) + iface->ignoring_redraw_clips = clutter_stage_cogl_ignoring_redraw_clips; + iface->get_redraw_clip_bounds = clutter_stage_cogl_get_redraw_clip_bounds; + iface->redraw = clutter_stage_cogl_redraw; +- iface->get_dirty_pixel = clutter_stage_cogl_get_dirty_pixel; + } + + static void +diff --git a/clutter/clutter/deprecated/clutter-texture.c b/clutter/clutter/deprecated/clutter-texture.c +index bea239f454..2c677b8a44 100644 +--- a/clutter/clutter/deprecated/clutter-texture.c ++++ b/clutter/clutter/deprecated/clutter-texture.c +@@ -572,83 +572,6 @@ gen_texcoords_and_draw_cogl_rectangle (ClutterActor *self, + 0, 0, t_w, t_h); + } + +-static CoglPipeline * +-create_pick_pipeline (ClutterActor *self) +-{ +- ClutterTexture *texture = CLUTTER_TEXTURE (self); +- ClutterTexturePrivate *priv = texture->priv; +- CoglPipeline *pick_pipeline = cogl_pipeline_copy (texture_template_pipeline); +- GError *error = NULL; +- +- if (!cogl_pipeline_set_layer_combine (pick_pipeline, 0, +- "RGBA = " +- " MODULATE (CONSTANT, TEXTURE[A])", +- &error)) +- { +- if (!priv->seen_create_pick_pipeline_warning) +- g_warning ("Error setting up texture combine for shaped " +- "texture picking: %s", error->message); +- priv->seen_create_pick_pipeline_warning = TRUE; +- g_error_free (error); +- cogl_object_unref (pick_pipeline); +- return NULL; +- } +- +- cogl_pipeline_set_blend (pick_pipeline, +- "RGBA = ADD (SRC_COLOR[RGBA], 0)", +- NULL); +- +- cogl_pipeline_set_alpha_test_function (pick_pipeline, +- COGL_PIPELINE_ALPHA_FUNC_EQUAL, +- 1.0); +- +- return pick_pipeline; +-} +- +-static void +-clutter_texture_pick (ClutterActor *self, +- const ClutterColor *color) +-{ +- ClutterTexture *texture = CLUTTER_TEXTURE (self); +- ClutterTexturePrivate *priv = texture->priv; +- CoglFramebuffer *framebuffer = cogl_get_draw_framebuffer (); +- +- if (!clutter_actor_should_pick_paint (self)) +- return; +- +- if (G_LIKELY (priv->pick_with_alpha_supported) && priv->pick_with_alpha) +- { +- CoglColor pick_color; +- +- if (priv->pick_pipeline == NULL) +- priv->pick_pipeline = create_pick_pipeline (self); +- +- if (priv->pick_pipeline == NULL) +- { +- priv->pick_with_alpha_supported = FALSE; +- CLUTTER_ACTOR_CLASS (clutter_texture_parent_class)->pick (self, +- color); +- return; +- } +- +- if (priv->fbo_handle != NULL) +- update_fbo (self); +- +- cogl_color_init_from_4ub (&pick_color, +- color->red, +- color->green, +- color->blue, +- 0xff); +- cogl_pipeline_set_layer_combine_constant (priv->pick_pipeline, +- 0, &pick_color); +- cogl_pipeline_set_layer_texture (priv->pick_pipeline, 0, +- clutter_texture_get_cogl_texture (texture)); +- gen_texcoords_and_draw_cogl_rectangle (self, priv->pick_pipeline, framebuffer); +- } +- else +- CLUTTER_ACTOR_CLASS (clutter_texture_parent_class)->pick (self, color); +-} +- + static void + clutter_texture_paint (ClutterActor *self) + { +@@ -767,12 +690,6 @@ clutter_texture_dispose (GObject *object) + priv->pipeline = NULL; + } + +- if (priv->pick_pipeline != NULL) +- { +- cogl_object_unref (priv->pick_pipeline); +- priv->pick_pipeline = NULL; +- } +- + G_OBJECT_CLASS (clutter_texture_parent_class)->dispose (object); + } + +@@ -944,7 +861,6 @@ clutter_texture_class_init (ClutterTextureClass *klass) + GParamSpec *pspec; + + actor_class->paint = clutter_texture_paint; +- actor_class->pick = clutter_texture_pick; + actor_class->get_paint_volume = clutter_texture_get_paint_volume; + actor_class->realize = clutter_texture_realize; + actor_class->unrealize = clutter_texture_unrealize; +@@ -1263,11 +1179,9 @@ clutter_texture_init (ClutterTexture *self) + priv->repeat_y = FALSE; + priv->sync_actor_size = TRUE; + priv->fbo_handle = NULL; +- priv->pick_pipeline = NULL; + priv->keep_aspect_ratio = FALSE; + priv->pick_with_alpha = FALSE; + priv->pick_with_alpha_supported = TRUE; +- priv->seen_create_pick_pipeline_warning = FALSE; + + if (G_UNLIKELY (texture_template_pipeline == NULL)) + { +@@ -3052,13 +2966,8 @@ clutter_texture_set_pick_with_alpha (ClutterTexture *texture, + if (priv->pick_with_alpha == pick_with_alpha) + return; + +- if (!pick_with_alpha && priv->pick_pipeline != NULL) +- { +- cogl_object_unref (priv->pick_pipeline); +- priv->pick_pipeline = NULL; +- } ++ g_assert (!pick_with_alpha); /* No longer supported */ + +- /* NB: the pick pipeline is created lazily when we first pick */ + priv->pick_with_alpha = pick_with_alpha; + + /* NB: actors are expected to call clutter_actor_queue_redraw when +diff --git a/clutter/tests/conform/actor-pick.c b/clutter/tests/conform/actor-pick.c +index 969b4920ac..2bf5954c73 100644 +--- a/clutter/tests/conform/actor-pick.c ++++ b/clutter/tests/conform/actor-pick.c +@@ -5,7 +5,6 @@ + #define STAGE_HEIGHT 480 + #define ACTORS_X 12 + #define ACTORS_Y 16 +-#define SHIFT_STEP STAGE_WIDTH / ACTORS_X + + typedef struct _State State; + +@@ -20,84 +19,11 @@ struct _State + gboolean pass; + }; + +-struct _ShiftEffect +-{ +- ClutterShaderEffect parent_instance; +-}; +- +-struct _ShiftEffectClass +-{ +- ClutterShaderEffectClass parent_class; +-}; +- +-typedef struct _ShiftEffect ShiftEffect; +-typedef struct _ShiftEffectClass ShiftEffectClass; +- +-#define TYPE_SHIFT_EFFECT (shift_effect_get_type ()) +- +-GType shift_effect_get_type (void); +- +-G_DEFINE_TYPE (ShiftEffect, +- shift_effect, +- CLUTTER_TYPE_SHADER_EFFECT); +- +-static void +-shader_paint (ClutterEffect *effect, +- ClutterEffectPaintFlags flags) +-{ +- ClutterShaderEffect *shader = CLUTTER_SHADER_EFFECT (effect); +- float tex_width; +- ClutterActor *actor = +- clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (effect)); +- +- if (g_test_verbose ()) +- g_debug ("shader_paint"); +- +- clutter_shader_effect_set_shader_source (shader, +- "uniform sampler2D tex;\n" +- "uniform float step;\n" +- "void main (void)\n" +- "{\n" +- " cogl_color_out = texture2D(tex, vec2 (cogl_tex_coord_in[0].s + step,\n" +- " cogl_tex_coord_in[0].t));\n" +- "}\n"); +- +- tex_width = clutter_actor_get_width (actor); +- +- clutter_shader_effect_set_uniform (shader, "tex", G_TYPE_INT, 1, 0); +- clutter_shader_effect_set_uniform (shader, "step", G_TYPE_FLOAT, 1, +- SHIFT_STEP / tex_width); +- +- CLUTTER_EFFECT_CLASS (shift_effect_parent_class)->paint (effect, flags); +-} +- +-static void +-shader_pick (ClutterEffect *effect, +- ClutterEffectPaintFlags flags) +-{ +- shader_paint (effect, flags); +-} +- +-static void +-shift_effect_class_init (ShiftEffectClass *klass) +-{ +- ClutterEffectClass *shader_class = CLUTTER_EFFECT_CLASS (klass); +- +- shader_class->paint = shader_paint; +- shader_class->pick = shader_pick; +-} +- +-static void +-shift_effect_init (ShiftEffect *self) +-{ +-} +- + static const char *test_passes[] = { + "No covering actor", + "Invisible covering actor", + "Clipped covering actor", + "Blur effect", +- "Shift effect", + }; + + static gboolean +@@ -165,30 +91,10 @@ on_timeout (gpointer data) + if (g_test_verbose ()) + g_print ("With blur effect:\n"); + } +- else if (test_num == 4) +- { +- if (!clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL)) +- continue; +- +- clutter_actor_hide (over_actor); +- clutter_actor_remove_effect_by_name (CLUTTER_ACTOR (state->stage), +- "blur"); +- +- clutter_actor_add_effect_with_name (CLUTTER_ACTOR (state->stage), +- "shift", +- g_object_new (TYPE_SHIFT_EFFECT, +- NULL)); +- +- if (g_test_verbose ()) +- g_print ("With shift effect:\n"); +- } + + for (y = 0; y < ACTORS_Y; y++) + { +- if (test_num == 4) +- x = 1; +- else +- x = 0; ++ x = 0; + + for (; x < ACTORS_X; x++) + { +@@ -198,9 +104,6 @@ on_timeout (gpointer data) + + pick_x = x * state->actor_width + state->actor_width / 2; + +- if (test_num == 4) +- pick_x -= SHIFT_STEP; +- + actor = + clutter_stage_get_actor_at_pos (CLUTTER_STAGE (state->stage), + CLUTTER_PICK_ALL, +diff --git a/clutter/tests/conform/meson.build b/clutter/tests/conform/meson.build +index a9f2d7e20c..fffc9014c4 100644 +--- a/clutter/tests/conform/meson.build ++++ b/clutter/tests/conform/meson.build +@@ -42,7 +42,6 @@ clutter_conform_tests_deprecated_tests = [ + 'behaviours', + 'group', + 'rectangle', +- 'texture', + ] + + clutter_conform_tests = [] +diff --git a/clutter/tests/conform/texture.c b/clutter/tests/conform/texture.c +deleted file mode 100644 +index 392fd5c47e..0000000000 +--- a/clutter/tests/conform/texture.c ++++ /dev/null +@@ -1,84 +0,0 @@ +-#define CLUTTER_DISABLE_DEPRECATION_WARNINGS +-#include +-#include +- +-static CoglHandle +-make_texture (void) +-{ +- guint32 *data = g_malloc (100 * 100 * 4); +- int x; +- int y; +- +- for (y = 0; y < 100; y ++) +- for (x = 0; x < 100; x++) +- { +- if (x < 50 && y < 50) +- data[y * 100 + x] = 0xff00ff00; +- else +- data[y * 100 + x] = 0xff00ffff; +- } +- return cogl_texture_new_from_data (100, +- 100, +- COGL_TEXTURE_NONE, +- COGL_PIXEL_FORMAT_ARGB_8888, +- COGL_PIXEL_FORMAT_ARGB_8888, +- 400, +- (guchar *)data); +-} +- +-static void +-texture_pick_with_alpha (void) +-{ +- ClutterTexture *tex = CLUTTER_TEXTURE (clutter_texture_new ()); +- ClutterStage *stage = CLUTTER_STAGE (clutter_test_get_stage ()); +- ClutterActor *actor; +- +- clutter_texture_set_cogl_texture (tex, make_texture ()); +- +- clutter_actor_add_child (CLUTTER_ACTOR (stage), CLUTTER_ACTOR (tex)); +- +- clutter_actor_show (CLUTTER_ACTOR (stage)); +- +- if (g_test_verbose ()) +- { +- g_print ("\nstage = %p\n", stage); +- g_print ("texture = %p\n\n", tex); +- } +- +- clutter_texture_set_pick_with_alpha (tex, TRUE); +- if (g_test_verbose ()) +- g_print ("Testing with pick-with-alpha enabled:\n"); +- +- /* This should fall through and hit the stage: */ +- actor = clutter_stage_get_actor_at_pos (stage, CLUTTER_PICK_ALL, 10, 10); +- if (g_test_verbose ()) +- g_print ("actor @ (10, 10) = %p\n", actor); +- g_assert (actor == CLUTTER_ACTOR (stage)); +- +- /* The rest should hit the texture */ +- actor = clutter_stage_get_actor_at_pos (stage, CLUTTER_PICK_ALL, 90, 10); +- if (g_test_verbose ()) +- g_print ("actor @ (90, 10) = %p\n", actor); +- g_assert (actor == CLUTTER_ACTOR (tex)); +- actor = clutter_stage_get_actor_at_pos (stage, CLUTTER_PICK_ALL, 90, 90); +- if (g_test_verbose ()) +- g_print ("actor @ (90, 90) = %p\n", actor); +- g_assert (actor == CLUTTER_ACTOR (tex)); +- actor = clutter_stage_get_actor_at_pos (stage, CLUTTER_PICK_ALL, 10, 90); +- if (g_test_verbose ()) +- g_print ("actor @ (10, 90) = %p\n", actor); +- g_assert (actor == CLUTTER_ACTOR (tex)); +- +- clutter_texture_set_pick_with_alpha (tex, FALSE); +- if (g_test_verbose ()) +- g_print ("Testing with pick-with-alpha disabled:\n"); +- +- actor = clutter_stage_get_actor_at_pos (stage, CLUTTER_PICK_ALL, 10, 10); +- if (g_test_verbose ()) +- g_print ("actor @ (10, 10) = %p\n", actor); +- g_assert (actor == CLUTTER_ACTOR (tex)); +-} +- +-CLUTTER_TEST_SUITE ( +- CLUTTER_TEST_UNIT ("/texture/pick-with-alpha", texture_pick_with_alpha) +-) +diff --git a/src/compositor/meta-surface-actor.c b/src/compositor/meta-surface-actor.c +index ca4ca19a99..814199145a 100644 +--- a/src/compositor/meta-surface-actor.c ++++ b/src/compositor/meta-surface-actor.c +@@ -70,38 +70,23 @@ meta_surface_actor_pick (ClutterActor *actor, + else + { + int n_rects; +- float *rectangles; + int i; +- CoglPipeline *pipeline; +- CoglContext *ctx; +- CoglFramebuffer *fb; +- CoglColor cogl_color; + + n_rects = cairo_region_num_rectangles (priv->input_region); +- rectangles = g_alloca (sizeof (float) * 4 * n_rects); + + for (i = 0; i < n_rects; i++) + { + cairo_rectangle_int_t rect; +- int pos = i * 4; ++ ClutterActorBox box; + + cairo_region_get_rectangle (priv->input_region, i, &rect); + +- rectangles[pos + 0] = rect.x; +- rectangles[pos + 1] = rect.y; +- rectangles[pos + 2] = rect.x + rect.width; +- rectangles[pos + 3] = rect.y + rect.height; ++ box.x1 = rect.x; ++ box.y1 = rect.y; ++ box.x2 = rect.x + rect.width; ++ box.y2 = rect.y + rect.height; ++ clutter_actor_pick_box (actor, &box); + } +- +- ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ()); +- fb = cogl_get_draw_framebuffer (); +- +- cogl_color_init_from_4ub (&cogl_color, color->red, color->green, color->blue, color->alpha); +- +- pipeline = cogl_pipeline_new (ctx); +- cogl_pipeline_set_color (pipeline, &cogl_color); +- cogl_framebuffer_draw_rectangles (fb, pipeline, rectangles, n_rects); +- cogl_object_unref (pipeline); + } + + clutter_actor_iter_init (&iter, actor); +-- +2.29.2 + + +From 254e93de8d60393ca94fa430c0acc6f6a7b9516e Mon Sep 17 00:00:00 2001 +From: Iain Lane +Date: Mon, 9 Sep 2019 10:17:22 +0100 +Subject: [PATCH 3/4] build: Compile with `-ffloat-store` on x86 (32 bit) + +GCC's manpage says that this flag does the following: + + Do not store floating-point variables in registers, and inhibit other + options that might change whether a floating-point value is taken from + a register or memory. + + This option prevents undesirable excess precision on machines such as + the 68000 where the floating registers (of the 68881) keep more + precision than a "double" is supposed to have. Similarly for the x86 + architecture. For most programs, the excess precision does only good, + but a few programs rely on the precise definition of IEEE floating + point. + +We rely on this behaviour in our fork of clutter. When performing +floating point computations on x86, we are getting the wrong results +because of this architecture's use of the CPU's extended (x87, non-IEEE +confirming) precision by default. If we enable `-ffloat-store` here, +then we'll get the same results everywhere by storing into variables +instead of registers. This does not remove the need to be correct when +handling floats, but it does mean we don't need to be more correct than +the IEEE spec requires. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/785 +--- + meson.build | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/meson.build b/meson.build +index 8ef592bc58..e1edb78ba7 100644 +--- a/meson.build ++++ b/meson.build +@@ -267,6 +267,9 @@ foreach function : required_functions + endif + endforeach + ++if host_machine.cpu_family() == 'x86' ++ add_project_arguments('-ffloat-store', language: 'c') ++endif + add_project_arguments('-D_GNU_SOURCE', language: 'c') + + all_warnings = [ +-- +2.29.2 + + +From 2d42caef14772984344e62ce40957d3b40e1f2b6 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= +Date: Thu, 19 Sep 2019 11:27:50 +0200 +Subject: [PATCH 4/4] stage: Compute view perspective when parameters changed + +Clutter stage used to compute the initial projection using a fixed z +translation which wasn't matching the one we computed in +calculate_z_translation(). +This caused to have a wrong initial projection on startup which was then +correctly recomputed only at the first paint. + +However, since this calculation doesn't depend on view, but only on viewport +size, perspective's fovy and z_near we can safely do this at startup and +only when any of those parameters change. + +Then we can move the computation out _clutter_stage_maybe_setup_viewport() +since the cogl framebuffer viewport sizes aren't affecting this. + +Fixes https://gitlab.gnome.org/GNOME/gnome-shell/issues/1639 +https://gitlab.gnome.org/GNOME/mutter/merge_requests/803 +--- + clutter/clutter/clutter-stage.c | 104 +++++++++++++++----------------- + 1 file changed, 47 insertions(+), 57 deletions(-) + +diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c +index 7d88d5752f..0cfa87486e 100644 +--- a/clutter/clutter/clutter-stage.c ++++ b/clutter/clutter/clutter-stage.c +@@ -234,6 +234,7 @@ static void capture_view_into (ClutterStage *stage, + cairo_rectangle_int_t *rect, + uint8_t *data, + int stride); ++static void clutter_stage_update_view_perspective (ClutterStage *stage); + + static void clutter_container_iface_init (ClutterContainerIface *iface); + +@@ -2492,29 +2493,6 @@ clutter_stage_init (ClutterStage *self) + clutter_actor_set_background_color (CLUTTER_ACTOR (self), + &default_stage_color); + +- priv->perspective.fovy = 60.0; /* 60 Degrees */ +- priv->perspective.aspect = (float) geom.width / (float) geom.height; +- priv->perspective.z_near = 0.1; +- priv->perspective.z_far = 100.0; +- +- cogl_matrix_init_identity (&priv->projection); +- cogl_matrix_perspective (&priv->projection, +- priv->perspective.fovy, +- priv->perspective.aspect, +- priv->perspective.z_near, +- priv->perspective.z_far); +- cogl_matrix_get_inverse (&priv->projection, +- &priv->inverse_projection); +- cogl_matrix_init_identity (&priv->view); +- cogl_matrix_view_2d_in_perspective (&priv->view, +- priv->perspective.fovy, +- priv->perspective.aspect, +- priv->perspective.z_near, +- 50, /* distance to 2d plane */ +- geom.width, +- geom.height); +- +- + /* FIXME - remove for 2.0 */ + priv->fog.z_near = 1.0; + priv->fog.z_far = 2.0; +@@ -2682,6 +2660,7 @@ clutter_stage_set_perspective (ClutterStage *stage, + priv->has_custom_perspective = TRUE; + + clutter_stage_set_perspective_internal (stage, perspective); ++ clutter_stage_update_view_perspective (stage); + } + + /** +@@ -2808,6 +2787,7 @@ _clutter_stage_set_viewport (ClutterStage *stage, + priv->viewport[2] = width; + priv->viewport[3] = height; + ++ clutter_stage_update_view_perspective (stage); + _clutter_stage_dirty_viewport (stage); + + queue_full_redraw (stage); +@@ -3788,6 +3768,50 @@ calculate_z_translation (float z_near) + + z_near; + } + ++static void ++clutter_stage_update_view_perspective (ClutterStage *stage) ++{ ++ ClutterStagePrivate *priv = stage->priv; ++ ClutterPerspective perspective; ++ float z_2d; ++ ++ perspective = priv->perspective; ++ ++ /* Ideally we want to regenerate the perspective matrix whenever ++ * the size changes but if the user has provided a custom matrix ++ * then we don't want to override it */ ++ if (!priv->has_custom_perspective) ++ { ++ perspective.fovy = 60.0; /* 60 Degrees */ ++ perspective.z_near = 0.1; ++ perspective.aspect = priv->viewport[2] / priv->viewport[3]; ++ z_2d = calculate_z_translation (perspective.z_near); ++ ++ /* NB: z_2d is only enough room for 85% of the stage_height between ++ * the stage and the z_near plane. For behind the stage plane we ++ * want a more consistent gap of 10 times the stage_height before ++ * hitting the far plane so we calculate that relative to the final ++ * height of the stage plane at the z_2d_distance we got... */ ++ perspective.z_far = z_2d + ++ tanf (_DEG_TO_RAD (perspective.fovy / 2.0f)) * z_2d * 20.0f; ++ ++ clutter_stage_set_perspective_internal (stage, &perspective); ++ } ++ else ++ { ++ z_2d = calculate_z_translation (perspective.z_near); ++ } ++ ++ cogl_matrix_init_identity (&priv->view); ++ cogl_matrix_view_2d_in_perspective (&priv->view, ++ perspective.fovy, ++ perspective.aspect, ++ perspective.z_near, ++ z_2d, ++ priv->viewport[2], ++ priv->viewport[3]); ++} ++ + void + _clutter_stage_maybe_setup_viewport (ClutterStage *stage, + ClutterStageView *view) +@@ -3797,7 +3821,6 @@ _clutter_stage_maybe_setup_viewport (ClutterStage *stage, + if (clutter_stage_view_is_dirty_viewport (view)) + { + cairo_rectangle_int_t view_layout; +- ClutterPerspective perspective; + float fb_scale; + float viewport_offset_x; + float viewport_offset_y; +@@ -3805,7 +3828,6 @@ _clutter_stage_maybe_setup_viewport (ClutterStage *stage, + float viewport_y; + float viewport_width; + float viewport_height; +- float z_2d; + + CLUTTER_NOTE (PAINT, + "Setting up the viewport { w:%f, h:%f }", +@@ -3825,38 +3847,6 @@ _clutter_stage_maybe_setup_viewport (ClutterStage *stage, + clutter_stage_view_set_viewport (view, + viewport_x, viewport_y, + viewport_width, viewport_height); +- +- perspective = priv->perspective; +- +- /* Ideally we want to regenerate the perspective matrix whenever +- * the size changes but if the user has provided a custom matrix +- * then we don't want to override it */ +- if (!priv->has_custom_perspective) +- { +- perspective.aspect = priv->viewport[2] / priv->viewport[3]; +- z_2d = calculate_z_translation (perspective.z_near); +- +- /* NB: z_2d is only enough room for 85% of the stage_height between +- * the stage and the z_near plane. For behind the stage plane we +- * want a more consistent gap of 10 times the stage_height before +- * hitting the far plane so we calculate that relative to the final +- * height of the stage plane at the z_2d_distance we got... */ +- perspective.z_far = z_2d + +- tanf (_DEG_TO_RAD (perspective.fovy / 2.0f)) * z_2d * 20.0f; +- +- clutter_stage_set_perspective_internal (stage, &perspective); +- } +- else +- z_2d = calculate_z_translation (perspective.z_near); +- +- cogl_matrix_init_identity (&priv->view); +- cogl_matrix_view_2d_in_perspective (&priv->view, +- perspective.fovy, +- perspective.aspect, +- perspective.z_near, +- z_2d, +- priv->viewport[2], +- priv->viewport[3]); + } + + if (clutter_stage_view_is_dirty_projection (view)) +-- +2.29.2 + 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 dd96fb4..940675a 100644 --- a/SPECS/mutter.spec +++ b/SPECS/mutter.spec @@ -8,7 +8,7 @@ Name: mutter Version: 3.32.2 -Release: 48%{?dist} +Release: 57%{?dist} Summary: Window and compositing manager based on Clutter License: GPLv2+ @@ -159,6 +159,30 @@ Patch501: 0001-window-actor-Don-t-show-actor-until-meta_window_acto.patch 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 + +# Backport of geometric picking, improving performance and fixing picking +# 10bpc pixel formats (#1919467) +Patch511: geometric-picking.patch + +Patch520: 0001-clutter-Backport-of-touch-mode.patch + BuildRequires: chrpath BuildRequires: pango-devel BuildRequires: startup-notification-devel @@ -300,9 +324,43 @@ desktop-file-validate %{buildroot}/%{_datadir}/applications/%{name}.desktop %{_datadir}/mutter-%{mutter_api_version}/tests %changelog -* Mon Sep 21 2020 Jonas Ådahl - 3.32.2-48 +* Mon Feb 22 2021 Carlos Garnacho - 3.32.2-57 +- Backport touch-mode + Resolves: #1833787 + +* Tue Feb 09 2021 Jonas Ådahl - 3.32.2-56 +- Backport geometric picking patches + Resolves: #1919467 + +* 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: #1880339 + 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