[mutter/gnome-3-24] monitor: Handle tiled monitors where (0, 0) is not the main output



commit a3b4ee568985ddb10cc71f58c5c937c1dd86581f
Author: Jonas Ådahl <jadahl gmail com>
Date:   Mon May 1 12:01:41 2017 +0800

    monitor: Handle tiled monitors where (0, 0) is not the main output
    
    In some circumstances, the origin tile (0, 0) is not the one that
    should be used to drive the monitor when using a non-tiled mode. Update
    MetaMonitorTiled to support this case. It also seems to be so that the
    preferred mode might be some low resolution or bogus mode on these
    monitors, so also adapt MetaMonitorTiled to manage to ignore the
    preferred mode of a tiled monitor if the preferred mode doesn't use
    both tiles.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=781723

 src/backends/meta-monitor.c |  296 +++++++++++++++++++++++++++++++++++++++----
 1 files changed, 269 insertions(+), 27 deletions(-)
---
diff --git a/src/backends/meta-monitor.c b/src/backends/meta-monitor.c
index 279e5f3..7d67989 100644
--- a/src/backends/meta-monitor.c
+++ b/src/backends/meta-monitor.c
@@ -69,6 +69,10 @@ struct _MetaMonitorTiled
 
   uint32_t tile_group_id;
 
+  /* The tile (0, 0) output. */
+  MetaOutput *origin_output;
+
+  /* The output enabled even when a non-tiled mode is used. */
   MetaOutput *main_output;
 };
 
@@ -447,7 +451,7 @@ add_tiled_monitor_outputs (MetaMonitorManager *monitor_manager,
         continue;
 
       g_warn_if_fail (output->subpixel_order ==
-                      monitor_tiled->main_output->subpixel_order);
+                      monitor_tiled->origin_output->subpixel_order);
 
       monitor_priv->outputs = g_list_append (monitor_priv->outputs, output);
     }
@@ -541,15 +545,45 @@ is_crtc_mode_tiled (MetaOutput   *output,
           crtc_mode->height == (int) output->tile_info.tile_h);
 }
 
+static MetaCrtcMode *
+find_tiled_crtc_mode (MetaOutput *output,
+                      float       refresh_rate)
+{
+  MetaCrtcMode *crtc_mode;
+  unsigned int i;
+
+  crtc_mode = output->preferred_mode;
+  if (is_crtc_mode_tiled (output, crtc_mode))
+    return crtc_mode;
+
+  for (i = 0; i < output->n_modes; i++)
+    {
+      crtc_mode = output->modes[i];
+
+      if (!is_crtc_mode_tiled (output, crtc_mode))
+        continue;
+
+      if (crtc_mode->refresh_rate != refresh_rate)
+        continue;
+
+      return crtc_mode;
+    }
+
+  return NULL;
+}
+
 static MetaMonitorMode *
-create_tiled_monitor_mode (MetaMonitorTiled *monitor_tiled)
+create_tiled_monitor_mode (MetaMonitorTiled *monitor_tiled,
+                           float             refresh_rate,
+                           gboolean         *out_is_preferred)
 {
   MetaMonitor *monitor = META_MONITOR (monitor_tiled);
   MetaMonitorPrivate *monitor_priv =
     meta_monitor_get_instance_private (monitor);
   MetaMonitorMode *mode;
   GList *l;
-  int i;
+  unsigned int i;
+  gboolean is_preferred = TRUE;
 
   mode = g_new0 (MetaMonitorMode, 1);
   meta_monitor_tiled_calculate_tiled_size (monitor,
@@ -560,29 +594,93 @@ create_tiled_monitor_mode (MetaMonitorTiled *monitor_tiled)
   for (l = monitor_priv->outputs, i = 0; l; l = l->next, i++)
     {
       MetaOutput *output = l->data;
-      MetaCrtcMode *preferred_crtc_mode = output->preferred_mode;
+      MetaCrtcMode *tiled_crtc_mode;
       int x;
       int y;
 
+      tiled_crtc_mode = find_tiled_crtc_mode (output, refresh_rate);
+      if (!tiled_crtc_mode)
+        {
+          g_warning ("No tiled mode with refresh rate %f on %s",
+                     refresh_rate, output->name);
+          meta_monitor_mode_free (mode);
+          return NULL;
+        }
+
       calculate_tile_coordinate (monitor, output, &x, &y);
 
       mode->crtc_modes[i] = (MetaMonitorCrtcMode) {
         .x = x,
         .y = y,
         .output = output,
-        .crtc_mode = preferred_crtc_mode
+        .crtc_mode = tiled_crtc_mode
       };
 
-      g_warn_if_fail (mode->spec.refresh_rate == 0.0f ||
-                      (mode->spec.refresh_rate ==
-                       preferred_crtc_mode->refresh_rate));
+      mode->spec.refresh_rate = refresh_rate;
 
-      mode->spec.refresh_rate = preferred_crtc_mode->refresh_rate;
+      is_preferred = is_preferred && tiled_crtc_mode == output->preferred_mode;
     }
 
+  *out_is_preferred = is_preferred;
+
   return mode;
 }
 
+static void
+generate_tiled_monitor_modes (MetaMonitorTiled *monitor_tiled)
+{
+  MetaMonitor *monitor = META_MONITOR (monitor_tiled);
+  MetaMonitorPrivate *monitor_priv =
+    meta_monitor_get_instance_private (monitor);
+  MetaOutput *main_output;
+  GList *tiled_modes = NULL;
+  unsigned int i;
+
+  main_output = meta_monitor_get_main_output (META_MONITOR (monitor_tiled));
+
+  for (i = 0; i < main_output->n_modes; i++)
+    {
+      MetaCrtcMode *crtc_mode = main_output->modes[i];
+      MetaMonitorMode *mode;
+      gboolean is_preferred;
+
+      if (!is_crtc_mode_tiled (main_output, crtc_mode))
+        continue;
+
+      mode = create_tiled_monitor_mode (monitor_tiled, crtc_mode->refresh_rate,
+                                        &is_preferred);
+      if (!mode)
+        continue;
+
+      tiled_modes = g_list_append (tiled_modes, mode);
+
+      if (is_monitor_mode_assigned (monitor, mode))
+        monitor_priv->current_mode = mode;
+
+      if (is_preferred)
+        monitor_priv->preferred_mode = mode;
+    }
+
+  if (!monitor_priv->preferred_mode)
+    {
+      MetaMonitorMode *best_mode = NULL;
+      GList *l;
+
+      for (l = tiled_modes; l; l = l->next)
+        {
+          MetaMonitorMode *mode = l->data;
+
+          if (!best_mode ||
+              mode->spec.refresh_rate > best_mode->spec.refresh_rate)
+            best_mode = mode;
+        }
+
+      monitor_priv->preferred_mode = best_mode;
+    }
+
+  monitor_priv->modes = g_list_concat (monitor_priv->modes, tiled_modes);
+}
+
 static MetaMonitorMode *
 create_untiled_monitor_mode (MetaMonitorTiled *monitor_tiled,
                              MetaOutput       *main_output,
@@ -633,42 +731,184 @@ create_untiled_monitor_mode (MetaMonitorTiled *monitor_tiled,
   return mode;
 }
 
-static void
-meta_monitor_tiled_generate_modes (MetaMonitorTiled *monitor_tiled)
+static int
+count_untiled_crtc_modes (MetaOutput *output)
+{
+  int count;
+  unsigned int i;
+
+  count = 0;
+  for (i = 0; i < output->n_modes; i++)
+    {
+      MetaCrtcMode *crtc_mode = output->modes[i];
+
+      if (!is_crtc_mode_tiled (output, crtc_mode))
+        count++;
+    }
+
+  return count;
+}
+
+static MetaOutput *
+find_untiled_output (MetaMonitorTiled *monitor_tiled)
 {
   MetaMonitor *monitor = META_MONITOR (monitor_tiled);
   MetaMonitorPrivate *monitor_priv =
     meta_monitor_get_instance_private (monitor);
-  MetaMonitorMode *mode;
-  MetaOutput *main_output;
-  unsigned int i;
+  MetaOutput *best_output;
+  int best_untiled_crtc_mode_count;
+  GList *l;
 
-  mode = create_tiled_monitor_mode (monitor_tiled);
-  monitor_priv->modes = g_list_append (monitor_priv->modes, mode);
+  best_output = monitor_tiled->origin_output;
+  best_untiled_crtc_mode_count =
+    count_untiled_crtc_modes (monitor_tiled->origin_output);
 
-  monitor_priv->preferred_mode = mode;
+  for (l = monitor_priv->outputs; l; l = l->next)
+    {
+      MetaOutput *output = l->data;
+      int untiled_crtc_mode_count;
 
-  if (is_monitor_mode_assigned (monitor, mode))
-    monitor_priv->current_mode = mode;
+      if (output == monitor_tiled->origin_output)
+        continue;
+
+      untiled_crtc_mode_count = count_untiled_crtc_modes (output);
+      if (untiled_crtc_mode_count > best_untiled_crtc_mode_count)
+        {
+          best_untiled_crtc_mode_count = untiled_crtc_mode_count;
+          best_output = output;
+        }
+    }
+
+  return best_output;
+}
+
+static void
+generate_untiled_monitor_modes (MetaMonitorTiled *monitor_tiled)
+{
+  MetaMonitor *monitor = META_MONITOR (monitor_tiled);
+  MetaMonitorPrivate *monitor_priv =
+    meta_monitor_get_instance_private (monitor);
+  MetaOutput *main_output;
+  unsigned int i;
 
   main_output = meta_monitor_get_main_output (monitor);
+
   for (i = 0; i < main_output->n_modes; i++)
     {
       MetaCrtcMode *crtc_mode = main_output->modes[i];
+      MetaMonitorMode *mode;
 
       mode = create_untiled_monitor_mode (monitor_tiled,
                                           main_output,
                                           crtc_mode);
-      if (mode)
+      if (!mode)
+        continue;
+
+      monitor_priv->modes = g_list_append (monitor_priv->modes, mode);
+
+      if (is_monitor_mode_assigned (monitor, mode))
         {
-          monitor_priv->modes = g_list_append (monitor_priv->modes, mode);
+          g_assert (!monitor_priv->current_mode);
+          monitor_priv->current_mode = mode;
+        }
+
+      if (!monitor_priv->preferred_mode &&
+          crtc_mode == main_output->preferred_mode)
+        monitor_priv->preferred_mode = mode;
+    }
+}
 
-          if (is_monitor_mode_assigned (monitor, mode))
-            {
-              g_assert (!monitor_priv->current_mode);
-              monitor_priv->current_mode = mode;
-            }
+static MetaMonitorMode *
+find_best_mode (MetaMonitor *monitor)
+{
+  MetaMonitorPrivate *monitor_priv =
+    meta_monitor_get_instance_private (monitor);
+  MetaMonitorMode *best_mode = NULL;
+  GList *l;
+
+  for (l = monitor_priv->modes; l; l = l->next)
+    {
+      MetaMonitorMode *mode = l->data;
+      int area, best_area;
+
+      if (!best_mode)
+        {
+          best_mode = mode;
+          continue;
+        }
+
+      area = mode->spec.width * mode->spec.height;
+      best_area = best_mode->spec.width * best_mode->spec.height;
+      if (area > best_area)
+        {
+          best_mode = mode;
+          continue;
         }
+
+      if (mode->spec.refresh_rate > best_mode->spec.refresh_rate)
+        {
+          best_mode = mode;
+          continue;
+        }
+    }
+
+  return best_mode;
+}
+
+static void
+meta_monitor_tiled_generate_modes (MetaMonitorTiled *monitor_tiled)
+{
+  MetaMonitor *monitor = META_MONITOR (monitor_tiled);
+  MetaMonitorPrivate *monitor_priv =
+    meta_monitor_get_instance_private (monitor);
+
+  /*
+   * Tiled monitors may look a bit different from each other, depending on the
+   * monitor itself, the driver, etc.
+   *
+   * On some, the tiled modes will be the preferred CRTC modes, and running
+   * untiled is done by only enabling (0, 0) tile. In this case, things are
+   * pretty straight forward.
+   *
+   * Other times a monitor may have some bogus mode preferred on the main tile,
+   * and an untiled mode preferred on the non-main tile, and there seems to be
+   * no guarantee that the (0, 0) tile is the one that should drive the
+   * non-tiled mode.
+   *
+   * To handle both these cases, the following hueristics are implemented:
+   *
+   *  1) Find all the tiled CRTC modes of the (0, 0) tile, and create tiled
+   *     monitor modes for all tiles based on these.
+   *  2) If there is any tiled monitor mode combination where all CRTC modes
+   *     are the preferred ones, that one is marked as preferred.
+   *  3) If there is no preferred mode determined so far, assume the tiled
+   *     monitor mode with the highest refresh rate is preferred.
+   *  4) Find the tile with highest number of untiled CRTC modes available,
+   *     assume this is the one driving the monitor in untiled mode, and
+   *     create monitor modes for all untiled CRTC modes of that tile. If
+   *     there is still no preferred mode, set any untiled mode as preferred
+   *     if the CRTC mode is marked as such.
+   *  5) If at this point there is still no preferred mode, just pick the one
+   *     with the highest number of pixels and highest refresh rate.
+   *
+   * Note that this ignores the preference if the preference is a non-tiled
+   * mode. This seems to be the case on some systems, where the user tends to
+   * manually set up the tiled mode anyway.
+   */
+
+  generate_tiled_monitor_modes (monitor_tiled);
+
+  if (!monitor_priv->preferred_mode)
+    g_warning ("Tiled monitor on %s didn't have any tiled modes",
+               monitor_priv->spec->connector);
+
+  generate_untiled_monitor_modes (monitor_tiled);
+
+  if (!monitor_priv->preferred_mode)
+    {
+      g_warning ("Tiled monitor on %s didn't have a valid preferred mode",
+                 monitor_priv->spec->connector);
+      monitor_priv->preferred_mode = find_best_mode (monitor);
     }
 }
 
@@ -687,9 +927,11 @@ meta_monitor_tiled_new (MetaMonitorManager *monitor_manager,
   monitor_tiled->tile_group_id = output->tile_info.group_id;
   monitor_priv->winsys_id = output->winsys_id;
 
-  monitor_tiled->main_output = output;
+  monitor_tiled->origin_output = output;
   add_tiled_monitor_outputs (monitor_manager, monitor_tiled);
 
+  monitor_tiled->main_output = find_untiled_output (monitor_tiled);
+
   meta_monitor_generate_spec (monitor);
 
   meta_monitor_manager_tiled_monitor_added (monitor_manager,


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