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

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: 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
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 undrawn frame. If the last frame hasn't been drawn by now then
96 if (frame_data && !frame_data->drawn) { 96 // the rasterizer must be falling behind.
97 // Dropped an undrawn frame. 97 if (!shared_state_.frames.empty() && shared_state_.frames.front()->finished)
mikejurka 2016/05/25 21:11:46 if you introduce a pinned_for_draw variable, i wou
jeffbrown 2016/05/25 23:45:43 Maybe add a DCHECK here that the queue size is exa
98 DVLOG(2) << "Rasterizer stalled, dropping frame to catch up."; 98 shared_state_.frames.pop();
mikejurka 2016/05/25 21:11:46 should we have a DCHECK after this pop that the fr
mikejurka 2016/05/25 23:54:06 actually, on second thought, shouldn't the above "
99 if (shared_state_.frames.empty() || shared_state_.frames.back()->drawn) {
100 shared_state_.frames.emplace(std::move(frame_data));
101 } else {
jeffbrown 2016/05/25 23:45:43 Add a comment here explaining why it's safe to rep
102 shared_state_.frames.back().swap(frame_data);
mikejurka 2016/05/25 21:11:46 if you introduce a pinned_for_draw variable, i wou
99 TRACE_EVENT_FLOW_END1("gfx", "Frame Queued", frame_data.get(), "drawn", 103 TRACE_EVENT_FLOW_END1("gfx", "Frame Queued", frame_data.get(), "drawn",
100 false); 104 false);
105 DVLOG(2) << "Rasterizer stalled, dropped a frame to catch up.";
101 } 106 }
102 107
103 // TODO(jeffbrown): If the draw queue is full, we should pause 108 TRACE_EVENT_FLOW_BEGIN0("gfx", "Frame Queued",
109 shared_state_.frames.back().get());
110
111 if (!shared_state_.rasterizer_ready)
112 return;
113
114 // TODO(jeffbrown): If the draw queue is overfull, we should pause
104 // scheduling until the queue drains. 115 // scheduling until the queue drains.
105 if (shared_state_.rasterizer_ready && 116 if (shared_state_.frames.size() <= pipeline_depth_)
106 shared_state_.drawn_frames_awaiting_finish.size() < pipeline_depth_)
107 ScheduleDrawLocked(); 117 ScheduleDrawLocked();
mikejurka 2016/05/25 21:11:46 might be useful to track another time here ie draw
jeffbrown 2016/05/25 23:45:43 I'm going to leave this out. The other two timest
108 } 118 }
109 } 119 }
110 120
111 void GpuOutput::OnRasterizerReady(int64_t vsync_timebase, 121 void GpuOutput::OnRasterizerReady(int64_t vsync_timebase,
112 int64_t vsync_interval) { 122 int64_t vsync_interval) {
113 DCHECK(rasterizer_task_runner_->RunsTasksOnCurrentThread()); 123 DCHECK(rasterizer_task_runner_->RunsTasksOnCurrentThread());
114 124
115 // TODO(jeffbrown): This shouldn't be hardcoded. 125 // TODO(jeffbrown): This shouldn't be hardcoded.
116 // Need to do some real tuning and possibly determine values adaptively. 126 // Need to do some real tuning and possibly determine values adaptively.
117 // We should probably split the Start() method in two to separate the 127 // We should probably split the Start() method in two to separate the
118 // process of setting parameters from starting / stopping scheduling. 128 // process of setting parameters from starting / stopping scheduling.
119 const int64_t update_phase = -vsync_interval; 129 const int64_t update_phase = -vsync_interval;
120 const int64_t snapshot_phase = -vsync_interval / 6; 130 const int64_t snapshot_phase = -vsync_interval / 6;
121 // TODO(jeffbrown): Determine the presentation phase based on queue depth. 131 // TODO(jeffbrown): Determine the presentation phase based on queue depth.
122 const int64_t presentation_phase = vsync_interval * pipeline_depth_; 132 const int64_t presentation_phase = vsync_interval * pipeline_depth_;
123 if (!vsync_scheduler_->Start(vsync_timebase, vsync_interval, update_phase, 133 if (!vsync_scheduler_->Start(vsync_timebase, vsync_interval, update_phase,
124 snapshot_phase, presentation_phase)) { 134 snapshot_phase, presentation_phase)) {
125 LOG(ERROR) << "Received invalid vsync parameters: timebase=" 135 LOG(ERROR) << "Received invalid vsync parameters: timebase="
126 << vsync_timebase << ", interval=" << vsync_interval; 136 << vsync_timebase << ", interval=" << vsync_interval;
127 PostErrorCallback(); 137 PostErrorCallback();
128 return; 138 return;
129 } 139 }
130 140
131 { 141 {
132 std::lock_guard<std::mutex> lock(shared_state_.mutex); 142 std::lock_guard<std::mutex> lock(shared_state_.mutex);
133 143
134 if (shared_state_.rasterizer_ready) 144 if (shared_state_.rasterizer_ready)
135 return; 145 return;
136 146
137 DCHECK(shared_state_.drawn_frames_awaiting_finish.empty());
138 shared_state_.rasterizer_ready = true; 147 shared_state_.rasterizer_ready = true;
139 148
140 if (!shared_state_.current_frame_data) 149 if (shared_state_.frames.empty())
141 return; 150 return;
142 151
143 shared_state_.current_frame_data->Recycle(); 152 shared_state_.frames.back()->ClearDrawState();
144 TRACE_EVENT_FLOW_BEGIN0("gfx", "Frame Queued", 153 TRACE_EVENT_FLOW_BEGIN0("gfx", "Frame Queued",
145 shared_state_.current_frame_data.get()); 154 shared_state_.frames.back().get());
146 ScheduleDrawLocked(); 155 ScheduleDrawLocked();
147 } 156 }
148 } 157 }
149 158
150 void GpuOutput::OnRasterizerSuspended() { 159 void GpuOutput::OnRasterizerSuspended() {
151 DCHECK(rasterizer_task_runner_->RunsTasksOnCurrentThread()); 160 DCHECK(rasterizer_task_runner_->RunsTasksOnCurrentThread());
152 161
153 vsync_scheduler_->Stop(); 162 vsync_scheduler_->Stop();
154 163
155 { 164 {
156 std::lock_guard<std::mutex> lock(shared_state_.mutex); 165 std::lock_guard<std::mutex> lock(shared_state_.mutex);
157 166
158 if (!shared_state_.rasterizer_ready) 167 if (!shared_state_.rasterizer_ready)
159 return; 168 return;
160 169
161 shared_state_.rasterizer_ready = false; 170 shared_state_.rasterizer_ready = false;
162 } 171 }
163 } 172 }
164 173
165 void GpuOutput::OnRasterizerError() { 174 void GpuOutput::OnRasterizerError() {
166 DCHECK(rasterizer_task_runner_->RunsTasksOnCurrentThread()); 175 DCHECK(rasterizer_task_runner_->RunsTasksOnCurrentThread());
167 176
168 PostErrorCallback(); 177 PostErrorCallback();
169 } 178 }
170 179
171 void GpuOutput::ScheduleDrawLocked() { 180 void GpuOutput::ScheduleDrawLocked() {
172 DCHECK(shared_state_.current_frame_data); 181 DCHECK(!shared_state_.frames.empty());
173 DCHECK(!shared_state_.current_frame_data->drawn); 182 DCHECK(!shared_state_.frames.back()->drawn);
183 DCHECK(shared_state_.frames.size() <= pipeline_depth_);
174 184
175 if (shared_state_.draw_scheduled) 185 if (shared_state_.draw_scheduled)
176 return; 186 return;
177 187
178 shared_state_.draw_scheduled = true; 188 shared_state_.draw_scheduled = true;
179 rasterizer_task_runner_->PostTask( 189 rasterizer_task_runner_->PostTask(
180 FROM_HERE, base::Bind(&GpuOutput::OnDraw, base::Unretained(this))); 190 FROM_HERE, base::Bind(&GpuOutput::OnDraw, base::Unretained(this)));
181 } 191 }
182 192
183 void GpuOutput::OnDraw() { 193 void GpuOutput::OnDraw() {
184 DCHECK(rasterizer_task_runner_->RunsTasksOnCurrentThread()); 194 DCHECK(rasterizer_task_runner_->RunsTasksOnCurrentThread());
185 TRACE_EVENT0("gfx", "GpuOutput::OnDraw"); 195 TRACE_EVENT0("gfx", "GpuOutput::OnDraw");
186 196
187 scoped_refptr<FrameData> frame_data; // used outside lock 197 FrameData* frame_data;
mikejurka 2016/05/25 21:11:46 probably a good idea to initialize to null (just f
jeffbrown 2016/05/25 23:45:43 The compiler will yell at me if I try to use an un
188 { 198 {
189 std::lock_guard<std::mutex> lock(shared_state_.mutex); 199 std::lock_guard<std::mutex> lock(shared_state_.mutex);
190 200
191 DCHECK(shared_state_.draw_scheduled); 201 DCHECK(shared_state_.draw_scheduled);
192 DCHECK(shared_state_.current_frame_data); 202 DCHECK(!shared_state_.frames.empty());
193 DCHECK(!shared_state_.current_frame_data->drawn); 203 DCHECK(!shared_state_.frames.back()->drawn);
194 204
195 shared_state_.draw_scheduled = false; 205 shared_state_.draw_scheduled = false;
196 206 if (!shared_state_.rasterizer_ready)
197 if (!shared_state_.rasterizer_ready ||
198 shared_state_.drawn_frames_awaiting_finish.size() >= pipeline_depth_)
199 return; 207 return;
200 208
201 frame_data = shared_state_.current_frame_data; 209 frame_data = shared_state_.frames.back().get();
mikejurka 2016/05/25 21:11:46 maybe move DCHECK(!frame_data->drawn) to here inst
jeffbrown 2016/05/25 23:45:43 I'd rather leave it where it is since the invarian
202 frame_data->drawn = true; 210 frame_data->drawn = true;
203 frame_data->draw_time = MojoGetTimeTicksNow(); 211 frame_data->draw_time = MojoGetTimeTicksNow();
204 TRACE_EVENT_FLOW_END1("gfx", "Frame Queued", frame_data.get(), "drawn", 212 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 } 213 }
210 214
mikejurka 2016/05/25 21:11:46 Do a DCHECK to ensure frame_data is not null here
jeffbrown 2016/05/25 23:45:43 I don't think that's really necessary. It must ob
215 // It is safe to access |frame_data| outside of the lock here because
216 // it will not be dequeued until |OnRasterizerFinishedDraw| gets posted
217 // to this thread's message loop. Moreover |SubmitFrame| will not replace
218 // the frame because the |drawn| flag has been set.
mikejurka 2016/05/25 21:11:46 I would put a note in the header file next to the
jeffbrown 2016/05/25 23:45:43 I don't actually need extra state here. I could a
219 TRACE_EVENT_ASYNC_BEGIN0("gfx", "Rasterize", frame_data);
211 rasterizer_->DrawFrame(frame_data->frame); 220 rasterizer_->DrawFrame(frame_data->frame);
212 frame_data->wait_time = MojoGetTimeTicksNow(); 221 frame_data->wait_time = MojoGetTimeTicksNow();
213 } 222 }
214 223
215 void GpuOutput::OnRasterizerFinishedDraw(bool presented) { 224 void GpuOutput::OnRasterizerFinishedDraw(bool presented) {
216 DCHECK(rasterizer_task_runner_->RunsTasksOnCurrentThread()); 225 DCHECK(rasterizer_task_runner_->RunsTasksOnCurrentThread());
217 TRACE_EVENT0("gfx", "GpuOutput::OnRasterizerFinishedDraw"); 226 TRACE_EVENT0("gfx", "GpuOutput::OnRasterizerFinishedDraw");
218 227
219 const int64_t finish_time = MojoGetTimeTicksNow(); 228 const int64_t finish_time = MojoGetTimeTicksNow();
220 scoped_refptr<FrameData> frame_data; // drop outside lock 229 std::unique_ptr<FrameData> old_frame_data; // drop outside lock
mikejurka 2016/05/25 21:11:46 change comment for "drop outside lock" to "keep ol
jeffbrown 2016/05/25 23:45:43 Done.
221 { 230 {
222 std::lock_guard<std::mutex> lock(shared_state_.mutex); 231 std::lock_guard<std::mutex> lock(shared_state_.mutex);
223 232
224 DCHECK(shared_state_.rasterizer_ready); 233 DCHECK(shared_state_.rasterizer_ready);
225 DCHECK(!shared_state_.drawn_frames_awaiting_finish.empty()); 234 DCHECK(!shared_state_.frames.empty());
226 size_t draw_queue_depth = shared_state_.drawn_frames_awaiting_finish.size(); 235
227 shared_state_.drawn_frames_awaiting_finish.front().swap(frame_data); 236 FrameData* frame_data = shared_state_.frames.front().get();
228 shared_state_.drawn_frames_awaiting_finish.pop();
229 DCHECK(frame_data); 237 DCHECK(frame_data);
230 DCHECK(frame_data->drawn); 238 DCHECK(frame_data->drawn);
231 TRACE_EVENT_ASYNC_END1("gfx", "Rasterize", frame_data.get(), "presented", 239 TRACE_EVENT_ASYNC_END1("gfx", "Rasterize", frame_data, "presented",
232 presented); 240 presented);
241 frame_data->finished = true;
mikejurka 2016/05/25 21:11:46 if you decide to use a "pinned_for_draw" var, i gu
233 242
234 // TODO(jeffbrown): Adjust scheduler behavior based on observed timing. 243 // TODO(jeffbrown): Adjust scheduler behavior based on observed timing.
235 // Note: These measurements don't account for systematic downstream delay 244 // Note: These measurements don't account for systematic downstream delay
236 // in the display pipeline (how long it takes pixels to actually light up). 245 // in the display pipeline (how long it takes pixels to actually light up).
237 if (presented) { 246 if (presented) {
238 const RenderFrame::Metadata& frame_metadata = 247 const RenderFrame::Metadata& frame_metadata =
239 frame_data->frame->metadata(); 248 frame_data->frame->metadata();
240 const mojo::gfx::composition::FrameInfo& frame_info = 249 const mojo::gfx::composition::FrameInfo& frame_info =
241 frame_metadata.frame_info(); 250 frame_metadata.frame_info();
242 const int64_t frame_time = frame_info.frame_time; 251 const int64_t frame_time = frame_info.frame_time;
243 const int64_t presentation_time = frame_info.presentation_time; 252 const int64_t presentation_time = frame_info.presentation_time;
244 const int64_t composition_time = frame_metadata.composition_time(); 253 const int64_t composition_time = frame_metadata.composition_time();
245 const int64_t draw_time = frame_data->draw_time; 254 const int64_t draw_time = frame_data->draw_time;
246 const int64_t wait_time = frame_data->wait_time; 255 const int64_t wait_time = frame_data->wait_time;
247 const int64_t submit_time = frame_data->submit_time; 256 const int64_t submit_time = frame_data->submit_time;
257 const size_t draw_queue_depth = shared_state_.frames.size();
248 258
249 DVLOG(2) << "Presented frame: composition latency " 259 DVLOG(2) << "Presented frame: composition latency "
250 << (composition_time - frame_time) << " us, submission latency " 260 << (composition_time - frame_time) << " us, submission latency "
251 << (submit_time - composition_time) << " us, queue latency " 261 << (submit_time - composition_time) << " us, queue latency "
252 << (draw_time - submit_time) << " us, draw latency " 262 << (draw_time - submit_time) << " us, draw latency "
253 << (wait_time - draw_time) << " us, GPU latency " 263 << (wait_time - draw_time) << " us, GPU latency "
254 << (finish_time - wait_time) << " us, total latency " 264 << (finish_time - wait_time) << " us, total latency "
255 << (finish_time - frame_time) << " us, presentation time error " 265 << (finish_time - frame_time) << " us, presentation time error "
256 << (finish_time - presentation_time) << " us" 266 << (finish_time - presentation_time) << " us"
257 << ", draw queue depth " << draw_queue_depth; 267 << ", draw queue depth " << draw_queue_depth;
258 } else { 268 } else {
259 DVLOG(2) << "Rasterizer dropped frame."; 269 DVLOG(2) << "Rasterizer dropped frame.";
260 } 270 }
261 271
262 DCHECK(shared_state_.current_frame_data); 272 if (shared_state_.frames.size() > 1u) {
263 if (!shared_state_.current_frame_data->drawn) 273 shared_state_.frames.front().swap(old_frame_data);
mikejurka 2016/05/25 21:11:46 I would add the earlier comment here again to clar
jeffbrown 2016/05/25 23:45:43 Done.
264 ScheduleDrawLocked(); 274 shared_state_.frames.pop();
275 if (!shared_state_.frames.back()->drawn)
276 ScheduleDrawLocked();
277 }
265 } 278 }
266 } 279 }
267 280
268 void GpuOutput::InitializeRasterizer( 281 void GpuOutput::InitializeRasterizer(
269 mojo::InterfaceHandle<mojo::ContextProvider> context_provider) { 282 mojo::InterfaceHandle<mojo::ContextProvider> context_provider) {
270 DCHECK(rasterizer_task_runner_->RunsTasksOnCurrentThread()); 283 DCHECK(rasterizer_task_runner_->RunsTasksOnCurrentThread());
271 DCHECK(!rasterizer_); 284 DCHECK(!rasterizer_);
272 TRACE_EVENT0("gfx", "GpuOutput::InitializeRasterizer"); 285 TRACE_EVENT0("gfx", "GpuOutput::InitializeRasterizer");
273 286
274 rasterizer_.reset(new GpuRasterizer( 287 rasterizer_.reset(new GpuRasterizer(
(...skipping 13 matching lines...) Expand all
288 void GpuOutput::PostErrorCallback() { 301 void GpuOutput::PostErrorCallback() {
289 compositor_task_runner_->PostTask(FROM_HERE, error_callback_); 302 compositor_task_runner_->PostTask(FROM_HERE, error_callback_);
290 } 303 }
291 304
292 GpuOutput::FrameData::FrameData(const scoped_refptr<RenderFrame>& frame, 305 GpuOutput::FrameData::FrameData(const scoped_refptr<RenderFrame>& frame,
293 int64_t submit_time) 306 int64_t submit_time)
294 : frame(frame), submit_time(submit_time) {} 307 : frame(frame), submit_time(submit_time) {}
295 308
296 GpuOutput::FrameData::~FrameData() {} 309 GpuOutput::FrameData::~FrameData() {}
297 310
298 void GpuOutput::FrameData::Recycle() { 311 void GpuOutput::FrameData::ClearDrawState() {
299 drawn = false; 312 drawn = false;
313 finished = false;
300 draw_time = 0; 314 draw_time = 0;
301 wait_time = 0; 315 wait_time = 0;
302 } 316 }
303 317
304 } // namespace compositor 318 } // namespace compositor
OLDNEW
« services/gfx/compositor/backend/gpu_output.h ('K') | « 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