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