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/hardware_display_controller.h"
6 
7 #include <drm.h>
8 #include <string.h>
9 #include <xf86drm.h>
10 #include <memory>
11 #include <utility>
12 
13 #include "base/bind.h"
14 #include "base/logging.h"
15 #include "base/stl_util.h"
16 #include "base/trace_event/trace_event.h"
17 #include "third_party/libdrm/src/include/drm/drm_fourcc.h"
18 #include "third_party/skia/include/core/SkCanvas.h"
19 #include "ui/gfx/geometry/point.h"
20 #include "ui/gfx/geometry/size.h"
21 #include "ui/gfx/gpu_fence.h"
22 #include "ui/gfx/native_pixmap.h"
23 #include "ui/gfx/presentation_feedback.h"
24 #include "ui/gfx/swap_result.h"
25 #include "ui/ozone/platform/drm/common/drm_util.h"
26 #include "ui/ozone/platform/drm/gpu/crtc_controller.h"
27 #include "ui/ozone/platform/drm/gpu/drm_device.h"
28 #include "ui/ozone/platform/drm/gpu/drm_dumb_buffer.h"
29 #include "ui/ozone/platform/drm/gpu/hardware_display_plane.h"
30 #include "ui/ozone/platform/drm/gpu/page_flip_request.h"
31 
32 // Vendor ID for downstream, interim ChromeOS specific modifiers.
33 #define DRM_FORMAT_MOD_VENDOR_CHROMEOS 0xf0
34 // TODO(gurchetansingh) Remove once DRM_FORMAT_MOD_ARM_AFBC is used by all
35 // kernels and allocators.
36 #define DRM_FORMAT_MOD_CHROMEOS_ROCKCHIP_AFBC fourcc_mod_code(CHROMEOS, 1)
37 
38 namespace ui {
39 
40 namespace {
41 
CompletePageFlip(base::WeakPtr<HardwareDisplayController> hardware_display_controller_,PresentationOnceCallback callback,DrmOverlayPlaneList plane_list,const gfx::PresentationFeedback & presentation_feedback)42 void CompletePageFlip(
43     base::WeakPtr<HardwareDisplayController> hardware_display_controller_,
44     PresentationOnceCallback callback,
45     DrmOverlayPlaneList plane_list,
46     const gfx::PresentationFeedback& presentation_feedback) {
47   if (hardware_display_controller_) {
48     hardware_display_controller_->OnPageFlipComplete(std::move(plane_list),
49                                                      presentation_feedback);
50   }
51   std::move(callback).Run(presentation_feedback);
52 }
53 
DrawCursor(DrmDumbBuffer * cursor,const SkBitmap & image)54 void DrawCursor(DrmDumbBuffer* cursor, const SkBitmap& image) {
55   SkRect damage;
56   image.getBounds(&damage);
57 
58   // Clear to transparent in case |image| is smaller than the canvas.
59   SkCanvas* canvas = cursor->GetCanvas();
60   canvas->clear(SK_ColorTRANSPARENT);
61   canvas->drawBitmapRect(image, damage, nullptr);
62 }
63 
64 }  // namespace
65 
HardwareDisplayController(std::unique_ptr<CrtcController> controller,const gfx::Point & origin)66 HardwareDisplayController::HardwareDisplayController(
67     std::unique_ptr<CrtcController> controller,
68     const gfx::Point& origin)
69     : origin_(origin) {
70   AddCrtc(std::move(controller));
71   AllocateCursorBuffers();
72 }
73 
74 HardwareDisplayController::~HardwareDisplayController() = default;
75 
GetModesetProps(CommitRequest * commit_request,const DrmOverlayPlane & primary,const drmModeModeInfo & mode)76 void HardwareDisplayController::GetModesetProps(CommitRequest* commit_request,
77                                                 const DrmOverlayPlane& primary,
78                                                 const drmModeModeInfo& mode) {
79   TRACE_EVENT0("drm", "HDC::GetModesetProps");
80   GetModesetPropsForCrtcs(commit_request, primary,
81                           /*use_current_crtc_mode=*/false, mode);
82 }
83 
GetEnableProps(CommitRequest * commit_request,const DrmOverlayPlane & primary)84 void HardwareDisplayController::GetEnableProps(CommitRequest* commit_request,
85                                                const DrmOverlayPlane& primary) {
86   TRACE_EVENT0("drm", "HDC::GetEnableProps");
87   // TODO(markyacoub): Simplify and remove the use of empty_mode.
88   drmModeModeInfo empty_mode = {};
89   GetModesetPropsForCrtcs(commit_request, primary,
90                           /*use_current_crtc_mode=*/true, empty_mode);
91 }
92 
GetModesetPropsForCrtcs(CommitRequest * commit_request,const DrmOverlayPlane & primary,bool use_current_crtc_mode,const drmModeModeInfo & mode)93 void HardwareDisplayController::GetModesetPropsForCrtcs(
94     CommitRequest* commit_request,
95     const DrmOverlayPlane& primary,
96     bool use_current_crtc_mode,
97     const drmModeModeInfo& mode) {
98   DCHECK(commit_request);
99 
100   GetDrmDevice()->plane_manager()->BeginFrame(&owned_hardware_planes_);
101 
102   for (const auto& controller : crtc_controllers_) {
103     drmModeModeInfo modeset_mode =
104         use_current_crtc_mode ? controller->mode() : mode;
105 
106     DrmOverlayPlaneList overlays;
107     overlays.push_back(primary.Clone());
108 
109     CrtcCommitRequest request = CrtcCommitRequest::EnableCrtcRequest(
110         controller->crtc(), controller->connector(), modeset_mode,
111         &owned_hardware_planes_, std::move(overlays));
112     commit_request->push_back(std::move(request));
113   }
114 }
115 
GetDisableProps(CommitRequest * commit_request)116 void HardwareDisplayController::GetDisableProps(CommitRequest* commit_request) {
117   TRACE_EVENT0("drm", "HDC::GetDisableProps");
118 
119   for (const auto& controller : crtc_controllers_) {
120     CrtcCommitRequest request = CrtcCommitRequest::DisableCrtcRequest(
121         controller->crtc(), controller->connector(), &owned_hardware_planes_);
122     commit_request->push_back(std::move(request));
123   }
124 }
125 
UpdateState(bool enable_requested,const DrmOverlayPlane * primary_plane)126 void HardwareDisplayController::UpdateState(
127     bool enable_requested,
128     const DrmOverlayPlane* primary_plane) {
129   // Verify that the current state matches the requested state.
130   if (enable_requested && IsEnabled()) {
131     DCHECK(primary_plane);
132     // TODO(markyacoub): This should be absorbed in the commit request.
133     ResetCursor();
134     OnModesetComplete(*primary_plane);
135   }
136 }
137 
SchedulePageFlip(DrmOverlayPlaneList plane_list,SwapCompletionOnceCallback submission_callback,PresentationOnceCallback presentation_callback)138 void HardwareDisplayController::SchedulePageFlip(
139     DrmOverlayPlaneList plane_list,
140     SwapCompletionOnceCallback submission_callback,
141     PresentationOnceCallback presentation_callback) {
142   DCHECK(!page_flip_request_);
143   scoped_refptr<PageFlipRequest> page_flip_request =
144       base::MakeRefCounted<PageFlipRequest>(GetRefreshInterval());
145   std::unique_ptr<gfx::GpuFence> out_fence;
146 
147   bool status =
148       ScheduleOrTestPageFlip(plane_list, page_flip_request, &out_fence);
149   CHECK(status) << "SchedulePageFlip failed";
150 
151   if (page_flip_request->page_flip_count() == 0) {
152     // Apparently, there was nothing to do. This probably should not be
153     // able to happen but both CrtcController::AssignOverlayPlanes and
154     // HardwareDisplayPlaneManagerLegacy::Commit appear to have cases
155     // where we ACK without actually scheduling a page flip.
156     std::move(submission_callback).Run(gfx::SwapResult::SWAP_ACK, nullptr);
157     std::move(presentation_callback).Run(gfx::PresentationFeedback::Failure());
158     return;
159   }
160 
161   std::move(submission_callback)
162       .Run(gfx::SwapResult::SWAP_ACK, std::move(out_fence));
163 
164   // Everything was submitted successfully, wait for asynchronous completion.
165   page_flip_request->TakeCallback(
166       base::BindOnce(&CompletePageFlip, weak_ptr_factory_.GetWeakPtr(),
167                      std::move(presentation_callback), std::move(plane_list)));
168   page_flip_request_ = std::move(page_flip_request);
169 }
170 
TestPageFlip(const DrmOverlayPlaneList & plane_list)171 bool HardwareDisplayController::TestPageFlip(
172     const DrmOverlayPlaneList& plane_list) {
173   return ScheduleOrTestPageFlip(plane_list, nullptr, nullptr);
174 }
175 
ScheduleOrTestPageFlip(const DrmOverlayPlaneList & plane_list,scoped_refptr<PageFlipRequest> page_flip_request,std::unique_ptr<gfx::GpuFence> * out_fence)176 bool HardwareDisplayController::ScheduleOrTestPageFlip(
177     const DrmOverlayPlaneList& plane_list,
178     scoped_refptr<PageFlipRequest> page_flip_request,
179     std::unique_ptr<gfx::GpuFence>* out_fence) {
180   TRACE_EVENT0("drm", "HDC::SchedulePageFlip");
181   DCHECK(IsEnabled());
182 
183   // Ignore requests with no planes to schedule.
184   if (plane_list.empty())
185     return true;
186 
187   DrmOverlayPlaneList pending_planes = DrmOverlayPlane::Clone(plane_list);
188   std::sort(pending_planes.begin(), pending_planes.end(),
189             [](const DrmOverlayPlane& l, const DrmOverlayPlane& r) {
190               return l.z_order < r.z_order;
191             });
192   GetDrmDevice()->plane_manager()->BeginFrame(&owned_hardware_planes_);
193 
194   bool status = true;
195   for (const auto& controller : crtc_controllers_) {
196     status &= controller->AssignOverlayPlanes(
197         &owned_hardware_planes_, pending_planes, /*is_modesetting=*/false);
198   }
199 
200   status &= GetDrmDevice()->plane_manager()->Commit(
201       &owned_hardware_planes_, page_flip_request, out_fence);
202 
203   return status;
204 }
205 
GetFormatModifiers(uint32_t format) const206 std::vector<uint64_t> HardwareDisplayController::GetFormatModifiers(
207     uint32_t format) const {
208   std::vector<uint64_t> modifiers;
209 
210   if (crtc_controllers_.empty())
211     return modifiers;
212 
213   modifiers = crtc_controllers_[0]->GetFormatModifiers(format);
214 
215   for (size_t i = 1; i < crtc_controllers_.size(); ++i) {
216     std::vector<uint64_t> other =
217         crtc_controllers_[i]->GetFormatModifiers(format);
218     std::vector<uint64_t> intersection;
219 
220     std::set_intersection(modifiers.begin(), modifiers.end(), other.begin(),
221                           other.end(), std::back_inserter(intersection));
222     modifiers = std::move(intersection);
223   }
224 
225   return modifiers;
226 }
227 
228 std::vector<uint64_t>
GetFormatModifiersForModesetting(uint32_t fourcc_format) const229 HardwareDisplayController::GetFormatModifiersForModesetting(
230     uint32_t fourcc_format) const {
231   const auto& modifiers = GetFormatModifiers(fourcc_format);
232   std::vector<uint64_t> filtered_modifiers;
233   for (auto modifier : modifiers) {
234     // AFBC for modeset buffers doesn't work correctly, as we can't fill it with
235     // a valid AFBC buffer. For now, don't use AFBC for modeset buffers.
236     // TODO: Use AFBC for modeset buffers if it is available.
237     // See https://crbug.com/852675.
238     if (modifier != DRM_FORMAT_MOD_CHROMEOS_ROCKCHIP_AFBC) {
239       filtered_modifiers.push_back(modifier);
240     }
241   }
242   return filtered_modifiers;
243 }
244 
MoveCursor(const gfx::Point & location)245 void HardwareDisplayController::MoveCursor(const gfx::Point& location) {
246   cursor_location_ = location;
247   UpdateCursorLocation();
248 }
249 
SetCursor(SkBitmap bitmap)250 void HardwareDisplayController::SetCursor(SkBitmap bitmap) {
251   if (bitmap.drawsNothing()) {
252     current_cursor_ = nullptr;
253   } else {
254     current_cursor_ = NextCursorBuffer();
255     DrawCursor(current_cursor_, bitmap);
256   }
257 
258   UpdateCursorImage();
259 }
260 
AddCrtc(std::unique_ptr<CrtcController> controller)261 void HardwareDisplayController::AddCrtc(
262     std::unique_ptr<CrtcController> controller) {
263   scoped_refptr<DrmDevice> drm = controller->drm();
264   DCHECK(crtc_controllers_.empty() || drm == GetDrmDevice());
265 
266   // Check if this controller owns any planes and ensure we keep track of them.
267   const std::vector<std::unique_ptr<HardwareDisplayPlane>>& all_planes =
268       drm->plane_manager()->planes();
269   uint32_t crtc = controller->crtc();
270   for (const auto& plane : all_planes) {
271     if (plane->in_use() && (plane->owning_crtc() == crtc))
272       owned_hardware_planes_.old_plane_list.push_back(plane.get());
273   }
274 
275   crtc_controllers_.push_back(std::move(controller));
276 }
277 
RemoveCrtc(const scoped_refptr<DrmDevice> & drm,uint32_t crtc)278 std::unique_ptr<CrtcController> HardwareDisplayController::RemoveCrtc(
279     const scoped_refptr<DrmDevice>& drm,
280     uint32_t crtc) {
281   auto controller_it = std::find_if(
282       crtc_controllers_.begin(), crtc_controllers_.end(),
283       [drm, crtc](const std::unique_ptr<CrtcController>& crtc_controller) {
284         return crtc_controller->drm() == drm && crtc_controller->crtc() == crtc;
285       });
286   if (controller_it == crtc_controllers_.end())
287     return nullptr;
288 
289   std::unique_ptr<CrtcController> controller(std::move(*controller_it));
290   crtc_controllers_.erase(controller_it);
291 
292   // Move all the planes that have been committed in the last pageflip for this
293   // CRTC at the end of the collection.
294   auto first_plane_to_disable_it =
295       std::partition(owned_hardware_planes_.old_plane_list.begin(),
296                      owned_hardware_planes_.old_plane_list.end(),
297                      [crtc](const HardwareDisplayPlane* plane) {
298                        return plane->owning_crtc() != crtc;
299                      });
300 
301   // Disable the planes enabled with the last commit on |crtc|, otherwise
302   // the planes will be visible if the crtc is reassigned to another connector.
303   HardwareDisplayPlaneList hardware_plane_list;
304   std::copy(first_plane_to_disable_it,
305             owned_hardware_planes_.old_plane_list.end(),
306             std::back_inserter(hardware_plane_list.old_plane_list));
307   drm->plane_manager()->DisableOverlayPlanes(&hardware_plane_list);
308 
309   // Remove the planes assigned to |crtc|.
310   owned_hardware_planes_.old_plane_list.erase(
311       first_plane_to_disable_it, owned_hardware_planes_.old_plane_list.end());
312 
313   return controller;
314 }
315 
HasCrtc(const scoped_refptr<DrmDevice> & drm,uint32_t crtc) const316 bool HardwareDisplayController::HasCrtc(const scoped_refptr<DrmDevice>& drm,
317                                         uint32_t crtc) const {
318   for (const auto& controller : crtc_controllers_) {
319     if (controller->drm() == drm && controller->crtc() == crtc)
320       return true;
321   }
322 
323   return false;
324 }
325 
IsMirrored() const326 bool HardwareDisplayController::IsMirrored() const {
327   return crtc_controllers_.size() > 1;
328 }
329 
IsEnabled() const330 bool HardwareDisplayController::IsEnabled() const {
331   bool is_enabled = true;
332 
333   for (const auto& controller : crtc_controllers_)
334     is_enabled &= controller->is_enabled();
335 
336   return is_enabled;
337 }
338 
GetModeSize() const339 gfx::Size HardwareDisplayController::GetModeSize() const {
340   // If there are multiple CRTCs they should all have the same size.
341   return gfx::Size(crtc_controllers_[0]->mode().hdisplay,
342                    crtc_controllers_[0]->mode().vdisplay);
343 }
344 
GetRefreshInterval() const345 base::TimeDelta HardwareDisplayController::GetRefreshInterval() const {
346   // If there are multiple CRTCs they should all have the same refresh rate.
347   float vrefresh = ModeRefreshRate(crtc_controllers_[0]->mode());
348   return vrefresh ? base::TimeDelta::FromSeconds(1) / vrefresh
349                   : base::TimeDelta();
350 }
351 
GetTimeOfLastFlip() const352 base::TimeTicks HardwareDisplayController::GetTimeOfLastFlip() const {
353   return time_of_last_flip_;
354 }
355 
GetDrmDevice() const356 scoped_refptr<DrmDevice> HardwareDisplayController::GetDrmDevice() const {
357   DCHECK(!crtc_controllers_.empty());
358   // TODO(dnicoara) When we support mirroring across DRM devices, figure out
359   // which device should be used for allocations.
360   return crtc_controllers_[0]->drm();
361 }
362 
OnPageFlipComplete(DrmOverlayPlaneList pending_planes,const gfx::PresentationFeedback & presentation_feedback)363 void HardwareDisplayController::OnPageFlipComplete(
364     DrmOverlayPlaneList pending_planes,
365     const gfx::PresentationFeedback& presentation_feedback) {
366   if (!page_flip_request_)
367     return;  // Modeset occured during this page flip.
368   time_of_last_flip_ = presentation_feedback.timestamp;
369   current_planes_ = std::move(pending_planes);
370   for (const auto& controller : crtc_controllers_) {
371     GetDrmDevice()->plane_manager()->ResetModesetBufferOfCrtc(
372         controller->crtc());
373   }
374   page_flip_request_ = nullptr;
375 }
376 
OnModesetComplete(const DrmOverlayPlane & primary)377 void HardwareDisplayController::OnModesetComplete(
378     const DrmOverlayPlane& primary) {
379   // drmModeSetCrtc has an immediate effect, so we can assume that the current
380   // planes have been updated. However if a page flip is still pending, set the
381   // pending planes to the same values so that the callback keeps the correct
382   // state.
383   page_flip_request_ = nullptr;
384   owned_hardware_planes_.legacy_page_flips.clear();
385   current_planes_.clear();
386   current_planes_.push_back(primary.Clone());
387   time_of_last_flip_ = base::TimeTicks::Now();
388 }
389 
AllocateCursorBuffers()390 void HardwareDisplayController::AllocateCursorBuffers() {
391   TRACE_EVENT0("drm", "HDC::AllocateCursorBuffers");
392   gfx::Size max_cursor_size = GetMaximumCursorSize(GetDrmDevice()->get_fd());
393   SkImageInfo info = SkImageInfo::MakeN32Premul(max_cursor_size.width(),
394                                                 max_cursor_size.height());
395   for (size_t i = 0; i < base::size(cursor_buffers_); ++i) {
396     cursor_buffers_[i] = std::make_unique<DrmDumbBuffer>(GetDrmDevice());
397     // Don't register a framebuffer for cursors since they are special (they
398     // aren't modesetting buffers and drivers may fail to register them due to
399     // their small sizes).
400     if (!cursor_buffers_[i]->Initialize(info)) {
401       LOG(FATAL) << "Failed to initialize cursor buffer";
402       return;
403     }
404   }
405 }
406 
NextCursorBuffer()407 DrmDumbBuffer* HardwareDisplayController::NextCursorBuffer() {
408   ++cursor_frontbuffer_;
409   cursor_frontbuffer_ %= base::size(cursor_buffers_);
410   return cursor_buffers_[cursor_frontbuffer_].get();
411 }
412 
UpdateCursorImage()413 void HardwareDisplayController::UpdateCursorImage() {
414   uint32_t handle = 0;
415   gfx::Size size;
416 
417   if (current_cursor_) {
418     handle = current_cursor_->GetHandle();
419     size = current_cursor_->GetSize();
420   }
421 
422   for (const auto& controller : crtc_controllers_)
423     controller->SetCursor(handle, size);
424 }
425 
UpdateCursorLocation()426 void HardwareDisplayController::UpdateCursorLocation() {
427   for (const auto& controller : crtc_controllers_)
428     controller->MoveCursor(cursor_location_);
429 }
430 
ResetCursor()431 void HardwareDisplayController::ResetCursor() {
432   UpdateCursorLocation();
433   UpdateCursorImage();
434 }
435 
436 }  // namespace ui
437