| Index: ui/ozone/platform/dri/hardware_display_controller.cc
|
| diff --git a/ui/ozone/platform/dri/hardware_display_controller.cc b/ui/ozone/platform/dri/hardware_display_controller.cc
|
| index 7ec4f0dcbc95c4e1f5cc0b4e7883a3fffd9b2db1..7f81355ac93eb4ba436e05cb961114d26bb6286a 100644
|
| --- a/ui/ozone/platform/dri/hardware_display_controller.cc
|
| +++ b/ui/ozone/platform/dri/hardware_display_controller.cc
|
| @@ -22,39 +22,19 @@
|
|
|
| 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) {
|
| memset(&mode_, 0, sizeof(mode_));
|
| + pending_planes_.push_back(OverlayPlaneList());
|
| AddCrtc(controller.Pass());
|
| }
|
|
|
| HardwareDisplayController::~HardwareDisplayController() {
|
| // Reset the cursor.
|
| UnsetCursor();
|
| + if (!page_flip_callback_.is_null())
|
| + page_flip_callback_.Run();
|
| }
|
|
|
| bool HardwareDisplayController::Modeset(const OverlayPlane& primary,
|
| @@ -65,10 +45,25 @@ bool HardwareDisplayController::Modeset(const OverlayPlane& primary,
|
| 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;
|
| +
|
| + current_planes_ = std::vector<OverlayPlane>(1, primary);
|
| + pending_planes_.clear();
|
| +
|
| + while (!callbacks_.empty()) {
|
| + callbacks_.front().Run();
|
| + callbacks_.pop_front();
|
| + }
|
| +
|
| + // Because a page flip is pending we need to leave some state for the
|
| + // callback. We use the modeset state since it is the only valid state.
|
| + if (HasPendingPageFlips())
|
| + pending_planes_.push_back(current_planes_);
|
| +
|
| + // Need to insert an empty list to accomodate future requests.
|
| + pending_planes_.push_back(OverlayPlaneList());
|
| +
|
| return status;
|
| }
|
|
|
| @@ -89,64 +84,28 @@ void HardwareDisplayController::Disable() {
|
| }
|
|
|
| void HardwareDisplayController::QueueOverlayPlane(const OverlayPlane& plane) {
|
| - pending_planes_.push_back(plane);
|
| + pending_planes_.back().push_back(plane);
|
| }
|
|
|
| -bool HardwareDisplayController::SchedulePageFlip() {
|
| - DCHECK(!pending_planes_.empty());
|
| +bool HardwareDisplayController::SchedulePageFlip(
|
| + const base::Closure& callback) {
|
| + TRACE_EVENT1("dri", "HDC::SchedulePageFlip", "this", this);
|
|
|
| - if (is_disabled_)
|
| + if (pending_planes_.size() > 1) {
|
| + callbacks_.push_back(callback);
|
| return true;
|
| -
|
| - std::sort(pending_planes_.begin(), pending_planes_.end(),
|
| - [](const OverlayPlane& l, const OverlayPlane& r) {
|
| - return l.z_order < r.z_order;
|
| - });
|
| -
|
| - bool status = true;
|
| - for (size_t i = 0; i < crtc_controllers_.size(); ++i) {
|
| - status &= crtc_controllers_[i]->SchedulePageFlip(
|
| - owned_hardware_planes_.get(crtc_controllers_[i]->drm()),
|
| - pending_planes_);
|
| }
|
|
|
| - for (const auto& planes : owned_hardware_planes_) {
|
| - if (!planes.first->plane_manager()->Commit(planes.second)) {
|
| - status = false;
|
| - }
|
| + bool status =
|
| + ActualSchedulePageFlip(pending_planes_.front(), callbacks_.front());
|
| + if (!status) {
|
| + pending_planes_.pop_front();
|
| + callbacks_.pop_front();
|
| }
|
|
|
| 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;
|
| @@ -183,6 +142,7 @@ void HardwareDisplayController::AddCrtc(scoped_ptr<CrtcController> controller) {
|
| owned_hardware_planes_.add(
|
| controller->drm(),
|
| scoped_ptr<HardwareDisplayPlaneList>(new HardwareDisplayPlaneList()));
|
| + controller->AddObserver(this);
|
| crtc_controllers_.push_back(controller.release());
|
| }
|
|
|
| @@ -192,6 +152,7 @@ scoped_ptr<CrtcController> HardwareDisplayController::RemoveCrtc(
|
| it != crtc_controllers_.end(); ++it) {
|
| if ((*it)->crtc() == crtc) {
|
| scoped_ptr<CrtcController> controller(*it);
|
| + controller->RemoveObserver(this);
|
| crtc_controllers_.weak_erase(it);
|
| // Remove entry from |owned_hardware_planes_| iff no other crtcs share it.
|
| bool found = false;
|
| @@ -205,6 +166,11 @@ scoped_ptr<CrtcController> HardwareDisplayController::RemoveCrtc(
|
| }
|
| if (!found)
|
| owned_hardware_planes_.erase(controller->drm());
|
| +
|
| + // If a display configuration happens mid page flip we want to make sure
|
| + // the HDC won't wait for an event from a CRTC that is no longer
|
| + // associated with it.
|
| + OnPageFlipEvent();
|
| return controller.Pass();
|
| }
|
| }
|
| @@ -241,4 +207,85 @@ uint64_t HardwareDisplayController::GetTimeOfLastFlip() const {
|
| return time;
|
| }
|
|
|
| +void HardwareDisplayController::OnPageFlipEvent() {
|
| + TRACE_EVENT1("dri", "HDC::OnPageFlipEvent", "this", this);
|
| + if (page_flip_callback_.is_null())
|
| + return;
|
| +
|
| + if (HasPendingPageFlips())
|
| + return;
|
| +
|
| + current_planes_.swap(pending_planes_.front());
|
| + pending_planes_.pop_front();
|
| +
|
| + // Modesetting can clear the callbacks.
|
| + if (!callbacks_.empty()) {
|
| + base::Closure callback = callbacks_.front();
|
| + callbacks_.pop_front();
|
| + callback.Run();
|
| + }
|
| +
|
| + // If we have pending requests immediately start the next one.
|
| + if (!pending_planes_.empty() && !pending_planes_.front().empty() &&
|
| + !callbacks_.empty()) {
|
| + base::Closure callback = callbacks_.front();
|
| + bool status = ActualSchedulePageFlip(pending_planes_.front(), callback);
|
| + if (!status) {
|
| + pending_planes_.pop_front();
|
| + callbacks_.pop_front();
|
| +
|
| + // Normally the caller would handle the error call, but because we're in a
|
| + // delayed schedule the initial SchedulePageFlip() already returned true,
|
| + // this we need to run the callback.
|
| + callback.Run();
|
| + }
|
| + }
|
| +}
|
| +
|
| +bool HardwareDisplayController::HasPendingPageFlips() const {
|
| + for (size_t i = 0; i < crtc_controllers_.size(); ++i)
|
| + if (crtc_controllers_[i]->page_flip_pending())
|
| + return true;
|
| +
|
| + return false;
|
| +}
|
| +
|
| +bool HardwareDisplayController::ActualSchedulePageFlip(
|
| + OverlayPlaneList pending_planes,
|
| + const base::Closure& callback) {
|
| + TRACE_EVENT0("dri", "HDC::ActualSchedulePageFlip");
|
| + if (is_disabled_) {
|
| + callback.Run();
|
| + return true;
|
| + }
|
| +
|
| + // Any overlays in the last list will be scheduled together. Insert an empty
|
| + // list for the next request.
|
| + if (!pending_planes_.back().empty())
|
| + pending_planes_.push_back(OverlayPlaneList());
|
| +
|
| + std::sort(pending_planes.begin(), pending_planes.end(),
|
| + [](const OverlayPlane& l, const OverlayPlane& r) {
|
| + return l.z_order < r.z_order;
|
| + });
|
| +
|
| + bool status = true;
|
| + for (size_t i = 0; i < crtc_controllers_.size(); ++i) {
|
| + status &= crtc_controllers_[i]->SchedulePageFlip(
|
| + owned_hardware_planes_.get(crtc_controllers_[i]->drm()),
|
| + pending_planes);
|
| + }
|
| +
|
| + for (const auto& planes : owned_hardware_planes_) {
|
| + if (!planes.first->plane_manager()->Commit(planes.second)) {
|
| + status = false;
|
| + }
|
| + }
|
| +
|
| + if (!status)
|
| + callback.Run();
|
| +
|
| + return status;
|
| +}
|
| +
|
| } // namespace ui
|
|
|