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/location.h" | 11 #include "base/location.h" |
11 #include "base/logging.h" | 12 #include "base/logging.h" |
12 #include "base/message_loop/message_loop.h" | 13 #include "base/message_loop/message_loop.h" |
14 #include "base/strings/string_number_conversions.h" | |
13 #include "base/trace_event/trace_event.h" | 15 #include "base/trace_event/trace_event.h" |
14 #include "services/gfx/compositor/backend/gpu_rasterizer.h" | |
15 #include "services/gfx/compositor/render/render_frame.h" | 16 #include "services/gfx/compositor/render/render_frame.h" |
16 | 17 |
17 namespace compositor { | 18 namespace compositor { |
18 namespace { | 19 namespace { |
19 // Maximum number of frames to hold in the queue for rendering. | 20 constexpr const char* kPipelineDepthSwitch = "pipeline-depth"; |
20 constexpr size_t kMaxPipelineDepth = 1; | 21 constexpr uint32_t kDefaultPipelineDepth = 2u; // ideally should be 1 |
21 } | 22 constexpr uint32_t kMinPipelineDepth = 1u; |
22 | 23 constexpr uint32_t kMaxPipelineDepth = 10u; |
mikejurka
2016/05/19 23:26:18
why should we ever be more than 3 frames ahead?
jeffbrown
2016/05/20 01:09:47
This is sort of arbitrary. The limit only exists
| |
23 template <typename T> | 24 |
24 static void Drop(scoped_ptr<T> ptr) {} | 25 scoped_ptr<base::MessagePump> CreateMessagePumpMojo() { |
25 | |
26 static scoped_ptr<base::MessagePump> CreateMessagePumpMojo() { | |
27 return base::MessageLoop::CreateMessagePumpForType( | 26 return base::MessageLoop::CreateMessagePumpForType( |
28 base::MessageLoop::TYPE_DEFAULT); | 27 base::MessageLoop::TYPE_DEFAULT); |
29 } | 28 } |
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, |
34 const base::Closure& error_callback) | 34 const base::Closure& error_callback) |
35 : scheduler_(new VsyncScheduler(base::MessageLoop::current()->task_runner(), | 35 : compositor_task_runner_(base::MessageLoop::current()->task_runner()), |
36 scheduler_callbacks)), | 36 vsync_scheduler_( |
37 rasterizer_delegate_(make_scoped_ptr(new RasterizerDelegate())) { | 37 new VsyncScheduler(compositor_task_runner_, scheduler_callbacks)), |
38 rasterizer_thread_(new base::Thread("gpu_rasterizer")), | |
39 rasterizer_initialized_(true, false) { | |
38 DCHECK(context_provider); | 40 DCHECK(context_provider); |
39 | 41 |
40 rasterizer_delegate_->PostInitialize( | 42 pipeline_depth_ = kDefaultPipelineDepth; |
41 std::move(context_provider), scheduler_, | 43 auto command_line = base::CommandLine::ForCurrentProcess(); |
42 base::MessageLoop::current()->task_runner(), error_callback); | 44 if (command_line->HasSwitch(kPipelineDepthSwitch)) { |
43 } | 45 std::string str(command_line->GetSwitchValueASCII(kPipelineDepthSwitch)); |
44 | 46 unsigned value; |
45 GpuOutput::~GpuOutput() { | 47 if (base::StringToUint(str, &value) && value >= kMinPipelineDepth && |
46 // Ensure destruction happens on the correct thread. | 48 value <= kMaxPipelineDepth) { |
47 rasterizer_delegate_->PostDestroy(rasterizer_delegate_.Pass()); | 49 pipeline_depth_ = value; |
48 } | 50 } else { |
49 | 51 LOG(ERROR) << "Invalid value for --" << kPipelineDepthSwitch << ": \"" |
50 Scheduler* GpuOutput::GetScheduler() { | 52 << str << "\""; |
mikejurka
2016/05/19 23:26:18
is there a way to exit/crash here? should we call
jeffbrown
2016/05/20 01:09:47
Hmm. Not a bad idea.
| |
51 return scheduler_.get(); | 53 } |
52 } | 54 } |
53 | 55 DVLOG(2) << "Using pipeline depth " << pipeline_depth_; |
54 void GpuOutput::SubmitFrame(const scoped_refptr<RenderFrame>& frame) { | 56 |
55 rasterizer_delegate_->PostFrame(frame); | |
56 } | |
57 | |
58 GpuOutput::RasterizerDelegate::RasterizerDelegate() { | |
59 base::Thread::Options options; | 57 base::Thread::Options options; |
60 options.message_pump_factory = base::Bind(&CreateMessagePumpMojo); | 58 options.message_pump_factory = base::Bind(&CreateMessagePumpMojo); |
61 | 59 rasterizer_thread_->StartWithOptions(options); |
62 thread_.reset(new base::Thread("gpu_rasterizer")); | 60 rasterizer_task_runner_ = rasterizer_thread_->message_loop()->task_runner(); |
63 thread_->StartWithOptions(options); | 61 rasterizer_task_runner_->PostTask( |
64 task_runner_ = thread_->message_loop()->task_runner(); | |
65 } | |
66 | |
67 GpuOutput::RasterizerDelegate::~RasterizerDelegate() {} | |
68 | |
69 void GpuOutput::RasterizerDelegate::PostInitialize( | |
70 mojo::InterfaceHandle<mojo::ContextProvider> context_provider, | |
71 const scoped_refptr<VsyncScheduler>& scheduler, | |
72 const scoped_refptr<base::TaskRunner>& task_runner, | |
73 const base::Closure& error_callback) { | |
74 task_runner_->PostTask( | |
75 FROM_HERE, | 62 FROM_HERE, |
76 base::Bind(&RasterizerDelegate::InitializeTask, base::Unretained(this), | 63 base::Bind(&GpuOutput::InitializeRasterizer, base::Unretained(this), |
77 base::Passed(std::move(context_provider)), scheduler, | 64 base::Passed(std::move(context_provider)))); |
78 base::MessageLoop::current()->task_runner(), error_callback)); | 65 rasterizer_initialized_.Wait(); |
79 } | 66 DCHECK(rasterizer_); |
80 | 67 } |
81 void GpuOutput::RasterizerDelegate::PostDestroy( | 68 |
82 scoped_ptr<RasterizerDelegate> self) { | 69 GpuOutput::~GpuOutput() { |
83 task_runner_->PostTask( | 70 // Ensure rasterizer destruction happens on the rasterizer thread. |
84 FROM_HERE, base::Bind(&Drop<RasterizerDelegate>, base::Passed(&self))); | 71 rasterizer_task_runner_->PostTask( |
85 } | 72 FROM_HERE, |
86 | 73 base::Bind(&GpuOutput::DestroyRasterizer, base::Unretained(this))); |
87 void GpuOutput::RasterizerDelegate::PostFrame( | 74 rasterizer_thread_->Stop(); |
mikejurka
2016/05/19 23:26:18
does ->Stop() block until the thread is done?
jeffbrown
2016/05/20 01:09:47
Yes.
| |
88 const scoped_refptr<RenderFrame>& frame) { | 75 DCHECK(!rasterizer_); |
89 bool was_empty; | 76 } |
90 scoped_refptr<RenderFrame> dropped_frame; | 77 |
91 { | 78 Scheduler* GpuOutput::GetScheduler() { |
92 std::lock_guard<std::mutex> lock(mutex_); | 79 return vsync_scheduler_.get(); |
93 was_empty = frames_.empty(); | 80 } |
94 if (frames_.size() == kMaxPipelineDepth) { | 81 |
95 // TODO(jeffbrown): Adjust scheduler behavior to compensate. | 82 void GpuOutput::SubmitFrame(const scoped_refptr<RenderFrame>& frame) { |
96 LOG(ERROR) << "Renderer pipeline stalled, dropping a frame to catch up."; | 83 DCHECK(frame); |
97 dropped_frame = frames_.front(); // drop an old frame outside the lock | 84 TRACE_EVENT0("gfx", "GpuOutput::SubmitFrame"); |
98 frames_.pop(); | 85 |
86 const int64_t submit_time = MojoGetTimeTicksNow(); | |
87 scoped_refptr<FrameData> frame_data( | |
mikejurka
2016/05/19 23:26:18
is there a way we can avoid using a scoped_refptr
jeffbrown
2016/05/20 01:09:47
These two things go together. There's no wrapping
| |
88 new FrameData(frame, submit_time)); // drop outside lock | |
89 { | |
90 std::lock_guard<std::mutex> lock(shared_state_.mutex); | |
91 | |
92 TRACE_EVENT_FLOW_BEGIN0("gfx", "Frame Queued", frame_data.get()); | |
93 shared_state_.current_frame_data.swap(frame_data); | |
94 if (frame_data && !frame_data->draw_time) { | |
mikejurka
2016/05/19 23:26:18
what does it mean if draw_time is 0? does it mean
jeffbrown
2016/05/20 01:09:47
Added a comment to the header file.
| |
95 // Dropped an undrawn frame. | |
96 DVLOG(2) << "Rasterizer stalled, dropping frame to catch up."; | |
97 TRACE_EVENT_FLOW_END1("gfx", "Frame Queued", frame_data.get(), "drawn", | |
98 false); | |
99 } | 99 } |
100 frames_.push(frame); | 100 |
101 } | 101 // TODO(jeffbrown): If the draw queue is full, we should pause |
102 | 102 // scheduling until the queue drains. |
103 if (was_empty) | 103 if (shared_state_.rasterizer_ready && |
104 PostSubmit(); | 104 shared_state_.drawn_frames_awaiting_finish.size() < pipeline_depth_) |
105 } | 105 ScheduleDrawLocked(); |
106 | 106 } |
107 void GpuOutput::RasterizerDelegate::PostSubmit() { | 107 } |
108 TRACE_EVENT0("gfx", "GpuOutput::RasterizerDelegate::PostSubmit"); | 108 |
109 task_runner_->PostTask(FROM_HERE, base::Bind(&RasterizerDelegate::SubmitTask, | 109 void GpuOutput::OnRasterizerReady(int64_t vsync_timebase, |
110 base::Unretained(this))); | 110 int64_t vsync_interval) { |
111 } | 111 DCHECK(rasterizer_task_runner_->RunsTasksOnCurrentThread()); |
112 | 112 |
113 void GpuOutput::RasterizerDelegate::InitializeTask( | 113 // TODO(jeffbrown): This shouldn't be hardcoded. |
114 mojo::InterfaceHandle<mojo::ContextProvider> context_provider, | 114 // Need to do some real tuning and possibly determine values adaptively. |
115 const scoped_refptr<VsyncScheduler>& scheduler, | 115 // We should probably split the Start() method in two to separate the |
116 const scoped_refptr<base::TaskRunner>& task_runner, | 116 // process of setting parameters from starting / stopping scheduling. |
117 const base::Closure& error_callback) { | 117 const int64_t update_phase = -vsync_interval; |
118 const int64_t snapshot_phase = -vsync_interval / 6; | |
119 // TODO(jeffbrown): Determine the presentation phase based on queue depth. | |
120 const int64_t presentation_phase = vsync_interval * pipeline_depth_; | |
121 if (!vsync_scheduler_->Start(vsync_timebase, vsync_interval, update_phase, | |
122 snapshot_phase, presentation_phase)) { | |
123 LOG(ERROR) << "Received invalid vsync parameters: timebase=" | |
124 << vsync_timebase << ", interval=" << vsync_interval; | |
125 PostErrorCallback(); | |
126 return; | |
127 } | |
128 | |
129 { | |
130 std::lock_guard<std::mutex> lock(shared_state_.mutex); | |
131 | |
132 if (shared_state_.rasterizer_ready) | |
133 return; | |
134 | |
135 DCHECK(shared_state_.drawn_frames_awaiting_finish.empty()); | |
136 shared_state_.rasterizer_ready = true; | |
137 | |
138 if (!shared_state_.current_frame_data) | |
139 return; | |
140 | |
141 shared_state_.current_frame_data->Recycle(); | |
142 TRACE_EVENT_FLOW_BEGIN0("gfx", "Frame Queued", | |
143 shared_state_.current_frame_data.get()); | |
144 ScheduleDrawLocked(); | |
145 } | |
146 } | |
147 | |
148 void GpuOutput::OnRasterizerSuspended() { | |
149 DCHECK(rasterizer_task_runner_->RunsTasksOnCurrentThread()); | |
150 | |
151 vsync_scheduler_->Stop(); | |
152 | |
153 { | |
154 std::lock_guard<std::mutex> lock(shared_state_.mutex); | |
155 | |
156 if (!shared_state_.rasterizer_ready) | |
157 return; | |
158 | |
159 shared_state_.rasterizer_ready = false; | |
160 } | |
161 } | |
162 | |
163 void GpuOutput::OnRasterizerError() { | |
164 DCHECK(rasterizer_task_runner_->RunsTasksOnCurrentThread()); | |
165 | |
166 PostErrorCallback(); | |
167 } | |
168 | |
169 void GpuOutput::ScheduleDrawLocked() { | |
170 DCHECK(shared_state_.current_frame_data); | |
171 DCHECK(!shared_state_.current_frame_data->draw_time); | |
172 | |
173 if (shared_state_.draw_scheduled) | |
174 return; | |
175 | |
176 shared_state_.draw_scheduled = true; | |
177 rasterizer_task_runner_->PostTask( | |
178 FROM_HERE, base::Bind(&GpuOutput::OnDraw, base::Unretained(this))); | |
179 } | |
180 | |
181 void GpuOutput::OnDraw() { | |
182 DCHECK(rasterizer_task_runner_->RunsTasksOnCurrentThread()); | |
183 TRACE_EVENT0("gfx", "GpuOutput::OnDraw"); | |
184 | |
185 scoped_refptr<FrameData> frame_data; // used outside lock | |
186 { | |
187 std::lock_guard<std::mutex> lock(shared_state_.mutex); | |
188 | |
189 DCHECK(shared_state_.draw_scheduled); | |
190 DCHECK(shared_state_.current_frame_data); | |
191 DCHECK(!shared_state_.current_frame_data->draw_time); | |
192 | |
193 shared_state_.draw_scheduled = false; | |
194 | |
195 if (!shared_state_.rasterizer_ready || | |
196 shared_state_.drawn_frames_awaiting_finish.size() >= pipeline_depth_) | |
197 return; | |
198 | |
199 frame_data = shared_state_.current_frame_data; | |
200 frame_data->draw_time = MojoGetTimeTicksNow(); | |
201 TRACE_EVENT_FLOW_END1("gfx", "Frame Queued", frame_data.get(), "drawn", | |
202 true); | |
203 | |
204 TRACE_EVENT_ASYNC_BEGIN0("gfx", "Rasterize", frame_data.get()); | |
205 shared_state_.drawn_frames_awaiting_finish.emplace(frame_data); | |
206 } | |
207 | |
208 rasterizer_->DrawFrame(frame_data->frame); | |
209 frame_data->wait_time = MojoGetTimeTicksNow(); | |
210 } | |
211 | |
212 void GpuOutput::OnRasterizerFinishedDraw(bool presented) { | |
213 DCHECK(rasterizer_task_runner_->RunsTasksOnCurrentThread()); | |
214 TRACE_EVENT0("gfx", "GpuOutput::OnRasterizerFinishedDraw"); | |
215 | |
216 const int64_t finish_time = MojoGetTimeTicksNow(); | |
217 scoped_refptr<FrameData> frame_data; // drop outside lock | |
218 { | |
219 std::lock_guard<std::mutex> lock(shared_state_.mutex); | |
220 | |
221 DCHECK(shared_state_.rasterizer_ready); | |
222 DCHECK(!shared_state_.drawn_frames_awaiting_finish.empty()); | |
223 size_t draw_queue_depth = shared_state_.drawn_frames_awaiting_finish.size(); | |
224 shared_state_.drawn_frames_awaiting_finish.front().swap(frame_data); | |
225 shared_state_.drawn_frames_awaiting_finish.pop(); | |
226 DCHECK(frame_data); | |
227 DCHECK(frame_data->draw_time); | |
228 TRACE_EVENT_ASYNC_END1("gfx", "Rasterize", frame_data.get(), "presented", | |
229 presented); | |
230 | |
231 // TODO(jeffbrown): Adjust scheduler behavior based on observed timing. | |
232 // Note: These measurements don't account for systematic downstream delay | |
233 // in the display pipeline (how long it takes pixels to actually light up). | |
234 if (presented) { | |
235 const RenderFrame::Metadata& frame_metadata = | |
236 frame_data->frame->metadata(); | |
237 const mojo::gfx::composition::FrameInfo& frame_info = | |
238 frame_metadata.frame_info(); | |
239 const int64_t frame_time = frame_info.frame_time; | |
240 const int64_t presentation_time = frame_info.presentation_time; | |
241 const int64_t composition_time = frame_metadata.composition_time(); | |
242 const int64_t draw_time = frame_data->draw_time; | |
243 const int64_t wait_time = frame_data->wait_time; | |
244 const int64_t submit_time = frame_data->submit_time; | |
245 | |
246 DVLOG(2) << "Presented frame: composition latency " | |
247 << (composition_time - frame_time) << " us, submission latency " | |
248 << (submit_time - composition_time) << " us, queue latency " | |
249 << (draw_time - submit_time) << " us, draw latency " | |
250 << (wait_time - draw_time) << " us, GPU latency " | |
251 << (finish_time - wait_time) << " us, total latency " | |
252 << (finish_time - frame_time) << " us, presentation time error " | |
253 << (finish_time - presentation_time) << " us" | |
254 << ", draw queue depth " << draw_queue_depth; | |
255 } else { | |
256 DVLOG(2) << "Rasterizer dropped frame."; | |
257 } | |
258 | |
259 DCHECK(shared_state_.current_frame_data); | |
260 if (!shared_state_.current_frame_data->draw_time) | |
mikejurka
2016/05/19 23:26:18
again, what does it mean if draw_time is 0? how do
jeffbrown
2016/05/20 01:09:47
It's set in OnDraw() above. Basically means DrawF
| |
261 ScheduleDrawLocked(); | |
262 } | |
263 } | |
264 | |
265 void GpuOutput::InitializeRasterizer( | |
266 mojo::InterfaceHandle<mojo::ContextProvider> context_provider) { | |
267 DCHECK(rasterizer_task_runner_->RunsTasksOnCurrentThread()); | |
268 DCHECK(!rasterizer_); | |
269 TRACE_EVENT0("gfx", "GpuOutput::InitializeRasterizer"); | |
270 | |
118 rasterizer_.reset(new GpuRasterizer( | 271 rasterizer_.reset(new GpuRasterizer( |
119 mojo::ContextProviderPtr::Create(std::move(context_provider)), scheduler, | 272 mojo::ContextProviderPtr::Create(std::move(context_provider)), this)); |
120 task_runner, error_callback)); | 273 rasterizer_initialized_.Signal(); |
121 } | 274 } |
122 | 275 |
123 void GpuOutput::RasterizerDelegate::SubmitTask() { | 276 void GpuOutput::DestroyRasterizer() { |
124 TRACE_EVENT0("gfx", "GpuOutput::RasterizerDelegate::SubmitTask"); | 277 DCHECK(rasterizer_task_runner_->RunsTasksOnCurrentThread()); |
125 bool have_more; | 278 DCHECK(rasterizer_); |
126 scoped_refptr<RenderFrame> frame; | 279 TRACE_EVENT0("gfx", "GpuOutput::DestroyRasterizer"); |
127 { | 280 |
128 std::lock_guard<std::mutex> lock(mutex_); | 281 rasterizer_.reset(); |
129 DCHECK(!frames_.empty()); | 282 rasterizer_initialized_.Reset(); |
130 frame = frames_.front(); | 283 } |
131 frames_.pop(); | 284 |
132 have_more = !frames_.empty(); | 285 void GpuOutput::PostErrorCallback() { |
133 } | 286 compositor_task_runner_->PostTask(FROM_HERE, error_callback_); |
134 | 287 } |
135 if (have_more) | 288 |
136 PostSubmit(); | 289 GpuOutput::FrameData::FrameData(const scoped_refptr<RenderFrame>& frame, |
137 | 290 int64_t submit_time) |
138 int64_t submit_time = MojoGetTimeTicksNow(); | 291 : frame(frame), submit_time(submit_time), draw_time(0), wait_time(0) {} |
mikejurka
2016/05/20 01:49:23
shouldn't we still initialize these guys to 0?
jeffbrown
2016/05/20 19:31:35
It's done in the header file.
| |
139 rasterizer_->SubmitFrame( | 292 |
140 frame, base::Bind(&RasterizerDelegate::OnFrameSubmitted, | 293 GpuOutput::FrameData::~FrameData() {} |
141 base::Unretained(this), frame->frame_info().frame_time, | 294 |
142 frame->frame_info().presentation_time, submit_time)); | 295 void GpuOutput::FrameData::Recycle() { |
143 } | 296 draw_time = 0; |
144 | 297 wait_time = 0; |
145 void GpuOutput::RasterizerDelegate::OnFrameSubmitted(int64_t frame_time, | |
146 int64_t presentation_time, | |
147 int64_t submit_time, | |
148 bool presented) { | |
149 TRACE_EVENT0("gfx", "GpuOutput::RasterizerDelegate::OnFrameSubmitted"); | |
150 // TODO(jeffbrown): Adjust scheduler behavior based on observed timing. | |
151 // Note: These measurements don't account for systematic downstream delay | |
152 // in the display pipeline (how long it takes pixels to actually light up). | |
153 int64_t complete_time = MojoGetTimeTicksNow(); | |
154 if (presented) { | |
155 DVLOG(3) << "Frame presented: submission latency " | |
156 << (submit_time - frame_time) << " us, rasterization latency " | |
157 << (complete_time - submit_time) << " us, total latency " | |
158 << (complete_time - frame_time) << " us, presentation time error " | |
159 << (complete_time - presentation_time); | |
160 } else { | |
161 DVLOG(3) << "Frame deferred."; | |
162 } | |
163 } | 298 } |
164 | 299 |
165 } // namespace compositor | 300 } // namespace compositor |
OLD | NEW |