[chromium] Allow us to disable overdraw metrics when tracing is off
[WebKit-https.git] / Source / WebCore / platform / graphics / chromium / cc / CCOcclusionTracker.cpp
1 /*
2  * Copyright (C) 2012 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer.
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
18  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27
28 #if USE(ACCELERATED_COMPOSITING)
29
30 #include "cc/CCOcclusionTracker.h"
31
32 #include "LayerChromium.h"
33 #include "cc/CCLayerImpl.h"
34
35 #include <algorithm>
36
37 using namespace std;
38
39 namespace WebCore {
40
41 template<typename LayerType, typename RenderSurfaceType>
42 CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::CCOcclusionTrackerBase(IntRect scissorRectInScreenSpace, bool recordMetricsForFrame)
43     : m_scissorRectInScreenSpace(scissorRectInScreenSpace)
44     , m_overdrawMetrics(CCOverdrawMetrics::create(recordMetricsForFrame))
45     , m_usePaintTracking(true) // FIXME: Remove this when paint tracking is on for paint culling.
46 {
47 }
48
49 template<typename LayerType, typename RenderSurfaceType>
50 void CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::enterTargetRenderSurface(const RenderSurfaceType* newTarget)
51 {
52     if (!m_stack.isEmpty() && m_stack.last().surface == newTarget)
53         return;
54
55     const RenderSurfaceType* oldTarget = m_stack.isEmpty() ? 0 : m_stack.last().surface;
56     const RenderSurfaceType* oldAncestorThatMovesPixels = !oldTarget ? 0 : oldTarget->nearestAncestorThatMovesPixels();
57     const RenderSurfaceType* newAncestorThatMovesPixels = newTarget->nearestAncestorThatMovesPixels();
58
59     m_stack.append(StackObject(newTarget));
60
61     // We copy the screen occlusion into the new RenderSurface subtree, but we never copy in the
62     // target occlusion, since we are looking at a new RenderSurface target.
63
64     // If we are entering a subtree that is going to move pixels around, then the occlusion we've computed
65     // so far won't apply to the pixels we're drawing here in the same way. We discard the occlusion thus
66     // far to be safe, and ensure we don't cull any pixels that are moved such that they become visible.
67     bool enteringSubtreeThatMovesPixels = newAncestorThatMovesPixels && newAncestorThatMovesPixels != oldAncestorThatMovesPixels;
68
69     bool copyScreenOcclusionForward = m_stack.size() > 1 && !enteringSubtreeThatMovesPixels;
70     if (copyScreenOcclusionForward) {
71         int lastIndex = m_stack.size() - 1;
72         m_stack[lastIndex].occlusionInScreen = m_stack[lastIndex - 1].occlusionInScreen;
73     }
74 }
75
76 static inline bool layerOpacityKnown(const LayerChromium* layer) { return !layer->drawOpacityIsAnimating(); }
77 static inline bool layerOpacityKnown(const CCLayerImpl*) { return true; }
78 static inline bool layerTransformsToTargetKnown(const LayerChromium* layer) { return !layer->drawTransformIsAnimating(); }
79 static inline bool layerTransformsToTargetKnown(const CCLayerImpl*) { return true; }
80 static inline bool layerTransformsToScreenKnown(const LayerChromium* layer) { return !layer->screenSpaceTransformIsAnimating(); }
81 static inline bool layerTransformsToScreenKnown(const CCLayerImpl*) { return true; }
82
83 static inline bool surfaceOpacityKnown(const RenderSurfaceChromium* surface) { return !surface->drawOpacityIsAnimating(); }
84 static inline bool surfaceOpacityKnown(const CCRenderSurface*) { return true; }
85 static inline bool surfaceTransformsToTargetKnown(const RenderSurfaceChromium* surface) { return !surface->targetSurfaceTransformsAreAnimating(); }
86 static inline bool surfaceTransformsToTargetKnown(const CCRenderSurface*) { return true; }
87 static inline bool surfaceTransformsToScreenKnown(const RenderSurfaceChromium* surface) { return !surface->screenSpaceTransformsAreAnimating(); }
88 static inline bool surfaceTransformsToScreenKnown(const CCRenderSurface*) { return true; }
89
90 template<typename LayerType, typename RenderSurfaceType>
91 void CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::finishedTargetRenderSurface(const LayerType* owningLayer, const RenderSurfaceType* finishedTarget)
92 {
93     // FIXME: Remove the owningLayer parameter when we can get all the info from the surface.
94     ASSERT(owningLayer->renderSurface() == finishedTarget);
95
96     // Make sure we know about the target surface.
97     enterTargetRenderSurface(finishedTarget);
98
99     // 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.
100     if (owningLayer->maskLayer() || !surfaceOpacityKnown(finishedTarget) || finishedTarget->drawOpacity() < 1 || finishedTarget->filters().hasFilterThatAffectsOpacity()) {
101         m_stack.last().occlusionInScreen = Region();
102         m_stack.last().occlusionInTarget = Region();
103     } else {
104         if (!surfaceTransformsToTargetKnown(finishedTarget))
105             m_stack.last().occlusionInTarget = Region();
106         if (!surfaceTransformsToScreenKnown(finishedTarget))
107             m_stack.last().occlusionInScreen = Region();
108     }
109 }
110
111 template<typename RenderSurfaceType>
112 static inline Region transformSurfaceOpaqueRegion(const RenderSurfaceType* surface, const Region& region, const TransformationMatrix& transform)
113 {
114     // Verify that rects within the |surface| will remain rects in its target surface after applying |transform|. If this is true, then
115     // apply |transform| to each rect within |region| in order to transform the entire Region.
116
117     IntRect bounds = region.bounds();
118     FloatRect centeredBounds(-bounds.width() / 2.0, -bounds.height() / 2.0, bounds.width(), bounds.height());
119     FloatQuad transformedBoundsQuad = transform.mapQuad(FloatQuad(centeredBounds));
120     if (!transformedBoundsQuad.isRectilinear())
121         return Region();
122
123     Region transformedRegion;
124
125     IntRect surfaceBounds = surface->contentRect();
126     Vector<IntRect> rects = region.rects();
127     Vector<IntRect>::const_iterator end = rects.end();
128     for (Vector<IntRect>::const_iterator i = rects.begin(); i != end; ++i) {
129         FloatRect centeredOriginRect(-i->width() / 2.0 + i->x() - surfaceBounds.x(), -i->height() / 2.0 + i->y() - surfaceBounds.y(), i->width(), i->height());
130         FloatRect transformedRect = transform.mapRect(FloatRect(centeredOriginRect));
131         transformedRegion.unite(enclosedIntRect(transformedRect));
132     }
133     return transformedRegion;
134 }
135
136 template<typename LayerType, typename RenderSurfaceType>
137 void CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::leaveToTargetRenderSurface(const RenderSurfaceType* newTarget)
138 {
139     int lastIndex = m_stack.size() - 1;
140     bool surfaceWillBeAtTopAfterPop = m_stack.size() > 1 && m_stack[lastIndex - 1].surface == newTarget;
141
142     // We merge the screen occlusion from the current RenderSurface subtree out to its parent target RenderSurface.
143     // The target occlusion can be merged out as well but needs to be transformed to the new target.
144
145     const RenderSurfaceType* oldTarget = m_stack[lastIndex].surface;
146     Region oldTargetOcclusionInNewTarget = transformSurfaceOpaqueRegion<RenderSurfaceType>(oldTarget, m_stack[lastIndex].occlusionInTarget, oldTarget->drawTransform());
147
148     if (surfaceWillBeAtTopAfterPop) {
149         // Merge the top of the stack down.
150
151         m_stack[lastIndex - 1].occlusionInScreen.unite(m_stack[lastIndex].occlusionInScreen);
152         m_stack[lastIndex - 1].occlusionInTarget.unite(oldTargetOcclusionInNewTarget);
153         m_stack.removeLast();
154     } else {
155         // Replace the top of the stack with the new pushed surface. Copy the occluded screen region to the top.
156         m_stack.last().surface = newTarget;
157         m_stack.last().occlusionInTarget = oldTargetOcclusionInNewTarget;
158     }
159 }
160
161 template<typename LayerType>
162 static inline TransformationMatrix contentToScreenSpaceTransform(const LayerType* layer)
163 {
164     ASSERT(layerTransformsToScreenKnown(layer));
165     IntSize boundsInLayerSpace = layer->bounds();
166     IntSize boundsInContentSpace = layer->contentBounds();
167
168     TransformationMatrix transform = layer->screenSpaceTransform();
169
170     if (boundsInContentSpace.isEmpty())
171         return transform;
172
173     // Scale from content space to layer space
174     transform.scaleNonUniform(boundsInLayerSpace.width() / static_cast<double>(boundsInContentSpace.width()),
175                               boundsInLayerSpace.height() / static_cast<double>(boundsInContentSpace.height()));
176
177     return transform;
178 }
179
180 template<typename LayerType>
181 static inline TransformationMatrix contentToTargetSurfaceTransform(const LayerType* layer)
182 {
183     ASSERT(layerTransformsToTargetKnown(layer));
184     IntSize boundsInLayerSpace = layer->bounds();
185     IntSize boundsInContentSpace = layer->contentBounds();
186
187     TransformationMatrix transform = layer->drawTransform();
188
189     if (boundsInContentSpace.isEmpty())
190         return transform;
191
192     // Scale from content space to layer space
193     transform.scaleNonUniform(boundsInLayerSpace.width() / static_cast<double>(boundsInContentSpace.width()),
194                               boundsInLayerSpace.height() / static_cast<double>(boundsInContentSpace.height()));
195
196     // The draw transform expects the origin to be in the middle of the layer.
197     transform.translate(-boundsInContentSpace.width() / 2.0, -boundsInContentSpace.height() / 2.0);
198
199     return transform;
200 }
201
202 // FIXME: Remove usePaintTracking when paint tracking is on for paint culling.
203 template<typename LayerType>
204 static inline Region computeOcclusionBehindLayer(const LayerType* layer, const TransformationMatrix& transform, bool usePaintTracking)
205 {
206     Region opaqueRegion;
207
208     FloatQuad unoccludedQuad = transform.mapQuad(FloatQuad(layer->visibleLayerRect()));
209     bool isPaintedAxisAligned = unoccludedQuad.isRectilinear();
210     if (!isPaintedAxisAligned)
211         return opaqueRegion;
212
213     if (layer->opaque())
214         opaqueRegion = enclosedIntRect(unoccludedQuad.boundingBox());
215     else if (usePaintTracking && transform.isIdentity())
216         opaqueRegion = layer->opaqueContentsRegion();
217     else if (usePaintTracking) {
218         Region contentRegion = layer->opaqueContentsRegion();
219         Vector<IntRect> contentRects = contentRegion.rects();
220         for (size_t i = 0; i < contentRects.size(); ++i)
221             opaqueRegion.unite(enclosedIntRect(transform.mapRect(FloatRect(contentRects[i]))));
222     }
223     return opaqueRegion;
224 }
225
226 template<typename LayerType, typename RenderSurfaceType>
227 void CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::markOccludedBehindLayer(const LayerType* layer)
228 {
229     ASSERT(!m_stack.isEmpty());
230     ASSERT(layer->targetRenderSurface() == m_stack.last().surface);
231     if (m_stack.isEmpty())
232         return;
233
234     if (!layerOpacityKnown(layer) || layer->drawOpacity() < 1)
235         return;
236
237     // FIXME: Remove m_usePaintTracking when paint tracking is on for paint culling.
238     if (layerTransformsToScreenKnown(layer))
239         m_stack.last().occlusionInScreen.unite(computeOcclusionBehindLayer<LayerType>(layer, contentToScreenSpaceTransform<LayerType>(layer), m_usePaintTracking));
240     if (layerTransformsToTargetKnown(layer))
241         m_stack.last().occlusionInTarget.unite(computeOcclusionBehindLayer<LayerType>(layer, contentToTargetSurfaceTransform<LayerType>(layer), m_usePaintTracking));
242 }
243
244 static inline bool testContentRectOccluded(const IntRect& contentRect, const TransformationMatrix& contentSpaceTransform, const IntRect& scissorRect, const Region& occlusion)
245 {
246     FloatRect transformedRect = contentSpaceTransform.mapRect(FloatRect(contentRect));
247     // Take the enclosingIntRect, as we want to include partial pixels in the test.
248     IntRect targetRect = intersection(enclosingIntRect(transformedRect), scissorRect);
249     return targetRect.isEmpty() || occlusion.contains(targetRect);
250 }
251
252 template<typename LayerType, typename RenderSurfaceType>
253 bool CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::occluded(const LayerType* layer, const IntRect& contentRect) const
254 {
255     ASSERT(!m_stack.isEmpty());
256     if (m_stack.isEmpty())
257         return false;
258     if (contentRect.isEmpty())
259         return true;
260
261     ASSERT(layer->targetRenderSurface() == m_stack.last().surface);
262
263     if (layerTransformsToScreenKnown(layer) && testContentRectOccluded(contentRect, contentToScreenSpaceTransform<LayerType>(layer), m_scissorRectInScreenSpace, m_stack.last().occlusionInScreen))
264         return true;
265     if (layerTransformsToTargetKnown(layer) && testContentRectOccluded(contentRect, contentToTargetSurfaceTransform<LayerType>(layer), layerScissorRectInTargetSurface(layer), m_stack.last().occlusionInTarget))
266         return true;
267     return false;
268 }
269
270 // Determines what portion of rect, if any, is unoccluded (not occluded by region). If
271 // the resulting unoccluded region is not rectangular, we return a rect containing it.
272 static inline IntRect rectSubtractRegion(const IntRect& rect, const Region& region)
273 {
274     Region rectRegion(rect);
275     Region intersectRegion(intersect(region, rectRegion));
276
277     if (intersectRegion.isEmpty())
278         return rect;
279
280     rectRegion.subtract(intersectRegion);
281     IntRect boundsRect = rectRegion.bounds();
282     return boundsRect;
283 }
284
285 static FloatQuad projectQuad(const TransformationMatrix& transform, const FloatQuad& q, bool& clamped)
286 {
287     FloatQuad projectedQuad;
288     bool clampedPoint;
289     projectedQuad.setP1(transform.projectPoint(q.p1(), &clampedPoint));
290     clamped = clampedPoint;
291     projectedQuad.setP2(transform.projectPoint(q.p2(), &clampedPoint));
292     clamped |= clampedPoint;
293     projectedQuad.setP3(transform.projectPoint(q.p3(), &clampedPoint));
294     clamped |= clampedPoint;
295     projectedQuad.setP4(transform.projectPoint(q.p4(), &clampedPoint));
296     clamped |= clampedPoint;
297
298     return projectedQuad;
299 }
300
301 static inline IntRect computeUnoccludedContentRect(const IntRect& contentRect, const TransformationMatrix& contentSpaceTransform, const IntRect& scissorRect, const Region& occlusion)
302 {
303     if (!contentSpaceTransform.isInvertible())
304         return contentRect;
305
306     FloatRect transformedRect = contentSpaceTransform.mapRect(FloatRect(contentRect));
307     // Take the enclosingIntRect at each step, as we want to contain any unoccluded partial pixels in the resulting IntRect.
308     IntRect shrunkRect = rectSubtractRegion(intersection(enclosingIntRect(transformedRect), scissorRect), occlusion);
309     bool clamped; // FIXME: projectQuad returns invalid results when a point gets clamped. To be fixed in bug https://bugs.webkit.org/show_bug.cgi?id=80806.
310     IntRect unoccludedRect = enclosingIntRect(projectQuad(contentSpaceTransform.inverse(), FloatQuad(FloatRect(shrunkRect)), clamped).boundingBox());
311     if (clamped)
312         return contentRect;
313     // The rect back in content space is a bounding box and may extend outside of the original contentRect, so clamp it to the contentRectBounds.
314     return intersection(unoccludedRect, contentRect);
315 }
316
317 template<typename LayerType, typename RenderSurfaceType>
318 IntRect CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::unoccludedContentRect(const LayerType* layer, const IntRect& contentRect) const
319 {
320     ASSERT(!m_stack.isEmpty());
321     if (m_stack.isEmpty())
322         return contentRect;
323     if (contentRect.isEmpty())
324         return contentRect;
325
326     ASSERT(layer->targetRenderSurface() == m_stack.last().surface);
327
328     // We want to return a rect that contains all the visible parts of |contentRect| in both screen space and in the target surface.
329     // So we find the visible parts of |contentRect| in each space, and take the intersection.
330
331     IntRect unoccludedInScreen = contentRect;
332     if (layerTransformsToScreenKnown(layer))
333         unoccludedInScreen = computeUnoccludedContentRect(contentRect, contentToScreenSpaceTransform<LayerType>(layer), m_scissorRectInScreenSpace, m_stack.last().occlusionInScreen);
334
335     if (unoccludedInScreen.isEmpty())
336         return unoccludedInScreen;
337
338     IntRect unoccludedInTarget = contentRect;
339     if (layerTransformsToTargetKnown(layer))
340         unoccludedInTarget = computeUnoccludedContentRect(contentRect, contentToTargetSurfaceTransform<LayerType>(layer), layerScissorRectInTargetSurface(layer), m_stack.last().occlusionInTarget);
341
342     return intersection(unoccludedInScreen, unoccludedInTarget);
343 }
344
345 template<typename LayerType, typename RenderSurfaceType>
346 IntRect CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::layerScissorRectInTargetSurface(const LayerType* layer) const
347 {
348     const RenderSurfaceType* targetSurface = m_stack.last().surface;
349     FloatRect totalScissor = targetSurface->contentRect();
350     // FIXME: layer->clipRect() and layer->usesLayerClipping() is changing: https://bugs.webkit.org/show_bug.cgi?id=80622
351     if (!layer->clipRect().isEmpty())
352         totalScissor.intersect(layer->clipRect());
353     return enclosingIntRect(totalScissor);
354 }
355
356 template<typename LayerType, typename RenderSurfaceType>
357 const Region& CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::currentOcclusionInScreenSpace() const
358 {
359     ASSERT(!m_stack.isEmpty());
360     return m_stack.last().occlusionInScreen;
361 }
362
363 template<typename LayerType, typename RenderSurfaceType>
364 const Region& CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::currentOcclusionInTargetSurface() const
365 {
366     ASSERT(!m_stack.isEmpty());
367     return m_stack.last().occlusionInTarget;
368 }
369
370
371 // Declare the possible functions here for the linker.
372 template CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium>::CCOcclusionTrackerBase(IntRect scissorRectInScreenSpace, bool recordMetricsForFrame);
373 template void CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium>::enterTargetRenderSurface(const RenderSurfaceChromium* newTarget);
374 template void CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium>::finishedTargetRenderSurface(const LayerChromium* owningLayer, const RenderSurfaceChromium* finishedTarget);
375 template void CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium>::leaveToTargetRenderSurface(const RenderSurfaceChromium* newTarget);
376 template void CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium>::markOccludedBehindLayer(const LayerChromium*);
377 template bool CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium>::occluded(const LayerChromium*, const IntRect& contentRect) const;
378 template IntRect CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium>::unoccludedContentRect(const LayerChromium*, const IntRect& contentRect) const;
379 template const Region& CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium>::currentOcclusionInScreenSpace() const;
380 template const Region& CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium>::currentOcclusionInTargetSurface() const;
381 template IntRect CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium>::layerScissorRectInTargetSurface(const LayerChromium*) const;
382
383 template CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::CCOcclusionTrackerBase(IntRect scissorRectInScreenSpace, bool recordMetricsForFrame);
384 template void CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::enterTargetRenderSurface(const CCRenderSurface* newTarget);
385 template void CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::finishedTargetRenderSurface(const CCLayerImpl* owningLayer, const CCRenderSurface* finishedTarget);
386 template void CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::leaveToTargetRenderSurface(const CCRenderSurface* newTarget);
387 template void CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::markOccludedBehindLayer(const CCLayerImpl*);
388 template bool CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::occluded(const CCLayerImpl*, const IntRect& contentRect) const;
389 template IntRect CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::unoccludedContentRect(const CCLayerImpl*, const IntRect& contentRect) const;
390 template const Region& CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::currentOcclusionInScreenSpace() const;
391 template const Region& CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::currentOcclusionInTargetSurface() const;
392 template IntRect CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::layerScissorRectInTargetSurface(const CCLayerImpl*) const;
393
394
395 } // namespace WebCore
396 #endif // USE(ACCELERATED_COMPOSITING)