OLD | NEW |
---|---|
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "ui/ozone/platform/dri/hardware_display_controller.h" | 5 #include "ui/ozone/platform/dri/hardware_display_controller.h" |
6 | 6 |
7 #include <drm.h> | 7 #include <drm.h> |
8 #include <errno.h> | 8 #include <errno.h> |
9 #include <string.h> | 9 #include <string.h> |
10 #include <xf86drm.h> | 10 #include <xf86drm.h> |
11 | 11 |
12 #include "base/basictypes.h" | 12 #include "base/basictypes.h" |
13 #include "base/debug/trace_event.h" | 13 #include "base/debug/trace_event.h" |
14 #include "base/logging.h" | 14 #include "base/logging.h" |
15 #include "third_party/skia/include/core/SkCanvas.h" | 15 #include "third_party/skia/include/core/SkCanvas.h" |
16 #include "ui/gfx/geometry/point.h" | 16 #include "ui/gfx/geometry/point.h" |
17 #include "ui/gfx/geometry/size.h" | 17 #include "ui/gfx/geometry/size.h" |
18 #include "ui/ozone/platform/dri/crtc_controller.h" | 18 #include "ui/ozone/platform/dri/crtc_controller.h" |
19 #include "ui/ozone/platform/dri/dri_buffer.h" | 19 #include "ui/ozone/platform/dri/dri_buffer.h" |
20 #include "ui/ozone/platform/dri/dri_wrapper.h" | 20 #include "ui/ozone/platform/dri/dri_wrapper.h" |
21 #include "ui/ozone/public/native_pixmap.h" | 21 #include "ui/ozone/public/native_pixmap.h" |
22 | 22 |
23 namespace ui { | 23 namespace ui { |
24 | 24 |
25 namespace { | 25 HardwareDisplayController::PageFlipRequest::PageFlipRequest( |
26 | 26 const OverlayPlaneList& planes, |
27 // DRM callback on page flip events. This callback is triggered after the | 27 const base::Closure& callback) |
28 // page flip has happened and the backbuffer is now the new frontbuffer | 28 : planes(planes), callback(callback) { |
29 // The old frontbuffer is no longer used by the hardware and can be used for | |
30 // future draw operations. | |
31 // | |
32 // |device| will contain a reference to the |ScanoutSurface| object which | |
33 // the event belongs to. | |
34 // | |
35 // TODO(dnicoara) When we have a FD handler for the DRM calls in the message | |
36 // loop, we can move this function in the handler. | |
37 void HandlePageFlipEvent(int fd, | |
38 unsigned int frame, | |
39 unsigned int seconds, | |
40 unsigned int useconds, | |
41 void* controller) { | |
42 static_cast<CrtcController*>(controller) | |
43 ->OnPageFlipEvent(frame, seconds, useconds); | |
44 } | 29 } |
45 | 30 |
46 } // namespace | 31 HardwareDisplayController::PageFlipRequest::~PageFlipRequest() { |
32 } | |
47 | 33 |
48 HardwareDisplayController::HardwareDisplayController( | 34 HardwareDisplayController::HardwareDisplayController( |
49 scoped_ptr<CrtcController> controller) | 35 scoped_ptr<CrtcController> controller) |
50 : is_disabled_(true) { | 36 : is_disabled_(true) { |
51 memset(&mode_, 0, sizeof(mode_)); | 37 memset(&mode_, 0, sizeof(mode_)); |
52 AddCrtc(controller.Pass()); | 38 AddCrtc(controller.Pass()); |
53 } | 39 } |
54 | 40 |
55 HardwareDisplayController::~HardwareDisplayController() { | 41 HardwareDisplayController::~HardwareDisplayController() { |
56 // Reset the cursor. | 42 // Reset the cursor. |
57 UnsetCursor(); | 43 UnsetCursor(); |
44 ClearPendingRequests(); | |
58 } | 45 } |
59 | 46 |
60 bool HardwareDisplayController::Modeset(const OverlayPlane& primary, | 47 bool HardwareDisplayController::Modeset(const OverlayPlane& primary, |
61 drmModeModeInfo mode) { | 48 drmModeModeInfo mode) { |
62 TRACE_EVENT0("dri", "HDC::Modeset"); | 49 TRACE_EVENT0("dri", "HDC::Modeset"); |
63 DCHECK(primary.buffer.get()); | 50 DCHECK(primary.buffer.get()); |
64 bool status = true; | 51 bool status = true; |
65 for (size_t i = 0; i < crtc_controllers_.size(); ++i) | 52 for (size_t i = 0; i < crtc_controllers_.size(); ++i) |
66 status &= crtc_controllers_[i]->Modeset(primary, mode); | 53 status &= crtc_controllers_[i]->Modeset(primary, mode); |
67 | 54 |
55 is_disabled_ = false; | |
56 mode_ = mode; | |
57 | |
68 current_planes_ = std::vector<OverlayPlane>(1, primary); | 58 current_planes_ = std::vector<OverlayPlane>(1, primary); |
69 pending_planes_.clear(); | 59 pending_planes_.clear(); |
70 is_disabled_ = false; | 60 ClearPendingRequests(); |
71 mode_ = mode; | 61 |
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()) | |
65 requests_.push_back( | |
66 PageFlipRequest(current_planes_, base::Bind(&base::DoNothing))); | |
67 | |
72 return status; | 68 return status; |
73 } | 69 } |
74 | 70 |
75 bool HardwareDisplayController::Enable() { | 71 bool HardwareDisplayController::Enable() { |
76 TRACE_EVENT0("dri", "HDC::Enable"); | 72 TRACE_EVENT0("dri", "HDC::Enable"); |
77 DCHECK(!current_planes_.empty()); | 73 DCHECK(!current_planes_.empty()); |
78 const OverlayPlane* primary = OverlayPlane::GetPrimaryPlane(current_planes_); | 74 const OverlayPlane* primary = OverlayPlane::GetPrimaryPlane(current_planes_); |
79 | 75 |
80 return Modeset(*primary, mode_); | 76 return Modeset(*primary, mode_); |
81 } | 77 } |
82 | 78 |
83 void HardwareDisplayController::Disable() { | 79 void HardwareDisplayController::Disable() { |
84 TRACE_EVENT0("dri", "HDC::Disable"); | 80 TRACE_EVENT0("dri", "HDC::Disable"); |
85 for (size_t i = 0; i < crtc_controllers_.size(); ++i) | 81 for (size_t i = 0; i < crtc_controllers_.size(); ++i) |
86 crtc_controllers_[i]->Disable(); | 82 crtc_controllers_[i]->Disable(); |
87 | 83 |
88 is_disabled_ = true; | 84 is_disabled_ = true; |
89 } | 85 } |
90 | 86 |
91 void HardwareDisplayController::QueueOverlayPlane(const OverlayPlane& plane) { | 87 void HardwareDisplayController::QueueOverlayPlane(const OverlayPlane& plane) { |
92 pending_planes_.push_back(plane); | 88 pending_planes_.push_back(plane); |
93 } | 89 } |
94 | 90 |
95 bool HardwareDisplayController::SchedulePageFlip() { | 91 bool HardwareDisplayController::SchedulePageFlip( |
96 DCHECK(!pending_planes_.empty()); | 92 const base::Closure& callback) { |
93 TRACE_EVENT0("dri", "HDC::SchedulePageFlip"); | |
97 | 94 |
98 if (is_disabled_) | 95 // Ignore requests with no planes to schedule. |
96 if (pending_planes_.empty()) { | |
97 callback.Run(); | |
98 return true; | |
99 } | |
100 | |
101 requests_.push_back(PageFlipRequest(pending_planes_, callback)); | |
102 pending_planes_.clear(); | |
103 | |
104 // A request is being serviced right now. | |
105 if (HasPendingPageFlips()) | |
99 return true; | 106 return true; |
100 | 107 |
101 std::sort(pending_planes_.begin(), pending_planes_.end(), | 108 bool status = ActualSchedulePageFlip(); |
102 [](const OverlayPlane& l, const OverlayPlane& r) { | |
103 return l.z_order < r.z_order; | |
104 }); | |
105 | 109 |
106 bool status = true; | 110 // No page flip event on failure so discard failed request. |
107 for (size_t i = 0; i < crtc_controllers_.size(); ++i) { | 111 if (!status) |
108 status &= crtc_controllers_[i]->SchedulePageFlip( | 112 requests_.pop_front(); |
109 owned_hardware_planes_.get(crtc_controllers_[i]->drm()), | |
110 pending_planes_); | |
111 } | |
112 | |
113 for (const auto& planes : owned_hardware_planes_) { | |
114 if (!planes.first->plane_manager()->Commit(planes.second)) { | |
115 status = false; | |
116 } | |
117 } | |
118 | 113 |
119 return status; | 114 return status; |
120 } | 115 } |
121 | 116 |
122 void HardwareDisplayController::WaitForPageFlipEvent() { | |
123 TRACE_EVENT0("dri", "HDC::WaitForPageFlipEvent"); | |
124 | |
125 drmEventContext drm_event; | |
126 drm_event.version = DRM_EVENT_CONTEXT_VERSION; | |
127 drm_event.page_flip_handler = HandlePageFlipEvent; | |
128 drm_event.vblank_handler = NULL; | |
129 | |
130 bool has_pending_page_flips = false; | |
131 // Wait for the page-flips to complete. | |
132 for (size_t i = 0; i < crtc_controllers_.size(); ++i) { | |
133 // In mirror mode the page flip callbacks can happen in different order than | |
134 // scheduled, so we need to make sure that the event for the current CRTC is | |
135 // processed before moving to the next CRTC. | |
136 while (crtc_controllers_[i]->page_flip_pending()) { | |
137 has_pending_page_flips = true; | |
138 crtc_controllers_[i]->drm()->HandleEvent(drm_event); | |
139 } | |
140 } | |
141 | |
142 // In case there are no pending pageflips do not replace the current planes | |
143 // since they are still being used. | |
144 if (has_pending_page_flips) | |
145 current_planes_.swap(pending_planes_); | |
146 | |
147 pending_planes_.clear(); | |
148 } | |
149 | |
150 bool HardwareDisplayController::SetCursor( | 117 bool HardwareDisplayController::SetCursor( |
151 const scoped_refptr<ScanoutBuffer>& buffer) { | 118 const scoped_refptr<ScanoutBuffer>& buffer) { |
152 bool status = true; | 119 bool status = true; |
153 | 120 |
154 if (is_disabled_) | 121 if (is_disabled_) |
155 return true; | 122 return true; |
156 | 123 |
157 for (size_t i = 0; i < crtc_controllers_.size(); ++i) | 124 for (size_t i = 0; i < crtc_controllers_.size(); ++i) |
158 status &= crtc_controllers_[i]->SetCursor(buffer); | 125 status &= crtc_controllers_[i]->SetCursor(buffer); |
159 | 126 |
(...skipping 16 matching lines...) Expand all Loading... | |
176 for (size_t i = 0; i < crtc_controllers_.size(); ++i) | 143 for (size_t i = 0; i < crtc_controllers_.size(); ++i) |
177 status &= crtc_controllers_[i]->MoveCursor(location); | 144 status &= crtc_controllers_[i]->MoveCursor(location); |
178 | 145 |
179 return status; | 146 return status; |
180 } | 147 } |
181 | 148 |
182 void HardwareDisplayController::AddCrtc(scoped_ptr<CrtcController> controller) { | 149 void HardwareDisplayController::AddCrtc(scoped_ptr<CrtcController> controller) { |
183 owned_hardware_planes_.add( | 150 owned_hardware_planes_.add( |
184 controller->drm(), | 151 controller->drm(), |
185 scoped_ptr<HardwareDisplayPlaneList>(new HardwareDisplayPlaneList())); | 152 scoped_ptr<HardwareDisplayPlaneList>(new HardwareDisplayPlaneList())); |
153 controller->AddObserver(this); | |
186 crtc_controllers_.push_back(controller.release()); | 154 crtc_controllers_.push_back(controller.release()); |
187 } | 155 } |
188 | 156 |
189 scoped_ptr<CrtcController> HardwareDisplayController::RemoveCrtc( | 157 scoped_ptr<CrtcController> HardwareDisplayController::RemoveCrtc( |
190 uint32_t crtc) { | 158 uint32_t crtc) { |
191 for (ScopedVector<CrtcController>::iterator it = crtc_controllers_.begin(); | 159 for (ScopedVector<CrtcController>::iterator it = crtc_controllers_.begin(); |
192 it != crtc_controllers_.end(); ++it) { | 160 it != crtc_controllers_.end(); ++it) { |
193 if ((*it)->crtc() == crtc) { | 161 if ((*it)->crtc() == crtc) { |
194 scoped_ptr<CrtcController> controller(*it); | 162 scoped_ptr<CrtcController> controller(*it); |
195 crtc_controllers_.weak_erase(it); | 163 crtc_controllers_.weak_erase(it); |
196 // Release any planes this crtc might own. | |
197 HardwareDisplayPlaneManager::ResetPlanes( | |
198 owned_hardware_planes_.find(controller->drm())->second, crtc); | |
199 // Remove entry from |owned_hardware_planes_| iff no other crtcs share it. | 164 // Remove entry from |owned_hardware_planes_| iff no other crtcs share it. |
200 bool found = false; | 165 bool found = false; |
201 for (ScopedVector<CrtcController>::iterator it = | 166 for (ScopedVector<CrtcController>::iterator it = |
202 crtc_controllers_.begin(); | 167 crtc_controllers_.begin(); |
203 it != crtc_controllers_.end(); ++it) { | 168 it != crtc_controllers_.end(); ++it) { |
204 if ((*it)->drm() == controller->drm()) { | 169 if ((*it)->drm() == controller->drm()) { |
205 found = true; | 170 found = true; |
206 break; | 171 break; |
207 } | 172 } |
208 } | 173 } |
209 if (!found) | 174 if (!found) |
210 owned_hardware_planes_.erase(controller->drm()); | 175 owned_hardware_planes_.erase(controller->drm()); |
176 | |
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()) | |
182 OnPageFlipEvent(); | |
183 | |
211 return controller.Pass(); | 184 return controller.Pass(); |
212 } | 185 } |
213 } | 186 } |
214 | 187 |
215 return scoped_ptr<CrtcController>(); | 188 return scoped_ptr<CrtcController>(); |
216 } | 189 } |
217 | 190 |
218 bool HardwareDisplayController::HasCrtc(uint32_t crtc) const { | 191 bool HardwareDisplayController::HasCrtc(uint32_t crtc) const { |
219 for (size_t i = 0; i < crtc_controllers_.size(); ++i) | 192 for (size_t i = 0; i < crtc_controllers_.size(); ++i) |
220 if (crtc_controllers_[i]->crtc() == crtc) | 193 if (crtc_controllers_[i]->crtc() == crtc) |
(...skipping 16 matching lines...) Expand all Loading... | |
237 | 210 |
238 uint64_t HardwareDisplayController::GetTimeOfLastFlip() const { | 211 uint64_t HardwareDisplayController::GetTimeOfLastFlip() const { |
239 uint64_t time = 0; | 212 uint64_t time = 0; |
240 for (size_t i = 0; i < crtc_controllers_.size(); ++i) | 213 for (size_t i = 0; i < crtc_controllers_.size(); ++i) |
241 if (time < crtc_controllers_[i]->time_of_last_flip()) | 214 if (time < crtc_controllers_[i]->time_of_last_flip()) |
242 time = crtc_controllers_[i]->time_of_last_flip(); | 215 time = crtc_controllers_[i]->time_of_last_flip(); |
243 | 216 |
244 return time; | 217 return time; |
245 } | 218 } |
246 | 219 |
220 void HardwareDisplayController::OnPageFlipEvent() { | |
221 TRACE_EVENT0("dri", "HDC::OnPageFlipEvent"); | |
222 if (HasPendingPageFlips()) | |
223 return; | |
224 | |
225 // If a CRTC was moved between HDCs while page-flipping, it will report a page | |
226 // flip without any scheduled requests. | |
227 if (!requests_.empty()) | |
228 ProcessPageFlipRequest(); | |
229 | |
230 if (requests_.empty()) | |
231 return; | |
232 | |
233 bool status = ActualSchedulePageFlip(); | |
234 if (!status) { | |
235 PageFlipRequest request = requests_.front(); | |
236 requests_.pop_front(); | |
237 | |
238 // Normally the caller would handle the error call, but because we're in a | |
239 // delayed schedule the initial SchedulePageFlip() already returned true, | |
240 // this we need to run the callback. | |
241 request.callback.Run(); | |
242 } | |
243 } | |
244 | |
245 bool HardwareDisplayController::HasPendingPageFlips() const { | |
246 for (size_t i = 0; i < crtc_controllers_.size(); ++i) | |
247 if (crtc_controllers_[i]->page_flip_pending()) | |
248 return true; | |
249 | |
250 return false; | |
251 } | |
252 | |
253 bool HardwareDisplayController::ActualSchedulePageFlip() { | |
254 TRACE_EVENT0("dri", "HDC::ActualSchedulePageFlip"); | |
255 DCHECK(!requests_.empty()); | |
256 | |
257 if (is_disabled_) { | |
258 ProcessPageFlipRequest(); | |
259 return true; | |
260 } | |
261 | |
262 OverlayPlaneList pending_planes = requests_.front().planes; | |
263 std::sort(pending_planes.begin(), pending_planes.end(), | |
264 [](const OverlayPlane& l, const OverlayPlane& r) { | |
265 return l.z_order < r.z_order; | |
266 }); | |
267 | |
268 bool status = true; | |
269 for (size_t i = 0; i < crtc_controllers_.size(); ++i) { | |
270 status &= crtc_controllers_[i]->SchedulePageFlip( | |
271 owned_hardware_planes_.get(crtc_controllers_[i]->drm()), | |
272 pending_planes); | |
273 } | |
274 | |
275 for (const auto& planes : owned_hardware_planes_) { | |
276 if (!planes.first->plane_manager()->Commit(planes.second)) { | |
277 status = false; | |
278 } | |
279 } | |
280 | |
281 return status; | |
282 } | |
283 | |
284 void HardwareDisplayController::ProcessPageFlipRequest() { | |
285 DCHECK(!requests_.empty()); | |
286 PageFlipRequest request = requests_.front(); | |
alexst (slow to review)
2015/01/09 16:06:58
nit: PageFlipRequest& request = requests_.front();
dnicoara
2015/01/09 16:08:40
We do need to copy it since we're popping the elem
alexst (slow to review)
2015/01/09 16:10:52
Acknowledged.
| |
287 requests_.pop_front(); | |
288 | |
289 current_planes_.swap(request.planes); | |
290 request.callback.Run(); | |
291 } | |
292 | |
293 void HardwareDisplayController::ClearPendingRequests() { | |
294 while (!requests_.empty()) { | |
295 PageFlipRequest request = requests_.front(); | |
296 requests_.pop_front(); | |
297 request.callback.Run(); | |
298 } | |
299 } | |
300 | |
247 } // namespace ui | 301 } // namespace ui |
OLD | NEW |