Blob Blame History Raw
From d9f8efbefc0f3bc6457331f5c6870c6efbc0a506 Mon Sep 17 00:00:00 2001
From: Clement Lefebvre <clement.lefebvre@linuxmint.com>
Date: Sat, 6 Jul 2019 11:21:52 +0200
Subject: [PATCH] clutter: Deliver events sooner when possible.

From @vanvugt
https://gitlab.gnome.org/GNOME/mutter/commit/ae8fc6146b14c1905a99a0e929d1f192f2ed050a

Previously all events would be queued and their processing deferred till
the next master clock tick, at which point supersesed input events would
be dropped and only the latest of each type used. This was great for
minimizing CPU usage but had two drawbacks:

 * Clients would receive the next input event after it is already too
   late to make it to the next compositor frame.
 * Clients would receive a lower resolution event stream than the hardware
   is capable of.

We now instead scale performance dynamically according to available time.
If there is enough idle time available then that will be used to deliver
events immediately without delay. Otherwise event delivery will scale down
to the old minimal-CPU behaviour.

This allows clients to receive input events sufficiently in advance of the
next compositor frame that they can respond and redraw with one frame
lower latency than before. It also allows clients higher resolution input,
in case they are able to use it.
---
 clutter/clutter/clutter-stage.c | 50 +++++++++++++++++++++++++++------
 1 file changed, 42 insertions(+), 8 deletions(-)

diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c
index bc135b45..2c9c4da8 100644
--- a/clutter/clutter/clutter-stage.c
+++ b/clutter/clutter/clutter-stage.c
@@ -122,6 +122,7 @@ struct _ClutterStagePrivate
   ClutterActor *key_focused_actor;
 
   GQueue *event_queue;
+  guint event_flushing_idle_source;
 
   ClutterStageHint stage_hints;
 
@@ -876,6 +877,19 @@ clutter_stage_real_fullscreen (ClutterStage *stage)
                           CLUTTER_ALLOCATION_NONE);
 }
 
+static gboolean
+_clutter_stage_flush_events (gpointer user_data)
+{
+  ClutterStage *stage = CLUTTER_STAGE (user_data);
+  ClutterStagePrivate *priv = stage->priv;
+
+  priv->event_flushing_idle_source = 0;
+  _clutter_stage_process_queued_events (stage);
+
+  return G_SOURCE_REMOVE;
+}
+
+
 void
 _clutter_stage_queue_event (ClutterStage *stage,
                             ClutterEvent *event,
@@ -896,13 +910,6 @@ _clutter_stage_queue_event (ClutterStage *stage,
 
   g_queue_push_tail (priv->event_queue, event);
 
-  if (first_event)
-    {
-      ClutterMasterClock *master_clock = _clutter_master_clock_get_default ();
-      _clutter_master_clock_start_running (master_clock);
-      _clutter_stage_schedule_update (stage);
-    }
-
   /* if needed, update the state of the input device of the event.
    * we do it here to avoid calling the same code from every backend
    * event processing function
@@ -923,6 +930,30 @@ _clutter_stage_queue_event (ClutterStage *stage,
       _clutter_input_device_set_state (device, event_state);
       _clutter_input_device_set_time (device, event_time);
     }
+
+  if (!priv->throttle_motion_events)
+    {
+      if (!priv->event_flushing_idle_source)
+        {
+          /* Process events ASAP, but never at the expense of rendering
+           * performance. So a sufficiently fast machine will process all
+           * events synchronously. But in the worst case a slow machine will
+           * batch and throttle them to the refresh rate on the next master
+           * clock tick.
+           */
+          priv->event_flushing_idle_source =
+            g_idle_add_full (CLUTTER_PRIORITY_REDRAW + 1,
+                             _clutter_stage_flush_events,
+                             stage,
+                             NULL);
+        }
+    }
+  else if (first_event)
+    {
+      ClutterMasterClock *master_clock = _clutter_master_clock_get_default ();
+      _clutter_master_clock_start_running (master_clock);
+      _clutter_stage_schedule_update (stage);
+    }
 }
 
 gboolean
@@ -1883,6 +1914,9 @@ clutter_stage_finalize (GObject *object)
   ClutterStage *stage = CLUTTER_STAGE (object);
   ClutterStagePrivate *priv = stage->priv;
 
+  if (priv->event_flushing_idle_source)
+      g_source_remove (priv->event_flushing_idle_source);
+
   g_queue_foreach (priv->event_queue, (GFunc) clutter_event_free, NULL);
   g_queue_free (priv->event_queue);
 
@@ -2327,7 +2361,7 @@ clutter_stage_init (ClutterStage *self)
   priv->is_user_resizable = FALSE;
   priv->is_cursor_visible = TRUE;
   priv->use_fog = FALSE;
-  priv->throttle_motion_events = TRUE;
+  priv->throttle_motion_events = FALSE;
   priv->min_size_changed = FALSE;
   priv->sync_delay = -1;