blob: ee4e4a94626adf892b35a605a2b22e1ee7a540d2 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/ozone/platform/dri/hardware_display_controller.h"
#include <drm.h>
#include <errno.h>
#include <string.h>
#include <xf86drm.h>
#include "base/basictypes.h"
#include "base/debug/trace_event.h"
#include "base/logging.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/size.h"
#include "ui/ozone/platform/dri/crtc_controller.h"
#include "ui/ozone/platform/dri/dri_buffer.h"
#include "ui/ozone/platform/dri/dri_wrapper.h"
#include "ui/ozone/public/native_pixmap.h"
namespace ui {
namespace {
// DRM callback on page flip events. This callback is triggered after the
// page flip has happened and the backbuffer is now the new frontbuffer
// The old frontbuffer is no longer used by the hardware and can be used for
// future draw operations.
//
// |device| will contain a reference to the |ScanoutSurface| object which
// the event belongs to.
//
// TODO(dnicoara) When we have a FD handler for the DRM calls in the message
// loop, we can move this function in the handler.
void HandlePageFlipEvent(int fd,
unsigned int frame,
unsigned int seconds,
unsigned int useconds,
void* controller) {
static_cast<CrtcController*>(controller)
->OnPageFlipEvent(frame, seconds, useconds);
}
} // namespace
HardwareDisplayController::HardwareDisplayController(
scoped_ptr<CrtcController> controller)
: is_disabled_(true) {
crtc_controllers_.push_back(controller.release());
}
HardwareDisplayController::~HardwareDisplayController() {
// Reset the cursor.
UnsetCursor();
}
bool HardwareDisplayController::Modeset(const OverlayPlane& primary,
drmModeModeInfo mode) {
TRACE_EVENT0("dri", "HDC::Modeset");
DCHECK(primary.buffer.get());
bool status = true;
for (size_t i = 0; i < crtc_controllers_.size(); ++i)
status &= crtc_controllers_[i]->Modeset(primary, mode);
current_planes_ = std::vector<OverlayPlane>(1, primary);
pending_planes_.clear();
is_disabled_ = false;
mode_ = mode;
return status;
}
bool HardwareDisplayController::Enable() {
TRACE_EVENT0("dri", "HDC::Enable");
DCHECK(!current_planes_.empty());
OverlayPlane primary = OverlayPlane::GetPrimaryPlane(current_planes_);
DCHECK(primary.buffer.get());
bool status = true;
for (size_t i = 0; i < crtc_controllers_.size(); ++i)
status &= crtc_controllers_[i]->Modeset(primary, mode_);
return status;
}
void HardwareDisplayController::Disable() {
TRACE_EVENT0("dri", "HDC::Disable");
for (size_t i = 0; i < crtc_controllers_.size(); ++i)
crtc_controllers_[i]->Disable();
is_disabled_ = true;
}
void HardwareDisplayController::QueueOverlayPlane(const OverlayPlane& plane) {
pending_planes_.push_back(plane);
}
bool HardwareDisplayController::SchedulePageFlip() {
DCHECK(!pending_planes_.empty());
if (is_disabled_)
return true;
bool status = true;
for (size_t i = 0; i < crtc_controllers_.size(); ++i)
status &= crtc_controllers_[i]->SchedulePageFlip(pending_planes_);
return status;
}
void HardwareDisplayController::WaitForPageFlipEvent() {
TRACE_EVENT0("dri", "HDC::WaitForPageFlipEvent");
drmEventContext drm_event;
drm_event.version = DRM_EVENT_CONTEXT_VERSION;
drm_event.page_flip_handler = HandlePageFlipEvent;
drm_event.vblank_handler = NULL;
bool has_pending_page_flips = false;
// Wait for the page-flips to complete.
for (size_t i = 0; i < crtc_controllers_.size(); ++i) {
// In mirror mode the page flip callbacks can happen in different order than
// scheduled, so we need to make sure that the event for the current CRTC is
// processed before moving to the next CRTC.
while (crtc_controllers_[i]->page_flip_pending()) {
has_pending_page_flips = true;
crtc_controllers_[i]->drm()->HandleEvent(drm_event);
}
}
// In case there are no pending pageflips do not replace the current planes
// since they are still being used.
if (has_pending_page_flips)
current_planes_.swap(pending_planes_);
pending_planes_.clear();
}
bool HardwareDisplayController::SetCursor(
const scoped_refptr<ScanoutBuffer>& buffer) {
bool status = true;
if (is_disabled_)
return true;
for (size_t i = 0; i < crtc_controllers_.size(); ++i)
status &= crtc_controllers_[i]->SetCursor(buffer);
return status;
}
bool HardwareDisplayController::UnsetCursor() {
bool status = true;
for (size_t i = 0; i < crtc_controllers_.size(); ++i)
status &= crtc_controllers_[i]->UnsetCursor();
return status;
}
bool HardwareDisplayController::MoveCursor(const gfx::Point& location) {
if (is_disabled_)
return true;
bool status = true;
for (size_t i = 0; i < crtc_controllers_.size(); ++i)
status &= crtc_controllers_[i]->MoveCursor(location);
return status;
}
void HardwareDisplayController::AddCrtc(scoped_ptr<CrtcController> controller) {
crtc_controllers_.push_back(controller.release());
}
scoped_ptr<CrtcController> HardwareDisplayController::RemoveCrtc(
uint32_t crtc) {
for (ScopedVector<CrtcController>::iterator it = crtc_controllers_.begin();
it != crtc_controllers_.end();
++it) {
if ((*it)->crtc() == crtc) {
scoped_ptr<CrtcController> controller(*it);
crtc_controllers_.weak_erase(it);
return controller.Pass();
}
}
return scoped_ptr<CrtcController>();
}
bool HardwareDisplayController::HasCrtc(uint32_t crtc) const {
for (size_t i = 0; i < crtc_controllers_.size(); ++i)
if (crtc_controllers_[i]->crtc() == crtc)
return true;
return false;
}
bool HardwareDisplayController::IsMirrored() const {
return crtc_controllers_.size() > 1;
}
bool HardwareDisplayController::IsDisabled() const {
return is_disabled_;
}
gfx::Size HardwareDisplayController::GetModeSize() const {
return gfx::Size(mode_.hdisplay, mode_.vdisplay);
}
uint64_t HardwareDisplayController::GetTimeOfLastFlip() const {
uint64_t time = 0;
for (size_t i = 0; i < crtc_controllers_.size(); ++i)
if (time < crtc_controllers_[i]->time_of_last_flip())
time = crtc_controllers_[i]->time_of_last_flip();
return time;
}
} // namespace ui