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/graph/scene_def.h" | |
| 6 | |
| 7 #include <ostream> | |
| 8 #include <sstream> | |
| 9 | |
| 10 #include "base/bind.h" | |
| 11 #include "base/logging.h" | |
| 12 #include "base/message_loop/message_loop.h" | |
| 13 #include "mojo/services/gfx/composition/cpp/logging.h" | |
| 14 #include "services/gfx/compositor/graph/snapshot.h" | |
| 15 #include "services/gfx/compositor/render/render_image.h" | |
| 16 #include "services/gfx/compositor/render/render_layer.h" | |
| 17 | |
| 18 namespace compositor { | |
| 19 | |
| 20 // TODO(jeffbrown): Determine and document a more appropriate size limit | |
| 21 // for transferred images as part of the image pipe abstraction instead. | |
| 22 static const int32_t kMaxTextureWidth = 65536; | |
| 23 static const int32_t kMaxTextureHeight = 65536; | |
| 24 | |
| 25 static void ReleaseMailboxTexture( | |
| 26 mojo::gfx::composition::MailboxTextureCallbackPtr callback) { | |
| 27 if (callback) | |
| 28 callback->OnMailboxTextureReleased(); | |
| 29 } | |
| 30 | |
| 31 SceneDef::SceneDef(mojo::gfx::composition::SceneTokenPtr scene_token, | |
| 32 const std::string& label) | |
| 33 : scene_token_(scene_token.Pass()), label_(label) { | |
| 34 DCHECK(scene_token_); | |
| 35 } | |
| 36 | |
| 37 SceneDef::~SceneDef() {} | |
| 38 | |
| 39 void SceneDef::EnqueueUpdate(mojo::gfx::composition::SceneUpdatePtr update) { | |
| 40 DCHECK(update); | |
| 41 pending_updates_.push_back(update.Pass()); | |
| 42 } | |
| 43 | |
| 44 void SceneDef::EnqueuePublish( | |
| 45 mojo::gfx::composition::SceneMetadataPtr metadata) { | |
| 46 DCHECK(metadata); | |
| 47 pending_publications_.emplace_back(new Publication(metadata.Pass())); | |
| 48 pending_updates_.swap(pending_publications_.back()->updates); | |
| 49 } | |
| 50 | |
| 51 SceneDef::Disposition SceneDef::Present( | |
| 52 int64_t presentation_time, | |
| 53 const SceneResolver& resolver, | |
| 54 const SceneUnavailableSender& unavailable_sender, | |
| 55 std::ostream& err) { | |
| 56 // Walk backwards through the pending publications to find the index | |
| 57 // just beyond the last one which is due to be presented at or before the | |
| 58 // presentation time. | |
| 59 size_t end = pending_publications_.size(); | |
| 60 for (;;) { | |
| 61 if (!end) | |
| 62 return Disposition::kUnchanged; | |
| 63 if (pending_publications_[end - 1]->is_due(presentation_time)) | |
| 64 break; // found last presentable publication | |
| 65 end--; | |
| 66 } | |
| 67 | |
| 68 // Prepare to apply all publications up to this point. | |
| 69 uint32_t version = pending_publications_[end - 1]->metadata->version; | |
| 70 if (version_ != version) { | |
| 71 version_ = version; | |
| 72 formatted_label_cache_.clear(); | |
| 73 } | |
| 74 Invalidate(); | |
| 75 | |
| 76 // Apply all updates sequentially. | |
| 77 for (size_t index = 0; index < end; ++index) { | |
| 78 for (auto& update : pending_publications_[index]->updates) { | |
| 79 if (!ApplyUpdate(update.Pass(), resolver, unavailable_sender, err)) | |
| 80 return Disposition::kFailed; | |
| 81 } | |
| 82 } | |
| 83 | |
| 84 // Dequeue the publications we processed. | |
| 85 pending_publications_.erase(pending_publications_.begin(), | |
| 86 pending_publications_.begin() + end); | |
| 87 | |
| 88 // Ensure the scene is in a valid state. | |
| 89 if (!Validate(err)) | |
| 90 return Disposition::kFailed; | |
| 91 return Disposition::kSucceeded; | |
| 92 } | |
| 93 | |
| 94 bool SceneDef::ApplyUpdate(mojo::gfx::composition::SceneUpdatePtr update, | |
| 95 const SceneResolver& resolver, | |
| 96 const SceneUnavailableSender& unavailable_sender, | |
| 97 std::ostream& err) { | |
| 98 DCHECK(update); | |
| 99 | |
| 100 // Update resources. | |
| 101 if (update->clear_resources) { | |
| 102 resources_.clear(); | |
| 103 } | |
| 104 for (auto it = update->resources.begin(); it != update->resources.end(); | |
| 105 ++it) { | |
| 106 uint32_t resource_id = it.GetKey(); | |
| 107 mojo::gfx::composition::ResourcePtr& resource_decl = it.GetValue(); | |
| 108 if (resource_decl) { | |
| 109 ResourceDef* resource = CreateResource(resource_id, resource_decl.Pass(), | |
| 110 resolver, unavailable_sender, err); | |
| 111 if (!resource) | |
| 112 return false; | |
| 113 resources_[resource_id].reset(resource); | |
| 114 } else { | |
| 115 resources_.erase(resource_id); | |
| 116 } | |
| 117 } | |
| 118 | |
| 119 // Update nodes. | |
| 120 if (update->clear_nodes) { | |
| 121 nodes_.clear(); | |
| 122 } | |
| 123 for (auto it = update->nodes.begin(); it != update->nodes.end(); ++it) { | |
| 124 uint32_t node_id = it.GetKey(); | |
| 125 mojo::gfx::composition::NodePtr& node_decl = it.GetValue(); | |
| 126 if (node_decl) { | |
| 127 NodeDef* node = CreateNode(node_id, node_decl.Pass(), err); | |
| 128 if (!node) | |
| 129 return false; | |
| 130 nodes_[node_id].reset(node); | |
| 131 } else { | |
| 132 nodes_.erase(node_id); | |
| 133 } | |
| 134 } | |
| 135 return true; | |
| 136 } | |
| 137 | |
| 138 bool SceneDef::Validate(std::ostream& err) { | |
| 139 // Validate all nodes. | |
| 140 // TODO(jeffbrown): Figure out how to do this incrementally if it gets | |
| 141 // too expensive to process all nodes each time. | |
| 142 root_node_ = nullptr; | |
| 143 for (auto& pair : nodes_) { | |
| 144 uint32_t node_id = pair.first; | |
| 145 NodeDef* node = pair.second.get(); | |
| 146 if (!node->Validate(this, err)) | |
| 147 return false; | |
| 148 if (node_id == mojo::gfx::composition::kSceneRootNodeId) | |
| 149 root_node_ = node; | |
| 150 } | |
| 151 return true; | |
| 152 } | |
| 153 | |
| 154 bool SceneDef::UnlinkReferencedScene( | |
| 155 SceneDef* scene, | |
| 156 const SceneUnavailableSender& unavailable_sender) { | |
| 157 DCHECK(scene); | |
| 158 | |
| 159 bool changed = false; | |
| 160 for (auto& pair : resources_) { | |
| 161 if (pair.second->type() == ResourceDef::Type::kScene) { | |
| 162 auto scene_resource = static_cast<SceneResourceDef*>(pair.second.get()); | |
| 163 if (scene_resource->referenced_scene() == scene) { | |
| 164 scene_resource->clear_referenced_scene(); | |
| 165 Invalidate(); | |
| 166 changed = true; | |
| 167 unavailable_sender.Run(pair.first); | |
|
abarth
2016/01/10 22:55:36
This really looks like a weak pointer with a callb
jeffbrown
2016/01/16 03:28:32
Perhaps, but I don't see a way to set a callback o
| |
| 168 } | |
| 169 } | |
| 170 } | |
| 171 return changed; | |
| 172 } | |
| 173 | |
| 174 bool SceneDef::Snapshot(SnapshotBuilder* snapshot_builder, | |
| 175 RenderLayerBuilder* layer_builder) { | |
| 176 DCHECK(snapshot_builder); | |
| 177 DCHECK(layer_builder); | |
| 178 | |
| 179 // Detect cycles. | |
| 180 if (visited_) { | |
| 181 if (snapshot_builder->block_log()) { | |
| 182 *snapshot_builder->block_log() | |
| 183 << "Scene blocked due to recursive cycle: " << FormattedLabel() | |
| 184 << std::endl; | |
| 185 } | |
| 186 return false; | |
| 187 } | |
| 188 | |
| 189 // Snapshot the contents of the scene. | |
| 190 visited_ = true; | |
| 191 bool success = SnapshotInner(snapshot_builder, layer_builder); | |
| 192 visited_ = false; | |
| 193 return success; | |
| 194 } | |
| 195 | |
| 196 bool SceneDef::SnapshotInner(SnapshotBuilder* snapshot_builder, | |
| 197 RenderLayerBuilder* layer_builder) { | |
| 198 // Note the dependency even if blocked. | |
| 199 snapshot_builder->AddSceneDependency(this); | |
| 200 | |
| 201 // Ensure we have a root node. | |
| 202 if (!root_node_) { | |
| 203 if (snapshot_builder->block_log()) { | |
| 204 *snapshot_builder->block_log() | |
| 205 << "Scene blocked due because it has no root node: " | |
| 206 << FormattedLabel() << std::endl; | |
| 207 } | |
| 208 return false; | |
| 209 } | |
| 210 | |
| 211 // Snapshot and draw the layer. | |
| 212 std::shared_ptr<RenderLayer> scene_layer = SnapshotLayer(snapshot_builder); | |
| 213 if (!scene_layer) | |
| 214 return false; | |
| 215 layer_builder->DrawLayer(scene_layer); | |
| 216 return true; | |
| 217 } | |
| 218 | |
| 219 std::shared_ptr<RenderLayer> SceneDef::SnapshotLayer( | |
| 220 SnapshotBuilder* snapshot_builder) { | |
| 221 if (cached_layer_) | |
| 222 return cached_layer_; | |
| 223 | |
| 224 RenderLayerBuilder scene_layer_builder; | |
| 225 scene_layer_builder.PushScene(scene_token_->value, version_); | |
| 226 if (!root_node_->Snapshot(snapshot_builder, &scene_layer_builder, this)) | |
| 227 return nullptr; | |
| 228 scene_layer_builder.PopScene(); | |
| 229 | |
| 230 // TODO(jeffbrown): Implement caching even when the scene has dependencies. | |
| 231 // There are some subtleties to be dealt with to ensure that caches | |
| 232 // are properly invalidated and that we don't accidentally cache layers which | |
| 233 // bake in decisions which counteract the intended cycle detection and | |
| 234 // avoidance behavior. Basically just need better bookkeeping. | |
|
abarth
2016/01/10 22:55:36
The DAG is making this harder for you. I'm not su
jeffbrown
2016/01/16 03:28:32
I think the DAG will be quite useful for cross-app
| |
| 235 std::shared_ptr<RenderLayer> scene_layer = scene_layer_builder.Build(); | |
| 236 if (!HasSceneResources()) | |
| 237 cached_layer_ = scene_layer; | |
| 238 return scene_layer; | |
| 239 } | |
| 240 | |
| 241 void SceneDef::Invalidate() { | |
| 242 cached_layer_.reset(); | |
| 243 } | |
| 244 | |
| 245 bool SceneDef::HasSceneResources() { | |
| 246 for (auto& pair : resources_) { | |
| 247 if (pair.second->type() == ResourceDef::Type::kScene) | |
| 248 return true; | |
| 249 } | |
| 250 return false; | |
| 251 } | |
| 252 | |
| 253 ResourceDef* SceneDef::CreateResource( | |
| 254 uint32_t resource_id, | |
| 255 mojo::gfx::composition::ResourcePtr resource_decl, | |
| 256 const SceneResolver& resolver, | |
| 257 const SceneUnavailableSender& unavailable_sender, | |
| 258 std::ostream& err) { | |
| 259 DCHECK(resource_decl); | |
| 260 | |
| 261 if (resource_decl->is_scene()) { | |
| 262 auto& scene_resource_decl = resource_decl->get_scene(); | |
| 263 DCHECK(scene_resource_decl->scene_token); | |
| 264 SceneDef* referenced_scene = | |
| 265 resolver.Run(scene_resource_decl->scene_token.get()); | |
| 266 if (!referenced_scene) { | |
| 267 unavailable_sender.Run(resource_id); | |
| 268 } | |
| 269 return new SceneResourceDef(referenced_scene); | |
| 270 } | |
| 271 | |
| 272 if (resource_decl->is_mailbox_texture()) { | |
| 273 auto& mailbox_texture_resource_decl = resource_decl->get_mailbox_texture(); | |
| 274 DCHECK(mailbox_texture_resource_decl->mailbox_name.size() == | |
| 275 GL_MAILBOX_SIZE_CHROMIUM); | |
| 276 DCHECK(mailbox_texture_resource_decl->size); | |
| 277 int32_t width = mailbox_texture_resource_decl->size->width; | |
| 278 int32_t height = mailbox_texture_resource_decl->size->height; | |
| 279 if (width < 1 || width > kMaxTextureWidth || height < 1 || | |
| 280 height > kMaxTextureHeight) { | |
| 281 err << "MailboxTexture resource has invalid size: " | |
| 282 << "resource_id=" << resource_id << ", width=" << width | |
| 283 << ", height=" << height; | |
| 284 return nullptr; | |
| 285 } | |
| 286 return new ImageResourceDef(RenderImage::FromMailboxTexture( | |
| 287 mailbox_texture_resource_decl->mailbox_name.data(), | |
| 288 mailbox_texture_resource_decl->sync_point, width, height, | |
| 289 base::MessageLoop::current()->task_runner(), | |
| 290 base::Bind( | |
| 291 &ReleaseMailboxTexture, | |
| 292 base::Passed(mailbox_texture_resource_decl->callback.Pass())))); | |
| 293 } | |
| 294 | |
| 295 err << "Unsupported resource type: resource_id=" << resource_id; | |
| 296 return nullptr; | |
| 297 } | |
| 298 | |
| 299 NodeDef* SceneDef::CreateNode(uint32_t node_id, | |
| 300 mojo::gfx::composition::NodePtr node_decl, | |
| 301 std::ostream& err) { | |
| 302 DCHECK(node_decl); | |
| 303 | |
| 304 NodeOp* op = nullptr; | |
| 305 if (node_decl->op) { | |
| 306 op = CreateNodeOp(node_id, node_decl->op.Pass(), err); | |
| 307 if (!op) | |
| 308 return nullptr; | |
| 309 } | |
| 310 | |
| 311 return new NodeDef(node_id, node_decl->content_transform.Pass(), | |
| 312 node_decl->content_clip.Pass(), node_decl->hit_id, | |
| 313 node_decl->combinator, node_decl->child_node_ids.storage(), | |
| 314 op); | |
| 315 } | |
| 316 | |
| 317 NodeOp* SceneDef::CreateNodeOp(uint32_t node_id, | |
| 318 mojo::gfx::composition::NodeOpPtr node_op_decl, | |
| 319 std::ostream& err) { | |
| 320 DCHECK(node_op_decl); | |
| 321 | |
| 322 if (node_op_decl->is_rect()) { | |
| 323 auto& rect_node_op_decl = node_op_decl->get_rect(); | |
| 324 DCHECK(rect_node_op_decl->content_rect); | |
| 325 DCHECK(rect_node_op_decl->color); | |
| 326 return new RectNodeOp(*rect_node_op_decl->content_rect, | |
| 327 *rect_node_op_decl->color); | |
| 328 } | |
| 329 | |
| 330 if (node_op_decl->is_image()) { | |
| 331 auto& image_node_op_decl = node_op_decl->get_image(); | |
| 332 DCHECK(image_node_op_decl->content_rect); | |
| 333 return new ImageNodeOp(*image_node_op_decl->content_rect, | |
| 334 image_node_op_decl->image_rect.Pass(), | |
| 335 image_node_op_decl->image_resource_id, | |
| 336 image_node_op_decl->blend.Pass()); | |
| 337 } | |
| 338 | |
| 339 if (node_op_decl->is_scene()) { | |
| 340 auto& scene_node_op_decl = node_op_decl->get_scene(); | |
| 341 return new SceneNodeOp(scene_node_op_decl->scene_resource_id, | |
| 342 scene_node_op_decl->scene_version); | |
| 343 } | |
| 344 | |
| 345 if (node_op_decl->is_layer()) { | |
| 346 auto& layer_node_op_decl = node_op_decl->get_layer(); | |
| 347 DCHECK(layer_node_op_decl->layer_size); | |
| 348 return new LayerNodeOp(*layer_node_op_decl->layer_size, | |
| 349 layer_node_op_decl->blend.Pass()); | |
| 350 } | |
| 351 | |
| 352 err << "Unsupported node op type: node_id=" << node_id | |
| 353 << ", node_op=" << node_op_decl; | |
| 354 return nullptr; | |
| 355 } | |
| 356 | |
| 357 NodeDef* SceneDef::FindNode(uint32_t node_id) { | |
| 358 auto it = nodes_.find(node_id); | |
| 359 return it != nodes_.end() ? it->second.get() : nullptr; | |
| 360 } | |
| 361 | |
| 362 ResourceDef* SceneDef::FindResource(uint32_t resource_id, | |
| 363 ResourceDef::Type resource_type) { | |
| 364 auto it = resources_.find(resource_id); | |
| 365 return it != resources_.end() && it->second->type() == resource_type | |
| 366 ? it->second.get() | |
| 367 : nullptr; | |
| 368 } | |
| 369 | |
| 370 std::string SceneDef::FormattedLabel() { | |
| 371 if (formatted_label_cache_.empty()) { | |
| 372 std::ostringstream s; | |
| 373 s << "<" << scene_token_->value; | |
| 374 if (!label_.empty()) | |
| 375 s << ":" << label_; | |
| 376 s << "/" << version_ << ">"; | |
| 377 formatted_label_cache_ = s.str(); | |
| 378 } | |
| 379 return formatted_label_cache_; | |
| 380 } | |
| 381 | |
| 382 SceneDef::Publication::Publication( | |
| 383 mojo::gfx::composition::SceneMetadataPtr metadata) | |
| 384 : metadata(metadata.Pass()) { | |
| 385 DCHECK(this->metadata); | |
| 386 } | |
| 387 | |
| 388 SceneDef::Publication::~Publication() {} | |
| 389 | |
| 390 } // namespace compositor | |
| OLD | NEW |