Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(407)

Side by Side Diff: services/gfx/compositor/backend/gpu_output.cc

Issue 2009503003: Mozart: Reduce pipeline depth and unify frame queue. (Closed) Base URL: git@github.com:domokit/mojo.git@master
Patch Set: address review comments Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « services/gfx/compositor/backend/gpu_output.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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 std::unique_ptr<FrameData> frame_data(
90 new FrameData(frame, submit_time)); // drop outside lock 90 new FrameData(frame, submit_time)); // drop outside lock
91 { 91 {
92 std::lock_guard<std::mutex> lock(shared_state_.mutex); 92 std::lock_guard<std::mutex> lock(shared_state_.mutex);
93 93
94 TRACE_EVENT_FLOW_BEGIN0("gfx", "Frame Queued", frame_data.get()); 94 // Enqueue the frame, ensuring that the queue only contains at most
95 shared_state_.current_frame_data.swap(frame_data); 95 // one pending or scheduled frame. If the last frame hasn't been drawn by
96 if (frame_data && !frame_data->drawn) { 96 // now then the rasterizer must be falling behind.
97 // Dropped an undrawn frame. 97 if (shared_state_.frames.empty() ||
98 DVLOG(2) << "Rasterizer stalled, dropping frame to catch up."; 98 shared_state_.frames.back()->state == FrameData::State::Drawing) {
99 // The queue is busy drawing. Enqueue the new frame at the end.
100 shared_state_.frames.emplace(std::move(frame_data));
101 } else if (shared_state_.frames.back()->state ==
102 FrameData::State::Finished) {
103 // The queue contains a finished frame which we had retained to prevent
104 // the queue from becoming empty and losing track of the current frame.
105 // Replace it with the new frame.
106 DCHECK(shared_state_.frames.size() == 1u);
107 shared_state_.frames.back().swap(frame_data);
108 } else {
109 // The queue already contains a pending frame which means the rasterizer
110 // has gotten so far behind it wasn't even able to issue the previous
111 // undrawn frame. Replace it with the new frame, thereby ensuring
112 // the queue never contains more than one pending frame at a time.
113 DCHECK(shared_state_.frames.back()->state == FrameData::State::Pending);
114 shared_state_.frames.back().swap(frame_data);
99 TRACE_EVENT_FLOW_END1("gfx", "Frame Queued", frame_data.get(), "drawn", 115 TRACE_EVENT_FLOW_END1("gfx", "Frame Queued", frame_data.get(), "drawn",
100 false); 116 false);
117 DVLOG(2) << "Rasterizer stalled, dropped a frame to catch up.";
101 } 118 }
102 119
103 // TODO(jeffbrown): If the draw queue is full, we should pause 120 TRACE_EVENT_FLOW_BEGIN0("gfx", "Frame Queued",
121 shared_state_.frames.back().get());
122
123 if (!shared_state_.rasterizer_ready)
124 return;
125
126 // TODO(jeffbrown): If the draw queue is overfull, we should pause
104 // scheduling until the queue drains. 127 // scheduling until the queue drains.
105 if (shared_state_.rasterizer_ready && 128 if (shared_state_.frames.size() <= pipeline_depth_)
106 shared_state_.drawn_frames_awaiting_finish.size() < pipeline_depth_)
107 ScheduleDrawLocked(); 129 ScheduleDrawLocked();
108 } 130 }
109 } 131 }
110 132
111 void GpuOutput::OnRasterizerReady(int64_t vsync_timebase, 133 void GpuOutput::OnRasterizerReady(int64_t vsync_timebase,
112 int64_t vsync_interval) { 134 int64_t vsync_interval) {
113 DCHECK(rasterizer_task_runner_->RunsTasksOnCurrentThread()); 135 DCHECK(rasterizer_task_runner_->RunsTasksOnCurrentThread());
114 136
115 // TODO(jeffbrown): This shouldn't be hardcoded. 137 // TODO(jeffbrown): This shouldn't be hardcoded.
116 // Need to do some real tuning and possibly determine values adaptively. 138 // Need to do some real tuning and possibly determine values adaptively.
(...skipping 10 matching lines...) Expand all
127 PostErrorCallback(); 149 PostErrorCallback();
128 return; 150 return;
129 } 151 }
130 152
131 { 153 {
132 std::lock_guard<std::mutex> lock(shared_state_.mutex); 154 std::lock_guard<std::mutex> lock(shared_state_.mutex);
133 155
134 if (shared_state_.rasterizer_ready) 156 if (shared_state_.rasterizer_ready)
135 return; 157 return;
136 158
137 DCHECK(shared_state_.drawn_frames_awaiting_finish.empty());
138 shared_state_.rasterizer_ready = true; 159 shared_state_.rasterizer_ready = true;
139 160
140 if (!shared_state_.current_frame_data) 161 if (shared_state_.frames.empty())
141 return; 162 return;
142 163
143 shared_state_.current_frame_data->Recycle(); 164 shared_state_.frames.back()->ResetDrawState();
144 TRACE_EVENT_FLOW_BEGIN0("gfx", "Frame Queued", 165 TRACE_EVENT_FLOW_BEGIN0("gfx", "Frame Queued",
145 shared_state_.current_frame_data.get()); 166 shared_state_.frames.back().get());
146 ScheduleDrawLocked(); 167 ScheduleDrawLocked();
147 } 168 }
148 } 169 }
149 170
150 void GpuOutput::OnRasterizerSuspended() { 171 void GpuOutput::OnRasterizerSuspended() {
151 DCHECK(rasterizer_task_runner_->RunsTasksOnCurrentThread()); 172 DCHECK(rasterizer_task_runner_->RunsTasksOnCurrentThread());
152 173
153 vsync_scheduler_->Stop(); 174 vsync_scheduler_->Stop();
154 175
155 { 176 {
156 std::lock_guard<std::mutex> lock(shared_state_.mutex); 177 std::lock_guard<std::mutex> lock(shared_state_.mutex);
157 178
158 if (!shared_state_.rasterizer_ready) 179 if (!shared_state_.rasterizer_ready)
159 return; 180 return;
160 181
161 shared_state_.rasterizer_ready = false; 182 shared_state_.rasterizer_ready = false;
162 } 183 }
163 } 184 }
164 185
165 void GpuOutput::OnRasterizerError() { 186 void GpuOutput::OnRasterizerError() {
166 DCHECK(rasterizer_task_runner_->RunsTasksOnCurrentThread()); 187 DCHECK(rasterizer_task_runner_->RunsTasksOnCurrentThread());
167 188
168 PostErrorCallback(); 189 PostErrorCallback();
169 } 190 }
170 191
171 void GpuOutput::ScheduleDrawLocked() { 192 void GpuOutput::ScheduleDrawLocked() {
172 DCHECK(shared_state_.current_frame_data); 193 DCHECK(!shared_state_.frames.empty());
173 DCHECK(!shared_state_.current_frame_data->drawn); 194 DCHECK(shared_state_.frames.back()->state == FrameData::State::Pending);
195 DCHECK(shared_state_.frames.size() <= pipeline_depth_);
174 196
175 if (shared_state_.draw_scheduled) 197 if (shared_state_.draw_scheduled)
176 return; 198 return;
177 199
178 shared_state_.draw_scheduled = true; 200 shared_state_.draw_scheduled = true;
179 rasterizer_task_runner_->PostTask( 201 rasterizer_task_runner_->PostTask(
180 FROM_HERE, base::Bind(&GpuOutput::OnDraw, base::Unretained(this))); 202 FROM_HERE, base::Bind(&GpuOutput::OnDraw, base::Unretained(this)));
181 } 203 }
182 204
183 void GpuOutput::OnDraw() { 205 void GpuOutput::OnDraw() {
184 DCHECK(rasterizer_task_runner_->RunsTasksOnCurrentThread()); 206 DCHECK(rasterizer_task_runner_->RunsTasksOnCurrentThread());
185 TRACE_EVENT0("gfx", "GpuOutput::OnDraw"); 207 TRACE_EVENT0("gfx", "GpuOutput::OnDraw");
186 208
187 scoped_refptr<FrameData> frame_data; // used outside lock 209 FrameData* frame_data; // used outside lock
188 { 210 {
189 std::lock_guard<std::mutex> lock(shared_state_.mutex); 211 std::lock_guard<std::mutex> lock(shared_state_.mutex);
190 212
191 DCHECK(shared_state_.draw_scheduled); 213 DCHECK(shared_state_.draw_scheduled);
192 DCHECK(shared_state_.current_frame_data); 214 DCHECK(!shared_state_.frames.empty());
193 DCHECK(!shared_state_.current_frame_data->drawn); 215 DCHECK(shared_state_.frames.back()->state == FrameData::State::Pending);
194 216
195 shared_state_.draw_scheduled = false; 217 shared_state_.draw_scheduled = false;
196 218 if (!shared_state_.rasterizer_ready)
197 if (!shared_state_.rasterizer_ready ||
198 shared_state_.drawn_frames_awaiting_finish.size() >= pipeline_depth_)
199 return; 219 return;
200 220
201 frame_data = shared_state_.current_frame_data; 221 frame_data = shared_state_.frames.back().get();
202 frame_data->drawn = true; 222 frame_data->state = FrameData::State::Drawing;
203 frame_data->draw_time = MojoGetTimeTicksNow(); 223 frame_data->draw_started_time = MojoGetTimeTicksNow();
204 TRACE_EVENT_FLOW_END1("gfx", "Frame Queued", frame_data.get(), "drawn", 224 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 } 225 }
210 226
227 // It is safe to access |frame_data| outside of the lock here because
228 // it will not be dequeued until |OnRasterizerFinishedDraw| gets posted
229 // to this thread's message loop. Moreover |SubmitFrame| will not discard
230 // or replace the frame because its state is |Drawing|.
231 TRACE_EVENT_ASYNC_BEGIN0("gfx", "Rasterize", frame_data);
211 rasterizer_->DrawFrame(frame_data->frame); 232 rasterizer_->DrawFrame(frame_data->frame);
212 frame_data->wait_time = MojoGetTimeTicksNow(); 233 frame_data->draw_issued_time = MojoGetTimeTicksNow();
213 } 234 }
214 235
215 void GpuOutput::OnRasterizerFinishedDraw(bool presented) { 236 void GpuOutput::OnRasterizerFinishedDraw(bool presented) {
216 DCHECK(rasterizer_task_runner_->RunsTasksOnCurrentThread()); 237 DCHECK(rasterizer_task_runner_->RunsTasksOnCurrentThread());
217 TRACE_EVENT0("gfx", "GpuOutput::OnRasterizerFinishedDraw"); 238 TRACE_EVENT0("gfx", "GpuOutput::OnRasterizerFinishedDraw");
218 239
219 const int64_t finish_time = MojoGetTimeTicksNow(); 240 const int64_t finish_time = MojoGetTimeTicksNow();
220 scoped_refptr<FrameData> frame_data; // drop outside lock 241 std::unique_ptr<FrameData> old_frame_data; // drop outside lock
mikejurka 2016/05/25 23:54:06 change comment for "drop outside lock" to "keep ol
221 { 242 {
222 std::lock_guard<std::mutex> lock(shared_state_.mutex); 243 std::lock_guard<std::mutex> lock(shared_state_.mutex);
223 244
224 DCHECK(shared_state_.rasterizer_ready); 245 DCHECK(shared_state_.rasterizer_ready);
225 DCHECK(!shared_state_.drawn_frames_awaiting_finish.empty()); 246 DCHECK(!shared_state_.frames.empty());
226 size_t draw_queue_depth = shared_state_.drawn_frames_awaiting_finish.size(); 247
227 shared_state_.drawn_frames_awaiting_finish.front().swap(frame_data); 248 FrameData* frame_data = shared_state_.frames.front().get();
228 shared_state_.drawn_frames_awaiting_finish.pop();
229 DCHECK(frame_data); 249 DCHECK(frame_data);
230 DCHECK(frame_data->drawn); 250 DCHECK(frame_data->state == FrameData::State::Drawing);
231 TRACE_EVENT_ASYNC_END1("gfx", "Rasterize", frame_data.get(), "presented", 251 TRACE_EVENT_ASYNC_END1("gfx", "Rasterize", frame_data, "presented",
232 presented); 252 presented);
233 253
254 frame_data->state = FrameData::State::Finished;
255
234 // TODO(jeffbrown): Adjust scheduler behavior based on observed timing. 256 // TODO(jeffbrown): Adjust scheduler behavior based on observed timing.
235 // Note: These measurements don't account for systematic downstream delay 257 // Note: These measurements don't account for systematic downstream delay
236 // in the display pipeline (how long it takes pixels to actually light up). 258 // in the display pipeline (how long it takes pixels to actually light up).
237 if (presented) { 259 if (presented) {
238 const RenderFrame::Metadata& frame_metadata = 260 const RenderFrame::Metadata& frame_metadata =
239 frame_data->frame->metadata(); 261 frame_data->frame->metadata();
240 const mojo::gfx::composition::FrameInfo& frame_info = 262 const mojo::gfx::composition::FrameInfo& frame_info =
241 frame_metadata.frame_info(); 263 frame_metadata.frame_info();
242 const int64_t frame_time = frame_info.frame_time; 264 const int64_t frame_time = frame_info.frame_time;
243 const int64_t presentation_time = frame_info.presentation_time; 265 const int64_t presentation_time = frame_info.presentation_time;
244 const int64_t composition_time = frame_metadata.composition_time(); 266 const int64_t composition_time = frame_metadata.composition_time();
245 const int64_t draw_time = frame_data->draw_time; 267 const int64_t draw_started_time = frame_data->draw_started_time;
246 const int64_t wait_time = frame_data->wait_time; 268 const int64_t draw_issued_time = frame_data->draw_issued_time;
247 const int64_t submit_time = frame_data->submit_time; 269 const int64_t submit_time = frame_data->submit_time;
270 const size_t draw_queue_depth = shared_state_.frames.size();
248 271
249 DVLOG(2) << "Presented frame: composition latency " 272 DVLOG(2) << "Presented frame: composition latency "
250 << (composition_time - frame_time) << " us, submission latency " 273 << (composition_time - frame_time) << " us, submission latency "
251 << (submit_time - composition_time) << " us, queue latency " 274 << (submit_time - composition_time) << " us, queue latency "
252 << (draw_time - submit_time) << " us, draw latency " 275 << (draw_started_time - submit_time) << " us, draw latency "
253 << (wait_time - draw_time) << " us, GPU latency " 276 << (draw_issued_time - draw_started_time) << " us, GPU latency "
254 << (finish_time - wait_time) << " us, total latency " 277 << (finish_time - draw_issued_time) << " us, total latency "
255 << (finish_time - frame_time) << " us, presentation time error " 278 << (finish_time - frame_time) << " us, presentation time error "
256 << (finish_time - presentation_time) << " us" 279 << (finish_time - presentation_time) << " us"
257 << ", draw queue depth " << draw_queue_depth; 280 << ", draw queue depth " << draw_queue_depth;
258 } else { 281 } else {
259 DVLOG(2) << "Rasterizer dropped frame."; 282 DVLOG(2) << "Rasterizer dropped frame.";
260 } 283 }
261 284
262 DCHECK(shared_state_.current_frame_data); 285 if (shared_state_.frames.size() > 1u) {
263 if (!shared_state_.current_frame_data->drawn) 286 shared_state_.frames.front().swap(old_frame_data);
mikejurka 2016/05/25 23:54:06 I would add the earlier comment here again to clar
264 ScheduleDrawLocked(); 287 shared_state_.frames.pop();
288 if (shared_state_.frames.back()->state == FrameData::State::Pending)
289 ScheduleDrawLocked();
290 }
265 } 291 }
266 } 292 }
267 293
268 void GpuOutput::InitializeRasterizer( 294 void GpuOutput::InitializeRasterizer(
269 mojo::InterfaceHandle<mojo::ContextProvider> context_provider) { 295 mojo::InterfaceHandle<mojo::ContextProvider> context_provider) {
270 DCHECK(rasterizer_task_runner_->RunsTasksOnCurrentThread()); 296 DCHECK(rasterizer_task_runner_->RunsTasksOnCurrentThread());
271 DCHECK(!rasterizer_); 297 DCHECK(!rasterizer_);
272 TRACE_EVENT0("gfx", "GpuOutput::InitializeRasterizer"); 298 TRACE_EVENT0("gfx", "GpuOutput::InitializeRasterizer");
273 299
274 rasterizer_.reset(new GpuRasterizer( 300 rasterizer_.reset(new GpuRasterizer(
(...skipping 13 matching lines...) Expand all
288 void GpuOutput::PostErrorCallback() { 314 void GpuOutput::PostErrorCallback() {
289 compositor_task_runner_->PostTask(FROM_HERE, error_callback_); 315 compositor_task_runner_->PostTask(FROM_HERE, error_callback_);
290 } 316 }
291 317
292 GpuOutput::FrameData::FrameData(const scoped_refptr<RenderFrame>& frame, 318 GpuOutput::FrameData::FrameData(const scoped_refptr<RenderFrame>& frame,
293 int64_t submit_time) 319 int64_t submit_time)
294 : frame(frame), submit_time(submit_time) {} 320 : frame(frame), submit_time(submit_time) {}
295 321
296 GpuOutput::FrameData::~FrameData() {} 322 GpuOutput::FrameData::~FrameData() {}
297 323
298 void GpuOutput::FrameData::Recycle() { 324 void GpuOutput::FrameData::ResetDrawState() {
299 drawn = false; 325 state = State::Pending;
300 draw_time = 0; 326 draw_started_time = 0;
301 wait_time = 0; 327 draw_issued_time = 0;
302 } 328 }
303 329
304 } // namespace compositor 330 } // namespace compositor
OLDNEW
« no previous file with comments | « services/gfx/compositor/backend/gpu_output.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698