[gtk+] Fix irregular gdk_frame_clock_get_frame_time



commit 3b2f9395905ec2d9696bcf51497781236c95ec63
Author: Daniel van Vugt <daniel van vugt canonical com>
Date:   Fri Sep 15 17:49:12 2017 +0800

    Fix irregular gdk_frame_clock_get_frame_time
    
    This fixes stuttering in animations that rely on the regularity of
    gdk_frame_clock_get_frame_time.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=787665
    
    BEFORE
    gdkgears:
    58 FPS and visibly stuttering
    gnome-maps on a 59.95Hz monitor:
    "paint" g_get_monotonic_time +17278μs, gdk_frame_clock_get_frame_time +17278μs
    "paint" g_get_monotonic_time +17449μs, gdk_frame_clock_get_frame_time +17426μs
    "paint" g_get_monotonic_time +17620μs, gdk_frame_clock_get_frame_time +17600μs
    
    AFTER
    gdkgears:
    60 FPS and smoother
    gnome-maps on a 59.95Hz monitor:
    "paint" g_get_monotonic_time +18228μs, gdk_frame_clock_get_frame_time +16680μs
    "paint" g_get_monotonic_time +15010μs, gdk_frame_clock_get_frame_time +16680μs
    "paint" g_get_monotonic_time +17134μs, gdk_frame_clock_get_frame_time +16680μs

 gdk/gdkframeclockidle.c |   31 ++++++++++++++++++++++++++++++-
 1 files changed, 30 insertions(+), 1 deletions(-)
---
diff --git a/gdk/gdkframeclockidle.c b/gdk/gdkframeclockidle.c
index 0af296c..3714502 100644
--- a/gdk/gdkframeclockidle.c
+++ b/gdk/gdkframeclockidle.c
@@ -123,6 +123,7 @@ gdk_frame_clock_idle_init (GdkFrameClockIdle *frame_clock_idle)
   frame_clock_idle->priv = priv =
     gdk_frame_clock_idle_get_instance_private (frame_clock_idle);
 
+  priv->frame_time = g_get_monotonic_time (); /* more sane than zero */
   priv->freeze_count = 0;
 }
 
@@ -350,9 +351,37 @@ gdk_frame_clock_paint_idle (void *data)
         case GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT:
           if (priv->freeze_count == 0)
             {
-              priv->frame_time = compute_frame_time (clock_idle);
+              gint64 frame_interval = FRAME_INTERVAL;
+              gint64 reset_frame_time;
+              gint64 smoothest_frame_time;
+              gint64 frame_time_error;
+              GdkFrameTimings *prev_timings =
+                gdk_frame_clock_get_current_timings (clock);
+
+              if (prev_timings && prev_timings->refresh_interval)
+                frame_interval = prev_timings->refresh_interval;
+
+              /* We are likely not getting precisely even callbacks in real
+               * time, particularly if the event loop is busy.
+               * This is a documented limitation in the precision of
+               * gdk_threads_add_timeout_full and g_timeout_add_full.
+               *
+               * In order to avoid this imprecision from compounding between
+               * frames and affecting visual smoothness, we correct frame_time
+               * to more precisely match the even refresh interval of the
+               * physical display. This also means we proactively avoid (most)
+               * missed frames before they occur.
+               */
+              smoothest_frame_time = priv->frame_time + frame_interval;
+              reset_frame_time = compute_frame_time (clock_idle);
+              frame_time_error = ABS (reset_frame_time - smoothest_frame_time);
+              if (frame_time_error >= frame_interval)
+                priv->frame_time = reset_frame_time;
+              else
+                priv->frame_time = smoothest_frame_time;
 
               _gdk_frame_clock_begin_frame (clock);
+              /* Note "current" is different now so timings != prev_timings */
               timings = gdk_frame_clock_get_current_timings (clock);
 
               timings->frame_time = priv->frame_time;


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