[retro-gtk] Add RetroMainLoopSource



commit 959da69d227b403ac6f712174bddd0d3b27b843d
Author: Alexander Mikhaylenko <alexm gnome org>
Date:   Thu Jan 16 19:57:05 2020 +0500

    Add RetroMainLoopSource
    
    This is a custom GSource that supports more precise delays than
    GTimeoutSource, as it counts time in microseconds instead of milliseconds.
    
    Handle rounding error by inserting an extra microsecond when needed.
    
    Take extra care not to miss the ready time by setting the corresponding
    timeout in prepare(), and exit immediately if we've missed the time.
    
    In case we missed it after all, set ready time based on the previous ready
    time if it's available, not on current time.
    
    While this still isn't enough to make the timing 100% consistent, as other
    sources, as well as blocking code, can increase the delay between frames,
    it's an improvement over rounding the delay between frames to milliseconds.

 retro-gtk/meson.build                      |  1 +
 retro-gtk/retro-main-loop-source-private.h | 11 ++++
 retro-gtk/retro-main-loop-source.c         | 86 ++++++++++++++++++++++++++++++
 3 files changed, 98 insertions(+)
---
diff --git a/retro-gtk/meson.build b/retro-gtk/meson.build
index 2db7991..e31ae46 100644
--- a/retro-gtk/meson.build
+++ b/retro-gtk/meson.build
@@ -25,6 +25,7 @@ retro_gtk_sources = [
   'retro-keyboard.c',
   'retro-key-joypad-mapping.c',
   'retro-log.c',
+  'retro-main-loop-source.c',
   'retro-memory-type.c',
   'retro-module.c',
   'retro-module-iterator.c',
diff --git a/retro-gtk/retro-main-loop-source-private.h b/retro-gtk/retro-main-loop-source-private.h
new file mode 100644
index 0000000..9f09cb9
--- /dev/null
+++ b/retro-gtk/retro-main-loop-source-private.h
@@ -0,0 +1,11 @@
+// This file is part of retro-gtk. License: GPL-3.0+.
+
+#pragma once
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+GSource *retro_main_loop_source_new (gdouble framerate);
+
+G_END_DECLS
diff --git a/retro-gtk/retro-main-loop-source.c b/retro-gtk/retro-main-loop-source.c
new file mode 100644
index 0000000..c2be9d7
--- /dev/null
+++ b/retro-gtk/retro-main-loop-source.c
@@ -0,0 +1,86 @@
+// This file is part of retro-gtk. License: GPL-3.0+.
+
+#include "retro-main-loop-source-private.h"
+
+#include <math.h>
+
+typedef struct {
+  GSource parent;
+  gdouble delay;
+  gint64 next_time;
+  gdouble rounding_error;
+} RetroMainLoopSource;
+
+static gboolean
+retro_main_loop_source_prepare (GSource *source,
+                                gint    *timeout)
+{
+  RetroMainLoopSource *self = (RetroMainLoopSource *) source;
+  gint64 delay = self->next_time - g_get_monotonic_time ();
+
+  *timeout = MAX (0, (gint) floor (delay / 1000.0) - 1);
+
+  return delay <= 0;
+}
+
+static gboolean
+retro_main_loop_source_dispatch (GSource     *source,
+                                 GSourceFunc  callback,
+                                 gpointer     user_data)
+{
+  RetroMainLoopSource *self = (RetroMainLoopSource *) source;
+  gboolean result;
+  gint64 time = g_source_get_time (source);
+  gdouble delay;
+
+  if (!callback)
+    return G_SOURCE_REMOVE;
+
+  result = callback (user_data);
+
+  delay = round (self->delay);
+
+  self->rounding_error += (delay - self->delay);
+  if (self->rounding_error >= 1) {
+    delay--;
+    self->rounding_error--;
+  } else if (self->rounding_error <= -1) {
+    delay++;
+    self->rounding_error++;
+  }
+
+  if (self->next_time == 0 || self->next_time + delay <= time)
+    self->next_time = time;
+
+  self->next_time += delay;
+  g_source_set_ready_time (source, self->next_time);
+
+  return result;
+}
+
+static GSourceFuncs retro_main_loop_source_funcs =
+  {
+    retro_main_loop_source_prepare,
+    NULL, /* check */
+    retro_main_loop_source_dispatch,
+    NULL, /* finalize */
+    NULL,
+    NULL,
+  };
+
+GSource *
+retro_main_loop_source_new (gdouble framerate)
+{
+  RetroMainLoopSource *self;
+  GSource *source;
+
+  source = g_source_new (&retro_main_loop_source_funcs,
+                         sizeof (RetroMainLoopSource));
+  self = (RetroMainLoopSource *) source;
+  self->delay = 1000000.0 / framerate;
+  g_source_set_priority (source, G_PRIORITY_DEFAULT_IDLE);
+  g_source_set_ready_time (source, 0);
+  g_source_set_name (source, "RetroMainLoopSource");
+
+  return source;
+}


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