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

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: fix comments Created 4 years, 7 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
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
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
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
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