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