summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cc/layers/delegated_renderer_layer_impl.cc1
-rw-r--r--cc/layers/draw_properties.h5
-rw-r--r--cc/layers/layer.h3
-rw-r--r--cc/layers/layer_impl.cc5
-rw-r--r--cc/layers/layer_impl.h3
-rw-r--r--cc/trees/layer_tree_host_common.cc435
-rw-r--r--cc/trees/layer_tree_host_common_unittest.cc556
-rw-r--r--cc/trees/layer_tree_host_impl.cc8
8 files changed, 929 insertions, 87 deletions
diff --git a/cc/layers/delegated_renderer_layer_impl.cc b/cc/layers/delegated_renderer_layer_impl.cc
index 426dcbb..17a0c1c 100644
--- a/cc/layers/delegated_renderer_layer_impl.cc
+++ b/cc/layers/delegated_renderer_layer_impl.cc
@@ -421,6 +421,7 @@ void DelegatedRendererLayerImpl::AppendRenderPassQuads(
if (render_target() == this) {
DCHECK(!is_clipped());
DCHECK(render_surface());
+ DCHECK_EQ(0, num_unclipped_descendants());
output_shared_quad_state->clip_rect = MathUtil::MapClippedRect(
delegated_frame_to_target_transform,
output_shared_quad_state->clip_rect);
diff --git a/cc/layers/draw_properties.h b/cc/layers/draw_properties.h
index bb80dce..181d900 100644
--- a/cc/layers/draw_properties.h
+++ b/cc/layers/draw_properties.h
@@ -27,6 +27,7 @@ struct CC_EXPORT DrawProperties {
contents_scale_x(1.f),
contents_scale_y(1.f),
num_descendants_that_draw_content(0),
+ num_unclipped_descendants(0),
descendants_can_clip_selves(false),
can_draw_directly_to_backbuffer(false),
layer_or_descendant_has_copy_request(false) {}
@@ -88,6 +89,10 @@ struct CC_EXPORT DrawProperties {
// Does not include this layer itself, only its children and descendants.
int num_descendants_that_draw_content;
+ // Number of descendants with a clip parent that is our ancestor. NB - this
+ // does not include our clip children because they are clipped by us.
+ int num_unclipped_descendants;
+
// If true, every descendant in the sub-tree can clip itself without the
// need to use hardware sissoring or a new render target.
bool descendants_can_clip_selves;
diff --git a/cc/layers/layer.h b/cc/layers/layer.h
index ff3ff20..b659ab2 100644
--- a/cc/layers/layer.h
+++ b/cc/layers/layer.h
@@ -236,6 +236,9 @@ class CC_EXPORT Layer : public base::RefCounted<Layer>,
RenderSurface* render_surface() const {
return draw_properties_.render_surface.get();
}
+ int num_unclipped_descendants() const {
+ return draw_properties_.num_unclipped_descendants;
+ }
void SetScrollOffset(gfx::Vector2d scroll_offset);
gfx::Vector2d scroll_offset() const { return scroll_offset_; }
diff --git a/cc/layers/layer_impl.cc b/cc/layers/layer_impl.cc
index 3a92195..8dcf1ff 100644
--- a/cc/layers/layer_impl.cc
+++ b/cc/layers/layer_impl.cc
@@ -140,6 +140,9 @@ void LayerImpl::SetScrollParent(LayerImpl* parent) {
if (scroll_parent_ == parent)
return;
+ // Having both a scroll parent and a scroll offset delegate is unsupported.
+ DCHECK(!scroll_offset_delegate_);
+
if (scroll_parent_)
scroll_parent_->RemoveScrollChild(this);
@@ -988,6 +991,8 @@ void LayerImpl::UpdateScrollbarPositions() {
void LayerImpl::SetScrollOffsetDelegate(
LayerScrollOffsetDelegate* scroll_offset_delegate) {
+ // Having both a scroll parent and a scroll offset delegate is unsupported.
+ DCHECK(!scroll_parent_);
if (!scroll_offset_delegate && scroll_offset_delegate_) {
scroll_delta_ =
scroll_offset_delegate_->GetTotalScrollOffset() - scroll_offset_;
diff --git a/cc/layers/layer_impl.h b/cc/layers/layer_impl.h
index f06bfe4..dc7508d 100644
--- a/cc/layers/layer_impl.h
+++ b/cc/layers/layer_impl.h
@@ -317,6 +317,9 @@ class CC_EXPORT LayerImpl : LayerAnimationValueObserver {
RenderSurfaceImpl* render_surface() const {
return draw_properties_.render_surface.get();
}
+ int num_unclipped_descendants() const {
+ return draw_properties_.num_unclipped_descendants;
+ }
// The client should be responsible for setting bounds, content bounds and
// contents scale to appropriate values. LayerImpl doesn't calculate any of
diff --git a/cc/trees/layer_tree_host_common.cc b/cc/trees/layer_tree_host_common.cc
index dba7b50..70060b2 100644
--- a/cc/trees/layer_tree_host_common.cc
+++ b/cc/trees/layer_tree_host_common.cc
@@ -40,6 +40,29 @@ static void SortLayers(LayerImplList::iterator first,
layer_sorter->Sort(first, end);
}
+template <typename LayerType>
+static gfx::Vector2dF GetEffectiveScrollDelta(LayerType* layer) {
+ gfx::Vector2dF scroll_delta = layer->ScrollDelta();
+ // The scroll parent's scroll delta is the amount we've scrolled on the
+ // compositor thread since the commit for this layer tree's source frame.
+ // we last reported to the main thread. I.e., it's the discrepancy between
+ // a scroll parent's scroll delta and offset, so we must add it here.
+ if (layer->scroll_parent())
+ scroll_delta += layer->scroll_parent()->ScrollDelta();
+ return scroll_delta;
+}
+
+template <typename LayerType>
+static gfx::Vector2dF GetEffectiveTotalScrollOffset(LayerType* layer) {
+ gfx::Vector2dF offset = layer->TotalScrollOffset();
+ // The scroll parent's total scroll offset (scroll offset + scroll delta)
+ // can't be used because its scroll offset has already been applied to the
+ // scroll children's positions by the main thread layer positioning code.
+ if (layer->scroll_parent())
+ offset += layer->scroll_parent()->ScrollDelta();
+ return offset;
+}
+
inline gfx::Rect CalculateVisibleRectWithCachedLayerRect(
gfx::Rect target_surface_rect,
gfx::Rect layer_bound_rect,
@@ -90,6 +113,196 @@ gfx::Rect LayerTreeHostCommon::CalculateVisibleRect(
target_surface_rect, layer_bound_rect, layer_in_surface_space, transform);
}
+template <typename LayerType>
+static LayerType* NextTargetSurface(LayerType* layer) {
+ return layer->parent() ? layer->parent()->render_target() : 0;
+}
+
+// Given two layers, this function finds their respective render targets and,
+// computes a change of basis translation. It does this by accumulating the
+// translation components of the draw transforms of each target between the
+// ancestor and descendant. These transforms must be 2D translations, and this
+// requirement is enforced at every step.
+template <typename LayerType, typename RenderSurfaceType>
+static gfx::Vector2dF ComputeChangeOfBasisTranslation(
+ const LayerType& ancestor_layer,
+ const LayerType& descendant_layer) {
+ DCHECK(descendant_layer.HasAncestor(&ancestor_layer));
+ const LayerType* descendant_target = descendant_layer.render_target();
+ DCHECK(descendant_target);
+ const LayerType* ancestor_target = ancestor_layer.render_target();
+ DCHECK(ancestor_target);
+
+ gfx::Vector2dF translation;
+ for (const LayerType* target = descendant_target; target != ancestor_target;
+ target = NextTargetSurface(target))
+ translation += target->render_surface()->draw_transform().To2dTranslation();
+
+ return translation;
+}
+
+enum TranslateRectDirection {
+ TranslateRectDirectionToAncestor,
+ TranslateRectDirectionToDescendant
+};
+
+template <typename LayerType, typename RenderSurfaceType>
+static gfx::Rect TranslateRectToTargetSpace(const LayerType& ancestor_layer,
+ const LayerType& descendant_layer,
+ gfx::Rect rect,
+ TranslateRectDirection direction) {
+ gfx::Vector2dF translation =
+ ComputeChangeOfBasisTranslation<LayerType, RenderSurfaceType>(
+ ancestor_layer, descendant_layer);
+ if (direction == TranslateRectDirectionToDescendant)
+ translation.Scale(-1.f);
+ return gfx::ToEnclosingRect(
+ gfx::RectF(rect.origin() + translation, rect.size()));
+}
+
+// Attempts to update the clip rects for the given layer. If the layer has a
+// clip_parent, it may not inherit its immediate ancestor's clip.
+template <typename LayerType, typename RenderSurfaceType>
+static void UpdateClipRectsForClipChild(
+ const LayerType* layer,
+ gfx::Rect* clip_rect_in_parent_target_space,
+ bool* subtree_should_be_clipped) {
+ // If the layer has no clip_parent, or the ancestor is the same as its actual
+ // parent, then we don't need special clip rects. Bail now and leave the out
+ // parameters untouched.
+ const LayerType* clip_parent = layer->clip_parent();
+ if (!clip_parent || clip_parent == layer->parent())
+ return;
+
+ // The root layer is never a clip child.
+ DCHECK(layer->parent());
+
+ // Grab the cached values.
+ *clip_rect_in_parent_target_space = clip_parent->clip_rect();
+ *subtree_should_be_clipped = clip_parent->is_clipped();
+
+ // We may have to project the clip rect into our parent's target space. Note,
+ // it must be our parent's target space, not ours. For one, we haven't
+ // computed our transforms, so we couldn't put it in our space yet even if we
+ // wanted to. But more importantly, this matches the expectations of
+ // CalculateDrawPropertiesInternal. If we, say, create a render surface, these
+ // clip rects will want to be in its target space, not ours.
+ *clip_rect_in_parent_target_space =
+ TranslateRectToTargetSpace<LayerType, RenderSurfaceType>(
+ *clip_parent,
+ *layer->parent(),
+ *clip_rect_in_parent_target_space,
+ TranslateRectDirectionToDescendant);
+}
+
+// We collect an accumulated drawable content rect per render surface.
+// Typically, a layer will contribute to only one surface, the surface
+// associated with its render target. Clip children, however, may affect
+// several surfaces since there may be several surfaces between the clip child
+// and its parent.
+//
+// NB: we accumulate the layer's *clipped* drawable content rect.
+template <typename LayerType>
+struct AccumulatedSurfaceState {
+ explicit AccumulatedSurfaceState(LayerType* render_target)
+ : render_target(render_target) {}
+
+ // The accumulated drawable content rect for the surface associated with the
+ // given |render_target|.
+ gfx::Rect drawable_content_rect;
+
+ // The target owning the surface. (We hang onto the target rather than the
+ // surface so that we can DCHECK that the surface's draw transform is simply
+ // a translation when |render_target| reports that it has no unclipped
+ // descendants).
+ LayerType* render_target;
+};
+
+template <typename LayerType, typename RenderSurfaceType>
+void UpdateAccumulatedSurfaceState(
+ LayerType* layer,
+ gfx::Rect drawable_content_rect,
+ std::vector<AccumulatedSurfaceState<LayerType> >*
+ accumulated_surface_state) {
+ if (IsRootLayer(layer))
+ return;
+
+ // We will apply our drawable content rect to the accumulated rects for all
+ // surfaces between us and |render_target| (inclusive). This is either our
+ // clip parent's target if we are a clip child, or else simply our parent's
+ // target. We use our parent's target because we're either the owner of a
+ // render surface and we'll want to add our rect to our *surface's* target, or
+ // we're not and our target is the same as our parent's. In both cases, the
+ // parent's target gives us what we want.
+ LayerType* render_target = layer->clip_parent()
+ ? layer->clip_parent()->render_target()
+ : layer->parent()->render_target();
+
+ // If the layer owns a surface, then the content rect is in the wrong space.
+ // Instead, we will use the surface's DrawableContentRect which is in target
+ // space as required.
+ gfx::Rect target_rect = drawable_content_rect;
+ if (layer->render_surface()) {
+ target_rect =
+ gfx::ToEnclosedRect(layer->render_surface()->DrawableContentRect());
+ }
+
+ if (render_target->is_clipped()) {
+ gfx::Rect clip_rect = render_target->clip_rect();
+ // If the layer has a clip parent, the clip rect may be in the wrong space,
+ // so we'll need to transform it before it is applied.
+ if (layer->clip_parent()) {
+ clip_rect = TranslateRectToTargetSpace<LayerType, RenderSurfaceType>(
+ *layer->clip_parent(),
+ *layer,
+ clip_rect,
+ TranslateRectDirectionToDescendant);
+ }
+ target_rect.Intersect(clip_rect);
+ }
+
+ // We must have at least one entry in the vector for the root.
+ DCHECK_LT(0ul, accumulated_surface_state->size());
+
+ typedef typename std::vector<AccumulatedSurfaceState<LayerType> >
+ AccumulatedSurfaceStateVector;
+ typedef typename AccumulatedSurfaceStateVector::reverse_iterator
+ AccumulatedSurfaceStateIterator;
+ AccumulatedSurfaceStateIterator current_state =
+ accumulated_surface_state->rbegin();
+
+ // Add this rect to the accumulated content rect for all surfaces until we
+ // reach the target surface.
+ bool found_render_target = false;
+ for (; current_state != accumulated_surface_state->rend(); ++current_state) {
+ current_state->drawable_content_rect.Union(target_rect);
+
+ // If we've reached |render_target| our work is done and we can bail.
+ if (current_state->render_target == render_target) {
+ found_render_target = true;
+ break;
+ }
+
+ // Transform rect from the current target's space to the next.
+ LayerType* current_target = current_state->render_target;
+ DCHECK(current_target->render_surface());
+ const gfx::Transform& current_draw_transform =
+ current_target->render_surface()->draw_transform();
+
+ // If we have unclipped descendants, the draw transform is a translation.
+ DCHECK(current_target->num_unclipped_descendants() == 0 ||
+ current_draw_transform.IsIdentityOrTranslation());
+
+ target_rect = gfx::ToEnclosingRect(
+ MathUtil::MapClippedRect(current_draw_transform, target_rect));
+ }
+
+ // It is an error to not reach |render_target|. If this happens, it means that
+ // either the clip parent is not an ancestor of the clip child or the surface
+ // state vector is empty, both of which should be impossible.
+ DCHECK(found_render_target);
+}
+
template <typename LayerType> static inline bool IsRootLayer(LayerType* layer) {
return !layer->parent();
}
@@ -416,10 +629,6 @@ static bool SubtreeShouldRenderToSeparateSurface(
return false;
}
-static LayerImpl* NextTargetSurface(LayerImpl* layer) {
- return layer->parent() ? layer->parent()->render_target() : 0;
-}
-
// This function returns a translation matrix that can be applied on a vector
// that's in the layer's target surface coordinate, while the position offset is
// specified in some ancestor layer's coordinate.
@@ -550,9 +759,10 @@ gfx::Transform ComputeScrollCompensationForThisLayer(
//
gfx::Transform scroll_compensation_for_this_layer = parent_matrix; // Step 3
+ gfx::Vector2dF scroll_delta = GetEffectiveScrollDelta(scrolling_layer);
scroll_compensation_for_this_layer.Translate(
- scrolling_layer->ScrollDelta().x(),
- scrolling_layer->ScrollDelta().y()); // Step 2
+ scroll_delta.x(),
+ scroll_delta.y()); // Step 2
gfx::Transform inverse_parent_matrix(gfx::Transform::kSkipInitialization);
if (!parent_matrix.GetInverse(&inverse_parent_matrix)) {
@@ -601,8 +811,9 @@ gfx::Transform ComputeScrollCompensationMatrixForChildren(
// Avoid the overheads (including stack allocation and matrix
// initialization/copy) if we know that the scroll compensation doesn't need
// to be reset or adjusted.
+ gfx::Vector2dF scroll_delta = GetEffectiveScrollDelta(layer);
if (!layer->IsContainerForFixedPositionLayers() &&
- layer->ScrollDelta().IsZero() && !layer->render_surface())
+ scroll_delta.IsZero() && !layer->render_surface())
return current_scroll_compensation_matrix;
// Start as identity matrix.
@@ -616,7 +827,7 @@ gfx::Transform ComputeScrollCompensationMatrixForChildren(
// If the current layer has a non-zero scroll_delta, then we should compute
// its local scroll compensation and accumulate it to the
// next_scroll_compensation_matrix.
- if (!layer->ScrollDelta().IsZero()) {
+ if (!scroll_delta.IsZero()) {
gfx::Transform scroll_compensation_for_this_layer =
ComputeScrollCompensationForThisLayer(
layer, parent_matrix);
@@ -785,13 +996,17 @@ static inline void RemoveSurfaceForEarlyExit(
struct PreCalculateMetaInformationRecursiveData {
bool layer_or_descendant_has_copy_request;
+ int num_unclipped_descendants;
PreCalculateMetaInformationRecursiveData()
- : layer_or_descendant_has_copy_request(false) {}
+ : layer_or_descendant_has_copy_request(false),
+ num_unclipped_descendants(0) {}
void Merge(const PreCalculateMetaInformationRecursiveData& data) {
layer_or_descendant_has_copy_request |=
data.layer_or_descendant_has_copy_request;
+ num_unclipped_descendants +=
+ data.num_unclipped_descendants;
}
};
@@ -814,6 +1029,9 @@ static void PreCalculateMetaInformation(
descendants_can_clip_selves = false;
}
+ if (layer->clip_parent())
+ recursive_data->num_unclipped_descendants++;
+
for (size_t i = 0; i < layer->children().size(); ++i) {
LayerType* child_layer =
LayerTreeHostCommon::get_child_as_raw_ptr(layer->children(), i);
@@ -839,11 +1057,19 @@ static void PreCalculateMetaInformation(
recursive_data->Merge(data_for_child);
}
+ if (layer->clip_children()) {
+ int num_clip_children = layer->clip_children()->size();
+ DCHECK_GE(recursive_data->num_unclipped_descendants, num_clip_children);
+ recursive_data->num_unclipped_descendants -= num_clip_children;
+ }
+
if (layer->HasCopyRequest())
recursive_data->layer_or_descendant_has_copy_request = true;
layer->draw_properties().num_descendants_that_draw_content =
num_descendants_that_draw_content;
+ layer->draw_properties().num_unclipped_descendants =
+ recursive_data->num_unclipped_descendants;
layer->draw_properties().descendants_can_clip_selves =
descendants_can_clip_selves;
layer->draw_properties().layer_or_descendant_has_copy_request =
@@ -917,7 +1143,8 @@ static void CalculateDrawPropertiesInternal(
const DataForRecursion<LayerType, RenderSurfaceType>& data_from_ancestor,
LayerListType* render_surface_layer_list,
LayerListType* layer_list,
- gfx::Rect* drawable_content_rect_of_subtree) {
+ std::vector<AccumulatedSurfaceState<LayerType> >*
+ accumulated_surface_state) {
// This function computes the new matrix transformations recursively for this
// layer and all its descendants. It also computes the appropriate render
// surfaces.
@@ -1045,10 +1272,6 @@ static void CalculateDrawPropertiesInternal(
DCHECK(globals.page_scale_application_layer ||
(globals.page_scale_factor == 1.f));
- // If we early-exit anywhere in this function, the drawable_content_rect of
- // this subtree should be considered empty.
- *drawable_content_rect_of_subtree = gfx::Rect();
-
DataForRecursion<LayerType, RenderSurfaceType> data_for_children;
RenderSurfaceType* nearest_ancestor_surface_that_moves_pixels =
data_from_ancestor.nearest_ancestor_surface_that_moves_pixels;
@@ -1067,8 +1290,25 @@ static void CalculateDrawPropertiesInternal(
layer_is_visible = true;
// The root layer cannot skip CalcDrawProperties.
- if (!IsRootLayer(layer) && SubtreeShouldBeSkipped(layer, layer_is_visible))
+ if (!IsRootLayer(layer) && SubtreeShouldBeSkipped(layer, layer_is_visible)) {
+ if (layer->render_surface())
+ layer->ClearRenderSurface();
return;
+ }
+
+ // We need to circumvent the normal recursive flow of information for clip
+ // children (they don't inherit their direct ancestor's clip information).
+ // This is unfortunate, and would be unnecessary if we were to formally
+ // separate the clipping hierarchy from the layer hierarchy.
+ bool ancestor_clips_subtree = data_from_ancestor.ancestor_clips_subtree;
+ gfx::Rect ancestor_clip_rect_in_target_space =
+ data_from_ancestor.clip_rect_in_target_space;
+
+ // Update our clipping state. If we have a clip parent we will need to pull
+ // from the clip state cache rather than using the clip state passed from our
+ // immediate ancestor.
+ UpdateClipRectsForClipChild<LayerType, RenderSurfaceType>(
+ layer, &ancestor_clip_rect_in_target_space, &ancestor_clips_subtree);
// As this function proceeds, these are the properties for the current
// layer that actually get computed. To avoid unnecessary copies
@@ -1108,7 +1348,8 @@ static void CalculateDrawPropertiesInternal(
gfx::Size bounds = layer->bounds();
gfx::PointF anchor_point = layer->anchor_point();
- gfx::PointF position = layer->position() - layer->TotalScrollOffset();
+ gfx::Vector2dF scroll_offset = GetEffectiveTotalScrollOffset(layer);
+ gfx::PointF position = layer->position() - scroll_offset;
gfx::Transform combined_transform = data_from_ancestor.parent_matrix;
if (!layer->transform().IsIdentity()) {
@@ -1285,13 +1526,6 @@ static void CalculateDrawPropertiesInternal(
data_for_children.full_hierarchy_matrix.PreconcatTransform(
render_surface->draw_transform());
- // The new render_surface here will correctly clip the entire subtree. So,
- // we do not need to continue propagating the clipping state further down
- // the tree. This way, we can avoid transforming clip rects from ancestor
- // target surface space to current target surface space that could cause
- // more w < 0 headaches.
- layer_or_ancestor_clips_descendants = false;
-
if (layer->mask_layer()) {
DrawProperties<LayerType, RenderSurfaceType>& mask_layer_draw_properties =
layer->mask_layer()->draw_properties();
@@ -1314,14 +1548,15 @@ static void CalculateDrawPropertiesInternal(
if (layer->filters().HasFilterThatMovesPixels() || layer->filter())
nearest_ancestor_surface_that_moves_pixels = render_surface;
- // The render surface clip rect is expressed in the space where this surface
- // draws, i.e. the same space as
- // data_from_ancestor.clip_rect_in_target_space.
- render_surface->SetIsClipped(data_from_ancestor.ancestor_clips_subtree);
- if (data_from_ancestor.ancestor_clips_subtree) {
- render_surface->SetClipRect(
- data_from_ancestor.clip_rect_in_target_space);
+ render_surface->SetNearestAncestorThatMovesPixels(
+ nearest_ancestor_surface_that_moves_pixels);
+ layer_or_ancestor_clips_descendants = false;
+ bool subtree_is_clipped_by_surface_bounds = false;
+ if (ancestor_clips_subtree) {
+ // It may be the layer or the surface doing the clipping of the subtree,
+ // but in either case, we'll be clipping to the projected clip rect of our
+ // ancestor.
gfx::Transform inverse_surface_draw_transform(
gfx::Transform::kSkipInitialization);
if (!render_surface->draw_transform().GetInverse(
@@ -1329,18 +1564,48 @@ static void CalculateDrawPropertiesInternal(
// TODO(shawnsingh): Either we need to handle uninvertible transforms
// here, or DCHECK that the transform is invertible.
}
- clip_rect_of_target_surface_in_target_space =
- gfx::ToEnclosingRect(MathUtil::ProjectClippedRect(
- inverse_surface_draw_transform, render_surface->clip_rect()));
- } else {
+
+ gfx::Rect projected_surface_rect = gfx::ToEnclosingRect(
+ MathUtil::ProjectClippedRect(inverse_surface_draw_transform,
+ ancestor_clip_rect_in_target_space));
+
+ if (layer_draw_properties.num_unclipped_descendants > 0) {
+ // If we have unclipped descendants, we cannot count on the render
+ // surface's bounds clipping our subtree: the unclipped descendants
+ // could cause us to expand our bounds. In this case, we must rely on
+ // layer clipping for correctess. NB: since we can only encounter
+ // translations between a clip child and its clip parent, clipping is
+ // guaranteed to be exact in this case.
+ layer_or_ancestor_clips_descendants = true;
+ clip_rect_in_target_space = projected_surface_rect;
+ } else {
+ // The new render_surface here will correctly clip the entire subtree.
+ // So, we do not need to continue propagating the clipping state further
+ // down the tree. This way, we can avoid transforming clip rects from
+ // ancestor target surface space to current target surface space that
+ // could cause more w < 0 headaches. The render surface clip rect is
+ // expressed in the space where this surface draws, i.e. the same space
+ // as clip_rect_from_ancestor_in_ancestor_target_space.
+ render_surface->SetClipRect(ancestor_clip_rect_in_target_space);
+ clip_rect_of_target_surface_in_target_space = projected_surface_rect;
+ subtree_is_clipped_by_surface_bounds = true;
+ }
+ }
+
+ DCHECK(layer->render_surface());
+ DCHECK(!layer->parent() || layer->parent()->render_target() ==
+ accumulated_surface_state->back().render_target);
+
+ accumulated_surface_state->push_back(
+ AccumulatedSurfaceState<LayerType>(layer));
+
+ render_surface->SetIsClipped(subtree_is_clipped_by_surface_bounds);
+ if (!subtree_is_clipped_by_surface_bounds) {
render_surface->SetClipRect(gfx::Rect());
clip_rect_of_target_surface_in_target_space =
data_from_ancestor.clip_rect_of_target_surface_in_target_space;
}
- render_surface->SetNearestAncestorThatMovesPixels(
- nearest_ancestor_surface_that_moves_pixels);
-
// If the new render surface is drawn translucent or with a non-integral
// translation then the subtree that gets drawn on this render surface
// cannot use LCD text.
@@ -1366,11 +1631,10 @@ static void CalculateDrawPropertiesInternal(
// Layers without render_surfaces directly inherit the ancestor's clip
// status.
- layer_or_ancestor_clips_descendants =
- data_from_ancestor.ancestor_clips_subtree;
- if (data_from_ancestor.ancestor_clips_subtree) {
+ layer_or_ancestor_clips_descendants = ancestor_clips_subtree;
+ if (ancestor_clips_subtree) {
clip_rect_in_target_space =
- data_from_ancestor.clip_rect_in_target_space;
+ ancestor_clip_rect_in_target_space;
}
// The surface's cached clip rect value propagates regardless of what
@@ -1406,16 +1670,30 @@ static void CalculateDrawPropertiesInternal(
if (LayerClipsSubtree(layer)) {
layer_or_ancestor_clips_descendants = true;
- if (data_from_ancestor.ancestor_clips_subtree && !layer->render_surface()) {
+ if (ancestor_clips_subtree && !layer->render_surface()) {
// A layer without render surface shares the same target as its ancestor.
clip_rect_in_target_space =
- data_from_ancestor.clip_rect_in_target_space;
+ ancestor_clip_rect_in_target_space;
clip_rect_in_target_space.Intersect(rect_in_target_space);
} else {
clip_rect_in_target_space = rect_in_target_space;
}
}
+ // Tell the layer the rect that it's clipped by. In theory we could use a
+ // tighter clip rect here (drawable_content_rect), but that actually does not
+ // reduce how much would be drawn, and instead it would create unnecessary
+ // changes to scissor state affecting GPU performance. Our clip information
+ // is used in the recursion below, so we must set it beforehand.
+ layer_draw_properties.is_clipped = layer_or_ancestor_clips_descendants;
+ if (layer_or_ancestor_clips_descendants) {
+ layer_draw_properties.clip_rect = clip_rect_in_target_space;
+ } else {
+ // Initialize the clip rect to a safe value that will not clip the
+ // layer, just in case clipping is still accidentally used.
+ layer_draw_properties.clip_rect = rect_in_target_space;
+ }
+
LayerListType& descendants =
(layer->render_surface() ? layer->render_surface()->layer_list()
: *layer_list);
@@ -1470,7 +1748,6 @@ static void CalculateDrawPropertiesInternal(
data_for_children.subtree_is_visible_from_ancestor = layer_is_visible;
}
- gfx::Rect accumulated_drawable_content_rect_of_children;
for (size_t i = 0; i < layer->children().size(); ++i) {
LayerType* child =
LayerTreeHostCommon::get_child_as_raw_ptr(layer->children(), i);
@@ -1484,29 +1761,34 @@ static void CalculateDrawPropertiesInternal(
data_for_children,
render_surface_layer_list,
&descendants,
- &drawable_content_rect_of_child_subtree);
- if (!drawable_content_rect_of_child_subtree.IsEmpty()) {
- accumulated_drawable_content_rect_of_children.Union(
- drawable_content_rect_of_child_subtree);
- if (child->render_surface())
- descendants.push_back(child);
+ accumulated_surface_state);
+ if (child->render_surface() &&
+ !child->render_surface()->content_rect().IsEmpty()) {
+ descendants.push_back(child);
}
}
+ // Compute the total drawable_content_rect for this subtree (the rect is in
+ // target surface space).
+ gfx::Rect local_drawable_content_rect_of_subtree =
+ accumulated_surface_state->back().drawable_content_rect;
+ if (layer->render_surface()) {
+ DCHECK(accumulated_surface_state->back().render_target == layer);
+ accumulated_surface_state->pop_back();
+ }
+
if (layer->render_surface() && !IsRootLayer(layer) &&
layer->render_surface()->layer_list().empty()) {
RemoveSurfaceForEarlyExit(layer, render_surface_layer_list);
return;
}
- // Compute the total drawable_content_rect for this subtree (the rect is in
- // target surface space).
- gfx::Rect local_drawable_content_rect_of_subtree =
- accumulated_drawable_content_rect_of_children;
- if (layer->DrawsContent())
- local_drawable_content_rect_of_subtree.Union(rect_in_target_space);
- if (layer_or_ancestor_clips_descendants)
- local_drawable_content_rect_of_subtree.Intersect(clip_rect_in_target_space);
+ if (layer->DrawsContent()) {
+ gfx::Rect local_drawable_content_rect = rect_in_target_space;
+ if (layer_or_ancestor_clips_descendants)
+ local_drawable_content_rect.Intersect(clip_rect_in_target_space);
+ local_drawable_content_rect_of_subtree.Union(local_drawable_content_rect);
+ }
// Compute the layer's drawable content rect (the rect is in target surface
// space).
@@ -1516,19 +1798,6 @@ static void CalculateDrawPropertiesInternal(
Intersect(clip_rect_in_target_space);
}
- // Tell the layer the rect that is clipped by. In theory we could use a
- // tighter clip rect here (drawable_content_rect), but that actually does not
- // reduce how much would be drawn, and instead it would create unnecessary
- // changes to scissor state affecting GPU performance.
- layer_draw_properties.is_clipped = layer_or_ancestor_clips_descendants;
- if (layer_or_ancestor_clips_descendants) {
- layer_draw_properties.clip_rect = clip_rect_in_target_space;
- } else {
- // Initialize the clip rect to a safe value that will not clip the
- // layer, just in case clipping is still accidentally used.
- layer_draw_properties.clip_rect = rect_in_target_space;
- }
-
// Compute the layer's visible content rect (the rect is in content space).
layer_draw_properties.visible_content_rect = CalculateVisibleContentRect(
layer, clip_rect_of_target_surface_in_target_space, rect_in_target_space);
@@ -1539,7 +1808,7 @@ static void CalculateDrawPropertiesInternal(
// The root layer's surface's content_rect is always the entire viewport.
DCHECK(layer->render_surface());
layer->render_surface()->SetContentRect(
- data_from_ancestor.clip_rect_in_target_space);
+ ancestor_clip_rect_in_target_space);
} else if (layer->render_surface() && !IsRootLayer(layer)) {
RenderSurfaceType* render_surface = layer->render_surface();
gfx::Rect clipped_content_rect = local_drawable_content_rect_of_subtree;
@@ -1552,8 +1821,7 @@ static void CalculateDrawPropertiesInternal(
// Note, it is correct to use data_from_ancestor.ancestor_clips_subtree
// here, because we are looking at this layer's render_surface, not the
// layer itself.
- if (data_from_ancestor.ancestor_clips_subtree &&
- !clipped_content_rect.IsEmpty()) {
+ if (render_surface->is_clipped() && !clipped_content_rect.IsEmpty()) {
gfx::Rect surface_clip_rect = LayerTreeHostCommon::CalculateVisibleRect(
render_surface->clip_rect(),
clipped_content_rect,
@@ -1638,12 +1906,8 @@ static void CalculateDrawPropertiesInternal(
globals.layer_sorter);
}
- if (layer->render_surface()) {
- *drawable_content_rect_of_subtree =
- gfx::ToEnclosingRect(layer->render_surface()->DrawableContentRect());
- } else {
- *drawable_content_rect_of_subtree = local_drawable_content_rect_of_subtree;
- }
+ UpdateAccumulatedSurfaceState<LayerType, RenderSurfaceType>(
+ layer, local_drawable_content_rect_of_subtree, accumulated_surface_state);
if (layer->HasContributingDelegatedRenderPasses()) {
layer->render_target()->render_surface()->
@@ -1656,7 +1920,6 @@ void LayerTreeHostCommon::CalculateDrawProperties(
DCHECK(inputs->root_layer);
DCHECK(IsRootLayer(inputs->root_layer));
DCHECK(inputs->render_surface_layer_list);
- gfx::Rect total_drawable_content_rect;
gfx::Transform identity_matrix;
gfx::Transform scaled_device_transform = inputs->device_transform;
scaled_device_transform.Scale(inputs->device_scale_factor,
@@ -1691,14 +1954,14 @@ void LayerTreeHostCommon::CalculateDrawProperties(
PreCalculateMetaInformationRecursiveData recursive_data;
PreCalculateMetaInformation(inputs->root_layer, &recursive_data);
-
+ std::vector<AccumulatedSurfaceState<Layer> > accumulated_surface_state;
CalculateDrawPropertiesInternal<Layer, RenderSurfaceLayerList, RenderSurface>(
inputs->root_layer,
globals,
data_for_recursion,
inputs->render_surface_layer_list,
&dummy_layer_list,
- &total_drawable_content_rect);
+ &accumulated_surface_state);
// The dummy layer list should not have been used.
DCHECK_EQ(0u, dummy_layer_list.size());
@@ -1713,7 +1976,6 @@ void LayerTreeHostCommon::CalculateDrawProperties(
DCHECK(IsRootLayer(inputs->root_layer));
DCHECK(inputs->render_surface_layer_list);
- gfx::Rect total_drawable_content_rect;
gfx::Transform identity_matrix;
gfx::Transform scaled_device_transform = inputs->device_transform;
scaled_device_transform.Scale(inputs->device_scale_factor,
@@ -1749,14 +2011,15 @@ void LayerTreeHostCommon::CalculateDrawProperties(
PreCalculateMetaInformationRecursiveData recursive_data;
PreCalculateMetaInformation(inputs->root_layer, &recursive_data);
-
+ std::vector<AccumulatedSurfaceState<LayerImpl> >
+ accumulated_surface_state;
CalculateDrawPropertiesInternal<LayerImpl, LayerImplList, RenderSurfaceImpl>(
inputs->root_layer,
globals,
data_for_recursion,
inputs->render_surface_layer_list,
&dummy_layer_list,
- &total_drawable_content_rect);
+ &accumulated_surface_state);
// The dummy layer list should not have been used.
DCHECK_EQ(0u, dummy_layer_list.size());
diff --git a/cc/trees/layer_tree_host_common_unittest.cc b/cc/trees/layer_tree_host_common_unittest.cc
index 49b80e8..c275bc85 100644
--- a/cc/trees/layer_tree_host_common_unittest.cc
+++ b/cc/trees/layer_tree_host_common_unittest.cc
@@ -8350,5 +8350,561 @@ TEST_F(LayerTreeHostCommonTest, VisibleContentRectInsideSurface) {
surface_child->visible_content_rect().ToString());
}
+TEST_F(LayerTreeHostCommonTest, TransformedClipParent) {
+ // Ensure that a transform between the layer and its render surface is not a
+ // problem. Constructs the following layer tree.
+ //
+ // root (a render surface)
+ // + render_surface
+ // + clip_parent (scaled)
+ // + intervening_clipping_layer
+ // + clip_child
+ //
+ // The render surface should be resized correctly and the clip child should
+ // inherit the right clip rect.
+ scoped_refptr<Layer> root = Layer::Create();
+ scoped_refptr<Layer> render_surface = Layer::Create();
+ scoped_refptr<Layer> clip_parent = Layer::Create();
+ scoped_refptr<Layer> intervening = Layer::Create();
+ scoped_refptr<LayerWithForcedDrawsContent> clip_child =
+ make_scoped_refptr(new LayerWithForcedDrawsContent);
+
+ root->AddChild(render_surface);
+ render_surface->AddChild(clip_parent);
+ clip_parent->AddChild(intervening);
+ intervening->AddChild(clip_child);
+
+ clip_child->SetClipParent(clip_parent.get());
+
+ intervening->SetMasksToBounds(true);
+ clip_parent->SetMasksToBounds(true);
+
+ render_surface->SetForceRenderSurface(true);
+
+ gfx::Transform scale_transform;
+ scale_transform.Scale(2, 2);
+
+ gfx::Transform identity_transform;
+
+ SetLayerPropertiesForTesting(root.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(),
+ gfx::Size(50, 50),
+ false);
+ SetLayerPropertiesForTesting(render_surface.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(),
+ gfx::Size(10, 10),
+ false);
+ SetLayerPropertiesForTesting(clip_parent.get(),
+ scale_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(1.f, 1.f),
+ gfx::Size(10, 10),
+ false);
+ SetLayerPropertiesForTesting(intervening.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(1.f, 1.f),
+ gfx::Size(5, 5),
+ false);
+ SetLayerPropertiesForTesting(clip_child.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(1.f, 1.f),
+ gfx::Size(10, 10),
+ false);
+
+ scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create();
+ host->SetRootLayer(root);
+
+ ExecuteCalculateDrawProperties(root.get());
+
+ ASSERT_TRUE(root->render_surface());
+ ASSERT_TRUE(render_surface->render_surface());
+
+ // Ensure that we've inherited our clip parent's clip and weren't affected
+ // by the intervening clip layer.
+ ASSERT_EQ(gfx::Rect(1, 1, 20, 20).ToString(),
+ clip_parent->clip_rect().ToString());
+ ASSERT_EQ(clip_parent->clip_rect().ToString(),
+ clip_child->clip_rect().ToString());
+ ASSERT_EQ(gfx::Rect(3, 3, 10, 10).ToString(),
+ intervening->clip_rect().ToString());
+
+ // Ensure that the render surface reports a content rect that has been grown
+ // to accomodate for the clip child.
+ ASSERT_EQ(gfx::Rect(5, 5, 16, 16).ToString(),
+ render_surface->render_surface()->content_rect().ToString());
+
+ // The above check implies the two below, but they nicely demonstrate that
+ // we've grown, despite the intervening layer's clip.
+ ASSERT_TRUE(clip_parent->clip_rect().Contains(
+ render_surface->render_surface()->content_rect()));
+ ASSERT_FALSE(intervening->clip_rect().Contains(
+ render_surface->render_surface()->content_rect()));
+}
+
+TEST_F(LayerTreeHostCommonTest, ClipParentWithInterveningRenderSurface) {
+ // Ensure that intervening render surfaces are not a problem in the basic
+ // case. In the following tree, both render surfaces should be resized to
+ // accomodate for the clip child, despite an intervening clip.
+ //
+ // root (a render surface)
+ // + clip_parent (masks to bounds)
+ // + render_surface1 (sets opacity)
+ // + intervening (masks to bounds)
+ // + render_surface2 (also sets opacity)
+ // + clip_child
+ //
+ scoped_refptr<Layer> root = Layer::Create();
+ scoped_refptr<Layer> clip_parent = Layer::Create();
+ scoped_refptr<Layer> render_surface1 = Layer::Create();
+ scoped_refptr<Layer> intervening = Layer::Create();
+ scoped_refptr<Layer> render_surface2 = Layer::Create();
+ scoped_refptr<LayerWithForcedDrawsContent> clip_child =
+ make_scoped_refptr(new LayerWithForcedDrawsContent);
+
+ root->AddChild(clip_parent);
+ clip_parent->AddChild(render_surface1);
+ render_surface1->AddChild(intervening);
+ intervening->AddChild(render_surface2);
+ render_surface2->AddChild(clip_child);
+
+ clip_child->SetClipParent(clip_parent.get());
+
+ intervening->SetMasksToBounds(true);
+ clip_parent->SetMasksToBounds(true);
+
+ render_surface1->SetForceRenderSurface(true);
+ render_surface2->SetForceRenderSurface(true);
+
+ gfx::Transform translation_transform;
+ translation_transform.Translate(2, 2);
+
+ gfx::Transform identity_transform;
+ SetLayerPropertiesForTesting(root.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(),
+ gfx::Size(50, 50),
+ false);
+ SetLayerPropertiesForTesting(clip_parent.get(),
+ translation_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(1.f, 1.f),
+ gfx::Size(40, 40),
+ false);
+ SetLayerPropertiesForTesting(render_surface1.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(),
+ gfx::Size(10, 10),
+ false);
+ SetLayerPropertiesForTesting(intervening.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(1.f, 1.f),
+ gfx::Size(5, 5),
+ false);
+ SetLayerPropertiesForTesting(render_surface2.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(),
+ gfx::Size(10, 10),
+ false);
+ SetLayerPropertiesForTesting(clip_child.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(-10.f, -10.f),
+ gfx::Size(60, 60),
+ false);
+
+ scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create();
+ host->SetRootLayer(root);
+
+ ExecuteCalculateDrawProperties(root.get());
+
+ EXPECT_TRUE(root->render_surface());
+ EXPECT_TRUE(render_surface1->render_surface());
+ EXPECT_TRUE(render_surface2->render_surface());
+
+ // Since the render surfaces could have expanded, they should not clip (their
+ // bounds would no longer be reliable). We should resort to layer clipping
+ // in this case.
+ EXPECT_EQ(gfx::Rect(0, 0, 0, 0).ToString(),
+ render_surface1->render_surface()->clip_rect().ToString());
+ EXPECT_FALSE(render_surface1->render_surface()->is_clipped());
+ EXPECT_EQ(gfx::Rect(0, 0, 0, 0).ToString(),
+ render_surface2->render_surface()->clip_rect().ToString());
+ EXPECT_FALSE(render_surface2->render_surface()->is_clipped());
+
+ // NB: clip rects are in target space.
+ EXPECT_EQ(gfx::Rect(0, 0, 40, 40).ToString(),
+ render_surface1->clip_rect().ToString());
+ EXPECT_TRUE(render_surface1->is_clipped());
+
+ // This value is inherited from the clipping ancestor layer, 'intervening'.
+ EXPECT_EQ(gfx::Rect(0, 0, 5, 5).ToString(),
+ render_surface2->clip_rect().ToString());
+ EXPECT_TRUE(render_surface2->is_clipped());
+
+ // The content rects of both render surfaces should both have expanded to
+ // contain the clip child.
+ EXPECT_EQ(gfx::Rect(0, 0, 40, 40).ToString(),
+ render_surface1->render_surface()->content_rect().ToString());
+ EXPECT_EQ(gfx::Rect(-1, -1, 40, 40).ToString(),
+ render_surface2->render_surface()->content_rect().ToString());
+
+ // The clip child should have inherited the clip parent's clip (projected to
+ // the right space, of course), and should have the correctly sized visible
+ // content rect.
+ EXPECT_EQ(gfx::Rect(-1, -1, 40, 40).ToString(),
+ clip_child->clip_rect().ToString());
+ EXPECT_EQ(gfx::Rect(9, 9, 40, 40).ToString(),
+ clip_child->visible_content_rect().ToString());
+ EXPECT_TRUE(clip_child->is_clipped());
+}
+
+TEST_F(LayerTreeHostCommonTest, ClipParentScrolledInterveningLayer) {
+ // Ensure that intervening render surfaces are not a problem, even if there
+ // is a scroll involved. Note, we do _not_ have to consider any other sort
+ // of transform.
+ //
+ // root (a render surface)
+ // + clip_parent (masks to bounds)
+ // + render_surface1 (sets opacity)
+ // + intervening (masks to bounds AND scrolls)
+ // + render_surface2 (also sets opacity)
+ // + clip_child
+ //
+ scoped_refptr<Layer> root = Layer::Create();
+ scoped_refptr<Layer> clip_parent = Layer::Create();
+ scoped_refptr<Layer> render_surface1 = Layer::Create();
+ scoped_refptr<Layer> intervening = Layer::Create();
+ scoped_refptr<Layer> render_surface2 = Layer::Create();
+ scoped_refptr<LayerWithForcedDrawsContent> clip_child =
+ make_scoped_refptr(new LayerWithForcedDrawsContent);
+
+ root->AddChild(clip_parent);
+ clip_parent->AddChild(render_surface1);
+ render_surface1->AddChild(intervening);
+ intervening->AddChild(render_surface2);
+ render_surface2->AddChild(clip_child);
+
+ clip_child->SetClipParent(clip_parent.get());
+
+ intervening->SetMasksToBounds(true);
+ clip_parent->SetMasksToBounds(true);
+ intervening->SetScrollable(true);
+ intervening->SetMaxScrollOffset(gfx::Vector2d(50, 50));
+ intervening->SetScrollOffset(gfx::Vector2d(3, 3));
+
+ render_surface1->SetForceRenderSurface(true);
+ render_surface2->SetForceRenderSurface(true);
+
+ gfx::Transform translation_transform;
+ translation_transform.Translate(2, 2);
+
+ gfx::Transform identity_transform;
+ SetLayerPropertiesForTesting(root.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(),
+ gfx::Size(50, 50),
+ false);
+ SetLayerPropertiesForTesting(clip_parent.get(),
+ translation_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(1.f, 1.f),
+ gfx::Size(40, 40),
+ false);
+ SetLayerPropertiesForTesting(render_surface1.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(),
+ gfx::Size(10, 10),
+ false);
+ SetLayerPropertiesForTesting(intervening.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(1.f, 1.f),
+ gfx::Size(5, 5),
+ false);
+ SetLayerPropertiesForTesting(render_surface2.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(),
+ gfx::Size(10, 10),
+ false);
+ SetLayerPropertiesForTesting(clip_child.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(-10.f, -10.f),
+ gfx::Size(60, 60),
+ false);
+
+ scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create();
+ host->SetRootLayer(root);
+
+ ExecuteCalculateDrawProperties(root.get());
+
+ EXPECT_TRUE(root->render_surface());
+ EXPECT_TRUE(render_surface1->render_surface());
+ EXPECT_TRUE(render_surface2->render_surface());
+
+ // Since the render surfaces could have expanded, they should not clip (their
+ // bounds would no longer be reliable). We should resort to layer clipping
+ // in this case.
+ EXPECT_EQ(gfx::Rect(0, 0, 0, 0).ToString(),
+ render_surface1->render_surface()->clip_rect().ToString());
+ EXPECT_FALSE(render_surface1->render_surface()->is_clipped());
+ EXPECT_EQ(gfx::Rect(0, 0, 0, 0).ToString(),
+ render_surface2->render_surface()->clip_rect().ToString());
+ EXPECT_FALSE(render_surface2->render_surface()->is_clipped());
+
+ // NB: clip rects are in target space.
+ EXPECT_EQ(gfx::Rect(0, 0, 40, 40).ToString(),
+ render_surface1->clip_rect().ToString());
+ EXPECT_TRUE(render_surface1->is_clipped());
+
+ // This value is inherited from the clipping ancestor layer, 'intervening'.
+ EXPECT_EQ(gfx::Rect(2, 2, 3, 3).ToString(),
+ render_surface2->clip_rect().ToString());
+ EXPECT_TRUE(render_surface2->is_clipped());
+
+ // The content rects of both render surfaces should both have expanded to
+ // contain the clip child.
+ EXPECT_EQ(gfx::Rect(0, 0, 40, 40).ToString(),
+ render_surface1->render_surface()->content_rect().ToString());
+ EXPECT_EQ(gfx::Rect(2, 2, 40, 40).ToString(),
+ render_surface2->render_surface()->content_rect().ToString());
+
+ // The clip child should have inherited the clip parent's clip (projected to
+ // the right space, of course), and should have the correctly sized visible
+ // content rect.
+ EXPECT_EQ(gfx::Rect(2, 2, 40, 40).ToString(),
+ clip_child->clip_rect().ToString());
+ EXPECT_EQ(gfx::Rect(12, 12, 40, 40).ToString(),
+ clip_child->visible_content_rect().ToString());
+ EXPECT_TRUE(clip_child->is_clipped());
+}
+
+TEST_F(LayerTreeHostCommonTest, DescendantsOfClipChildren) {
+ // Ensures that descendants of the clip child inherit the correct clip.
+ //
+ // root (a render surface)
+ // + clip_parent (masks to bounds)
+ // + intervening (masks to bounds)
+ // + clip_child
+ // + child
+ //
+ scoped_refptr<Layer> root = Layer::Create();
+ scoped_refptr<Layer> clip_parent = Layer::Create();
+ scoped_refptr<Layer> intervening = Layer::Create();
+ scoped_refptr<Layer> clip_child = Layer::Create();
+ scoped_refptr<LayerWithForcedDrawsContent> child =
+ make_scoped_refptr(new LayerWithForcedDrawsContent);
+
+ root->AddChild(clip_parent);
+ clip_parent->AddChild(intervening);
+ intervening->AddChild(clip_child);
+ clip_child->AddChild(child);
+
+ clip_child->SetClipParent(clip_parent.get());
+
+ intervening->SetMasksToBounds(true);
+ clip_parent->SetMasksToBounds(true);
+
+ gfx::Transform identity_transform;
+ SetLayerPropertiesForTesting(root.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(),
+ gfx::Size(50, 50),
+ false);
+ SetLayerPropertiesForTesting(clip_parent.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(),
+ gfx::Size(40, 40),
+ false);
+ SetLayerPropertiesForTesting(intervening.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(),
+ gfx::Size(5, 5),
+ false);
+ SetLayerPropertiesForTesting(clip_child.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(),
+ gfx::Size(60, 60),
+ false);
+ SetLayerPropertiesForTesting(child.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(),
+ gfx::Size(60, 60),
+ false);
+
+ scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create();
+ host->SetRootLayer(root);
+
+ ExecuteCalculateDrawProperties(root.get());
+
+ EXPECT_TRUE(root->render_surface());
+
+ // Neither the clip child nor its descendant should have inherited the clip
+ // from |intervening|.
+ EXPECT_EQ(gfx::Rect(0, 0, 40, 40).ToString(),
+ clip_child->clip_rect().ToString());
+ EXPECT_TRUE(clip_child->is_clipped());
+ EXPECT_EQ(gfx::Rect(0, 0, 40, 40).ToString(),
+ child->visible_content_rect().ToString());
+ EXPECT_TRUE(child->is_clipped());
+}
+
+TEST_F(LayerTreeHostCommonTest,
+ SurfacesShouldBeUnaffectedByNonDescendantClipChildren) {
+ // Ensures that non-descendant clip children in the tree do not affect
+ // render surfaces.
+ //
+ // root (a render surface)
+ // + clip_parent (masks to bounds)
+ // + render_surface1
+ // + clip_child
+ // + render_surface2
+ // + non_clip_child
+ //
+ // In this example render_surface2 should be unaffected by clip_child.
+ scoped_refptr<Layer> root = Layer::Create();
+ scoped_refptr<Layer> clip_parent = Layer::Create();
+ scoped_refptr<Layer> render_surface1 = Layer::Create();
+ scoped_refptr<LayerWithForcedDrawsContent> clip_child =
+ make_scoped_refptr(new LayerWithForcedDrawsContent);
+ scoped_refptr<Layer> render_surface2 = Layer::Create();
+ scoped_refptr<LayerWithForcedDrawsContent> non_clip_child =
+ make_scoped_refptr(new LayerWithForcedDrawsContent);
+
+ root->AddChild(clip_parent);
+ clip_parent->AddChild(render_surface1);
+ render_surface1->AddChild(clip_child);
+ clip_parent->AddChild(render_surface2);
+ render_surface2->AddChild(non_clip_child);
+
+ clip_child->SetClipParent(clip_parent.get());
+
+ clip_parent->SetMasksToBounds(true);
+ render_surface1->SetMasksToBounds(true);
+
+ gfx::Transform identity_transform;
+ SetLayerPropertiesForTesting(root.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(),
+ gfx::Size(15, 15),
+ false);
+ SetLayerPropertiesForTesting(clip_parent.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(),
+ gfx::Size(10, 10),
+ false);
+ SetLayerPropertiesForTesting(render_surface1.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(5, 5),
+ gfx::Size(5, 5),
+ false);
+ SetLayerPropertiesForTesting(render_surface2.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(),
+ gfx::Size(5, 5),
+ false);
+ SetLayerPropertiesForTesting(clip_child.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(-1, 1),
+ gfx::Size(10, 10),
+ false);
+ SetLayerPropertiesForTesting(non_clip_child.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(),
+ gfx::Size(5, 5),
+ false);
+
+ render_surface1->SetForceRenderSurface(true);
+ render_surface2->SetForceRenderSurface(true);
+
+ scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create();
+ host->SetRootLayer(root);
+
+ ExecuteCalculateDrawProperties(root.get());
+
+ EXPECT_TRUE(root->render_surface());
+ EXPECT_TRUE(render_surface1->render_surface());
+ EXPECT_TRUE(render_surface2->render_surface());
+
+ EXPECT_EQ(gfx::Rect(0, 0, 5, 5).ToString(),
+ render_surface1->clip_rect().ToString());
+ EXPECT_TRUE(render_surface1->is_clipped());
+
+ // The render surface should not clip (it has unclipped descendants), instead
+ // it should rely on layer clipping.
+ EXPECT_EQ(gfx::Rect(0, 0, 0, 0).ToString(),
+ render_surface1->render_surface()->clip_rect().ToString());
+ EXPECT_FALSE(render_surface1->render_surface()->is_clipped());
+
+ // That said, it should have grown to accomodate the unclipped descendant.
+ EXPECT_EQ(gfx::Rect(-1, 1, 6, 4).ToString(),
+ render_surface1->render_surface()->content_rect().ToString());
+
+ // This render surface should clip. It has no unclipped descendants.
+ EXPECT_EQ(gfx::Rect(0, 0, 5, 5).ToString(),
+ render_surface2->clip_rect().ToString());
+ EXPECT_TRUE(render_surface2->render_surface()->is_clipped());
+
+ // It also shouldn't have grown to accomodate the clip child.
+ EXPECT_EQ(gfx::Rect(0, 0, 5, 5).ToString(),
+ render_surface2->render_surface()->content_rect().ToString());
+
+ // Sanity check our num_unclipped_descendants values.
+ EXPECT_EQ(1, render_surface1->num_unclipped_descendants());
+ EXPECT_EQ(0, render_surface2->num_unclipped_descendants());
+}
+
} // namespace
} // namespace cc
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 85f1a22..1131050 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -1815,6 +1815,12 @@ void LayerTreeHostImpl::BindToClient(InputHandlerClient* client) {
input_handler_client_ = client;
}
+static LayerImpl* NextScrollLayer(LayerImpl* layer) {
+ if (LayerImpl* scroll_parent = layer->scroll_parent())
+ return scroll_parent;
+ return layer->parent();
+}
+
InputHandler::ScrollStatus LayerTreeHostImpl::ScrollBegin(
gfx::Point viewport_point, InputHandler::ScrollInputType type) {
TRACE_EVENT0("cc", "LayerTreeHostImpl::ScrollBegin");
@@ -1838,7 +1844,7 @@ InputHandler::ScrollStatus LayerTreeHostImpl::ScrollBegin(
// Walk up the hierarchy and look for a scrollable layer.
LayerImpl* potentially_scrolling_layer_impl = 0;
- for (; layer_impl; layer_impl = layer_impl->parent()) {
+ for (; layer_impl; layer_impl = NextScrollLayer(layer_impl)) {
// The content layer can also block attempts to scroll outside the main
// thread.
ScrollStatus status = layer_impl->TryScroll(device_viewport_point, type);