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