[mutter] cogl: Make presentation time always MONOTONIC



commit 48101648850c841317e1c04e6ebc13f2adae143d
Author: Ivan Molodetskikh <yalterz gmail com>
Date:   Sat Jan 30 23:14:55 2021 +0300

    cogl: Make presentation time always MONOTONIC
    
    This concerns only the cases when the presentation timestamp is received
    directly from the device (from KMS or from GLX). In the majority of
    cases this timestamp is already MONOTONIC. When it isn't, after this
    commit, the current value of the MONOTONIC clock is sampled instead.
    
    The alternative is to store the clock id alongside the timestamp, with
    possible values of MONOTONIC, REALTIME (from KMS) and GETTIMEOFDAY (from
    GLX; this might be the same as REALTIME, I'm not sure), and then
    "convert" the timestamp to MONOTONIC when needed. An example of such a
    conversion was done in compositor.c (removed in this commit). It would
    also be needed for the presentation-time Wayland protocol. However, it
    seems that the vast majority of up-to-date systems are using MONOTONIC
    anyway, making this effort not justified.
    
    Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1484>

 clutter/clutter/clutter-stage.h            |  2 +-
 cogl/cogl/cogl-frame-info-private.h        |  2 +-
 cogl/cogl/cogl-frame-info.h                |  6 ++--
 cogl/cogl/winsys/cogl-onscreen-glx.c       | 38 +++++++++++++++++++++-----
 src/backends/native/meta-gpu-kms.c         |  6 ++++
 src/backends/native/meta-gpu-kms.h         |  2 ++
 src/backends/native/meta-onscreen-native.c | 44 +++++++++++++++++++-----------
 src/compositor/compositor.c                | 29 +-------------------
 8 files changed, 72 insertions(+), 57 deletions(-)
---
diff --git a/clutter/clutter/clutter-stage.h b/clutter/clutter/clutter-stage.h
index 758cfade68..9c4abfe536 100644
--- a/clutter/clutter/clutter-stage.h
+++ b/clutter/clutter/clutter-stage.h
@@ -127,7 +127,7 @@ typedef enum
 struct _ClutterFrameInfo
 {
   int64_t frame_counter;
-  int64_t presentation_time;
+  int64_t presentation_time; /* microseconds; CLOCK_MONOTONIC */
   float refresh_rate;
 
   ClutterFrameInfoFlag flags;
diff --git a/cogl/cogl/cogl-frame-info-private.h b/cogl/cogl/cogl-frame-info-private.h
index 35d2bd97f9..debeb5dffe 100644
--- a/cogl/cogl/cogl-frame-info-private.h
+++ b/cogl/cogl/cogl-frame-info-private.h
@@ -47,7 +47,7 @@ struct _CoglFrameInfo
   CoglObject _parent;
 
   int64_t frame_counter;
-  int64_t presentation_time_us;
+  int64_t presentation_time_us; /* CLOCK_MONOTONIC */
   float refresh_rate;
 
   int64_t global_frame_counter;
diff --git a/cogl/cogl/cogl-frame-info.h b/cogl/cogl/cogl-frame-info.h
index 97f0d7c146..c128bf8209 100644
--- a/cogl/cogl/cogl-frame-info.h
+++ b/cogl/cogl/cogl-frame-info.h
@@ -97,11 +97,9 @@ int64_t cogl_frame_info_get_frame_counter (CoglFrameInfo *info);
  * the frame became visible to the user.
  *
  * The presentation time measured in microseconds, is based on
- * cogl_get_clock_time().
+ * CLOCK_MONOTONIC.
  *
- * <note>Linux kernel version less that 3.8 can result in
- * non-monotonic timestamps being reported when using a drm based
- * OpenGL driver. Also some buggy Mesa drivers up to 9.0.1 may also
+ * <note>Some buggy Mesa drivers up to 9.0.1 may
  * incorrectly report non-monotonic timestamps.</note>
  *
  * Return value: the presentation time for the frame
diff --git a/cogl/cogl/winsys/cogl-onscreen-glx.c b/cogl/cogl/winsys/cogl-onscreen-glx.c
index 19aca8cf72..6526ae799f 100644
--- a/cogl/cogl/winsys/cogl-onscreen-glx.c
+++ b/cogl/cogl/winsys/cogl-onscreen-glx.c
@@ -454,6 +454,17 @@ ust_to_microseconds (CoglRenderer *renderer,
   return 0;
 }
 
+static gboolean
+is_ust_monotonic (CoglRenderer *renderer,
+                  GLXDrawable   drawable)
+{
+  CoglGLXRenderer *glx_renderer = renderer->winsys;
+
+  ensure_ust_type (renderer, drawable);
+
+  return (glx_renderer->ust_type == COGL_GLX_UST_IS_MONOTONIC_TIME);
+}
+
 static void
 _cogl_winsys_wait_for_vblank (CoglOnscreen *onscreen)
 {
@@ -482,10 +493,19 @@ _cogl_winsys_wait_for_vblank (CoglOnscreen *onscreen)
           glx_renderer->glXWaitForMsc (xlib_renderer->xdpy, drawable,
                                        0, 1, 0,
                                        &ust, &msc, &sbc);
-          info->presentation_time_us = ust_to_microseconds (ctx->display->renderer,
-                                                            drawable,
-                                                            ust);
-          info->flags |= COGL_FRAME_INFO_FLAG_HW_CLOCK;
+
+          if (is_ust_monotonic (ctx->display->renderer, drawable))
+            {
+              info->presentation_time_us =
+                ust_to_microseconds (ctx->display->renderer,
+                                     drawable,
+                                     ust);
+              info->flags |= COGL_FRAME_INFO_FLAG_HW_CLOCK;
+            }
+          else
+            {
+              info->presentation_time_us = g_get_monotonic_time ();
+            }
         }
       else
         {
@@ -962,16 +982,20 @@ cogl_onscreen_glx_notify_swap_buffers (CoglOnscreen          *onscreen,
                                        GLXBufferSwapComplete *swap_event)
 {
   CoglOnscreenGlx *onscreen_glx = COGL_ONSCREEN_GLX (onscreen);
+  CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
+  CoglContext *context = cogl_framebuffer_get_context (framebuffer);
+  gboolean ust_is_monotonic;
 
   /* We only want to notify that the swap is complete when the
      application calls cogl_context_dispatch so instead of immediately
      notifying we'll set a flag to remember to notify later */
   set_sync_pending (onscreen);
 
-  if (swap_event->ust != 0)
+  ust_is_monotonic = is_ust_monotonic (context->display->renderer,
+                                       onscreen_glx->glxwin);
+
+  if (swap_event->ust != 0 && ust_is_monotonic)
     {
-      CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
-      CoglContext *context = cogl_framebuffer_get_context (framebuffer);
       CoglFrameInfo *info;
 
       info = cogl_onscreen_peek_head_frame_info (onscreen);
diff --git a/src/backends/native/meta-gpu-kms.c b/src/backends/native/meta-gpu-kms.c
index 643c258829..7cec11325c 100644
--- a/src/backends/native/meta-gpu-kms.c
+++ b/src/backends/native/meta-gpu-kms.c
@@ -143,6 +143,12 @@ meta_gpu_kms_get_current_time_ns (MetaGpuKms *gpu_kms)
   return timespec_to_nanoseconds (&ts);
 }
 
+gboolean
+meta_gpu_kms_is_clock_monotonic (MetaGpuKms *gpu_kms)
+{
+  return gpu_kms->clock_id == CLOCK_MONOTONIC;
+}
+
 gboolean
 meta_gpu_kms_is_boot_vga (MetaGpuKms *gpu_kms)
 {
diff --git a/src/backends/native/meta-gpu-kms.h b/src/backends/native/meta-gpu-kms.h
index d75c8a40fd..8af89b0ce5 100644
--- a/src/backends/native/meta-gpu-kms.h
+++ b/src/backends/native/meta-gpu-kms.h
@@ -59,6 +59,8 @@ const char * meta_gpu_kms_get_file_path (MetaGpuKms *gpu_kms);
 
 int64_t meta_gpu_kms_get_current_time_ns (MetaGpuKms *gpu_kms);
 
+gboolean meta_gpu_kms_is_clock_monotonic (MetaGpuKms *gpu_kms);
+
 void meta_gpu_kms_set_power_save_mode (MetaGpuKms    *gpu_kms,
                                        uint64_t       state,
                                        MetaKmsUpdate *kms_update);
diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c
index 0fd6e237e5..3c81c9601d 100644
--- a/src/backends/native/meta-onscreen-native.c
+++ b/src/backends/native/meta-onscreen-native.c
@@ -259,15 +259,35 @@ page_flip_feedback_flipped (MetaKmsCrtc  *kms_crtc,
 {
   MetaRendererView *view = user_data;
   struct timeval page_flip_time;
+  MetaCrtc *crtc;
+  MetaGpuKms *gpu_kms;
+  int64_t presentation_time_us;
+  CoglFrameInfoFlag flags = COGL_FRAME_INFO_FLAG_NONE;
 
   page_flip_time = (struct timeval) {
     .tv_sec = tv_sec,
     .tv_usec = tv_usec,
   };
 
+  crtc = META_CRTC (meta_crtc_kms_from_kms_crtc (kms_crtc));
+  gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (crtc));
+  if (meta_gpu_kms_is_clock_monotonic (gpu_kms))
+    {
+      presentation_time_us = timeval_to_microseconds (&page_flip_time);
+      flags |= COGL_FRAME_INFO_FLAG_HW_CLOCK;
+    }
+  else
+    {
+      /*
+       * Other parts of the code assume MONOTONIC timestamps. So, if the device
+       * timestamp isn't MONOTONIC, don't use it.
+       */
+      presentation_time_us = g_get_monotonic_time ();
+    }
+
   notify_view_crtc_presented (view, kms_crtc,
-                              timeval_to_microseconds (&page_flip_time),
-                              COGL_FRAME_INFO_FLAG_HW_CLOCK);
+                              presentation_time_us,
+                              flags);
 }
 
 static void
@@ -291,22 +311,18 @@ page_flip_feedback_mode_set_fallback (MetaKmsCrtc *kms_crtc,
                                       gpointer     user_data)
 {
   MetaRendererView *view = user_data;
-  MetaCrtc *crtc;
-  MetaGpuKms *gpu_kms;
-  int64_t now_ns;
+  int64_t now_us;
 
   /*
    * We ended up not page flipping, thus we don't have a presentation time to
    * use. Lets use the next best thing: the current time.
    */
 
-  crtc = META_CRTC (meta_crtc_kms_from_kms_crtc (kms_crtc));
-  gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (crtc));
-  now_ns = meta_gpu_kms_get_current_time_ns (gpu_kms);
+  now_us = g_get_monotonic_time ();
 
   notify_view_crtc_presented (view,
                               kms_crtc,
-                              ns2us (now_ns),
+                              now_us,
                               COGL_FRAME_INFO_FLAG_NONE);
 }
 
@@ -316,9 +332,7 @@ page_flip_feedback_discarded (MetaKmsCrtc  *kms_crtc,
                               const GError *error)
 {
   MetaRendererView *view = user_data;
-  MetaCrtc *crtc;
-  MetaGpuKms *gpu_kms;
-  int64_t now_ns;
+  int64_t now_us;
 
   /*
    * Page flipping failed, but we want to fail gracefully, so to avoid freezing
@@ -331,13 +345,11 @@ page_flip_feedback_discarded (MetaKmsCrtc  *kms_crtc,
                         G_IO_ERROR_PERMISSION_DENIED))
     g_warning ("Page flip discarded: %s", error->message);
 
-  crtc = META_CRTC (meta_crtc_kms_from_kms_crtc (kms_crtc));
-  gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (crtc));
-  now_ns = meta_gpu_kms_get_current_time_ns (gpu_kms);
+  now_us = g_get_monotonic_time ();
 
   notify_view_crtc_presented (view,
                               kms_crtc,
-                              ns2us (now_ns),
+                              now_us,
                               COGL_FRAME_INFO_FLAG_NONE);
 }
 
diff --git a/src/compositor/compositor.c b/src/compositor/compositor.c
index 639d6225c7..1770550d4c 100644
--- a/src/compositor/compositor.c
+++ b/src/compositor/compositor.c
@@ -1033,36 +1033,9 @@ on_presented (ClutterStage     *stage,
 {
   MetaCompositorPrivate *priv =
     meta_compositor_get_instance_private (compositor);
-  int64_t presentation_time_cogl = frame_info->presentation_time;
-  int64_t presentation_time;
+  int64_t presentation_time = frame_info->presentation_time;
   GList *l;
 
-  if (presentation_time_cogl != 0)
-    {
-      int64_t current_cogl_time;
-      int64_t current_monotonic_time;
-
-      /* Cogl reports presentation in terms of its own clock, which is
-       * guaranteed to be in nanoseconds but with no specified base. The
-       * normal case with the open source GPU drivers on Linux 3.8 and
-       * newer is that the base of cogl_get_clock_time() is that of
-       * clock_gettime(CLOCK_MONOTONIC), so the same as g_get_monotonic_time),
-       * but there's no exposure of that through the API. clock_gettime()
-       * is fairly fast, so calling it twice and subtracting to get a
-       * nearly-zero number is acceptable, if a little ugly.
-       */
-      current_cogl_time = cogl_get_clock_time (priv->context);
-      current_monotonic_time = g_get_monotonic_time ();
-
-      presentation_time =
-        current_monotonic_time +
-        (presentation_time_cogl - current_cogl_time) / 1000;
-    }
-  else
-    {
-      presentation_time = 0;
-    }
-
   for (l = priv->windows; l; l = l->next)
     {
       ClutterActor *actor = l->data;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]