| // Copyright 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "cc/occlusion_tracker.h" |
| |
| #include <algorithm> |
| |
| #include "cc/layer.h" |
| #include "cc/layer_impl.h" |
| #include "cc/math_util.h" |
| #include "cc/overdraw_metrics.h" |
| #include "ui/gfx/quad_f.h" |
| #include "ui/gfx/rect_conversions.h" |
| |
| using namespace std; |
| |
| namespace cc { |
| |
| template<typename LayerType, typename RenderSurfaceType> |
| OcclusionTrackerBase<LayerType, RenderSurfaceType>::OcclusionTrackerBase(gfx::Rect rootTargetRect, bool recordMetricsForFrame) |
| : m_rootTargetRect(rootTargetRect) |
| , m_overdrawMetrics(OverdrawMetrics::create(recordMetricsForFrame)) |
| , m_occludingScreenSpaceRects(0) |
| , m_nonOccludingScreenSpaceRects(0) |
| { |
| } |
| |
| template<typename LayerType, typename RenderSurfaceType> |
| OcclusionTrackerBase<LayerType, RenderSurfaceType>::~OcclusionTrackerBase() |
| { |
| } |
| |
| template<typename LayerType, typename RenderSurfaceType> |
| void OcclusionTrackerBase<LayerType, RenderSurfaceType>::enterLayer(const LayerIteratorPosition<LayerType>& layerIterator) |
| { |
| LayerType* renderTarget = layerIterator.targetRenderSurfaceLayer; |
| |
| if (layerIterator.representsItself) |
| enterRenderTarget(renderTarget); |
| else if (layerIterator.representsTargetRenderSurface) |
| finishedRenderTarget(renderTarget); |
| } |
| |
| template<typename LayerType, typename RenderSurfaceType> |
| void OcclusionTrackerBase<LayerType, RenderSurfaceType>::leaveLayer(const LayerIteratorPosition<LayerType>& layerIterator) |
| { |
| LayerType* renderTarget = layerIterator.targetRenderSurfaceLayer; |
| |
| if (layerIterator.representsItself) |
| markOccludedBehindLayer(layerIterator.currentLayer); |
| else if (layerIterator.representsContributingRenderSurface) |
| leaveToRenderTarget(renderTarget); |
| } |
| |
| template<typename LayerType, typename RenderSurfaceType> |
| void OcclusionTrackerBase<LayerType, RenderSurfaceType>::enterRenderTarget(const LayerType* newTarget) |
| { |
| if (!m_stack.empty() && m_stack.back().target == newTarget) |
| return; |
| |
| const LayerType* oldTarget = m_stack.empty() ? 0 : m_stack.back().target; |
| const RenderSurfaceType* oldAncestorThatMovesPixels = !oldTarget ? 0 : oldTarget->renderSurface()->nearestAncestorThatMovesPixels(); |
| const RenderSurfaceType* newAncestorThatMovesPixels = newTarget->renderSurface()->nearestAncestorThatMovesPixels(); |
| |
| m_stack.push_back(StackObject(newTarget)); |
| |
| // We copy the screen occlusion into the new RenderSurfaceImpl subtree, but we never copy in the |
| // target occlusion, since we are looking at a new RenderSurfaceImpl target. |
| |
| // If we are entering a subtree that is going to move pixels around, then the occlusion we've computed |
| // so far won't apply to the pixels we're drawing here in the same way. We discard the occlusion thus |
| // far to be safe, and ensure we don't cull any pixels that are moved such that they become visible. |
| bool enteringSubtreeThatMovesPixels = newAncestorThatMovesPixels && newAncestorThatMovesPixels != oldAncestorThatMovesPixels; |
| |
| bool copyScreenOcclusionForward = m_stack.size() > 1 && !enteringSubtreeThatMovesPixels; |
| if (copyScreenOcclusionForward) { |
| int lastIndex = m_stack.size() - 1; |
| m_stack[lastIndex].occlusionInScreen = m_stack[lastIndex - 1].occlusionInScreen; |
| } |
| } |
| |
| static inline bool layerOpacityKnown(const Layer* layer) { return !layer->drawOpacityIsAnimating(); } |
| static inline bool layerOpacityKnown(const LayerImpl*) { return true; } |
| static inline bool layerTransformsToTargetKnown(const Layer* layer) { return !layer->drawTransformIsAnimating(); } |
| static inline bool layerTransformsToTargetKnown(const LayerImpl*) { return true; } |
| static inline bool layerTransformsToScreenKnown(const Layer* layer) { return !layer->screenSpaceTransformIsAnimating(); } |
| static inline bool layerTransformsToScreenKnown(const LayerImpl*) { return true; } |
| |
| static inline bool surfaceOpacityKnown(const RenderSurface* surface) { return !surface->drawOpacityIsAnimating(); } |
| static inline bool surfaceOpacityKnown(const RenderSurfaceImpl*) { return true; } |
| static inline bool surfaceTransformsToTargetKnown(const RenderSurface* surface) { return !surface->targetSurfaceTransformsAreAnimating(); } |
| static inline bool surfaceTransformsToTargetKnown(const RenderSurfaceImpl*) { return true; } |
| static inline bool surfaceTransformsToScreenKnown(const RenderSurface* surface) { return !surface->screenSpaceTransformsAreAnimating(); } |
| static inline bool surfaceTransformsToScreenKnown(const RenderSurfaceImpl*) { return true; } |
| |
| static inline bool layerIsInUnsorted3dRenderingContext(const Layer* layer) { return layer->parent() && layer->parent()->preserves3D(); } |
| static inline bool layerIsInUnsorted3dRenderingContext(const LayerImpl*) { return false; } |
| |
| template<typename LayerType, typename RenderSurfaceType> |
| void OcclusionTrackerBase<LayerType, RenderSurfaceType>::finishedRenderTarget(const LayerType* finishedTarget) |
| { |
| // Make sure we know about the target surface. |
| enterRenderTarget(finishedTarget); |
| |
| RenderSurfaceType* surface = finishedTarget->renderSurface(); |
| |
| // 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. |
| // TODO(senorblanco): Make this smarter for SkImageFilter case: once |
| // SkImageFilters can report affectsOpacity(), call that. |
| if (finishedTarget->maskLayer() || !surfaceOpacityKnown(surface) || surface->drawOpacity() < 1 || finishedTarget->filters().hasFilterThatAffectsOpacity() || finishedTarget->filter()) { |
| m_stack.back().occlusionInScreen.Clear(); |
| m_stack.back().occlusionInTarget.Clear(); |
| } else { |
| if (!surfaceTransformsToTargetKnown(surface)) |
| m_stack.back().occlusionInTarget.Clear(); |
| if (!surfaceTransformsToScreenKnown(surface)) |
| m_stack.back().occlusionInScreen.Clear(); |
| } |
| } |
| |
| template<typename RenderSurfaceType> |
| static inline Region transformSurfaceOpaqueRegion(const RenderSurfaceType* surface, const Region& region, const gfx::Transform& transform) |
| { |
| // Verify that rects within the |surface| will remain rects in its target surface after applying |transform|. If this is true, then |
| // apply |transform| to each rect within |region| in order to transform the entire Region. |
| |
| bool clipped; |
| gfx::QuadF transformedBoundsQuad = MathUtil::mapQuad(transform, gfx::QuadF(region.bounds()), clipped); |
| // FIXME: Find a rect interior to each transformed quad. |
| if (clipped || !transformedBoundsQuad.IsRectilinear()) |
| return Region(); |
| |
| Region transformedRegion; |
| |
| for (Region::Iterator rects(region); rects.has_rect(); rects.next()) { |
| // We've already checked for clipping in the mapQuad call above, these calls should not clip anything further. |
| gfx::Rect transformedRect = gfx::ToEnclosedRect(MathUtil::mapClippedRect(transform, gfx::RectF(rects.rect()))); |
| if (!surface->clipRect().IsEmpty()) |
| transformedRect.Intersect(surface->clipRect()); |
| transformedRegion.Union(transformedRect); |
| } |
| return transformedRegion; |
| } |
| |
| static inline void reduceOcclusion(const gfx::Rect& affectedArea, const gfx::Rect& expandedPixel, Region& occlusion) |
| { |
| if (affectedArea.IsEmpty()) |
| return; |
| |
| Region affectedOcclusion = IntersectRegions(occlusion, affectedArea); |
| Region::Iterator affectedOcclusionRects(affectedOcclusion); |
| |
| occlusion.Subtract(affectedArea); |
| for (; affectedOcclusionRects.has_rect(); affectedOcclusionRects.next()) { |
| gfx::Rect occlusionRect = affectedOcclusionRects.rect(); |
| |
| // Shrink the rect by expanding the non-opaque pixels outside the rect. |
| |
| // The expandedPixel is the Rect for a single pixel after being |
| // expanded by filters on the layer. The original pixel would be |
| // Rect(0, 0, 1, 1), and the expanded pixel is the rect, relative |
| // to this original rect, that the original pixel can influence after |
| // being filtered. |
| // To convert the expandedPixel Rect back to filter outsets: |
| // x = -leftOutset |
| // width = leftOutset + rightOutset |
| // right = x + width = -leftOutset + leftOutset + rightOutset = rightOutset |
| |
| // The leftOutset of the filters moves pixels on the right side of |
| // the occlusionRect into it, shrinking its right edge. |
| int shrinkLeft = occlusionRect.x() == affectedArea.x() ? 0 : expandedPixel.right(); |
| int shrinkTop = occlusionRect.y() == affectedArea.y() ? 0 : expandedPixel.bottom(); |
| int shrinkRight = occlusionRect.right() == affectedArea.right() ? 0 : -expandedPixel.x(); |
| int shrinkBottom = occlusionRect.bottom() == affectedArea.bottom() ? 0 : -expandedPixel.y(); |
| |
| occlusionRect.Inset(shrinkLeft, shrinkTop, shrinkRight, shrinkBottom); |
| |
| occlusion.Union(occlusionRect); |
| } |
| } |
| |
| template<typename LayerType> |
| static void reduceOcclusionBelowSurface(LayerType* contributingLayer, const gfx::Rect& surfaceRect, const gfx::Transform& surfaceTransform, LayerType* renderTarget, Region& occlusionInTarget, Region& occlusionInScreen) |
| { |
| if (surfaceRect.IsEmpty()) |
| return; |
| |
| gfx::Rect boundsInTarget = gfx::ToEnclosingRect(MathUtil::mapClippedRect(surfaceTransform, gfx::RectF(surfaceRect))); |
| if (!contributingLayer->renderSurface()->clipRect().IsEmpty()) |
| boundsInTarget.Intersect(contributingLayer->renderSurface()->clipRect()); |
| |
| int outsetTop, outsetRight, outsetBottom, outsetLeft; |
| contributingLayer->backgroundFilters().getOutsets(outsetTop, outsetRight, outsetBottom, outsetLeft); |
| |
| // The filter can move pixels from outside of the clip, so allow affectedArea to expand outside the clip. |
| boundsInTarget.Inset(-outsetLeft, -outsetTop, -outsetRight, -outsetBottom); |
| |
| gfx::Rect boundsInScreen = gfx::ToEnclosingRect(MathUtil::mapClippedRect(renderTarget->renderSurface()->screenSpaceTransform(), gfx::RectF(boundsInTarget))); |
| |
| gfx::Rect filterOutsetsInTarget(-outsetLeft, -outsetTop, outsetLeft + outsetRight, outsetTop + outsetBottom); |
| gfx::Rect filterOutsetsInScreen = gfx::ToEnclosingRect(MathUtil::mapClippedRect(renderTarget->renderSurface()->screenSpaceTransform(), gfx::RectF(filterOutsetsInTarget))); |
| |
| reduceOcclusion(boundsInTarget, filterOutsetsInTarget, occlusionInTarget); |
| reduceOcclusion(boundsInScreen, filterOutsetsInScreen, occlusionInScreen); |
| } |
| |
| template<typename LayerType, typename RenderSurfaceType> |
| void OcclusionTrackerBase<LayerType, RenderSurfaceType>::leaveToRenderTarget(const LayerType* newTarget) |
| { |
| int lastIndex = m_stack.size() - 1; |
| bool surfaceWillBeAtTopAfterPop = m_stack.size() > 1 && m_stack[lastIndex - 1].target == newTarget; |
| |
| // We merge the screen occlusion from the current RenderSurfaceImpl subtree out to its parent target RenderSurfaceImpl. |
| // The target occlusion can be merged out as well but needs to be transformed to the new target. |
| |
| const LayerType* oldTarget = m_stack[lastIndex].target; |
| const RenderSurfaceType* oldSurface = oldTarget->renderSurface(); |
| Region oldTargetOcclusionInNewTarget = transformSurfaceOpaqueRegion<RenderSurfaceType>(oldSurface, m_stack[lastIndex].occlusionInTarget, oldSurface->drawTransform()); |
| if (oldTarget->hasReplica() && !oldTarget->replicaHasMask()) |
| oldTargetOcclusionInNewTarget.Union(transformSurfaceOpaqueRegion<RenderSurfaceType>(oldSurface, m_stack[lastIndex].occlusionInTarget, oldSurface->replicaDrawTransform())); |
| |
| gfx::Rect unoccludedSurfaceRect; |
| gfx::Rect unoccludedReplicaRect; |
| if (oldTarget->backgroundFilters().hasFilterThatMovesPixels()) { |
| unoccludedSurfaceRect = unoccludedContributingSurfaceContentRect(oldTarget, false, oldSurface->contentRect()); |
| if (oldTarget->hasReplica()) |
| unoccludedReplicaRect = unoccludedContributingSurfaceContentRect(oldTarget, true, oldSurface->contentRect()); |
| } |
| |
| if (surfaceWillBeAtTopAfterPop) { |
| // Merge the top of the stack down. |
| m_stack[lastIndex - 1].occlusionInScreen.Union(m_stack[lastIndex].occlusionInScreen); |
| m_stack[lastIndex - 1].occlusionInTarget.Union(oldTargetOcclusionInNewTarget); |
| m_stack.pop_back(); |
| } else { |
| // Replace the top of the stack with the new pushed surface. Copy the occluded screen region to the top. |
| m_stack.back().target = newTarget; |
| m_stack.back().occlusionInTarget = oldTargetOcclusionInNewTarget; |
| } |
| |
| if (oldTarget->backgroundFilters().hasFilterThatMovesPixels()) { |
| reduceOcclusionBelowSurface(oldTarget, unoccludedSurfaceRect, oldSurface->drawTransform(), newTarget, m_stack.back().occlusionInTarget, m_stack.back().occlusionInScreen); |
| if (oldTarget->hasReplica()) |
| reduceOcclusionBelowSurface(oldTarget, unoccludedReplicaRect, oldSurface->replicaDrawTransform(), newTarget, m_stack.back().occlusionInTarget, m_stack.back().occlusionInScreen); |
| } |
| } |
| |
| // FIXME: Remove usePaintTracking when paint tracking is on for paint culling. |
| template<typename LayerType> |
| 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) |
| { |
| DCHECK(layer->visibleContentRect().Contains(opaqueContents.bounds())); |
| |
| bool clipped; |
| gfx::QuadF visibleTransformedQuad = MathUtil::mapQuad(transform, gfx::QuadF(layer->visibleContentRect()), clipped); |
| // FIXME: Find a rect interior to each transformed quad. |
| if (clipped || !visibleTransformedQuad.IsRectilinear()) |
| return; |
| |
| for (Region::Iterator opaqueContentRects(opaqueContents); opaqueContentRects.has_rect(); opaqueContentRects.next()) { |
| // We've already checked for clipping in the mapQuad call above, these calls should not clip anything further. |
| gfx::Rect transformedRect = gfx::ToEnclosedRect(MathUtil::mapClippedRect(transform, gfx::RectF(opaqueContentRects.rect()))); |
| transformedRect.Intersect(clipRectInTarget); |
| if (transformedRect.width() >= minimumTrackingSize.width() || transformedRect.height() >= minimumTrackingSize.height()) { |
| if (occludingScreenSpaceRects) |
| occludingScreenSpaceRects->push_back(transformedRect); |
| region.Union(transformedRect); |
| } |
| } |
| |
| if (nonOccludingScreenSpaceRects) { |
| Region nonOpaqueContents = SubtractRegions(gfx::Rect(layer->contentBounds()), opaqueContents); |
| for (Region::Iterator nonOpaqueContentRects(nonOpaqueContents); nonOpaqueContentRects.has_rect(); nonOpaqueContentRects.next()) { |
| // We've already checked for clipping in the mapQuad call above, these calls should not clip anything further. |
| gfx::Rect transformedRect = gfx::ToEnclosedRect(MathUtil::mapClippedRect(transform, gfx::RectF(nonOpaqueContentRects.rect()))); |
| transformedRect.Intersect(clipRectInTarget); |
| if (transformedRect.IsEmpty()) |
| continue; |
| nonOccludingScreenSpaceRects->push_back(transformedRect); |
| } |
| } |
| } |
| |
| template<typename LayerType, typename RenderSurfaceType> |
| void OcclusionTrackerBase<LayerType, RenderSurfaceType>::markOccludedBehindLayer(const LayerType* layer) |
| { |
| DCHECK(!m_stack.empty()); |
| DCHECK(layer->renderTarget() == m_stack.back().target); |
| if (m_stack.empty()) |
| return; |
| |
| if (!layerOpacityKnown(layer) || layer->drawOpacity() < 1) |
| return; |
| |
| if (layerIsInUnsorted3dRenderingContext(layer)) |
| return; |
| |
| Region opaqueContents = layer->visibleContentOpaqueRegion(); |
| if (opaqueContents.IsEmpty()) |
| return; |
| |
| gfx::Rect clipRectInTarget = layerClipRectInTarget(layer); |
| if (layerTransformsToTargetKnown(layer)) |
| addOcclusionBehindLayer<LayerType>(m_stack.back().occlusionInTarget, layer, layer->drawTransform(), opaqueContents, clipRectInTarget, m_minimumTrackingSize, 0, 0); |
| |
| // 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 |
| // remain rectilinear, then we don't add any occlusion in screen space. |
| |
| if (layerTransformsToScreenKnown(layer)) { |
| gfx::Transform targetToScreenTransform = m_stack.back().target->renderSurface()->screenSpaceTransform(); |
| bool clipped; |
| gfx::QuadF clipQuadInScreen = MathUtil::mapQuad(targetToScreenTransform, gfx::QuadF(clipRectInTarget), clipped); |
| // FIXME: Find a rect interior to the transformed clip quad. |
| if (clipped || !clipQuadInScreen.IsRectilinear()) |
| return; |
| gfx::Rect clipRectInScreen = gfx::IntersectRects(m_rootTargetRect, gfx::ToEnclosedRect(clipQuadInScreen.BoundingBox())); |
| addOcclusionBehindLayer<LayerType>(m_stack.back().occlusionInScreen, layer, layer->screenSpaceTransform(), opaqueContents, clipRectInScreen, m_minimumTrackingSize, m_occludingScreenSpaceRects, m_nonOccludingScreenSpaceRects); |
| } |
| } |
| |
| static inline bool testContentRectOccluded(const gfx::Rect& contentRect, const gfx::Transform& contentSpaceTransform, const gfx::Rect& clipRectInTarget, const Region& occlusion) |
| { |
| gfx::RectF transformedRect = MathUtil::mapClippedRect(contentSpaceTransform, gfx::RectF(contentRect)); |
| // Take the gfx::ToEnclosingRect, as we want to include partial pixels in the test. |
| gfx::Rect targetRect = gfx::IntersectRects(gfx::ToEnclosingRect(transformedRect), clipRectInTarget); |
| return occlusion.Contains(targetRect); |
| } |
| |
| template<typename LayerType, typename RenderSurfaceType> |
| 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 |
| { |
| if (hasOcclusionFromOutsideTargetSurface) |
| *hasOcclusionFromOutsideTargetSurface = false; |
| |
| DCHECK(!m_stack.empty()); |
| if (m_stack.empty()) |
| return false; |
| if (contentRect.IsEmpty()) |
| return true; |
| |
| DCHECK(renderTarget == m_stack.back().target); |
| |
| if (!implDrawTransformIsUnknown && testContentRectOccluded(contentRect, drawTransform, clippedRectInTarget, m_stack.back().occlusionInTarget)) |
| return true; |
| |
| // renderTarget can be NULL in some tests. |
| bool transformToScreenKnown = renderTarget && !implDrawTransformIsUnknown && layerTransformsToScreenKnown(renderTarget); |
| if (transformToScreenKnown && testContentRectOccluded(contentRect, renderTarget->renderSurface()->screenSpaceTransform() * drawTransform, m_rootTargetRect, m_stack.back().occlusionInScreen)) { |
| if (hasOcclusionFromOutsideTargetSurface) |
| *hasOcclusionFromOutsideTargetSurface = true; |
| return true; |
| } |
| |
| return false; |
| } |
| |
| // Determines what portion of rect, if any, is unoccluded (not occluded by region). If |
| // the resulting unoccluded region is not rectangular, we return a rect containing it. |
| static inline gfx::Rect rectSubtractRegion(const gfx::Rect& rect, const Region& region) |
| { |
| if (region.IsEmpty()) |
| return rect; |
| |
| Region rectRegion(rect); |
| rectRegion.Subtract(region); |
| return rectRegion.bounds(); |
| } |
| |
| static inline gfx::Rect computeUnoccludedContentRect(const gfx::Rect& contentRect, const gfx::Transform& contentSpaceTransform, const gfx::Rect& clipRectInTarget, const Region& occlusion) |
| { |
| if (!contentSpaceTransform.IsInvertible()) |
| return contentRect; |
| |
| // Take the ToEnclosingRect at each step, as we want to contain any unoccluded partial pixels in the resulting Rect. |
| gfx::RectF transformedRect = MathUtil::mapClippedRect(contentSpaceTransform, gfx::RectF(contentRect)); |
| gfx::Rect shrunkRect = rectSubtractRegion(gfx::IntersectRects(gfx::ToEnclosingRect(transformedRect), clipRectInTarget), occlusion); |
| gfx::Rect unoccludedRect = gfx::ToEnclosingRect(MathUtil::projectClippedRect(MathUtil::inverse(contentSpaceTransform), gfx::RectF(shrunkRect))); |
| // The rect back in content space is a bounding box and may extend outside of the original contentRect, so clamp it to the contentRectBounds. |
| return gfx::IntersectRects(unoccludedRect, contentRect); |
| } |
| |
| template<typename LayerType, typename RenderSurfaceType> |
| 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 |
| { |
| DCHECK(!m_stack.empty()); |
| if (m_stack.empty()) |
| return contentRect; |
| if (contentRect.IsEmpty()) |
| return contentRect; |
| |
| DCHECK(renderTarget->renderTarget() == renderTarget); |
| DCHECK(renderTarget->renderSurface()); |
| DCHECK(renderTarget == m_stack.back().target); |
| |
| // We want to return a rect that contains all the visible parts of |contentRect| in both screen space and in the target surface. |
| // So we find the visible parts of |contentRect| in each space, and take the intersection. |
| |
| gfx::Rect unoccludedInScreen = contentRect; |
| if (layerTransformsToScreenKnown(renderTarget) && !implDrawTransformIsUnknown) |
| unoccludedInScreen = computeUnoccludedContentRect(contentRect, renderTarget->renderSurface()->screenSpaceTransform() * drawTransform, m_rootTargetRect, m_stack.back().occlusionInScreen); |
| |
| gfx::Rect unoccludedInTarget = contentRect; |
| if (!implDrawTransformIsUnknown) |
| unoccludedInTarget = computeUnoccludedContentRect(contentRect, drawTransform, clippedRectInTarget, m_stack.back().occlusionInTarget); |
| |
| if (hasOcclusionFromOutsideTargetSurface) |
| *hasOcclusionFromOutsideTargetSurface = (gfx::IntersectRects(unoccludedInScreen, unoccludedInTarget) != unoccludedInTarget); |
| |
| return gfx::IntersectRects(unoccludedInScreen, unoccludedInTarget); |
| } |
| |
| template<typename LayerType, typename RenderSurfaceType> |
| gfx::Rect OcclusionTrackerBase<LayerType, RenderSurfaceType>::unoccludedContributingSurfaceContentRect(const LayerType* layer, bool forReplica, const gfx::Rect& contentRect, bool* hasOcclusionFromOutsideTargetSurface) const |
| { |
| DCHECK(!m_stack.empty()); |
| // The layer is a contributing renderTarget so it should have a surface. |
| DCHECK(layer->renderSurface()); |
| // The layer is a contributing renderTarget so its target should be itself. |
| DCHECK(layer->renderTarget() == layer); |
| // The layer should not be the root, else what is is contributing to? |
| DCHECK(layer->parent()); |
| // This should be called while the layer is still considered the current target in the occlusion tracker. |
| DCHECK(layer == m_stack.back().target); |
| |
| if (contentRect.IsEmpty()) |
| return contentRect; |
| |
| RenderSurfaceType* surface = layer->renderSurface(); |
| |
| gfx::Rect surfaceClipRect = surface->clipRect(); |
| if (surfaceClipRect.IsEmpty()) { |
| const LayerType* contributingSurfaceRenderTarget = layer->parent()->renderTarget(); |
| surfaceClipRect = gfx::IntersectRects(contributingSurfaceRenderTarget->renderSurface()->contentRect(), gfx::ToEnclosingRect(surface->drawableContentRect())); |
| } |
| |
| // 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 |
| // found just below the top of the stack (if it exists). |
| bool hasOcclusion = m_stack.size() > 1; |
| |
| const gfx::Transform& transformToScreen = forReplica ? surface->replicaScreenSpaceTransform() : surface->screenSpaceTransform(); |
| const gfx::Transform& transformToTarget = forReplica ? surface->replicaDrawTransform() : surface->drawTransform(); |
| |
| gfx::Rect unoccludedInScreen = contentRect; |
| if (surfaceTransformsToScreenKnown(surface)) { |
| if (hasOcclusion) { |
| const StackObject& secondLast = m_stack[m_stack.size() - 2]; |
| unoccludedInScreen = computeUnoccludedContentRect(contentRect, transformToScreen, m_rootTargetRect, secondLast.occlusionInScreen); |
| } else |
| unoccludedInScreen = computeUnoccludedContentRect(contentRect, transformToScreen, m_rootTargetRect, Region()); |
| } |
| |
| gfx::Rect unoccludedInTarget = contentRect; |
| if (surfaceTransformsToTargetKnown(surface)) { |
| if (hasOcclusion) { |
| const StackObject& secondLast = m_stack[m_stack.size() - 2]; |
| unoccludedInTarget = computeUnoccludedContentRect(contentRect, transformToTarget, surfaceClipRect, secondLast.occlusionInTarget); |
| } else |
| unoccludedInTarget = computeUnoccludedContentRect(contentRect, transformToTarget, surfaceClipRect, Region()); |
| } |
| |
| if (hasOcclusionFromOutsideTargetSurface) |
| *hasOcclusionFromOutsideTargetSurface = (gfx::IntersectRects(unoccludedInScreen, unoccludedInTarget) != unoccludedInTarget); |
| |
| return gfx::IntersectRects(unoccludedInScreen, unoccludedInTarget); |
| } |
| |
| template<typename LayerType, typename RenderSurfaceType> |
| gfx::Rect OcclusionTrackerBase<LayerType, RenderSurfaceType>::layerClipRectInTarget(const LayerType* layer) const |
| { |
| // FIXME: we could remove this helper function, but unit tests currently override this |
| // function, and they need to be verified/adjusted before this can be removed. |
| return layer->drawableContentRect(); |
| } |
| |
| // Instantiate (and export) templates here for the linker. |
| template class OcclusionTrackerBase<Layer, RenderSurface>; |
| template class OcclusionTrackerBase<LayerImpl, RenderSurfaceImpl>; |
| |
| } // namespace cc |