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