Chromium Code Reviews| Index: services/gfx/compositor/graph/snapshot.cc |
| diff --git a/services/gfx/compositor/graph/snapshot.cc b/services/gfx/compositor/graph/snapshot.cc |
| index 935fa2055b790c2a5b402736cb9be84e08e02344..8e3834bd5f0e4c31a46c25c46a847b6d44e53748 100644 |
| --- a/services/gfx/compositor/graph/snapshot.cc |
| +++ b/services/gfx/compositor/graph/snapshot.cc |
| @@ -5,9 +5,10 @@ |
| #include "services/gfx/compositor/graph/snapshot.h" |
| #include "base/logging.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 "services/gfx/compositor/render/render_layer.h" |
| +#include "third_party/skia/include/core/SkPictureRecorder.h" |
| #include "third_party/skia/include/core/SkRect.h" |
| namespace compositor { |
| @@ -20,19 +21,42 @@ bool Snapshot::Invalidate() { |
| if (valid_) { |
| valid_ = false; |
| dependencies_.clear(); |
| - frame_.reset(); |
| + ClearContent(); |
| return true; |
| } |
| return false; |
| } |
| -bool Snapshot::InvalidateScene(SceneDef* scene_def) { |
| +bool Snapshot::InvalidateScene(const SceneDef* scene_def) { |
| DCHECK(scene_def); |
| + return valid_ && |
| + dependencies_.find(scene_def->label().token()) != |
| + dependencies_.end() && |
| + Invalidate(); |
| +} |
| - if (valid_ && dependencies_.find(scene_def) != dependencies_.end()) { |
| - return Invalidate(); |
| - } |
| - return false; |
| +void Snapshot::ClearContent() { |
| + root_scene_content_ = nullptr; |
| + resolved_scene_contents_.clear(); |
| + node_dispositions_.clear(); |
| + frame_.reset(); |
| +} |
| + |
| +bool Snapshot::IsBlocked(const NodeDef* node) const { |
| + DCHECK(valid_); |
| + 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(valid_); |
| + auto it = resolved_scene_contents_.find(scene_node); |
| + DCHECK(it != resolved_scene_contents_.end()); |
| + return it->second.get(); |
| } |
| SnapshotBuilder::SnapshotBuilder(std::ostream* block_log) |
| @@ -40,26 +64,162 @@ SnapshotBuilder::SnapshotBuilder(std::ostream* block_log) |
| SnapshotBuilder::~SnapshotBuilder() {} |
| -void SnapshotBuilder::AddSceneDependency(SceneDef* scene) { |
| +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 it = snapshot_->node_dispositions_.find(node); |
| + if (it != snapshot_->node_dispositions_.end()) { |
| + if (it->second == Snapshot::Disposition::kCycle) |
| + cycle_ = content; // start unwinding, remember where to stop |
| + return it->second; |
| + } |
| + |
| + snapshot_->node_dispositions_[node] = Snapshot::Disposition::kCycle; |
|
abarth
2016/03/01 17:21:26
There's also a pattern for doing this with one has
jeffbrown
2016/03/02 00:32:16
True. Note that here rehashing may occur after th
|
| + |
| + Snapshot::Disposition disposition = node->RecordSnapshot(content, this); |
| + if (disposition == Snapshot::Disposition::kSuccess) { |
| + snapshot_->node_dispositions_[node] = disposition; |
| + return disposition; |
| + } |
| + |
| + 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_); |
| - snapshot_->dependencies_.insert(scene); |
| + 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); |
| } |
| std::unique_ptr<Snapshot> SnapshotBuilder::Build( |
| - SceneDef* root_scene, |
| + const SceneDef* root_scene, |
| const mojo::Rect& viewport, |
| const mojo::gfx::composition::FrameInfo& frame_info) { |
| DCHECK(snapshot_); |
| DCHECK(root_scene); |
| - SkRect sk_viewport = |
| - SkRect::MakeXYWH(viewport.x, viewport.y, viewport.width, viewport.height); |
| - RenderLayerBuilder layer_builder(&sk_viewport); |
| - if (root_scene->Snapshot(this, &layer_builder)) { |
| + Snapshot::Disposition disposition = SnapshotRenderer(root_scene); |
| + DCHECK(!cycle_); // must have properly unwound any cycles by now |
| + |
| + if (disposition == Snapshot::Disposition::kSuccess) { |
| + SkRect sk_viewport = SkRect::MakeXYWH(viewport.x, viewport.y, |
| + viewport.width, viewport.height); |
| + SkPictureRecorder recorder; |
| + recorder.beginRecording(sk_viewport); |
| + |
| + const NodeDef* root_node = |
| + snapshot_->root_scene_content_->GetRootNodeIfExists(); |
| + DCHECK(root_node); // otherwise would have failed to snapshot |
| + root_node->RecordPicture(snapshot_->root_scene_content_.get(), |
| + snapshot_.get(), recorder.getRecordingCanvas()); |
| + |
| snapshot_->frame_ = |
| - RenderFrame::Create(layer_builder.Build(), sk_viewport, frame_info); |
| + RenderFrame::Create(skia::AdoptRef(recorder.endRecordingAsPicture()), |
| + sk_viewport, frame_info); |
| } else { |
| - snapshot_->valid_ = false; |
| + snapshot_->ClearContent(); |
| } |
| return std::move(snapshot_); |
| } |