[gtk+/wip/chergert/quartz-frame-clock: 209/228] quartz: add GdkDisplayLinkSource and sync to frame clock
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+/wip/chergert/quartz-frame-clock: 209/228] quartz: add GdkDisplayLinkSource and sync to frame clock
- Date: Fri, 25 Sep 2015 09:43:01 +0000 (UTC)
commit ba3c26c1da0fea1041cccb382f7ccd0956760a0c
Author: Christian Hergert <christian hergert me>
Date: Fri Sep 4 14:06:55 2015 -0700
quartz: add GdkDisplayLinkSource and sync to frame clock
This is a GSource that will fire when a new frame should be drawn.
It receives callbacks from CVDisplayLink on the high-priority thread
and then marshals that request to the GSource.
gdk/quartz/Makefile.am | 2 +
gdk/quartz/gdkdisplay-quartz.c | 117 +++++++++++++++++++
gdk/quartz/gdkdisplaylinksource.c | 228 +++++++++++++++++++++++++++++++++++++
gdk/quartz/gdkdisplaylinksource.h | 48 ++++++++
gdk/quartz/gdkprivate-quartz.h | 13 ++
gdk/quartz/gdkwindow-quartz.c | 49 ++++++++
6 files changed, 457 insertions(+), 0 deletions(-)
---
diff --git a/gdk/quartz/Makefile.am b/gdk/quartz/Makefile.am
index 8954849..f7c30e9 100644
--- a/gdk/quartz/Makefile.am
+++ b/gdk/quartz/Makefile.am
@@ -28,6 +28,8 @@ libgdk_quartz_la_SOURCES = \
gdkdevicemanager-core-quartz.c \
gdkdevicemanager-core-quartz.h \
gdkdisplay-quartz.c \
+ gdkdisplaylinksource.c \
+ gdkdisplaylinksource.h \
gdkdisplaymanager-quartz.c \
gdkdnd-quartz.c \
gdkdnd-quartz.h \
diff --git a/gdk/quartz/gdkdisplay-quartz.c b/gdk/quartz/gdkdisplay-quartz.c
index 6b4afc8..82a00cd 100644
--- a/gdk/quartz/gdkdisplay-quartz.c
+++ b/gdk/quartz/gdkdisplay-quartz.c
@@ -20,12 +20,14 @@
#include <gdk/gdk.h>
#include <gdk/gdkdisplayprivate.h>
+#include <gdk/gdkframeclockprivate.h>
#include "gdkprivate-quartz.h"
#include "gdkquartzscreen.h"
#include "gdkquartzwindow.h"
#include "gdkquartzdisplay.h"
#include "gdkquartzdevicemanager-core.h"
+#include "gdkdisplaylinksource.h"
struct _GdkQuartzDisplay
@@ -33,6 +35,9 @@ struct _GdkQuartzDisplay
GdkDisplay display;
GList *input_devices;
+
+ GSource *frame_source;
+ GArray *frame_handlers;
};
struct _GdkQuartzDisplayClass
@@ -40,6 +45,12 @@ struct _GdkQuartzDisplayClass
GdkDisplayClass display_class;
};
+typedef struct
+{
+ GdkQuartzFrameCallback callback;
+ GdkWindow *window;
+} FrameHandler;
+
static GdkWindow *
gdk_quartz_display_get_default_group (GdkDisplay *display)
{
@@ -108,6 +119,107 @@ gdk_quartz_display_init_input (GdkDisplay *display)
g_list_free (list);
}
+void
+_gdk_quartz_display_add_frame_callback (GdkDisplay *display,
+ GdkQuartzFrameCallback callback,
+ GdkWindow *window)
+{
+ GdkQuartzDisplay *display_quartz;
+ FrameHandler handler;
+
+ display_quartz = GDK_QUARTZ_DISPLAY (display);
+
+ handler.callback = callback;
+ handler.window = window;
+
+ if (display_quartz->frame_handlers == NULL)
+ display_quartz->frame_handlers = g_array_new (FALSE, FALSE, sizeof (FrameHandler));
+
+ g_array_append_val (display_quartz->frame_handlers, handler);
+
+ if (display_quartz->frame_handlers->len == 1)
+ gdk_display_link_source_unpause ((GdkDisplayLinkSource *)display_quartz->frame_source);
+}
+
+void
+_gdk_quartz_display_remove_frame_callback (GdkDisplay *display,
+ GdkQuartzFrameCallback callback,
+ GdkWindow *window)
+{
+ GdkQuartzDisplay *display_quartz;
+ gint i;
+
+ display_quartz = GDK_QUARTZ_DISPLAY (display);
+
+ for (i = 0; i < display_quartz->frame_handlers->len; i++)
+ {
+ FrameHandler *handler = &g_array_index (display_quartz->frame_handlers, FrameHandler, i);
+
+ if ((handler->window == window) && (handler->callback == callback))
+ {
+ g_array_remove_index_fast (display_quartz->frame_handlers, i);
+ break;
+ }
+ }
+
+ if (display_quartz->frame_handlers->len == 0)
+ gdk_display_link_source_pause ((GdkDisplayLinkSource *)display_quartz->frame_source);
+}
+
+static gboolean
+gdk_quartz_display_frame_cb (gpointer data)
+{
+ GdkDisplayLinkSource *source;
+ GdkQuartzDisplay *display_quartz = data;
+ GdkDisplay *display = data;
+ GArray *frame_handlers;
+ gint64 presentation_time;
+ gint64 now;
+ guint i;
+
+ source = (GdkDisplayLinkSource *)display_quartz->frame_source;
+
+ frame_handlers = display_quartz->frame_handlers;
+ display_quartz->frame_handlers = NULL;
+
+ if (frame_handlers == NULL)
+ {
+ gdk_display_link_source_pause (source);
+ return G_SOURCE_CONTINUE;
+ }
+
+ presentation_time = source->presentation_time;
+ now = g_source_get_time (display_quartz->frame_source);
+
+ for (i = 0; i < frame_handlers->len; i++)
+ {
+ FrameHandler *handler = &g_array_index (frame_handlers, FrameHandler, i);
+
+ handler->callback (display,
+ handler->window,
+ source->refresh_interval,
+ now,
+ source->presentation_time);
+ }
+
+ g_array_unref (frame_handlers);
+
+ return G_SOURCE_CONTINUE;
+}
+
+static void
+gdk_quartz_display_init_display_link (GdkDisplay *display)
+{
+ GdkQuartzDisplay *display_quartz = GDK_QUARTZ_DISPLAY (display);
+
+ display_quartz->frame_source = gdk_display_link_source_new ();
+ g_source_set_callback (display_quartz->frame_source,
+ gdk_quartz_display_frame_cb,
+ display,
+ NULL);
+ g_source_attach (display_quartz->frame_source, NULL);
+}
+
GdkDisplay *
_gdk_quartz_display_open (const gchar *display_name)
{
@@ -129,6 +241,8 @@ _gdk_quartz_display_open (const gchar *display_name)
gdk_quartz_display_init_input (_gdk_display);
+ gdk_quartz_display_init_display_link (_gdk_display);
+
#if 0
/* FIXME: Remove the #if 0 when we have these functions */
_gdk_quartz_dnd_init ();
@@ -284,6 +398,9 @@ gdk_quartz_display_finalize (GObject *object)
g_list_free_full (display_quartz->input_devices, g_object_unref);
+ g_source_unref (display_quartz->frame_source);
+ g_clear_pointer (&display_quartz->frame_handlers, g_array_unref);
+
G_OBJECT_CLASS (gdk_quartz_display_parent_class)->finalize (object);
}
diff --git a/gdk/quartz/gdkdisplaylinksource.c b/gdk/quartz/gdkdisplaylinksource.c
new file mode 100644
index 0000000..202918f
--- /dev/null
+++ b/gdk/quartz/gdkdisplaylinksource.c
@@ -0,0 +1,228 @@
+/* gdkdisplaylinksource.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Christian Hergert <christian hergert me>
+ */
+
+#include "config.h"
+
+#include <mach/mach_time.h>
+
+#include "gdkdisplaylinksource.h"
+
+static gint64 host_to_frame_clock_time (gint64 host_time);
+
+static gboolean
+gdk_display_link_source_prepare (GSource *source,
+ gint *timeout_)
+{
+ GdkDisplayLinkSource *impl = (GdkDisplayLinkSource *)source;
+
+ *timeout_ = -1;
+
+ return impl->needs_dispatch;
+}
+
+static gboolean
+gdk_display_link_source_check (GSource *source)
+{
+ GdkDisplayLinkSource *impl = (GdkDisplayLinkSource *)source;
+ return impl->needs_dispatch;
+}
+
+static gboolean
+gdk_display_link_source_dispatch (GSource *source,
+ GSourceFunc callback,
+ gpointer user_data)
+{
+ GdkDisplayLinkSource *impl = (GdkDisplayLinkSource *)source;
+ gboolean ret = G_SOURCE_CONTINUE;
+
+ impl->needs_dispatch = FALSE;
+
+ if (callback != NULL)
+ ret = callback (user_data);
+
+ return ret;
+}
+
+static void
+gdk_display_link_source_finalize (GSource *source)
+{
+ GdkDisplayLinkSource *impl = (GdkDisplayLinkSource *)source;
+
+ /*
+ * TODO: ask worker to destroy itself, and then unpause it
+ * so that it can do so.
+ */
+
+ CVDisplayLinkStop (impl->display_link);
+ CVDisplayLinkRelease (impl->display_link);
+}
+
+static GSourceFuncs gdk_display_link_source_funcs = {
+ gdk_display_link_source_prepare,
+ gdk_display_link_source_check,
+ gdk_display_link_source_dispatch,
+ gdk_display_link_source_finalize
+};
+
+void
+gdk_display_link_source_pause (GdkDisplayLinkSource *source)
+{
+ CVDisplayLinkStop (source->display_link);
+}
+
+void
+gdk_display_link_source_unpause (GdkDisplayLinkSource *source)
+{
+ CVDisplayLinkStart (source->display_link);
+}
+
+static CVReturn
+gdk_display_link_source_frame_cb (CVDisplayLinkRef display_link,
+ const CVTimeStamp *inNow,
+ const CVTimeStamp *inOutputTime,
+ CVOptionFlags flagsIn,
+ CVOptionFlags *flagsOut,
+ void *user_data)
+{
+ GdkDisplayLinkSource *impl = user_data;
+ GMainContext *context;
+ gint64 presentation_time;
+
+ presentation_time = host_to_frame_clock_time (inOutputTime->hostTime);
+
+ impl->presentation_time = presentation_time;
+ impl->needs_dispatch = TRUE;
+
+ context = g_source_get_context ((GSource *)impl);
+ if (context != NULL)
+ g_main_context_wakeup (context);
+
+ return kCVReturnSuccess;
+}
+
+/**
+ * gdk_display_link_source_new:
+ *
+ * Creates a new #GSource that will activate the dispatch function upon
+ * notification from a CVDisplayLink that a new frame should be drawn.
+ *
+ * Effort is made to keep the transition from the high-priority
+ * CVDisplayLink thread into this GSource lightweight. However, this is
+ * somewhat non-ideal since the best case would be to do the drawing
+ * from the high-priority thread.
+ *
+ * Returns: (transfer full): A newly created #GSource.
+ */
+GSource *
+gdk_display_link_source_new (void)
+{
+ GdkDisplayLinkSource *impl;
+ GSource *source;
+ CVReturn ret;
+ double period;
+
+ source = g_source_new (&gdk_display_link_source_funcs, sizeof *impl);
+ impl = (GdkDisplayLinkSource *)source;
+
+ /*
+ * Create our link based on currently connected displays.
+ * If there are multiple displays, this will be something that tries
+ * to work for all of them. In the future, we may want to explore multiple
+ * links based on the connected displays.
+ */
+ ret = CVDisplayLinkCreateWithActiveCGDisplays (&impl->display_link);
+ if (ret != kCVReturnSuccess)
+ {
+ g_warning ("Failed to initialize CVDisplayLink!");
+ return source;
+ }
+
+ /*
+ * Determine our nominal period between frames.
+ */
+ period = CVDisplayLinkGetActualOutputVideoRefreshPeriod (impl->display_link);
+ if (period == 0.0)
+ period = 1.0 / 60.0;
+ impl->refresh_interval = period * 1000000L;
+
+
+ /*
+ * Wire up our callback to be executed within the high-priority thread.
+ */
+ CVDisplayLinkSetOutputCallback (impl->display_link,
+ gdk_display_link_source_frame_cb,
+ source);
+
+ g_source_set_name (source, "[gdk] quartz frame timer");
+
+ return source;
+}
+
+static gint64
+host_to_frame_clock_time (gint64 host_time)
+{
+ static mach_timebase_info_data_t timebase_info;
+
+ /*
+ * NOTE:
+ *
+ * This code is taken from GLib to match g_get_monotonic_time().
+ */
+ if (timebase_info.denom == 0)
+ {
+ /* This is a fraction that we must use to scale
+ * mach_absolute_time() by in order to reach nanoseconds.
+ *
+ * We've only ever observed this to be 1/1, but maybe it could be
+ * 1000/1 if mach time is microseconds already, or 1/1000 if
+ * picoseconds. Try to deal nicely with that.
+ */
+ mach_timebase_info (&timebase_info);
+
+ /* We actually want microseconds... */
+ if (timebase_info.numer % 1000 == 0)
+ timebase_info.numer /= 1000;
+ else
+ timebase_info.denom *= 1000;
+
+ /* We want to make the numer 1 to avoid having to multiply... */
+ if (timebase_info.denom % timebase_info.numer == 0)
+ {
+ timebase_info.denom /= timebase_info.numer;
+ timebase_info.numer = 1;
+ }
+ else
+ {
+ /* We could just multiply by timebase_info.numer below, but why
+ * bother for a case that may never actually exist...
+ *
+ * Plus -- performing the multiplication would risk integer
+ * overflow. If we ever actually end up in this situation, we
+ * should more carefully evaluate the correct course of action.
+ */
+ mach_timebase_info (&timebase_info); /* Get a fresh copy for a better message */
+ g_error ("Got weird mach timebase info of %d/%d. Please file a bug against GLib.",
+ timebase_info.numer, timebase_info.denom);
+ }
+ }
+
+ return host_time / timebase_info.denom;
+}
diff --git a/gdk/quartz/gdkdisplaylinksource.h b/gdk/quartz/gdkdisplaylinksource.h
new file mode 100644
index 0000000..805a9e0
--- /dev/null
+++ b/gdk/quartz/gdkdisplaylinksource.h
@@ -0,0 +1,48 @@
+/* gdkdisplaylinksource.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Christian Hergert <christian hergert me>
+ */
+
+#ifndef GDK_DISPLAY_LINK_SOURCE_H
+#define GDK_DISPLAY_LINK_SOURCE_H
+
+#include <glib.h>
+
+#include <QuartzCore/QuartzCore.h>
+
+G_BEGIN_DECLS
+
+typedef struct
+{
+ GSource source;
+
+ CVDisplayLinkRef display_link;
+ gint64 refresh_interval;
+
+ volatile gint64 presentation_time;
+ volatile guint needs_dispatch : 1;
+} GdkDisplayLinkSource;
+
+GSource *gdk_display_link_source_new (void);
+void gdk_display_link_source_pause (GdkDisplayLinkSource *source);
+void gdk_display_link_source_unpause (GdkDisplayLinkSource *source);
+
+G_END_DECLS
+
+#endif /* GDK_DISPLAY_LINK_SOURCE_H */
diff --git a/gdk/quartz/gdkprivate-quartz.h b/gdk/quartz/gdkprivate-quartz.h
index 5d2fcc1..67a392a 100644
--- a/gdk/quartz/gdkprivate-quartz.h
+++ b/gdk/quartz/gdkprivate-quartz.h
@@ -125,6 +125,19 @@ void _gdk_quartz_display_create_window_impl (GdkDisplay *display,
GdkWindowAttr *attributes,
gint attributes_mask);
+/* Display methods - frame clock */
+typedef void (*GdkQuartzFrameCallback) (GdkDisplay *display,
+ GdkWindow *window,
+ gint64 refresh_interval,
+ gint64 now,
+ gint64 presentation_time);
+void _gdk_quartz_display_add_frame_callback (GdkDisplay *display,
+ GdkQuartzFrameCallback callback,
+ GdkWindow *window);
+void _gdk_quartz_display_remove_frame_callback (GdkDisplay *display,
+ GdkQuartzFrameCallback callback,
+ GdkWindow *window);
+
/* Display methods - keymap */
GdkKeymap * _gdk_quartz_display_get_keymap (GdkDisplay *display);
diff --git a/gdk/quartz/gdkwindow-quartz.c b/gdk/quartz/gdkwindow-quartz.c
index 62710a2..ee1a272 100644
--- a/gdk/quartz/gdkwindow-quartz.c
+++ b/gdk/quartz/gdkwindow-quartz.c
@@ -22,6 +22,7 @@
#include <gdk/gdk.h>
#include <gdk/gdkdeviceprivate.h>
#include <gdk/gdkdisplayprivate.h>
+#include <gdk/gdkframeclockprivate.h>
#include "gdkwindowimpl.h"
#include "gdkprivate-quartz.h"
@@ -776,6 +777,46 @@ get_nsscreen_for_point (gint x, gint y)
return screen;
}
+static void
+frame_callback (GdkDisplay *display,
+ GdkWindow *window,
+ gint64 refresh_interval,
+ gint64 now,
+ gint64 presentation_time)
+{
+ GdkFrameClock *frame_clock;
+ GdkFrameTimings *timings;
+
+ frame_clock = gdk_window_get_frame_clock (window);
+ if (frame_clock == NULL)
+ return;
+
+ _gdk_frame_clock_thaw (frame_clock);
+
+ timings = gdk_frame_clock_get_current_timings (frame_clock);
+ if (timings == NULL)
+ return;
+
+ timings->refresh_interval = refresh_interval;
+ timings->presentation_time = presentation_time;
+}
+
+static void
+on_frame_clock_before_paint (GdkFrameClock *frame_clock,
+ GdkWindow *window)
+{
+}
+
+static void
+on_frame_clock_after_paint (GdkFrameClock *frame_clock,
+ GdkWindow *window)
+{
+ GdkDisplay *display = gdk_window_get_display (window);
+
+ _gdk_quartz_display_add_frame_callback (display, frame_callback, window);
+ _gdk_frame_clock_freeze (frame_clock);
+}
+
void
_gdk_quartz_display_create_window_impl (GdkDisplay *display,
GdkWindow *window,
@@ -787,6 +828,7 @@ _gdk_quartz_display_create_window_impl (GdkDisplay *display,
{
GdkWindowImplQuartz *impl;
GdkWindowImplQuartz *parent_impl;
+ GdkFrameClock *frame_clock;
GDK_QUARTZ_ALLOC_POOL;
@@ -921,6 +963,13 @@ _gdk_quartz_display_create_window_impl (GdkDisplay *display,
if (attributes_mask & GDK_WA_TYPE_HINT)
gdk_window_set_type_hint (window, attributes->type_hint);
+
+ frame_clock = gdk_window_get_frame_clock (window);
+
+ g_signal_connect (frame_clock, "before-paint",
+ G_CALLBACK (on_frame_clock_before_paint), window);
+ g_signal_connect (frame_clock, "after-paint",
+ G_CALLBACK (on_frame_clock_after_paint), window);
}
void
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]