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/debug/trace_event.h"
14 #include "base/logging.h"
15 #include "base/time/time.h"
16 #include "third_party/skia/include/core/SkCanvas.h"
17 #include "ui/gfx/geometry/point.h"
18 #include "ui/gfx/geometry/size.h"
19 #include "ui/ozone/platform/dri/crtc_state.h"
20 #include "ui/ozone/platform/dri/dri_buffer.h"
21 #include "ui/ozone/platform/dri/dri_wrapper.h"
22 #include "ui/ozone/public/native_pixmap.h"
28 // DRM callback on page flip events. This callback is triggered after the
29 // page flip has happened and the backbuffer is now the new frontbuffer
30 // The old frontbuffer is no longer used by the hardware and can be used for
31 // future draw operations.
33 // |device| will contain a reference to the |ScanoutSurface| object which
34 // the event belongs to.
36 // TODO(dnicoara) When we have a FD handler for the DRM calls in the message
37 // loop, we can move this function in the handler.
38 void HandlePageFlipEvent(int fd,
41 unsigned int useconds,
43 static_cast<HardwareDisplayController*>(controller)
44 ->OnPageFlipEvent(frame, seconds, useconds);
47 const OverlayPlane& GetPrimaryPlane(const OverlayPlaneList& overlays) {
48 for (size_t i = 0; i < overlays.size(); ++i) {
49 if (overlays[i].z_order == 0)
59 OverlayPlane::OverlayPlane(scoped_refptr<ScanoutBuffer> buffer)
62 display_bounds(gfx::Point(), buffer->GetSize()),
63 crop_rect(0, 0, 1, 1),
66 OverlayPlane::OverlayPlane(scoped_refptr<ScanoutBuffer> buffer,
68 gfx::OverlayTransform plane_transform,
69 const gfx::Rect& display_bounds,
70 const gfx::RectF& crop_rect)
73 plane_transform(plane_transform),
74 display_bounds(display_bounds),
79 OverlayPlane::~OverlayPlane() {}
81 HardwareDisplayController::HardwareDisplayController(
83 scoped_ptr<CrtcState> state)
86 time_of_last_flip_(0),
87 pending_page_flips_(0) {
88 crtc_states_.push_back(state.release());
91 HardwareDisplayController::~HardwareDisplayController() {
96 bool HardwareDisplayController::Modeset(const OverlayPlane& primary,
97 drmModeModeInfo mode) {
98 TRACE_EVENT0("dri", "HDC::Modeset");
99 DCHECK(primary.buffer.get());
100 pending_page_flips_ = 0;
102 for (size_t i = 0; i < crtc_states_.size(); ++i) {
103 status &= ModesetCrtc(primary.buffer, mode, crtc_states_[i]);
104 crtc_states_[i]->set_is_disabled(false);
107 // Since a subset of controllers may be actively using |primary|, just keep
109 current_planes_ = std::vector<OverlayPlane>(1, primary);
110 pending_planes_.clear();
111 is_disabled_ = false;
116 bool HardwareDisplayController::Enable() {
117 TRACE_EVENT0("dri", "HDC::Enable");
118 DCHECK(!current_planes_.empty());
119 OverlayPlane primary = GetPrimaryPlane(current_planes_);
120 DCHECK(primary.buffer.get());
121 pending_page_flips_ = 0;
123 for (size_t i = 0; i < crtc_states_.size(); ++i)
124 status &= ModesetCrtc(primary.buffer, mode_, crtc_states_[i]);
129 void HardwareDisplayController::Disable() {
130 TRACE_EVENT0("dri", "HDC::Disable");
131 pending_page_flips_ = 0;
132 for (size_t i = 0; i < crtc_states_.size(); ++i) {
133 drm_->DisableCrtc(crtc_states_[i]->crtc());
134 crtc_states_[i]->set_is_disabled(true);
140 void HardwareDisplayController::QueueOverlayPlane(const OverlayPlane& plane) {
141 pending_planes_.push_back(plane);
144 bool HardwareDisplayController::SchedulePageFlip() {
145 DCHECK(!pending_planes_.empty());
146 DCHECK_EQ(0u, pending_page_flips_);
152 for (size_t i = 0; i < crtc_states_.size(); ++i)
153 status &= SchedulePageFlipOnCrtc(pending_planes_, crtc_states_[i]);
158 void HardwareDisplayController::WaitForPageFlipEvent() {
159 TRACE_EVENT1("dri", "HDC::WaitForPageFlipEvent",
160 "pending_pageflips", pending_page_flips_);
162 bool has_pending_page_flips = pending_page_flips_ != 0;
163 drmEventContext drm_event;
164 drm_event.version = DRM_EVENT_CONTEXT_VERSION;
165 drm_event.page_flip_handler = HandlePageFlipEvent;
166 drm_event.vblank_handler = NULL;
168 // Wait for the page-flips to complete.
169 while (pending_page_flips_ > 0)
170 drm_->HandleEvent(drm_event);
172 // In case there are no pending pageflips do not replace the current planes
173 // since they are still being used.
174 if (has_pending_page_flips)
175 current_planes_.swap(pending_planes_);
177 pending_planes_.clear();
180 void HardwareDisplayController::OnPageFlipEvent(unsigned int frame,
181 unsigned int seconds,
182 unsigned int useconds) {
183 TRACE_EVENT0("dri", "HDC::OnPageFlipEvent");
185 --pending_page_flips_;
187 static_cast<uint64_t>(seconds) * base::Time::kMicrosecondsPerSecond +
191 bool HardwareDisplayController::SetCursor(scoped_refptr<ScanoutBuffer> buffer) {
193 cursor_buffer_ = buffer;
198 for (size_t i = 0; i < crtc_states_.size(); ++i) {
199 status &= drm_->SetCursor(
200 crtc_states_[i]->crtc(), buffer->GetHandle(), buffer->GetSize());
206 bool HardwareDisplayController::UnsetCursor() {
208 cursor_buffer_ = NULL;
209 for (size_t i = 0; i < crtc_states_.size(); ++i)
210 status &= drm_->SetCursor(crtc_states_[i]->crtc(), 0, gfx::Size());
215 bool HardwareDisplayController::MoveCursor(const gfx::Point& location) {
220 for (size_t i = 0; i < crtc_states_.size(); ++i)
221 status &= drm_->MoveCursor(crtc_states_[i]->crtc(), location);
226 void HardwareDisplayController::AddCrtc(
227 scoped_ptr<CrtcState> state) {
228 crtc_states_.push_back(state.release());
231 scoped_ptr<CrtcState> HardwareDisplayController::RemoveCrtc(uint32_t crtc) {
232 for (ScopedVector<CrtcState>::iterator it = crtc_states_.begin();
233 it != crtc_states_.end();
235 if ((*it)->crtc() == crtc) {
236 scoped_ptr<CrtcState> controller(*it);
237 crtc_states_.weak_erase(it);
238 return controller.Pass();
242 return scoped_ptr<CrtcState>();
245 bool HardwareDisplayController::HasCrtc(uint32_t crtc) const {
246 for (size_t i = 0; i < crtc_states_.size(); ++i)
247 if (crtc_states_[i]->crtc() == crtc)
253 bool HardwareDisplayController::IsMirrored() const {
254 return crtc_states_.size() > 1;
257 bool HardwareDisplayController::IsDisabled() const {
261 gfx::Size HardwareDisplayController::GetModeSize() const {
262 return gfx::Size(mode_.hdisplay, mode_.vdisplay);
265 bool HardwareDisplayController::ModesetCrtc(
266 const scoped_refptr<ScanoutBuffer>& buffer,
267 drmModeModeInfo mode,
269 if (!drm_->SetCrtc(state->crtc(),
270 buffer->GetFramebufferId(),
271 std::vector<uint32_t>(1, state->connector()),
273 LOG(ERROR) << "Failed to modeset: error='" << strerror(errno)
274 << "' crtc=" << state->crtc()
275 << " connector=" << state->connector()
276 << " framebuffer_id=" << buffer->GetFramebufferId()
277 << " mode=" << mode.hdisplay << "x" << mode.vdisplay << "@"
285 bool HardwareDisplayController::SchedulePageFlipOnCrtc(
286 const OverlayPlaneList& overlays,
288 const OverlayPlane& primary = GetPrimaryPlane(overlays);
289 DCHECK(primary.buffer.get());
291 if (primary.buffer->GetSize() != gfx::Size(mode_.hdisplay, mode_.vdisplay)) {
292 LOG(WARNING) << "Trying to pageflip a buffer with the wrong size. Expected "
293 << mode_.hdisplay << "x" << mode_.vdisplay
294 << " got " << primary.buffer->GetSize().ToString() << " for"
295 << " crtc=" << state->crtc()
296 << " connector=" << state->connector();
300 if (!drm_->PageFlip(state->crtc(),
301 primary.buffer->GetFramebufferId(),
303 LOG(ERROR) << "Cannot page flip: error='" << strerror(errno) << "'"
304 << " crtc=" << state->crtc()
305 << " framebuffer=" << primary.buffer->GetFramebufferId()
306 << " size=" << primary.buffer->GetSize().ToString();
310 ++pending_page_flips_;
312 for (size_t i = 0; i < overlays.size(); i++) {
313 const OverlayPlane& plane = overlays[i];
314 if (!plane.overlay_plane)
317 const gfx::Size& size = plane.buffer->GetSize();
318 gfx::RectF crop_rect = plane.crop_rect;
319 crop_rect.Scale(size.width(), size.height());
320 if (!drm_->PageFlipOverlay(state->crtc(),
321 plane.buffer->GetFramebufferId(),
322 plane.display_bounds,
324 plane.overlay_plane)) {
325 LOG(ERROR) << "Cannot display on overlay: " << strerror(errno);