blob: 66b86d15bfbb71fe278ff96d684a3df45c2f11c0 [file] [log] [blame]
// Copyright 2014 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/trees/draw_property_utils.h"
#include <stddef.h>
#include <vector>
#include "cc/base/math_util.h"
#include "cc/layers/draw_properties.h"
#include "cc/layers/layer.h"
#include "cc/layers/layer_impl.h"
#include "cc/trees/clip_node.h"
#include "cc/trees/effect_node.h"
#include "cc/trees/layer_tree_impl.h"
#include "cc/trees/property_tree.h"
#include "cc/trees/property_tree_builder.h"
#include "cc/trees/scroll_node.h"
#include "cc/trees/transform_node.h"
#include "ui/gfx/geometry/rect_conversions.h"
namespace cc {
namespace draw_property_utils {
namespace {
static bool IsRootLayer(const Layer* layer) {
return !layer->parent();
}
static bool IsRootLayer(const LayerImpl* layer) {
return layer->layer_tree_impl()->IsRootLayer(layer);
}
static void PostConcatSurfaceContentsScale(const EffectNode* effect_node,
gfx::Transform* transform) {
if (!effect_node) {
// This can happen when PaintArtifactCompositor builds property trees as it
// doesn't set effect ids on clip nodes.
return;
}
DCHECK(effect_node->has_render_surface);
transform->matrix().postScale(effect_node->surface_contents_scale.x(),
effect_node->surface_contents_scale.y(), 1.f);
}
static bool ConvertRectBetweenSurfaceSpaces(const PropertyTrees* property_trees,
int source_effect_id,
int dest_effect_id,
gfx::RectF clip_in_source_space,
gfx::RectF* clip_in_dest_space) {
const EffectNode* source_effect_node =
property_trees->effect_tree.Node(source_effect_id);
int source_transform_id = source_effect_node->transform_id;
const EffectNode* dest_effect_node =
property_trees->effect_tree.Node(dest_effect_id);
int dest_transform_id = dest_effect_node->transform_id;
gfx::Transform source_to_dest;
if (source_transform_id > dest_transform_id) {
if (property_trees->GetToTarget(source_transform_id, dest_effect_id,
&source_to_dest)) {
ConcatInverseSurfaceContentsScale(source_effect_node, &source_to_dest);
*clip_in_dest_space =
MathUtil::MapClippedRect(source_to_dest, clip_in_source_space);
} else {
return false;
}
} else {
if (property_trees->GetFromTarget(dest_transform_id, source_effect_id,
&source_to_dest)) {
PostConcatSurfaceContentsScale(dest_effect_node, &source_to_dest);
*clip_in_dest_space =
MathUtil::ProjectClippedRect(source_to_dest, clip_in_source_space);
} else {
return false;
}
}
return true;
}
static ConditionalClip ComputeTargetRectInLocalSpace(
gfx::RectF rect,
const PropertyTrees* property_trees,
int target_transform_id,
int local_transform_id,
const int target_effect_id) {
gfx::Transform target_to_local;
bool success = property_trees->GetFromTarget(
local_transform_id, target_effect_id, &target_to_local);
// If transform is not invertible, cannot apply clip.
if (!success)
return ConditionalClip{false, gfx::RectF()};
if (target_transform_id > local_transform_id)
return ConditionalClip{true, // is_clipped.
MathUtil::MapClippedRect(target_to_local, rect)};
return ConditionalClip{true, // is_clipped.
MathUtil::ProjectClippedRect(target_to_local, rect)};
}
static ConditionalClip ComputeLocalRectInTargetSpace(
gfx::RectF rect,
const PropertyTrees* property_trees,
int current_transform_id,
int target_transform_id,
int target_effect_id) {
gfx::Transform current_to_target;
if (!property_trees->GetToTarget(current_transform_id, target_effect_id,
&current_to_target)) {
// If transform is not invertible, cannot apply clip.
return ConditionalClip{false, gfx::RectF()};
}
if (current_transform_id > target_transform_id)
return ConditionalClip{true, // is_clipped.
MathUtil::MapClippedRect(current_to_target, rect)};
return ConditionalClip{true, // is_clipped.
MathUtil::ProjectClippedRect(current_to_target, rect)};
}
static ConditionalClip ComputeCurrentClip(const ClipNode* clip_node,
const PropertyTrees* property_trees,
int target_transform_id,
int target_effect_id) {
if (clip_node->transform_id != target_transform_id)
return ComputeLocalRectInTargetSpace(clip_node->clip, property_trees,
clip_node->transform_id,
target_transform_id, target_effect_id);
const EffectTree& effect_tree = property_trees->effect_tree;
gfx::RectF current_clip = clip_node->clip;
gfx::Vector2dF surface_contents_scale =
effect_tree.Node(target_effect_id)->surface_contents_scale;
// The viewport clip should not be scaled.
if (surface_contents_scale.x() > 0 && surface_contents_scale.y() > 0 &&
clip_node->transform_id != TransformTree::kRootNodeId)
current_clip.Scale(surface_contents_scale.x(), surface_contents_scale.y());
return ConditionalClip{true /* is_clipped */, current_clip};
}
static bool ApplyClipNodeToAccumulatedClip(const PropertyTrees* property_trees,
bool include_expanding_clips,
int target_id,
int target_transform_id,
const ClipNode* clip_node,
gfx::RectF* accumulated_clip) {
switch (clip_node->clip_type) {
case ClipNode::ClipType::APPLIES_LOCAL_CLIP: {
ConditionalClip current_clip = ComputeCurrentClip(
clip_node, property_trees, target_transform_id, target_id);
// If transform is not invertible, no clip will be applied.
if (!current_clip.is_clipped)
return false;
*accumulated_clip =
gfx::IntersectRects(*accumulated_clip, current_clip.clip_rect);
return true;
}
case ClipNode::ClipType::EXPANDS_CLIP: {
if (!include_expanding_clips)
return true;
// Bring the accumulated clip to the space of the expanding effect.
const EffectNode* expanding_effect_node =
property_trees->effect_tree.Node(
clip_node->clip_expander->target_effect_id());
gfx::RectF accumulated_clip_rect_in_expanding_space;
bool success = ConvertRectBetweenSurfaceSpaces(
property_trees, target_id, expanding_effect_node->id,
*accumulated_clip, &accumulated_clip_rect_in_expanding_space);
// If transform is not invertible, no clip will be applied.
if (!success)
return false;
// Do the expansion.
gfx::RectF expanded_clip_in_expanding_space =
gfx::RectF(clip_node->clip_expander->MapRectReverse(
gfx::ToEnclosingRect(accumulated_clip_rect_in_expanding_space),
property_trees));
// Put the expanded clip back into the original target space.
success = ConvertRectBetweenSurfaceSpaces(
property_trees, expanding_effect_node->id, target_id,
expanded_clip_in_expanding_space, accumulated_clip);
// If transform is not invertible, no clip will be applied.
if (!success)
return false;
return true;
}
}
NOTREACHED();
return true;
}
static ConditionalClip ComputeAccumulatedClip(PropertyTrees* property_trees,
bool include_expanding_clips,
int local_clip_id,
int target_id) {
ClipRectData* cached_data =
property_trees->FetchClipRectFromCache(local_clip_id, target_id);
if (cached_data->target_id != EffectTree::kInvalidNodeId) {
// Cache hit
return cached_data->clip;
}
cached_data->target_id = target_id;
const ClipTree& clip_tree = property_trees->clip_tree;
const ClipNode* clip_node = clip_tree.Node(local_clip_id);
const EffectTree& effect_tree = property_trees->effect_tree;
const EffectNode* target_node = effect_tree.Node(target_id);
int target_transform_id = target_node->transform_id;
bool cache_hit = false;
ConditionalClip cached_clip = ConditionalClip{false, gfx::RectF()};
ConditionalClip unclipped = ConditionalClip{false, gfx::RectF()};
// Collect all the clips that need to be accumulated.
std::stack<const ClipNode*, std::vector<const ClipNode*>> parent_chain;
// If target is not direct ancestor of clip, this will find least common
// ancestor between the target and the clip. Or, if the target has a
// contributing layer that escapes clip, this will find the nearest ancestor
// that doesn't.
while (target_node->clip_id > clip_node->id ||
effect_tree.GetRenderSurface(target_node->id)
->has_contributing_layer_that_escapes_clip()) {
target_node = effect_tree.Node(target_node->target_id);
}
// Collect clip nodes up to the least common ancestor or till we get a cache
// hit.
while (target_node->clip_id < clip_node->id) {
if (parent_chain.size() > 0) {
// Search the cache.
for (auto& data : clip_node->cached_clip_rects) {
if (data.target_id == target_id) {
cache_hit = true;
cached_clip = data.clip;
}
}
}
parent_chain.push(clip_node);
clip_node = clip_tree.parent(clip_node);
}
if (parent_chain.size() == 0) {
// No accumulated clip nodes.
cached_data->clip = unclipped;
return unclipped;
}
clip_node = parent_chain.top();
parent_chain.pop();
gfx::RectF accumulated_clip;
if (cache_hit && cached_clip.is_clipped) {
// Apply the first clip in parent_chain to the cached clip.
accumulated_clip = cached_clip.clip_rect;
bool success = ApplyClipNodeToAccumulatedClip(
property_trees, include_expanding_clips, target_id, target_transform_id,
clip_node, &accumulated_clip);
if (!success) {
// Singular transform
cached_data->clip = unclipped;
return unclipped;
}
} else {
// No cache hit or the cached clip has no clip to apply. We need to find
// the first clip that applies clip as there is no clip to expand.
while (clip_node->clip_type != ClipNode::ClipType::APPLIES_LOCAL_CLIP &&
parent_chain.size() > 0) {
clip_node = parent_chain.top();
parent_chain.pop();
}
if (clip_node->clip_type != ClipNode::ClipType::APPLIES_LOCAL_CLIP) {
// No clip to apply.
cached_data->clip = unclipped;
return unclipped;
}
ConditionalClip current_clip = ComputeCurrentClip(
clip_node, property_trees, target_transform_id, target_id);
if (!current_clip.is_clipped) {
// Singular transform
cached_data->clip = unclipped;
return unclipped;
}
accumulated_clip = current_clip.clip_rect;
}
// Apply remaining clips
while (parent_chain.size() > 0) {
clip_node = parent_chain.top();
parent_chain.pop();
bool success = ApplyClipNodeToAccumulatedClip(
property_trees, include_expanding_clips, target_id, target_transform_id,
clip_node, &accumulated_clip);
if (!success) {
// Singular transform
cached_data->clip = unclipped;
return unclipped;
}
}
ConditionalClip clip = ConditionalClip{
true /* is_clipped */,
accumulated_clip.IsEmpty() ? gfx::RectF() : accumulated_clip};
cached_data->clip = clip;
return clip;
}
static bool HasSingularTransform(int transform_tree_index,
const TransformTree& tree) {
const TransformNode* node = tree.Node(transform_tree_index);
return !node->is_invertible || !node->ancestors_are_invertible;
}
static int LowestCommonAncestor(int clip_id_1,
int clip_id_2,
const ClipTree* clip_tree) {
const ClipNode* clip_node_1 = clip_tree->Node(clip_id_1);
const ClipNode* clip_node_2 = clip_tree->Node(clip_id_2);
while (clip_node_1->id != clip_node_2->id) {
if (clip_node_1->id < clip_node_2->id)
clip_node_2 = clip_tree->parent(clip_node_2);
else
clip_node_1 = clip_tree->parent(clip_node_1);
}
return clip_node_1->id;
}
static void SetHasContributingLayerThatEscapesClip(int lca_clip_id,
int target_effect_id,
EffectTree* effect_tree) {
const EffectNode* effect_node = effect_tree->Node(target_effect_id);
// Find all ancestor targets starting from effect_node who are clipped by
// a descendant of lowest ancestor clip and set their
// has_contributing_layer_that_escapes_clip to true.
while (effect_node->clip_id > lca_clip_id) {
RenderSurfaceImpl* render_surface =
effect_tree->GetRenderSurface(effect_node->id);
DCHECK(render_surface);
render_surface->set_has_contributing_layer_that_escapes_clip(true);
effect_node = effect_tree->Node(effect_node->target_id);
}
}
template <typename LayerType>
static int TransformTreeIndexForBackfaceVisibility(LayerType* layer,
const TransformTree& tree) {
if (!layer->use_parent_backface_visibility())
return layer->transform_tree_index();
const TransformNode* node = tree.Node(layer->transform_tree_index());
return layer->id() == node->owning_layer_id ? tree.parent(node)->id
: node->id;
}
static bool IsTargetSpaceTransformBackFaceVisible(
Layer* layer,
int transform_tree_index,
const PropertyTrees* property_trees) {
// We do not skip back face invisible layers on main thread as target space
// transform will not be available here.
return false;
}
static bool IsTargetSpaceTransformBackFaceVisible(
LayerImpl* layer,
int transform_tree_index,
const PropertyTrees* property_trees) {
gfx::Transform to_target;
property_trees->GetToTarget(transform_tree_index,
layer->render_target_effect_tree_index(),
&to_target);
return to_target.IsBackFaceVisible();
}
template <typename LayerType>
static bool IsLayerBackFaceVisible(LayerType* layer,
int transform_tree_index,
const PropertyTrees* property_trees) {
const TransformNode* node =
property_trees->transform_tree.Node(transform_tree_index);
return layer->use_local_transform_for_backface_visibility()
? node->local.IsBackFaceVisible()
: IsTargetSpaceTransformBackFaceVisible(
layer, transform_tree_index, property_trees);
}
static inline bool TransformToScreenIsKnown(Layer* layer,
int transform_tree_index,
const TransformTree& tree) {
const TransformNode* node = tree.Node(transform_tree_index);
return !node->to_screen_is_potentially_animated;
}
static inline bool TransformToScreenIsKnown(LayerImpl* layer,
int transform_tree_index,
const TransformTree& tree) {
return true;
}
template <typename LayerType>
static bool LayerNeedsUpdateInternal(LayerType* layer,
bool layer_is_drawn,
const PropertyTrees* property_trees) {
// Layers can be skipped if any of these conditions are met.
// - is not drawn due to it or one of its ancestors being hidden (or having
// no copy requests).
// - does not draw content.
// - is transparent.
// - has empty bounds
// - the layer is not double-sided, but its back face is visible.
//
// Some additional conditions need to be computed at a later point after the
// recursion is finished.
// - the intersection of render_surface content and layer clip_rect is empty
// - the visible_layer_rect is empty
//
// Note, if the layer should not have been drawn due to being fully
// transparent, we would have skipped the entire subtree and never made it
// into this function, so it is safe to omit this check here.
if (!layer_is_drawn)
return false;
if (!layer->DrawsContent() || layer->bounds().IsEmpty())
return false;
// The layer should not be drawn if (1) it is not double-sided and (2) the
// back of the layer is known to be facing the screen.
const TransformTree& tree = property_trees->transform_tree;
if (layer->should_check_backface_visibility()) {
int backface_transform_id =
TransformTreeIndexForBackfaceVisibility(layer, tree);
// A layer with singular transform is not drawn. So, we can assume that its
// backface is not visible.
if (TransformToScreenIsKnown(layer, backface_transform_id, tree) &&
!HasSingularTransform(backface_transform_id, tree) &&
IsLayerBackFaceVisible(layer, backface_transform_id, property_trees))
return false;
}
return true;
}
template <typename LayerType>
static inline bool LayerShouldBeSkippedInternal(
LayerType* layer,
const TransformTree& transform_tree,
const EffectTree& effect_tree) {
const TransformNode* transform_node =
transform_tree.Node(layer->transform_tree_index());
const EffectNode* effect_node = effect_tree.Node(layer->effect_tree_index());
if (effect_node->has_render_surface && effect_node->subtree_has_copy_request)
return false;
// If the layer transform is not invertible, it should be skipped.
// TODO(ajuma): Correctly process subtrees with singular transform for the
// case where we may animate to a non-singular transform and wish to
// pre-raster.
return !transform_node->node_and_ancestors_are_animated_or_invertible ||
effect_node->hidden_by_backface_visibility || !effect_node->is_drawn;
}
template <typename LayerType>
static void UpdateElasticOverscrollInternal(
PropertyTrees* property_trees,
const LayerType* overscroll_elasticity_layer,
const gfx::Vector2dF& elastic_overscroll) {
if (!overscroll_elasticity_layer) {
DCHECK(elastic_overscroll.IsZero());
return;
}
TransformNode* node = property_trees->transform_tree.Node(
overscroll_elasticity_layer->transform_tree_index());
if (node->scroll_offset == gfx::ScrollOffset(elastic_overscroll))
return;
node->scroll_offset = gfx::ScrollOffset(elastic_overscroll);
node->needs_local_transform_update = true;
property_trees->transform_tree.set_needs_update(true);
}
#if DCHECK_IS_ON()
static void ValidatePageScaleLayer(const Layer* page_scale_layer) {
DCHECK_EQ(page_scale_layer->position().ToString(), gfx::PointF().ToString());
DCHECK_EQ(page_scale_layer->transform_origin().ToString(),
gfx::Point3F().ToString());
}
static void ValidatePageScaleLayer(const LayerImpl* page_scale_layer) {}
#endif
template <typename LayerType>
static void UpdatePageScaleFactorInternal(PropertyTrees* property_trees,
const LayerType* page_scale_layer,
float page_scale_factor,
float device_scale_factor,
gfx::Transform device_transform) {
if (property_trees->transform_tree.page_scale_factor() == page_scale_factor)
return;
property_trees->transform_tree.set_page_scale_factor(page_scale_factor);
DCHECK(page_scale_layer);
DCHECK_GE(page_scale_layer->transform_tree_index(),
TransformTree::kRootNodeId);
TransformNode* node = property_trees->transform_tree.Node(
page_scale_layer->transform_tree_index());
// TODO(enne): property trees can't ask the layer these things, but
// the page scale layer should *just* be the page scale.
#if DCHECK_IS_ON()
ValidatePageScaleLayer(page_scale_layer);
#endif
if (IsRootLayer(page_scale_layer)) {
// When the page scale layer is also the root layer, the node should also
// store the combined scale factor and not just the page scale factor.
float post_local_scale_factor = page_scale_factor * device_scale_factor;
node->post_local_scale_factor = post_local_scale_factor;
node->post_local = device_transform;
node->post_local.Scale(post_local_scale_factor, post_local_scale_factor);
} else {
node->post_local_scale_factor = page_scale_factor;
node->update_post_local_transform(gfx::PointF(), gfx::Point3F());
}
node->needs_local_transform_update = true;
property_trees->transform_tree.set_needs_update(true);
}
static gfx::Rect LayerDrawableContentRect(
const LayerImpl* layer,
const gfx::Rect& layer_bounds_in_target_space,
const gfx::Rect& clip_rect) {
if (layer->is_clipped())
return IntersectRects(layer_bounds_in_target_space, clip_rect);
return layer_bounds_in_target_space;
}
static void SetSurfaceIsClipped(const ClipTree& clip_tree,
RenderSurfaceImpl* render_surface) {
bool is_clipped;
if (render_surface->EffectTreeIndex() == EffectTree::kContentsRootNodeId) {
// Root render surface is always clipped.
is_clipped = true;
} else if (render_surface->has_contributing_layer_that_escapes_clip()) {
// We cannot clip a surface that has a contribuitng layer which escapes the
// clip.
is_clipped = false;
} else if (render_surface->ClipTreeIndex() ==
render_surface->render_target()->ClipTreeIndex()) {
// There is no clip between between the render surface and its target, so
// the surface need not be clipped.
is_clipped = false;
} else {
// If the clips between the render surface and its target only expand the
// clips and do not apply any new clip, we need not clip the render surface.
const ClipNode* clip_node = clip_tree.Node(render_surface->ClipTreeIndex());
is_clipped = clip_node->clip_type != ClipNode::ClipType::EXPANDS_CLIP;
}
render_surface->SetIsClipped(is_clipped);
}
static void SetSurfaceDrawOpacity(const EffectTree& tree,
RenderSurfaceImpl* render_surface) {
// Draw opacity of a surface is the product of opacities between the surface
// (included) and its target surface (excluded).
const EffectNode* node = tree.Node(render_surface->EffectTreeIndex());
float draw_opacity = tree.EffectiveOpacity(node);
for (node = tree.parent(node); node && !node->has_render_surface;
node = tree.parent(node)) {
draw_opacity *= tree.EffectiveOpacity(node);
}
render_surface->SetDrawOpacity(draw_opacity);
}
static float LayerDrawOpacity(const LayerImpl* layer, const EffectTree& tree) {
if (!layer->render_target())
return 0.f;
const EffectNode* target_node =
tree.Node(layer->render_target()->EffectTreeIndex());
const EffectNode* node = tree.Node(layer->effect_tree_index());
if (node == target_node)
return 1.f;
float draw_opacity = 1.f;
while (node != target_node) {
draw_opacity *= tree.EffectiveOpacity(node);
node = tree.parent(node);
}
return draw_opacity;
}
template <typename LayerType>
static gfx::Transform ScreenSpaceTransformInternal(LayerType* layer,
const TransformTree& tree) {
gfx::Transform xform(1, 0, 0, 1, layer->offset_to_transform_parent().x(),
layer->offset_to_transform_parent().y());
gfx::Transform ssxform = tree.ToScreen(layer->transform_tree_index());
xform.ConcatTransform(ssxform);
if (layer->should_flatten_transform_from_property_tree())
xform.FlattenTo2d();
return xform;
}
static void SetSurfaceClipRect(const ClipNode* parent_clip_node,
PropertyTrees* property_trees,
RenderSurfaceImpl* render_surface) {
if (!render_surface->is_clipped()) {
render_surface->SetClipRect(gfx::Rect());
return;
}
const EffectTree& effect_tree = property_trees->effect_tree;
const ClipTree& clip_tree = property_trees->clip_tree;
const EffectNode* effect_node =
effect_tree.Node(render_surface->EffectTreeIndex());
const EffectNode* target_node = effect_tree.Node(effect_node->target_id);
bool include_expanding_clips = false;
if (render_surface->EffectTreeIndex() == EffectTree::kContentsRootNodeId) {
render_surface->SetClipRect(
gfx::ToEnclosingRect(clip_tree.Node(effect_node->clip_id)->clip));
} else {
ConditionalClip accumulated_clip_rect =
ComputeAccumulatedClip(property_trees, include_expanding_clips,
effect_node->clip_id, target_node->id);
render_surface->SetClipRect(
gfx::ToEnclosingRect(accumulated_clip_rect.clip_rect));
}
}
static void SetSurfaceDrawTransform(const PropertyTrees* property_trees,
RenderSurfaceImpl* render_surface) {
const TransformTree& transform_tree = property_trees->transform_tree;
const EffectTree& effect_tree = property_trees->effect_tree;
const TransformNode* transform_node =
transform_tree.Node(render_surface->TransformTreeIndex());
const EffectNode* effect_node =
effect_tree.Node(render_surface->EffectTreeIndex());
// The draw transform of root render surface is identity tranform.
if (transform_node->id == TransformTree::kRootNodeId) {
render_surface->SetDrawTransform(gfx::Transform());
return;
}
gfx::Transform render_surface_transform;
const EffectNode* target_effect_node =
effect_tree.Node(effect_node->target_id);
property_trees->GetToTarget(transform_node->id, target_effect_node->id,
&render_surface_transform);
ConcatInverseSurfaceContentsScale(effect_node, &render_surface_transform);
render_surface->SetDrawTransform(render_surface_transform);
}
static gfx::Rect LayerVisibleRect(PropertyTrees* property_trees,
LayerImpl* layer) {
int effect_ancestor_with_copy_request =
property_trees->effect_tree.ClosestAncestorWithCopyRequest(
layer->effect_tree_index());
bool non_root_copy_request =
effect_ancestor_with_copy_request > EffectTree::kContentsRootNodeId;
gfx::Rect layer_content_rect = gfx::Rect(layer->bounds());
gfx::RectF accumulated_clip_in_root_space;
if (non_root_copy_request) {
bool include_expanding_clips = true;
ConditionalClip accumulated_clip = ComputeAccumulatedClip(
property_trees, include_expanding_clips, layer->clip_tree_index(),
effect_ancestor_with_copy_request);
if (!accumulated_clip.is_clipped)
return layer_content_rect;
accumulated_clip_in_root_space = accumulated_clip.clip_rect;
} else {
const ClipNode* clip_node =
property_trees->clip_tree.Node(layer->clip_tree_index());
accumulated_clip_in_root_space =
clip_node->cached_accumulated_rect_in_screen_space;
}
const EffectNode* root_effect_node =
non_root_copy_request
? property_trees->effect_tree.Node(effect_ancestor_with_copy_request)
: property_trees->effect_tree.Node(EffectTree::kContentsRootNodeId);
ConditionalClip accumulated_clip_in_layer_space =
ComputeTargetRectInLocalSpace(
accumulated_clip_in_root_space, property_trees,
root_effect_node->transform_id, layer->transform_tree_index(),
root_effect_node->id);
if (!accumulated_clip_in_layer_space.is_clipped) {
return layer_content_rect;
}
gfx::RectF clip_in_layer_space = accumulated_clip_in_layer_space.clip_rect;
clip_in_layer_space.Offset(-layer->offset_to_transform_parent());
gfx::Rect visible_rect = gfx::ToEnclosingRect(clip_in_layer_space);
visible_rect.Intersect(layer_content_rect);
return visible_rect;
}
static ConditionalClip LayerClipRect(PropertyTrees* property_trees,
LayerImpl* layer) {
const EffectTree* effect_tree = &property_trees->effect_tree;
const EffectNode* effect_node = effect_tree->Node(layer->effect_tree_index());
const EffectNode* target_node =
effect_node->has_render_surface
? effect_node
: effect_tree->Node(effect_node->target_id);
// TODO(weiliangc): When effect node has up to date render surface info on
// compositor thread, no need to check for resourceless draw mode
if (!property_trees->non_root_surfaces_enabled) {
target_node = effect_tree->Node(1);
}
bool include_expanding_clips = false;
return ComputeAccumulatedClip(property_trees, include_expanding_clips,
layer->clip_tree_index(), target_node->id);
}
static void UpdateRenderTarget(EffectTree* effect_tree,
bool can_render_to_separate_surface) {
for (int i = EffectTree::kContentsRootNodeId;
i < static_cast<int>(effect_tree->size()); ++i) {
EffectNode* node = effect_tree->Node(i);
if (i == EffectTree::kContentsRootNodeId) {
// Render target of the node corresponding to root is itself.
node->target_id = EffectTree::kContentsRootNodeId;
} else if (!can_render_to_separate_surface) {
node->target_id = EffectTree::kContentsRootNodeId;
} else if (effect_tree->parent(node)->has_render_surface) {
node->target_id = node->parent_id;
} else {
node->target_id = effect_tree->parent(node)->target_id;
}
}
}
static void UpdateScrollTree(ScrollTree* scroll_tree,
const LayerTreeHost* layer_tree_host) {
if (!scroll_tree->needs_update())
return;
for (int i = ScrollTree::kRootNodeId;
i < static_cast<int>(scroll_tree->size()); ++i) {
ScrollNode* scroll_node = scroll_tree->Node(i);
if (Layer* scroll_layer =
layer_tree_host->LayerById(scroll_node->owning_layer_id)) {
if (Layer* scroll_clip_layer = scroll_layer->scroll_clip_layer()) {
scroll_node->scroll_clip_layer_bounds = scroll_clip_layer->bounds();
}
}
}
}
static void ComputeClips(PropertyTrees* property_trees) {
DCHECK(!property_trees->transform_tree.needs_update());
ClipTree* clip_tree = &property_trees->clip_tree;
if (!clip_tree->needs_update())
return;
const int target_effect_id = EffectTree::kContentsRootNodeId;
const int target_transform_id = TransformTree::kRootNodeId;
const bool include_expanding_clips = true;
for (int i = ClipTree::kViewportNodeId;
i < static_cast<int>(clip_tree->size()); ++i) {
ClipNode* clip_node = clip_tree->Node(i);
// Clear the clip rect cache
clip_node->cached_clip_rects = std::vector<ClipRectData>(1);
if (clip_node->id == ClipTree::kViewportNodeId) {
clip_node->cached_accumulated_rect_in_screen_space = clip_node->clip;
continue;
}
ClipNode* parent_clip_node = clip_tree->parent(clip_node);
DCHECK(parent_clip_node);
gfx::RectF accumulated_clip =
parent_clip_node->cached_accumulated_rect_in_screen_space;
bool success = ApplyClipNodeToAccumulatedClip(
property_trees, include_expanding_clips, target_effect_id,
target_transform_id, clip_node, &accumulated_clip);
DCHECK(success);
clip_node->cached_accumulated_rect_in_screen_space = accumulated_clip;
}
clip_tree->set_needs_update(false);
}
} // namespace
void ConcatInverseSurfaceContentsScale(const EffectNode* effect_node,
gfx::Transform* transform) {
DCHECK(effect_node->has_render_surface);
if (effect_node->surface_contents_scale.x() != 0.0 &&
effect_node->surface_contents_scale.y() != 0.0)
transform->Scale(1.0 / effect_node->surface_contents_scale.x(),
1.0 / effect_node->surface_contents_scale.y());
}
bool LayerShouldBeSkipped(LayerImpl* layer,
const TransformTree& transform_tree,
const EffectTree& effect_tree) {
return LayerShouldBeSkippedInternal(layer, transform_tree, effect_tree);
}
bool LayerShouldBeSkipped(Layer* layer,
const TransformTree& transform_tree,
const EffectTree& effect_tree) {
return LayerShouldBeSkippedInternal(layer, transform_tree, effect_tree);
}
void FindLayersThatNeedUpdates(LayerTreeHost* layer_tree_host,
const PropertyTrees* property_trees,
LayerList* update_layer_list) {
const TransformTree& transform_tree = property_trees->transform_tree;
const EffectTree& effect_tree = property_trees->effect_tree;
for (auto* layer : *layer_tree_host) {
if (!IsRootLayer(layer) &&
LayerShouldBeSkipped(layer, transform_tree, effect_tree))
continue;
bool layer_is_drawn =
effect_tree.Node(layer->effect_tree_index())->is_drawn;
if (LayerNeedsUpdate(layer, layer_is_drawn, property_trees)) {
update_layer_list->push_back(layer);
}
// Append mask layers to the update layer list. They don't have valid
// visible rects, so need to get added after the above calculation.
if (Layer* mask_layer = layer->mask_layer())
update_layer_list->push_back(mask_layer);
}
}
void FindLayersThatNeedUpdates(LayerTreeImpl* layer_tree_impl,
const PropertyTrees* property_trees,
std::vector<LayerImpl*>* visible_layer_list) {
const TransformTree& transform_tree = property_trees->transform_tree;
const EffectTree& effect_tree = property_trees->effect_tree;
for (auto* layer_impl : *layer_tree_impl) {
if (!IsRootLayer(layer_impl) &&
LayerShouldBeSkipped(layer_impl, transform_tree, effect_tree))
continue;
bool layer_is_drawn =
effect_tree.Node(layer_impl->effect_tree_index())->is_drawn;
if (LayerNeedsUpdate(layer_impl, layer_is_drawn, property_trees))
visible_layer_list->push_back(layer_impl);
}
}
void ComputeTransforms(TransformTree* transform_tree) {
if (!transform_tree->needs_update())
return;
for (int i = TransformTree::kContentsRootNodeId;
i < static_cast<int>(transform_tree->size()); ++i)
transform_tree->UpdateTransforms(i);
transform_tree->set_needs_update(false);
}
void ComputeEffects(EffectTree* effect_tree) {
if (!effect_tree->needs_update())
return;
for (int i = EffectTree::kContentsRootNodeId;
i < static_cast<int>(effect_tree->size()); ++i)
effect_tree->UpdateEffects(i);
effect_tree->set_needs_update(false);
}
void UpdatePropertyTrees(LayerTreeHost* layer_tree_host,
PropertyTrees* property_trees,
bool can_render_to_separate_surface) {
DCHECK(layer_tree_host);
DCHECK(property_trees);
DCHECK_EQ(layer_tree_host->property_trees(), property_trees);
if (property_trees->non_root_surfaces_enabled !=
can_render_to_separate_surface) {
property_trees->non_root_surfaces_enabled = can_render_to_separate_surface;
property_trees->transform_tree.set_needs_update(true);
}
if (property_trees->transform_tree.needs_update()) {
property_trees->clip_tree.set_needs_update(true);
property_trees->effect_tree.set_needs_update(true);
}
UpdateScrollTree(&property_trees->scroll_tree, layer_tree_host);
ComputeTransforms(&property_trees->transform_tree);
ComputeEffects(&property_trees->effect_tree);
// Computation of clips uses ToScreen which is updated while computing
// transforms. So, ComputeTransforms should be before ComputeClips.
ComputeClips(property_trees);
}
void UpdatePropertyTreesAndRenderSurfaces(LayerImpl* root_layer,
PropertyTrees* property_trees,
bool can_render_to_separate_surface,
bool can_adjust_raster_scales) {
bool render_surfaces_need_update = false;
if (property_trees->non_root_surfaces_enabled !=
can_render_to_separate_surface) {
property_trees->non_root_surfaces_enabled = can_render_to_separate_surface;
property_trees->transform_tree.set_needs_update(true);
render_surfaces_need_update = true;
}
if (property_trees->can_adjust_raster_scales != can_adjust_raster_scales) {
property_trees->can_adjust_raster_scales = can_adjust_raster_scales;
property_trees->transform_tree.set_needs_update(true);
render_surfaces_need_update = true;
}
if (property_trees->transform_tree.needs_update()) {
property_trees->clip_tree.set_needs_update(true);
property_trees->effect_tree.set_needs_update(true);
}
if (render_surfaces_need_update) {
property_trees->effect_tree.UpdateRenderSurfaces(
root_layer->layer_tree_impl(),
property_trees->non_root_surfaces_enabled);
}
UpdateRenderTarget(&property_trees->effect_tree,
property_trees->non_root_surfaces_enabled);
ComputeTransforms(&property_trees->transform_tree);
ComputeEffects(&property_trees->effect_tree);
// Computation of clips uses ToScreen which is updated while computing
// transforms. So, ComputeTransforms should be before ComputeClips.
ComputeClips(property_trees);
}
bool LayerNeedsUpdate(Layer* layer,
bool layer_is_drawn,
const PropertyTrees* property_trees) {
return LayerNeedsUpdateInternal(layer, layer_is_drawn, property_trees);
}
bool LayerNeedsUpdate(LayerImpl* layer,
bool layer_is_drawn,
const PropertyTrees* property_trees) {
return LayerNeedsUpdateInternal(layer, layer_is_drawn, property_trees);
}
gfx::Transform DrawTransform(const LayerImpl* layer,
const TransformTree& transform_tree,
const EffectTree& effect_tree) {
// TransformTree::ToTarget computes transform between the layer's transform
// node and surface's transform node and scales it by the surface's content
// scale.
gfx::Transform xform;
if (transform_tree.property_trees()->non_root_surfaces_enabled)
transform_tree.property_trees()->GetToTarget(
layer->transform_tree_index(), layer->render_target_effect_tree_index(),
&xform);
else
xform = transform_tree.ToScreen(layer->transform_tree_index());
if (layer->should_flatten_transform_from_property_tree())
xform.FlattenTo2d();
xform.Translate(layer->offset_to_transform_parent().x(),
layer->offset_to_transform_parent().y());
return xform;
}
gfx::Transform ScreenSpaceTransform(const Layer* layer,
const TransformTree& tree) {
return ScreenSpaceTransformInternal(layer, tree);
}
gfx::Transform ScreenSpaceTransform(const LayerImpl* layer,
const TransformTree& tree) {
return ScreenSpaceTransformInternal(layer, tree);
}
void ComputeDrawPropertiesOfVisibleLayers(const LayerImplList* layer_list,
PropertyTrees* property_trees) {
// Compute transforms
for (LayerImpl* layer : *layer_list) {
const TransformNode* transform_node =
property_trees->transform_tree.Node(layer->transform_tree_index());
layer->draw_properties().screen_space_transform =
ScreenSpaceTransformInternal(layer, property_trees->transform_tree);
layer->draw_properties().target_space_transform = DrawTransform(
layer, property_trees->transform_tree, property_trees->effect_tree);
layer->draw_properties().screen_space_transform_is_animating =
transform_node->to_screen_is_potentially_animated;
}
// Compute effects and determine if render surfaces have contributing layers
// that escape clip.
for (LayerImpl* layer : *layer_list) {
layer->draw_properties().opacity =
LayerDrawOpacity(layer, property_trees->effect_tree);
RenderSurfaceImpl* render_target = layer->render_target();
int lca_clip_id = LowestCommonAncestor(layer->clip_tree_index(),
render_target->ClipTreeIndex(),
&property_trees->clip_tree);
if (lca_clip_id != render_target->ClipTreeIndex()) {
SetHasContributingLayerThatEscapesClip(lca_clip_id,
render_target->EffectTreeIndex(),
&property_trees->effect_tree);
}
}
// Compute clips and visible rects
for (LayerImpl* layer : *layer_list) {
ConditionalClip clip = LayerClipRect(property_trees, layer);
// is_clipped should be set before visible rect computation as it is used
// there.
layer->draw_properties().is_clipped = clip.is_clipped;
layer->draw_properties().clip_rect = gfx::ToEnclosingRect(clip.clip_rect);
layer->draw_properties().visible_layer_rect =
LayerVisibleRect(property_trees, layer);
}
// Compute drawable content rects
for (LayerImpl* layer : *layer_list) {
gfx::Rect bounds_in_target_space = MathUtil::MapEnclosingClippedRect(
layer->draw_properties().target_space_transform,
gfx::Rect(layer->bounds()));
layer->draw_properties().drawable_content_rect = LayerDrawableContentRect(
layer, bounds_in_target_space, layer->draw_properties().clip_rect);
}
}
void ComputeMaskDrawProperties(LayerImpl* mask_layer,
const PropertyTrees* property_trees) {
// Mask draw properties are used only for rastering, so most of the draw
// properties computed for other layers are not needed.
// Draw transform of a mask layer has to be a 2d scale.
// TODO(sunxd): the draw transform of a mask layer misses the "scale to fit"
// factor from mask layer to its parent. So does the screen space transform.
// It does not cause a problem because currently we only have 1:1 mask layer.
mask_layer->draw_properties().target_space_transform = DrawTransform(
mask_layer, property_trees->transform_tree, property_trees->effect_tree);
mask_layer->draw_properties().screen_space_transform =
ScreenSpaceTransformInternal(mask_layer,
property_trees->transform_tree);
mask_layer->draw_properties().visible_layer_rect =
gfx::Rect(mask_layer->bounds());
}
void ComputeSurfaceDrawProperties(PropertyTrees* property_trees,
RenderSurfaceImpl* render_surface) {
SetSurfaceIsClipped(property_trees->clip_tree, render_surface);
SetSurfaceDrawOpacity(property_trees->effect_tree, render_surface);
SetSurfaceDrawTransform(property_trees, render_surface);
render_surface->SetScreenSpaceTransform(
property_trees->ToScreenSpaceTransformWithoutSurfaceContentsScale(
render_surface->TransformTreeIndex(),
render_surface->EffectTreeIndex()));
const ClipNode* clip_node =
property_trees->clip_tree.Node(render_surface->ClipTreeIndex());
SetSurfaceClipRect(clip_node, property_trees, render_surface);
}
void UpdatePageScaleFactor(PropertyTrees* property_trees,
const LayerImpl* page_scale_layer,
float page_scale_factor,
float device_scale_factor,
const gfx::Transform device_transform) {
UpdatePageScaleFactorInternal(property_trees, page_scale_layer,
page_scale_factor, device_scale_factor,
device_transform);
}
void UpdatePageScaleFactor(PropertyTrees* property_trees,
const Layer* page_scale_layer,
float page_scale_factor,
float device_scale_factor,
const gfx::Transform device_transform) {
UpdatePageScaleFactorInternal(property_trees, page_scale_layer,
page_scale_factor, device_scale_factor,
device_transform);
}
void UpdateElasticOverscroll(PropertyTrees* property_trees,
const LayerImpl* overscroll_elasticity_layer,
const gfx::Vector2dF& elastic_overscroll) {
UpdateElasticOverscrollInternal(property_trees, overscroll_elasticity_layer,
elastic_overscroll);
}
void UpdateElasticOverscroll(PropertyTrees* property_trees,
const Layer* overscroll_elasticity_layer,
const gfx::Vector2dF& elastic_overscroll) {
UpdateElasticOverscrollInternal(property_trees, overscroll_elasticity_layer,
elastic_overscroll);
}
} // namespace draw_property_utils
} // namespace cc