OLD | NEW |
---|---|
(Empty) | |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 #include "services/gfx/compositor/compositor_engine.h" | |
6 | |
7 #include <algorithm> | |
8 #include <sstream> | |
9 #include <utility> | |
10 | |
11 #include "base/bind.h" | |
12 #include "base/bind_helpers.h" | |
13 #include "base/time/time.h" | |
14 #include "mojo/services/gfx/composition/cpp/logging.h" | |
15 #include "services/gfx/compositor/backend/gpu_output.h" | |
16 #include "services/gfx/compositor/graph/snapshot.h" | |
17 #include "services/gfx/compositor/render/render_frame.h" | |
18 #include "services/gfx/compositor/renderer_impl.h" | |
19 #include "services/gfx/compositor/scene_impl.h" | |
20 | |
21 namespace compositor { | |
22 | |
23 // TODO(jeffbrown): Determine and document a more appropriate size limit | |
24 // for viewports somewhere. May be limited by the renderer output. | |
25 static const int32_t kMaxViewportWidth = 65536; | |
26 static const int32_t kMaxViewportHeight = 65536; | |
27 | |
28 CompositorEngine::CompositorEngine() : weak_factory_(this) {} | |
29 | |
30 CompositorEngine::~CompositorEngine() {} | |
31 | |
32 mojo::gfx::composition::SceneTokenPtr CompositorEngine::CreateScene( | |
33 mojo::InterfaceRequest<mojo::gfx::composition::Scene> scene_request, | |
34 const mojo::String& label) { | |
35 auto scene_token = mojo::gfx::composition::SceneToken::New(); | |
36 scene_token->value = next_scene_token_value_++; | |
37 CHECK(scene_token->value); | |
38 CHECK(!FindScene(scene_token->value)); | |
39 | |
40 // Create the state and bind implementation to it. | |
41 std::string sanitized_label = | |
42 label.get().substr(0, mojo::gfx::composition::kLabelMaxLength); | |
43 SceneState* scene_state = new SceneState(scene_token.Pass(), sanitized_label); | |
44 SceneImpl* scene_impl = | |
45 new SceneImpl(this, scene_state, scene_request.Pass()); | |
46 scene_state->set_scene_impl(scene_impl); | |
47 base::Closure error_handler = | |
48 base::Bind(&CompositorEngine::OnSceneConnectionError, | |
49 base::Unretained(this), scene_state); | |
50 scene_impl->set_connection_error_handler(error_handler); | |
51 | |
52 // Add to registry. | |
53 scenes_by_token_.insert({scene_state->scene_token()->value, scene_state}); | |
54 DVLOG(1) << "CreateScene: scene=" << scene_state; | |
55 return scene_state->scene_token()->Clone(); | |
56 } | |
57 | |
58 void CompositorEngine::OnSceneConnectionError(SceneState* scene_state) { | |
59 DCHECK(IsSceneStateRegisteredDebug(scene_state)); | |
60 DVLOG(1) << "OnSceneConnectionError: scene=" << scene_state; | |
61 | |
62 DestroyScene(scene_state); | |
63 } | |
64 | |
65 void CompositorEngine::DestroyScene(SceneState* scene_state) { | |
66 DCHECK(IsSceneStateRegisteredDebug(scene_state)); | |
67 DVLOG(1) << "DestroyScene: scene=" << scene_state; | |
68 | |
69 // Unlink from other scenes. | |
70 for (auto& pair : scenes_by_token_) { | |
71 SceneState* other_scene_state = pair.second; | |
72 other_scene_state->scene_def()->UnlinkReferencedScene( | |
73 scene_state->scene_def(), | |
74 base::Bind(&CompositorEngine::SendResourceUnavailable, | |
75 base::Unretained(this), | |
76 base::Unretained(other_scene_state))); | |
77 } | |
78 | |
79 // Destroy any renderers using this scene. | |
80 for (auto& renderer : renderers_) { | |
81 if (renderer->root_scene() == scene_state) { | |
82 LOG(ERROR) << "Destroying renderer whose root scene has become " | |
83 "unavailable: renderer=" | |
84 << renderer; | |
85 DestroyRenderer(renderer); | |
86 } | |
87 } | |
88 | |
89 // Destroy. | |
90 InvalidateScene(scene_state); | |
91 | |
92 // Remove from registry. | |
93 scenes_by_token_.erase(scene_state->scene_token()->value); | |
94 delete scene_state; | |
95 } | |
96 | |
97 void CompositorEngine::CreateRenderer( | |
98 mojo::ContextProviderPtr context_provider, | |
99 mojo::InterfaceRequest<mojo::gfx::composition::Renderer> renderer_request, | |
100 const mojo::String& label) { | |
101 DCHECK(context_provider); | |
102 uint32_t renderer_id = next_renderer_id_++; | |
103 | |
104 // Create the state and bind implementation to it. | |
105 std::string sanitized_label = | |
106 label.get().substr(0, mojo::gfx::composition::kLabelMaxLength); | |
abarth
2016/01/10 22:55:35
This pattern recurs a bunch. Maybe factor it out
jeffbrown
2016/01/16 03:28:31
Done.
| |
107 RendererState* renderer_state = | |
108 new RendererState(renderer_id, sanitized_label); | |
109 RendererImpl* renderer_impl = | |
110 new RendererImpl(this, renderer_state, renderer_request.Pass()); | |
111 renderer_state->set_renderer_impl(renderer_impl); | |
112 renderer_impl->set_connection_error_handler( | |
113 base::Bind(&CompositorEngine::OnRendererConnectionError, | |
114 base::Unretained(this), renderer_state)); | |
115 | |
116 // Create the renderer. | |
117 SchedulerCallbacks scheduler_callbacks( | |
118 base::Bind(&CompositorEngine::OnOutputUpdateRequest, | |
119 weak_factory_.GetWeakPtr(), renderer_state->GetWeakPtr()), | |
120 base::Bind(&CompositorEngine::OnOutputSnapshotRequest, | |
121 weak_factory_.GetWeakPtr(), renderer_state->GetWeakPtr())); | |
122 std::unique_ptr<Output> output(new GpuOutput( | |
123 context_provider.Pass(), scheduler_callbacks, | |
124 base::Bind(&CompositorEngine::OnOutputError, weak_factory_.GetWeakPtr(), | |
125 renderer_state->GetWeakPtr()))); | |
126 renderer_state->set_output(std::move(output)); | |
127 | |
128 // Add to registry. | |
129 renderers_.push_back(renderer_state); | |
130 DVLOG(1) << "CreateRenderer: " << renderer_state; | |
131 } | |
132 | |
133 void CompositorEngine::OnRendererConnectionError( | |
134 RendererState* renderer_state) { | |
135 DCHECK(IsRendererStateRegisteredDebug(renderer_state)); | |
136 DVLOG(1) << "OnRendererConnectionError: renderer=" << renderer_state; | |
137 | |
138 DestroyRenderer(renderer_state); | |
139 } | |
140 | |
141 void CompositorEngine::DestroyRenderer(RendererState* renderer_state) { | |
142 DCHECK(IsRendererStateRegisteredDebug(renderer_state)); | |
143 DVLOG(1) << "DestroyRenderer: renderer=" << renderer_state; | |
144 | |
145 // Remove from registry. | |
146 renderers_.erase( | |
147 std::find(renderers_.begin(), renderers_.end(), renderer_state)); | |
148 delete renderer_state; | |
149 } | |
150 | |
151 void CompositorEngine::SetListener( | |
152 SceneState* scene_state, | |
153 mojo::gfx::composition::SceneListenerPtr listener) { | |
154 DCHECK(IsSceneStateRegisteredDebug(scene_state)); | |
155 DVLOG(1) << "SetSceneListener: scene=" << scene_state; | |
156 | |
157 scene_state->set_scene_listener(listener.Pass()); | |
158 } | |
159 | |
160 void CompositorEngine::Update(SceneState* scene_state, | |
161 mojo::gfx::composition::SceneUpdatePtr update) { | |
162 DCHECK(IsSceneStateRegisteredDebug(scene_state)); | |
163 DVLOG(1) << "Update: scene=" << scene_state << ", update=" << update; | |
164 | |
165 scene_state->scene_def()->EnqueueUpdate(update.Pass()); | |
166 } | |
167 | |
168 void CompositorEngine::Publish( | |
169 SceneState* scene_state, | |
170 mojo::gfx::composition::SceneMetadataPtr metadata) { | |
171 DCHECK(IsSceneStateRegisteredDebug(scene_state)); | |
172 DVLOG(1) << "Publish: scene=" << scene_state << ", metadata=" << metadata; | |
173 | |
174 if (!metadata) | |
175 metadata = mojo::gfx::composition::SceneMetadata::New(); | |
176 int64_t presentation_time = metadata->presentation_time; | |
177 scene_state->scene_def()->EnqueuePublish(metadata.Pass()); | |
178 | |
179 // Implicitly schedule fresh snapshots. | |
180 InvalidateScene(scene_state); | |
181 | |
182 // Ensure that the scene will be presented eventually, even if it is | |
183 // not associated with any renderer. Note that this is only a backstop | |
184 // in case the scene does not get presented sooner as part of snapshotting | |
185 // a renderer. | |
186 MojoTimeTicks now = MojoGetTimeTicksNow(); | |
187 DCHECK(now >= 0); | |
188 if (presentation_time <= now) { | |
189 SceneDef::Disposition disposition = PresentScene(scene_state, now); | |
190 if (disposition == SceneDef::Disposition::kFailed) | |
191 DestroyScene(scene_state); | |
192 } else { | |
193 base::MessageLoop::current()->PostDelayedTask( | |
194 FROM_HERE, base::Bind(&CompositorEngine::OnPresentScene, | |
195 weak_factory_.GetWeakPtr(), | |
196 scene_state->GetWeakPtr(), presentation_time), | |
197 base::TimeDelta::FromMicroseconds(presentation_time - now)); | |
abarth
2016/01/10 22:55:35
Do we need to schedule this for earlier so that it
jeffbrown
2016/01/16 03:28:32
It actually doesn't matter here. This only happen
| |
198 } | |
199 } | |
200 | |
201 void CompositorEngine::ScheduleFrame(SceneState* scene_state, | |
202 const SceneFrameCallback& callback) { | |
203 DCHECK(IsSceneStateRegisteredDebug(scene_state)); | |
204 | |
205 scene_state->AddSceneFrameCallback(callback); | |
206 | |
207 // TODO(jeffbrown): Be more selective and do this work only for scenes | |
208 // which are strongly associated with the renderer so it doesn't receive | |
209 // conflicting timing signals coming from multiple renderers. | |
210 for (auto& renderer : renderers_) { | |
211 ScheduleFrameForRenderer(renderer, SchedulingMode::kUpdateAndSnapshot); | |
212 } | |
213 } | |
214 | |
215 void CompositorEngine::SetRootScene( | |
216 RendererState* renderer_state, | |
217 mojo::gfx::composition::SceneTokenPtr scene_token, | |
218 uint32 scene_version, | |
219 mojo::RectPtr viewport) { | |
220 DCHECK(IsRendererStateRegisteredDebug(renderer_state)); | |
221 DCHECK(scene_token); | |
222 DCHECK(viewport); | |
223 DVLOG(1) << "SetRootScene: renderer=" << renderer_state | |
224 << ", scene_token=" << scene_token | |
225 << ", scene_version=" << scene_version << ", viewport=" << viewport; | |
226 | |
227 if (viewport->width < 1 || viewport->width > kMaxViewportWidth || | |
228 viewport->height < 1 || viewport->height > kMaxViewportHeight) { | |
abarth
2016/01/10 22:55:35
nit: I would have tested <= 0
jeffbrown
2016/01/16 03:28:31
Done.
| |
229 LOG(ERROR) << "Invalid viewport size: " << viewport; | |
230 DestroyRenderer(renderer_state); | |
231 return; | |
232 } | |
233 | |
234 // Find the scene. | |
235 SceneState* scene_state = FindScene(scene_token->value); | |
236 if (!scene_state) { | |
237 LOG(ERROR) << "Could not set the renderer's root scene, scene not found: " | |
238 "scene_token=" | |
239 << scene_token; | |
240 DestroyRenderer(renderer_state); | |
241 return; | |
242 } | |
243 | |
244 // Update the root. | |
245 if (renderer_state->SetRootScene(scene_state, scene_version, *viewport)) { | |
246 ScheduleFrameForRenderer(renderer_state, SchedulingMode::kSnapshot); | |
247 } | |
248 } | |
249 | |
250 void CompositorEngine::HitTest( | |
251 RendererState* renderer_state, | |
252 mojo::PointPtr point, | |
253 const mojo::gfx::composition::HitTester::HitTestCallback& callback) { | |
254 DCHECK(IsRendererStateRegisteredDebug(renderer_state)); | |
255 DCHECK(point); | |
256 DVLOG(1) << "HitTest: renderer=" << renderer_state << ", point=" << point; | |
257 | |
258 if (renderer_state->frame()) { | |
259 callback.Run( | |
260 renderer_state->frame()->HitTest(SkPoint::Make(point->x, point->y))); | |
261 } else { | |
262 callback.Run(nullptr); | |
abarth
2016/01/10 22:55:35
It's odd that there are two "failed" results here:
jeffbrown
2016/01/16 03:28:32
Good point.
| |
263 } | |
264 } | |
265 | |
266 SceneDef* CompositorEngine::ResolveSceneReference( | |
267 mojo::gfx::composition::SceneToken* scene_token) { | |
268 SceneState* scene_state = FindScene(scene_token->value); | |
269 return scene_state ? scene_state->scene_def() : nullptr; | |
270 } | |
271 | |
272 void CompositorEngine::SendResourceUnavailable(SceneState* scene_state, | |
273 uint32_t resource_id) { | |
274 DCHECK(IsSceneStateRegisteredDebug(scene_state)); | |
275 DVLOG(2) << "SendResourceUnavailable: resource_id=" << resource_id; | |
276 | |
277 // TODO: Detect ANRs | |
abarth
2016/01/10 22:55:35
ANRs?
jeffbrown
2016/01/16 03:28:31
"Application Not Responding"
| |
278 if (scene_state->scene_listener()) | |
abarth
2016/01/10 22:55:35
Multiline ifs require { }
jeffbrown
2016/01/16 03:28:32
Done.
| |
279 scene_state->scene_listener()->OnResourceUnavailable( | |
280 resource_id, base::Bind(&base::DoNothing)); | |
281 } | |
282 | |
283 SceneState* CompositorEngine::FindScene(uint32_t scene_token) { | |
284 auto it = scenes_by_token_.find(scene_token); | |
285 return it != scenes_by_token_.end() ? it->second : nullptr; | |
286 } | |
287 | |
288 void CompositorEngine::InvalidateScene(SceneState* scene_state) { | |
289 DCHECK(IsSceneStateRegisteredDebug(scene_state)); | |
290 DVLOG(2) << "InvalidateScene: scene=" << scene_state; | |
291 | |
292 for (auto& renderer : renderers_) { | |
293 if (renderer->snapshot() && | |
294 renderer->snapshot()->InvalidateScene(scene_state->scene_def())) { | |
295 ScheduleFrameForRenderer(renderer, SchedulingMode::kSnapshot); | |
296 } | |
297 } | |
298 } | |
299 | |
300 SceneDef::Disposition CompositorEngine::PresentScene( | |
301 SceneState* scene_state, | |
302 int64_t presentation_time) { | |
303 DCHECK(IsSceneStateRegisteredDebug(scene_state)); | |
304 DVLOG(2) << "PresentScene: scene=" << scene_state; | |
305 | |
306 std::ostringstream errs; | |
307 SceneDef::Disposition disposition = scene_state->scene_def()->Present( | |
308 presentation_time, base::Bind(&CompositorEngine::ResolveSceneReference, | |
309 base::Unretained(this)), | |
310 base::Bind(&CompositorEngine::SendResourceUnavailable, | |
311 base::Unretained(this), base::Unretained(scene_state)), | |
312 errs); | |
313 if (disposition == SceneDef::Disposition::kFailed) { | |
314 LOG(ERROR) << "Scene published invalid updates: scene=" << scene_state; | |
315 LOG(ERROR) << errs.str(); | |
316 // Caller is responsible for destroying the scene. | |
317 } | |
318 return disposition; | |
319 } | |
320 | |
321 void CompositorEngine::PresentRenderer(RendererState* renderer_state, | |
322 int64_t presentation_time) { | |
323 DCHECK(IsRendererStateRegisteredDebug(renderer_state)); | |
324 DVLOG(2) << "PresentRenderer: renderer_state=" << renderer_state; | |
325 | |
326 // TODO(jeffbrown): Be more selective and do this work only for scenes | |
327 // associated with the renderer that actually have pending updates. | |
328 std::vector<SceneState*> dead_scenes; | |
329 for (auto& pair : scenes_by_token_) { | |
330 SceneState* scene_state = pair.second; | |
331 SceneDef::Disposition disposition = | |
332 PresentScene(scene_state, presentation_time); | |
333 if (disposition == SceneDef::Disposition::kFailed) | |
334 dead_scenes.push_back(scene_state); | |
335 } | |
336 for (SceneState* scene_state : dead_scenes) | |
337 DestroyScene(scene_state); | |
abarth
2016/01/10 22:55:35
Should we DCHECK that scene_state isn't in scenes_
jeffbrown
2016/01/16 03:28:32
You mean after DestroyScene returns? I don't thin
| |
338 } | |
339 | |
340 void CompositorEngine::SnapshotRenderer( | |
341 RendererState* renderer_state, | |
342 const mojo::gfx::composition::FrameInfo& frame_info) { | |
343 DCHECK(IsRendererStateRegisteredDebug(renderer_state)); | |
344 DVLOG(2) << "SnapshotRenderer: renderer_state=" << renderer_state; | |
345 | |
346 if (VLOG_IS_ON(2)) { | |
347 std::ostringstream block_log; | |
348 SnapshotRendererInner(renderer_state, frame_info, &block_log); | |
349 if (!renderer_state->valid()) { | |
350 DVLOG(2) << "Rendering completely blocked: " << block_log.str(); | |
351 } else if (!block_log.str().empty()) { | |
352 DVLOG(2) << "Rendering partially blocked: " << block_log.str(); | |
353 } else { | |
354 DVLOG(2) << "Rendering unblocked"; | |
355 } | |
356 } else { | |
357 SnapshotRendererInner(renderer_state, frame_info, nullptr); | |
358 } | |
359 } | |
360 | |
361 void CompositorEngine::SnapshotRendererInner( | |
362 RendererState* renderer_state, | |
363 const mojo::gfx::composition::FrameInfo& frame_info, | |
364 std::ostream* block_log) { | |
365 if (!renderer_state->root_scene()) { | |
366 if (block_log) | |
367 *block_log << "No root scene" << std::endl; | |
368 renderer_state->SetSnapshot(nullptr); | |
369 return; | |
370 } | |
371 | |
372 SnapshotBuilder builder(block_log); | |
373 renderer_state->SetSnapshot( | |
374 builder.Build(renderer_state->root_scene()->scene_def(), | |
375 renderer_state->root_scene_viewport(), frame_info)); | |
376 | |
377 if (renderer_state->valid()) | |
378 renderer_state->output()->SubmitFrame(renderer_state->frame()); | |
379 } | |
380 | |
381 void CompositorEngine::ScheduleFrameForRenderer( | |
382 RendererState* renderer_state, | |
383 SchedulingMode scheduling_mode) { | |
384 DCHECK(IsRendererStateRegisteredDebug(renderer_state)); | |
385 renderer_state->output()->scheduler()->ScheduleFrame(scheduling_mode); | |
386 } | |
387 | |
388 void CompositorEngine::OnOutputError( | |
389 const base::WeakPtr<RendererState>& renderer_state_weak) { | |
390 RendererState* renderer_state = renderer_state_weak.get(); | |
391 if (!renderer_state) | |
392 return; | |
393 DCHECK(IsRendererStateRegisteredDebug(renderer_state)); | |
394 | |
395 LOG(ERROR) << "Renderer encountered a fatal error: renderer=" | |
396 << renderer_state; | |
397 | |
398 DestroyRenderer(renderer_state); | |
399 } | |
400 | |
401 void CompositorEngine::OnOutputUpdateRequest( | |
402 const base::WeakPtr<RendererState>& renderer_state_weak, | |
403 const mojo::gfx::composition::FrameInfo& frame_info) { | |
404 RendererState* renderer_state = renderer_state_weak.get(); | |
405 if (!renderer_state) | |
406 return; | |
407 DCHECK(IsRendererStateRegisteredDebug(renderer_state)); | |
408 | |
409 // TODO(jeffbrown): Be more selective and do this work only for scenes | |
410 // associated with the renderer. | |
411 for (auto& pair : scenes_by_token_) { | |
412 pair.second->DispatchSceneFrameCallbacks(frame_info); | |
413 } | |
414 } | |
415 | |
416 void CompositorEngine::OnOutputSnapshotRequest( | |
417 const base::WeakPtr<RendererState>& renderer_state_weak, | |
418 const mojo::gfx::composition::FrameInfo& frame_info) { | |
419 RendererState* renderer_state = renderer_state_weak.get(); | |
420 if (!renderer_state) | |
421 return; | |
422 DCHECK(IsRendererStateRegisteredDebug(renderer_state)); | |
423 | |
424 PresentRenderer(renderer_state, frame_info.presentation_time); | |
425 SnapshotRenderer(renderer_state, frame_info); | |
426 } | |
427 | |
428 void CompositorEngine::OnPresentScene( | |
429 const base::WeakPtr<SceneState>& scene_state_weak, | |
430 int64_t presentation_time) { | |
431 SceneState* scene_state = scene_state_weak.get(); | |
432 if (!scene_state) | |
433 return; | |
434 DCHECK(IsSceneStateRegisteredDebug(scene_state)); | |
435 | |
436 SceneDef::Disposition disposition = | |
437 PresentScene(scene_state, presentation_time); | |
438 if (disposition == SceneDef::Disposition::kFailed) | |
439 DestroyScene(scene_state); | |
440 else if (disposition == SceneDef::Disposition::kSucceeded) | |
441 InvalidateScene(scene_state); | |
442 } | |
443 | |
444 } // namespace compositor | |
OLD | NEW |