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