Disable StorageInfoProviderTest.* on Valgrind bots.
[chromium-blink-merge.git] / cc / occlusion_tracker.cc
blob4ea3d3cff553a072df89ef97178ce9c49e17e110
1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "cc/occlusion_tracker.h"
7 #include <algorithm>
9 #include "cc/layer.h"
10 #include "cc/layer_impl.h"
11 #include "cc/math_util.h"
12 #include "cc/overdraw_metrics.h"
13 #include "ui/gfx/quad_f.h"
14 #include "ui/gfx/rect_conversions.h"
16 using namespace std;
18 namespace cc {
20 template<typename LayerType, typename RenderSurfaceType>
21 OcclusionTrackerBase<LayerType, RenderSurfaceType>::OcclusionTrackerBase(gfx::Rect rootTargetRect, bool recordMetricsForFrame)
22 : m_rootTargetRect(rootTargetRect)
23 , m_overdrawMetrics(OverdrawMetrics::create(recordMetricsForFrame))
24 , m_occludingScreenSpaceRects(0)
25 , m_nonOccludingScreenSpaceRects(0)
29 template<typename LayerType, typename RenderSurfaceType>
30 OcclusionTrackerBase<LayerType, RenderSurfaceType>::~OcclusionTrackerBase()
34 template<typename LayerType, typename RenderSurfaceType>
35 void OcclusionTrackerBase<LayerType, RenderSurfaceType>::enterLayer(const LayerIteratorPosition<LayerType>& layerIterator)
37 LayerType* renderTarget = layerIterator.targetRenderSurfaceLayer;
39 if (layerIterator.representsItself)
40 enterRenderTarget(renderTarget);
41 else if (layerIterator.representsTargetRenderSurface)
42 finishedRenderTarget(renderTarget);
45 template<typename LayerType, typename RenderSurfaceType>
46 void OcclusionTrackerBase<LayerType, RenderSurfaceType>::leaveLayer(const LayerIteratorPosition<LayerType>& layerIterator)
48 LayerType* renderTarget = layerIterator.targetRenderSurfaceLayer;
50 if (layerIterator.representsItself)
51 markOccludedBehindLayer(layerIterator.currentLayer);
52 else if (layerIterator.representsContributingRenderSurface)
53 leaveToRenderTarget(renderTarget);
56 template<typename LayerType, typename RenderSurfaceType>
57 void OcclusionTrackerBase<LayerType, RenderSurfaceType>::enterRenderTarget(const LayerType* newTarget)
59 if (!m_stack.empty() && m_stack.back().target == newTarget)
60 return;
62 const LayerType* oldTarget = m_stack.empty() ? 0 : m_stack.back().target;
63 const RenderSurfaceType* oldAncestorThatMovesPixels = !oldTarget ? 0 : oldTarget->renderSurface()->nearestAncestorThatMovesPixels();
64 const RenderSurfaceType* newAncestorThatMovesPixels = newTarget->renderSurface()->nearestAncestorThatMovesPixels();
66 m_stack.push_back(StackObject(newTarget));
68 // We copy the screen occlusion into the new RenderSurfaceImpl subtree, but we never copy in the
69 // target occlusion, since we are looking at a new RenderSurfaceImpl target.
71 // If we are entering a subtree that is going to move pixels around, then the occlusion we've computed
72 // so far won't apply to the pixels we're drawing here in the same way. We discard the occlusion thus
73 // far to be safe, and ensure we don't cull any pixels that are moved such that they become visible.
74 bool enteringSubtreeThatMovesPixels = newAncestorThatMovesPixels && newAncestorThatMovesPixels != oldAncestorThatMovesPixels;
76 bool copyScreenOcclusionForward = m_stack.size() > 1 && !enteringSubtreeThatMovesPixels;
77 if (copyScreenOcclusionForward) {
78 int lastIndex = m_stack.size() - 1;
79 m_stack[lastIndex].occlusionInScreen = m_stack[lastIndex - 1].occlusionInScreen;
83 static inline bool layerOpacityKnown(const Layer* layer) { return !layer->drawOpacityIsAnimating(); }
84 static inline bool layerOpacityKnown(const LayerImpl*) { return true; }
85 static inline bool layerTransformsToTargetKnown(const Layer* layer) { return !layer->drawTransformIsAnimating(); }
86 static inline bool layerTransformsToTargetKnown(const LayerImpl*) { return true; }
87 static inline bool layerTransformsToScreenKnown(const Layer* layer) { return !layer->screenSpaceTransformIsAnimating(); }
88 static inline bool layerTransformsToScreenKnown(const LayerImpl*) { return true; }
90 static inline bool surfaceOpacityKnown(const RenderSurface* surface) { return !surface->drawOpacityIsAnimating(); }
91 static inline bool surfaceOpacityKnown(const RenderSurfaceImpl*) { return true; }
92 static inline bool surfaceTransformsToTargetKnown(const RenderSurface* surface) { return !surface->targetSurfaceTransformsAreAnimating(); }
93 static inline bool surfaceTransformsToTargetKnown(const RenderSurfaceImpl*) { return true; }
94 static inline bool surfaceTransformsToScreenKnown(const RenderSurface* surface) { return !surface->screenSpaceTransformsAreAnimating(); }
95 static inline bool surfaceTransformsToScreenKnown(const RenderSurfaceImpl*) { return true; }
97 static inline bool layerIsInUnsorted3dRenderingContext(const Layer* layer) { return layer->parent() && layer->parent()->preserves3D(); }
98 static inline bool layerIsInUnsorted3dRenderingContext(const LayerImpl*) { return false; }
100 template<typename LayerType, typename RenderSurfaceType>
101 void OcclusionTrackerBase<LayerType, RenderSurfaceType>::finishedRenderTarget(const LayerType* finishedTarget)
103 // Make sure we know about the target surface.
104 enterRenderTarget(finishedTarget);
106 RenderSurfaceType* surface = finishedTarget->renderSurface();
108 // If the occlusion within the surface can not be applied to things outside of the surface's subtree, then clear the occlusion here so it won't be used.
109 // TODO(senorblanco): Make this smarter for SkImageFilter case: once
110 // SkImageFilters can report affectsOpacity(), call that.
111 if (finishedTarget->maskLayer() || !surfaceOpacityKnown(surface) || surface->drawOpacity() < 1 || finishedTarget->filters().hasFilterThatAffectsOpacity() || finishedTarget->filter()) {
112 m_stack.back().occlusionInScreen.Clear();
113 m_stack.back().occlusionInTarget.Clear();
114 } else {
115 if (!surfaceTransformsToTargetKnown(surface))
116 m_stack.back().occlusionInTarget.Clear();
117 if (!surfaceTransformsToScreenKnown(surface))
118 m_stack.back().occlusionInScreen.Clear();
122 template<typename RenderSurfaceType>
123 static inline Region transformSurfaceOpaqueRegion(const RenderSurfaceType* surface, const Region& region, const gfx::Transform& transform)
125 // Verify that rects within the |surface| will remain rects in its target surface after applying |transform|. If this is true, then
126 // apply |transform| to each rect within |region| in order to transform the entire Region.
128 bool clipped;
129 gfx::QuadF transformedBoundsQuad = MathUtil::mapQuad(transform, gfx::QuadF(region.bounds()), clipped);
130 // FIXME: Find a rect interior to each transformed quad.
131 if (clipped || !transformedBoundsQuad.IsRectilinear())
132 return Region();
134 Region transformedRegion;
136 for (Region::Iterator rects(region); rects.has_rect(); rects.next()) {
137 // We've already checked for clipping in the mapQuad call above, these calls should not clip anything further.
138 gfx::Rect transformedRect = gfx::ToEnclosedRect(MathUtil::mapClippedRect(transform, gfx::RectF(rects.rect())));
139 if (!surface->clipRect().IsEmpty())
140 transformedRect.Intersect(surface->clipRect());
141 transformedRegion.Union(transformedRect);
143 return transformedRegion;
146 static inline void reduceOcclusion(const gfx::Rect& affectedArea, const gfx::Rect& expandedPixel, Region& occlusion)
148 if (affectedArea.IsEmpty())
149 return;
151 Region affectedOcclusion = IntersectRegions(occlusion, affectedArea);
152 Region::Iterator affectedOcclusionRects(affectedOcclusion);
154 occlusion.Subtract(affectedArea);
155 for (; affectedOcclusionRects.has_rect(); affectedOcclusionRects.next()) {
156 gfx::Rect occlusionRect = affectedOcclusionRects.rect();
158 // Shrink the rect by expanding the non-opaque pixels outside the rect.
160 // The expandedPixel is the Rect for a single pixel after being
161 // expanded by filters on the layer. The original pixel would be
162 // Rect(0, 0, 1, 1), and the expanded pixel is the rect, relative
163 // to this original rect, that the original pixel can influence after
164 // being filtered.
165 // To convert the expandedPixel Rect back to filter outsets:
166 // x = -leftOutset
167 // width = leftOutset + rightOutset
168 // right = x + width = -leftOutset + leftOutset + rightOutset = rightOutset
170 // The leftOutset of the filters moves pixels on the right side of
171 // the occlusionRect into it, shrinking its right edge.
172 int shrinkLeft = occlusionRect.x() == affectedArea.x() ? 0 : expandedPixel.right();
173 int shrinkTop = occlusionRect.y() == affectedArea.y() ? 0 : expandedPixel.bottom();
174 int shrinkRight = occlusionRect.right() == affectedArea.right() ? 0 : -expandedPixel.x();
175 int shrinkBottom = occlusionRect.bottom() == affectedArea.bottom() ? 0 : -expandedPixel.y();
177 occlusionRect.Inset(shrinkLeft, shrinkTop, shrinkRight, shrinkBottom);
179 occlusion.Union(occlusionRect);
183 template<typename LayerType>
184 static void reduceOcclusionBelowSurface(LayerType* contributingLayer, const gfx::Rect& surfaceRect, const gfx::Transform& surfaceTransform, LayerType* renderTarget, Region& occlusionInTarget, Region& occlusionInScreen)
186 if (surfaceRect.IsEmpty())
187 return;
189 gfx::Rect boundsInTarget = gfx::ToEnclosingRect(MathUtil::mapClippedRect(surfaceTransform, gfx::RectF(surfaceRect)));
190 if (!contributingLayer->renderSurface()->clipRect().IsEmpty())
191 boundsInTarget.Intersect(contributingLayer->renderSurface()->clipRect());
193 int outsetTop, outsetRight, outsetBottom, outsetLeft;
194 contributingLayer->backgroundFilters().getOutsets(outsetTop, outsetRight, outsetBottom, outsetLeft);
196 // The filter can move pixels from outside of the clip, so allow affectedArea to expand outside the clip.
197 boundsInTarget.Inset(-outsetLeft, -outsetTop, -outsetRight, -outsetBottom);
199 gfx::Rect boundsInScreen = gfx::ToEnclosingRect(MathUtil::mapClippedRect(renderTarget->renderSurface()->screenSpaceTransform(), gfx::RectF(boundsInTarget)));
201 gfx::Rect filterOutsetsInTarget(-outsetLeft, -outsetTop, outsetLeft + outsetRight, outsetTop + outsetBottom);
202 gfx::Rect filterOutsetsInScreen = gfx::ToEnclosingRect(MathUtil::mapClippedRect(renderTarget->renderSurface()->screenSpaceTransform(), gfx::RectF(filterOutsetsInTarget)));
204 reduceOcclusion(boundsInTarget, filterOutsetsInTarget, occlusionInTarget);
205 reduceOcclusion(boundsInScreen, filterOutsetsInScreen, occlusionInScreen);
208 template<typename LayerType, typename RenderSurfaceType>
209 void OcclusionTrackerBase<LayerType, RenderSurfaceType>::leaveToRenderTarget(const LayerType* newTarget)
211 int lastIndex = m_stack.size() - 1;
212 bool surfaceWillBeAtTopAfterPop = m_stack.size() > 1 && m_stack[lastIndex - 1].target == newTarget;
214 // We merge the screen occlusion from the current RenderSurfaceImpl subtree out to its parent target RenderSurfaceImpl.
215 // The target occlusion can be merged out as well but needs to be transformed to the new target.
217 const LayerType* oldTarget = m_stack[lastIndex].target;
218 const RenderSurfaceType* oldSurface = oldTarget->renderSurface();
219 Region oldTargetOcclusionInNewTarget = transformSurfaceOpaqueRegion<RenderSurfaceType>(oldSurface, m_stack[lastIndex].occlusionInTarget, oldSurface->drawTransform());
220 if (oldTarget->hasReplica() && !oldTarget->replicaHasMask())
221 oldTargetOcclusionInNewTarget.Union(transformSurfaceOpaqueRegion<RenderSurfaceType>(oldSurface, m_stack[lastIndex].occlusionInTarget, oldSurface->replicaDrawTransform()));
223 gfx::Rect unoccludedSurfaceRect;
224 gfx::Rect unoccludedReplicaRect;
225 if (oldTarget->backgroundFilters().hasFilterThatMovesPixels()) {
226 unoccludedSurfaceRect = unoccludedContributingSurfaceContentRect(oldTarget, false, oldSurface->contentRect());
227 if (oldTarget->hasReplica())
228 unoccludedReplicaRect = unoccludedContributingSurfaceContentRect(oldTarget, true, oldSurface->contentRect());
231 if (surfaceWillBeAtTopAfterPop) {
232 // Merge the top of the stack down.
233 m_stack[lastIndex - 1].occlusionInScreen.Union(m_stack[lastIndex].occlusionInScreen);
234 m_stack[lastIndex - 1].occlusionInTarget.Union(oldTargetOcclusionInNewTarget);
235 m_stack.pop_back();
236 } else {
237 // Replace the top of the stack with the new pushed surface. Copy the occluded screen region to the top.
238 m_stack.back().target = newTarget;
239 m_stack.back().occlusionInTarget = oldTargetOcclusionInNewTarget;
242 if (oldTarget->backgroundFilters().hasFilterThatMovesPixels()) {
243 reduceOcclusionBelowSurface(oldTarget, unoccludedSurfaceRect, oldSurface->drawTransform(), newTarget, m_stack.back().occlusionInTarget, m_stack.back().occlusionInScreen);
244 if (oldTarget->hasReplica())
245 reduceOcclusionBelowSurface(oldTarget, unoccludedReplicaRect, oldSurface->replicaDrawTransform(), newTarget, m_stack.back().occlusionInTarget, m_stack.back().occlusionInScreen);
249 // FIXME: Remove usePaintTracking when paint tracking is on for paint culling.
250 template<typename LayerType>
251 static inline void addOcclusionBehindLayer(Region& region, const LayerType* layer, const gfx::Transform& transform, const Region& opaqueContents, const gfx::Rect& clipRectInTarget, const gfx::Size& minimumTrackingSize, std::vector<gfx::Rect>* occludingScreenSpaceRects, std::vector<gfx::Rect>* nonOccludingScreenSpaceRects)
253 DCHECK(layer->visibleContentRect().Contains(opaqueContents.bounds()));
255 bool clipped;
256 gfx::QuadF visibleTransformedQuad = MathUtil::mapQuad(transform, gfx::QuadF(layer->visibleContentRect()), clipped);
257 // FIXME: Find a rect interior to each transformed quad.
258 if (clipped || !visibleTransformedQuad.IsRectilinear())
259 return;
261 for (Region::Iterator opaqueContentRects(opaqueContents); opaqueContentRects.has_rect(); opaqueContentRects.next()) {
262 // We've already checked for clipping in the mapQuad call above, these calls should not clip anything further.
263 gfx::Rect transformedRect = gfx::ToEnclosedRect(MathUtil::mapClippedRect(transform, gfx::RectF(opaqueContentRects.rect())));
264 transformedRect.Intersect(clipRectInTarget);
265 if (transformedRect.width() >= minimumTrackingSize.width() || transformedRect.height() >= minimumTrackingSize.height()) {
266 if (occludingScreenSpaceRects)
267 occludingScreenSpaceRects->push_back(transformedRect);
268 region.Union(transformedRect);
272 if (nonOccludingScreenSpaceRects) {
273 Region nonOpaqueContents = SubtractRegions(gfx::Rect(layer->contentBounds()), opaqueContents);
274 for (Region::Iterator nonOpaqueContentRects(nonOpaqueContents); nonOpaqueContentRects.has_rect(); nonOpaqueContentRects.next()) {
275 // We've already checked for clipping in the mapQuad call above, these calls should not clip anything further.
276 gfx::Rect transformedRect = gfx::ToEnclosedRect(MathUtil::mapClippedRect(transform, gfx::RectF(nonOpaqueContentRects.rect())));
277 transformedRect.Intersect(clipRectInTarget);
278 if (transformedRect.IsEmpty())
279 continue;
280 nonOccludingScreenSpaceRects->push_back(transformedRect);
285 template<typename LayerType, typename RenderSurfaceType>
286 void OcclusionTrackerBase<LayerType, RenderSurfaceType>::markOccludedBehindLayer(const LayerType* layer)
288 DCHECK(!m_stack.empty());
289 DCHECK(layer->renderTarget() == m_stack.back().target);
290 if (m_stack.empty())
291 return;
293 if (!layerOpacityKnown(layer) || layer->drawOpacity() < 1)
294 return;
296 if (layerIsInUnsorted3dRenderingContext(layer))
297 return;
299 Region opaqueContents = layer->visibleContentOpaqueRegion();
300 if (opaqueContents.IsEmpty())
301 return;
303 gfx::Rect clipRectInTarget = layerClipRectInTarget(layer);
304 if (layerTransformsToTargetKnown(layer))
305 addOcclusionBehindLayer<LayerType>(m_stack.back().occlusionInTarget, layer, layer->drawTransform(), opaqueContents, clipRectInTarget, m_minimumTrackingSize, 0, 0);
307 // We must clip the occlusion within the layer's clipRectInTarget within screen space as well. If the clip rect can't be moved to screen space and
308 // remain rectilinear, then we don't add any occlusion in screen space.
310 if (layerTransformsToScreenKnown(layer)) {
311 gfx::Transform targetToScreenTransform = m_stack.back().target->renderSurface()->screenSpaceTransform();
312 bool clipped;
313 gfx::QuadF clipQuadInScreen = MathUtil::mapQuad(targetToScreenTransform, gfx::QuadF(clipRectInTarget), clipped);
314 // FIXME: Find a rect interior to the transformed clip quad.
315 if (clipped || !clipQuadInScreen.IsRectilinear())
316 return;
317 gfx::Rect clipRectInScreen = gfx::IntersectRects(m_rootTargetRect, gfx::ToEnclosedRect(clipQuadInScreen.BoundingBox()));
318 addOcclusionBehindLayer<LayerType>(m_stack.back().occlusionInScreen, layer, layer->screenSpaceTransform(), opaqueContents, clipRectInScreen, m_minimumTrackingSize, m_occludingScreenSpaceRects, m_nonOccludingScreenSpaceRects);
322 static inline bool testContentRectOccluded(const gfx::Rect& contentRect, const gfx::Transform& contentSpaceTransform, const gfx::Rect& clipRectInTarget, const Region& occlusion)
324 gfx::RectF transformedRect = MathUtil::mapClippedRect(contentSpaceTransform, gfx::RectF(contentRect));
325 // Take the gfx::ToEnclosingRect, as we want to include partial pixels in the test.
326 gfx::Rect targetRect = gfx::IntersectRects(gfx::ToEnclosingRect(transformedRect), clipRectInTarget);
327 return occlusion.Contains(targetRect);
330 template<typename LayerType, typename RenderSurfaceType>
331 bool OcclusionTrackerBase<LayerType, RenderSurfaceType>::occluded(const LayerType* renderTarget, const gfx::Rect& contentRect, const gfx::Transform& drawTransform, bool implDrawTransformIsUnknown, const gfx::Rect& clippedRectInTarget, bool* hasOcclusionFromOutsideTargetSurface) const
333 if (hasOcclusionFromOutsideTargetSurface)
334 *hasOcclusionFromOutsideTargetSurface = false;
336 DCHECK(!m_stack.empty());
337 if (m_stack.empty())
338 return false;
339 if (contentRect.IsEmpty())
340 return true;
342 DCHECK(renderTarget == m_stack.back().target);
344 if (!implDrawTransformIsUnknown && testContentRectOccluded(contentRect, drawTransform, clippedRectInTarget, m_stack.back().occlusionInTarget))
345 return true;
347 // renderTarget can be NULL in some tests.
348 bool transformToScreenKnown = renderTarget && !implDrawTransformIsUnknown && layerTransformsToScreenKnown(renderTarget);
349 if (transformToScreenKnown && testContentRectOccluded(contentRect, renderTarget->renderSurface()->screenSpaceTransform() * drawTransform, m_rootTargetRect, m_stack.back().occlusionInScreen)) {
350 if (hasOcclusionFromOutsideTargetSurface)
351 *hasOcclusionFromOutsideTargetSurface = true;
352 return true;
355 return false;
358 // Determines what portion of rect, if any, is unoccluded (not occluded by region). If
359 // the resulting unoccluded region is not rectangular, we return a rect containing it.
360 static inline gfx::Rect rectSubtractRegion(const gfx::Rect& rect, const Region& region)
362 if (region.IsEmpty())
363 return rect;
365 Region rectRegion(rect);
366 rectRegion.Subtract(region);
367 return rectRegion.bounds();
370 static inline gfx::Rect computeUnoccludedContentRect(const gfx::Rect& contentRect, const gfx::Transform& contentSpaceTransform, const gfx::Rect& clipRectInTarget, const Region& occlusion)
372 if (!contentSpaceTransform.IsInvertible())
373 return contentRect;
375 // Take the ToEnclosingRect at each step, as we want to contain any unoccluded partial pixels in the resulting Rect.
376 gfx::RectF transformedRect = MathUtil::mapClippedRect(contentSpaceTransform, gfx::RectF(contentRect));
377 gfx::Rect shrunkRect = rectSubtractRegion(gfx::IntersectRects(gfx::ToEnclosingRect(transformedRect), clipRectInTarget), occlusion);
378 gfx::Rect unoccludedRect = gfx::ToEnclosingRect(MathUtil::projectClippedRect(MathUtil::inverse(contentSpaceTransform), gfx::RectF(shrunkRect)));
379 // The rect back in content space is a bounding box and may extend outside of the original contentRect, so clamp it to the contentRectBounds.
380 return gfx::IntersectRects(unoccludedRect, contentRect);
383 template<typename LayerType, typename RenderSurfaceType>
384 gfx::Rect OcclusionTrackerBase<LayerType, RenderSurfaceType>::unoccludedContentRect(const LayerType* renderTarget, const gfx::Rect& contentRect, const gfx::Transform& drawTransform, bool implDrawTransformIsUnknown, const gfx::Rect& clippedRectInTarget, bool* hasOcclusionFromOutsideTargetSurface) const
386 DCHECK(!m_stack.empty());
387 if (m_stack.empty())
388 return contentRect;
389 if (contentRect.IsEmpty())
390 return contentRect;
392 DCHECK(renderTarget->renderTarget() == renderTarget);
393 DCHECK(renderTarget->renderSurface());
394 DCHECK(renderTarget == m_stack.back().target);
396 // We want to return a rect that contains all the visible parts of |contentRect| in both screen space and in the target surface.
397 // So we find the visible parts of |contentRect| in each space, and take the intersection.
399 gfx::Rect unoccludedInScreen = contentRect;
400 if (layerTransformsToScreenKnown(renderTarget) && !implDrawTransformIsUnknown)
401 unoccludedInScreen = computeUnoccludedContentRect(contentRect, renderTarget->renderSurface()->screenSpaceTransform() * drawTransform, m_rootTargetRect, m_stack.back().occlusionInScreen);
403 gfx::Rect unoccludedInTarget = contentRect;
404 if (!implDrawTransformIsUnknown)
405 unoccludedInTarget = computeUnoccludedContentRect(contentRect, drawTransform, clippedRectInTarget, m_stack.back().occlusionInTarget);
407 if (hasOcclusionFromOutsideTargetSurface)
408 *hasOcclusionFromOutsideTargetSurface = (gfx::IntersectRects(unoccludedInScreen, unoccludedInTarget) != unoccludedInTarget);
410 return gfx::IntersectRects(unoccludedInScreen, unoccludedInTarget);
413 template<typename LayerType, typename RenderSurfaceType>
414 gfx::Rect OcclusionTrackerBase<LayerType, RenderSurfaceType>::unoccludedContributingSurfaceContentRect(const LayerType* layer, bool forReplica, const gfx::Rect& contentRect, bool* hasOcclusionFromOutsideTargetSurface) const
416 DCHECK(!m_stack.empty());
417 // The layer is a contributing renderTarget so it should have a surface.
418 DCHECK(layer->renderSurface());
419 // The layer is a contributing renderTarget so its target should be itself.
420 DCHECK(layer->renderTarget() == layer);
421 // The layer should not be the root, else what is is contributing to?
422 DCHECK(layer->parent());
423 // This should be called while the layer is still considered the current target in the occlusion tracker.
424 DCHECK(layer == m_stack.back().target);
426 if (contentRect.IsEmpty())
427 return contentRect;
429 RenderSurfaceType* surface = layer->renderSurface();
431 gfx::Rect surfaceClipRect = surface->clipRect();
432 if (surfaceClipRect.IsEmpty()) {
433 const LayerType* contributingSurfaceRenderTarget = layer->parent()->renderTarget();
434 surfaceClipRect = gfx::IntersectRects(contributingSurfaceRenderTarget->renderSurface()->contentRect(), gfx::ToEnclosingRect(surface->drawableContentRect()));
437 // A contributing surface doesn't get occluded by things inside its own surface, so only things outside the surface can occlude it. That occlusion is
438 // found just below the top of the stack (if it exists).
439 bool hasOcclusion = m_stack.size() > 1;
441 const gfx::Transform& transformToScreen = forReplica ? surface->replicaScreenSpaceTransform() : surface->screenSpaceTransform();
442 const gfx::Transform& transformToTarget = forReplica ? surface->replicaDrawTransform() : surface->drawTransform();
444 gfx::Rect unoccludedInScreen = contentRect;
445 if (surfaceTransformsToScreenKnown(surface)) {
446 if (hasOcclusion) {
447 const StackObject& secondLast = m_stack[m_stack.size() - 2];
448 unoccludedInScreen = computeUnoccludedContentRect(contentRect, transformToScreen, m_rootTargetRect, secondLast.occlusionInScreen);
449 } else
450 unoccludedInScreen = computeUnoccludedContentRect(contentRect, transformToScreen, m_rootTargetRect, Region());
453 gfx::Rect unoccludedInTarget = contentRect;
454 if (surfaceTransformsToTargetKnown(surface)) {
455 if (hasOcclusion) {
456 const StackObject& secondLast = m_stack[m_stack.size() - 2];
457 unoccludedInTarget = computeUnoccludedContentRect(contentRect, transformToTarget, surfaceClipRect, secondLast.occlusionInTarget);
458 } else
459 unoccludedInTarget = computeUnoccludedContentRect(contentRect, transformToTarget, surfaceClipRect, Region());
462 if (hasOcclusionFromOutsideTargetSurface)
463 *hasOcclusionFromOutsideTargetSurface = (gfx::IntersectRects(unoccludedInScreen, unoccludedInTarget) != unoccludedInTarget);
465 return gfx::IntersectRects(unoccludedInScreen, unoccludedInTarget);
468 template<typename LayerType, typename RenderSurfaceType>
469 gfx::Rect OcclusionTrackerBase<LayerType, RenderSurfaceType>::layerClipRectInTarget(const LayerType* layer) const
471 // FIXME: we could remove this helper function, but unit tests currently override this
472 // function, and they need to be verified/adjusted before this can be removed.
473 return layer->drawableContentRect();
476 // Instantiate (and export) templates here for the linker.
477 template class OcclusionTrackerBase<Layer, RenderSurface>;
478 template class OcclusionTrackerBase<LayerImpl, RenderSurfaceImpl>;
480 } // namespace cc