// Copyright 2010 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 "cc/layers/layer.h" #include #include #include #include #include #include #include "base/atomic_sequence_num.h" #include "base/location.h" #include "base/metrics/histogram.h" #include "base/single_thread_task_runner.h" #include "base/strings/stringprintf.h" #include "base/time/time.h" #include "base/trace_event/trace_event.h" #include "cc/base/features.h" #include "cc/base/simple_enclosed_region.h" #include "cc/layers/layer_impl.h" #include "cc/layers/picture_layer.h" #include "cc/trees/clip_node.h" #include "cc/trees/draw_property_utils.h" #include "cc/trees/layer_tree_host.h" #include "cc/trees/layer_tree_impl.h" #include "cc/trees/mutator_host.h" #include "cc/trees/property_tree_builder.h" #include "cc/trees/scroll_node.h" #include "cc/trees/transform_node.h" #include "components/viz/common/frame_sinks/copy_output_request.h" #include "components/viz/common/frame_sinks/copy_output_result.h" #include "third_party/skia/include/core/SkImageFilter.h" #include "ui/gfx/geometry/rect_conversions.h" #include "ui/gfx/geometry/vector2d_conversions.h" namespace cc { struct SameSizeAsLayer : public base::RefCounted { private: SameSizeAsLayer(); virtual ~SameSizeAsLayer(); void* pointers[2]; struct { LayerList children; gfx::Rect update_rect; gfx::Size bounds; int layer_id; unsigned bitfields; SkColor background_color; Region non_fast_scrollable_region; TouchActionRegion touch_action_region; Region wheel_event_region; ElementId element_id; } inputs; void* layer_tree_inputs; int int_fields[6]; gfx::Vector2dF offset; unsigned bitfields; void* debug_info; }; static_assert(sizeof(Layer) == sizeof(SameSizeAsLayer), "Layer should stay small"); base::AtomicSequenceNumber g_next_layer_id; constexpr gfx::Transform Layer::kIdentityTransform; constexpr gfx::RoundedCornersF Layer::kNoRoundedCornersF; LayerDebugInfo::LayerDebugInfo() = default; LayerDebugInfo::LayerDebugInfo(const LayerDebugInfo&) = default; LayerDebugInfo::~LayerDebugInfo() = default; Layer::Inputs::Inputs(int layer_id) : layer_id(layer_id), hit_testable(false), contents_opaque(false), contents_opaque_for_text(false), is_drawable(false), double_sided(true), background_color(0) {} Layer::Inputs::~Inputs() = default; Layer::LayerTreeInputs::LayerTreeInputs() : masks_to_bounds(false), is_fast_rounded_corner(false), user_scrollable_horizontal(true), user_scrollable_vertical(true), trilinear_filtering(false), hide_layer_and_subtree(false), scrollable(false) {} Layer::LayerTreeInputs::~LayerTreeInputs() = default; scoped_refptr Layer::Create() { return base::WrapRefCounted(new Layer()); } Layer::Layer() : parent_(nullptr), layer_tree_host_(nullptr), // Layer IDs start from 1. inputs_(g_next_layer_id.GetNext() + 1), num_descendants_that_draw_content_(0), transform_tree_index_(TransformTree::kInvalidNodeId), effect_tree_index_(EffectTree::kInvalidNodeId), clip_tree_index_(ClipTree::kInvalidNodeId), scroll_tree_index_(ScrollTree::kInvalidNodeId), property_tree_sequence_number_(-1), ignore_set_needs_commit_(false), draws_content_(false), should_check_backface_visibility_(false), cache_render_surface_(false), force_render_surface_for_testing_(false), subtree_property_changed_(false), may_contain_video_(false), has_transform_node_(false), has_clip_node_(false), subtree_has_copy_request_(false) {} Layer::~Layer() { // Our parent should be holding a reference to us so there should be no // way for us to be destroyed while we still have a parent. DCHECK(!parent()); // Similarly we shouldn't have a layer tree host since it also keeps a // reference to us. DCHECK(!layer_tree_host()); // Remove the parent reference from all children and dependents. RemoveAllChildren(); } Layer::LayerTreeInputs& Layer::EnsureLayerTreeInputs() { DCHECK(!layer_tree_host_ || !layer_tree_host_->IsUsingLayerLists()); if (!layer_tree_inputs_) layer_tree_inputs_ = std::make_unique(); return *layer_tree_inputs_; } #if DCHECK_IS_ON() const Layer::LayerTreeInputs* Layer::layer_tree_inputs() const { DCHECK(!layer_tree_host_ || !layer_tree_host_->IsUsingLayerLists()); return layer_tree_inputs_.get(); } #endif void Layer::SetLayerTreeHost(LayerTreeHost* host) { if (layer_tree_host_ == host) return; bool property_tree_indices_invalid = false; if (layer_tree_host_) { layer_tree_host_->UnregisterLayer(this); if (inputs_.element_id) layer_tree_host_->UnregisterElement(inputs_.element_id); if (!layer_tree_host_->IsUsingLayerLists()) { layer_tree_host_->property_trees()->needs_rebuild = true; property_tree_indices_invalid = true; } } if (host) { host->RegisterLayer(this); if (inputs_.element_id) host->RegisterElement(inputs_.element_id, this); if (!host->IsUsingLayerLists()) { host->property_trees()->needs_rebuild = true; property_tree_indices_invalid = true; } } layer_tree_host_ = host; if (property_tree_indices_invalid) InvalidatePropertyTreesIndices(); // When changing hosts, the layer needs to commit its properties to the impl // side for the new host. SetNeedsPushProperties(); for (size_t i = 0; i < inputs_.children.size(); ++i) inputs_.children[i]->SetLayerTreeHost(host); if (host && !host->IsUsingLayerLists() && host->mutator_host()->IsElementAnimating(element_id())) { host->SetNeedsCommit(); } } void Layer::SetNeedsCommit() { if (!layer_tree_host_) return; SetNeedsPushProperties(); if (ignore_set_needs_commit_) return; layer_tree_host_->SetNeedsCommit(); } void Layer::SetDebugName(const std::string& name) { if (name.empty() && !debug_info_) return; EnsureDebugInfo().name = name; } void Layer::SetNeedsFullTreeSync() { if (!layer_tree_host_) return; layer_tree_host_->SetNeedsFullTreeSync(); } void Layer::SetNeedsPushProperties() { if (layer_tree_host_) layer_tree_host_->AddLayerShouldPushProperties(this); } bool Layer::IsPropertyChangeAllowed() const { if (!layer_tree_host_) return true; return !layer_tree_host_->in_paint_layer_contents(); } void Layer::CaptureContent(const gfx::Rect& rect, std::vector* content) {} sk_sp Layer::GetPicture() const { return nullptr; } void Layer::SetParent(Layer* layer) { DCHECK(!layer || !layer->HasAncestor(this)); parent_ = layer; SetLayerTreeHost(parent_ ? parent_->layer_tree_host() : nullptr); SetPropertyTreesNeedRebuild(); } void Layer::AddChild(scoped_refptr child) { InsertChild(child, inputs_.children.size()); } void Layer::InsertChild(scoped_refptr child, size_t index) { DCHECK(IsPropertyChangeAllowed()); child->RemoveFromParent(); AddDrawableDescendants(child->NumDescendantsThatDrawContent() + (child->DrawsContent() ? 1 : 0)); child->SetParent(this); child->SetSubtreePropertyChanged(); index = std::min(index, inputs_.children.size()); if (layer_tree_inputs_ && layer_tree_inputs_->mask_layer && index && index == inputs_.children.size()) { // Ensure that the mask layer is always the last child. DCHECK_EQ(mask_layer(), inputs_.children.back().get()); index--; } inputs_.children.insert(inputs_.children.begin() + index, child); SetNeedsFullTreeSync(); } void Layer::RemoveFromParent() { DCHECK(IsPropertyChangeAllowed()); if (parent_) parent_->RemoveChild(this); } void Layer::RemoveChild(Layer* child) { if (layer_tree_inputs_ && child == layer_tree_inputs_->mask_layer) { DCHECK(layer_tree_inputs()); layer_tree_inputs_->mask_layer = nullptr; } for (auto iter = inputs_.children.begin(); iter != inputs_.children.end(); ++iter) { if (iter->get() != child) continue; child->SetParent(nullptr); AddDrawableDescendants(-child->NumDescendantsThatDrawContent() - (child->DrawsContent() ? 1 : 0)); inputs_.children.erase(iter); SetNeedsFullTreeSync(); return; } } void Layer::ReorderChildren(LayerList* new_children_order) { #if DCHECK_IS_ON() base::flat_set children_set; for (const auto& child : *new_children_order) { DCHECK_EQ(child->parent(), this); children_set.insert(child.get()); } for (const auto& child : inputs_.children) DCHECK_GT(children_set.count(child.get()), 0u); #endif inputs_.children = std::move(*new_children_order); // We do not need to call SetSubtreePropertyChanged for each child here // since SetSubtreePropertyChanged includes SetNeedsPushProperties, but this // change is not included in properties pushing. for (const auto& child : inputs_.children) child->subtree_property_changed_ = true; SetNeedsFullTreeSync(); } void Layer::ReplaceChild(Layer* reference, scoped_refptr new_layer) { DCHECK(reference); DCHECK_EQ(reference->parent(), this); DCHECK(IsPropertyChangeAllowed()); if (reference == new_layer.get()) return; // Find the index of |reference| in |children_|. auto reference_it = std::find_if(inputs_.children.begin(), inputs_.children.end(), [reference](const scoped_refptr& layer) { return layer.get() == reference; }); DCHECK(reference_it != inputs_.children.end()); size_t reference_index = reference_it - inputs_.children.begin(); reference->RemoveFromParent(); if (new_layer.get()) { new_layer->RemoveFromParent(); InsertChild(new_layer, reference_index); } } void Layer::SetBounds(const gfx::Size& size) { DCHECK(IsPropertyChangeAllowed()); if (bounds() == size) return; inputs_.bounds = size; if (!layer_tree_host_) return; // Rounded corner clipping, bounds clipping and mask clipping can result in // new areas of subtrees being exposed on a bounds change. Ensure the damaged // areas are updated. Also, if the layer subtree (rooted at this layer) is // marked as capturable (via a valid SubtreeCaptureId), then the property tree // needs rebuild so that |EffectNode::subtree_size| is updated with the new // size of this layer. if (!layer_tree_host_->IsUsingLayerLists()) { if (subtree_capture_id().is_valid() || masks_to_bounds() || mask_layer() || HasRoundedCorner()) { SetSubtreePropertyChanged(); SetPropertyTreesNeedRebuild(); } if (scrollable()) { auto& scroll_tree = layer_tree_host_->property_trees()->scroll_tree; if (auto* scroll_node = scroll_tree.Node(scroll_tree_index_)) scroll_node->bounds = inputs_.bounds; else SetPropertyTreesNeedRebuild(); } } SetNeedsCommit(); } Layer* Layer::RootLayer() { Layer* layer = this; while (layer->parent()) layer = layer->parent(); return layer; } void Layer::RemoveAllChildren() { DCHECK(IsPropertyChangeAllowed()); while (inputs_.children.size()) { Layer* layer = inputs_.children[0].get(); DCHECK_EQ(this, layer->parent()); layer->RemoveFromParent(); } } void Layer::SetChildLayerList(LayerList new_children) { DCHECK(layer_tree_host_->IsUsingLayerLists()); // Early out without calling |LayerTreeHost::SetNeedsFullTreeSync| if no // layer has changed. if (children() == new_children) return; // Remove existing children that will not be in the new child list. { std::unordered_set children_to_remove; for (auto& existing_child : children()) children_to_remove.insert(existing_child.get()); for (auto& new_child : new_children) children_to_remove.erase(new_child.get()); for (auto* child : children_to_remove) { child->SetParent(nullptr); AddDrawableDescendants(-child->NumDescendantsThatDrawContent() - (child->DrawsContent() ? 1 : 0)); } } // Mark existing children as changed if their order changes. auto existing_child_it = children().begin(); for (auto& child : new_children) { if (child->parent() == this) { // Search forward in the existing child list to find the new child. existing_child_it = std::find(existing_child_it, children().end(), child); if (existing_child_it == children().end()) child->SetSubtreePropertyChanged(); } } // Process new children and mark them as changed. // Because this changes the child's parent, it must be after code that uses // |child->parent()| such as the above loop. for (auto& child : new_children) { if (child->parent() != this) { child->RemoveFromParent(); AddDrawableDescendants(child->NumDescendantsThatDrawContent() + (child->DrawsContent() ? 1 : 0)); child->SetParent(this); child->SetSubtreePropertyChanged(); } } inputs_.children = std::move(new_children); layer_tree_host_->SetNeedsFullTreeSync(); } bool Layer::HasAncestor(const Layer* ancestor) const { for (const Layer* layer = parent(); layer; layer = layer->parent()) { if (layer == ancestor) return true; } return false; } void Layer::RequestCopyOfOutput( std::unique_ptr request) { DCHECK(IsPropertyChangeAllowed()); auto& inputs = EnsureLayerTreeInputs(); if (request->has_source()) { const base::UnguessableToken& source = request->source(); auto it = std::find_if( inputs.copy_requests.begin(), inputs.copy_requests.end(), [&source](const std::unique_ptr& x) { return x->has_source() && x->source() == source; }); if (it != inputs.copy_requests.end()) inputs.copy_requests.erase(it); } inputs.copy_requests.push_back(std::move(request)); SetSubtreePropertyChanged(); SetPropertyTreesNeedRebuild(); SetNeedsCommit(); if (layer_tree_host_) layer_tree_host_->SetHasCopyRequest(true); } void Layer::SetSubtreeHasCopyRequest(bool subtree_has_copy_request) { subtree_has_copy_request_ = subtree_has_copy_request; } bool Layer::SubtreeHasCopyRequest() const { DCHECK(layer_tree_host_); // When the copy request is pushed to effect tree, we reset layer tree host's // has_copy_request but do not clear subtree_has_copy_request on individual // layers. return layer_tree_host_->has_copy_request() && subtree_has_copy_request_; } void Layer::SetBackgroundColor(SkColor background_color) { DCHECK(IsPropertyChangeAllowed()); if (inputs_.background_color == background_color) return; inputs_.background_color = background_color; SetPropertyTreesNeedRebuild(); SetNeedsCommit(); } void Layer::SetSafeOpaqueBackgroundColor(SkColor background_color) { DCHECK(IsPropertyChangeAllowed()); SkColor opaque_color = SkColorSetA(background_color, SK_AlphaOPAQUE); auto& inputs = EnsureLayerTreeInputs(); if (inputs.safe_opaque_background_color == opaque_color) return; inputs.safe_opaque_background_color = opaque_color; SetNeedsPushProperties(); } SkColor Layer::SafeOpaqueBackgroundColor() const { if (contents_opaque()) { if (!layer_tree_host_ || !layer_tree_host_->IsUsingLayerLists()) { // In layer tree mode, PropertyTreeBuilder should have calculated the safe // opaque background color and called SetSafeOpaqueBackgroundColor(). DCHECK(layer_tree_inputs()); DCHECK_EQ(SkColorGetA(layer_tree_inputs()->safe_opaque_background_color), SK_AlphaOPAQUE); return layer_tree_inputs()->safe_opaque_background_color; } // In layer list mode, the PropertyTreeBuilder algorithm doesn't apply // because it depends on the layer tree hierarchy. Instead we use // background_color() if it's not transparent, or layer_tree_host_'s // background_color(), with the alpha channel forced to be opaque. SkColor color = background_color() == SK_ColorTRANSPARENT ? layer_tree_host_->background_color() : background_color(); return SkColorSetA(color, SK_AlphaOPAQUE); } if (SkColorGetA(background_color()) == SK_AlphaOPAQUE) { // The layer is not opaque while the background color is, meaning that the // background color doesn't cover the whole layer. Use SK_ColorTRANSPARENT // to avoid intrusive checkerboard where the layer is not covered by the // background color. return SK_ColorTRANSPARENT; } return background_color(); } void Layer::SetMasksToBounds(bool masks_to_bounds) { DCHECK(IsPropertyChangeAllowed()); auto& inputs = EnsureLayerTreeInputs(); if (inputs.masks_to_bounds == masks_to_bounds) return; inputs.masks_to_bounds = masks_to_bounds; SetNeedsCommit(); SetPropertyTreesNeedRebuild(); SetSubtreePropertyChanged(); } void Layer::SetClipRect(const gfx::Rect& clip_rect) { DCHECK(IsPropertyChangeAllowed()); auto& inputs = EnsureLayerTreeInputs(); if (inputs.clip_rect == clip_rect) return; inputs.clip_rect = clip_rect; // If the clip bounds have been cleared, the property trees needs a rebuild. const bool force_rebuild = clip_rect.IsEmpty() || !has_clip_node_; SetSubtreePropertyChanged(); if (clip_tree_index() != ClipTree::kInvalidNodeId && !force_rebuild) { PropertyTrees* property_trees = layer_tree_host_->property_trees(); gfx::RectF effective_clip_rect = EffectiveClipRect(); if (ClipNode* node = property_trees->clip_tree.Node(clip_tree_index())) { node->clip = effective_clip_rect; node->clip += offset_to_transform_parent(); property_trees->clip_tree.set_needs_update(true); } if (HasRoundedCorner() && effect_tree_index() != EffectTree::kInvalidNodeId) { if (EffectNode* node = property_trees->effect_tree.Node(effect_tree_index())) { node->mask_filter_info = gfx::MaskFilterInfo(effective_clip_rect, corner_radii()); node->effect_changed = true; property_trees->effect_tree.set_needs_update(true); } } } else { SetPropertyTreesNeedRebuild(); } SetNeedsCommit(); } gfx::RectF Layer::EffectiveClipRect() { // If this does not have a clip rect set, then the subtree is clipped by // the bounds. const gfx::RectF layer_bounds = gfx::RectF(gfx::SizeF(bounds())); if (clip_rect().IsEmpty()) return layer_bounds; const gfx::RectF clip_rect_f(clip_rect()); // Layer needs to clip to its bounds as well apply a clip rect. Intersect the // two to get the effective clip. if (masks_to_bounds() || mask_layer() || filters().HasFilterThatMovesPixels()) return gfx::IntersectRects(layer_bounds, clip_rect_f); // Clip rect is the only clip effecting the layer. return clip_rect_f; } void Layer::SetMaskLayer(scoped_refptr mask_layer) { DCHECK(IsPropertyChangeAllowed()); auto& inputs = EnsureLayerTreeInputs(); if (inputs.mask_layer == mask_layer) return; if (inputs.mask_layer) { DCHECK_EQ(this, inputs.mask_layer->parent()); inputs.mask_layer->RemoveFromParent(); } // Clear mask_layer first and set it later because InsertChild() checks it to // ensure the mask layer is the last child. inputs.mask_layer = nullptr; if (mask_layer) { // The mask layer should not have any children. DCHECK(mask_layer->children().empty()); mask_layer->EnsureLayerTreeInputs().position = gfx::PointF(); mask_layer->SetIsDrawable(true); mask_layer->SetBlendMode(SkBlendMode::kDstIn); // This flag will be updated in PropertyTreeBuilder. mask_layer->SetIsBackdropFilterMask(false); AddChild(mask_layer); } inputs.mask_layer = mask_layer.get(); SetSubtreePropertyChanged(); } void Layer::SetFilters(const FilterOperations& filters) { DCHECK(IsPropertyChangeAllowed()); auto& inputs = EnsureLayerTreeInputs(); if (inputs.filters == filters) return; inputs.filters = filters; SetSubtreePropertyChanged(); SetPropertyTreesNeedRebuild(); SetNeedsCommit(); } void Layer::SetBackdropFilters(const FilterOperations& filters) { DCHECK(IsPropertyChangeAllowed()); auto& inputs = EnsureLayerTreeInputs(); if (inputs.backdrop_filters == filters) return; inputs.backdrop_filters = filters; SetSubtreePropertyChanged(); SetPropertyTreesNeedRebuild(); SetNeedsCommit(); } void Layer::SetBackdropFilterBounds(const gfx::RRectF& backdrop_filter_bounds) { EnsureLayerTreeInputs().backdrop_filter_bounds = backdrop_filter_bounds; } void Layer::ClearBackdropFilterBounds() { if (layer_tree_inputs()) layer_tree_inputs_->backdrop_filter_bounds.reset(); } void Layer::SetBackdropFilterQuality(const float quality) { EnsureLayerTreeInputs().backdrop_filter_quality = quality; } void Layer::SetRoundedCorner(const gfx::RoundedCornersF& corner_radii) { DCHECK(IsPropertyChangeAllowed()); auto& inputs = EnsureLayerTreeInputs(); if (inputs.corner_radii == corner_radii) return; inputs.corner_radii = corner_radii; SetSubtreePropertyChanged(); SetNeedsCommit(); PropertyTrees* property_trees = layer_tree_host_ ? layer_tree_host_->property_trees() : nullptr; EffectNode* node = nullptr; if (property_trees && effect_tree_index() != EffectTree::kInvalidNodeId && (node = property_trees->effect_tree.Node(effect_tree_index()))) { node->mask_filter_info = gfx::MaskFilterInfo(EffectiveClipRect(), corner_radii); node->effect_changed = true; property_trees->effect_tree.set_needs_update(true); } else { SetPropertyTreesNeedRebuild(); } } void Layer::SetIsFastRoundedCorner(bool enable) { DCHECK(IsPropertyChangeAllowed()); auto& inputs = EnsureLayerTreeInputs(); if (inputs.is_fast_rounded_corner == enable) return; inputs.is_fast_rounded_corner = enable; // If this layer does not have a rounded corner, then modifying this flag is // going to have no effect. if (!HasRoundedCorner()) return; SetSubtreePropertyChanged(); SetNeedsCommit(); SetPropertyTreesNeedRebuild(); } void Layer::SetOpacity(float opacity) { DCHECK(IsPropertyChangeAllowed()); DCHECK_GE(opacity, 0.f); DCHECK_LE(opacity, 1.f); auto& inputs = EnsureLayerTreeInputs(); if (inputs.opacity == opacity) return; // We need to force a property tree rebuild when opacity changes from 1 to a // non-1 value or vice-versa as render surfaces can change. bool force_rebuild = opacity == 1.f || inputs.opacity == 1.f; inputs.opacity = opacity; SetSubtreePropertyChanged(); if (layer_tree_host_) { if (!force_rebuild) { PropertyTrees* property_trees = layer_tree_host_->property_trees(); if (EffectNode* node = property_trees->effect_tree.Node(effect_tree_index())) { node->opacity = opacity; node->effect_changed = true; property_trees->effect_tree.set_needs_update(true); } } else { SetPropertyTreesNeedRebuild(); } } SetNeedsCommit(); } float Layer::EffectiveOpacity() const { if (!layer_tree_inputs()) return 1.0f; return layer_tree_inputs()->hide_layer_and_subtree ? 0.f : layer_tree_inputs()->opacity; } bool Layer::OpacityCanAnimateOnImplThread() const { return false; } void Layer::SetBlendMode(SkBlendMode blend_mode) { DCHECK(IsPropertyChangeAllowed()); auto& inputs = EnsureLayerTreeInputs(); if (inputs.blend_mode == blend_mode) return; // Allowing only blend modes that are defined in the CSS Compositing standard, // plus destination-in which is used to implement masks. // http://dev.w3.org/fxtf/compositing-1/#blending switch (blend_mode) { case SkBlendMode::kSrcOver: case SkBlendMode::kDstIn: case SkBlendMode::kScreen: case SkBlendMode::kOverlay: case SkBlendMode::kDarken: case SkBlendMode::kLighten: case SkBlendMode::kColorDodge: case SkBlendMode::kColorBurn: case SkBlendMode::kHardLight: case SkBlendMode::kSoftLight: case SkBlendMode::kDifference: case SkBlendMode::kExclusion: case SkBlendMode::kMultiply: case SkBlendMode::kHue: case SkBlendMode::kSaturation: case SkBlendMode::kColor: case SkBlendMode::kLuminosity: // supported blend modes break; case SkBlendMode::kClear: case SkBlendMode::kSrc: case SkBlendMode::kDst: case SkBlendMode::kDstOver: case SkBlendMode::kSrcIn: case SkBlendMode::kSrcOut: case SkBlendMode::kDstOut: case SkBlendMode::kSrcATop: case SkBlendMode::kDstATop: case SkBlendMode::kXor: case SkBlendMode::kPlus: case SkBlendMode::kModulate: // Porter Duff Compositing Operators are not yet supported // http://dev.w3.org/fxtf/compositing-1/#porterduffcompositingoperators NOTREACHED(); return; } inputs.blend_mode = blend_mode; SetNeedsCommit(); SetSubtreePropertyChanged(); SetPropertyTreesNeedRebuild(); } void Layer::SetHitTestable(bool should_hit_test) { DCHECK(IsPropertyChangeAllowed()); if (inputs_.hit_testable == should_hit_test) return; inputs_.hit_testable = should_hit_test; SetPropertyTreesNeedRebuild(); SetNeedsCommit(); } bool Layer::HitTestable() const { return inputs_.hit_testable; } void Layer::SetContentsOpaque(bool opaque) { DCHECK(IsPropertyChangeAllowed()); if (inputs_.contents_opaque == opaque) return; inputs_.contents_opaque = opaque; inputs_.contents_opaque_for_text = opaque; SetNeedsCommit(); SetSubtreePropertyChanged(); SetPropertyTreesNeedRebuild(); } void Layer::SetContentsOpaqueForText(bool opaque) { DCHECK(IsPropertyChangeAllowed()); if (inputs_.contents_opaque_for_text == opaque) return; DCHECK(!contents_opaque() || opaque); inputs_.contents_opaque_for_text = opaque; SetNeedsCommit(); } void Layer::SetPosition(const gfx::PointF& position) { DCHECK(!layer_tree_host_ || !layer_tree_host_->IsUsingLayerLists()); // The mask layer should always be at the same location as the masked layer // which is its parent, so its position should be always zero. if (parent() && parent()->mask_layer() == this) { DCHECK(this->position().IsOrigin()); return; } DCHECK(IsPropertyChangeAllowed()); auto& inputs = EnsureLayerTreeInputs(); if (inputs.position == position) return; inputs.position = position; if (!layer_tree_host_) return; SetSubtreePropertyChanged(); if (has_transform_node_) { TransformNode* transform_node = layer_tree_host_->property_trees()->transform_tree.Node( transform_tree_index_); // We should never set root layer's position to non-zero. DCHECK(parent()); transform_node->post_translation = position.OffsetFromOrigin() + parent()->offset_to_transform_parent(); transform_node->needs_local_transform_update = true; transform_node->transform_changed = true; layer_tree_host_->property_trees()->transform_tree.set_needs_update(true); } else { SetPropertyTreesNeedRebuild(); } SetNeedsCommit(); } bool Are2dAxisAligned(const gfx::Transform& a, const gfx::Transform& b) { if (a.IsScaleOrTranslation() && b.IsScaleOrTranslation()) { return true; } gfx::Transform inverse(gfx::Transform::kSkipInitialization); if (b.GetInverse(&inverse)) { inverse *= a; return inverse.Preserves2dAxisAlignment(); } else { // TODO(weiliangc): Should return false because b is not invertible. return a.Preserves2dAxisAlignment(); } } void Layer::SetTransform(const gfx::Transform& transform) { DCHECK(IsPropertyChangeAllowed()); auto& inputs = EnsureLayerTreeInputs(); if (inputs.transform == transform) return; SetSubtreePropertyChanged(); if (layer_tree_host_) { if (has_transform_node_) { TransformNode* transform_node = layer_tree_host_->property_trees()->transform_tree.Node( transform_tree_index_); // We need to trigger a rebuild if we could have affected 2d axis // alignment. We'll check to see if transform and inputs_.transform are // axis align with respect to one another. DCHECK_EQ(transform_tree_index(), transform_node->id); bool preserves_2d_axis_alignment = Are2dAxisAligned(inputs.transform, transform); transform_node->local = transform; transform_node->needs_local_transform_update = true; transform_node->transform_changed = true; layer_tree_host_->property_trees()->transform_tree.set_needs_update(true); if (!preserves_2d_axis_alignment) SetPropertyTreesNeedRebuild(); } else { SetPropertyTreesNeedRebuild(); } } inputs.transform = transform; SetNeedsCommit(); } void Layer::SetTransformOrigin(const gfx::Point3F& transform_origin) { DCHECK(IsPropertyChangeAllowed()); auto& inputs = EnsureLayerTreeInputs(); if (inputs.transform_origin == transform_origin) return; inputs.transform_origin = transform_origin; if (!layer_tree_host_) return; SetSubtreePropertyChanged(); if (has_transform_node_) { TransformNode* transform_node = layer_tree_host_->property_trees()->transform_tree.Node( transform_tree_index_); DCHECK_EQ(transform_tree_index(), transform_node->id); transform_node->origin = transform_origin; transform_node->needs_local_transform_update = true; transform_node->transform_changed = true; layer_tree_host_->property_trees()->transform_tree.set_needs_update(true); } else { SetPropertyTreesNeedRebuild(); } SetNeedsCommit(); } void Layer::SetScrollOffset(const gfx::ScrollOffset& scroll_offset) { DCHECK(IsPropertyChangeAllowed()); auto& inputs = EnsureLayerTreeInputs(); if (inputs.scroll_offset == scroll_offset) return; inputs.scroll_offset = scroll_offset; if (!layer_tree_host_) return; UpdatePropertyTreeScrollOffset(); SetNeedsCommit(); } void Layer::SetScrollOffsetFromImplSide( const gfx::ScrollOffset& scroll_offset) { DCHECK(IsPropertyChangeAllowed()); // This function only gets called during a BeginMainFrame, so there // is no need to call SetNeedsUpdate here. DCHECK(layer_tree_host_ && layer_tree_host_->CommitRequested()); auto& inputs = EnsureLayerTreeInputs(); if (inputs.scroll_offset == scroll_offset) return; inputs.scroll_offset = scroll_offset; UpdatePropertyTreeScrollOffset(); if (!inputs.did_scroll_callback.is_null()) inputs.did_scroll_callback.Run(scroll_offset, element_id()); // The callback could potentially change the layer structure: // "this" may have been destroyed during the process. } void Layer::UpdatePropertyTreeScrollOffset() { DCHECK(scrollable()); DCHECK(!layer_tree_host_->IsUsingLayerLists()); if (scroll_tree_index() == ScrollTree::kInvalidNodeId) { // Ensure the property trees just have not been built yet but are marked for // being built which will set the correct scroll offset values. DCHECK(layer_tree_host_->property_trees()->needs_rebuild); return; } // If a scroll node exists, it should have an associated transform node. DCHECK(transform_tree_index() != TransformTree::kInvalidNodeId); auto& property_trees = *layer_tree_host_->property_trees(); property_trees.scroll_tree.SetScrollOffset(element_id(), scroll_offset()); auto* transform_node = property_trees.transform_tree.Node(transform_tree_index()); DCHECK_EQ(transform_tree_index(), transform_node->id); transform_node->scroll_offset = scroll_offset(); transform_node->needs_local_transform_update = true; property_trees.transform_tree.set_needs_update(true); } void Layer::SetDidScrollCallback( base::RepeatingCallback callback) { EnsureLayerTreeInputs().did_scroll_callback = std::move(callback); } void Layer::SetSubtreeCaptureId(viz::SubtreeCaptureId subtree_id) { DCHECK(IsPropertyChangeAllowed()); auto& inputs = EnsureLayerTreeInputs(); if (inputs.subtree_capture_id == subtree_id) return; DCHECK(!inputs.subtree_capture_id.is_valid() || !subtree_id.is_valid()) << "Not allowed to change from a valid ID to another valid ID, as it may " "already be in use."; inputs.subtree_capture_id = subtree_id; SetPropertyTreesNeedRebuild(); SetNeedsCommit(); } void Layer::SetScrollable(const gfx::Size& bounds) { DCHECK(IsPropertyChangeAllowed()); auto& inputs = EnsureLayerTreeInputs(); if (inputs.scrollable && inputs.scroll_container_bounds == bounds) return; bool was_scrollable = inputs.scrollable; inputs.scrollable = true; inputs.scroll_container_bounds = bounds; if (!layer_tree_host_) return; auto& scroll_tree = layer_tree_host_->property_trees()->scroll_tree; auto* scroll_node = scroll_tree.Node(scroll_tree_index_); if (was_scrollable && scroll_node) scroll_node->container_bounds = inputs.scroll_container_bounds; else SetPropertyTreesNeedRebuild(); SetNeedsCommit(); } bool Layer::IsScrollbarLayerForTesting() const { return false; } void Layer::SetUserScrollable(bool horizontal, bool vertical) { DCHECK(IsPropertyChangeAllowed()); auto& inputs = EnsureLayerTreeInputs(); if (inputs.user_scrollable_horizontal == horizontal && inputs.user_scrollable_vertical == vertical) return; inputs.user_scrollable_horizontal = horizontal; inputs.user_scrollable_vertical = vertical; if (!layer_tree_host_) return; if (scrollable()) { auto& scroll_tree = layer_tree_host_->property_trees()->scroll_tree; if (auto* scroll_node = scroll_tree.Node(scroll_tree_index_)) { scroll_node->user_scrollable_horizontal = horizontal; scroll_node->user_scrollable_vertical = vertical; } else { SetPropertyTreesNeedRebuild(); } } SetNeedsCommit(); } bool Layer::GetUserScrollableHorizontal() const { // user_scrollable_horizontal is true by default. return !layer_tree_inputs() || layer_tree_inputs()->user_scrollable_horizontal; } bool Layer::GetUserScrollableVertical() const { // user_scrollable_vertical is true by default. return !layer_tree_inputs() || layer_tree_inputs()->user_scrollable_vertical; } void Layer::SetNonFastScrollableRegion(const Region& region) { DCHECK(IsPropertyChangeAllowed()); if (inputs_.non_fast_scrollable_region == region) return; inputs_.non_fast_scrollable_region = region; SetPropertyTreesNeedRebuild(); SetNeedsCommit(); } void Layer::SetTouchActionRegion(TouchActionRegion touch_action_region) { DCHECK(IsPropertyChangeAllowed()); if (inputs_.touch_action_region == touch_action_region) return; inputs_.touch_action_region = std::move(touch_action_region); SetPropertyTreesNeedRebuild(); SetNeedsCommit(); } void Layer::SetWheelEventRegion(Region wheel_event_region) { DCHECK(IsPropertyChangeAllowed()); if (inputs_.wheel_event_region == wheel_event_region) return; inputs_.wheel_event_region = std::move(wheel_event_region); SetNeedsCommit(); } void Layer::SetCacheRenderSurface(bool cache) { DCHECK(IsPropertyChangeAllowed()); if (cache_render_surface_ == cache) return; cache_render_surface_ = cache; SetPropertyTreesNeedRebuild(); SetNeedsCommit(); } RenderSurfaceReason Layer::GetRenderSurfaceReason() const { if (!layer_tree_host_) return RenderSurfaceReason::kNone; PropertyTrees* property_trees = layer_tree_host_->property_trees(); DCHECK(!property_trees->needs_rebuild); EffectNode* effect_node = property_trees->effect_tree.Node(this->effect_tree_index()); // Effect node can also be the effect node of an ancestor layer. // Check if this effect node was created for this layer specifically. if (!effect_node || (parent_ && this->effect_tree_index() == parent_->effect_tree_index())) { return RenderSurfaceReason::kNone; } return effect_node->render_surface_reason; } void Layer::SetForceRenderSurfaceForTesting(bool force) { DCHECK(IsPropertyChangeAllowed()); if (force_render_surface_for_testing_ == force) return; force_render_surface_for_testing_ = force; SetPropertyTreesNeedRebuild(); SetNeedsCommit(); } void Layer::SetTransformTreeIndex(int index) { DCHECK(IsPropertyChangeAllowed()); if (transform_tree_index_ == index) return; if (index == TransformTree::kInvalidNodeId) has_transform_node_ = false; transform_tree_index_ = index; SetNeedsPushProperties(); } int Layer::transform_tree_index() const { if (!layer_tree_host_ || layer_tree_host_->property_trees()->sequence_number != property_tree_sequence_number_) { return TransformTree::kInvalidNodeId; } return transform_tree_index_; } void Layer::SetClipTreeIndex(int index) { DCHECK(IsPropertyChangeAllowed()); if (clip_tree_index_ == index) return; clip_tree_index_ = index; SetNeedsPushProperties(); } int Layer::clip_tree_index() const { if (!layer_tree_host_ || layer_tree_host_->property_trees()->sequence_number != property_tree_sequence_number_) { return ClipTree::kInvalidNodeId; } return clip_tree_index_; } void Layer::SetEffectTreeIndex(int index) { DCHECK(IsPropertyChangeAllowed()); if (effect_tree_index_ == index) return; effect_tree_index_ = index; SetNeedsPushProperties(); } int Layer::effect_tree_index() const { if (!layer_tree_host_ || layer_tree_host_->property_trees()->sequence_number != property_tree_sequence_number_) { return EffectTree::kInvalidNodeId; } return effect_tree_index_; } void Layer::SetScrollTreeIndex(int index) { DCHECK(IsPropertyChangeAllowed()); if (scroll_tree_index_ == index) return; scroll_tree_index_ = index; SetNeedsPushProperties(); } int Layer::scroll_tree_index() const { if (!layer_tree_host_ || layer_tree_host_->property_trees()->sequence_number != property_tree_sequence_number_) { return ScrollTree::kInvalidNodeId; } return scroll_tree_index_; } void Layer::SetOffsetToTransformParent(gfx::Vector2dF offset) { if (offset_to_transform_parent_ == offset) return; offset_to_transform_parent_ = offset; SetNeedsPushProperties(); SetSubtreePropertyChanged(); } void Layer::InvalidatePropertyTreesIndices() { SetTransformTreeIndex(TransformTree::kInvalidNodeId); SetClipTreeIndex(ClipTree::kInvalidNodeId); SetEffectTreeIndex(EffectTree::kInvalidNodeId); SetScrollTreeIndex(ScrollTree::kInvalidNodeId); } void Layer::SetPropertyTreesNeedRebuild() { if (layer_tree_host_) layer_tree_host_->property_trees()->needs_rebuild = true; } LayerDebugInfo& Layer::EnsureDebugInfo() { if (!debug_info_) { debug_info_ = std::make_unique(); // We just enabled debug info collection. Force PushPropertiesTo() to ensure // the first layer tree snapshot contains the debug info. Otherwise we will // push debug_info when we have other changes to push. SetNeedsPushProperties(); } return *debug_info_; } void Layer::ClearDebugInfo() { if (!debug_info_) return; debug_info_.reset(); SetNeedsPushProperties(); } std::string Layer::DebugName() const { return debug_info_ ? debug_info_->name : ""; } std::string Layer::ToString() const { return base::StringPrintf( "layer_id: %d\n" " name: %s\n" " Bounds: %s\n" " ElementId: %s\n" " HitTestable: %d\n" " OffsetToTransformParent: %s\n" " clip_tree_index: %d\n" " effect_tree_index: %d\n" " scroll_tree_index: %d\n" " transform_tree_index: %d\n", id(), DebugName().c_str(), bounds().ToString().c_str(), element_id().ToString().c_str(), HitTestable(), offset_to_transform_parent().ToString().c_str(), clip_tree_index(), effect_tree_index(), scroll_tree_index(), transform_tree_index()); } void Layer::SetShouldCheckBackfaceVisibility( bool should_check_backface_visibility) { if (should_check_backface_visibility_ == should_check_backface_visibility) return; should_check_backface_visibility_ = should_check_backface_visibility; SetNeedsPushProperties(); } void Layer::SetIsDrawable(bool is_drawable) { DCHECK(IsPropertyChangeAllowed()); if (inputs_.is_drawable == is_drawable) return; inputs_.is_drawable = is_drawable; UpdateDrawsContent(HasDrawableContent()); } void Layer::SetHideLayerAndSubtree(bool hide) { DCHECK(IsPropertyChangeAllowed()); auto& inputs = EnsureLayerTreeInputs(); if (inputs.hide_layer_and_subtree == hide) return; inputs.hide_layer_and_subtree = hide; SetNeedsCommit(); SetPropertyTreesNeedRebuild(); SetSubtreePropertyChanged(); } void Layer::SetNeedsDisplayRect(const gfx::Rect& dirty_rect) { if (dirty_rect.IsEmpty()) return; SetNeedsPushProperties(); inputs_.update_rect.Union(dirty_rect); if (DrawsContent() && layer_tree_host_ && !ignore_set_needs_commit_) layer_tree_host_->SetNeedsUpdateLayers(); } bool Layer::IsSnappedToPixelGridInTarget() { return false; } void Layer::PushPropertiesTo(LayerImpl* layer) { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "Layer::PushPropertiesTo"); DCHECK(layer_tree_host_); // The element id should be set first because other setters may // depend on it. Referencing element id on a layer is // deprecated. http://crbug.com/709137 layer->SetElementId(inputs_.element_id); layer->SetHasTransformNode(has_transform_node_); layer->SetBackgroundColor(inputs_.background_color); layer->SetSafeOpaqueBackgroundColor(SafeOpaqueBackgroundColor()); layer->SetBounds(inputs_.bounds); layer->SetTransformTreeIndex(transform_tree_index()); layer->SetEffectTreeIndex(effect_tree_index()); layer->SetClipTreeIndex(clip_tree_index()); layer->SetScrollTreeIndex(scroll_tree_index()); layer->SetOffsetToTransformParent(offset_to_transform_parent_); layer->SetDrawsContent(DrawsContent()); layer->SetHitTestable(HitTestable()); // subtree_property_changed_ is propagated to all descendants while building // property trees. So, it is enough to check it only for the current layer. if (subtree_property_changed_) layer->NoteLayerPropertyChanged(); layer->set_may_contain_video(may_contain_video_); layer->SetNonFastScrollableRegion(inputs_.non_fast_scrollable_region); layer->SetTouchActionRegion(inputs_.touch_action_region); // TODO(https://crbug.com/841364): This block is optimized to avoid checks // for kWheelEventRegions. It will be simplified once kWheelEventRegions // feature flag is removed. EventListenerProperties mouse_wheel_props = layer_tree_host()->event_listener_properties( EventListenerClass::kMouseWheel); if ((mouse_wheel_props == EventListenerProperties::kBlocking || mouse_wheel_props == EventListenerProperties::kBlockingAndPassive) && !base::FeatureList::IsEnabled(::features::kWheelEventRegions)) layer->SetWheelEventHandlerRegion(Region(gfx::Rect(bounds()))); else layer->SetWheelEventHandlerRegion(inputs_.wheel_event_region); layer->SetContentsOpaque(inputs_.contents_opaque); layer->SetContentsOpaqueForText(inputs_.contents_opaque_for_text); layer->SetShouldCheckBackfaceVisibility(should_check_backface_visibility_); layer->UpdateScrollable(); // The property trees must be safe to access because they will be used below // to call |SetScrollOffsetClobberActiveValue|. DCHECK(layer->layer_tree_impl()->lifecycle().AllowsPropertyTreeAccess()); // When a scroll offset animation is interrupted the new scroll position on // the pending tree will clobber any impl-side scrolling occuring on the // active tree. To do so, avoid scrolling the pending tree along with it // instead of trying to undo that scrolling later. if (layer_tree_host_->mutator_host()->ScrollOffsetAnimationWasInterrupted( element_id())) { PropertyTrees* trees = layer->layer_tree_impl()->property_trees(); trees->scroll_tree.SetScrollOffsetClobberActiveValue(layer->element_id()); } layer->UnionUpdateRect(inputs_.update_rect); layer->SetNeedsPushProperties(); // debug_info_->invalidations, if exist, will be cleared in the function. layer->UpdateDebugInfo(debug_info_.get()); // Reset any state that should be cleared for the next update. subtree_property_changed_ = false; inputs_.update_rect = gfx::Rect(); } void Layer::TakeCopyRequests( std::vector>* requests) { if (!layer_tree_inputs()) return; for (std::unique_ptr& request : layer_tree_inputs_->copy_requests) { // Ensure the result callback is not invoked on the compositing thread. if (!request->has_result_task_runner()) { request->set_result_task_runner( layer_tree_host()->GetTaskRunnerProvider()->MainThreadTaskRunner()); } if (request->has_area()) { request->set_area( gfx::IntersectRects(request->area(), gfx::Rect(bounds()))); } requests->push_back(std::move(request)); } layer_tree_inputs_->copy_requests.clear(); } std::unique_ptr Layer::CreateLayerImpl(LayerTreeImpl* tree_impl) { return LayerImpl::Create(tree_impl, inputs_.layer_id); } bool Layer::DrawsContent() const { return draws_content_; } bool Layer::HasDrawableContent() const { return inputs_.is_drawable; } void Layer::UpdateDrawsContent(bool has_drawable_content) { bool draws_content = has_drawable_content; DCHECK(inputs_.is_drawable || !has_drawable_content); if (draws_content == draws_content_) return; if (parent()) parent()->AddDrawableDescendants(draws_content ? 1 : -1); draws_content_ = draws_content; SetPropertyTreesNeedRebuild(); SetNeedsCommit(); } int Layer::NumDescendantsThatDrawContent() const { return num_descendants_that_draw_content_; } bool Layer::Update() { DCHECK(layer_tree_host_); return false; } void Layer::SetSubtreePropertyChanged() { if (subtree_property_changed_) return; subtree_property_changed_ = true; SetNeedsPushProperties(); } void Layer::SetMayContainVideo(bool yes) { if (may_contain_video_ == yes) return; may_contain_video_ = yes; SetNeedsPushProperties(); } // OnAnimated is called due to an ongoing accelerated animation. // Since this animation is also being run on the compositor thread, there // is no need to request a commit to push this value over, so the value is // set directly rather than by calling Set. void Layer::OnFilterAnimated(const FilterOperations& filters) { EnsureLayerTreeInputs().filters = filters; } void Layer::OnBackdropFilterAnimated(const FilterOperations& backdrop_filters) { EnsureLayerTreeInputs().backdrop_filters = backdrop_filters; } void Layer::OnOpacityAnimated(float opacity) { EnsureLayerTreeInputs().opacity = opacity; } void Layer::OnTransformAnimated(const gfx::Transform& transform) { EnsureLayerTreeInputs().transform = transform; } void Layer::SetTrilinearFiltering(bool trilinear_filtering) { auto& inputs = EnsureLayerTreeInputs(); if (inputs.trilinear_filtering == trilinear_filtering) return; inputs.trilinear_filtering = trilinear_filtering; // When true, makes a RenderSurface which makes an effect node. SetPropertyTreesNeedRebuild(); // Adding a RenderSurface may change how things in the subtree appear, since // it flattens transforms. SetSubtreePropertyChanged(); SetNeedsCommit(); } void Layer::IncrementMirrorCount() { SetMirrorCount(mirror_count() + 1); } void Layer::DecrementMirrorCount() { SetMirrorCount(mirror_count() - 1); } void Layer::SetMirrorCount(int mirror_count) { auto& inputs = EnsureLayerTreeInputs(); if (inputs.mirror_count == mirror_count) return; DCHECK_LE(0, mirror_count); bool was_mirrored = inputs.mirror_count > 0; inputs.mirror_count = mirror_count; bool is_mirrored = inputs.mirror_count > 0; if (was_mirrored != is_mirrored) SetPropertyTreesNeedRebuild(); SetNeedsPushProperties(); } ElementListType Layer::GetElementTypeForAnimation() const { return ElementListType::ACTIVE; } void Layer::AddDrawableDescendants(int num) { DCHECK_GE(num_descendants_that_draw_content_, 0); DCHECK_GE(num_descendants_that_draw_content_ + num, 0); if (num == 0) return; num_descendants_that_draw_content_ += num; SetNeedsCommit(); if (parent()) parent()->AddDrawableDescendants(num); } void Layer::RunMicroBenchmark(MicroBenchmark* benchmark) {} void Layer::SetElementId(ElementId id) { DCHECK(IsPropertyChangeAllowed()); if (inputs_.element_id == id) return; TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "Layer::SetElementId", "element", id.ToString()); if (layer_tree_host_ && inputs_.element_id) layer_tree_host_->UnregisterElement(inputs_.element_id); inputs_.element_id = id; if (layer_tree_host_ && inputs_.element_id) layer_tree_host_->RegisterElement(inputs_.element_id, this); SetNeedsCommit(); } gfx::Transform Layer::ScreenSpaceTransform() const { DCHECK_NE(transform_tree_index_, TransformTree::kInvalidNodeId); return draw_property_utils::ScreenSpaceTransform( this, layer_tree_host_->property_trees()->transform_tree); } } // namespace cc