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