summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonas Ã…dahl <jadahl@gmail.com>2020-09-11 16:32:12 +0200
committerGeorges Basile Stavracas Neto <georges.stavracas@gmail.com>2020-09-28 13:08:52 +0000
commit5703a56996ab8ef4214678ac18ecaf9ffdc2077a (patch)
tree4ed4f655829d1426e0bf70556952bd17e0ea6662
parenta532eeb07faa69c5700368d2280c402703512a20 (diff)
downloadmutter-5703a56996ab8ef4214678ac18ecaf9ffdc2077a.tar.gz
compositor: Make sure _NET_WM_FRAME_DRAWN timestamp has the right scope
The timestamp sent with _NET_WM_FRAME_DRAWN should be in "high resolution X server timestamps", meaning they should have the same scope as the built in X11 32 bit unsigned integer timestamps, i.e. overflow at the same time. This was not done correctly when mutter had determined the X server used the monotonic clock, where it'd just forward the monotonic clock, confusing any client using _NET_WM_FRAME_DRAWN and friends. Fix this by 1) splitting the timestamp conversiot into an X11 case and a display server case, where the display server case simply clamps the monotonic clock, as it is assumed Xwayland is always usign the monotonic clock, and 2) if we're a X11 compositing manager, if the X server is using the monotonic clock, apply the same semantics as the display server case and always just clamp, or if not, calculate the offset every 10 seconds, and offset the monotonic clock timestamp with the calculated X server timestamp offset. This fixes an issue that would occur if mutter (or rather GNOME Shell) would have been started before a X11 timestamp overflow, after the overflow happened. In this case, GTK3 clients would get unclamped timestamps, and get very confused, resulting in frames queued several weeks into the future.
-rw-r--r--src/compositor/compositor-private.h43
-rw-r--r--src/compositor/compositor.c95
-rw-r--r--src/compositor/meta-window-actor.c22
3 files changed, 123 insertions, 37 deletions
diff --git a/src/compositor/compositor-private.h b/src/compositor/compositor-private.h
index 4582792d4..f40db9a05 100644
--- a/src/compositor/compositor-private.h
+++ b/src/compositor/compositor-private.h
@@ -21,8 +21,9 @@ struct _MetaCompositor
guint stage_presented_id;
guint stage_after_paint_id;
- gint64 server_time_query_time;
- gint64 server_time_offset;
+ gboolean xserver_uses_monotonic_clock;
+ int64_t xserver_time_query_time_us;
+ int64_t xserver_time_offset_us;
guint server_time_is_monotonic_time : 1;
guint no_mipmaps : 1;
@@ -61,8 +62,8 @@ void meta_end_modal_for_plugin (MetaCompositor *compositor,
MetaPlugin *plugin,
guint32 timestamp);
-gint64 meta_compositor_monotonic_time_to_server_time (MetaDisplay *display,
- gint64 monotonic_time);
+int64_t meta_compositor_monotonic_to_high_res_xserver_time (MetaCompositor *compositor,
+ int64_t monotonic_time);
void meta_compositor_flash_window (MetaCompositor *compositor,
MetaWindow *window);
@@ -75,4 +76,38 @@ MetaInhibitShortcutsDialog * meta_compositor_create_inhibit_shortcuts_dialog (Me
void meta_compositor_unmanage_window_actors (MetaCompositor *compositor);
+static inline int64_t
+us (int64_t us)
+{
+ return us;
+}
+
+static inline int64_t
+ms2us (int64_t ms)
+{
+ return us (ms * 1000);
+}
+
+static inline int64_t
+s2us (int64_t s)
+{
+ return ms2us(s * 1000);
+}
+
+/*
+ * This function takes a 64 bit time stamp from the monotonic clock, and clamps
+ * it to the scope of the X server clock, without losing the granularity.
+ */
+static inline int64_t
+meta_translate_to_high_res_xserver_time (int64_t time_us)
+{
+ int64_t us;
+ int64_t ms;
+
+ us = time_us % 1000;
+ ms = time_us / 1000;
+
+ return ms2us (ms & 0xffffffff) + us;
+}
+
#endif /* META_COMPOSITOR_PRIVATE_H */
diff --git a/src/compositor/compositor.c b/src/compositor/compositor.c
index 6d045339c..8b2ed47a9 100644
--- a/src/compositor/compositor.c
+++ b/src/compositor/compositor.c
@@ -500,6 +500,30 @@ redirect_windows (MetaScreen *screen)
}
}
+static void
+determine_server_clock_source (MetaCompositor *compositor)
+{
+ MetaDisplay *display = compositor->display;
+ uint32_t server_time_ms;
+ int64_t server_time_us;
+ int64_t translate_monotonic_now_us;
+
+ server_time_ms = meta_display_get_current_time_roundtrip (display);
+ server_time_us = ms2us (server_time_ms);
+ translate_monotonic_now_us =
+ meta_translate_to_high_res_xserver_time (g_get_monotonic_time ());
+
+ /* If the server time offset is within a second of the monotonic time, we
+ * assume that they are identical. This seems like a big margin, but we want
+ * to be as robust as possible even if the system is under load and our
+ * processing of the server response is delayed.
+ */
+ if (ABS (server_time_us - translated_monotonic_now_us) < s2us (1))
+ compositor->xserver_uses_monotonic_clock = TRUE;
+ else
+ compositor->xserver_uses_monotonic_clock = FALSE;
+}
+
void
meta_compositor_manage (MetaCompositor *compositor)
{
@@ -508,6 +532,8 @@ meta_compositor_manage (MetaCompositor *compositor)
MetaScreen *screen = display->screen;
MetaBackend *backend = meta_get_backend ();
+ determine_server_clock_source (compositor);
+
meta_screen_set_cm_selection (display->screen);
compositor->stage = meta_backend_get_stage (backend);
@@ -1428,6 +1454,40 @@ meta_compositor_flash_window (MetaCompositor *compositor,
clutter_actor_restore_easing_state (flash);
}
+static int64_t
+meta_compositor_monotonic_to_high_res_xserver_time_x11 (MetaCompositor *compositor,
+ int64_t monotonic_time_us)
+{
+ int64_t now_us;
+
+ if (compositor->xserver_uses_monotonic_clock)
+ return meta_translate_to_high_res_xserver_time (monotonic_time_us);
+
+ now_us = g_get_monotonic_time ();
+
+ if (compositor->xserver_time_query_time_us == 0 ||
+ now_us > (compositor->xserver_time_query_time_us + s2us (10)))
+ {
+ MetaDisplay *display = compositor->display;
+ uint32_t xserver_time_ms;
+ int64_t xserver_time_us;
+
+ compositor->xserver_time_query_time_us = now_us;
+
+ xserver_time_ms = meta_display_get_current_time_roundtrip (display);
+ xserver_time_us = ms2us (xserver_time_ms);
+ compositor->xserver_time_offset_us = xserver_time_us - now_us;
+ }
+
+ return monotonic_time_us + compositor->xserver_time_offset_us;
+}
+
+static int64_t
+meta_compositor_monotonic_to_high_res_xserver_time_server (int64_t monotonic_time_us)
+{
+ return meta_translate_to_high_res_xserver_time (monotonic_time_us);
+}
+
/**
* meta_compositor_monotonic_time_to_server_time:
* @display: a #MetaDisplay
@@ -1443,37 +1503,18 @@ meta_compositor_flash_window (MetaCompositor *compositor,
* time source, then the time synchronization will be less accurate.
*/
gint64
-meta_compositor_monotonic_time_to_server_time (MetaDisplay *display,
- gint64 monotonic_time)
+meta_compositor_monotonic_to_high_res_xserver_time (MetaCompositor *compositor,
+ int64_t monotonic_time)
{
- MetaCompositor *compositor = display->compositor;
-
- if (compositor->server_time_query_time == 0 ||
- (!compositor->server_time_is_monotonic_time &&
- monotonic_time > compositor->server_time_query_time + 10*1000*1000)) /* 10 seconds */
+ if (meta_is_wayland_compositor ())
{
- guint32 server_time = meta_display_get_current_time_roundtrip (display);
- gint64 server_time_usec = (gint64)server_time * 1000;
- gint64 current_monotonic_time = g_get_monotonic_time ();
- compositor->server_time_query_time = current_monotonic_time;
-
- /* If the server time is within a second of the monotonic time,
- * we assume that they are identical. This seems like a big margin,
- * but we want to be as robust as possible even if the system
- * is under load and our processing of the server response is
- * delayed.
- */
- if (server_time_usec > current_monotonic_time - 1000*1000 &&
- server_time_usec < current_monotonic_time + 1000*1000)
- compositor->server_time_is_monotonic_time = TRUE;
-
- compositor->server_time_offset = server_time_usec - current_monotonic_time;
+ return meta_compositor_monotonic_to_high_res_xserver_time_server (monotonic_time);
}
-
- if (compositor->server_time_is_monotonic_time)
- return monotonic_time;
else
- return monotonic_time + compositor->server_time_offset;
+ {
+ return meta_compositor_monotonic_to_high_res_xserver_time_x11 (compositor,
+ monotonic_time);
+ }
}
void
diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c
index 5078624cc..1cc2db488 100644
--- a/src/compositor/meta-window-actor.c
+++ b/src/compositor/meta-window-actor.c
@@ -965,6 +965,7 @@ queue_send_frame_messages_timeout (MetaWindowActor *self)
MetaWindow *window = priv->window;
MetaDisplay *display = meta_window_get_display (priv->window);
MetaLogicalMonitor *logical_monitor;
+ int64_t now_us;
int64_t current_time;
float refresh_rate;
int interval, offset;
@@ -989,9 +990,10 @@ queue_send_frame_messages_timeout (MetaWindowActor *self)
refresh_rate = 60.0f;
}
+ now_us = g_get_monotonic_time ();
current_time =
- meta_compositor_monotonic_time_to_server_time (display,
- g_get_monotonic_time ());
+ meta_compositor_monotonic_to_high_res_xserver_time (display->compositor,
+ now_us);
interval = (int)(1000000 / refresh_rate) * 6;
offset = MAX (0, priv->frame_drawn_time + interval - current_time) / 1000;
@@ -1972,11 +1974,14 @@ do_send_frame_drawn (MetaWindowActor *self, FrameData *frame)
MetaWindowActorPrivate *priv = self->priv;
MetaDisplay *display = meta_window_get_display (priv->window);
Display *xdisplay = meta_display_get_xdisplay (display);
+ int64_t now_us;
XClientMessageEvent ev = { 0, };
- frame->frame_drawn_time = meta_compositor_monotonic_time_to_server_time (display,
- g_get_monotonic_time ());
+ now_us = g_get_monotonic_time ();
+ frame->frame_drawn_time =
+ meta_compositor_monotonic_to_high_res_xserver_time (display->compositor, now_us);
+
priv->frame_drawn_time = frame->frame_drawn_time;
ev.type = ClientMessage;
@@ -2052,8 +2057,13 @@ do_send_frame_timings (MetaWindowActor *self,
if (presentation_time != 0)
{
- gint64 presentation_time_server = meta_compositor_monotonic_time_to_server_time (display,
- presentation_time);
+ MetaCompositor *compositor = display->compositor;
+ int64_t presentation_time_server;
+
+ presentation_time_server =
+ meta_compositor_monotonic_to_high_res_xserver_time (compositor,
+ presentation_time);
+
gint64 presentation_time_offset = presentation_time_server - frame->frame_drawn_time;
if (presentation_time_offset == 0)
presentation_time_offset = 1;