blob: f1d8601ef57d537374476640d7423ab4928b1112 [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
7#include "base/auto_reset.h"
8#include "base/bind.h"
Brett Wilsonb02c0a22017-09-25 22:34:429#include "base/containers/queue.h"
Hans Wennborg3930cf32020-06-17 16:29:5210#include "base/logging.h"
Daniele Castagna4f0689b2019-10-30 01:16:4711#include "base/metrics/histogram_functions.h"
12#include "base/stl_util.h"
Joone Hurd3ae8732018-04-17 18:05:0913#include "ui/display/manager/display_util.h"
Mark Yacoubd18a2922020-07-07 01:14:1514#include "ui/display/types/display_configuration_params.h"
dnicoara9372a7912014-12-11 01:29:0615#include "ui/display/types/display_snapshot.h"
16#include "ui/display/types/native_display_delegate.h"
17
kylechar7a067ec2017-01-07 01:16:2818namespace display {
dnicoara9372a7912014-12-11 01:29:0619
20namespace {
21
22// Find the next best mode after |display_mode|. If none can be found return
23// nullptr.
24const DisplayMode* FindNextMode(const DisplaySnapshot& display_state,
dnicoaraa89c2082015-01-05 16:49:0625 const DisplayMode* display_mode) {
26 if (!display_mode)
27 return nullptr;
28
dnicoara9372a7912014-12-11 01:29:0629 int best_mode_pixels = 0;
30 const DisplayMode* best_mode = nullptr;
dnicoaraa89c2082015-01-05 16:49:0631 int current_mode_pixels = display_mode->size().GetArea();
dbasehore01e90042016-05-27 06:16:5132 for (const std::unique_ptr<const DisplayMode>& mode : display_state.modes()) {
dnicoara9372a7912014-12-11 01:29:0633 int pixel_count = mode->size().GetArea();
34 if (pixel_count < current_mode_pixels && pixel_count > best_mode_pixels) {
dbasehore01e90042016-05-27 06:16:5135 best_mode = mode.get();
dnicoara9372a7912014-12-11 01:29:0636 best_mode_pixels = pixel_count;
37 }
38 }
39
40 return best_mode;
41}
42
Daniele Castagna4f0689b2019-10-30 01:16:4743// Samples used to define buckets used by DisplayResolution enum.
44// The enum is used to record screen resolution statistics.
45const int32_t kDisplayResolutionSamples[] = {1024, 1280, 1440, 1920,
46 2560, 3840, 5120, 7680};
47
48// Computes the index of the enum DisplayResolution.
49// The index has to match the definition of the enum in enums.xml
50int ComputeDisplayResolutionEnum(const DisplayMode* mode) {
51 if (!mode)
52 return 0; // Display is powered off
53
54 const gfx::Size size = mode->size();
55 uint32_t width_idx = 0;
56 uint32_t height_idx = 0;
57 for (; width_idx < base::size(kDisplayResolutionSamples); width_idx++) {
58 if (size.width() <= kDisplayResolutionSamples[width_idx])
59 break;
60 }
61 for (; height_idx < base::size(kDisplayResolutionSamples); height_idx++) {
62 if (size.height() <= kDisplayResolutionSamples[height_idx])
63 break;
64 }
65
66 if (width_idx == base::size(kDisplayResolutionSamples) ||
67 height_idx == base::size(kDisplayResolutionSamples))
68 return base::size(kDisplayResolutionSamples) *
69 base::size(kDisplayResolutionSamples) +
70 1; // Overflow bucket
71 // Computes the index of DisplayResolution, starting from 1, since 0 is used
72 // when powering off the display.
73 return width_idx * base::size(kDisplayResolutionSamples) + height_idx + 1;
74}
75
Mark Yacoub214f0dd02020-07-17 00:07:4476std::__wrap_iter<const DisplayConfigureRequest*> GetRequestForDisplayId(
77 int64_t display_id,
78 const std::vector<DisplayConfigureRequest>& requests) {
79 return find_if(requests.begin(), requests.end(),
80 [display_id](const DisplayConfigureRequest& request) {
81 return request.display->display_id() == display_id;
82 });
83}
84
dnicoara9372a7912014-12-11 01:29:0685} // namespace
86
87DisplayConfigureRequest::DisplayConfigureRequest(DisplaySnapshot* display,
88 const DisplayMode* mode,
89 const gfx::Point& origin)
kylechar731f85f92016-12-01 20:50:4690 : display(display), mode(mode), origin(origin) {}
dnicoara9372a7912014-12-11 01:29:0691
92ConfigureDisplaysTask::ConfigureDisplaysTask(
93 NativeDisplayDelegate* delegate,
94 const std::vector<DisplayConfigureRequest>& requests,
Sylvain Defresne23395c7a2019-10-02 10:07:4595 ResponseCallback callback)
dnicoara9372a7912014-12-11 01:29:0696 : delegate_(delegate),
97 requests_(requests),
Sylvain Defresne23395c7a2019-10-02 10:07:4598 callback_(std::move(callback)),
dnicoara9372a7912014-12-11 01:29:0699 is_configuring_(false),
100 num_displays_configured_(0),
Jeremy Roman47d432e2019-08-20 14:24:00101 task_status_(SUCCESS) {
dnicoara9372a7912014-12-11 01:29:06102 for (size_t i = 0; i < requests_.size(); ++i)
103 pending_request_indexes_.push(i);
afakhry4e92e8c2017-04-20 17:04:59104 delegate_->AddObserver(this);
dnicoara9372a7912014-12-11 01:29:06105}
106
afakhry4e92e8c2017-04-20 17:04:59107ConfigureDisplaysTask::~ConfigureDisplaysTask() {
108 delegate_->RemoveObserver(this);
109}
dnicoara9372a7912014-12-11 01:29:06110
111void ConfigureDisplaysTask::Run() {
112 // Synchronous configurators will recursively call Run(). In that case just
113 // defer their call to the next iteration in the while-loop. This is done to
114 // guard against stack overflows if the display has a large list of broken
115 // modes.
116 if (is_configuring_)
117 return;
118
dnicoara65d9d312014-12-13 02:30:47119 {
120 base::AutoReset<bool> recursivity_guard(&is_configuring_, true);
Mark Yacoubd8a32382020-09-18 19:27:55121 // The callback passed to delegate_->Configure() could be run synchronously
122 // or async. If it runs synchronously and any display fails and tries to
123 // reconfigure, new requests will get added to |pending_request_indexes_|,
124 // then another attempt to reconfigure will recursively call Run(). However
125 // |is_configuring_| will block the run due to the reason mentioned above.
126 // Hence, after we configure the first set of pending requests (loop over
127 // |current_pending_requests_count|), we check again if the new requests
128 // were added (!pending_request_indexes_.empty()).
dnicoara65d9d312014-12-13 02:30:47129 while (!pending_request_indexes_.empty()) {
Mark Yacoub214f0dd02020-07-17 00:07:44130 std::vector<display::DisplayConfigurationParams> config_requests;
Mark Yacoubd8a32382020-09-18 19:27:55131 size_t current_pending_requests_count = pending_request_indexes_.size();
132 for (size_t i = 0; i < current_pending_requests_count; ++i) {
Mark Yacoub214f0dd02020-07-17 00:07:44133 size_t index = pending_request_indexes_.front();
134 DisplayConfigureRequest* request = &requests_[index];
135 pending_request_indexes_.pop();
Daniele Castagna4f0689b2019-10-30 01:16:47136
Mark Yacoub214f0dd02020-07-17 00:07:44137 const bool internal =
138 request->display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL;
139 base::UmaHistogramExactLinear(
140 internal ? "ConfigureDisplays.Internal.Modeset.Resolution"
141 : "ConfigureDisplays.External.Modeset.Resolution",
142 ComputeDisplayResolutionEnum(request->mode),
143 base::size(kDisplayResolutionSamples) *
144 base::size(kDisplayResolutionSamples) +
145 2);
146 base::HistogramBase* histogram = base::LinearHistogram::FactoryGet(
147 internal ? "ConfigureDisplays.Internal.Modeset.RefreshRate"
148 : "ConfigureDisplays.External.Modeset.RefreshRate",
149 1, 240, 18, base::HistogramBase::kUmaTargetedHistogramFlag);
150 histogram->Add(request->mode ? std::round(request->mode->refresh_rate())
151 : 0);
Daniele Castagna4f0689b2019-10-30 01:16:47152
Mark Yacoub214f0dd02020-07-17 00:07:44153 display::DisplayConfigurationParams display_config_params(
154 request->display->display_id(), request->origin, request->mode);
155 config_requests.push_back(std::move(display_config_params));
156 }
157 if (!config_requests.empty()) {
158 delegate_->Configure(
159 config_requests,
160 base::BindOnce(&ConfigureDisplaysTask::OnConfigured,
161 weak_ptr_factory_.GetWeakPtr()));
162 }
dnicoara65d9d312014-12-13 02:30:47163 }
dnicoara9372a7912014-12-11 01:29:06164 }
165
dnicoara65d9d312014-12-13 02:30:47166 // Nothing should be modified after the |callback_| is called since the
dnicoara9372a7912014-12-11 01:29:06167 // task may be deleted in the callback.
168 if (num_displays_configured_ == requests_.size())
Sylvain Defresne23395c7a2019-10-02 10:07:45169 std::move(callback_).Run(task_status_);
dnicoara9372a7912014-12-11 01:29:06170}
171
afakhry4e92e8c2017-04-20 17:04:59172void ConfigureDisplaysTask::OnConfigurationChanged() {}
173
174void ConfigureDisplaysTask::OnDisplaySnapshotsInvalidated() {
Brett Wilsonb02c0a22017-09-25 22:34:42175 base::queue<size_t> empty_queue;
afakhry4e92e8c2017-04-20 17:04:59176 pending_request_indexes_.swap(empty_queue);
177 // From now on, don't access |requests_[index]->display|; they're invalid.
178 task_status_ = ERROR;
179 weak_ptr_factory_.InvalidateWeakPtrs();
180 Run();
181}
182
Mark Yacoub214f0dd02020-07-17 00:07:44183void ConfigureDisplaysTask::OnConfigured(
184 const base::flat_map<int64_t, bool>& statuses) {
185 bool config_success = true;
Daniele Castagna4f0689b2019-10-30 01:16:47186
Mark Yacoub214f0dd02020-07-17 00:07:44187 // Check if all displays are successfully configured.
188 for (const auto& status : statuses) {
189 int64_t display_id = status.first;
190 bool display_success = status.second;
191 config_success &= display_success;
Daniele Castagna4f0689b2019-10-30 01:16:47192
Mark Yacoub214f0dd02020-07-17 00:07:44193 auto request = GetRequestForDisplayId(display_id, requests_);
194 DCHECK(request != requests_.end());
195
196 VLOG(2) << "Configured status=" << display_success
197 << " display=" << request->display->display_id()
198 << " origin=" << request->origin.ToString()
199 << " mode=" << (request->mode ? request->mode->ToString() : "null");
200
201 bool internal =
202 request->display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL;
203 base::UmaHistogramBoolean(
204 internal ? "ConfigureDisplays.Internal.Modeset.AttemptSucceeded"
205 : "ConfigureDisplays.External.Modeset.AttemptSucceeded",
206 display_success);
207 }
208
209 if (config_success) {
210 for (const auto& status : statuses) {
211 auto request = GetRequestForDisplayId(status.first, requests_);
212 request->display->set_current_mode(request->mode);
213 request->display->set_origin(request->origin);
214 }
215 } else {
216 bool should_reconfigure = false;
217 // For the failing config, check if there is another mode to be requested.
218 // If there is one, attempt to reconfigure everything again.
219 for (const auto& status : statuses) {
220 int64_t display_id = status.first;
221 bool display_success = status.second;
222 if (!display_success) {
223 const DisplayConfigureRequest* request =
224 GetRequestForDisplayId(display_id, requests_).base();
Mark Yacoubd8a32382020-09-18 19:27:55225 const DisplayMode* next_mode =
Mark Yacoub214f0dd02020-07-17 00:07:44226 FindNextMode(*request->display, request->mode);
Mark Yacoubd8a32382020-09-18 19:27:55227 if (next_mode) {
228 const_cast<DisplayConfigureRequest*>(request)->mode = next_mode;
229 should_reconfigure = true;
230 }
Mark Yacoub214f0dd02020-07-17 00:07:44231 }
232 }
233 // When reconfiguring, reconfigure all displays, not only the failing ones
234 // as they could potentially depend on each other.
235 if (should_reconfigure) {
236 for (const auto& status : statuses) {
237 auto const_iterator = GetRequestForDisplayId(status.first, requests_);
238 auto request = requests_.erase(const_iterator, const_iterator);
239 size_t index = std::distance(requests_.begin(), request);
240 pending_request_indexes_.push(index);
241 }
dnicoara9372a7912014-12-11 01:29:06242 if (task_status_ == SUCCESS)
243 task_status_ = PARTIAL_SUCCESS;
dnicoara9372a7912014-12-11 01:29:06244 Run();
245 return;
246 }
dnicoara9372a7912014-12-11 01:29:06247 }
248
Mark Yacoub214f0dd02020-07-17 00:07:44249 // If no reconfigurations are happening, update the final state.
250 for (const auto& status : statuses) {
251 auto request = GetRequestForDisplayId(status.first, requests_);
252 bool internal =
253 request->display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL;
254 base::UmaHistogramBoolean(
255 internal ? "ConfigureDisplays.Internal.Modeset.FinalStatus"
256 : "ConfigureDisplays.External.Modeset.FinalStatus",
257 config_success);
258 }
Daniele Castagna4f0689b2019-10-30 01:16:47259
Mark Yacoub214f0dd02020-07-17 00:07:44260 num_displays_configured_ += statuses.size();
261 if (!config_success)
dnicoara9372a7912014-12-11 01:29:06262 task_status_ = ERROR;
dnicoara9372a7912014-12-11 01:29:06263 Run();
264}
265
kylechar7a067ec2017-01-07 01:16:28266} // namespace display