blob: 69c4244d9b75b6cef7bfb8c6120516a62f76ebf4 [file] [log] [blame]
// Copyright 2016 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_content.h"
#include <ostream>
#include "base/logging.h"
#include "services/gfx/compositor/graph/scene_def.h"
namespace compositor {
SceneContent::SceneContent(const SceneLabel& label,
uint32_t version,
int64_t presentation_time,
size_t max_resources,
size_t max_nodes)
: label_(label),
version_(version),
presentation_time_(presentation_time),
resources_(max_resources),
nodes_(max_nodes) {}
SceneContent::~SceneContent() {}
bool SceneContent::MatchesVersion(uint32_t requested_version) const {
return requested_version == mojo::gfx::composition::kSceneVersionNone ||
requested_version == version_ ||
version_ == mojo::gfx::composition::kSceneVersionNone;
}
void SceneContent::Paint(const Snapshot* snapshot, SkCanvas* canvas) const {
const Node* root = GetRootNodeIfExists();
if (root)
root->Paint(this, snapshot, canvas);
}
bool SceneContent::HitTest(
const Snapshot* snapshot,
const SkPoint& scene_point,
const SkMatrix44& global_to_scene_transform,
mojo::gfx::composition::SceneHitPtr* out_scene_hit) const {
DCHECK(snapshot);
DCHECK(out_scene_hit);
const Node* root = GetRootNodeIfExists();
if (!root)
return false;
mojo::Array<mojo::gfx::composition::HitPtr> hits;
bool opaque = root->HitTest(this, snapshot, scene_point,
global_to_scene_transform, &hits);
if (hits.size()) {
auto scene_hit = mojo::gfx::composition::SceneHit::New();
scene_hit->scene_token = mojo::gfx::composition::SceneToken::New();
scene_hit->scene_token->value = label_.token();
scene_hit->scene_version = version_;
scene_hit->hits = hits.Pass();
*out_scene_hit = scene_hit.Pass();
}
return opaque;
}
const Resource* SceneContent::GetResource(uint32_t resource_id,
Resource::Type resource_type) const {
auto it = resources_.find(resource_id);
DCHECK(it != resources_.end());
DCHECK(it->second->type() == resource_type);
return it->second.get();
}
const Node* SceneContent::GetNode(uint32_t node_id) const {
auto it = nodes_.find(node_id);
DCHECK(it != nodes_.end());
return it->second.get();
}
const Node* SceneContent::GetRootNodeIfExists() const {
auto it = nodes_.find(mojo::gfx::composition::kSceneRootNodeId);
return it != nodes_.end() ? it->second.get() : nullptr;
}
SceneContentBuilder::SceneContentBuilder(const SceneLabel& label,
uint32_t version,
int64_t presentation_time,
size_t max_resources,
size_t max_nodes,
std::ostream& err)
: content_(new SceneContent(label,
version,
presentation_time,
max_resources,
max_nodes)),
err_(err) {}
SceneContentBuilder::~SceneContentBuilder() {}
const Resource* SceneContentBuilder::RequireResource(
uint32_t resource_id,
Resource::Type resource_type,
uint32_t referrer_node_id) {
DCHECK(content_);
auto it = content_->resources_.find(resource_id);
if (it != content_->resources_.end())
return it->second.get();
const Resource* resource = FindResource(resource_id);
if (!resource) {
err_ << "Missing resource " << resource_id << " referenced from node "
<< content_->FormattedLabelForNode(referrer_node_id) << std::endl;
return nullptr;
}
if (resource->type() != resource_type) {
err_ << "Resource " << resource_id << " referenced from node "
<< content_->FormattedLabelForNode(referrer_node_id)
<< " has incorrect type for its intended usage" << std::endl;
return nullptr;
}
content_->resources_.emplace(std::make_pair(resource_id, resource));
return resource;
}
const Node* SceneContentBuilder::RequireNode(uint32_t node_id,
uint32_t referrer_node_id) {
DCHECK(content_);
auto it = content_->nodes_.find(node_id);
if (it != content_->nodes_.end()) {
if (it->second)
return it->second.get();
err_ << "Cycle detected at node " << node_id << " referenced from node "
<< content_->FormattedLabelForNode(referrer_node_id) << std::endl;
return nullptr;
}
const Node* node = FindNode(node_id);
if (!node) {
err_ << "Missing node " << node_id << " referenced from node "
<< content_->FormattedLabelForNode(referrer_node_id) << std::endl;
return nullptr;
}
return AddNode(node) ? node : nullptr;
}
bool SceneContentBuilder::AddNode(const Node* node) {
DCHECK(content_);
DCHECK(node);
// Reserve a spot in the table to mark the node recording in progress.
DCHECK(content_->nodes_.size() < content_->nodes_.bucket_count());
auto storage = content_->nodes_.emplace(node->node_id(), nullptr);
DCHECK(storage.second);
// Record the node's content.
// This performs a depth first search of the node. If it succeeds, we
// will know that this part of the graph has no cycles. Note that this
// function may recurse back into |AddNode| to add additional nodes.
if (!node->RecordContent(this))
return false;
// Store the node in the table.
// It is safe to use the interator returned by emplace even though additional
// nodes may have been added since the map's bucket count was initialized
// at creation time to the total number of nodes so it should never be
// rehashed during this traversal.
storage.first->second = node;
return true;
}
scoped_refptr<const SceneContent> SceneContentBuilder::Build() {
DCHECK(content_);
const Node* root = FindNode(mojo::gfx::composition::kSceneRootNodeId);
return !root || AddNode(root) ? std::move(content_) : nullptr;
}
} // namespace compositor