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/dri_wrapper.h"
21 #include "ui/ozone/public/native_pixmap.h"
25 HardwareDisplayController::PageFlipRequest::PageFlipRequest(
26 const OverlayPlaneList
& planes
,
27 const base::Closure
& callback
)
28 : planes(planes
), callback(callback
) {
31 HardwareDisplayController::PageFlipRequest::~PageFlipRequest() {
34 HardwareDisplayController::HardwareDisplayController(
35 scoped_ptr
<CrtcController
> controller
)
36 : is_disabled_(true) {
37 memset(&mode_
, 0, sizeof(mode_
));
38 AddCrtc(controller
.Pass());
41 HardwareDisplayController::~HardwareDisplayController() {
44 ClearPendingRequests();
47 bool HardwareDisplayController::Modeset(const OverlayPlane
& primary
,
48 drmModeModeInfo mode
) {
49 TRACE_EVENT0("dri", "HDC::Modeset");
50 DCHECK(primary
.buffer
.get());
52 for (size_t i
= 0; i
< crtc_controllers_
.size(); ++i
)
53 status
&= crtc_controllers_
[i
]->Modeset(primary
, mode
);
58 current_planes_
= std::vector
<OverlayPlane
>(1, primary
);
59 pending_planes_
.clear();
60 ClearPendingRequests();
62 // Because a page flip is pending we need to leave some state for the
63 // callback. We use the modeset state since it is the only valid state.
64 if (HasPendingPageFlips())
66 PageFlipRequest(current_planes_
, base::Bind(&base::DoNothing
)));
71 bool HardwareDisplayController::Enable() {
72 TRACE_EVENT0("dri", "HDC::Enable");
73 DCHECK(!current_planes_
.empty());
74 const OverlayPlane
* primary
= OverlayPlane::GetPrimaryPlane(current_planes_
);
76 return Modeset(*primary
, mode_
);
79 void HardwareDisplayController::Disable() {
80 TRACE_EVENT0("dri", "HDC::Disable");
81 for (size_t i
= 0; i
< crtc_controllers_
.size(); ++i
)
82 crtc_controllers_
[i
]->Disable();
87 void HardwareDisplayController::QueueOverlayPlane(const OverlayPlane
& plane
) {
88 pending_planes_
.push_back(plane
);
91 bool HardwareDisplayController::SchedulePageFlip(
92 const base::Closure
& callback
) {
93 TRACE_EVENT0("dri", "HDC::SchedulePageFlip");
95 // Ignore requests with no planes to schedule.
96 if (pending_planes_
.empty()) {
101 requests_
.push_back(PageFlipRequest(pending_planes_
, callback
));
102 pending_planes_
.clear();
104 // A request is being serviced right now.
105 if (HasPendingPageFlips())
108 bool status
= ActualSchedulePageFlip();
110 // No page flip event on failure so discard failed request.
112 requests_
.pop_front();
117 bool HardwareDisplayController::SetCursor(
118 const scoped_refptr
<ScanoutBuffer
>& buffer
) {
124 for (size_t i
= 0; i
< crtc_controllers_
.size(); ++i
)
125 status
&= crtc_controllers_
[i
]->SetCursor(buffer
);
130 bool HardwareDisplayController::UnsetCursor() {
132 for (size_t i
= 0; i
< crtc_controllers_
.size(); ++i
)
133 status
&= crtc_controllers_
[i
]->UnsetCursor();
138 bool HardwareDisplayController::MoveCursor(const gfx::Point
& location
) {
143 for (size_t i
= 0; i
< crtc_controllers_
.size(); ++i
)
144 status
&= crtc_controllers_
[i
]->MoveCursor(location
);
149 void HardwareDisplayController::AddCrtc(scoped_ptr
<CrtcController
> controller
) {
150 owned_hardware_planes_
.add(
152 scoped_ptr
<HardwareDisplayPlaneList
>(new HardwareDisplayPlaneList()));
153 controller
->AddObserver(this);
154 crtc_controllers_
.push_back(controller
.release());
157 scoped_ptr
<CrtcController
> HardwareDisplayController::RemoveCrtc(
159 for (ScopedVector
<CrtcController
>::iterator it
= crtc_controllers_
.begin();
160 it
!= crtc_controllers_
.end(); ++it
) {
161 if ((*it
)->crtc() == crtc
) {
162 scoped_ptr
<CrtcController
> controller(*it
);
163 crtc_controllers_
.weak_erase(it
);
164 // Remove entry from |owned_hardware_planes_| iff no other crtcs share it.
166 for (ScopedVector
<CrtcController
>::iterator it
=
167 crtc_controllers_
.begin();
168 it
!= crtc_controllers_
.end(); ++it
) {
169 if ((*it
)->drm() == controller
->drm()) {
175 owned_hardware_planes_
.erase(controller
->drm());
177 controller
->RemoveObserver(this);
178 // If a display configuration happens mid page flip we want to make sure
179 // the HDC won't wait for an event from a CRTC that is no longer
180 // associated with it.
181 if (controller
->page_flip_pending())
184 return controller
.Pass();
191 bool HardwareDisplayController::HasCrtc(uint32_t crtc
) const {
192 for (size_t i
= 0; i
< crtc_controllers_
.size(); ++i
)
193 if (crtc_controllers_
[i
]->crtc() == crtc
)
199 bool HardwareDisplayController::IsMirrored() const {
200 return crtc_controllers_
.size() > 1;
203 bool HardwareDisplayController::IsDisabled() const {
207 gfx::Size
HardwareDisplayController::GetModeSize() const {
208 return gfx::Size(mode_
.hdisplay
, mode_
.vdisplay
);
211 uint64_t HardwareDisplayController::GetTimeOfLastFlip() const {
213 for (size_t i
= 0; i
< crtc_controllers_
.size(); ++i
)
214 if (time
< crtc_controllers_
[i
]->time_of_last_flip())
215 time
= crtc_controllers_
[i
]->time_of_last_flip();
220 void HardwareDisplayController::OnPageFlipEvent() {
221 TRACE_EVENT0("dri", "HDC::OnPageFlipEvent");
222 // OnPageFlipEvent() needs to handle 2 cases:
223 // 1) Normal page flips in which case:
224 // a) HasPendingPageFlips() may return false if we're in mirror mode and
225 // one of the CRTCs hasn't finished page flipping. In this case we want
226 // to wait for all the CRTCs.
227 // b) HasPendingPageFlips() returns true in which case all CRTCs are ready
228 // for the next request. In this case we expect that |requests_| isn't
230 // 2) A CRTC was added while it was page flipping. In this case a modeset
231 // must be performed. Modesetting clears all pending requests, however the
232 // CRTCs will honor the scheduled page flip. Thus we need to handle page
233 // flip events with no requests.
235 if (HasPendingPageFlips())
238 if (!requests_
.empty())
239 ProcessPageFlipRequest();
241 // ProcessPageFlipRequest() consumes a request.
242 if (requests_
.empty())
245 // At this point we still have requests pending, so schedule the next request.
246 bool status
= ActualSchedulePageFlip();
248 PageFlipRequest request
= requests_
.front();
249 requests_
.pop_front();
251 // Normally the caller would handle the error call, but because we're in a
252 // delayed schedule the initial SchedulePageFlip() already returned true,
253 // thus we need to run the callback.
254 request
.callback
.Run();
258 bool HardwareDisplayController::HasPendingPageFlips() const {
259 for (size_t i
= 0; i
< crtc_controllers_
.size(); ++i
)
260 if (crtc_controllers_
[i
]->page_flip_pending())
266 bool HardwareDisplayController::ActualSchedulePageFlip() {
267 TRACE_EVENT0("dri", "HDC::ActualSchedulePageFlip");
268 DCHECK(!requests_
.empty());
271 ProcessPageFlipRequest();
275 OverlayPlaneList pending_planes
= requests_
.front().planes
;
276 std::sort(pending_planes
.begin(), pending_planes
.end(),
277 [](const OverlayPlane
& l
, const OverlayPlane
& r
) {
278 return l
.z_order
< r
.z_order
;
282 for (size_t i
= 0; i
< crtc_controllers_
.size(); ++i
) {
283 status
&= crtc_controllers_
[i
]->SchedulePageFlip(
284 owned_hardware_planes_
.get(crtc_controllers_
[i
]->drm()),
288 for (const auto& planes
: owned_hardware_planes_
) {
289 if (!planes
.first
->plane_manager()->Commit(planes
.second
)) {
297 void HardwareDisplayController::ProcessPageFlipRequest() {
298 DCHECK(!requests_
.empty());
299 PageFlipRequest request
= requests_
.front();
300 requests_
.pop_front();
302 current_planes_
.swap(request
.planes
);
303 request
.callback
.Run();
306 void HardwareDisplayController::ClearPendingRequests() {
307 while (!requests_
.empty()) {
308 PageFlipRequest request
= requests_
.front();
309 requests_
.pop_front();
310 request
.callback
.Run();