e0a3e4f8eae0e9c09feb1dadba9ea48283e50968
[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)
43     : m_scissorRectInScreenSpace(scissorRectInScreenSpace)
44     , m_surfaceDamageClient(0)
45     , m_overdrawMetrics(CCOverdrawMetrics::create())
46     , m_usePaintTracking(true) // FIXME: Remove this when paint tracking is on for paint culling.
47 {
48 }
49
50 template<typename LayerType, typename RenderSurfaceType>
51 CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::CCOcclusionTrackerBase(IntRect scissorRectInScreenSpace, const DamageClientType* surfaceDamageClient)
52     : m_scissorRectInScreenSpace(scissorRectInScreenSpace)
53     , m_surfaceDamageClient(surfaceDamageClient)
54     , m_overdrawMetrics(CCOverdrawMetrics::create())
55     , m_usePaintTracking(true) // FIXME: Remove this when paint tracking is on for paint culling.
56 {
57 }
58
59 template<typename LayerType, typename RenderSurfaceType>
60 void CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::enterTargetRenderSurface(const RenderSurfaceType* newTarget)
61 {
62     if (!m_stack.isEmpty() && m_stack.last().surface == newTarget)
63         return;
64
65     const RenderSurfaceType* oldTarget = m_stack.isEmpty() ? 0 : m_stack.last().surface;
66     const RenderSurfaceType* oldAncestorThatMovesPixels = !oldTarget ? 0 : oldTarget->nearestAncestorThatMovesPixels();
67     const RenderSurfaceType* newAncestorThatMovesPixels = newTarget->nearestAncestorThatMovesPixels();
68
69     m_stack.append(StackObject(newTarget));
70
71     // We copy the screen occlusion into the new RenderSurface subtree, but we never copy in the
72     // target occlusion, since we are looking at a new RenderSurface target.
73
74     // If we are entering a subtree that is going to move pixels around, then the occlusion we've computed
75     // so far won't apply to the pixels we're drawing here in the same way. We discard the occlusion thus
76     // far to be safe, and ensure we don't cull any pixels that are moved such that they become visible.
77     bool enteringSubtreeThatMovesPixels = newAncestorThatMovesPixels && newAncestorThatMovesPixels != oldAncestorThatMovesPixels;
78
79     bool copyScreenOcclusionForward = m_stack.size() > 1 && !enteringSubtreeThatMovesPixels;
80     if (copyScreenOcclusionForward) {
81         int lastIndex = m_stack.size() - 1;
82         m_stack[lastIndex].occlusionInScreen = m_stack[lastIndex - 1].occlusionInScreen;
83     }
84 }
85
86 template<typename LayerType, typename RenderSurfaceType>
87 void CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::finishedTargetRenderSurface(const LayerType* owningLayer, const RenderSurfaceType* finishedTarget)
88 {
89     // FIXME: Remove the owningLayer parameter when we can get all the info from the surface.
90     ASSERT(owningLayer->renderSurface() == finishedTarget);
91
92     // Make sure we know about the target surface.
93     enterTargetRenderSurface(finishedTarget);
94
95     if (owningLayer->maskLayer() || finishedTarget->drawOpacity() < 1 || finishedTarget->filters().hasFilterThatAffectsOpacity()) {
96         m_stack.last().occlusionInScreen = Region();
97         m_stack.last().occlusionInTarget = Region();
98     }
99 }
100
101 template<typename RenderSurfaceType>
102 static inline Region transformSurfaceOpaqueRegion(const RenderSurfaceType* surface, const Region& region, const TransformationMatrix& transform)
103 {
104     // Verify that rects within the |surface| will remain rects in its target surface after applying |transform|. If this is true, then
105     // apply |transform| to each rect within |region| in order to transform the entire Region.
106
107     IntRect bounds = region.bounds();
108     FloatRect centeredBounds(-bounds.width() / 2.0, -bounds.height() / 2.0, bounds.width(), bounds.height());
109     FloatQuad transformedBoundsQuad = transform.mapQuad(FloatQuad(centeredBounds));
110     if (!transformedBoundsQuad.isRectilinear())
111         return Region();
112
113     Region transformedRegion;
114
115     IntRect surfaceBounds = surface->contentRect();
116     Vector<IntRect> rects = region.rects();
117     Vector<IntRect>::const_iterator end = rects.end();
118     for (Vector<IntRect>::const_iterator i = rects.begin(); i != end; ++i) {
119         FloatRect centeredOriginRect(-i->width() / 2.0 + i->x() - surfaceBounds.x(), -i->height() / 2.0 + i->y() - surfaceBounds.y(), i->width(), i->height());
120         FloatRect transformedRect = transform.mapRect(FloatRect(centeredOriginRect));
121         transformedRegion.unite(enclosedIntRect(transformedRect));
122     }
123     return transformedRegion;
124 }
125
126 template<typename LayerType, typename RenderSurfaceType>
127 void CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::leaveToTargetRenderSurface(const RenderSurfaceType* newTarget)
128 {
129     int lastIndex = m_stack.size() - 1;
130     bool surfaceWillBeAtTopAfterPop = m_stack.size() > 1 && m_stack[lastIndex - 1].surface == newTarget;
131
132     // We merge the screen occlusion from the current RenderSurface subtree out to its parent target RenderSurface.
133     // The target occlusion can be merged out as well but needs to be transformed to the new target.
134
135     const RenderSurfaceType* oldTarget = m_stack[lastIndex].surface;
136     Region oldTargetOcclusionInNewTarget = transformSurfaceOpaqueRegion<RenderSurfaceType>(oldTarget, m_stack[lastIndex].occlusionInTarget, oldTarget->drawTransform());
137
138     if (surfaceWillBeAtTopAfterPop) {
139         // Merge the top of the stack down.
140
141         m_stack[lastIndex - 1].occlusionInScreen.unite(m_stack[lastIndex].occlusionInScreen);
142         m_stack[lastIndex - 1].occlusionInTarget.unite(oldTargetOcclusionInNewTarget);
143         m_stack.removeLast();
144     } else {
145         // Replace the top of the stack with the new pushed surface. Copy the occluded screen region to the top.
146         m_stack.last().surface = newTarget;
147         m_stack.last().occlusionInTarget = oldTargetOcclusionInNewTarget;
148     }
149 }
150
151 template<typename LayerType>
152 static inline TransformationMatrix contentToScreenSpaceTransform(const LayerType* layer)
153 {
154     IntSize boundsInLayerSpace = layer->bounds();
155     IntSize boundsInContentSpace = layer->contentBounds();
156
157     TransformationMatrix transform = layer->screenSpaceTransform();
158
159     if (boundsInContentSpace.isEmpty())
160         return transform;
161
162     // Scale from content space to layer space
163     transform.scaleNonUniform(boundsInLayerSpace.width() / static_cast<double>(boundsInContentSpace.width()),
164                               boundsInLayerSpace.height() / static_cast<double>(boundsInContentSpace.height()));
165
166     return transform;
167 }
168
169 template<typename LayerType>
170 static inline TransformationMatrix contentToTargetSurfaceTransform(const LayerType* layer)
171 {
172     IntSize boundsInLayerSpace = layer->bounds();
173     IntSize boundsInContentSpace = layer->contentBounds();
174
175     TransformationMatrix transform = layer->drawTransform();
176
177     if (boundsInContentSpace.isEmpty())
178         return transform;
179
180     // Scale from content space to layer space
181     transform.scaleNonUniform(boundsInLayerSpace.width() / static_cast<double>(boundsInContentSpace.width()),
182                               boundsInLayerSpace.height() / static_cast<double>(boundsInContentSpace.height()));
183
184     // The draw transform expects the origin to be in the middle of the layer.
185     transform.translate(-boundsInContentSpace.width() / 2.0, -boundsInContentSpace.height() / 2.0);
186
187     return transform;
188 }
189
190 // FIXME: Remove usePaintTracking when paint tracking is on for paint culling.
191 template<typename LayerType>
192 static inline Region computeOcclusionBehindLayer(const LayerType* layer, const TransformationMatrix& transform, bool usePaintTracking)
193 {
194     Region opaqueRegion;
195
196     FloatQuad unoccludedQuad = transform.mapQuad(FloatQuad(layer->visibleLayerRect()));
197     bool isPaintedAxisAligned = unoccludedQuad.isRectilinear();
198     if (!isPaintedAxisAligned)
199         return opaqueRegion;
200
201     if (layer->opaque())
202         opaqueRegion = enclosedIntRect(unoccludedQuad.boundingBox());
203     else if (usePaintTracking && transform.isIdentity())
204         opaqueRegion = layer->opaqueContentsRegion();
205     else if (usePaintTracking) {
206         Region contentRegion = layer->opaqueContentsRegion();
207         Vector<IntRect> contentRects = contentRegion.rects();
208         for (size_t i = 0; i < contentRects.size(); ++i)
209             opaqueRegion.unite(enclosedIntRect(transform.mapRect(FloatRect(contentRects[i]))));
210     }
211     return opaqueRegion;
212 }
213
214 template<typename LayerType, typename RenderSurfaceType>
215 void CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::markOccludedBehindLayer(const LayerType* layer)
216 {
217     ASSERT(!m_stack.isEmpty());
218     ASSERT(layer->targetRenderSurface() == m_stack.last().surface);
219     if (m_stack.isEmpty())
220         return;
221
222     if (layer->drawOpacity() != 1)
223         return;
224
225     TransformationMatrix contentToScreenSpace = contentToScreenSpaceTransform<LayerType>(layer);
226     TransformationMatrix contentToTargetSurface = contentToTargetSurfaceTransform<LayerType>(layer);
227
228     // FIXME: Remove m_usePaintTracking when paint tracking is on for paint culling.
229     m_stack.last().occlusionInScreen.unite(computeOcclusionBehindLayer<LayerType>(layer, contentToScreenSpace, m_usePaintTracking));
230     m_stack.last().occlusionInTarget.unite(computeOcclusionBehindLayer<LayerType>(layer, contentToTargetSurface, m_usePaintTracking));
231 }
232
233 static inline bool testContentRectOccluded(const IntRect& contentRect, const TransformationMatrix& contentSpaceTransform, const IntRect& scissorRect, const Region& occlusion)
234 {
235     FloatRect transformedRect = contentSpaceTransform.mapRect(FloatRect(contentRect));
236     // Take the enclosingIntRect, as we want to include partial pixels in the test.
237     IntRect targetRect = intersection(enclosingIntRect(transformedRect), scissorRect);
238     return targetRect.isEmpty() || occlusion.contains(targetRect);
239 }
240
241 template<typename LayerType, typename RenderSurfaceType>
242 bool CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::occluded(const LayerType* layer, const IntRect& contentRect) const
243 {
244     ASSERT(!m_stack.isEmpty());
245     if (m_stack.isEmpty())
246         return false;
247     if (contentRect.isEmpty())
248         return true;
249
250     ASSERT(layer->targetRenderSurface() == m_stack.last().surface);
251
252     if (testContentRectOccluded(contentRect, contentToScreenSpaceTransform<LayerType>(layer), m_scissorRectInScreenSpace, m_stack.last().occlusionInScreen))
253         return true;
254     if (testContentRectOccluded(contentRect, contentToTargetSurfaceTransform<LayerType>(layer), layerScissorRectInTargetSurface(layer), m_stack.last().occlusionInTarget))
255         return true;
256     return false;
257 }
258
259 // Determines what portion of rect, if any, is unoccluded (not occluded by region). If
260 // the resulting unoccluded region is not rectangular, we return a rect containing it.
261 static inline IntRect rectSubtractRegion(const IntRect& rect, const Region& region)
262 {
263     Region rectRegion(rect);
264     Region intersectRegion(intersect(region, rectRegion));
265
266     if (intersectRegion.isEmpty())
267         return rect;
268
269     rectRegion.subtract(intersectRegion);
270     IntRect boundsRect = rectRegion.bounds();
271     return boundsRect;
272 }
273
274 static FloatQuad projectQuad(const TransformationMatrix& transform, const FloatQuad& q, bool& clamped)
275 {
276     FloatQuad projectedQuad;
277     bool clampedPoint;
278     projectedQuad.setP1(transform.projectPoint(q.p1(), &clampedPoint));
279     clamped = clampedPoint;
280     projectedQuad.setP2(transform.projectPoint(q.p2(), &clampedPoint));
281     clamped |= clampedPoint;
282     projectedQuad.setP3(transform.projectPoint(q.p3(), &clampedPoint));
283     clamped |= clampedPoint;
284     projectedQuad.setP4(transform.projectPoint(q.p4(), &clampedPoint));
285     clamped |= clampedPoint;
286
287     return projectedQuad;
288 }
289
290 static inline IntRect computeUnoccludedContentRect(const IntRect& contentRect, const TransformationMatrix& contentSpaceTransform, const IntRect& scissorRect, const Region& occlusion)
291 {
292     if (!contentSpaceTransform.isInvertible())
293         return contentRect;
294
295     FloatRect transformedRect = contentSpaceTransform.mapRect(FloatRect(contentRect));
296     // Take the enclosingIntRect at each step, as we want to contain any unoccluded partial pixels in the resulting IntRect.
297     IntRect shrunkRect = rectSubtractRegion(intersection(enclosingIntRect(transformedRect), scissorRect), occlusion);
298     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.
299     IntRect unoccludedRect = enclosingIntRect(projectQuad(contentSpaceTransform.inverse(), FloatQuad(FloatRect(shrunkRect)), clamped).boundingBox());
300     if (clamped)
301         return contentRect;
302     // The rect back in content space is a bounding box and may extend outside of the original contentRect, so clamp it to the contentRectBounds.
303     return intersection(unoccludedRect, contentRect);
304 }
305
306 template<typename LayerType, typename RenderSurfaceType>
307 IntRect CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::unoccludedContentRect(const LayerType* layer, const IntRect& contentRect) const
308 {
309     ASSERT(!m_stack.isEmpty());
310     if (m_stack.isEmpty())
311         return contentRect;
312     if (contentRect.isEmpty())
313         return contentRect;
314
315     ASSERT(layer->targetRenderSurface() == m_stack.last().surface);
316
317     // We want to return a rect that contains all the visible parts of |contentRect| in both screen space and in the target surface.
318     // So we find the visible parts of |contentRect| in each space, and take the intersection.
319
320     TransformationMatrix contentToScreenSpace = contentToScreenSpaceTransform<LayerType>(layer);
321     TransformationMatrix contentToTargetSurface = contentToTargetSurfaceTransform<LayerType>(layer);
322
323     IntRect unoccludedInScreen = computeUnoccludedContentRect(contentRect, contentToScreenSpace, m_scissorRectInScreenSpace, m_stack.last().occlusionInScreen);
324     if (unoccludedInScreen.isEmpty())
325         return IntRect();
326     IntRect unoccludedInTarget = computeUnoccludedContentRect(contentRect, contentToTargetSurface, layerScissorRectInTargetSurface(layer), m_stack.last().occlusionInTarget);
327
328     return intersection(unoccludedInScreen, unoccludedInTarget);
329 }
330
331 template<typename LayerType, typename RenderSurfaceType>
332 IntRect CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::layerScissorRectInTargetSurface(const LayerType* layer) const
333 {
334     const RenderSurfaceType* targetSurface = m_stack.last().surface;
335     FloatRect totalScissor = targetSurface->contentRect();
336     if (m_surfaceDamageClient)
337         totalScissor.intersect(m_surfaceDamageClient->damageRect(targetSurface));
338     if (!layer->clipRect().isEmpty())
339         totalScissor.intersect(layer->clipRect());
340     return enclosingIntRect(totalScissor);
341 }
342
343 template<typename LayerType, typename RenderSurfaceType>
344 const Region& CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::currentOcclusionInScreenSpace() const
345 {
346     ASSERT(!m_stack.isEmpty());
347     return m_stack.last().occlusionInScreen;
348 }
349
350 template<typename LayerType, typename RenderSurfaceType>
351 const Region& CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::currentOcclusionInTargetSurface() const
352 {
353     ASSERT(!m_stack.isEmpty());
354     return m_stack.last().occlusionInTarget;
355 }
356
357
358 // Declare the possible functions here for the linker.
359 template CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium>::CCOcclusionTrackerBase(IntRect scissorRectInScreenSpace);
360 template CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium>::CCOcclusionTrackerBase(IntRect scissorRectInScreenSpace, const CCOcclusionTrackerDamageClient* surfaceDamageClient);
361 template void CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium>::enterTargetRenderSurface(const RenderSurfaceChromium* newTarget);
362 template void CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium>::finishedTargetRenderSurface(const LayerChromium* owningLayer, const RenderSurfaceChromium* finishedTarget);
363 template void CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium>::leaveToTargetRenderSurface(const RenderSurfaceChromium* newTarget);
364 template void CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium>::markOccludedBehindLayer(const LayerChromium*);
365 template bool CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium>::occluded(const LayerChromium*, const IntRect& contentRect) const;
366 template IntRect CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium>::unoccludedContentRect(const LayerChromium*, const IntRect& contentRect) const;
367 template const Region& CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium>::currentOcclusionInScreenSpace() const;
368 template const Region& CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium>::currentOcclusionInTargetSurface() const;
369 template IntRect CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium>::layerScissorRectInTargetSurface(const LayerChromium*) const;
370
371 template CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::CCOcclusionTrackerBase(IntRect scissorRectInScreenSpace);
372 template CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::CCOcclusionTrackerBase(IntRect scissorRectInScreenSpace, const CCOcclusionTrackerDamageClientImpl* surfaceDamageClient);
373 template void CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::enterTargetRenderSurface(const CCRenderSurface* newTarget);
374 template void CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::finishedTargetRenderSurface(const CCLayerImpl* owningLayer, const CCRenderSurface* finishedTarget);
375 template void CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::leaveToTargetRenderSurface(const CCRenderSurface* newTarget);
376 template void CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::markOccludedBehindLayer(const CCLayerImpl*);
377 template bool CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::occluded(const CCLayerImpl*, const IntRect& contentRect) const;
378 template IntRect CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::unoccludedContentRect(const CCLayerImpl*, const IntRect& contentRect) const;
379 template const Region& CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::currentOcclusionInScreenSpace() const;
380 template const Region& CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::currentOcclusionInTargetSurface() const;
381 template IntRect CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::layerScissorRectInTargetSurface(const CCLayerImpl*) const;
382
383
384 } // namespace WebCore
385 #endif // USE(ACCELERATED_COMPOSITING)