CCOcclusionTracker.cpp [plain text]
#include "config.h"
#if USE(ACCELERATED_COMPOSITING)
#include "cc/CCOcclusionTracker.h"
#include "LayerChromium.h"
#include "cc/CCLayerImpl.h"
#include "cc/CCMathUtil.h"
#include <algorithm>
using namespace std;
namespace WebCore {
template<typename LayerType, typename RenderSurfaceType>
CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::CCOcclusionTrackerBase(IntRect scissorRectInScreenSpace, bool recordMetricsForFrame)
: m_scissorRectInScreenSpace(scissorRectInScreenSpace)
, m_overdrawMetrics(CCOverdrawMetrics::create(recordMetricsForFrame))
{
}
template<typename LayerType, typename RenderSurfaceType>
void CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::enterLayer(const CCLayerIteratorPosition<LayerType>& layerIterator)
{
RenderSurfaceType* renderSurface = layerIterator.targetRenderSurfaceLayer->renderSurface();
if (layerIterator.representsItself)
enterTargetRenderSurface(renderSurface);
else if (layerIterator.representsTargetRenderSurface)
finishedTargetRenderSurface(layerIterator.currentLayer, renderSurface);
}
template<typename LayerType, typename RenderSurfaceType>
void CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::leaveLayer(const CCLayerIteratorPosition<LayerType>& layerIterator)
{
RenderSurfaceType* renderSurface = layerIterator.targetRenderSurfaceLayer->renderSurface();
if (layerIterator.representsItself)
markOccludedBehindLayer(layerIterator.currentLayer);
else if (layerIterator.representsContributingRenderSurface)
leaveToTargetRenderSurface(renderSurface);
}
template<typename LayerType, typename RenderSurfaceType>
void CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::enterTargetRenderSurface(const RenderSurfaceType* newTarget)
{
if (!m_stack.isEmpty() && m_stack.last().surface == newTarget)
return;
const RenderSurfaceType* oldTarget = m_stack.isEmpty() ? 0 : m_stack.last().surface;
const RenderSurfaceType* oldAncestorThatMovesPixels = !oldTarget ? 0 : oldTarget->nearestAncestorThatMovesPixels();
const RenderSurfaceType* newAncestorThatMovesPixels = newTarget->nearestAncestorThatMovesPixels();
m_stack.append(StackObject(newTarget));
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 LayerChromium* layer) { return !layer->drawOpacityIsAnimating(); }
static inline bool layerOpacityKnown(const CCLayerImpl*) { return true; }
static inline bool layerTransformsToTargetKnown(const LayerChromium* layer) { return !layer->drawTransformIsAnimating(); }
static inline bool layerTransformsToTargetKnown(const CCLayerImpl*) { return true; }
static inline bool layerTransformsToScreenKnown(const LayerChromium* layer) { return !layer->screenSpaceTransformIsAnimating(); }
static inline bool layerTransformsToScreenKnown(const CCLayerImpl*) { return true; }
static inline bool surfaceOpacityKnown(const RenderSurfaceChromium* surface) { return !surface->drawOpacityIsAnimating(); }
static inline bool surfaceOpacityKnown(const CCRenderSurface*) { return true; }
static inline bool surfaceTransformsToTargetKnown(const RenderSurfaceChromium* surface) { return !surface->targetSurfaceTransformsAreAnimating(); }
static inline bool surfaceTransformsToTargetKnown(const CCRenderSurface*) { return true; }
static inline bool surfaceTransformsToScreenKnown(const RenderSurfaceChromium* surface) { return !surface->screenSpaceTransformsAreAnimating(); }
static inline bool surfaceTransformsToScreenKnown(const CCRenderSurface*) { return true; }
template<typename LayerType, typename RenderSurfaceType>
void CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::finishedTargetRenderSurface(const LayerType* owningLayer, const RenderSurfaceType* finishedTarget)
{
ASSERT(owningLayer->renderSurface() == finishedTarget);
enterTargetRenderSurface(finishedTarget);
if (owningLayer->maskLayer() || !surfaceOpacityKnown(finishedTarget) || finishedTarget->drawOpacity() < 1 || finishedTarget->filters().hasFilterThatAffectsOpacity()) {
m_stack.last().occlusionInScreen = Region();
m_stack.last().occlusionInTarget = Region();
} else {
if (!surfaceTransformsToTargetKnown(finishedTarget))
m_stack.last().occlusionInTarget = Region();
if (!surfaceTransformsToScreenKnown(finishedTarget))
m_stack.last().occlusionInScreen = Region();
}
}
template<typename RenderSurfaceType>
static inline Region transformSurfaceOpaqueRegion(const RenderSurfaceType* surface, const Region& region, const TransformationMatrix& transform)
{
bool clipped;
FloatQuad transformedBoundsQuad = CCMathUtil::mapQuad(transform, FloatQuad(region.bounds()), clipped);
if (clipped || !transformedBoundsQuad.isRectilinear())
return Region();
Region transformedRegion;
Vector<IntRect> rects = region.rects();
for (size_t i = 0; i < rects.size(); ++i) {
IntRect transformedRect = enclosedIntRect(transform.mapRect(FloatRect(rects[i])));
if (!surface->clipRect().isEmpty())
transformedRect.intersect(surface->clipRect());
transformedRegion.unite(transformedRect);
}
return transformedRegion;
}
static inline void reduceOcclusion(const IntRect& affectedArea, const IntRect& expandedPixel, Region& occlusion)
{
if (affectedArea.isEmpty())
return;
Region affectedOcclusion = intersect(occlusion, affectedArea);
Vector<IntRect> affectedOcclusionRects = affectedOcclusion.rects();
occlusion.subtract(affectedArea);
for (size_t j = 0; j < affectedOcclusionRects.size(); ++j) {
IntRect& occlusionRect = affectedOcclusionRects[j];
int shrinkLeft = occlusionRect.x() == affectedArea.x() ? 0 : expandedPixel.maxX();
int shrinkTop = occlusionRect.y() == affectedArea.y() ? 0 : expandedPixel.maxY();
int shrinkRight = occlusionRect.maxX() == affectedArea.maxX() ? 0 : -expandedPixel.x();
int shrinkBottom = occlusionRect.maxY() == affectedArea.maxY() ? 0 : -expandedPixel.y();
occlusionRect.move(shrinkLeft, shrinkTop);
occlusionRect.contract(shrinkLeft + shrinkRight, shrinkTop + shrinkBottom);
occlusion.unite(occlusionRect);
}
}
template<typename RenderSurfaceType>
static void reduceOcclusionBelowSurface(RenderSurfaceType* surface, const IntRect& surfaceRect, const TransformationMatrix& surfaceTransform, RenderSurfaceType* surfaceTarget, Region& occlusionInTarget, Region& occlusionInScreen)
{
if (surfaceRect.isEmpty())
return;
IntRect boundsInTarget = enclosingIntRect(CCMathUtil::mapClippedRect(surfaceTransform, FloatRect(surfaceRect)));
if (!surface->clipRect().isEmpty())
boundsInTarget.intersect(surface->clipRect());
int outsetTop, outsetRight, outsetBottom, outsetLeft;
surface->backgroundFilters().getOutsets(outsetTop, outsetRight, outsetBottom, outsetLeft);
boundsInTarget.move(-outsetLeft, -outsetTop);
boundsInTarget.expand(outsetLeft + outsetRight, outsetTop + outsetBottom);
IntRect boundsInScreen = enclosingIntRect(CCMathUtil::mapClippedRect(surfaceTarget->screenSpaceTransform(), FloatRect(boundsInTarget)));
IntRect filterOutsetsInTarget(-outsetLeft, -outsetTop, outsetLeft + outsetRight, outsetTop + outsetBottom);
IntRect filterOutsetsInScreen = enclosingIntRect(CCMathUtil::mapClippedRect(surfaceTarget->screenSpaceTransform(), FloatRect(filterOutsetsInTarget)));
reduceOcclusion(boundsInTarget, filterOutsetsInTarget, occlusionInTarget);
reduceOcclusion(boundsInScreen, filterOutsetsInScreen, occlusionInScreen);
}
template<typename LayerType, typename RenderSurfaceType>
void CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::leaveToTargetRenderSurface(const RenderSurfaceType* newTarget)
{
int lastIndex = m_stack.size() - 1;
bool surfaceWillBeAtTopAfterPop = m_stack.size() > 1 && m_stack[lastIndex - 1].surface == newTarget;
const RenderSurfaceType* oldTarget = m_stack[lastIndex].surface;
Region oldTargetOcclusionInNewTarget = transformSurfaceOpaqueRegion<RenderSurfaceType>(oldTarget, m_stack[lastIndex].occlusionInTarget, oldTarget->originTransform());
if (oldTarget->hasReplica())
oldTargetOcclusionInNewTarget.unite(transformSurfaceOpaqueRegion<RenderSurfaceType>(oldTarget, m_stack[lastIndex].occlusionInTarget, oldTarget->replicaOriginTransform()));
IntRect unoccludedSurfaceRect;
IntRect unoccludedReplicaRect;
if (oldTarget->backgroundFilters().hasFilterThatMovesPixels()) {
unoccludedSurfaceRect = unoccludedContributingSurfaceContentRect(oldTarget, false, oldTarget->contentRect());
if (oldTarget->hasReplica())
unoccludedReplicaRect = unoccludedContributingSurfaceContentRect(oldTarget, true, oldTarget->contentRect());
}
if (surfaceWillBeAtTopAfterPop) {
m_stack[lastIndex - 1].occlusionInScreen.unite(m_stack[lastIndex].occlusionInScreen);
m_stack[lastIndex - 1].occlusionInTarget.unite(oldTargetOcclusionInNewTarget);
m_stack.removeLast();
} else {
m_stack.last().surface = newTarget;
m_stack.last().occlusionInTarget = oldTargetOcclusionInNewTarget;
}
if (oldTarget->backgroundFilters().hasFilterThatMovesPixels()) {
reduceOcclusionBelowSurface(oldTarget, unoccludedSurfaceRect, oldTarget->originTransform(), newTarget, m_stack.last().occlusionInTarget, m_stack.last().occlusionInScreen);
if (oldTarget->hasReplica())
reduceOcclusionBelowSurface(oldTarget, unoccludedReplicaRect, oldTarget->replicaOriginTransform(), newTarget, m_stack.last().occlusionInTarget, m_stack.last().occlusionInScreen);
}
}
template<typename LayerType>
static inline TransformationMatrix contentToScreenSpaceTransform(const LayerType* layer)
{
ASSERT(layerTransformsToScreenKnown(layer));
IntSize boundsInLayerSpace = layer->bounds();
IntSize boundsInContentSpace = layer->contentBounds();
TransformationMatrix transform = layer->screenSpaceTransform();
if (boundsInContentSpace.isEmpty())
return transform;
transform.scaleNonUniform(boundsInLayerSpace.width() / static_cast<double>(boundsInContentSpace.width()),
boundsInLayerSpace.height() / static_cast<double>(boundsInContentSpace.height()));
return transform;
}
template<typename LayerType>
static inline TransformationMatrix contentToTargetSurfaceTransform(const LayerType* layer)
{
ASSERT(layerTransformsToTargetKnown(layer));
IntSize boundsInLayerSpace = layer->bounds();
IntSize boundsInContentSpace = layer->contentBounds();
TransformationMatrix transform = layer->drawTransform();
if (boundsInContentSpace.isEmpty())
return transform;
transform.scaleNonUniform(boundsInLayerSpace.width() / static_cast<double>(boundsInContentSpace.width()),
boundsInLayerSpace.height() / static_cast<double>(boundsInContentSpace.height()));
transform.translate(-boundsInContentSpace.width() / 2.0, -boundsInContentSpace.height() / 2.0);
return transform;
}
template<typename LayerType>
static inline void addOcclusionBehindLayer(Region& region, const LayerType* layer, const TransformationMatrix& transform, const Region& opaqueContents, const IntRect& scissorRect, const IntSize& minimumTrackingSize)
{
ASSERT(layer->visibleLayerRect().contains(opaqueContents.bounds()));
bool clipped;
FloatQuad visibleTransformedQuad = CCMathUtil::mapQuad(transform, FloatQuad(layer->visibleLayerRect()), clipped);
if (clipped || !visibleTransformedQuad.isRectilinear())
return;
Vector<IntRect> contentRects = opaqueContents.rects();
for (size_t i = 0; i < contentRects.size(); ++i) {
IntRect transformedRect = enclosedIntRect(transform.mapRect(FloatRect(contentRects[i])));
transformedRect.intersect(scissorRect);
if (transformedRect.width() >= minimumTrackingSize.width() || transformedRect.height() >= minimumTrackingSize.height())
region.unite(transformedRect);
}
}
template<typename LayerType, typename RenderSurfaceType>
void CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::markOccludedBehindLayer(const LayerType* layer)
{
ASSERT(!m_stack.isEmpty());
ASSERT(layer->targetRenderSurface() == m_stack.last().surface);
if (m_stack.isEmpty())
return;
if (!layerOpacityKnown(layer) || layer->drawOpacity() < 1)
return;
Region opaqueContents = layer->visibleContentOpaqueRegion();
if (opaqueContents.isEmpty())
return;
IntRect scissorInTarget = layerScissorRectInTargetSurface(layer);
if (layerTransformsToTargetKnown(layer))
addOcclusionBehindLayer<LayerType>(m_stack.last().occlusionInTarget, layer, contentToTargetSurfaceTransform<LayerType>(layer), opaqueContents, scissorInTarget, m_minimumTrackingSize);
if (layerTransformsToScreenKnown(layer)) {
TransformationMatrix targetToScreenTransform = m_stack.last().surface->screenSpaceTransform();
bool clipped;
FloatQuad scissorInScreenQuad = CCMathUtil::mapQuad(targetToScreenTransform, FloatQuad(FloatRect(scissorInTarget)), clipped);
if (clipped || !scissorInScreenQuad.isRectilinear())
return;
IntRect scissorInScreenRect = intersection(m_scissorRectInScreenSpace, enclosedIntRect(scissorInScreenQuad.boundingBox()));
addOcclusionBehindLayer<LayerType>(m_stack.last().occlusionInScreen, layer, contentToScreenSpaceTransform<LayerType>(layer), opaqueContents, scissorInScreenRect, m_minimumTrackingSize);
}
}
static inline bool testContentRectOccluded(const IntRect& contentRect, const TransformationMatrix& contentSpaceTransform, const IntRect& scissorRect, const Region& occlusion)
{
FloatRect transformedRect = CCMathUtil::mapClippedRect(contentSpaceTransform, FloatRect(contentRect));
IntRect targetRect = intersection(enclosingIntRect(transformedRect), scissorRect);
return targetRect.isEmpty() || occlusion.contains(targetRect);
}
template<typename LayerType, typename RenderSurfaceType>
bool CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::occluded(const LayerType* layer, const IntRect& contentRect) const
{
ASSERT(!m_stack.isEmpty());
if (m_stack.isEmpty())
return false;
if (contentRect.isEmpty())
return true;
ASSERT(layer->targetRenderSurface() == m_stack.last().surface);
if (layerTransformsToScreenKnown(layer) && testContentRectOccluded(contentRect, contentToScreenSpaceTransform<LayerType>(layer), m_scissorRectInScreenSpace, m_stack.last().occlusionInScreen))
return true;
if (layerTransformsToTargetKnown(layer) && testContentRectOccluded(contentRect, contentToTargetSurfaceTransform<LayerType>(layer), layerScissorRectInTargetSurface(layer), m_stack.last().occlusionInTarget))
return true;
return false;
}
static inline IntRect rectSubtractRegion(const IntRect& rect, const Region& region)
{
Region rectRegion(rect);
rectRegion.subtract(region);
return rectRegion.bounds();
}
static inline IntRect computeUnoccludedContentRect(const IntRect& contentRect, const TransformationMatrix& contentSpaceTransform, const IntRect& scissorRect, const Region& occlusion)
{
if (!contentSpaceTransform.isInvertible())
return contentRect;
FloatRect transformedRect = CCMathUtil::mapClippedRect(contentSpaceTransform, FloatRect(contentRect));
IntRect shrunkRect = rectSubtractRegion(intersection(enclosingIntRect(transformedRect), scissorRect), occlusion);
IntRect unoccludedRect = enclosingIntRect(CCMathUtil::projectClippedRect(contentSpaceTransform.inverse(), FloatRect(shrunkRect)));
return intersection(unoccludedRect, contentRect);
}
template<typename LayerType, typename RenderSurfaceType>
IntRect CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::unoccludedContentRect(const LayerType* layer, const IntRect& contentRect) const
{
ASSERT(!m_stack.isEmpty());
if (m_stack.isEmpty())
return contentRect;
if (contentRect.isEmpty())
return contentRect;
ASSERT(layer->targetRenderSurface() == m_stack.last().surface);
IntRect unoccludedInScreen = contentRect;
if (layerTransformsToScreenKnown(layer))
unoccludedInScreen = computeUnoccludedContentRect(contentRect, contentToScreenSpaceTransform<LayerType>(layer), m_scissorRectInScreenSpace, m_stack.last().occlusionInScreen);
if (unoccludedInScreen.isEmpty())
return unoccludedInScreen;
IntRect unoccludedInTarget = contentRect;
if (layerTransformsToTargetKnown(layer))
unoccludedInTarget = computeUnoccludedContentRect(contentRect, contentToTargetSurfaceTransform<LayerType>(layer), layerScissorRectInTargetSurface(layer), m_stack.last().occlusionInTarget);
return intersection(unoccludedInScreen, unoccludedInTarget);
}
template<typename LayerType, typename RenderSurfaceType>
IntRect CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::unoccludedContributingSurfaceContentRect(const RenderSurfaceType* surface, bool forReplica, const IntRect& contentRect) const
{
ASSERT(!m_stack.isEmpty());
ASSERT(surface == m_stack.last().surface);
if (m_stack.size() < 2)
return contentRect;
if (contentRect.isEmpty())
return contentRect;
const StackObject& secondLast = m_stack[m_stack.size() - 2];
IntRect surfaceClipRect = surface->clipRect();
if (surfaceClipRect.isEmpty()) {
const RenderSurfaceType* targetSurface = secondLast.surface;
surfaceClipRect = intersection(targetSurface->contentRect(), enclosingIntRect(surface->drawableContentRect()));
}
const TransformationMatrix& transformToScreen = forReplica ? surface->replicaScreenSpaceTransform() : surface->screenSpaceTransform();
const TransformationMatrix& transformToTarget = forReplica ? surface->replicaOriginTransform() : surface->originTransform();
IntRect unoccludedInScreen = contentRect;
if (surfaceTransformsToScreenKnown(surface))
unoccludedInScreen = computeUnoccludedContentRect(contentRect, transformToScreen, m_scissorRectInScreenSpace, secondLast.occlusionInScreen);
if (unoccludedInScreen.isEmpty())
return unoccludedInScreen;
IntRect unoccludedInTarget = contentRect;
if (surfaceTransformsToTargetKnown(surface))
unoccludedInTarget = computeUnoccludedContentRect(contentRect, transformToTarget, surfaceClipRect, secondLast.occlusionInTarget);
return intersection(unoccludedInScreen, unoccludedInTarget);
}
template<typename LayerType, typename RenderSurfaceType>
IntRect CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::layerScissorRectInTargetSurface(const LayerType* layer) const
{
const RenderSurfaceType* targetSurface = m_stack.last().surface;
FloatRect totalScissor = targetSurface->contentRect();
if (layer->usesLayerClipping())
totalScissor.intersect(layer->clipRect());
return enclosingIntRect(totalScissor);
}
template CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium>::CCOcclusionTrackerBase(IntRect scissorRectInScreenSpace, bool recordMetricsForFrame);
template void CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium>::enterLayer(const CCLayerIteratorPosition<LayerChromium>&);
template void CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium>::leaveLayer(const CCLayerIteratorPosition<LayerChromium>&);
template void CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium>::enterTargetRenderSurface(const RenderSurfaceChromium* newTarget);
template void CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium>::finishedTargetRenderSurface(const LayerChromium* owningLayer, const RenderSurfaceChromium* finishedTarget);
template void CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium>::leaveToTargetRenderSurface(const RenderSurfaceChromium* newTarget);
template void CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium>::markOccludedBehindLayer(const LayerChromium*);
template bool CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium>::occluded(const LayerChromium*, const IntRect& contentRect) const;
template IntRect CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium>::unoccludedContentRect(const LayerChromium*, const IntRect& contentRect) const;
template IntRect CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium>::unoccludedContributingSurfaceContentRect(const RenderSurfaceChromium*, bool forReplica, const IntRect& contentRect) const;
template IntRect CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium>::layerScissorRectInTargetSurface(const LayerChromium*) const;
template CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::CCOcclusionTrackerBase(IntRect scissorRectInScreenSpace, bool recordMetricsForFrame);
template void CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::enterLayer(const CCLayerIteratorPosition<CCLayerImpl>&);
template void CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::leaveLayer(const CCLayerIteratorPosition<CCLayerImpl>&);
template void CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::enterTargetRenderSurface(const CCRenderSurface* newTarget);
template void CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::finishedTargetRenderSurface(const CCLayerImpl* owningLayer, const CCRenderSurface* finishedTarget);
template void CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::leaveToTargetRenderSurface(const CCRenderSurface* newTarget);
template void CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::markOccludedBehindLayer(const CCLayerImpl*);
template bool CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::occluded(const CCLayerImpl*, const IntRect& contentRect) const;
template IntRect CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::unoccludedContentRect(const CCLayerImpl*, const IntRect& contentRect) const;
template IntRect CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::unoccludedContributingSurfaceContentRect(const CCRenderSurface*, bool forReplica, const IntRect& contentRect) const;
template IntRect CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::layerScissorRectInTargetSurface(const CCLayerImpl*) const;
} #endif // USE(ACCELERATED_COMPOSITING)