Sync to the hardware refresh rate, not just 60.00Hz
authorDaniel van Vugt <daniel.van.vugt@canonical.com>
Thu, 29 Nov 2018 16:31:58 +0000 (17:31 +0100)
committerSimon McVittie <smcv@debian.org>
Wed, 6 Feb 2019 10:02:14 +0000 (10:02 +0000)
The Clutter master clock supports three synchronization methods in order of
preference:
  (a) Hardware presentation times
  (b) Swap throttling
  (c) Fake vsync at 60.00Hz
X11 already supports all three but Wayland only has (c). This patch implements
both (a) and (b) for Wayland bringing it in line with X11.

Origin: https://gitlab.gnome.org/GNOME/mutter/merge_requests/171 via backport https://gitlab.gnome.org/GNOME/mutter/merge_requests/318
Bug: https://bugzilla.gnome.org/show_bug.cgi?id=781296
Bug-Ubuntu: https://bugs.launchpad.net/bugs/1763892
Forwarded: yes
Last-Update: 2018-11-23

Gbp-Pq: Name Sync-to-the-hardware-refresh-rate-not-just-60.00Hz.patch

src/Makefile.am
src/backends/native/meta-gpu-kms.c
src/backends/native/meta-gpu-kms.h
src/backends/native/meta-renderer-native.c
src/meta-marshal.list [new file with mode: 0644]

index 811e2b86ba3ac0e3f19292de2833c015b818949f..56d539836f1c46cdbd018ec5263a6211e94e2aaf 100644 (file)
@@ -51,6 +51,8 @@ mutter_built_sources = \
        $(dbus_login1_built_sources)            \
        meta/meta-enum-types.h                  \
        meta-enum-types.c                       \
+       meta-marshal.c                          \
+       meta-marshal.h                          \
        $(NULL)
 
 if HAVE_REMOTE_DESKTOP
@@ -677,6 +679,7 @@ EXTRA_DIST +=                                       \
        libmutter.pc.in                         \
        meta-enum-types.h.in                    \
        meta-enum-types.c.in                    \
+       meta-marshal.list                       \
        org.freedesktop.login1.xml              \
        org.gnome.Mutter.DisplayConfig.xml      \
        org.gnome.Mutter.IdleMonitor.xml        \
@@ -690,7 +693,10 @@ BUILT_SOURCES =                                    \
        $(libmutterinclude_built_headers)
 
 MUTTER_STAMP_FILES = stamp-meta-enum-types.h
-CLEANFILES += $(MUTTER_STAMP_FILES)
+CLEANFILES +=                                  \
+       $(MUTTER_STAMP_FILES)                   \
+       meta-marshal.c                          \
+       meta-marshal.h
 
 meta/meta-enum-types.h: stamp-meta-enum-types.h Makefile
        @true
@@ -786,3 +792,20 @@ endef
        $(AM_V_GEN)$(WAYLAND_SCANNER) server-header $< $@
 %-server-protocol.h : $(WAYLAND_EGLSTREAM_DATADIR)/%.xml
        $(AM_V_GEN)$(WAYLAND_SCANNER) server-header $< $@
+
+meta_marshal_opts = --prefix=meta_marshal --internal
+
+meta-marshal.h: meta-marshal.list
+       $(AM_V_GEN)$(GLIB_GENMARSHAL) \
+               --header \
+               $(meta_marshal_opts) \
+               --output=$@ \
+               $<
+
+meta-marshal.c: meta-marshal.list meta-marshal.h
+       $(AM_V_GEN)$(GLIB_GENMARSHAL) \
+               --include-header=meta-marshal.h \
+               $(meta_marshal_opts) \
+               --body \
+               --output=$@ \
+               $<
index c3c5d0d6876cfe333e8e5bfea5ffcceb177d2ceb..26c759031b1ecb4dbdae888a18d9490fd33365db 100644 (file)
@@ -27,6 +27,7 @@
 #include <errno.h>
 #include <poll.h>
 #include <string.h>
+#include <time.h>
 #include <xf86drm.h>
 #include <xf86drmMode.h>
 
@@ -51,6 +52,7 @@ typedef struct _MetaGpuKmsFlipClosureContainer
 {
   GClosure *flip_closure;
   MetaGpuKms *gpu_kms;
+  MetaCrtc *crtc;
 } MetaGpuKmsFlipClosureContainer;
 
 struct _MetaGpuKms
@@ -61,6 +63,8 @@ struct _MetaGpuKms
   char *file_path;
   GSource *source;
 
+  clockid_t clock_id;
+
   drmModeConnector **connectors;
   unsigned int n_connectors;
 
@@ -165,18 +169,26 @@ meta_gpu_kms_apply_crtc_mode (MetaGpuKms *gpu_kms,
 
 static void
 invoke_flip_closure (GClosure   *flip_closure,
-                     MetaGpuKms *gpu_kms)
+                     MetaGpuKms *gpu_kms,
+                     MetaCrtc   *crtc,
+                     int64_t     page_flip_time_ns)
 {
   GValue params[] = {
     G_VALUE_INIT,
-    G_VALUE_INIT
+    G_VALUE_INIT,
+    G_VALUE_INIT,
+    G_VALUE_INIT,
   };
 
   g_value_init (&params[0], G_TYPE_POINTER);
   g_value_set_pointer (&params[0], flip_closure);
   g_value_init (&params[1], G_TYPE_OBJECT);
   g_value_set_object (&params[1], gpu_kms);
-  g_closure_invoke (flip_closure, NULL, 2, params, NULL);
+  g_value_init (&params[2], G_TYPE_OBJECT);
+  g_value_set_object (&params[2], crtc);
+  g_value_init (&params[3], G_TYPE_INT64);
+  g_value_set_int64 (&params[3], page_flip_time_ns);
+  g_closure_invoke (flip_closure, NULL, 4, params, NULL);
   g_closure_unref (flip_closure);
 }
 
@@ -216,6 +228,7 @@ meta_gpu_kms_is_crtc_active (MetaGpuKms *gpu_kms,
 
 MetaGpuKmsFlipClosureContainer *
 meta_gpu_kms_wrap_flip_closure (MetaGpuKms *gpu_kms,
+                                MetaCrtc   *crtc,
                                 GClosure   *flip_closure)
 {
   MetaGpuKmsFlipClosureContainer *closure_container;
@@ -223,7 +236,8 @@ meta_gpu_kms_wrap_flip_closure (MetaGpuKms *gpu_kms,
   closure_container = g_new0 (MetaGpuKmsFlipClosureContainer, 1);
   *closure_container = (MetaGpuKmsFlipClosureContainer) {
     .flip_closure = flip_closure,
-    .gpu_kms = gpu_kms
+    .gpu_kms = gpu_kms,
+    .crtc = crtc
   };
 
   return closure_container;
@@ -263,6 +277,7 @@ meta_gpu_kms_flip_crtc (MetaGpuKms *gpu_kms,
       int kms_fd = meta_gpu_kms_get_fd (gpu_kms);
 
       closure_container = meta_gpu_kms_wrap_flip_closure (gpu_kms,
+                                                          crtc,
                                                           flip_closure);
 
       ret = drmModePageFlip (kms_fd,
@@ -296,6 +311,23 @@ meta_gpu_kms_flip_crtc (MetaGpuKms *gpu_kms,
   return TRUE;
 }
 
+static int64_t
+timespec_to_nanoseconds (const struct timespec *ts)
+{
+  const int64_t one_billion = 1000000000;
+
+  return ((int64_t) ts->tv_sec) * one_billion + ts->tv_nsec;
+}
+
+static int64_t
+timeval_to_nanoseconds (const struct timeval *tv)
+{
+  int64_t usec = ((int64_t) tv->tv_sec) * G_USEC_PER_SEC + tv->tv_usec;
+  int64_t nsec = usec * 1000;
+
+  return nsec;
+}
+
 static void
 page_flip_handler (int           fd,
                    unsigned int  frame,
@@ -306,8 +338,12 @@ page_flip_handler (int           fd,
   MetaGpuKmsFlipClosureContainer *closure_container = user_data;
   GClosure *flip_closure = closure_container->flip_closure;
   MetaGpuKms *gpu_kms = closure_container->gpu_kms;
+  struct timeval page_flip_time = {sec, usec};
 
-  invoke_flip_closure (flip_closure, gpu_kms);
+  invoke_flip_closure (flip_closure,
+                       gpu_kms,
+                       closure_container->crtc,
+                       timeval_to_nanoseconds (&page_flip_time));
   meta_gpu_kms_flip_closure_container_free (closure_container);
 }
 
@@ -380,6 +416,17 @@ meta_gpu_kms_get_file_path (MetaGpuKms *gpu_kms)
   return gpu_kms->file_path;
 }
 
+int64_t
+meta_gpu_kms_get_current_time_ns (MetaGpuKms *gpu_kms)
+{
+  struct timespec ts;
+
+  if (clock_gettime (gpu_kms->clock_id, &ts))
+    return 0;
+
+  return timespec_to_nanoseconds (&ts);
+}
+
 void
 meta_gpu_kms_set_power_save_mode (MetaGpuKms *gpu_kms,
                                   uint64_t    state)
@@ -679,6 +726,17 @@ init_crtcs (MetaGpuKms       *gpu_kms,
   meta_gpu_take_crtcs (gpu, crtcs);
 }
 
+static void
+init_frame_clock (MetaGpuKms *gpu_kms)
+{
+  uint64_t uses_monotonic;
+
+  if (drmGetCap (gpu_kms->fd, DRM_CAP_TIMESTAMP_MONOTONIC, &uses_monotonic) != 0)
+    uses_monotonic = 0;
+
+  gpu_kms->clock_id = uses_monotonic ? CLOCK_MONOTONIC : CLOCK_REALTIME;
+}
+
 static void
 init_outputs (MetaGpuKms       *gpu_kms,
               MetaKmsResources *resources)
@@ -806,6 +864,7 @@ meta_gpu_kms_read_current (MetaGpu  *gpu,
   init_modes (gpu_kms, resources.resources);
   init_crtcs (gpu_kms, &resources);
   init_outputs (gpu_kms, &resources);
+  init_frame_clock (gpu_kms);
 
   meta_kms_resources_release (&resources);
 
index 5f77f191a2dc7000c3c51628cba0fd2e081676a4..3e3f45c7eddb3891f19fc65391546e089a71e255 100644 (file)
@@ -73,6 +73,8 @@ int meta_gpu_kms_get_fd (MetaGpuKms *gpu_kms);
 
 const char * meta_gpu_kms_get_file_path (MetaGpuKms *gpu_kms);
 
+int64_t meta_gpu_kms_get_current_time_ns (MetaGpuKms *gpu_kms);
+
 void meta_gpu_kms_get_max_buffer_size (MetaGpuKms *gpu_kms,
                                        int        *max_width,
                                        int        *max_height);
@@ -89,6 +91,7 @@ gboolean meta_drm_mode_equal (const drmModeModeInfo *one,
 float meta_calculate_drm_mode_refresh_rate (const drmModeModeInfo *mode);
 
 MetaGpuKmsFlipClosureContainer * meta_gpu_kms_wrap_flip_closure (MetaGpuKms *gpu_kms,
+                                                                 MetaCrtc   *crtc,
                                                                  GClosure   *flip_closure);
 
 void meta_gpu_kms_flip_closure_container_free (MetaGpuKmsFlipClosureContainer *closure_container);
index f58e6b3e03b6974db93bf95405c9af6384fab3f3..24d6b8b6df14d79347b7771b829a454c0f2b0faa 100644 (file)
@@ -60,8 +60,9 @@
 #include "backends/native/meta-crtc-kms.h"
 #include "backends/native/meta-gpu-kms.h"
 #include "backends/native/meta-monitor-manager-kms.h"
-#include "backends/native/meta-renderer-native.h"
 #include "backends/native/meta-renderer-native-gles3.h"
+#include "backends/native/meta-renderer-native.h"
+#include "meta-marshal.h"
 #include "cogl/cogl.h"
 #include "core/boxes-private.h"
 
@@ -1159,6 +1160,8 @@ meta_onscreen_native_swap_drm_fb (CoglOnscreen *onscreen)
 static void
 on_crtc_flipped (GClosure         *closure,
                  MetaGpuKms       *gpu_kms,
+                 MetaCrtc         *crtc,
+                 int64_t           page_flip_time_ns,
                  MetaRendererView *view)
 {
   ClutterStageView *stage_view = CLUTTER_STAGE_VIEW (view);
@@ -1169,6 +1172,24 @@ on_crtc_flipped (GClosure         *closure,
   MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
   MetaRendererNative *renderer_native = onscreen_native->renderer_native;
   MetaGpuKms *render_gpu = onscreen_native->render_gpu;
+  CoglFrameInfo *frame_info;
+  float refresh_rate;
+
+  frame_info = g_queue_peek_tail (&onscreen->pending_frame_infos);
+  refresh_rate = crtc && crtc->current_mode ?
+                 crtc->current_mode->refresh_rate :
+                 0.0f;
+
+  /* Only keep the frame info for the fastest CRTC in use, which may not be
+   * the first one to complete a flip. By only telling the compositor about the
+   * fastest monitor(s) we direct it to produce new frames fast enough to
+   * satisfy all monitors.
+   */
+  if (refresh_rate >= frame_info->refresh_rate)
+    {
+      frame_info->presentation_time = page_flip_time_ns;
+      frame_info->refresh_rate = refresh_rate;
+    }
 
   if (gpu_kms != render_gpu)
     {
@@ -1299,7 +1320,9 @@ flip_egl_stream (MetaOnscreenNative *onscreen_native,
     return FALSE;
 
   closure_container =
-    meta_gpu_kms_wrap_flip_closure (onscreen_native->render_gpu, flip_closure);
+    meta_gpu_kms_wrap_flip_closure (onscreen_native->render_gpu,
+                                    NULL,
+                                    flip_closure);
 
   acquire_attribs = (EGLAttrib[]) {
     EGL_DRM_FLIP_EVENT_DATA_NV,
@@ -1542,7 +1565,7 @@ meta_onscreen_native_flip_crtcs (CoglOnscreen *onscreen)
   flip_closure = g_cclosure_new (G_CALLBACK (on_crtc_flipped),
                                  g_object_ref (view),
                                  (GClosureNotify) flip_closure_destroyed);
-  g_closure_set_marshal (flip_closure, g_cclosure_marshal_VOID__OBJECT);
+  g_closure_set_marshal (flip_closure, meta_marshal_VOID__OBJECT_OBJECT_INT64);
 
   /* Either flip the CRTC's of the monitor info, if we are drawing just part
    * of the stage, or all of the CRTC's if we are drawing the whole stage.
@@ -1982,6 +2005,13 @@ meta_renderer_native_init_egl_context (CoglContext *cogl_context,
                   COGL_WINSYS_FEATURE_MULTIPLE_ONSCREEN,
                   TRUE);
 
+  /* COGL_WINSYS_FEATURE_SWAP_THROTTLE is always true for this renderer
+   * because we have the call to wait_for_pending_flips on every frame.
+   */
+  COGL_FLAGS_SET (cogl_context->winsys_features,
+                  COGL_WINSYS_FEATURE_SWAP_THROTTLE,
+                  TRUE);
+
 #ifdef HAVE_EGL_DEVICE
   if (renderer_gpu_data->mode == META_RENDERER_NATIVE_MODE_EGL_DEVICE)
     COGL_FLAGS_SET (cogl_context->features,
@@ -2616,8 +2646,12 @@ meta_renderer_native_create_onscreen (MetaRendererNative   *renderer_native,
     }
 
   onscreen = cogl_onscreen_new (context, width, height);
-  cogl_onscreen_set_swap_throttled (onscreen,
-                                    _clutter_get_sync_to_vblank ());
+
+  /* We have wait_for_pending_flips hardcoded, so throttling always. */
+  cogl_onscreen_set_swap_throttled (onscreen, TRUE);
+  if (!_clutter_get_sync_to_vblank ())
+    g_warning ("Request to disable sync-to-vblank is being ignored. "
+               "MetaRendererNative does not support disabling it.");
 
   if (!cogl_framebuffer_allocate (COGL_FRAMEBUFFER (onscreen), error))
     {
@@ -2687,6 +2721,15 @@ meta_renderer_native_create_offscreen (MetaRendererNative    *renderer,
   return fb;
 }
 
+static int64_t
+meta_renderer_native_get_clock_time (CoglContext *context)
+{
+  CoglRenderer *cogl_renderer = cogl_context_get_renderer (context);
+  MetaGpuKms *gpu_kms = cogl_renderer->custom_winsys_user_data;
+
+  return meta_gpu_kms_get_current_time_ns (gpu_kms);
+}
+
 static const CoglWinsysVtable *
 get_native_cogl_winsys_vtable (CoglRenderer *cogl_renderer)
 {
@@ -2715,6 +2758,8 @@ get_native_cogl_winsys_vtable (CoglRenderer *cogl_renderer)
       vtable.onscreen_swap_buffers_with_damage =
         meta_onscreen_native_swap_buffers_with_damage;
 
+      vtable.context_get_clock_time = meta_renderer_native_get_clock_time;
+
       vtable_inited = TRUE;
     }
 
diff --git a/src/meta-marshal.list b/src/meta-marshal.list
new file mode 100644 (file)
index 0000000..c1f4781
--- /dev/null
@@ -0,0 +1 @@
+VOID:OBJECT,OBJECT,INT64