[gtk+/wip/frame-synchronization: 10/19] GdkPaintClockIdle: add throttling to 60fps



commit e09a47b2427c16fd0853584ca771db85e0153390
Author: Owen W. Taylor <otaylor fishsoup net>
Date:   Thu Sep 27 17:55:55 2012 -0400

    GdkPaintClockIdle: add throttling to 60fps
    
    If the backend is throttling paints, then the paint clock will be
    frozen at the end of the frame. If not, then we need to add throttling,
    so wait until 16ms after the start of the frame before beginning the
    next frame.

 gdk/gdkpaintclockidle.c |   50 +++++++++++++++++++++++++++++++++++++---------
 1 files changed, 40 insertions(+), 10 deletions(-)
---
diff --git a/gdk/gdkpaintclockidle.c b/gdk/gdkpaintclockidle.c
index 88f376c..f9f3523 100644
--- a/gdk/gdkpaintclockidle.c
+++ b/gdk/gdkpaintclockidle.c
@@ -29,18 +29,23 @@
 #include "gdkpaintclockidle.h"
 #include "gdk.h"
 
+#define FRAME_INTERVAL 16667 // microseconds
+
 struct _GdkPaintClockIdlePrivate
 {
   GTimer *timer;
   /* timer_base is used to avoid ever going backward */
   guint64 timer_base;
   guint64 frame_time;
+  guint64 min_next_frame_time;
 
   guint idle_id;
   guint freeze_count;
 
   GdkPaintClockPhase requested;
   GdkPaintClockPhase phase;
+
+  guint in_paint_idle : 1;
 };
 
 static gboolean gdk_paint_clock_paint_idle (void *data);
@@ -93,7 +98,7 @@ compute_frame_time (GdkPaintClockIdle *idle)
   guint64 computed_frame_time;
   guint64 elapsed;
 
-  elapsed = ((guint64) (g_timer_elapsed (priv->timer, NULL) * 1000)) + priv->timer_base;
+  elapsed = g_get_monotonic_time () + priv->timer_base;
   if (elapsed < priv->frame_time)
     {
       /* clock went backward. adapt to that by forevermore increasing
@@ -118,7 +123,8 @@ gdk_paint_clock_idle_get_frame_time (GdkPaintClock *clock)
   guint64 computed_frame_time;
 
   /* can't change frame time during a paint */
-  if (priv->phase != GDK_PAINT_CLOCK_PHASE_NONE)
+  if (priv->phase != GDK_PAINT_CLOCK_PHASE_NONE &&
+      priv->phase != GDK_PAINT_CLOCK_PHASE_FLUSH_EVENTS)
     return priv->frame_time;
 
   /* Outside a paint, pick something close to "now" */
@@ -129,7 +135,7 @@ gdk_paint_clock_idle_get_frame_time (GdkPaintClock *clock)
    * get_frame_time() would normally be used outside of a paint to
    * record an animation start time for example.
    */
-  if ((computed_frame_time - priv->frame_time) > 16)
+  if ((computed_frame_time - priv->frame_time) > FRAME_INTERVAL)
     priv->frame_time = computed_frame_time;
 
   return priv->frame_time;
@@ -142,10 +148,20 @@ maybe_start_idle (GdkPaintClockIdle *clock_idle)
 
   if (priv->idle_id == 0 && priv->freeze_count == 0 && priv->requested != 0)
     {
-      priv->idle_id = gdk_threads_add_idle_full (GDK_PRIORITY_REDRAW,
-                                                 gdk_paint_clock_paint_idle,
-                                                 g_object_ref (clock_idle),
-                                                 (GDestroyNotify) g_object_unref);
+      guint min_interval = 0;
+
+      if (priv->min_next_frame_time != 0)
+        {
+          guint64 now = compute_frame_time (clock_idle);
+          guint64 min_interval_us = MAX (priv->min_next_frame_time, now) - now;
+          min_interval = (min_interval_us + 500) / 1000;
+        }
+
+      priv->idle_id = gdk_threads_add_timeout_full (GDK_PRIORITY_REDRAW,
+                                                    min_interval,
+                                                    gdk_paint_clock_paint_idle,
+                                                    g_object_ref (clock_idle),
+                                                    (GDestroyNotify) g_object_unref);
 
       gdk_paint_clock_frame_requested (GDK_PAINT_CLOCK (clock_idle));
     }
@@ -160,14 +176,14 @@ gdk_paint_clock_paint_idle (void *data)
 
   priv->idle_id = 0;
 
-  priv->frame_time = compute_frame_time (clock_idle);
-
   switch (priv->phase)
     {
     case GDK_PAINT_CLOCK_PHASE_NONE:
     case GDK_PAINT_CLOCK_PHASE_BEFORE_PAINT:
       if (priv->freeze_count == 0)
 	{
+          priv->frame_time = compute_frame_time (clock_idle);
+
 	  priv->phase = GDK_PAINT_CLOCK_PHASE_BEFORE_PAINT;
           priv->requested &= ~GDK_PAINT_CLOCK_PHASE_BEFORE_PAINT;
           /* We always emit ::before-paint and ::after-paint even if
@@ -208,7 +224,21 @@ gdk_paint_clock_paint_idle (void *data)
 	}
     }
 
-  maybe_start_idle (clock_idle);
+  priv->in_paint_idle = FALSE;
+
+  if (priv->freeze_count == 0 && priv->requested != 0)
+    {
+      /* We need to start over again immediately - this implies that there is no
+       * throttling at the backend layer, so we need to back-off ourselves.
+       */
+      gdk_flush ();
+      priv->min_next_frame_time = priv->frame_time + FRAME_INTERVAL;
+      maybe_start_idle (clock_idle);
+    }
+  else
+    {
+      priv->min_next_frame_time = 0;
+    }
 
   return FALSE;
 }



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