blob: fb78ce8afcf898ce07ffcde200820731c34f4e8f [file] [log] [blame]
dnicoara9372a7912014-12-11 01:29:061// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Joone Hurd3ae8732018-04-17 18:05:095#include "ui/display/manager/configure_displays_task.h"
dnicoara9372a7912014-12-11 01:29:066
Gil Dekel788edb62020-11-25 23:41:287#include <cstddef>
8
dnicoara9372a7912014-12-11 01:29:069#include "base/auto_reset.h"
10#include "base/bind.h"
Brett Wilsonb02c0a22017-09-25 22:34:4211#include "base/containers/queue.h"
Hans Wennborg3930cf32020-06-17 16:29:5212#include "base/logging.h"
Daniele Castagna4f0689b2019-10-30 01:16:4713#include "base/metrics/histogram_functions.h"
Gil Dekel788edb62020-11-25 23:41:2814#include "base/metrics/histogram_macros.h"
15#include "base/numerics/safe_conversions.h"
Daniele Castagna4f0689b2019-10-30 01:16:4716#include "base/stl_util.h"
Joone Hurd3ae8732018-04-17 18:05:0917#include "ui/display/manager/display_util.h"
Mark Yacoubd18a2922020-07-07 01:14:1518#include "ui/display/types/display_configuration_params.h"
Gil Dekelbbc40352021-01-05 18:33:5619#include "ui/display/types/display_constants.h"
dnicoara9372a7912014-12-11 01:29:0620#include "ui/display/types/display_snapshot.h"
21#include "ui/display/types/native_display_delegate.h"
22
kylechar7a067ec2017-01-07 01:16:2823namespace display {
dnicoara9372a7912014-12-11 01:29:0624
25namespace {
26
Gil Dekel788edb62020-11-25 23:41:2827// Because we do not offer hardware mirroring, the maximal number of external
28// displays that can be configured is limited by the number of available CRTCs,
29// which is usually three. Since the lifetime of the UMA using this value is one
30// year (exp. Nov. 2021), five buckets are more than enough for
31// its histogram (between 0 to 4 external monitors).
32constexpr int kMaxDisplaysCount = 5;
33
dnicoara9372a7912014-12-11 01:29:0634// Find the next best mode after |display_mode|. If none can be found return
35// nullptr.
36const DisplayMode* FindNextMode(const DisplaySnapshot& display_state,
dnicoaraa89c2082015-01-05 16:49:0637 const DisplayMode* display_mode) {
Gil Dekelbbc40352021-01-05 18:33:5638 // Internal displays are restricted to their native mode. We do not attempt to
39 // downgrade their modes upon failure.
40 if (display_state.type() == DISPLAY_CONNECTION_TYPE_INTERNAL) {
41 return nullptr;
42 }
43
dnicoaraa89c2082015-01-05 16:49:0644 if (!display_mode)
45 return nullptr;
46
dnicoara9372a7912014-12-11 01:29:0647 int best_mode_pixels = 0;
48 const DisplayMode* best_mode = nullptr;
dnicoaraa89c2082015-01-05 16:49:0649 int current_mode_pixels = display_mode->size().GetArea();
dbasehore01e90042016-05-27 06:16:5150 for (const std::unique_ptr<const DisplayMode>& mode : display_state.modes()) {
dnicoara9372a7912014-12-11 01:29:0651 int pixel_count = mode->size().GetArea();
52 if (pixel_count < current_mode_pixels && pixel_count > best_mode_pixels) {
dbasehore01e90042016-05-27 06:16:5153 best_mode = mode.get();
dnicoara9372a7912014-12-11 01:29:0654 best_mode_pixels = pixel_count;
55 }
56 }
57
58 return best_mode;
59}
60
Gil Dekelbbc40352021-01-05 18:33:5661void LogIfInvalidRequestForInternalDisplay(
62 const DisplayConfigureRequest& request) {
63 if (request.display->type() != DISPLAY_CONNECTION_TYPE_INTERNAL)
64 return;
65
66 if (request.mode == nullptr)
67 return;
68
69 if (request.mode == request.display->native_mode())
70 return;
71
72 LOG(ERROR) << "A mode other than the preferred mode was requested for the "
73 "internal display: preferred="
74 << request.display->native_mode()->ToString()
75 << " vs. requested=" << request.mode->ToString()
76 << ". Current mode="
77 << (request.display->current_mode()
78 ? request.display->current_mode()->ToString()
79 : "nullptr (disabled)")
80 << ".";
81}
82
Daniele Castagna4f0689b2019-10-30 01:16:4783// Samples used to define buckets used by DisplayResolution enum.
84// The enum is used to record screen resolution statistics.
85const int32_t kDisplayResolutionSamples[] = {1024, 1280, 1440, 1920,
86 2560, 3840, 5120, 7680};
87
88// Computes the index of the enum DisplayResolution.
89// The index has to match the definition of the enum in enums.xml
90int ComputeDisplayResolutionEnum(const DisplayMode* mode) {
91 if (!mode)
92 return 0; // Display is powered off
93
94 const gfx::Size size = mode->size();
95 uint32_t width_idx = 0;
96 uint32_t height_idx = 0;
97 for (; width_idx < base::size(kDisplayResolutionSamples); width_idx++) {
98 if (size.width() <= kDisplayResolutionSamples[width_idx])
99 break;
100 }
101 for (; height_idx < base::size(kDisplayResolutionSamples); height_idx++) {
102 if (size.height() <= kDisplayResolutionSamples[height_idx])
103 break;
104 }
105
106 if (width_idx == base::size(kDisplayResolutionSamples) ||
107 height_idx == base::size(kDisplayResolutionSamples))
108 return base::size(kDisplayResolutionSamples) *
109 base::size(kDisplayResolutionSamples) +
110 1; // Overflow bucket
111 // Computes the index of DisplayResolution, starting from 1, since 0 is used
112 // when powering off the display.
113 return width_idx * base::size(kDisplayResolutionSamples) + height_idx + 1;
114}
115
Mark Yacoub214f0dd02020-07-17 00:07:44116std::__wrap_iter<const DisplayConfigureRequest*> GetRequestForDisplayId(
117 int64_t display_id,
118 const std::vector<DisplayConfigureRequest>& requests) {
119 return find_if(requests.begin(), requests.end(),
120 [display_id](const DisplayConfigureRequest& request) {
121 return request.display->display_id() == display_id;
122 });
123}
124
Gil Dekel20dc5302020-12-01 20:03:41125void UpdateResolutionAndRefreshRateUma(const DisplayConfigureRequest& request) {
126 const bool internal =
127 request.display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL;
128
129 base::UmaHistogramExactLinear(
130 internal ? "ConfigureDisplays.Internal.Modeset.Resolution"
131 : "ConfigureDisplays.External.Modeset.Resolution",
132 ComputeDisplayResolutionEnum(request.mode),
133 base::size(kDisplayResolutionSamples) *
134 base::size(kDisplayResolutionSamples) +
135 2);
136
137 base::HistogramBase* histogram = base::LinearHistogram::FactoryGet(
138 internal ? "ConfigureDisplays.Internal.Modeset.RefreshRate"
139 : "ConfigureDisplays.External.Modeset.RefreshRate",
140 1, 240, 18, base::HistogramBase::kUmaTargetedHistogramFlag);
141 histogram->Add(request.mode ? std::round(request.mode->refresh_rate()) : 0);
142}
143
144void UpdateAttemptSucceededUma(DisplaySnapshot* display, bool display_success) {
145 const bool internal = display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL;
146 base::UmaHistogramBoolean(
147 internal ? "ConfigureDisplays.Internal.Modeset.AttemptSucceeded"
148 : "ConfigureDisplays.External.Modeset.AttemptSucceeded",
149 display_success);
150}
151
152void UpdateFinalStatusUma(const std::vector<DisplayConfigureRequest>& requests,
153 bool config_success) {
154 int mst_external_displays = 0;
155 size_t total_external_displays = requests.size();
156 for (auto& request : requests) {
157 // Is this display SST (single-stream vs. MST multi-stream).
158 bool sst_display = request.display->base_connector_id() &&
159 request.display->path_topology().empty();
160 if (!sst_display)
161 mst_external_displays++;
162
163 bool internal = request.display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL;
164 if (internal)
165 total_external_displays--;
166
167 base::UmaHistogramBoolean(
168 internal ? "ConfigureDisplays.Internal.Modeset.FinalStatus"
169 : "ConfigureDisplays.External.Modeset.FinalStatus",
170 config_success);
171 }
172
173 base::UmaHistogramExactLinear(
174 "ConfigureDisplays.Modeset.TotalExternalDisplaysCount",
175 base::checked_cast<int>(total_external_displays), kMaxDisplaysCount);
176
177 base::UmaHistogramExactLinear(
178 "ConfigureDisplays.Modeset.MstExternalDisplaysCount",
179 mst_external_displays, kMaxDisplaysCount);
180
181 if (total_external_displays > 0) {
182 const int mst_displays_percentage =
183 100.0 * mst_external_displays / total_external_displays;
184 UMA_HISTOGRAM_PERCENTAGE(
185 "ConfigureDisplays.Modeset.MstExternalDisplaysPercentage",
186 mst_displays_percentage);
187 }
188}
189
dnicoara9372a7912014-12-11 01:29:06190} // namespace
191
192DisplayConfigureRequest::DisplayConfigureRequest(DisplaySnapshot* display,
193 const DisplayMode* mode,
194 const gfx::Point& origin)
kylechar731f85f92016-12-01 20:50:46195 : display(display), mode(mode), origin(origin) {}
dnicoara9372a7912014-12-11 01:29:06196
197ConfigureDisplaysTask::ConfigureDisplaysTask(
198 NativeDisplayDelegate* delegate,
199 const std::vector<DisplayConfigureRequest>& requests,
Sylvain Defresne23395c7a2019-10-02 10:07:45200 ResponseCallback callback)
dnicoara9372a7912014-12-11 01:29:06201 : delegate_(delegate),
202 requests_(requests),
Sylvain Defresne23395c7a2019-10-02 10:07:45203 callback_(std::move(callback)),
Jeremy Roman47d432e2019-08-20 14:24:00204 task_status_(SUCCESS) {
afakhry4e92e8c2017-04-20 17:04:59205 delegate_->AddObserver(this);
dnicoara9372a7912014-12-11 01:29:06206}
207
afakhry4e92e8c2017-04-20 17:04:59208ConfigureDisplaysTask::~ConfigureDisplaysTask() {
209 delegate_->RemoveObserver(this);
210}
dnicoara9372a7912014-12-11 01:29:06211
212void ConfigureDisplaysTask::Run() {
Gil Dekel3369eec2020-10-06 19:53:10213 DCHECK(!requests_.empty());
dnicoara9372a7912014-12-11 01:29:06214
Gil Dekel3369eec2020-10-06 19:53:10215 std::vector<display::DisplayConfigurationParams> config_requests;
216 for (const auto& request : requests_) {
Gil Dekelbbc40352021-01-05 18:33:56217 LogIfInvalidRequestForInternalDisplay(request);
218
Gil Dekel3369eec2020-10-06 19:53:10219 config_requests.emplace_back(request.display->display_id(), request.origin,
220 request.mode);
Daniele Castagna4f0689b2019-10-30 01:16:47221
Gil Dekel20dc5302020-12-01 20:03:41222 UpdateResolutionAndRefreshRateUma(request);
dnicoara9372a7912014-12-11 01:29:06223 }
224
Gil Dekel3369eec2020-10-06 19:53:10225 delegate_->Configure(config_requests,
226 base::BindOnce(&ConfigureDisplaysTask::OnConfigured,
227 weak_ptr_factory_.GetWeakPtr()));
dnicoara9372a7912014-12-11 01:29:06228}
229
afakhry4e92e8c2017-04-20 17:04:59230void ConfigureDisplaysTask::OnConfigurationChanged() {}
231
232void ConfigureDisplaysTask::OnDisplaySnapshotsInvalidated() {
afakhry4e92e8c2017-04-20 17:04:59233 // From now on, don't access |requests_[index]->display|; they're invalid.
234 task_status_ = ERROR;
235 weak_ptr_factory_.InvalidateWeakPtrs();
Gil Dekel3369eec2020-10-06 19:53:10236 std::move(callback_).Run(task_status_);
afakhry4e92e8c2017-04-20 17:04:59237}
238
Mark Yacoub214f0dd02020-07-17 00:07:44239void ConfigureDisplaysTask::OnConfigured(
240 const base::flat_map<int64_t, bool>& statuses) {
241 bool config_success = true;
Mark Yacoub214f0dd02020-07-17 00:07:44242 // Check if all displays are successfully configured.
243 for (const auto& status : statuses) {
244 int64_t display_id = status.first;
245 bool display_success = status.second;
246 config_success &= display_success;
Daniele Castagna4f0689b2019-10-30 01:16:47247
Mark Yacoub214f0dd02020-07-17 00:07:44248 auto request = GetRequestForDisplayId(display_id, requests_);
249 DCHECK(request != requests_.end());
250
251 VLOG(2) << "Configured status=" << display_success
252 << " display=" << request->display->display_id()
253 << " origin=" << request->origin.ToString()
254 << " mode=" << (request->mode ? request->mode->ToString() : "null");
255
Gil Dekel20dc5302020-12-01 20:03:41256 UpdateAttemptSucceededUma(request->display, display_success);
Mark Yacoub214f0dd02020-07-17 00:07:44257 }
258
Gil Dekel3369eec2020-10-06 19:53:10259 // Update displays upon success or prep |requests_| for reconfiguration.
Mark Yacoub214f0dd02020-07-17 00:07:44260 if (config_success) {
Gil Dekel3369eec2020-10-06 19:53:10261 for (auto& request : requests_) {
262 request.display->set_current_mode(request.mode);
263 request.display->set_origin(request.origin);
Mark Yacoub214f0dd02020-07-17 00:07:44264 }
265 } else {
266 bool should_reconfigure = false;
267 // For the failing config, check if there is another mode to be requested.
268 // If there is one, attempt to reconfigure everything again.
269 for (const auto& status : statuses) {
270 int64_t display_id = status.first;
271 bool display_success = status.second;
272 if (!display_success) {
273 const DisplayConfigureRequest* request =
274 GetRequestForDisplayId(display_id, requests_).base();
Mark Yacoubd8a32382020-09-18 19:27:55275 const DisplayMode* next_mode =
Mark Yacoub214f0dd02020-07-17 00:07:44276 FindNextMode(*request->display, request->mode);
Mark Yacoubd8a32382020-09-18 19:27:55277 if (next_mode) {
278 const_cast<DisplayConfigureRequest*>(request)->mode = next_mode;
279 should_reconfigure = true;
280 }
Mark Yacoub214f0dd02020-07-17 00:07:44281 }
282 }
Mark Yacoub214f0dd02020-07-17 00:07:44283 if (should_reconfigure) {
Gil Dekel3369eec2020-10-06 19:53:10284 task_status_ = PARTIAL_SUCCESS;
dnicoara9372a7912014-12-11 01:29:06285 Run();
286 return;
287 }
dnicoara9372a7912014-12-11 01:29:06288 }
289
Gil Dekel3369eec2020-10-06 19:53:10290 // Update the final state.
Gil Dekel20dc5302020-12-01 20:03:41291 UpdateFinalStatusUma(requests_, config_success);
Gil Dekel788edb62020-11-25 23:41:28292
Mark Yacoub214f0dd02020-07-17 00:07:44293 if (!config_success)
dnicoara9372a7912014-12-11 01:29:06294 task_status_ = ERROR;
Gil Dekel3369eec2020-10-06 19:53:10295 std::move(callback_).Run(task_status_);
dnicoara9372a7912014-12-11 01:29:06296}
297
kylechar7a067ec2017-01-07 01:16:28298} // namespace display