blob: 59d65ffca4a322502d0ac020ca0dbd0316036da7 [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"
Gil Dekel36b941dd2021-01-23 00:37:2311#include "base/containers/flat_set.h"
Brett Wilsonb02c0a22017-09-25 22:34:4212#include "base/containers/queue.h"
Hans Wennborg3930cf32020-06-17 16:29:5213#include "base/logging.h"
Daniele Castagna4f0689b2019-10-30 01:16:4714#include "base/metrics/histogram_functions.h"
Gil Dekel788edb62020-11-25 23:41:2815#include "base/metrics/histogram_macros.h"
16#include "base/numerics/safe_conversions.h"
Mitsuru Oshima96ff4542022-05-02 17:31:3217#include "ui/display/manager/display_manager_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"
Gil Dekel36b941dd2021-01-23 00:37:2320#include "ui/display/types/display_mode.h"
dnicoara9372a7912014-12-11 01:29:0621#include "ui/display/types/display_snapshot.h"
22#include "ui/display/types/native_display_delegate.h"
23
kylechar7a067ec2017-01-07 01:16:2824namespace display {
dnicoara9372a7912014-12-11 01:29:0625
26namespace {
27
Gil Dekel788edb62020-11-25 23:41:2828// Because we do not offer hardware mirroring, the maximal number of external
29// displays that can be configured is limited by the number of available CRTCs,
30// which is usually three. Since the lifetime of the UMA using this value is one
31// year (exp. Nov. 2021), five buckets are more than enough for
32// its histogram (between 0 to 4 external monitors).
33constexpr int kMaxDisplaysCount = 5;
34
Gil Dekel66a97bc2022-04-29 04:21:5035// Find the next best mode that is smaller than |request->mode|. The next best
36// mode is found by comparing resolutions, and if those are similar, comparing
37// refresh rates. If no mode is found, return nullptr.
38const DisplayMode* FindNextMode(const DisplayConfigureRequest& request) {
39 DCHECK(request.mode);
Gil Dekelbbc40352021-01-05 18:33:5640
Gil Dekel66a97bc2022-04-29 04:21:5041 // Internal displays are restricted to their native mode. We do not
42 // attempt to downgrade their modes upon failure.
43 if (request.display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL)
dnicoaraa89c2082015-01-05 16:49:0644 return nullptr;
45
Gil Dekel66a97bc2022-04-29 04:21:5046 if (request.display->modes().size() <= 1)
47 return nullptr;
48
dnicoara9372a7912014-12-11 01:29:0649 const DisplayMode* best_mode = nullptr;
Gil Dekel66a97bc2022-04-29 04:21:5050 for (const auto& mode : request.display->modes()) {
51 if (*mode < *request.mode && (!best_mode || *mode > *best_mode))
dbasehore01e90042016-05-27 06:16:5152 best_mode = mode.get();
dnicoara9372a7912014-12-11 01:29:0653 }
54
55 return best_mode;
56}
57
Gil Dekelbbc40352021-01-05 18:33:5658void LogIfInvalidRequestForInternalDisplay(
59 const DisplayConfigureRequest& request) {
60 if (request.display->type() != DISPLAY_CONNECTION_TYPE_INTERNAL)
61 return;
62
63 if (request.mode == nullptr)
64 return;
65
66 if (request.mode == request.display->native_mode())
67 return;
68
69 LOG(ERROR) << "A mode other than the preferred mode was requested for the "
70 "internal display: preferred="
71 << request.display->native_mode()->ToString()
72 << " vs. requested=" << request.mode->ToString()
73 << ". Current mode="
74 << (request.display->current_mode()
75 ? request.display->current_mode()->ToString()
76 : "nullptr (disabled)")
77 << ".";
78}
79
Daniele Castagna4f0689b2019-10-30 01:16:4780// Samples used to define buckets used by DisplayResolution enum.
81// The enum is used to record screen resolution statistics.
82const int32_t kDisplayResolutionSamples[] = {1024, 1280, 1440, 1920,
83 2560, 3840, 5120, 7680};
84
85// Computes the index of the enum DisplayResolution.
86// The index has to match the definition of the enum in enums.xml
87int ComputeDisplayResolutionEnum(const DisplayMode* mode) {
88 if (!mode)
89 return 0; // Display is powered off
90
91 const gfx::Size size = mode->size();
92 uint32_t width_idx = 0;
93 uint32_t height_idx = 0;
Daniel Chengf9920542022-02-27 01:15:0894 for (; width_idx < std::size(kDisplayResolutionSamples); width_idx++) {
Daniele Castagna4f0689b2019-10-30 01:16:4795 if (size.width() <= kDisplayResolutionSamples[width_idx])
96 break;
97 }
Daniel Chengf9920542022-02-27 01:15:0898 for (; height_idx < std::size(kDisplayResolutionSamples); height_idx++) {
Daniele Castagna4f0689b2019-10-30 01:16:4799 if (size.height() <= kDisplayResolutionSamples[height_idx])
100 break;
101 }
102
Daniel Chengf9920542022-02-27 01:15:08103 if (width_idx == std::size(kDisplayResolutionSamples) ||
104 height_idx == std::size(kDisplayResolutionSamples))
105 return std::size(kDisplayResolutionSamples) *
106 std::size(kDisplayResolutionSamples) +
Daniele Castagna4f0689b2019-10-30 01:16:47107 1; // Overflow bucket
108 // Computes the index of DisplayResolution, starting from 1, since 0 is used
109 // when powering off the display.
Daniel Chengf9920542022-02-27 01:15:08110 return width_idx * std::size(kDisplayResolutionSamples) + height_idx + 1;
Daniele Castagna4f0689b2019-10-30 01:16:47111}
112
Gil Dekel20dc5302020-12-01 20:03:41113void UpdateResolutionAndRefreshRateUma(const DisplayConfigureRequest& request) {
114 const bool internal =
115 request.display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL;
116
117 base::UmaHistogramExactLinear(
118 internal ? "ConfigureDisplays.Internal.Modeset.Resolution"
119 : "ConfigureDisplays.External.Modeset.Resolution",
120 ComputeDisplayResolutionEnum(request.mode),
Daniel Chengf9920542022-02-27 01:15:08121 std::size(kDisplayResolutionSamples) *
122 std::size(kDisplayResolutionSamples) +
Gil Dekel20dc5302020-12-01 20:03:41123 2);
124
125 base::HistogramBase* histogram = base::LinearHistogram::FactoryGet(
126 internal ? "ConfigureDisplays.Internal.Modeset.RefreshRate"
127 : "ConfigureDisplays.External.Modeset.RefreshRate",
128 1, 240, 18, base::HistogramBase::kUmaTargetedHistogramFlag);
129 histogram->Add(request.mode ? std::round(request.mode->refresh_rate()) : 0);
130}
131
Gil Dekel36b941dd2021-01-23 00:37:23132void UpdateAttemptSucceededUma(
133 const std::vector<DisplayConfigureRequest>& requests,
134 bool display_success) {
135 for (const auto& request : requests) {
136 const bool internal =
137 request.display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL;
138 base::UmaHistogramBoolean(
139 internal ? "ConfigureDisplays.Internal.Modeset.AttemptSucceeded"
140 : "ConfigureDisplays.External.Modeset.AttemptSucceeded",
141 display_success);
142
143 VLOG(2) << "Configured status=" << display_success
144 << " display=" << request.display->display_id()
145 << " origin=" << request.origin.ToString()
146 << " mode=" << (request.mode ? request.mode->ToString() : "null");
147 }
Gil Dekel20dc5302020-12-01 20:03:41148}
149
Gil Dekel36b941dd2021-01-23 00:37:23150void UpdateFinalStatusUma(
151 const std::vector<RequestAndStatusList>& requests_and_statuses) {
Gil Dekel20dc5302020-12-01 20:03:41152 int mst_external_displays = 0;
Gil Dekel36b941dd2021-01-23 00:37:23153 size_t total_external_displays = requests_and_statuses.size();
154 for (auto& request_and_status : requests_and_statuses) {
155 const DisplayConfigureRequest& request = request_and_status.first;
156
Gil Dekel20dc5302020-12-01 20:03:41157 // 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",
Gil Dekel36b941dd2021-01-23 00:37:23170 request_and_status.second);
Gil Dekel20dc5302020-12-01 20:03:41171 }
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 Dekel36b941dd2021-01-23 00:37:23225 const auto& on_configured =
226 pending_display_group_requests_.empty()
227 ? &ConfigureDisplaysTask::OnFirstAttemptConfigured
228 : &ConfigureDisplaysTask::OnRetryConfigured;
229
230 delegate_->Configure(
231 config_requests,
232 base::BindOnce(on_configured, weak_ptr_factory_.GetWeakPtr()));
dnicoara9372a7912014-12-11 01:29:06233}
234
afakhry4e92e8c2017-04-20 17:04:59235void ConfigureDisplaysTask::OnConfigurationChanged() {}
236
237void ConfigureDisplaysTask::OnDisplaySnapshotsInvalidated() {
afakhry4e92e8c2017-04-20 17:04:59238 // From now on, don't access |requests_[index]->display|; they're invalid.
239 task_status_ = ERROR;
240 weak_ptr_factory_.InvalidateWeakPtrs();
Gil Dekel3369eec2020-10-06 19:53:10241 std::move(callback_).Run(task_status_);
afakhry4e92e8c2017-04-20 17:04:59242}
243
Gil Dekel36b941dd2021-01-23 00:37:23244void ConfigureDisplaysTask::OnFirstAttemptConfigured(bool config_success) {
245 UpdateAttemptSucceededUma(requests_, config_success);
Daniele Castagna4f0689b2019-10-30 01:16:47246
Gil Dekel36b941dd2021-01-23 00:37:23247 if (!config_success) {
248 // Partition |requests_| into smaller groups, update the task's state, and
249 // initiate the retry logic. The next time |delegate_|->Configure()
250 // terminates OnRetryConfigured() will be executed instead.
251 PartitionRequests();
252 DCHECK(!pending_display_group_requests_.empty());
253 requests_ = pending_display_group_requests_.front();
Gil Dekel42f7aba2021-01-08 18:59:55254 task_status_ = PARTIAL_SUCCESS;
255 Run();
256 return;
dnicoara9372a7912014-12-11 01:29:06257 }
258
Gil Dekel36b941dd2021-01-23 00:37:23259 // This code execute only when the first modeset attempt fully succeeds.
260 // Update the displays' status and report success.
261 for (const auto& request : requests_) {
262 request.display->set_current_mode(request.mode);
263 request.display->set_origin(request.origin);
264 final_requests_status_.emplace_back(std::make_pair(request, true));
265 }
Gil Dekel788edb62020-11-25 23:41:28266
Gil Dekel36b941dd2021-01-23 00:37:23267 UpdateFinalStatusUma(final_requests_status_);
Gil Dekel3369eec2020-10-06 19:53:10268 std::move(callback_).Run(task_status_);
dnicoara9372a7912014-12-11 01:29:06269}
270
Gil Dekel36b941dd2021-01-23 00:37:23271void ConfigureDisplaysTask::OnRetryConfigured(bool config_success) {
272 UpdateAttemptSucceededUma(requests_, config_success);
273
274 if (!config_success) {
275 // If one of the largest display request can be downgraded, try again.
276 // Otherwise this configuration task is a failure.
277 if (DowngradeLargestRequestWithAlternativeModes()) {
278 Run();
279 return;
280 } else {
281 task_status_ = ERROR;
282 }
283 }
284
285 // This code executes only when this display group request fully succeeds or
286 // fails to modeset. Update the final status of this group.
287 for (const auto& request : requests_) {
288 final_requests_status_.emplace_back(
289 std::make_pair(request, config_success));
290 if (config_success) {
291 request.display->set_current_mode(request.mode);
292 request.display->set_origin(request.origin);
293 }
294 }
295
296 // Subsequent modeset attempts will be done on the next pending display group,
297 // if one exists.
298 pending_display_group_requests_.pop();
299 requests_.clear();
300 if (!pending_display_group_requests_.empty()) {
301 requests_ = pending_display_group_requests_.front();
302 Run();
303 return;
304 }
305
306 // No more display groups to retry.
307 UpdateFinalStatusUma(final_requests_status_);
308 std::move(callback_).Run(task_status_);
309}
310
311void ConfigureDisplaysTask::PartitionRequests() {
312 pending_display_group_requests_ = PartitionedRequestsQueue();
Gil Dekel36b941dd2021-01-23 00:37:23313
Mark Yacoub100f2df2022-04-19 18:01:14314 // PartitionRequests occurs when the first modeset fails and we start by
315 // modesetting the groups of connectors one after the other. When doing this,
316 // we must start by resetting the state and the allocation of resources to
317 // turn off any displays hogging the resources.
318 std::vector<DisplayConfigureRequest> disable_requests;
319 for (const DisplayConfigureRequest& request : requests_)
320 disable_requests.emplace_back(request.display, nullptr, gfx::Point());
321 pending_display_group_requests_.push(disable_requests);
322
323 base::flat_set<uint64_t> handled_connectors;
Gil Dekel36b941dd2021-01-23 00:37:23324 for (size_t i = 0; i < requests_.size(); ++i) {
325 uint64_t connector_id = requests_[i].display->base_connector_id();
326 if (handled_connectors.find(connector_id) != handled_connectors.end())
327 continue;
328
329 std::vector<DisplayConfigureRequest> request_group;
330 for (size_t j = i; j < requests_.size(); ++j) {
331 if (connector_id == requests_[j].display->base_connector_id())
332 request_group.push_back(requests_[j]);
333 }
334
Gil Dekel36b941dd2021-01-23 00:37:23335 handled_connectors.insert(connector_id);
Mark Yacoub100f2df2022-04-19 18:01:14336 pending_display_group_requests_.push(request_group);
Gil Dekel36b941dd2021-01-23 00:37:23337 }
338}
339
340bool ConfigureDisplaysTask::DowngradeLargestRequestWithAlternativeModes() {
341 auto cmp = [](DisplayConfigureRequest* lhs, DisplayConfigureRequest* rhs) {
342 return *lhs->mode < *rhs->mode;
343 };
344 std::priority_queue<DisplayConfigureRequest*,
345 std::vector<DisplayConfigureRequest*>, decltype(cmp)>
346 sorted_requests(cmp);
347
348 for (auto& request : requests_) {
349 if (request.display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL)
350 continue;
351
352 if (!request.mode)
353 continue;
354
355 sorted_requests.push(&request);
356 }
357
358 // Fail if there are no viable candidates to downgrade
359 if (sorted_requests.empty())
360 return false;
361
362 while (!sorted_requests.empty()) {
363 DisplayConfigureRequest* next_request = sorted_requests.top();
364 sorted_requests.pop();
365
Gil Dekel66a97bc2022-04-29 04:21:50366 const DisplayMode* next_mode = FindNextMode(*next_request);
Gil Dekel36b941dd2021-01-23 00:37:23367 if (next_mode) {
368 next_request->mode = next_mode;
369 return true;
370 }
371 }
372
373 return false;
374}
375
kylechar7a067ec2017-01-07 01:16:28376} // namespace display