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