| 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 275467db9ea95903a584fd3aca813dfa5f42e0f4..375cc82b77c171d095316f5ddaeafd8bb912eddd 100644
|
| --- a/ui/ozone/platform/dri/hardware_display_controller.cc
|
| +++ b/ui/ozone/platform/dri/hardware_display_controller.cc
|
| @@ -22,28 +22,14 @@
|
|
|
| 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::PageFlipRequest::PageFlipRequest(
|
| + const OverlayPlaneList& planes,
|
| + const base::Closure& callback)
|
| + : planes(planes), callback(callback) {
|
| +}
|
| +
|
| +HardwareDisplayController::PageFlipRequest::~PageFlipRequest() {
|
| +}
|
|
|
| HardwareDisplayController::HardwareDisplayController(
|
| scoped_ptr<CrtcController> controller)
|
| @@ -55,6 +41,7 @@ HardwareDisplayController::HardwareDisplayController(
|
| HardwareDisplayController::~HardwareDisplayController() {
|
| // Reset the cursor.
|
| UnsetCursor();
|
| + ClearPendingRequests();
|
| }
|
|
|
| bool HardwareDisplayController::Modeset(const OverlayPlane& primary,
|
| @@ -65,10 +52,19 @@ 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();
|
| + ClearPendingRequests();
|
| +
|
| + // 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())
|
| + requests_.push_back(
|
| + PageFlipRequest(current_planes_, base::Bind(&base::DoNothing)));
|
| +
|
| return status;
|
| }
|
|
|
| @@ -92,59 +88,30 @@ void HardwareDisplayController::QueueOverlayPlane(const OverlayPlane& plane) {
|
| pending_planes_.push_back(plane);
|
| }
|
|
|
| -bool HardwareDisplayController::SchedulePageFlip() {
|
| - DCHECK(!pending_planes_.empty());
|
| +bool HardwareDisplayController::SchedulePageFlip(
|
| + const base::Closure& callback) {
|
| + TRACE_EVENT0("dri", "HDC::SchedulePageFlip");
|
|
|
| - if (is_disabled_)
|
| + // Ignore requests with no planes to schedule.
|
| + if (pending_planes_.empty()) {
|
| + callback.Run();
|
| 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;
|
| - }
|
| }
|
|
|
| - return status;
|
| -}
|
| -
|
| -void HardwareDisplayController::WaitForPageFlipEvent() {
|
| - TRACE_EVENT0("dri", "HDC::WaitForPageFlipEvent");
|
| + requests_.push_back(PageFlipRequest(pending_planes_, callback));
|
| + pending_planes_.clear();
|
|
|
| - drmEventContext drm_event;
|
| - drm_event.version = DRM_EVENT_CONTEXT_VERSION;
|
| - drm_event.page_flip_handler = HandlePageFlipEvent;
|
| - drm_event.vblank_handler = NULL;
|
| + // A request is being serviced right now.
|
| + if (HasPendingPageFlips())
|
| + return true;
|
|
|
| - 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);
|
| - }
|
| - }
|
| + bool status = ActualSchedulePageFlip();
|
|
|
| - // 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_);
|
| + // No page flip event on failure so discard failed request.
|
| + if (!status)
|
| + requests_.pop_front();
|
|
|
| - pending_planes_.clear();
|
| + return status;
|
| }
|
|
|
| bool HardwareDisplayController::SetCursor(
|
| @@ -183,6 +150,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());
|
| }
|
|
|
| @@ -193,9 +161,6 @@ scoped_ptr<CrtcController> HardwareDisplayController::RemoveCrtc(
|
| if ((*it)->crtc() == crtc) {
|
| scoped_ptr<CrtcController> controller(*it);
|
| crtc_controllers_.weak_erase(it);
|
| - // Release any planes this crtc might own.
|
| - HardwareDisplayPlaneManager::ResetPlanes(
|
| - owned_hardware_planes_.find(controller->drm())->second, crtc);
|
| // Remove entry from |owned_hardware_planes_| iff no other crtcs share it.
|
| bool found = false;
|
| for (ScopedVector<CrtcController>::iterator it =
|
| @@ -208,6 +173,14 @@ scoped_ptr<CrtcController> HardwareDisplayController::RemoveCrtc(
|
| }
|
| if (!found)
|
| owned_hardware_planes_.erase(controller->drm());
|
| +
|
| + controller->RemoveObserver(this);
|
| + // 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.
|
| + if (controller->page_flip_pending())
|
| + OnPageFlipEvent();
|
| +
|
| return controller.Pass();
|
| }
|
| }
|
| @@ -244,4 +217,98 @@ uint64_t HardwareDisplayController::GetTimeOfLastFlip() const {
|
| return time;
|
| }
|
|
|
| +void HardwareDisplayController::OnPageFlipEvent() {
|
| + TRACE_EVENT0("dri", "HDC::OnPageFlipEvent");
|
| + // OnPageFlipEvent() needs to handle 2 cases:
|
| + // 1) Normal page flips in which case:
|
| + // a) HasPendingPageFlips() may return false if we're in mirror mode and
|
| + // one of the CRTCs hasn't finished page flipping. In this case we want
|
| + // to wait for all the CRTCs.
|
| + // b) HasPendingPageFlips() returns true in which case all CRTCs are ready
|
| + // for the next request. In this case we expect that |requests_| isn't
|
| + // empty.
|
| + // 2) A CRTC was added while it was page flipping. In this case a modeset
|
| + // must be performed. Modesetting clears all pending requests, however the
|
| + // CRTCs will honor the scheduled page flip. Thus we need to handle page
|
| + // flip events with no requests.
|
| +
|
| + if (HasPendingPageFlips())
|
| + return;
|
| +
|
| + if (!requests_.empty())
|
| + ProcessPageFlipRequest();
|
| +
|
| + // ProcessPageFlipRequest() consumes a request.
|
| + if (requests_.empty())
|
| + return;
|
| +
|
| + // At this point we still have requests pending, so schedule the next request.
|
| + bool status = ActualSchedulePageFlip();
|
| + if (!status) {
|
| + PageFlipRequest request = requests_.front();
|
| + requests_.pop_front();
|
| +
|
| + // Normally the caller would handle the error call, but because we're in a
|
| + // delayed schedule the initial SchedulePageFlip() already returned true,
|
| + // thus we need to run the callback.
|
| + request.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() {
|
| + TRACE_EVENT0("dri", "HDC::ActualSchedulePageFlip");
|
| + DCHECK(!requests_.empty());
|
| +
|
| + if (is_disabled_) {
|
| + ProcessPageFlipRequest();
|
| + return true;
|
| + }
|
| +
|
| + OverlayPlaneList pending_planes = requests_.front().planes;
|
| + 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;
|
| + }
|
| + }
|
| +
|
| + return status;
|
| +}
|
| +
|
| +void HardwareDisplayController::ProcessPageFlipRequest() {
|
| + DCHECK(!requests_.empty());
|
| + PageFlipRequest request = requests_.front();
|
| + requests_.pop_front();
|
| +
|
| + current_planes_.swap(request.planes);
|
| + request.callback.Run();
|
| +}
|
| +
|
| +void HardwareDisplayController::ClearPendingRequests() {
|
| + while (!requests_.empty()) {
|
| + PageFlipRequest request = requests_.front();
|
| + requests_.pop_front();
|
| + request.callback.Run();
|
| + }
|
| +}
|
| +
|
| } // namespace ui
|
|
|