| // 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 "base/bind.h" |
| #include "base/logging.h" |
| #include "base/message_loop/message_loop.h" |
| #include "base/strings/stringprintf.h" |
| #include "mojo/services/gfx/composition/cpp/formatting.h" |
| #include "mojo/skia/type_converters.h" |
| #include "services/gfx/compositor/graph/scene_content.h" |
| #include "services/gfx/compositor/graph/transform_pair.h" |
| #include "services/gfx/compositor/graph/universe.h" |
| #include "services/gfx/compositor/render/render_image.h" |
| |
| namespace compositor { |
| |
| namespace { |
| // TODO(jeffbrown): Determine and document a more appropriate size limit |
| // for transferred images as part of the image pipe abstraction instead. |
| const int32_t kMaxTextureWidth = 65536; |
| const int32_t kMaxTextureHeight = 65536; |
| |
| void ReleaseMailboxTexture( |
| mojo::gfx::composition::MailboxTextureCallbackPtr callback) { |
| if (callback) |
| callback->OnMailboxTextureReleased(); |
| } |
| } // namespace |
| |
| SceneDef::SceneDef(const SceneLabel& label) : label_(label) {} |
| |
| 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, |
| Universe* universe, |
| 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--; |
| } |
| |
| // TODO(jeffbrown): Should we publish every individual update to the |
| // universe or is it good enough to only capture the most recent |
| // accumulated updates at presentation time as we do here? |
| |
| // Apply all updates sequentially up to this point. |
| uint32_t version = pending_publications_[end - 1]->metadata->version; |
| 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); |
| |
| // Rebuild the scene content, collecting all reachable nodes and resources |
| // and verifying that everything is correctly linked. |
| Collector collector(this, version, presentation_time, err); |
| scoped_refptr<const SceneContent> content = collector.Build(); |
| if (!content) |
| return Disposition::kFailed; |
| |
| universe->PresentScene(content); |
| return Disposition::kSucceeded; |
| } |
| |
| bool SceneDef::ApplyUpdate(mojo::gfx::composition::SceneUpdatePtr update, |
| const SceneResolver& resolver, |
| const SceneUnavailableSender& unavailable_sender, |
| std::ostream& err) { |
| DCHECK(update); |
| |
| // TODO(jeffbrown): We may be able to reuse some content from previous |
| // versions even when the client removes and recreates resources or nodes. |
| // To reduce unnecessary churn, consider keeping track of items which have |
| // been removed or are being replaced then checking to see whether they |
| // really changed. |
| |
| // 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) { |
| scoped_refptr<const Resource> resource = CreateResource( |
| resource_id, resource_decl.Pass(), resolver, unavailable_sender, err); |
| if (!resource) |
| return false; |
| resources_[resource_id] = std::move(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) { |
| scoped_refptr<const Node> node = |
| CreateNode(node_id, node_decl.Pass(), err); |
| if (!node) |
| return false; |
| nodes_[node_id] = std::move(node); |
| } else { |
| nodes_.erase(node_id); |
| } |
| } |
| return true; |
| } |
| |
| void SceneDef::NotifySceneUnavailable( |
| const mojo::gfx::composition::SceneToken& scene_token, |
| const SceneUnavailableSender& unavailable_sender) { |
| for (auto& pair : resources_) { |
| if (pair.second->type() == Resource::Type::kScene) { |
| auto scene_resource = |
| static_cast<const SceneResource*>(pair.second.get()); |
| if (scene_resource->scene_token().value == scene_token.value) |
| unavailable_sender.Run(pair.first); |
| } |
| } |
| } |
| |
| scoped_refptr<const Resource> 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); |
| |
| const mojo::gfx::composition::SceneToken& scene_token = |
| *scene_resource_decl->scene_token; |
| if (!resolver.Run(scene_token)) |
| unavailable_sender.Run(resource_id); |
| return new SceneResource(scene_token); |
| } |
| |
| 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); |
| |
| const int32_t width = mailbox_texture_resource_decl->size->width; |
| const 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; |
| } |
| const GLbyte* const mailbox_name = reinterpret_cast<GLbyte*>( |
| mailbox_texture_resource_decl->mailbox_name.data()); |
| const GLuint sync_point = mailbox_texture_resource_decl->sync_point; |
| const mojo::gfx::composition::MailboxTextureResource::Origin origin = |
| mailbox_texture_resource_decl->origin; |
| |
| scoped_refptr<RenderImage> image = RenderImage::CreateFromMailboxTexture( |
| mailbox_name, sync_point, width, height, origin, |
| base::MessageLoop::current()->task_runner(), |
| base::Bind( |
| &ReleaseMailboxTexture, |
| base::Passed( |
| mojo::gfx::composition::MailboxTextureCallbackPtr::Create( |
| std::move(mailbox_texture_resource_decl->callback))))); |
| if (!image) { |
| err << "Could not create MailboxTexture"; |
| return nullptr; |
| } |
| return new ImageResource(image); |
| } |
| |
| err << "Unsupported resource type: resource_id=" << resource_id; |
| return nullptr; |
| } |
| |
| scoped_refptr<const Node> SceneDef::CreateNode( |
| uint32_t node_id, |
| mojo::gfx::composition::NodePtr node_decl, |
| std::ostream& err) { |
| DCHECK(node_decl); |
| |
| std::unique_ptr<TransformPair> content_transform; |
| if (node_decl->content_transform) { |
| content_transform.reset( |
| new TransformPair(node_decl->content_transform.To<SkMatrix44>())); |
| } |
| mojo::RectFPtr content_clip = node_decl->content_clip.Pass(); |
| mojo::gfx::composition::HitTestBehaviorPtr hit_test_behavior = |
| node_decl->hit_test_behavior.Pass(); |
| const mojo::gfx::composition::Node::Combinator combinator = |
| node_decl->combinator; |
| const std::vector<uint32_t>& child_node_ids = |
| node_decl->child_node_ids.storage(); |
| |
| if (!node_decl->op) { |
| return new Node(node_id, std::move(content_transform), content_clip.Pass(), |
| hit_test_behavior.Pass(), combinator, child_node_ids); |
| } |
| |
| if (node_decl->op->is_rect()) { |
| auto& rect_node_decl = node_decl->op->get_rect(); |
| DCHECK(rect_node_decl->content_rect); |
| DCHECK(rect_node_decl->color); |
| |
| const mojo::RectF& content_rect = *rect_node_decl->content_rect; |
| const mojo::gfx::composition::Color& color = *rect_node_decl->color; |
| return new RectNode(node_id, std::move(content_transform), |
| content_clip.Pass(), hit_test_behavior.Pass(), |
| combinator, child_node_ids, content_rect, color); |
| } |
| |
| if (node_decl->op->is_image()) { |
| auto& image_node_decl = node_decl->op->get_image(); |
| DCHECK(image_node_decl->content_rect); |
| |
| const mojo::RectF& content_rect = *image_node_decl->content_rect; |
| mojo::RectFPtr image_rect = image_node_decl->image_rect.Pass(); |
| const uint32 image_resource_id = image_node_decl->image_resource_id; |
| mojo::gfx::composition::BlendPtr blend = image_node_decl->blend.Pass(); |
| return new ImageNode(node_id, std::move(content_transform), |
| content_clip.Pass(), hit_test_behavior.Pass(), |
| combinator, child_node_ids, content_rect, |
| image_rect.Pass(), image_resource_id, blend.Pass()); |
| } |
| |
| if (node_decl->op->is_scene()) { |
| auto& scene_node_decl = node_decl->op->get_scene(); |
| |
| const uint32_t scene_resource_id = scene_node_decl->scene_resource_id; |
| const uint32_t scene_version = scene_node_decl->scene_version; |
| return new SceneNode(node_id, std::move(content_transform), |
| content_clip.Pass(), hit_test_behavior.Pass(), |
| combinator, child_node_ids, scene_resource_id, |
| scene_version); |
| } |
| |
| if (node_decl->op->is_layer()) { |
| auto& layer_node_decl = node_decl->op->get_layer(); |
| DCHECK(layer_node_decl->layer_rect); |
| |
| const mojo::RectF& layer_rect = *layer_node_decl->layer_rect; |
| mojo::gfx::composition::BlendPtr blend = layer_node_decl->blend.Pass(); |
| return new LayerNode(node_id, std::move(content_transform), |
| content_clip.Pass(), hit_test_behavior.Pass(), |
| combinator, child_node_ids, layer_rect, blend.Pass()); |
| } |
| |
| err << "Unsupported node op type: node_id=" << node_id |
| << ", node_op=" << node_decl->op; |
| return nullptr; |
| } |
| |
| SceneDef::Collector::Collector(const SceneDef* scene, |
| uint32_t version, |
| int64_t presentation_time, |
| std::ostream& err) |
| : SceneContentBuilder(scene->label_, |
| version, |
| presentation_time, |
| scene->resources_.size(), |
| scene->nodes_.size(), |
| err), |
| scene_(scene) { |
| DCHECK(scene_); |
| } |
| |
| SceneDef::Collector::~Collector() {} |
| |
| const Node* SceneDef::Collector::FindNode(uint32_t node_id) const { |
| auto it = scene_->nodes_.find(node_id); |
| return it != scene_->nodes_.end() ? it->second.get() : nullptr; |
| } |
| |
| const Resource* SceneDef::Collector::FindResource(uint32_t resource_id) const { |
| auto it = scene_->resources_.find(resource_id); |
| return it != scene_->resources_.end() ? it->second.get() : nullptr; |
| } |
| |
| SceneDef::Publication::Publication( |
| mojo::gfx::composition::SceneMetadataPtr metadata) |
| : metadata(metadata.Pass()) { |
| DCHECK(this->metadata); |
| } |
| |
| SceneDef::Publication::~Publication() {} |
| |
| } // namespace compositor |