source: webkit/trunk/Source/WebCore/rendering/LayerOverlapMap.cpp

Last change on this file was 293126, checked in by Simon Fraser, 3 years ago

REGRESSION (r281913): Map toolbar flickers when dragging the map on https://gis.ee/
https://bugs.webkit.org/show_bug.cgi?id=238589
<rdar://90483049>

Reviewed by Alan Bujtas.
Source/WebCore:

If a layer has negative z-order descendants, and one of those descendants becomes
composited, then that layer also has to composite, because its foreground needs to render in
front of those descendants. But this is tricky, because the compositing toggle is
out-of-order; we only know that the layer has to get composited after processing
descendants.

In particular, this created a problem with the overlap map, which tracks sets of rectangles
in "compositing scopes" via LayerOverlapMap. The delayed compositing toggle of the layer
with negative z-order descendants meant that any layers processed up to that point had their
bounds added in the wrong overlap scope. This resulted in subsequent bad overlap testing,
and a failure to make later layers composited.

The previous fix in this area (r281913) was incorrect. Revert it, and do a better fix, which
is to speculatively push an overlap map entry for the layer with negative z-order
descendants, if necessary. If this layer ends up not getting composited, we unwind the
speculative push, which will merge that scope with its parent scope.

In addition give OverlapMapContainer (an "overlap scope") a RenderLayer& which we can use
for logging, and to assert on correct behavior; it is not otherwise used in release builds.

Tests: compositing/layer-creation/overlap-with-negative-z-layers.html

compositing/layer-creation/subtree-div-overlaps-multiple-negative-z-divs.html

  • rendering/LayerOverlapMap.cpp:

(WebCore::OverlapMapContainer::OverlapMapContainer):
(WebCore::OverlapMapContainer::scopeLayer const):
(WebCore::OverlapMapContainer::dump const):
(WebCore::LayerOverlapMap::LayerOverlapMap):
(WebCore::LayerOverlapMap::add):
(WebCore::LayerOverlapMap::pushCompositingContainer):
(WebCore::LayerOverlapMap::popCompositingContainer):

  • rendering/LayerOverlapMap.h:
  • rendering/RenderLayerCompositor.cpp:

(WebCore::RenderLayerCompositor::computeCompositingRequirements):
(WebCore::RenderLayerCompositor::traverseUnchangedSubtree):
(WebCore::RenderLayerCompositor::updateOverlapMap const):

LayoutTests:

Move subtree-div-overlaps-multiple-negative-z-divs.html into the layer-creation directory.
Add a new test.

  • compositing/layer-creation/overlap-with-negative-z-layers-expected.html: Added.
  • compositing/layer-creation/overlap-with-negative-z-layers.html: Added.
  • compositing/layer-creation/subtree-div-overlaps-multiple-negative-z-divs-expected.html: Renamed from LayoutTests/compositing/subtree-div-overlaps-multiple-negative-z-divs-expected.html.
  • compositing/layer-creation/subtree-div-overlaps-multiple-negative-z-divs.html: Renamed from LayoutTests/compositing/subtree-div-overlaps-multiple-negative-z-divs.html.
  • fast/harness/render-tree-as-text-options-expected.txt: Test progressed; update results.
File size: 12.3 KB
Line 
1/*
2 * Copyright (C) 2019 Apple 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 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "LayerOverlapMap.h"
28#include "Logging.h"
29#include "RenderLayer.h"
30#include <wtf/text/TextStream.h>
31
32namespace WebCore {
33
34struct RectList {
35 Vector<LayoutRect> rects;
36 LayoutRect boundingRect;
37
38 void append(const LayoutRect& rect)
39 {
40 rects.append(rect);
41 boundingRect.unite(rect);
42 }
43
44 void append(const RectList& rectList)
45 {
46 rects.appendVector(rectList.rects);
47 boundingRect.unite(rectList.boundingRect);
48 }
49
50 bool intersects(const LayoutRect& rect) const
51 {
52 if (!rects.size() || !rect.intersects(boundingRect))
53 return false;
54
55 for (const auto& currentRect : rects) {
56 if (currentRect.intersects(rect))
57 return true;
58 }
59 return false;
60 }
61};
62
63static TextStream& operator<<(TextStream& ts, const RectList& rectList)
64{
65 ts << "bounds " << rectList.boundingRect << " (" << rectList.rects << " rects)";
66 return ts;
67}
68
69// Used to store overlap rects in a way that takes overflow into account.
70// It stores a tree whose nodes are layers with composited scrolling. The tree is built lazily as layers are added whose containing block
71// chains contain composited scrollers. The tree always starts at the root layer.
72// Checking for overlap involves finding the node for the clipping layer enclosing the given layer (or the root),
73// and comparing against the bounds of earlier siblings.
74class OverlapMapContainer {
75 WTF_MAKE_FAST_ALLOCATED;
76public:
77 OverlapMapContainer(const RenderLayer& rootLayer, const RenderLayer& scopeLayer)
78 : m_rootScope(rootLayer)
79 , m_scopeLayer(scopeLayer)
80 {
81 }
82
83 // Layers are added in z-order, lazily creating clipping scopes as necessary.
84 void add(const RenderLayer&, const LayoutRect& bounds, const Vector<LayerOverlapMap::LayerAndBounds>& enclosingClippingLayers);
85 bool overlapsLayers(const RenderLayer&, const LayoutRect& bounds, const Vector<LayerOverlapMap::LayerAndBounds>& enclosingClippingLayers) const;
86 void append(std::unique_ptr<OverlapMapContainer>&&);
87
88 String dump(unsigned) const;
89
90 const RenderLayer& scopeLayer() const { return m_scopeLayer; }
91
92private:
93 struct ClippingScope {
94 ClippingScope(const RenderLayer& inLayer)
95 : layer(inLayer)
96 {
97 }
98
99 ClippingScope(const LayerOverlapMap::LayerAndBounds& layerAndBounds)
100 : layer(layerAndBounds.layer)
101 , bounds(layerAndBounds.bounds)
102 {
103 }
104
105 ClippingScope* childWithLayer(const RenderLayer& layer) const
106 {
107 for (auto& child : children) {
108 if (&child.layer == &layer)
109 return const_cast<ClippingScope*>(&child);
110 }
111 return nullptr;
112 }
113
114 ClippingScope* addChildWithLayerAndBounds(const LayerOverlapMap::LayerAndBounds& layerAndBounds)
115 {
116 children.append({ layerAndBounds });
117 return &children.last();
118 }
119
120 ClippingScope* addChild(const ClippingScope& child)
121 {
122 ASSERT(&layer != &child.layer);
123 children.append(child);
124 return &children.last();
125 }
126
127 void appendRect(const LayoutRect& bounds)
128 {
129 rectList.append(bounds);
130 }
131
132 const RenderLayer& layer;
133 LayoutRect bounds; // Bounds of the composited clip.
134 Vector<ClippingScope> children;
135 RectList rectList;
136 };
137
138 static ClippingScope* clippingScopeContainingLayerChildRecursive(const ClippingScope& currNode, const RenderLayer& layer)
139 {
140 for (auto& child : currNode.children) {
141 if (&layer == &child.layer)
142 return const_cast<ClippingScope*>(&currNode);
143
144 if (auto* foundNode = clippingScopeContainingLayerChildRecursive(child, layer))
145 return foundNode;
146 }
147
148 return nullptr;
149 }
150
151 ClippingScope* scopeContainingLayer(const RenderLayer& layer) const
152 {
153 return clippingScopeContainingLayerChildRecursive(m_rootScope, layer);
154 }
155
156 static void mergeClippingScopesRecursive(const ClippingScope& sourceScope, ClippingScope& destScope);
157
158 ClippingScope* ensureClippingScopeForLayers(const Vector<LayerOverlapMap::LayerAndBounds>& enclosingClippingLayers);
159 ClippingScope* findClippingScopeForLayers(const Vector<LayerOverlapMap::LayerAndBounds>& enclosingClippingLayers) const;
160
161 void recursiveOutputToStream(TextStream&, const ClippingScope&, unsigned depth) const;
162
163 const ClippingScope& rootScope() const { return m_rootScope; }
164 ClippingScope& rootScope() { return m_rootScope; }
165
166 ClippingScope m_rootScope;
167 const RenderLayer& m_scopeLayer;
168};
169
170void OverlapMapContainer::add(const RenderLayer&, const LayoutRect& bounds, const Vector<LayerOverlapMap::LayerAndBounds>& enclosingClippingLayers)
171{
172 auto* layerScope = ensureClippingScopeForLayers(enclosingClippingLayers);
173 layerScope->appendRect(bounds);
174}
175
176bool OverlapMapContainer::overlapsLayers(const RenderLayer&, const LayoutRect& bounds, const Vector<LayerOverlapMap::LayerAndBounds>& enclosingClippingLayers) const
177{
178 if (m_rootScope.rectList.intersects(bounds))
179 return true;
180
181 if (m_rootScope.children.isEmpty())
182 return false;
183
184 // Find the ClippingScope for which this layer is a child.
185 auto* clippingScope = findClippingScopeForLayers(enclosingClippingLayers);
186 if (!clippingScope)
187 return false;
188
189 if (clippingScope->rectList.intersects(bounds))
190 return true;
191
192 // FIXME: In some cases do we have to walk up the ancestor clipping scope chain?
193 return false;
194}
195
196void OverlapMapContainer::mergeClippingScopesRecursive(const ClippingScope& sourceScope, ClippingScope& destScope)
197{
198 ASSERT(&sourceScope.layer == &destScope.layer);
199 destScope.rectList.append(sourceScope.rectList);
200
201 for (auto& sourceChildScope : sourceScope.children) {
202 ClippingScope* destChild = destScope.childWithLayer(sourceChildScope.layer);
203 if (destChild)
204 mergeClippingScopesRecursive(sourceChildScope, *destChild);
205 else {
206 // New child, just copy the whole subtree.
207 destScope.addChild(sourceChildScope);
208 }
209 }
210}
211
212void OverlapMapContainer::append(std::unique_ptr<OverlapMapContainer>&& otherContainer)
213{
214 mergeClippingScopesRecursive(otherContainer->rootScope(), m_rootScope);
215}
216
217OverlapMapContainer::ClippingScope* OverlapMapContainer::ensureClippingScopeForLayers(const Vector<LayerOverlapMap::LayerAndBounds>& enclosingClippingLayers)
218{
219 ASSERT(enclosingClippingLayers.size());
220 ASSERT(enclosingClippingLayers[0].layer.isRenderViewLayer());
221
222 auto* currScope = &m_rootScope;
223 for (unsigned i = 1; i < enclosingClippingLayers.size(); ++i) {
224 auto& scopeLayerAndBounds = enclosingClippingLayers[i];
225 auto* childScope = currScope->childWithLayer(scopeLayerAndBounds.layer);
226 if (!childScope) {
227 currScope = currScope->addChildWithLayerAndBounds(scopeLayerAndBounds);
228 break;
229 }
230
231 currScope = childScope;
232 }
233
234 return const_cast<ClippingScope*>(currScope);
235}
236
237OverlapMapContainer::ClippingScope* OverlapMapContainer::findClippingScopeForLayers(const Vector<LayerOverlapMap::LayerAndBounds>& enclosingClippingLayers) const
238{
239 ASSERT(enclosingClippingLayers.size());
240 ASSERT(enclosingClippingLayers[0].layer.isRenderViewLayer());
241
242 const auto* currScope = &m_rootScope;
243 for (unsigned i = 1; i < enclosingClippingLayers.size(); ++i) {
244 auto& scopeLayerAndBounds = enclosingClippingLayers[i];
245 auto* childScope = currScope->childWithLayer(scopeLayerAndBounds.layer);
246 if (!childScope)
247 return nullptr;
248
249 currScope = childScope;
250 }
251
252 return const_cast<ClippingScope*>(currScope);
253}
254
255void OverlapMapContainer::recursiveOutputToStream(TextStream& ts, const ClippingScope& scope, unsigned depth) const
256{
257 ts << "\n" << indent << TextStream::Repeat { 2 * depth, ' ' } << " scope for layer " << &scope.layer << " rects " << scope.rectList;
258 for (auto& childScope : scope.children)
259 recursiveOutputToStream(ts, childScope, depth + 1);
260}
261
262String OverlapMapContainer::dump(unsigned indent) const
263{
264 TextStream multilineStream;
265 multilineStream.increaseIndent(indent);
266 multilineStream << "overlap container - root layer " << &m_rootScope.layer << " scope layer " << &m_scopeLayer << " rects " << m_rootScope.rectList;
267
268 for (auto& childScope : m_rootScope.children)
269 recursiveOutputToStream(multilineStream, childScope, 1);
270
271 return multilineStream.release();
272}
273
274LayerOverlapMap::LayerOverlapMap(const RenderLayer& rootLayer)
275 : m_geometryMap(UseTransforms)
276 , m_rootLayer(rootLayer)
277{
278 // Begin assuming the root layer will be composited so that there is
279 // something on the stack. The root layer should also never get an
280 // popCompositingContainer call.
281 pushCompositingContainer(rootLayer);
282}
283
284LayerOverlapMap::~LayerOverlapMap() = default;
285
286void LayerOverlapMap::add(const RenderLayer& layer, const LayoutRect& bounds, const Vector<LayerAndBounds>& enclosingClippingLayers)
287{
288 // Layers do not contribute to overlap immediately--instead, they will
289 // contribute to overlap as soon as their composited ancestor has been
290 // recursively processed and popped off the stack.
291 ASSERT(m_overlapStack.size() >= 2);
292 auto& container = m_overlapStack[m_overlapStack.size() - 2];
293 container->add(layer, bounds, enclosingClippingLayers);
294
295 LOG_WITH_STREAM(CompositingOverlap, stream << "layer " << &layer << " contributes to overlap in the scope of layer " << &container->scopeLayer() << ", added to map " << *this);
296
297 m_isEmpty = false;
298}
299
300bool LayerOverlapMap::overlapsLayers(const RenderLayer& layer, const LayoutRect& bounds, const Vector<LayerAndBounds>& enclosingClippingLayers) const
301{
302 return m_overlapStack.last()->overlapsLayers(layer, bounds, enclosingClippingLayers);
303}
304
305void LayerOverlapMap::pushCompositingContainer(const RenderLayer& layer)
306{
307 m_overlapStack.append(makeUnique<OverlapMapContainer>(m_rootLayer, layer));
308}
309
310void LayerOverlapMap::popCompositingContainer(const RenderLayer& layer)
311{
312 ASSERT_UNUSED(layer, &m_overlapStack.last()->scopeLayer() == &layer);
313 m_overlapStack[m_overlapStack.size() - 2]->append(WTFMove(m_overlapStack.last()));
314 m_overlapStack.removeLast();
315}
316
317static TextStream& operator<<(TextStream& ts, const OverlapMapContainer& container)
318{
319 ts << container.dump(ts.indent());
320 return ts;
321}
322
323TextStream& operator<<(TextStream& ts, const LayerOverlapMap& overlapMap)
324{
325 TextStream multilineStream;
326
327 TextStream::GroupScope scope(ts);
328 multilineStream << "LayerOverlapMap\n";
329 multilineStream.increaseIndent(2);
330
331 bool needNewline = false;
332 for (auto& container : overlapMap.overlapStack()) {
333 if (needNewline)
334 multilineStream << "\n";
335 else
336 needNewline = true;
337 multilineStream << indent << *container;
338 }
339
340 ts << multilineStream.release();
341 return ts;
342}
343
344} // namespace WebCore
345
Note: See TracBrowser for help on using the repository browser.