summaryrefslogtreecommitdiffstats
path: root/cc
diff options
context:
space:
mode:
authorvollick@chromium.org <vollick@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-08-26 20:36:38 +0000
committervollick@chromium.org <vollick@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-08-26 20:36:38 +0000
commit420fdf6e889e36c99aa128c0dc5b38469b5100a7 (patch)
tree1e240fa840974e2560ab2b1e3629e6df2aa5f168 /cc
parent2f632cac5ce83dcc9f11d1c90f74aad5330a7fde (diff)
downloadchromium_src-420fdf6e889e36c99aa128c0dc5b38469b5100a7.zip
chromium_src-420fdf6e889e36c99aa128c0dc5b38469b5100a7.tar.gz
chromium_src-420fdf6e889e36c99aa128c0dc5b38469b5100a7.tar.bz2
Make use of scroll parent relationships in cc/
Layers scroll with, and are clipped by, their scroll parents. In a previous patch (http://crrev.com/18133004), the scroll parent concept and plumbing were added. In this patch, we use the scroll parent relationship to apply scroll offsets to scroll children, and to avoid inheriting unwanted clips. BUG=254435 Review URL: https://chromiumcodereview.appspot.com/18191009 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@219598 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'cc')
-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);