blob: 51115e92a0d498038c7b6a4c80c1266ed3ee9504 [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/snapshot.h"
#include "base/logging.h"
#include "mojo/skia/type_converters.h"
#include "services/gfx/compositor/graph/scene_content.h"
#include "services/gfx/compositor/graph/scene_def.h"
#include "services/gfx/compositor/render/render_frame.h"
#include "third_party/skia/include/core/SkPictureRecorder.h"
#include "third_party/skia/include/core/SkRect.h"
#include "third_party/skia/include/utils/SkMatrix44.h"
namespace compositor {
Snapshot::Snapshot() {}
Snapshot::~Snapshot() {}
bool Snapshot::HasDependency(const SceneDef* scene) const {
return dependencies_.find(scene->label().token()) != dependencies_.end();
}
std::shared_ptr<RenderFrame> Snapshot::CreateFrame(
const mojo::Rect& viewport,
const mojo::gfx::composition::FrameInfo& frame_info) const {
DCHECK(!is_blocked());
DCHECK(root_scene_content_);
SkIRect sk_viewport = viewport.To<SkIRect>();
SkPictureRecorder recorder;
recorder.beginRecording(SkRect::Make(sk_viewport));
root_scene_content_->RecordPicture(this, recorder.getRecordingCanvas());
return RenderFrame::Create(skia::AdoptRef(recorder.endRecordingAsPicture()),
sk_viewport, frame_info);
}
void Snapshot::HitTest(const mojo::PointF& point,
mojo::gfx::composition::HitTestResult* result) const {
DCHECK(result);
DCHECK(!is_blocked());
DCHECK(root_scene_content_);
root_scene_content_->HitTest(this, point.To<SkPoint>(), SkMatrix44::I(),
&result->root);
}
bool Snapshot::IsNodeBlocked(const NodeDef* node) const {
DCHECK(!is_blocked());
auto it = node_dispositions_.find(node);
DCHECK(it != node_dispositions_.end());
DCHECK(it->second == Disposition::kSuccess ||
it->second == Disposition::kBlocked);
return it->second == Disposition::kBlocked;
}
const SceneContent* Snapshot::GetResolvedSceneContent(
const SceneNodeDef* scene_node) const {
DCHECK(!is_blocked());
auto it = resolved_scene_contents_.find(scene_node);
DCHECK(it != resolved_scene_contents_.end());
return it->second.get();
}
SnapshotBuilder::SnapshotBuilder(std::ostream* block_log)
: block_log_(block_log), snapshot_(new Snapshot()) {}
SnapshotBuilder::~SnapshotBuilder() {}
Snapshot::Disposition SnapshotBuilder::SnapshotNode(
const NodeDef* node,
const SceneContent* content) {
DCHECK(snapshot_);
DCHECK(node);
DCHECK(content);
DCHECK(node != content->GetRootNodeIfExists());
auto it = snapshot_->node_dispositions_.find(node);
if (it != snapshot_->node_dispositions_.end())
return it->second;
Snapshot::Disposition disposition = node->RecordSnapshot(content, this);
snapshot_->node_dispositions_[node] = disposition;
return disposition;
}
Snapshot::Disposition SnapshotBuilder::SnapshotRootAndDetectCycles(
const NodeDef* node,
const SceneContent* content) {
DCHECK(snapshot_);
DCHECK(node);
DCHECK(content);
DCHECK(node == content->GetRootNodeIfExists());
auto storage = snapshot_->node_dispositions_.emplace(
node, Snapshot::Disposition::kCycle);
if (!storage.second) {
if (storage.first->second == Snapshot::Disposition::kCycle)
cycle_ = content; // start unwinding, remember where to stop
return storage.first->second;
}
Snapshot::Disposition disposition = node->RecordSnapshot(content, this);
if (disposition == Snapshot::Disposition::kSuccess) {
snapshot_->node_dispositions_[node] = disposition;
return disposition;
}
// We cannot reuse the iterator in |storage.first| because it may have
// been invalidated by the call to |RecordSnapshot| due to rehashing so
// we must look up the node again just in case.
snapshot_->node_dispositions_[node] = Snapshot::Disposition::kBlocked;
if (disposition == Snapshot::Disposition::kCycle) {
DCHECK(cycle_);
if (block_log_) {
*block_log_ << "Scene blocked because it is part of a cycle: "
<< content->FormattedLabel() << std::endl;
}
if (cycle_ == content) {
cycle_ = nullptr; // found the ouroboros tail, stop unwinding
disposition = Snapshot::Disposition::kBlocked;
}
}
return disposition;
}
Snapshot::Disposition SnapshotBuilder::SnapshotScene(
const SceneDef* scene,
uint32_t version,
const SceneNodeDef* referrer_node,
const SceneContent* referrer_content) {
DCHECK(snapshot_);
DCHECK(scene);
DCHECK(referrer_node);
DCHECK(referrer_content);
// This function should only ever be called once when snapshotting the
// referring |SceneNodeDef| at which point the result will be memoized
// by |SnapshotNode| as usual so reentrance should not occur.
DCHECK(snapshot_->resolved_scene_contents_.find(referrer_node) ==
snapshot_->resolved_scene_contents_.end());
snapshot_->dependencies_.insert(scene->label().token());
const SceneContent* content = scene->FindContent(version);
if (!content) {
if (block_log_) {
*block_log_ << "Scene node blocked because its referenced scene is not "
"available with the requested version: "
<< referrer_node->FormattedLabel(referrer_content)
<< ", scene " << scene->label().FormattedLabel()
<< ", requested version " << version << ", current version "
<< scene->version() << std::endl;
}
return Snapshot::Disposition::kBlocked;
}
const NodeDef* root = content->GetRootNodeIfExists();
if (!root) {
if (block_log_) {
*block_log_ << "Scene node blocked because its referenced scene has no "
"root node: "
<< referrer_node->FormattedLabel(referrer_content)
<< ", scene " << content->FormattedLabel() << std::endl;
}
return Snapshot::Disposition::kBlocked;
}
snapshot_->resolved_scene_contents_[referrer_node] = content;
return SnapshotRootAndDetectCycles(root, content);
}
Snapshot::Disposition SnapshotBuilder::SnapshotRenderer(const SceneDef* scene) {
DCHECK(!snapshot_->root_scene_content_);
snapshot_->dependencies_.insert(scene->label().token());
const SceneContent* content =
scene->FindContent(mojo::gfx::composition::kSceneVersionNone);
if (!content) {
if (block_log_) {
*block_log_ << "Rendering blocked because the root scene has no content: "
<< scene->label().FormattedLabel() << std::endl;
}
return Snapshot::Disposition::kBlocked;
}
const NodeDef* root = content->GetRootNodeIfExists();
if (!root) {
if (block_log_) {
*block_log_ << "Rendering blocked the root scene has no root node: "
<< content->FormattedLabel() << std::endl;
}
return Snapshot::Disposition::kBlocked;
}
snapshot_->root_scene_content_ = content;
return SnapshotRootAndDetectCycles(root, content);
}
scoped_refptr<const Snapshot> SnapshotBuilder::Build(
const SceneDef* root_scene) {
DCHECK(snapshot_);
DCHECK(root_scene);
snapshot_->disposition_ = SnapshotRenderer(root_scene);
DCHECK(!cycle_); // must have properly unwound any cycles by now
if (snapshot_->is_blocked()) {
snapshot_->root_scene_content_ = nullptr;
snapshot_->resolved_scene_contents_.clear();
snapshot_->node_dispositions_.clear();
}
return std::move(snapshot_);
}
} // namespace compositor