Index: services/gfx/compositor/graph/scene_def.cc |
diff --git a/services/gfx/compositor/graph/scene_def.cc b/services/gfx/compositor/graph/scene_def.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..4791754a69ff7f9d375be956c54109630cf12017 |
--- /dev/null |
+++ b/services/gfx/compositor/graph/scene_def.cc |
@@ -0,0 +1,390 @@ |
+// Copyright 2015 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "services/gfx/compositor/graph/scene_def.h" |
+ |
+#include <ostream> |
+#include <sstream> |
+ |
+#include "base/bind.h" |
+#include "base/logging.h" |
+#include "base/message_loop/message_loop.h" |
+#include "mojo/services/gfx/composition/cpp/logging.h" |
+#include "services/gfx/compositor/graph/snapshot.h" |
+#include "services/gfx/compositor/render/render_image.h" |
+#include "services/gfx/compositor/render/render_layer.h" |
+ |
+namespace compositor { |
+ |
+// TODO(jeffbrown): Determine and document a more appropriate size limit |
+// for transferred images as part of the image pipe abstraction instead. |
+static const int32_t kMaxTextureWidth = 65536; |
+static const int32_t kMaxTextureHeight = 65536; |
+ |
+static void ReleaseMailboxTexture( |
+ mojo::gfx::composition::MailboxTextureCallbackPtr callback) { |
+ if (callback) |
+ callback->OnMailboxTextureReleased(); |
+} |
+ |
+SceneDef::SceneDef(mojo::gfx::composition::SceneTokenPtr scene_token, |
+ const std::string& label) |
+ : scene_token_(scene_token.Pass()), label_(label) { |
+ DCHECK(scene_token_); |
+} |
+ |
+SceneDef::~SceneDef() {} |
+ |
+void SceneDef::EnqueueUpdate(mojo::gfx::composition::SceneUpdatePtr update) { |
+ DCHECK(update); |
+ pending_updates_.push_back(update.Pass()); |
+} |
+ |
+void SceneDef::EnqueuePublish( |
+ mojo::gfx::composition::SceneMetadataPtr metadata) { |
+ DCHECK(metadata); |
+ pending_publications_.emplace_back(new Publication(metadata.Pass())); |
+ pending_updates_.swap(pending_publications_.back()->updates); |
+} |
+ |
+SceneDef::Disposition SceneDef::Present( |
+ int64_t presentation_time, |
+ const SceneResolver& resolver, |
+ const SceneUnavailableSender& unavailable_sender, |
+ std::ostream& err) { |
+ // Walk backwards through the pending publications to find the index |
+ // just beyond the last one which is due to be presented at or before the |
+ // presentation time. |
+ size_t end = pending_publications_.size(); |
+ for (;;) { |
+ if (!end) |
+ return Disposition::kUnchanged; |
+ if (pending_publications_[end - 1]->is_due(presentation_time)) |
+ break; // found last presentable publication |
+ end--; |
+ } |
+ |
+ // Prepare to apply all publications up to this point. |
+ uint32_t version = pending_publications_[end - 1]->metadata->version; |
+ if (version_ != version) { |
+ version_ = version; |
+ formatted_label_cache_.clear(); |
+ } |
+ Invalidate(); |
+ |
+ // Apply all updates sequentially. |
+ for (size_t index = 0; index < end; ++index) { |
+ for (auto& update : pending_publications_[index]->updates) { |
+ if (!ApplyUpdate(update.Pass(), resolver, unavailable_sender, err)) |
+ return Disposition::kFailed; |
+ } |
+ } |
+ |
+ // Dequeue the publications we processed. |
+ pending_publications_.erase(pending_publications_.begin(), |
+ pending_publications_.begin() + end); |
+ |
+ // Ensure the scene is in a valid state. |
+ if (!Validate(err)) |
+ return Disposition::kFailed; |
+ return Disposition::kSucceeded; |
+} |
+ |
+bool SceneDef::ApplyUpdate(mojo::gfx::composition::SceneUpdatePtr update, |
+ const SceneResolver& resolver, |
+ const SceneUnavailableSender& unavailable_sender, |
+ std::ostream& err) { |
+ DCHECK(update); |
+ |
+ // Update resources. |
+ if (update->clear_resources) { |
+ resources_.clear(); |
+ } |
+ for (auto it = update->resources.begin(); it != update->resources.end(); |
+ ++it) { |
+ uint32_t resource_id = it.GetKey(); |
+ mojo::gfx::composition::ResourcePtr& resource_decl = it.GetValue(); |
+ if (resource_decl) { |
+ ResourceDef* resource = CreateResource(resource_id, resource_decl.Pass(), |
+ resolver, unavailable_sender, err); |
+ if (!resource) |
+ return false; |
+ resources_[resource_id].reset(resource); |
+ } else { |
+ resources_.erase(resource_id); |
+ } |
+ } |
+ |
+ // Update nodes. |
+ if (update->clear_nodes) { |
+ nodes_.clear(); |
+ } |
+ for (auto it = update->nodes.begin(); it != update->nodes.end(); ++it) { |
+ uint32_t node_id = it.GetKey(); |
+ mojo::gfx::composition::NodePtr& node_decl = it.GetValue(); |
+ if (node_decl) { |
+ NodeDef* node = CreateNode(node_id, node_decl.Pass(), err); |
+ if (!node) |
+ return false; |
+ nodes_[node_id].reset(node); |
+ } else { |
+ nodes_.erase(node_id); |
+ } |
+ } |
+ return true; |
+} |
+ |
+bool SceneDef::Validate(std::ostream& err) { |
+ // Validate all nodes. |
+ // TODO(jeffbrown): Figure out how to do this incrementally if it gets |
+ // too expensive to process all nodes each time. |
+ root_node_ = nullptr; |
+ for (auto& pair : nodes_) { |
+ uint32_t node_id = pair.first; |
+ NodeDef* node = pair.second.get(); |
+ if (!node->Validate(this, err)) |
+ return false; |
+ if (node_id == mojo::gfx::composition::kSceneRootNodeId) |
+ root_node_ = node; |
+ } |
+ return true; |
+} |
+ |
+bool SceneDef::UnlinkReferencedScene( |
+ SceneDef* scene, |
+ const SceneUnavailableSender& unavailable_sender) { |
+ DCHECK(scene); |
+ |
+ bool changed = false; |
+ for (auto& pair : resources_) { |
+ if (pair.second->type() == ResourceDef::Type::kScene) { |
+ auto scene_resource = static_cast<SceneResourceDef*>(pair.second.get()); |
+ if (scene_resource->referenced_scene() == scene) { |
+ scene_resource->clear_referenced_scene(); |
+ Invalidate(); |
+ changed = true; |
+ 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
|
+ } |
+ } |
+ } |
+ return changed; |
+} |
+ |
+bool SceneDef::Snapshot(SnapshotBuilder* snapshot_builder, |
+ RenderLayerBuilder* layer_builder) { |
+ DCHECK(snapshot_builder); |
+ DCHECK(layer_builder); |
+ |
+ // Detect cycles. |
+ if (visited_) { |
+ if (snapshot_builder->block_log()) { |
+ *snapshot_builder->block_log() |
+ << "Scene blocked due to recursive cycle: " << FormattedLabel() |
+ << std::endl; |
+ } |
+ return false; |
+ } |
+ |
+ // Snapshot the contents of the scene. |
+ visited_ = true; |
+ bool success = SnapshotInner(snapshot_builder, layer_builder); |
+ visited_ = false; |
+ return success; |
+} |
+ |
+bool SceneDef::SnapshotInner(SnapshotBuilder* snapshot_builder, |
+ RenderLayerBuilder* layer_builder) { |
+ // Note the dependency even if blocked. |
+ snapshot_builder->AddSceneDependency(this); |
+ |
+ // Ensure we have a root node. |
+ if (!root_node_) { |
+ if (snapshot_builder->block_log()) { |
+ *snapshot_builder->block_log() |
+ << "Scene blocked due because it has no root node: " |
+ << FormattedLabel() << std::endl; |
+ } |
+ return false; |
+ } |
+ |
+ // Snapshot and draw the layer. |
+ std::shared_ptr<RenderLayer> scene_layer = SnapshotLayer(snapshot_builder); |
+ if (!scene_layer) |
+ return false; |
+ layer_builder->DrawLayer(scene_layer); |
+ return true; |
+} |
+ |
+std::shared_ptr<RenderLayer> SceneDef::SnapshotLayer( |
+ SnapshotBuilder* snapshot_builder) { |
+ if (cached_layer_) |
+ return cached_layer_; |
+ |
+ RenderLayerBuilder scene_layer_builder; |
+ scene_layer_builder.PushScene(scene_token_->value, version_); |
+ if (!root_node_->Snapshot(snapshot_builder, &scene_layer_builder, this)) |
+ return nullptr; |
+ scene_layer_builder.PopScene(); |
+ |
+ // TODO(jeffbrown): Implement caching even when the scene has dependencies. |
+ // There are some subtleties to be dealt with to ensure that caches |
+ // are properly invalidated and that we don't accidentally cache layers which |
+ // bake in decisions which counteract the intended cycle detection and |
+ // 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
|
+ std::shared_ptr<RenderLayer> scene_layer = scene_layer_builder.Build(); |
+ if (!HasSceneResources()) |
+ cached_layer_ = scene_layer; |
+ return scene_layer; |
+} |
+ |
+void SceneDef::Invalidate() { |
+ cached_layer_.reset(); |
+} |
+ |
+bool SceneDef::HasSceneResources() { |
+ for (auto& pair : resources_) { |
+ if (pair.second->type() == ResourceDef::Type::kScene) |
+ return true; |
+ } |
+ return false; |
+} |
+ |
+ResourceDef* SceneDef::CreateResource( |
+ uint32_t resource_id, |
+ mojo::gfx::composition::ResourcePtr resource_decl, |
+ const SceneResolver& resolver, |
+ const SceneUnavailableSender& unavailable_sender, |
+ std::ostream& err) { |
+ DCHECK(resource_decl); |
+ |
+ if (resource_decl->is_scene()) { |
+ auto& scene_resource_decl = resource_decl->get_scene(); |
+ DCHECK(scene_resource_decl->scene_token); |
+ SceneDef* referenced_scene = |
+ resolver.Run(scene_resource_decl->scene_token.get()); |
+ if (!referenced_scene) { |
+ unavailable_sender.Run(resource_id); |
+ } |
+ return new SceneResourceDef(referenced_scene); |
+ } |
+ |
+ if (resource_decl->is_mailbox_texture()) { |
+ auto& mailbox_texture_resource_decl = resource_decl->get_mailbox_texture(); |
+ DCHECK(mailbox_texture_resource_decl->mailbox_name.size() == |
+ GL_MAILBOX_SIZE_CHROMIUM); |
+ DCHECK(mailbox_texture_resource_decl->size); |
+ int32_t width = mailbox_texture_resource_decl->size->width; |
+ int32_t height = mailbox_texture_resource_decl->size->height; |
+ if (width < 1 || width > kMaxTextureWidth || height < 1 || |
+ height > kMaxTextureHeight) { |
+ err << "MailboxTexture resource has invalid size: " |
+ << "resource_id=" << resource_id << ", width=" << width |
+ << ", height=" << height; |
+ return nullptr; |
+ } |
+ return new ImageResourceDef(RenderImage::FromMailboxTexture( |
+ mailbox_texture_resource_decl->mailbox_name.data(), |
+ mailbox_texture_resource_decl->sync_point, width, height, |
+ base::MessageLoop::current()->task_runner(), |
+ base::Bind( |
+ &ReleaseMailboxTexture, |
+ base::Passed(mailbox_texture_resource_decl->callback.Pass())))); |
+ } |
+ |
+ err << "Unsupported resource type: resource_id=" << resource_id; |
+ return nullptr; |
+} |
+ |
+NodeDef* SceneDef::CreateNode(uint32_t node_id, |
+ mojo::gfx::composition::NodePtr node_decl, |
+ std::ostream& err) { |
+ DCHECK(node_decl); |
+ |
+ NodeOp* op = nullptr; |
+ if (node_decl->op) { |
+ op = CreateNodeOp(node_id, node_decl->op.Pass(), err); |
+ if (!op) |
+ return nullptr; |
+ } |
+ |
+ return new NodeDef(node_id, node_decl->content_transform.Pass(), |
+ node_decl->content_clip.Pass(), node_decl->hit_id, |
+ node_decl->combinator, node_decl->child_node_ids.storage(), |
+ op); |
+} |
+ |
+NodeOp* SceneDef::CreateNodeOp(uint32_t node_id, |
+ mojo::gfx::composition::NodeOpPtr node_op_decl, |
+ std::ostream& err) { |
+ DCHECK(node_op_decl); |
+ |
+ if (node_op_decl->is_rect()) { |
+ auto& rect_node_op_decl = node_op_decl->get_rect(); |
+ DCHECK(rect_node_op_decl->content_rect); |
+ DCHECK(rect_node_op_decl->color); |
+ return new RectNodeOp(*rect_node_op_decl->content_rect, |
+ *rect_node_op_decl->color); |
+ } |
+ |
+ if (node_op_decl->is_image()) { |
+ auto& image_node_op_decl = node_op_decl->get_image(); |
+ DCHECK(image_node_op_decl->content_rect); |
+ return new ImageNodeOp(*image_node_op_decl->content_rect, |
+ image_node_op_decl->image_rect.Pass(), |
+ image_node_op_decl->image_resource_id, |
+ image_node_op_decl->blend.Pass()); |
+ } |
+ |
+ if (node_op_decl->is_scene()) { |
+ auto& scene_node_op_decl = node_op_decl->get_scene(); |
+ return new SceneNodeOp(scene_node_op_decl->scene_resource_id, |
+ scene_node_op_decl->scene_version); |
+ } |
+ |
+ if (node_op_decl->is_layer()) { |
+ auto& layer_node_op_decl = node_op_decl->get_layer(); |
+ DCHECK(layer_node_op_decl->layer_size); |
+ return new LayerNodeOp(*layer_node_op_decl->layer_size, |
+ layer_node_op_decl->blend.Pass()); |
+ } |
+ |
+ err << "Unsupported node op type: node_id=" << node_id |
+ << ", node_op=" << node_op_decl; |
+ return nullptr; |
+} |
+ |
+NodeDef* SceneDef::FindNode(uint32_t node_id) { |
+ auto it = nodes_.find(node_id); |
+ return it != nodes_.end() ? it->second.get() : nullptr; |
+} |
+ |
+ResourceDef* SceneDef::FindResource(uint32_t resource_id, |
+ ResourceDef::Type resource_type) { |
+ auto it = resources_.find(resource_id); |
+ return it != resources_.end() && it->second->type() == resource_type |
+ ? it->second.get() |
+ : nullptr; |
+} |
+ |
+std::string SceneDef::FormattedLabel() { |
+ if (formatted_label_cache_.empty()) { |
+ std::ostringstream s; |
+ s << "<" << scene_token_->value; |
+ if (!label_.empty()) |
+ s << ":" << label_; |
+ s << "/" << version_ << ">"; |
+ formatted_label_cache_ = s.str(); |
+ } |
+ return formatted_label_cache_; |
+} |
+ |
+SceneDef::Publication::Publication( |
+ mojo::gfx::composition::SceneMetadataPtr metadata) |
+ : metadata(metadata.Pass()) { |
+ DCHECK(this->metadata); |
+} |
+ |
+SceneDef::Publication::~Publication() {} |
+ |
+} // namespace compositor |