OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 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 "services/gfx/compositor/backend/gpu_output.h" | 5 #include "services/gfx/compositor/backend/gpu_output.h" |
6 | 6 |
7 #include <utility> | 7 #include <utility> |
8 | 8 |
9 #include "base/bind.h" | 9 #include "base/bind.h" |
10 #include "base/command_line.h" | 10 #include "base/command_line.h" |
11 #include "base/location.h" | 11 #include "base/location.h" |
12 #include "base/logging.h" | 12 #include "base/logging.h" |
13 #include "base/message_loop/message_loop.h" | 13 #include "base/message_loop/message_loop.h" |
14 #include "base/strings/string_number_conversions.h" | 14 #include "base/strings/string_number_conversions.h" |
15 #include "base/trace_event/trace_event.h" | 15 #include "base/trace_event/trace_event.h" |
16 #include "services/gfx/compositor/render/render_frame.h" | 16 #include "services/gfx/compositor/render/render_frame.h" |
17 | 17 |
18 namespace compositor { | 18 namespace compositor { |
19 namespace { | 19 namespace { |
20 constexpr const char* kPipelineDepthSwitch = "pipeline-depth"; | 20 constexpr const char* kPipelineDepthSwitch = "pipeline-depth"; |
21 constexpr uint32_t kDefaultPipelineDepth = 2u; // ideally should be 1 | 21 constexpr uint32_t kDefaultPipelineDepth = 1u; |
22 constexpr uint32_t kMinPipelineDepth = 1u; | 22 constexpr uint32_t kMinPipelineDepth = 1u; |
23 constexpr uint32_t kMaxPipelineDepth = 10u; | 23 constexpr uint32_t kMaxPipelineDepth = 10u; // for experimentation |
24 | 24 |
25 scoped_ptr<base::MessagePump> CreateMessagePumpMojo() { | 25 scoped_ptr<base::MessagePump> CreateMessagePumpMojo() { |
26 return base::MessageLoop::CreateMessagePumpForType( | 26 return base::MessageLoop::CreateMessagePumpForType( |
27 base::MessageLoop::TYPE_DEFAULT); | 27 base::MessageLoop::TYPE_DEFAULT); |
28 } | 28 } |
29 } // namespace | 29 } // namespace |
30 | 30 |
31 GpuOutput::GpuOutput( | 31 GpuOutput::GpuOutput( |
32 mojo::InterfaceHandle<mojo::ContextProvider> context_provider, | 32 mojo::InterfaceHandle<mojo::ContextProvider> context_provider, |
33 const SchedulerCallbacks& scheduler_callbacks, | 33 const SchedulerCallbacks& scheduler_callbacks, |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
79 | 79 |
80 Scheduler* GpuOutput::GetScheduler() { | 80 Scheduler* GpuOutput::GetScheduler() { |
81 return vsync_scheduler_.get(); | 81 return vsync_scheduler_.get(); |
82 } | 82 } |
83 | 83 |
84 void GpuOutput::SubmitFrame(const scoped_refptr<RenderFrame>& frame) { | 84 void GpuOutput::SubmitFrame(const scoped_refptr<RenderFrame>& frame) { |
85 DCHECK(frame); | 85 DCHECK(frame); |
86 TRACE_EVENT0("gfx", "GpuOutput::SubmitFrame"); | 86 TRACE_EVENT0("gfx", "GpuOutput::SubmitFrame"); |
87 | 87 |
88 const int64_t submit_time = MojoGetTimeTicksNow(); | 88 const int64_t submit_time = MojoGetTimeTicksNow(); |
89 scoped_refptr<FrameData> frame_data( | 89 |
90 new FrameData(frame, submit_time)); // drop outside lock | 90 // Note: we may swap an old frame into |frame_data| to keep alive until |
| 91 // we exit the lock. |
| 92 std::unique_ptr<FrameData> frame_data(new FrameData(frame, submit_time)); |
91 { | 93 { |
92 std::lock_guard<std::mutex> lock(shared_state_.mutex); | 94 std::lock_guard<std::mutex> lock(shared_state_.mutex); |
93 | 95 |
94 TRACE_EVENT_FLOW_BEGIN0("gfx", "Frame Queued", frame_data.get()); | 96 // Enqueue the frame, ensuring that the queue only contains at most |
95 shared_state_.current_frame_data.swap(frame_data); | 97 // one pending or scheduled frame. If the last frame hasn't been drawn by |
96 if (frame_data && !frame_data->drawn) { | 98 // now then the rasterizer must be falling behind. |
97 // Dropped an undrawn frame. | 99 if (shared_state_.frames.empty() || |
98 DVLOG(2) << "Rasterizer stalled, dropping frame to catch up."; | 100 shared_state_.frames.back()->state == FrameData::State::Drawing) { |
| 101 // The queue is busy drawing. Enqueue the new frame at the end. |
| 102 shared_state_.frames.emplace(std::move(frame_data)); |
| 103 } else if (shared_state_.frames.back()->state == |
| 104 FrameData::State::Finished) { |
| 105 // The queue contains a finished frame which we had retained to prevent |
| 106 // the queue from becoming empty and losing track of the current frame. |
| 107 // Replace it with the new frame. |
| 108 DCHECK(shared_state_.frames.size() == 1u); |
| 109 shared_state_.frames.back().swap(frame_data); |
| 110 } else { |
| 111 // The queue already contains a pending frame which means the rasterizer |
| 112 // has gotten so far behind it wasn't even able to issue the previous |
| 113 // undrawn frame. Replace it with the new frame, thereby ensuring |
| 114 // the queue never contains more than one pending frame at a time. |
| 115 DCHECK(shared_state_.frames.back()->state == FrameData::State::Pending); |
| 116 shared_state_.frames.back().swap(frame_data); |
99 TRACE_EVENT_FLOW_END1("gfx", "Frame Queued", frame_data.get(), "drawn", | 117 TRACE_EVENT_FLOW_END1("gfx", "Frame Queued", frame_data.get(), "drawn", |
100 false); | 118 false); |
| 119 DVLOG(2) << "Rasterizer stalled, dropped a frame to catch up."; |
101 } | 120 } |
102 | 121 |
103 // TODO(jeffbrown): If the draw queue is full, we should pause | 122 TRACE_EVENT_FLOW_BEGIN0("gfx", "Frame Queued", |
| 123 shared_state_.frames.back().get()); |
| 124 |
| 125 if (!shared_state_.rasterizer_ready) |
| 126 return; |
| 127 |
| 128 // TODO(jeffbrown): If the draw queue is overfull, we should pause |
104 // scheduling until the queue drains. | 129 // scheduling until the queue drains. |
105 if (shared_state_.rasterizer_ready && | 130 if (shared_state_.frames.size() <= pipeline_depth_) |
106 shared_state_.drawn_frames_awaiting_finish.size() < pipeline_depth_) | |
107 ScheduleDrawLocked(); | 131 ScheduleDrawLocked(); |
108 } | 132 } |
109 } | 133 } |
110 | 134 |
111 void GpuOutput::OnRasterizerReady(int64_t vsync_timebase, | 135 void GpuOutput::OnRasterizerReady(int64_t vsync_timebase, |
112 int64_t vsync_interval) { | 136 int64_t vsync_interval) { |
113 DCHECK(rasterizer_task_runner_->RunsTasksOnCurrentThread()); | 137 DCHECK(rasterizer_task_runner_->RunsTasksOnCurrentThread()); |
114 | 138 |
115 // TODO(jeffbrown): This shouldn't be hardcoded. | 139 // TODO(jeffbrown): This shouldn't be hardcoded. |
116 // Need to do some real tuning and possibly determine values adaptively. | 140 // Need to do some real tuning and possibly determine values adaptively. |
(...skipping 10 matching lines...) Expand all Loading... |
127 PostErrorCallback(); | 151 PostErrorCallback(); |
128 return; | 152 return; |
129 } | 153 } |
130 | 154 |
131 { | 155 { |
132 std::lock_guard<std::mutex> lock(shared_state_.mutex); | 156 std::lock_guard<std::mutex> lock(shared_state_.mutex); |
133 | 157 |
134 if (shared_state_.rasterizer_ready) | 158 if (shared_state_.rasterizer_ready) |
135 return; | 159 return; |
136 | 160 |
137 DCHECK(shared_state_.drawn_frames_awaiting_finish.empty()); | |
138 shared_state_.rasterizer_ready = true; | 161 shared_state_.rasterizer_ready = true; |
139 | 162 |
140 if (!shared_state_.current_frame_data) | 163 if (shared_state_.frames.empty()) |
141 return; | 164 return; |
142 | 165 |
143 shared_state_.current_frame_data->Recycle(); | 166 shared_state_.frames.back()->ResetDrawState(); |
144 TRACE_EVENT_FLOW_BEGIN0("gfx", "Frame Queued", | 167 TRACE_EVENT_FLOW_BEGIN0("gfx", "Frame Queued", |
145 shared_state_.current_frame_data.get()); | 168 shared_state_.frames.back().get()); |
146 ScheduleDrawLocked(); | 169 ScheduleDrawLocked(); |
147 } | 170 } |
148 } | 171 } |
149 | 172 |
150 void GpuOutput::OnRasterizerSuspended() { | 173 void GpuOutput::OnRasterizerSuspended() { |
151 DCHECK(rasterizer_task_runner_->RunsTasksOnCurrentThread()); | 174 DCHECK(rasterizer_task_runner_->RunsTasksOnCurrentThread()); |
152 | 175 |
153 vsync_scheduler_->Stop(); | 176 vsync_scheduler_->Stop(); |
154 | 177 |
155 { | 178 { |
156 std::lock_guard<std::mutex> lock(shared_state_.mutex); | 179 std::lock_guard<std::mutex> lock(shared_state_.mutex); |
157 | 180 |
158 if (!shared_state_.rasterizer_ready) | 181 if (!shared_state_.rasterizer_ready) |
159 return; | 182 return; |
160 | 183 |
161 shared_state_.rasterizer_ready = false; | 184 shared_state_.rasterizer_ready = false; |
162 } | 185 } |
163 } | 186 } |
164 | 187 |
165 void GpuOutput::OnRasterizerError() { | 188 void GpuOutput::OnRasterizerError() { |
166 DCHECK(rasterizer_task_runner_->RunsTasksOnCurrentThread()); | 189 DCHECK(rasterizer_task_runner_->RunsTasksOnCurrentThread()); |
167 | 190 |
168 PostErrorCallback(); | 191 PostErrorCallback(); |
169 } | 192 } |
170 | 193 |
171 void GpuOutput::ScheduleDrawLocked() { | 194 void GpuOutput::ScheduleDrawLocked() { |
172 DCHECK(shared_state_.current_frame_data); | 195 DCHECK(!shared_state_.frames.empty()); |
173 DCHECK(!shared_state_.current_frame_data->drawn); | 196 DCHECK(shared_state_.frames.back()->state == FrameData::State::Pending); |
| 197 DCHECK(shared_state_.frames.size() <= pipeline_depth_); |
174 | 198 |
175 if (shared_state_.draw_scheduled) | 199 if (shared_state_.draw_scheduled) |
176 return; | 200 return; |
177 | 201 |
178 shared_state_.draw_scheduled = true; | 202 shared_state_.draw_scheduled = true; |
179 rasterizer_task_runner_->PostTask( | 203 rasterizer_task_runner_->PostTask( |
180 FROM_HERE, base::Bind(&GpuOutput::OnDraw, base::Unretained(this))); | 204 FROM_HERE, base::Bind(&GpuOutput::OnDraw, base::Unretained(this))); |
181 } | 205 } |
182 | 206 |
183 void GpuOutput::OnDraw() { | 207 void GpuOutput::OnDraw() { |
184 DCHECK(rasterizer_task_runner_->RunsTasksOnCurrentThread()); | 208 DCHECK(rasterizer_task_runner_->RunsTasksOnCurrentThread()); |
185 TRACE_EVENT0("gfx", "GpuOutput::OnDraw"); | 209 TRACE_EVENT0("gfx", "GpuOutput::OnDraw"); |
186 | 210 |
187 scoped_refptr<FrameData> frame_data; // used outside lock | 211 FrameData* frame_data; // used outside lock |
188 { | 212 { |
189 std::lock_guard<std::mutex> lock(shared_state_.mutex); | 213 std::lock_guard<std::mutex> lock(shared_state_.mutex); |
190 | 214 |
191 DCHECK(shared_state_.draw_scheduled); | 215 DCHECK(shared_state_.draw_scheduled); |
192 DCHECK(shared_state_.current_frame_data); | 216 DCHECK(!shared_state_.frames.empty()); |
193 DCHECK(!shared_state_.current_frame_data->drawn); | 217 DCHECK(shared_state_.frames.back()->state == FrameData::State::Pending); |
194 | 218 |
195 shared_state_.draw_scheduled = false; | 219 shared_state_.draw_scheduled = false; |
196 | 220 if (!shared_state_.rasterizer_ready) |
197 if (!shared_state_.rasterizer_ready || | |
198 shared_state_.drawn_frames_awaiting_finish.size() >= pipeline_depth_) | |
199 return; | 221 return; |
200 | 222 |
201 frame_data = shared_state_.current_frame_data; | 223 frame_data = shared_state_.frames.back().get(); |
202 frame_data->drawn = true; | 224 frame_data->state = FrameData::State::Drawing; |
203 frame_data->draw_time = MojoGetTimeTicksNow(); | 225 frame_data->draw_started_time = MojoGetTimeTicksNow(); |
204 TRACE_EVENT_FLOW_END1("gfx", "Frame Queued", frame_data.get(), "drawn", | 226 TRACE_EVENT_FLOW_END1("gfx", "Frame Queued", frame_data, "drawn", true); |
205 true); | |
206 | |
207 TRACE_EVENT_ASYNC_BEGIN0("gfx", "Rasterize", frame_data.get()); | |
208 shared_state_.drawn_frames_awaiting_finish.emplace(frame_data); | |
209 } | 227 } |
210 | 228 |
| 229 // It is safe to access |frame_data| outside of the lock here because |
| 230 // it will not be dequeued until |OnRasterizerFinishedDraw| gets posted |
| 231 // to this thread's message loop. Moreover |SubmitFrame| will not discard |
| 232 // or replace the frame because its state is |Drawing|. |
| 233 TRACE_EVENT_ASYNC_BEGIN0("gfx", "Rasterize", frame_data); |
211 rasterizer_->DrawFrame(frame_data->frame); | 234 rasterizer_->DrawFrame(frame_data->frame); |
212 frame_data->wait_time = MojoGetTimeTicksNow(); | 235 frame_data->draw_issued_time = MojoGetTimeTicksNow(); |
213 } | 236 } |
214 | 237 |
215 void GpuOutput::OnRasterizerFinishedDraw(bool presented) { | 238 void GpuOutput::OnRasterizerFinishedDraw(bool presented) { |
216 DCHECK(rasterizer_task_runner_->RunsTasksOnCurrentThread()); | 239 DCHECK(rasterizer_task_runner_->RunsTasksOnCurrentThread()); |
217 TRACE_EVENT0("gfx", "GpuOutput::OnRasterizerFinishedDraw"); | 240 TRACE_EVENT0("gfx", "GpuOutput::OnRasterizerFinishedDraw"); |
218 | 241 |
219 const int64_t finish_time = MojoGetTimeTicksNow(); | 242 const int64_t finish_time = MojoGetTimeTicksNow(); |
220 scoped_refptr<FrameData> frame_data; // drop outside lock | 243 |
| 244 // Note: we may swap an old frame into |old_frame_data| to keep alive until |
| 245 // we exit the lock. |
| 246 std::unique_ptr<FrameData> old_frame_data; |
221 { | 247 { |
222 std::lock_guard<std::mutex> lock(shared_state_.mutex); | 248 std::lock_guard<std::mutex> lock(shared_state_.mutex); |
223 | 249 |
224 DCHECK(shared_state_.rasterizer_ready); | 250 DCHECK(shared_state_.rasterizer_ready); |
225 DCHECK(!shared_state_.drawn_frames_awaiting_finish.empty()); | 251 DCHECK(!shared_state_.frames.empty()); |
226 size_t draw_queue_depth = shared_state_.drawn_frames_awaiting_finish.size(); | 252 |
227 shared_state_.drawn_frames_awaiting_finish.front().swap(frame_data); | 253 FrameData* frame_data = shared_state_.frames.front().get(); |
228 shared_state_.drawn_frames_awaiting_finish.pop(); | |
229 DCHECK(frame_data); | 254 DCHECK(frame_data); |
230 DCHECK(frame_data->drawn); | 255 DCHECK(frame_data->state == FrameData::State::Drawing); |
231 TRACE_EVENT_ASYNC_END1("gfx", "Rasterize", frame_data.get(), "presented", | 256 TRACE_EVENT_ASYNC_END1("gfx", "Rasterize", frame_data, "presented", |
232 presented); | 257 presented); |
233 | 258 |
| 259 frame_data->state = FrameData::State::Finished; |
| 260 |
234 // TODO(jeffbrown): Adjust scheduler behavior based on observed timing. | 261 // TODO(jeffbrown): Adjust scheduler behavior based on observed timing. |
235 // Note: These measurements don't account for systematic downstream delay | 262 // Note: These measurements don't account for systematic downstream delay |
236 // in the display pipeline (how long it takes pixels to actually light up). | 263 // in the display pipeline (how long it takes pixels to actually light up). |
237 if (presented) { | 264 if (presented) { |
238 const RenderFrame::Metadata& frame_metadata = | 265 const RenderFrame::Metadata& frame_metadata = |
239 frame_data->frame->metadata(); | 266 frame_data->frame->metadata(); |
240 const mojo::gfx::composition::FrameInfo& frame_info = | 267 const mojo::gfx::composition::FrameInfo& frame_info = |
241 frame_metadata.frame_info(); | 268 frame_metadata.frame_info(); |
242 const int64_t frame_time = frame_info.frame_time; | 269 const int64_t frame_time = frame_info.frame_time; |
243 const int64_t presentation_time = frame_info.presentation_time; | 270 const int64_t presentation_time = frame_info.presentation_time; |
244 const int64_t composition_time = frame_metadata.composition_time(); | 271 const int64_t composition_time = frame_metadata.composition_time(); |
245 const int64_t draw_time = frame_data->draw_time; | 272 const int64_t draw_started_time = frame_data->draw_started_time; |
246 const int64_t wait_time = frame_data->wait_time; | 273 const int64_t draw_issued_time = frame_data->draw_issued_time; |
247 const int64_t submit_time = frame_data->submit_time; | 274 const int64_t submit_time = frame_data->submit_time; |
| 275 const size_t draw_queue_depth = shared_state_.frames.size(); |
248 | 276 |
249 DVLOG(2) << "Presented frame: composition latency " | 277 DVLOG(2) << "Presented frame: composition latency " |
250 << (composition_time - frame_time) << " us, submission latency " | 278 << (composition_time - frame_time) << " us, submission latency " |
251 << (submit_time - composition_time) << " us, queue latency " | 279 << (submit_time - composition_time) << " us, queue latency " |
252 << (draw_time - submit_time) << " us, draw latency " | 280 << (draw_started_time - submit_time) << " us, draw latency " |
253 << (wait_time - draw_time) << " us, GPU latency " | 281 << (draw_issued_time - draw_started_time) << " us, GPU latency " |
254 << (finish_time - wait_time) << " us, total latency " | 282 << (finish_time - draw_issued_time) << " us, total latency " |
255 << (finish_time - frame_time) << " us, presentation time error " | 283 << (finish_time - frame_time) << " us, presentation time error " |
256 << (finish_time - presentation_time) << " us" | 284 << (finish_time - presentation_time) << " us" |
257 << ", draw queue depth " << draw_queue_depth; | 285 << ", draw queue depth " << draw_queue_depth; |
258 } else { | 286 } else { |
259 DVLOG(2) << "Rasterizer dropped frame."; | 287 DVLOG(2) << "Rasterizer dropped frame."; |
260 } | 288 } |
261 | 289 |
262 DCHECK(shared_state_.current_frame_data); | 290 if (shared_state_.frames.size() > 1u) { |
263 if (!shared_state_.current_frame_data->drawn) | 291 shared_state_.frames.front().swap(old_frame_data); |
264 ScheduleDrawLocked(); | 292 shared_state_.frames.pop(); |
| 293 if (shared_state_.frames.back()->state == FrameData::State::Pending) |
| 294 ScheduleDrawLocked(); |
| 295 } |
265 } | 296 } |
266 } | 297 } |
267 | 298 |
268 void GpuOutput::InitializeRasterizer( | 299 void GpuOutput::InitializeRasterizer( |
269 mojo::InterfaceHandle<mojo::ContextProvider> context_provider) { | 300 mojo::InterfaceHandle<mojo::ContextProvider> context_provider) { |
270 DCHECK(rasterizer_task_runner_->RunsTasksOnCurrentThread()); | 301 DCHECK(rasterizer_task_runner_->RunsTasksOnCurrentThread()); |
271 DCHECK(!rasterizer_); | 302 DCHECK(!rasterizer_); |
272 TRACE_EVENT0("gfx", "GpuOutput::InitializeRasterizer"); | 303 TRACE_EVENT0("gfx", "GpuOutput::InitializeRasterizer"); |
273 | 304 |
274 rasterizer_.reset(new GpuRasterizer( | 305 rasterizer_.reset(new GpuRasterizer( |
(...skipping 13 matching lines...) Expand all Loading... |
288 void GpuOutput::PostErrorCallback() { | 319 void GpuOutput::PostErrorCallback() { |
289 compositor_task_runner_->PostTask(FROM_HERE, error_callback_); | 320 compositor_task_runner_->PostTask(FROM_HERE, error_callback_); |
290 } | 321 } |
291 | 322 |
292 GpuOutput::FrameData::FrameData(const scoped_refptr<RenderFrame>& frame, | 323 GpuOutput::FrameData::FrameData(const scoped_refptr<RenderFrame>& frame, |
293 int64_t submit_time) | 324 int64_t submit_time) |
294 : frame(frame), submit_time(submit_time) {} | 325 : frame(frame), submit_time(submit_time) {} |
295 | 326 |
296 GpuOutput::FrameData::~FrameData() {} | 327 GpuOutput::FrameData::~FrameData() {} |
297 | 328 |
298 void GpuOutput::FrameData::Recycle() { | 329 void GpuOutput::FrameData::ResetDrawState() { |
299 drawn = false; | 330 state = State::Pending; |
300 draw_time = 0; | 331 draw_started_time = 0; |
301 wait_time = 0; | 332 draw_issued_time = 0; |
302 } | 333 } |
303 | 334 |
304 } // namespace compositor | 335 } // namespace compositor |
OLD | NEW |