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.
5 #include "ui/ozone/platform/dri/hardware_display_controller.h"
12 #include "base/basictypes.h"
13 #include "base/logging.h"
14 #include "base/trace_event/trace_event.h"
15 #include "third_party/skia/include/core/SkCanvas.h"
16 #include "ui/gfx/geometry/point.h"
17 #include "ui/gfx/geometry/size.h"
18 #include "ui/ozone/platform/dri/crtc_controller.h"
19 #include "ui/ozone/platform/dri/dri_buffer.h"
20 #include "ui/ozone/platform/dri/drm_device.h"
21 #include "ui/ozone/public/native_pixmap.h"
25 HardwareDisplayController::PageFlipRequest::PageFlipRequest(
26 const OverlayPlaneList
& planes
,
28 const base::Closure
& callback
)
29 : planes(planes
), is_sync(is_sync
), callback(callback
) {
32 HardwareDisplayController::PageFlipRequest::~PageFlipRequest() {
35 HardwareDisplayController::HardwareDisplayController(
36 scoped_ptr
<CrtcController
> controller
)
37 : is_disabled_(true) {
38 memset(&mode_
, 0, sizeof(mode_
));
39 AddCrtc(controller
.Pass());
42 HardwareDisplayController::~HardwareDisplayController() {
45 ClearPendingRequests();
48 bool HardwareDisplayController::Modeset(const OverlayPlane
& primary
,
49 drmModeModeInfo mode
) {
50 TRACE_EVENT0("dri", "HDC::Modeset");
51 DCHECK(primary
.buffer
.get());
53 for (size_t i
= 0; i
< crtc_controllers_
.size(); ++i
)
54 status
&= crtc_controllers_
[i
]->Modeset(primary
, mode
);
59 current_planes_
= std::vector
<OverlayPlane
>(1, primary
);
60 pending_planes_
.clear();
61 ClearPendingRequests();
63 // Because a page flip is pending we need to leave some state for the
64 // callback. We use the modeset state since it is the only valid state.
65 if (HasPendingPageFlips())
67 PageFlipRequest(current_planes_
, false, base::Bind(&base::DoNothing
)));
72 bool HardwareDisplayController::Enable() {
73 TRACE_EVENT0("dri", "HDC::Enable");
74 DCHECK(!current_planes_
.empty());
75 const OverlayPlane
* primary
= OverlayPlane::GetPrimaryPlane(current_planes_
);
77 return Modeset(*primary
, mode_
);
80 void HardwareDisplayController::Disable() {
81 TRACE_EVENT0("dri", "HDC::Disable");
82 for (size_t i
= 0; i
< crtc_controllers_
.size(); ++i
)
83 crtc_controllers_
[i
]->Disable();
88 void HardwareDisplayController::QueueOverlayPlane(const OverlayPlane
& plane
) {
89 pending_planes_
.push_back(plane
);
92 bool HardwareDisplayController::SchedulePageFlip(
94 const base::Closure
& callback
) {
95 TRACE_EVENT0("dri", "HDC::SchedulePageFlip");
97 // Ignore requests with no planes to schedule.
98 if (pending_planes_
.empty()) {
103 requests_
.push_back(PageFlipRequest(pending_planes_
, is_sync
, callback
));
104 pending_planes_
.clear();
106 // A request is being serviced right now.
107 if (HasPendingPageFlips())
110 bool status
= ActualSchedulePageFlip();
112 // No page flip event on failure so discard failed request.
114 requests_
.pop_front();
119 bool HardwareDisplayController::SetCursor(
120 const scoped_refptr
<ScanoutBuffer
>& buffer
) {
126 for (size_t i
= 0; i
< crtc_controllers_
.size(); ++i
)
127 status
&= crtc_controllers_
[i
]->SetCursor(buffer
);
132 bool HardwareDisplayController::UnsetCursor() {
134 for (size_t i
= 0; i
< crtc_controllers_
.size(); ++i
)
135 status
&= crtc_controllers_
[i
]->UnsetCursor();
140 bool HardwareDisplayController::MoveCursor(const gfx::Point
& location
) {
145 for (size_t i
= 0; i
< crtc_controllers_
.size(); ++i
)
146 status
&= crtc_controllers_
[i
]->MoveCursor(location
);
151 void HardwareDisplayController::AddCrtc(scoped_ptr
<CrtcController
> controller
) {
152 owned_hardware_planes_
.add(
153 controller
->drm().get(),
154 scoped_ptr
<HardwareDisplayPlaneList
>(new HardwareDisplayPlaneList()));
155 controller
->AddObserver(this);
156 crtc_controllers_
.push_back(controller
.release());
159 scoped_ptr
<CrtcController
> HardwareDisplayController::RemoveCrtc(
160 const scoped_refptr
<DrmDevice
>& drm
,
162 for (ScopedVector
<CrtcController
>::iterator it
= crtc_controllers_
.begin();
163 it
!= crtc_controllers_
.end(); ++it
) {
164 if ((*it
)->drm() == drm
&& (*it
)->crtc() == crtc
) {
165 scoped_ptr
<CrtcController
> controller(*it
);
166 crtc_controllers_
.weak_erase(it
);
167 // Remove entry from |owned_hardware_planes_| iff no other crtcs share it.
169 for (ScopedVector
<CrtcController
>::iterator it
=
170 crtc_controllers_
.begin();
171 it
!= crtc_controllers_
.end(); ++it
) {
172 if ((*it
)->drm() == controller
->drm()) {
178 owned_hardware_planes_
.erase(controller
->drm().get());
180 controller
->RemoveObserver(this);
181 // If a display configuration happens mid page flip we want to make sure
182 // the HDC won't wait for an event from a CRTC that is no longer
183 // associated with it.
184 if (controller
->page_flip_pending())
187 return controller
.Pass();
194 bool HardwareDisplayController::HasCrtc(const scoped_refptr
<DrmDevice
>& drm
,
195 uint32_t crtc
) const {
196 for (size_t i
= 0; i
< crtc_controllers_
.size(); ++i
)
197 if (crtc_controllers_
[i
]->drm() == drm
&&
198 crtc_controllers_
[i
]->crtc() == crtc
)
204 bool HardwareDisplayController::IsMirrored() const {
205 return crtc_controllers_
.size() > 1;
208 bool HardwareDisplayController::IsDisabled() const {
212 gfx::Size
HardwareDisplayController::GetModeSize() const {
213 return gfx::Size(mode_
.hdisplay
, mode_
.vdisplay
);
216 uint64_t HardwareDisplayController::GetTimeOfLastFlip() const {
218 for (size_t i
= 0; i
< crtc_controllers_
.size(); ++i
)
219 if (time
< crtc_controllers_
[i
]->time_of_last_flip())
220 time
= crtc_controllers_
[i
]->time_of_last_flip();
225 void HardwareDisplayController::OnPageFlipEvent() {
226 TRACE_EVENT0("dri", "HDC::OnPageFlipEvent");
227 // OnPageFlipEvent() needs to handle 2 cases:
228 // 1) Normal page flips in which case:
229 // a) HasPendingPageFlips() may return false if we're in mirror mode and
230 // one of the CRTCs hasn't finished page flipping. In this case we want
231 // to wait for all the CRTCs.
232 // b) HasPendingPageFlips() returns true in which case all CRTCs are ready
233 // for the next request. In this case we expect that |requests_| isn't
235 // 2) A CRTC was added while it was page flipping. In this case a modeset
236 // must be performed. Modesetting clears all pending requests, however the
237 // CRTCs will honor the scheduled page flip. Thus we need to handle page
238 // flip events with no requests.
240 if (HasPendingPageFlips())
243 if (!requests_
.empty())
244 ProcessPageFlipRequest();
246 // ProcessPageFlipRequest() consumes a request.
247 if (requests_
.empty())
250 // At this point we still have requests pending, so schedule the next request.
251 bool status
= ActualSchedulePageFlip();
253 PageFlipRequest request
= requests_
.front();
254 requests_
.pop_front();
256 // Normally the caller would handle the error call, but because we're in a
257 // delayed schedule the initial SchedulePageFlip() already returned true,
258 // thus we need to run the callback.
259 request
.callback
.Run();
263 scoped_refptr
<DrmDevice
> HardwareDisplayController::GetAllocationDrmDevice()
265 DCHECK(!crtc_controllers_
.empty());
266 // TODO(dnicoara) When we support mirroring across DRM devices, figure out
267 // which device should be used for allocations.
268 return crtc_controllers_
[0]->drm();
271 bool HardwareDisplayController::HasPendingPageFlips() const {
272 for (size_t i
= 0; i
< crtc_controllers_
.size(); ++i
)
273 if (crtc_controllers_
[i
]->page_flip_pending())
279 bool HardwareDisplayController::ActualSchedulePageFlip() {
280 TRACE_EVENT0("dri", "HDC::ActualSchedulePageFlip");
281 DCHECK(!requests_
.empty());
284 ProcessPageFlipRequest();
288 OverlayPlaneList pending_planes
= requests_
.front().planes
;
289 std::sort(pending_planes
.begin(), pending_planes
.end(),
290 [](const OverlayPlane
& l
, const OverlayPlane
& r
) {
291 return l
.z_order
< r
.z_order
;
295 for (size_t i
= 0; i
< crtc_controllers_
.size(); ++i
) {
296 status
&= crtc_controllers_
[i
]->SchedulePageFlip(
297 owned_hardware_planes_
.get(crtc_controllers_
[i
]->drm().get()),
301 bool is_sync
= requests_
.front().is_sync
;
302 for (const auto& planes
: owned_hardware_planes_
) {
303 if (!planes
.first
->plane_manager()->Commit(planes
.second
, is_sync
)) {
311 void HardwareDisplayController::ProcessPageFlipRequest() {
312 DCHECK(!requests_
.empty());
313 PageFlipRequest request
= requests_
.front();
314 requests_
.pop_front();
316 current_planes_
.swap(request
.planes
);
317 request
.callback
.Run();
320 void HardwareDisplayController::ClearPendingRequests() {
321 while (!requests_
.empty()) {
322 PageFlipRequest request
= requests_
.front();
323 requests_
.pop_front();
324 request
.callback
.Run();