Avi Drissman | 3e1a26c | 2022-09-15 20:26:03 | [diff] [blame] | 1 | // Copyright 2014 The Chromium Authors |
dnicoara | 9372a791 | 2014-12-11 01:29:06 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
Joone Hur | d3ae873 | 2018-04-17 18:05:09 | [diff] [blame] | 5 | #include "ui/display/manager/configure_displays_task.h" |
dnicoara | 9372a791 | 2014-12-11 01:29:06 | [diff] [blame] | 6 | |
Gil Dekel | 788edb6 | 2020-11-25 23:41:28 | [diff] [blame] | 7 | #include <cstddef> |
Gil Dekel | c8b8c4ed | 2022-05-04 20:30:38 | [diff] [blame] | 8 | #include <string> |
Gil Dekel | 788edb6 | 2020-11-25 23:41:28 | [diff] [blame] | 9 | |
dnicoara | 9372a791 | 2014-12-11 01:29:06 | [diff] [blame] | 10 | #include "base/auto_reset.h" |
Gil Dekel | 36b941dd | 2021-01-23 00:37:23 | [diff] [blame] | 11 | #include "base/containers/flat_set.h" |
Brett Wilson | b02c0a2 | 2017-09-25 22:34:42 | [diff] [blame] | 12 | #include "base/containers/queue.h" |
Avi Drissman | c149c16 | 2023-01-12 02:16:59 | [diff] [blame^] | 13 | #include "base/functional/bind.h" |
Hans Wennborg | 3930cf3 | 2020-06-17 16:29:52 | [diff] [blame] | 14 | #include "base/logging.h" |
Daniele Castagna | 4f0689b | 2019-10-30 01:16:47 | [diff] [blame] | 15 | #include "base/metrics/histogram_functions.h" |
Gil Dekel | 788edb6 | 2020-11-25 23:41:28 | [diff] [blame] | 16 | #include "base/metrics/histogram_macros.h" |
Gil Dekel | c8b8c4ed | 2022-05-04 20:30:38 | [diff] [blame] | 17 | #include "base/metrics/sparse_histogram.h" |
Gil Dekel | 788edb6 | 2020-11-25 23:41:28 | [diff] [blame] | 18 | #include "base/numerics/safe_conversions.h" |
Mitsuru Oshima | 96ff454 | 2022-05-02 17:31:32 | [diff] [blame] | 19 | #include "ui/display/manager/display_manager_util.h" |
Mark Yacoub | d18a292 | 2020-07-07 01:14:15 | [diff] [blame] | 20 | #include "ui/display/types/display_configuration_params.h" |
Gil Dekel | bbc4035 | 2021-01-05 18:33:56 | [diff] [blame] | 21 | #include "ui/display/types/display_constants.h" |
Gil Dekel | 36b941dd | 2021-01-23 00:37:23 | [diff] [blame] | 22 | #include "ui/display/types/display_mode.h" |
dnicoara | 9372a791 | 2014-12-11 01:29:06 | [diff] [blame] | 23 | #include "ui/display/types/display_snapshot.h" |
| 24 | #include "ui/display/types/native_display_delegate.h" |
| 25 | |
kylechar | 7a067ec | 2017-01-07 01:16:28 | [diff] [blame] | 26 | namespace display { |
dnicoara | 9372a791 | 2014-12-11 01:29:06 | [diff] [blame] | 27 | |
| 28 | namespace { |
| 29 | |
Gil Dekel | c8b8c4ed | 2022-05-04 20:30:38 | [diff] [blame] | 30 | // The epsilon by which a refresh rate value may drift. For example: |
| 31 | // 239.76Hz --> 240Hz. This value was chosen with the consideration of the |
| 32 | // refresh rate value drifts presented in the "Video Formats—Video ID Code and |
| 33 | // Aspect Ratios" table on p.40 of the CTA-861-G standard. |
| 34 | constexpr float kRefreshRateEpsilon = 0.5f; |
| 35 | |
Gil Dekel | 788edb6 | 2020-11-25 23:41:28 | [diff] [blame] | 36 | // Because we do not offer hardware mirroring, the maximal number of external |
| 37 | // displays that can be configured is limited by the number of available CRTCs, |
| 38 | // which is usually three. Since the lifetime of the UMA using this value is one |
| 39 | // year (exp. Nov. 2021), five buckets are more than enough for |
| 40 | // its histogram (between 0 to 4 external monitors). |
| 41 | constexpr int kMaxDisplaysCount = 5; |
| 42 | |
Gil Dekel | c8b8c4ed | 2022-05-04 20:30:38 | [diff] [blame] | 43 | // Consolidates the UMA name prefix creation to one location, since it is used |
| 44 | // in many different call-sites. |
| 45 | const std::string GetUmaNamePrefixForRequest( |
| 46 | const DisplayConfigureRequest& request) { |
| 47 | return request.display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL |
| 48 | ? std::string("ConfigureDisplays.Internal.Modeset.") |
| 49 | : std::string("ConfigureDisplays.External.Modeset."); |
| 50 | } |
| 51 | |
Gil Dekel | 66a97bc | 2022-04-29 04:21:50 | [diff] [blame] | 52 | // Find the next best mode that is smaller than |request->mode|. The next best |
| 53 | // mode is found by comparing resolutions, and if those are similar, comparing |
| 54 | // refresh rates. If no mode is found, return nullptr. |
| 55 | const DisplayMode* FindNextMode(const DisplayConfigureRequest& request) { |
| 56 | DCHECK(request.mode); |
Gil Dekel | bbc4035 | 2021-01-05 18:33:56 | [diff] [blame] | 57 | |
Gil Dekel | 66a97bc | 2022-04-29 04:21:50 | [diff] [blame] | 58 | // Internal displays are restricted to their native mode. We do not |
| 59 | // attempt to downgrade their modes upon failure. |
| 60 | if (request.display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL) |
dnicoara | a89c208 | 2015-01-05 16:49:06 | [diff] [blame] | 61 | return nullptr; |
| 62 | |
Gil Dekel | 66a97bc | 2022-04-29 04:21:50 | [diff] [blame] | 63 | if (request.display->modes().size() <= 1) |
| 64 | return nullptr; |
| 65 | |
dnicoara | 9372a791 | 2014-12-11 01:29:06 | [diff] [blame] | 66 | const DisplayMode* best_mode = nullptr; |
Gil Dekel | 66a97bc | 2022-04-29 04:21:50 | [diff] [blame] | 67 | for (const auto& mode : request.display->modes()) { |
| 68 | if (*mode < *request.mode && (!best_mode || *mode > *best_mode)) |
dbasehore | 01e9004 | 2016-05-27 06:16:51 | [diff] [blame] | 69 | best_mode = mode.get(); |
dnicoara | 9372a791 | 2014-12-11 01:29:06 | [diff] [blame] | 70 | } |
| 71 | |
| 72 | return best_mode; |
| 73 | } |
| 74 | |
Gil Dekel | bbc4035 | 2021-01-05 18:33:56 | [diff] [blame] | 75 | void LogIfInvalidRequestForInternalDisplay( |
| 76 | const DisplayConfigureRequest& request) { |
| 77 | if (request.display->type() != DISPLAY_CONNECTION_TYPE_INTERNAL) |
| 78 | return; |
| 79 | |
| 80 | if (request.mode == nullptr) |
| 81 | return; |
| 82 | |
| 83 | if (request.mode == request.display->native_mode()) |
| 84 | return; |
| 85 | |
| 86 | LOG(ERROR) << "A mode other than the preferred mode was requested for the " |
| 87 | "internal display: preferred=" |
| 88 | << request.display->native_mode()->ToString() |
| 89 | << " vs. requested=" << request.mode->ToString() |
| 90 | << ". Current mode=" |
| 91 | << (request.display->current_mode() |
| 92 | ? request.display->current_mode()->ToString() |
| 93 | : "nullptr (disabled)") |
| 94 | << "."; |
| 95 | } |
| 96 | |
Daniele Castagna | 4f0689b | 2019-10-30 01:16:47 | [diff] [blame] | 97 | // Samples used to define buckets used by DisplayResolution enum. |
| 98 | // The enum is used to record screen resolution statistics. |
| 99 | const int32_t kDisplayResolutionSamples[] = {1024, 1280, 1440, 1920, |
| 100 | 2560, 3840, 5120, 7680}; |
| 101 | |
Gil Dekel | c8b8c4ed | 2022-05-04 20:30:38 | [diff] [blame] | 102 | void UpdateResolutionUma(const DisplayConfigureRequest& request, |
| 103 | const std::string& uma_name) { |
| 104 | // Display is powered off. |
| 105 | if (!request.mode) |
| 106 | return; |
Daniele Castagna | 4f0689b | 2019-10-30 01:16:47 | [diff] [blame] | 107 | |
Gil Dekel | c8b8c4ed | 2022-05-04 20:30:38 | [diff] [blame] | 108 | // First, compute the index of the enum DisplayResolution. |
| 109 | // The index has to match the definition of the enum in enums.xml. |
| 110 | const uint32_t samples_list_size = std::size(kDisplayResolutionSamples); |
| 111 | const gfx::Size size = request.mode->size(); |
Daniele Castagna | 4f0689b | 2019-10-30 01:16:47 | [diff] [blame] | 112 | uint32_t width_idx = 0; |
| 113 | uint32_t height_idx = 0; |
Gil Dekel | c8b8c4ed | 2022-05-04 20:30:38 | [diff] [blame] | 114 | for (; width_idx < samples_list_size; width_idx++) { |
Daniele Castagna | 4f0689b | 2019-10-30 01:16:47 | [diff] [blame] | 115 | if (size.width() <= kDisplayResolutionSamples[width_idx]) |
| 116 | break; |
| 117 | } |
Gil Dekel | c8b8c4ed | 2022-05-04 20:30:38 | [diff] [blame] | 118 | for (; height_idx < samples_list_size; height_idx++) { |
Daniele Castagna | 4f0689b | 2019-10-30 01:16:47 | [diff] [blame] | 119 | if (size.height() <= kDisplayResolutionSamples[height_idx]) |
| 120 | break; |
| 121 | } |
| 122 | |
Gil Dekel | c8b8c4ed | 2022-05-04 20:30:38 | [diff] [blame] | 123 | int display_resolution_index = 0; |
| 124 | if (width_idx == samples_list_size || height_idx == samples_list_size) { |
| 125 | // Check if we are in the overflow bucket. |
| 126 | display_resolution_index = samples_list_size * samples_list_size + 1; |
| 127 | } else { |
| 128 | // Compute the index of DisplayResolution, starting from 1, since 0 is used |
| 129 | // when powering off the display. |
| 130 | display_resolution_index = width_idx * samples_list_size + height_idx + 1; |
| 131 | } |
| 132 | |
| 133 | base::UmaHistogramExactLinear(uma_name, display_resolution_index, |
| 134 | samples_list_size * samples_list_size + 2); |
Daniele Castagna | 4f0689b | 2019-10-30 01:16:47 | [diff] [blame] | 135 | } |
| 136 | |
Gil Dekel | c8b8c4ed | 2022-05-04 20:30:38 | [diff] [blame] | 137 | // A list of common refresh rates that are used to help fit approximate refresh |
| 138 | // rate values into one of the common refresh rate bins. |
| 139 | constexpr float kCommonDisplayRefreshRates[] = { |
| 140 | 24.0, 25.0, 30.0, 45.0, 48.0, 50.0, 60.0, 75.0, |
| 141 | 90.0, 100.0, 120.0, 144.0, 165.0, 200.0, 240.0}; |
Gil Dekel | 20dc530 | 2020-12-01 20:03:41 | [diff] [blame] | 142 | |
Gil Dekel | c8b8c4ed | 2022-05-04 20:30:38 | [diff] [blame] | 143 | void UpdateRefreshRateUma(const DisplayConfigureRequest& request, |
| 144 | const std::string& uma_name) { |
| 145 | // Display is powered off. |
| 146 | if (!request.mode) |
| 147 | return; |
Gil Dekel | 20dc530 | 2020-12-01 20:03:41 | [diff] [blame] | 148 | |
Gil Dekel | c8b8c4ed | 2022-05-04 20:30:38 | [diff] [blame] | 149 | base::HistogramBase* histogram = base::SparseHistogram::FactoryGet( |
| 150 | uma_name, base::HistogramBase::kUmaTargetedHistogramFlag); |
| 151 | |
| 152 | // Check if the refresh value is within an epsilon from one of the common |
| 153 | // refresh rate values. |
Gil Dekel | a7ca9fc | 2022-05-16 19:25:37 | [diff] [blame] | 154 | for (float common_rate : kCommonDisplayRefreshRates) { |
| 155 | const bool is_within_epsilon = std::abs(request.mode->refresh_rate() - |
| 156 | common_rate) < kRefreshRateEpsilon; |
Gil Dekel | c8b8c4ed | 2022-05-04 20:30:38 | [diff] [blame] | 157 | if (is_within_epsilon) { |
Gil Dekel | a7ca9fc | 2022-05-16 19:25:37 | [diff] [blame] | 158 | histogram->Add(common_rate); |
Gil Dekel | c8b8c4ed | 2022-05-04 20:30:38 | [diff] [blame] |
|