Chromium Code Reviews| 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 |