1 // 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
5 #include "ui/ozone/platform/drm/gpu/screen_manager.h"
6
7 #include <xf86drmMode.h>
8 #include <memory>
9 #include <utility>
10
11 #include "base/files/platform_file.h"
12 #include "third_party/skia/include/core/SkCanvas.h"
13 #include "third_party/skia/include/core/SkSurface.h"
14 #include "ui/display/types/display_snapshot.h"
15 #include "ui/gfx/geometry/point.h"
16 #include "ui/gfx/geometry/rect.h"
17 #include "ui/gfx/geometry/size.h"
18 #include "ui/gfx/gpu_fence.h"
19 #include "ui/gfx/linux/gbm_buffer.h"
20 #include "ui/gfx/skia_util.h"
21 #include "ui/ozone/platform/drm/common/drm_util.h"
22 #include "ui/ozone/platform/drm/gpu/crtc_controller.h"
23 #include "ui/ozone/platform/drm/gpu/drm_device.h"
24 #include "ui/ozone/platform/drm/gpu/drm_dumb_buffer.h"
25 #include "ui/ozone/platform/drm/gpu/drm_framebuffer.h"
26 #include "ui/ozone/platform/drm/gpu/drm_window.h"
27 #include "ui/ozone/platform/drm/gpu/hardware_display_controller.h"
28
29 namespace ui {
30
31 namespace {
32
33 // Copies the contents of the saved framebuffer from the CRTCs in |controller|
34 // to the surface for the new modeset buffer |surface|.
FillModesetBuffer(const scoped_refptr<DrmDevice> & drm,HardwareDisplayController * controller,SkSurface * surface,uint32_t fourcc_format)35 bool FillModesetBuffer(const scoped_refptr<DrmDevice>& drm,
36 HardwareDisplayController* controller,
37 SkSurface* surface,
38 uint32_t fourcc_format) {
39 DCHECK(!controller->crtc_controllers().empty());
40 CrtcController* first_crtc = controller->crtc_controllers()[0].get();
41 ScopedDrmCrtcPtr saved_crtc(drm->GetCrtc(first_crtc->crtc()));
42 if (!saved_crtc || !saved_crtc->buffer_id) {
43 VLOG(2) << "Crtc has no saved state or wasn't modeset";
44 return false;
45 }
46
47 const auto& modifiers = controller->GetFormatModifiers(fourcc_format);
48 for (const uint64_t modifier : modifiers) {
49 // A value of 0 means DRM_FORMAT_MOD_NONE. If the CRTC has any other
50 // modifier (tiling, compression, etc.) we can't read the fb and assume it's
51 // a linear buffer.
52 if (modifier) {
53 VLOG(2) << "Crtc has a modifier and we might not know how to interpret "
54 "the fb.";
55 return false;
56 }
57 }
58
59 // If the display controller is in mirror mode, the CRTCs should be sharing
60 // the same framebuffer.
61 DrmDumbBuffer saved_buffer(drm);
62 if (!saved_buffer.InitializeFromFramebuffer(saved_crtc->buffer_id)) {
63 VLOG(2) << "Failed to grab saved framebuffer " << saved_crtc->buffer_id;
64 return false;
65 }
66
67 // Don't copy anything if the sizes mismatch. This can happen when the user
68 // changes modes.
69 if (saved_buffer.GetCanvas()->getBaseLayerSize() !=
70 surface->getCanvas()->getBaseLayerSize()) {
71 VLOG(2) << "Previous buffer has a different size than modeset buffer";
72 return false;
73 }
74
75 SkPaint paint;
76 // Copy the source buffer. Do not perform any blending.
77 paint.setBlendMode(SkBlendMode::kSrc);
78 surface->getCanvas()->drawImage(saved_buffer.surface()->makeImageSnapshot(),
79 0, 0, &paint);
80 return true;
81 }
82
GetCrtcController(HardwareDisplayController * controller,const scoped_refptr<DrmDevice> & drm,uint32_t crtc)83 CrtcController* GetCrtcController(HardwareDisplayController* controller,
84 const scoped_refptr<DrmDevice>& drm,
85 uint32_t crtc) {
86 for (const auto& crtc_controller : controller->crtc_controllers()) {
87 if (crtc_controller->crtc() == crtc)
88 return crtc_controller.get();
89 }
90
91 NOTREACHED();
92 return nullptr;
93 }
94
95 } // namespace
96
ScreenManager()97 ScreenManager::ScreenManager() {}
98
~ScreenManager()99 ScreenManager::~ScreenManager() {
100 DCHECK(window_map_.empty());
101 }
102
AddDisplayController(const scoped_refptr<DrmDevice> & drm,uint32_t crtc,uint32_t connector)103 void ScreenManager::AddDisplayController(const scoped_refptr<DrmDevice>& drm,
104 uint32_t crtc,
105 uint32_t connector) {
106 HardwareDisplayControllers::iterator it = FindDisplayController(drm, crtc);
107 // TODO(dnicoara): Turn this into a DCHECK when async display configuration is
108 // properly supported. (When there can't be a race between forcing initial
109 // display configuration in ScreenManager and display::NativeDisplayDelegate
110 // creating the display controllers.)
111 if (it != controllers_.end()) {
112 LOG(WARNING) << "Display controller (crtc=" << crtc << ") already present.";
113 return;
114 }
115
116 controllers_.push_back(std::make_unique<HardwareDisplayController>(
117 std::unique_ptr<CrtcController>(new CrtcController(drm, crtc, connector)),
118 gfx::Point()));
119 }
120
RemoveDisplayController(const scoped_refptr<DrmDevice> & drm,uint32_t crtc)121 void ScreenManager::RemoveDisplayController(const scoped_refptr<DrmDevice>& drm,
122 uint32_t crtc) {
123 HardwareDisplayControllers::iterator it = FindDisplayController(drm, crtc);
124 if (it != controllers_.end()) {
125 bool is_mirrored = (*it)->IsMirrored();
126 (*it)->RemoveCrtc(drm, crtc);
127 if (!is_mirrored) {
128 controllers_.erase(it);
129 UpdateControllerToWindowMapping();
130 }
131 }
132 }
133
ConfigureDisplayController(const scoped_refptr<DrmDevice> & drm,uint32_t crtc,uint32_t connector,const gfx::Point & origin,const drmModeModeInfo & mode)134 bool ScreenManager::ConfigureDisplayController(
135 const scoped_refptr<DrmDevice>& drm,
136 uint32_t crtc,
137 uint32_t connector,
138 const gfx::Point& origin,
139 const drmModeModeInfo& mode) {
140 bool status =
141 ActualConfigureDisplayController(drm, crtc, connector, origin, mode);
142 if (status)
143 UpdateControllerToWindowMapping();
144
145 return status;
146 }
147
ActualConfigureDisplayController(const scoped_refptr<DrmDevice> & drm,uint32_t crtc,uint32_t connector,const gfx::Point & origin,const drmModeModeInfo & mode)148 bool ScreenManager::ActualConfigureDisplayController(
149 const scoped_refptr<DrmDevice>& drm,
150 uint32_t crtc,
151 uint32_t connector,
152 const gfx::Point& origin,
153 const drmModeModeInfo& mode) {
154 gfx::Rect modeset_bounds(origin.x(), origin.y(), mode.hdisplay,
155 mode.vdisplay);
156 HardwareDisplayControllers::iterator it = FindDisplayController(drm, crtc);
157 DCHECK(controllers_.end() != it)
158 << "Display controller (crtc=" << crtc << ") doesn't exist.";
159
160 HardwareDisplayController* controller = it->get();
161 CrtcController* crtc_controller = GetCrtcController(controller, drm, crtc);
162 // If nothing changed just enable the controller. Note, we perform an exact
163 // comparison on the mode since the refresh rate may have changed.
164 if (SameMode(mode, crtc_controller->mode()) &&
165 origin == controller->origin()) {
166 if (controller->IsDisabled()) {
167 HardwareDisplayControllers::iterator mirror =
168 FindActiveDisplayControllerByLocation(drm, modeset_bounds);
169 // If there is an active controller at the same location then start mirror
170 // mode.
171 if (mirror != controllers_.end())
172 return HandleMirrorMode(it, mirror, drm, crtc, connector, mode);
173 }
174
175 // Just re-enable the controller to re-use the current state.
176 return EnableController(controller);
177 }
178
179 // Either the mode or the location of the display changed, so exit mirror
180 // mode and configure the display independently. If the caller still wants
181 // mirror mode, subsequent calls configuring the other controllers will
182 // restore mirror mode.
183 if (controller->IsMirrored()) {
184 controllers_.push_back(std::make_unique<HardwareDisplayController>(
185 controller->RemoveCrtc(drm, crtc), controller->origin()));
186 it = controllers_.end() - 1;
187 controller = it->get();
188 }
189
190 HardwareDisplayControllers::iterator mirror =
191 FindActiveDisplayControllerByLocation(drm, modeset_bounds);
192 // Handle mirror mode.
193 if (mirror != controllers_.end() && it != mirror)
194 return HandleMirrorMode(it, mirror, drm, crtc, connector, mode);
195
196 return ModesetController(controller, origin, mode);
197 }
198
DisableDisplayController(const scoped_refptr<DrmDevice> & drm,uint32_t crtc)199 bool ScreenManager::DisableDisplayController(
200 const scoped_refptr<DrmDevice>& drm,
201 uint32_t crtc) {
202 HardwareDisplayControllers::iterator it = FindDisplayController(drm, crtc);
203 if (it != controllers_.end()) {
204 HardwareDisplayController* controller = it->get();
205 if (controller->IsMirrored()) {
206 controllers_.push_back(std::make_unique<HardwareDisplayController>(
207 controller->RemoveCrtc(drm, crtc), controller->origin()));
208 controller = controllers_.back().get();
209 }
210
211 controller->Disable();
212 UpdateControllerToWindowMapping();
213 return true;
214 }
215
216 LOG(ERROR) << "Failed to find display controller crtc=" << crtc;
217 return false;
218 }
219
GetDisplayController(const gfx::Rect & bounds)220 HardwareDisplayController* ScreenManager::GetDisplayController(
221 const gfx::Rect& bounds) {
222 HardwareDisplayControllers::iterator it =
223 FindActiveDisplayControllerByLocation(bounds);
224 if (it != controllers_.end())
225 return it->get();
226
227 return nullptr;
228 }
229
AddWindow(gfx::AcceleratedWidget widget,std::unique_ptr<DrmWindow> window)230 void ScreenManager::AddWindow(gfx::AcceleratedWidget widget,
231 std::unique_ptr<DrmWindow> window) {
232 std::pair<WidgetToWindowMap::iterator, bool> result =
233 window_map_.emplace(widget, std::move(window));
234 DCHECK(result.second) << "Window already added.";
235 UpdateControllerToWindowMapping();
236 }
237
RemoveWindow(gfx::AcceleratedWidget widget)238 std::unique_ptr<DrmWindow> ScreenManager::RemoveWindow(
239 gfx::AcceleratedWidget widget) {
240 std::unique_ptr<DrmWindow> window = std::move(window_map_[widget]);
241 window_map_.erase(widget);
242 DCHECK(window) << "Attempting to remove non-existing window for " << widget;
243 UpdateControllerToWindowMapping();
244 return window;
245 }
246
GetWindow(gfx::AcceleratedWidget widget)247 DrmWindow* ScreenManager::GetWindow(gfx::AcceleratedWidget widget) {
248 WidgetToWindowMap::iterator it = window_map_.find(widget);
249 if (it != window_map_.end())
250 return it->second.get();
251
252 return nullptr;
253 }
254
255 ScreenManager::HardwareDisplayControllers::iterator
FindDisplayController(const scoped_refptr<DrmDevice> & drm,uint32_t crtc)256 ScreenManager::FindDisplayController(const scoped_refptr<DrmDevice>& drm,
257 uint32_t crtc) {
258 for (auto it = controllers_.begin(); it != controllers_.end(); ++it) {
259 if ((*it)->HasCrtc(drm, crtc))
260 return it;
261 }
262
263 return controllers_.end();
264 }
265
266 ScreenManager::HardwareDisplayControllers::iterator
FindActiveDisplayControllerByLocation(const gfx::Rect & bounds)267 ScreenManager::FindActiveDisplayControllerByLocation(const gfx::Rect& bounds) {
268 for (auto it = controllers_.begin(); it != controllers_.end(); ++it) {
269 gfx::Rect controller_bounds((*it)->origin(), (*it)->GetModeSize());
270 if (controller_bounds == bounds && !(*it)->IsDisabled())
271 return it;
272 }
273
274 return controllers_.end();
275 }
276
277 ScreenManager::HardwareDisplayControllers::iterator
FindActiveDisplayControllerByLocation(const scoped_refptr<DrmDevice> & drm,const gfx::Rect & bounds)278 ScreenManager::FindActiveDisplayControllerByLocation(
279 const scoped_refptr<DrmDevice>& drm,
280 const gfx::Rect& bounds) {
281 for (auto it = controllers_.begin(); it != controllers_.end(); ++it) {
282 gfx::Rect controller_bounds((*it)->origin(), (*it)->GetModeSize());
283 if ((*it)->GetDrmDevice() == drm && controller_bounds == bounds &&
284 !(*it)->IsDisabled())
285 return it;
286 }
287
288 return controllers_.end();
289 }
290
HandleMirrorMode(HardwareDisplayControllers::iterator original,HardwareDisplayControllers::iterator mirror,const scoped_refptr<DrmDevice> & drm,uint32_t crtc,uint32_t connector,const drmModeModeInfo & mode)291 bool ScreenManager::HandleMirrorMode(
292 HardwareDisplayControllers::iterator original,
293 HardwareDisplayControllers::iterator mirror,
294 const scoped_refptr<DrmDevice>& drm,
295 uint32_t crtc,
296 uint32_t connector,
297 const drmModeModeInfo& mode) {
298 gfx::Point last_origin = (*original)->origin();
299 // There should only be one CRTC in this controller.
300 drmModeModeInfo last_mode = (*original)->crtc_controllers()[0]->mode();
301
302 // Modeset the CRTC with its mode in the original controller so that only this
303 // CRTC is affected by the mode. Otherwise it could apply a mode with the same
304 // resolution and refresh rate but with different timings to the other CRTC.
305 // TODO(dnicoara): This is hacky, instead the DrmDisplay and CrtcController
306 // should be merged and picking the mode should be done properly within
307 // HardwareDisplayController.
308 if (ModesetController(original->get(), (*mirror)->origin(), mode)) {
309 (*mirror)->AddCrtc((*original)->RemoveCrtc(drm, crtc));
310 controllers_.erase(original);
311 return true;
312 }
313
314 LOG(ERROR) << "Failed to switch to mirror mode";
315
316 // When things go wrong revert back to the previous configuration since
317 // it is expected that the configuration would not have changed if
318 // things fail.
319 ModesetController(original->get(), last_origin, last_mode);
320 return false;
321 }
322
UpdateControllerToWindowMapping()323 void ScreenManager::UpdateControllerToWindowMapping() {
324 std::map<DrmWindow*, HardwareDisplayController*> window_to_controller_map;
325 // First create a unique mapping between a window and a controller. Note, a
326 // controller may be associated with at most 1 window.
327 for (const auto& controller : controllers_) {
328 if (controller->IsDisabled())
329 continue;
330
331 DrmWindow* window = FindWindowAt(
332 gfx::Rect(controller->origin(), controller->GetModeSize()));
333 if (!window)
334 continue;
335
336 window_to_controller_map[window] = controller.get();
337 }
338
339 // Apply the new mapping to all windows.
340 for (auto& pair : window_map_) {
341 auto it = window_to_controller_map.find(pair.second.get());
342 HardwareDisplayController* controller = nullptr;
343 if (it != window_to_controller_map.end())
344 controller = it->second;
345
346 bool should_enable = controller && pair.second->GetController() &&
347 pair.second->GetController() != controller;
348 pair.second->SetController(controller);
349
350 // If we're moving windows between controllers modeset the controller
351 // otherwise the controller may be waiting for a page flip while the window
352 // tries to schedule another buffer.
353 if (should_enable) {
354 EnableController(controller);
355 }
356 }
357 }
358
GetModesetBuffer(HardwareDisplayController * controller,const gfx::Rect & bounds)359 DrmOverlayPlane ScreenManager::GetModesetBuffer(
360 HardwareDisplayController* controller,
361 const gfx::Rect& bounds) {
362 DrmWindow* window = FindWindowAt(bounds);
363
364 gfx::BufferFormat format = display::DisplaySnapshot::PrimaryFormat();
365 uint32_t fourcc_format = ui::GetFourCCFormatForOpaqueFramebuffer(format);
366 const auto& modifiers =
367 controller->GetFormatModifiersForModesetting(fourcc_format);
368 if (window) {
369 const DrmOverlayPlane* primary = window->GetLastModesetBuffer();
370 const DrmDevice* drm = controller->GetDrmDevice().get();
371 if (primary && primary->buffer->size() == bounds.size() &&
372 primary->buffer->drm_device() == drm) {
373 // If the controller doesn't advertise modifiers, wont have a
374 // modifier either and we can reuse the buffer. Otherwise, check
375 // to see if the controller supports the buffers format
376 // modifier.
377 if (modifiers.empty())
378 return primary->Clone();
379 for (const uint64_t modifier : modifiers) {
380 if (modifier == primary->buffer->format_modifier())
381 return primary->Clone();
382 }
383 }
384 }
385
386 scoped_refptr<DrmDevice> drm = controller->GetDrmDevice();
387 std::unique_ptr<GbmBuffer> buffer =
388 drm->gbm_device()->CreateBufferWithModifiers(
389 fourcc_format, bounds.size(), GBM_BO_USE_SCANOUT, modifiers);
390 if (!buffer) {
391 LOG(ERROR) << "Failed to create scanout buffer";
392 return DrmOverlayPlane::Error();
393 }
394
395 scoped_refptr<DrmFramebuffer> framebuffer = DrmFramebuffer::AddFramebuffer(
396 drm, buffer.get(), buffer->GetSize(), modifiers);
397 if (!framebuffer) {
398 LOG(ERROR) << "Failed to add framebuffer for scanout buffer";
399 return DrmOverlayPlane::Error();
400 }
401
402 sk_sp<SkSurface> surface = buffer->GetSurface();
403 if (!surface) {
404 VLOG(2) << "Can't get a SkSurface from the modeset gbm buffer.";
405 } else if (!FillModesetBuffer(drm, controller, surface.get(),
406 buffer->GetFormat())) {
407 // If we fail to fill the modeset buffer, clear it black to avoid displaying
408 // an uninitialized framebuffer.
409 surface->getCanvas()->clear(SK_ColorBLACK);
410 }
411 return DrmOverlayPlane(framebuffer, nullptr);
412 }
413
EnableController(HardwareDisplayController * controller)414 bool ScreenManager::EnableController(HardwareDisplayController* controller) {
415 DCHECK(!controller->crtc_controllers().empty());
416 gfx::Rect rect(controller->origin(), controller->GetModeSize());
417 DrmOverlayPlane plane = GetModesetBuffer(controller, rect);
418 if (!plane.buffer || !controller->Enable(plane)) {
419 LOG(ERROR) << "Failed to enable controller";
420 return false;
421 }
422
423 return true;
424 }
425
ModesetController(HardwareDisplayController * controller,const gfx::Point & origin,const drmModeModeInfo & mode)426 bool ScreenManager::ModesetController(HardwareDisplayController* controller,
427 const gfx::Point& origin,
428 const drmModeModeInfo& mode) {
429 DCHECK(!controller->crtc_controllers().empty());
430 gfx::Rect rect(origin, gfx::Size(mode.hdisplay, mode.vdisplay));
431 controller->set_origin(origin);
432
433 DrmOverlayPlane plane = GetModesetBuffer(controller, rect);
434 if (!plane.buffer || !controller->Modeset(plane, mode)) {
435 LOG(ERROR) << "Failed to modeset controller";
436 return false;
437 }
438
439 return true;
440 }
441
FindWindowAt(const gfx::Rect & bounds) const442 DrmWindow* ScreenManager::FindWindowAt(const gfx::Rect& bounds) const {
443 for (auto& pair : window_map_) {
444 if (pair.second->bounds() == bounds)
445 return pair.second.get();
446 }
447
448 return nullptr;
449 }
450
451 } // namespace ui
452