blob: 2af79d1c126539d63a61ab6edf73a868440a1058 [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);
121 while (!pending_request_indexes_.empty()) {
Mark Yacoub214f0dd02020-07-17 00:07:44122 // Loop over all the current requests, then it will loop again making sure
123 // no new requests were added and are pending.
124 std::vector<display::DisplayConfigurationParams> config_requests;
125 for (size_t i = 0; i < pending_request_indexes_.size(); ++i) {
126 size_t index = pending_request_indexes_.front();
127 DisplayConfigureRequest* request = &requests_[index];
128 pending_request_indexes_.pop();
Daniele Castagna4f0689b2019-10-30 01:16:47129
Mark Yacoub214f0dd02020-07-17 00:07:44130 const bool internal =
131 request->display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL;
132 base::UmaHistogramExactLinear(
133 internal ? "ConfigureDisplays.Internal.Modeset.Resolution"
134 : "ConfigureDisplays.External.Modeset.Resolution",
135 ComputeDisplayResolutionEnum(request->mode),
136 base::size(kDisplayResolutionSamples) *
137 base::size(kDisplayResolutionSamples) +
138 2);
139 base::HistogramBase* histogram = base::LinearHistogram::FactoryGet(
140 internal ? "ConfigureDisplays.Internal.Modeset.RefreshRate"
141 : "ConfigureDisplays.External.Modeset.RefreshRate",
142 1, 240, 18, base::HistogramBase::kUmaTargetedHistogramFlag);
143 histogram->Add(request->mode ? std::round(request->mode->refresh_rate())
144 : 0);
Daniele Castagna4f0689b2019-10-30 01:16:47145
Mark Yacoub214f0dd02020-07-17 00:07:44146 display::DisplayConfigurationParams display_config_params(
147 request->display->display_id(), request->origin, request->mode);
148 config_requests.push_back(std::move(display_config_params));
149 }
150 if (!config_requests.empty()) {
151 delegate_->Configure(
152 config_requests,
153 base::BindOnce(&ConfigureDisplaysTask::OnConfigured,
154 weak_ptr_factory_.GetWeakPtr()));
155 }
dnicoara65d9d312014-12-13 02:30:47156 }
dnicoara9372a7912014-12-11 01:29:06157 }
158
dnicoara65d9d312014-12-13 02:30:47159 // Nothing should be modified after the |callback_| is called since the
dnicoara9372a7912014-12-11 01:29:06160 // task may be deleted in the callback.
161 if (num_displays_configured_ == requests_.size())
Sylvain Defresne23395c7a2019-10-02 10:07:45162 std::move(callback_).Run(task_status_);
dnicoara9372a7912014-12-11 01:29:06163}
164
afakhry4e92e8c2017-04-20 17:04:59165void ConfigureDisplaysTask::OnConfigurationChanged() {}
166
167void ConfigureDisplaysTask::OnDisplaySnapshotsInvalidated() {
Brett Wilsonb02c0a22017-09-25 22:34:42168 base::queue<size_t> empty_queue;
afakhry4e92e8c2017-04-20 17:04:59169 pending_request_indexes_.swap(empty_queue);
170 // From now on, don't access |requests_[index]->display|; they're invalid.
171 task_status_ = ERROR;
172 weak_ptr_factory_.InvalidateWeakPtrs();
173 Run();
174}
175
Mark Yacoub214f0dd02020-07-17 00:07:44176void ConfigureDisplaysTask::OnConfigured(
177 const base::flat_map<int64_t, bool>& statuses) {
178 bool config_success = true;
Daniele Castagna4f0689b2019-10-30 01:16:47179
Mark Yacoub214f0dd02020-07-17 00:07:44180 // Check if all displays are successfully configured.
181 for (const auto& status : statuses) {
182 int64_t display_id = status.first;
183 bool display_success = status.second;
184 config_success &= display_success;
Daniele Castagna4f0689b2019-10-30 01:16:47185
Mark Yacoub214f0dd02020-07-17 00:07:44186 auto request = GetRequestForDisplayId(display_id, requests_);
187 DCHECK(request != requests_.end());
188
189 VLOG(2) << "Configured status=" << display_success
190 << " display=" << request->display->display_id()
191 << " origin=" << request->origin.ToString()
192 << " mode=" << (request->mode ? request->mode->ToString() : "null");
193
194 bool internal =
195 request->display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL;
196 base::UmaHistogramBoolean(
197 internal ? "ConfigureDisplays.Internal.Modeset.AttemptSucceeded"
198 : "ConfigureDisplays.External.Modeset.AttemptSucceeded",
199 display_success);
200 }
201
202 if (config_success) {
203 for (const auto& status : statuses) {
204 auto request = GetRequestForDisplayId(status.first, requests_);
205 request->display->set_current_mode(request->mode);
206 request->display->set_origin(request->origin);
207 }
208 } else {
209 bool should_reconfigure = false;
210 // For the failing config, check if there is another mode to be requested.
211 // If there is one, attempt to reconfigure everything again.
212 for (const auto& status : statuses) {
213 int64_t display_id = status.first;
214 bool display_success = status.second;
215 if (!display_success) {
216 const DisplayConfigureRequest* request =
217 GetRequestForDisplayId(display_id, requests_).base();
218 const_cast<DisplayConfigureRequest*>(request)->mode =
219 FindNextMode(*request->display, request->mode);
220 should_reconfigure = !!request->mode;
221 }
222 }
223 // When reconfiguring, reconfigure all displays, not only the failing ones
224 // as they could potentially depend on each other.
225 if (should_reconfigure) {
226 for (const auto& status : statuses) {
227 auto const_iterator = GetRequestForDisplayId(status.first, requests_);
228 auto request = requests_.erase(const_iterator, const_iterator);
229 size_t index = std::distance(requests_.begin(), request);
230 pending_request_indexes_.push(index);
231 }
dnicoara9372a7912014-12-11 01:29:06232 if (task_status_ == SUCCESS)
233 task_status_ = PARTIAL_SUCCESS;
dnicoara9372a7912014-12-11 01:29:06234 Run();
235 return;
236 }
dnicoara9372a7912014-12-11 01:29:06237 }
238
Mark Yacoub214f0dd02020-07-17 00:07:44239 // If no reconfigurations are happening, update the final state.
240 for (const auto& status : statuses) {
241 auto request = GetRequestForDisplayId(status.first, requests_);
242 bool internal =
243 request->display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL;
244 base::UmaHistogramBoolean(
245 internal ? "ConfigureDisplays.Internal.Modeset.FinalStatus"
246 : "ConfigureDisplays.External.Modeset.FinalStatus",
247 config_success);
248 }
Daniele Castagna4f0689b2019-10-30 01:16:47249
Mark Yacoub214f0dd02020-07-17 00:07:44250 num_displays_configured_ += statuses.size();
251 if (!config_success)
dnicoara9372a7912014-12-11 01:29:06252 task_status_ = ERROR;
dnicoara9372a7912014-12-11 01:29:06253 Run();
254}
255
kylechar7a067ec2017-01-07 01:16:28256} // namespace display