blob: 3287301495fe45e24ea286fbbe55a21ca2ddb9fa [file] [log] [blame]
// 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/node_def.h"
#include "base/logging.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_def.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 {
namespace {
SkColor MakeSkColor(const mojo::gfx::composition::Color& color) {
return SkColorSetARGBInline(color.alpha, color.red, color.green, color.blue);
}
SkPaint MakePaintForBlend(const mojo::gfx::composition::Blend& blend) {
SkPaint result;
result.setAlpha(blend.alpha);
return result;
}
} // namespace
NodeDef::NodeDef(uint32_t node_id,
mojo::TransformPtr content_transform,
mojo::RectPtr content_clip,
uint32_t hit_id,
Combinator combinator,
const std::vector<uint32_t>& child_node_ids,
NodeOp* op)
: node_id_(node_id),
content_transform_(content_transform.Pass()),
content_clip_(content_clip.Pass()),
hit_id_(hit_id),
combinator_(combinator),
child_node_ids_(child_node_ids),
op_(op) {}
NodeDef::~NodeDef() {}
bool NodeDef::Validate(SceneDef* scene, std::ostream& err) {
child_nodes_.clear();
for (uint32_t child_node_id : child_node_ids_) {
NodeDef* child_node = scene->FindNode(child_node_id);
if (!child_node) {
err << "Node refers to unknown child: " << FormattedLabel(scene)
<< ", child_node_id=" << child_node_id;
return false;
}
child_nodes_.push_back(child_node);
}
return !op_ || op_->Validate(scene, this, err);
}
bool NodeDef::Snapshot(SnapshotBuilder* snapshot_builder,
RenderLayerBuilder* layer_builder,
SceneDef* scene) {
DCHECK(snapshot_builder);
DCHECK(layer_builder);
DCHECK(scene);
// Detect cycles.
if (visited_) {
if (snapshot_builder->block_log()) {
*snapshot_builder->block_log()
<< "Node blocked due to recursive cycle: " << FormattedLabel(scene)
<< std::endl;
}
return false;
}
// Snapshot the contents of the node.
visited_ = true;
bool success = SnapshotInner(snapshot_builder, layer_builder, scene);
visited_ = false;
return success;
}
bool NodeDef::SnapshotInner(SnapshotBuilder* snapshot_builder,
RenderLayerBuilder* layer_builder,
SceneDef* scene) {
// TODO(jeffbrown): Frequently referenced and reused nodes, especially
// layer nodes, may benefit from caching.
layer_builder->PushNode(node_id_, hit_id_);
if (content_transform_)
layer_builder->ApplyTransform(content_transform_.To<SkMatrix>());
if (content_clip_)
layer_builder->ApplyClip(content_clip_->To<SkRect>());
bool success =
op_ ? op_->Snapshot(snapshot_builder, layer_builder, scene, this)
: SnapshotChildren(snapshot_builder, layer_builder, scene);
if (!success)
return false;
layer_builder->PopNode();
return true;
}
bool NodeDef::SnapshotChildren(SnapshotBuilder* snapshot_builder,
RenderLayerBuilder* layer_builder,
SceneDef* scene) {
DCHECK(snapshot_builder);
DCHECK(layer_builder);
DCHECK(scene);
switch (combinator_) {
// MERGE: All or nothing.
case Combinator::MERGE: {
for (NodeDef* child_node : child_nodes_) {
if (!child_node->Snapshot(snapshot_builder, layer_builder, scene)) {
if (snapshot_builder->block_log()) {
*snapshot_builder->block_log()
<< "Node with MERGE combinator blocked since "
"one of its children is blocked: "
<< FormattedLabel(scene) << ", blocked child "
<< child_node->FormattedLabel(scene) << std::endl;
}
return false; // blocked
}
}
return true;
}
// PRUNE: Silently discard blocked children.
case Combinator::PRUNE: {
for (NodeDef* child_node : child_nodes_) {
RenderLayerBuilder child_layer_builder;
if (child_node->Snapshot(snapshot_builder, &child_layer_builder,
scene)) {
layer_builder->DrawLayer(child_layer_builder.Build());
}
}
return true;
}
// FALLBACK: Keep only the first unblocked child.
case Combinator::FALLBACK: {
if (child_nodes_.empty())
return true;
for (NodeDef* child_node : child_nodes_) {
RenderLayerBuilder child_layer_builder;
if (child_node->Snapshot(snapshot_builder, &child_layer_builder,
scene)) {
layer_builder->DrawLayer(child_layer_builder.Build());
return true;
}
}
if (snapshot_builder->block_log()) {
*snapshot_builder->block_log()
<< "Node with FALLBACK combinator blocked since "
"all of its children are blocked: "
<< FormattedLabel(scene) << std::endl;
}
return false; // blocked
}
default: {
if (snapshot_builder->block_log()) {
*snapshot_builder->block_log()
<< "Unrecognized combinator: " << FormattedLabel(scene)
<< std::endl;
}
return false;
}
}
}
std::string NodeDef::FormattedLabel(SceneDef* scene) {
return base::StringPrintf("%s[%d]", scene->FormattedLabel().c_str(),
node_id_);
}
bool NodeOp::Validate(SceneDef* scene, NodeDef* node, std::ostream& err) {
return true;
}
RectNodeOp::RectNodeOp(const mojo::Rect& content_rect,
const mojo::gfx::composition::Color& color)
: content_rect_(content_rect), color_(color) {}
RectNodeOp::~RectNodeOp() {}
bool RectNodeOp::Snapshot(SnapshotBuilder* snapshot_builder,
RenderLayerBuilder* layer_builder,
SceneDef* scene,
NodeDef* node) {
if (!node->SnapshotChildren(snapshot_builder, layer_builder, scene))
return false;
SkPaint paint;
paint.setColor(MakeSkColor(color_));
layer_builder->DrawRect(content_rect_.To<SkRect>(), paint);
return true;
}
ImageNodeOp::ImageNodeOp(const mojo::Rect& content_rect,
mojo::RectPtr image_rect,
uint32 image_resource_id,
mojo::gfx::composition::BlendPtr blend)
: content_rect_(content_rect),
image_rect_(image_rect.Pass()),
image_resource_id_(image_resource_id),
blend_(blend.Pass()) {}
ImageNodeOp::~ImageNodeOp() {}
bool ImageNodeOp::Validate(SceneDef* scene, NodeDef* node, std::ostream& err) {
image_resource_ = scene->FindImageResource(image_resource_id_);
if (!image_resource_) {
err << "Node refers to unknown or invalid image resource: "
<< node->FormattedLabel(scene)
<< ", image_resource_id=" << image_resource_id_;
return false;
}
return true;
}
bool ImageNodeOp::Snapshot(SnapshotBuilder* snapshot_builder,
RenderLayerBuilder* layer_builder,
SceneDef* scene,
NodeDef* node) {
DCHECK(image_resource_);
if (!image_resource_->image()) {
if (snapshot_builder->block_log()) {
*snapshot_builder->block_log()
<< "Node blocked due to its referenced image "
"resource being unavailable: "
<< node->FormattedLabel(scene) << std::endl;
}
return false;
}
if (!node->SnapshotChildren(snapshot_builder, layer_builder, scene))
return false;
layer_builder->DrawImage(
image_resource_->image(), content_rect_.To<SkRect>(),
image_rect_ ? image_rect_->To<SkRect>()
: SkRect::MakeWH(image_resource_->image()->width(),
image_resource_->image()->height()),
blend_ ? MakePaintForBlend(*blend_) : SkPaint());
return true;
}
SceneNodeOp::SceneNodeOp(uint32_t scene_resource_id, uint32_t scene_version)
: scene_resource_id_(scene_resource_id), scene_version_(scene_version) {}
SceneNodeOp::~SceneNodeOp() {}
bool SceneNodeOp::Validate(SceneDef* scene, NodeDef* node, std::ostream& err) {
scene_resource_ = scene->FindSceneResource(scene_resource_id_);
if (!scene_resource_) {
err << "Node refers to unknown or invalid scene resource: "
<< node->FormattedLabel(scene)
<< ", scene_resource_id=" << scene_resource_id_;
return false;
}
return true;
}
bool SceneNodeOp::Snapshot(SnapshotBuilder* snapshot_builder,
RenderLayerBuilder* layer_builder,
SceneDef* scene,
NodeDef* node) {
DCHECK(scene_resource_);
SceneDef* referenced_scene = scene_resource_->referenced_scene();
if (!referenced_scene) {
if (snapshot_builder->block_log()) {
*snapshot_builder->block_log()
<< "Node blocked due to its referenced scene "
"resource being unavailable: "
<< node->FormattedLabel(scene) << std::endl;
}
return false;
}
uint32_t actual_version = referenced_scene->version();
if (scene_version_ != mojo::gfx::composition::kSceneVersionNone &&
actual_version != mojo::gfx::composition::kSceneVersionNone &&
scene_version_ != actual_version) {
if (snapshot_builder->block_log()) {
*snapshot_builder->block_log()
<< "Node blocked due to its referenced scene "
"resource not having the desired version: "
<< node->FormattedLabel(scene)
<< ", requested_version=" << scene_version_
<< ", actual_version=" << actual_version << std::endl;
}
return false;
}
if (!node->SnapshotChildren(snapshot_builder, layer_builder, scene))
return false;
return referenced_scene->Snapshot(snapshot_builder, layer_builder);
}
LayerNodeOp::LayerNodeOp(const mojo::Size& size,
mojo::gfx::composition::BlendPtr blend)
: size_(size), blend_(blend.Pass()) {}
LayerNodeOp::~LayerNodeOp() {}
bool LayerNodeOp::Snapshot(SnapshotBuilder* snapshot_builder,
RenderLayerBuilder* layer_builder,
SceneDef* scene,
NodeDef* node) {
SkRect content_rect = SkRect::MakeWH(size_.width, size_.height);
RenderLayerBuilder children_layer_builder(&content_rect);
if (!node->SnapshotChildren(snapshot_builder, &children_layer_builder, scene))
return false;
layer_builder->DrawSavedLayer(
children_layer_builder.Build(), content_rect,
blend_ ? MakePaintForBlend(*blend_) : SkPaint());
return true;
}
} // namespace compositor