macos: use video mode for refresh rate and interval
authorChristian Hergert <christian@hergert.me>
Mon, 28 Feb 2022 07:17:40 +0000 (23:17 -0800)
committerChristian Hergert <chergert@redhat.com>
Wed, 16 Mar 2022 19:24:11 +0000 (12:24 -0700)
Using the mode allows better detection of refresh rate and refresh
interval for the CVDisplayLink bridge to GdkFrameClock. Using it can help
ensure that our 144hz displays can actually reach that rather than falling
back to just 60hz.

This will also need future commits to rework the displaylink source to be
per-monitor.

gdk/macos/gdkdisplaylinksource.c
gdk/macos/gdkdisplaylinksource.h

index 8b2f1a013fd7856bd603bab305aad7a80a4d0ee0..fe75eb3a5502e9ef216a0e0ed8ad824f27000eed 100644 (file)
@@ -65,7 +65,7 @@ gdk_display_link_source_dispatch (GSource     *source,
 
   impl->needs_dispatch = FALSE;
 
-  if (callback != NULL)
+  if (!impl->paused && callback != NULL)
     ret = callback (user_data);
 
   return ret;
@@ -76,7 +76,9 @@ gdk_display_link_source_finalize (GSource *source)
 {
   GdkDisplayLinkSource *impl = (GdkDisplayLinkSource *)source;
 
-  CVDisplayLinkStop (impl->display_link);
+  if (!impl->paused)
+    CVDisplayLinkStop (impl->display_link);
+
   CVDisplayLinkRelease (impl->display_link);
 }
 
@@ -90,12 +92,18 @@ static GSourceFuncs gdk_display_link_source_funcs = {
 void
 gdk_display_link_source_pause (GdkDisplayLinkSource *source)
 {
+  g_return_if_fail (source->paused == FALSE);
+
+  source->paused = TRUE;
   CVDisplayLinkStop (source->display_link);
 }
 
 void
 gdk_display_link_source_unpause (GdkDisplayLinkSource *source)
 {
+  g_return_if_fail (source->paused == TRUE);
+
+  source->paused = FALSE;
   CVDisplayLinkStart (source->display_link);
 }
 
@@ -160,16 +168,16 @@ gdk_display_link_source_frame_cb (CVDisplayLinkRef   display_link,
  * Returns: (transfer full): A newly created `GSource`
  */
 GSource *
-gdk_display_link_source_new (CGDirectDisplayID display_id)
+gdk_display_link_source_new (CGDirectDisplayID display_id,
+                             CGDisplayModeRef  mode)
 {
   GdkDisplayLinkSource *impl;
   GSource *source;
-  CVReturn ret;
-  double period;
 
   source = g_source_new (&gdk_display_link_source_funcs, sizeof *impl);
   impl = (GdkDisplayLinkSource *)source;
   impl->display_id = display_id;
+  impl->paused = TRUE;
 
   /* Create DisplayLink for timing information for the display in
    * question so that we can produce graphics for that display at whatever
@@ -178,21 +186,34 @@ gdk_display_link_source_new (CGDirectDisplayID display_id)
   if (CVDisplayLinkCreateWithCGDisplay (display_id, &impl->display_link) != kCVReturnSuccess)
     {
       g_warning ("Failed to initialize CVDisplayLink!");
-      return source;
+      goto failure;
     }
 
-  /*
-   * 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;
-  impl->refresh_rate = 1.0 / period * 1000L;
-
-  /*
-   * Wire up our callback to be executed within the high-priority thread.
-   */
+  impl->refresh_rate = CGDisplayModeGetRefreshRate (mode) * 1000.0;
+
+  if (impl->refresh_rate == 0)
+    {
+      const CVTime time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod (impl->display_link);
+      if (!(time.flags & kCVTimeIsIndefinite))
+        impl->refresh_rate = (double)time.timeScale / (double)time.timeValue * 1000.0;
+    }
+
+  if (impl->refresh_rate != 0)
+    {
+      impl->refresh_interval = 1000000.0 / (double)impl->refresh_rate * 1000.0;
+    }
+  else
+    {
+      double period = CVDisplayLinkGetActualOutputVideoRefreshPeriod (impl->display_link);
+
+      if (period == 0.0)
+        period = 1.0 / 60.0;
+
+      impl->refresh_rate = 1.0 / period * 1000L;
+      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);
@@ -200,6 +221,10 @@ gdk_display_link_source_new (CGDirectDisplayID display_id)
   g_source_set_static_name (source, "[gdk] quartz frame clock");
 
   return source;
+
+failure:
+  g_source_unref (source);
+  return NULL;
 }
 
 static gint64
index bff1c91475295ff88445276262680fcf78da93d1..6f465907a27d07fda2adf18954a11b18aa180c68 100644 (file)
@@ -36,12 +36,14 @@ typedef struct
   CVDisplayLinkRef  display_link;
   gint64            refresh_interval;
   guint             refresh_rate;
+  guint             paused : 1;
 
   volatile gint64   presentation_time;
   volatile guint    needs_dispatch;
 } GdkDisplayLinkSource;
 
-GSource *gdk_display_link_source_new     (CGDirectDisplayID     display_id);
+GSource *gdk_display_link_source_new     (CGDirectDisplayID     display_id,
+                                          CGDisplayModeRef      mode);
 void     gdk_display_link_source_pause   (GdkDisplayLinkSource *source);
 void     gdk_display_link_source_unpause (GdkDisplayLinkSource *source);