/src/mozilla-central/gfx/layers/LayerTreeInvalidation.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
5 | | * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "LayerTreeInvalidation.h" |
8 | | |
9 | | #include <stdint.h> // for uint32_t |
10 | | #include "ImageContainer.h" // for ImageContainer |
11 | | #include "ImageLayers.h" // for ImageLayer, etc |
12 | | #include "Layers.h" // for Layer, ContainerLayer, etc |
13 | | #include "Units.h" // for ParentLayerIntRect |
14 | | #include "gfxRect.h" // for gfxRect |
15 | | #include "gfxUtils.h" // for gfxUtils |
16 | | #include "mozilla/ArrayUtils.h" // for ArrayEqual |
17 | | #include "mozilla/gfx/BaseSize.h" // for BaseSize |
18 | | #include "mozilla/gfx/Point.h" // for IntSize |
19 | | #include "mozilla/mozalloc.h" // for operator new, etc |
20 | | #include "nsDataHashtable.h" // for nsDataHashtable |
21 | | #include "nsDebug.h" // for NS_ASSERTION |
22 | | #include "nsHashKeys.h" // for nsPtrHashKey |
23 | | #include "nsISupportsImpl.h" // for Layer::AddRef, etc |
24 | | #include "nsRect.h" // for IntRect |
25 | | #include "nsTArray.h" // for AutoTArray, nsTArray_Impl |
26 | | #include "mozilla/Poison.h" |
27 | | #include "mozilla/layers/ImageHost.h" |
28 | | #include "mozilla/layers/LayerManagerComposite.h" |
29 | | #include "TreeTraversal.h" // for ForEachNode |
30 | | #include "LayersLogging.h" |
31 | | |
32 | | // LayerTreeInvalidation debugging |
33 | | #define LTI_DEBUG 0 |
34 | | |
35 | | #if LTI_DEBUG |
36 | | # define LTI_DEEPER(aPrefix) nsPrintfCString("%s ", aPrefix).get() |
37 | | # define LTI_DUMP(rgn, label) if (!(rgn).IsEmpty()) printf_stderr("%s%p: " label " portion is %s\n", aPrefix, mLayer.get(), Stringify(rgn).c_str()); |
38 | | # define LTI_LOG(...) printf_stderr(__VA_ARGS__) |
39 | | #else |
40 | 0 | # define LTI_DEEPER(aPrefix) nullptr |
41 | | # define LTI_DUMP(rgn, label) |
42 | | # define LTI_LOG(...) |
43 | | #endif |
44 | | |
45 | | using namespace mozilla::gfx; |
46 | | |
47 | | namespace mozilla { |
48 | | namespace layers { |
49 | | |
50 | | struct LayerPropertiesBase; |
51 | | UniquePtr<LayerPropertiesBase> CloneLayerTreePropertiesInternal(Layer* aRoot, bool aIsMask = false); |
52 | | |
53 | | /** |
54 | | * Get accumulated transform of from the context creating layer to the |
55 | | * given layer. |
56 | | */ |
57 | | static Matrix4x4 |
58 | 0 | GetTransformIn3DContext(Layer* aLayer) { |
59 | 0 | Matrix4x4 transform = aLayer->GetLocalTransform(); |
60 | 0 | for (Layer* layer = aLayer->GetParent(); |
61 | 0 | layer && layer->Extend3DContext(); |
62 | 0 | layer = layer->GetParent()) { |
63 | 0 | transform = transform * layer->GetLocalTransform(); |
64 | 0 | } |
65 | 0 | return transform; |
66 | 0 | } |
67 | | |
68 | | /** |
69 | | * Get a transform for the given layer depending on extending 3D |
70 | | * context. |
71 | | * |
72 | | * @return local transform for layers not participating 3D rendering |
73 | | * context, or the accmulated transform in the context for else. |
74 | | */ |
75 | | static Matrix4x4Flagged |
76 | 0 | GetTransformForInvalidation(Layer* aLayer) { |
77 | 0 | return (!aLayer->Is3DContextLeaf() && !aLayer->Extend3DContext() ? |
78 | 0 | aLayer->GetLocalTransform() : GetTransformIn3DContext(aLayer)); |
79 | 0 | } |
80 | | |
81 | | static IntRect |
82 | | TransformRect(const IntRect& aRect, const Matrix4x4Flagged& aTransform) |
83 | 0 | { |
84 | 0 | if (aRect.IsEmpty()) { |
85 | 0 | return IntRect(); |
86 | 0 | } |
87 | 0 | |
88 | 0 | Rect rect(aRect.X(), aRect.Y(), aRect.Width(), aRect.Height()); |
89 | 0 | rect = aTransform.TransformAndClipBounds(rect, Rect::MaxIntRect()); |
90 | 0 | rect.RoundOut(); |
91 | 0 |
|
92 | 0 | IntRect intRect; |
93 | 0 | if (!rect.ToIntRect(&intRect)) { |
94 | 0 | intRect = IntRect::MaxIntRect(); |
95 | 0 | } |
96 | 0 |
|
97 | 0 | return intRect; |
98 | 0 | } |
99 | | |
100 | | static void |
101 | | AddTransformedRegion(nsIntRegion& aDest, const nsIntRegion& aSource, const Matrix4x4Flagged& aTransform) |
102 | 0 | { |
103 | 0 | for (auto iter = aSource.RectIter(); !iter.Done(); iter.Next()) { |
104 | 0 | aDest.Or(aDest, TransformRect(iter.Get(), aTransform)); |
105 | 0 | } |
106 | 0 | aDest.SimplifyOutward(20); |
107 | 0 | } |
108 | | |
109 | | static void |
110 | | AddRegion(nsIntRegion& aDest, const nsIntRegion& aSource) |
111 | 0 | { |
112 | 0 | aDest.Or(aDest, aSource); |
113 | 0 | aDest.SimplifyOutward(20); |
114 | 0 | } |
115 | | |
116 | | /** |
117 | | * Walks over this layer, and all descendant layers. |
118 | | * If any of these are a ContainerLayer that reports invalidations to a PresShell, |
119 | | * then report that the entire bounds have changed. |
120 | | */ |
121 | | static void |
122 | | NotifySubdocumentInvalidation(Layer* aLayer, NotifySubDocInvalidationFunc aCallback) |
123 | 0 | { |
124 | 0 | ForEachNode<ForwardIterator>( |
125 | 0 | aLayer, |
126 | 0 | [aCallback] (Layer* layer) |
127 | 0 | { |
128 | 0 | layer->ClearInvalidRegion(); |
129 | 0 | if (layer->GetMaskLayer()) { |
130 | 0 | NotifySubdocumentInvalidation(layer->GetMaskLayer(), aCallback); |
131 | 0 | } |
132 | 0 | for (size_t i = 0; i < layer->GetAncestorMaskLayerCount(); i++) { |
133 | 0 | Layer* maskLayer = layer->GetAncestorMaskLayerAt(i); |
134 | 0 | NotifySubdocumentInvalidation(maskLayer, aCallback); |
135 | 0 | } |
136 | 0 | }, |
137 | 0 | [aCallback] (Layer* layer) |
138 | 0 | { |
139 | 0 | ContainerLayer* container = layer->AsContainerLayer(); |
140 | 0 | if (container) { |
141 | 0 | nsIntRegion region = container->GetLocalVisibleRegion().ToUnknownRegion(); |
142 | 0 | aCallback(container, ®ion); |
143 | 0 | } |
144 | 0 | }); |
145 | 0 | } |
146 | | |
147 | | struct LayerPropertiesBase : public LayerProperties |
148 | | { |
149 | | explicit LayerPropertiesBase(Layer* aLayer) |
150 | | : mLayer(aLayer) |
151 | | , mMaskLayer(nullptr) |
152 | | , mVisibleRegion(mLayer->GetLocalVisibleRegion().ToUnknownRegion()) |
153 | | , mPostXScale(aLayer->GetPostXScale()) |
154 | | , mPostYScale(aLayer->GetPostYScale()) |
155 | | , mOpacity(aLayer->GetLocalOpacity()) |
156 | | , mUseClipRect(!!aLayer->GetLocalClipRect()) |
157 | 0 | { |
158 | 0 | MOZ_COUNT_CTOR(LayerPropertiesBase); |
159 | 0 | if (aLayer->GetMaskLayer()) { |
160 | 0 | mMaskLayer = CloneLayerTreePropertiesInternal(aLayer->GetMaskLayer(), true); |
161 | 0 | } |
162 | 0 | for (size_t i = 0; i < aLayer->GetAncestorMaskLayerCount(); i++) { |
163 | 0 | Layer* maskLayer = aLayer->GetAncestorMaskLayerAt(i); |
164 | 0 | mAncestorMaskLayers.AppendElement(CloneLayerTreePropertiesInternal(maskLayer, true)); |
165 | 0 | } |
166 | 0 | if (mUseClipRect) { |
167 | 0 | mClipRect = *aLayer->GetLocalClipRect(); |
168 | 0 | } |
169 | 0 | mTransform = GetTransformForInvalidation(aLayer); |
170 | 0 | } |
171 | | LayerPropertiesBase() |
172 | | : mLayer(nullptr) |
173 | | , mMaskLayer(nullptr) |
174 | | , mPostXScale(0.0) |
175 | | , mPostYScale(0.0) |
176 | | , mOpacity(0.0) |
177 | | , mUseClipRect(false) |
178 | 0 | { |
179 | 0 | MOZ_COUNT_CTOR(LayerPropertiesBase); |
180 | 0 | } |
181 | | ~LayerPropertiesBase() override |
182 | 0 | { |
183 | 0 | MOZ_COUNT_DTOR(LayerPropertiesBase); |
184 | 0 | } |
185 | | |
186 | | protected: |
187 | | LayerPropertiesBase(const LayerPropertiesBase& a) = delete; |
188 | | LayerPropertiesBase& operator=(const LayerPropertiesBase& a) = delete; |
189 | | |
190 | | public: |
191 | | bool ComputeDifferences(Layer* aRoot, |
192 | | nsIntRegion& aOutRegion, |
193 | | NotifySubDocInvalidationFunc aCallback) override; |
194 | | |
195 | | void MoveBy(const IntPoint& aOffset) override; |
196 | | |
197 | | bool ComputeChange(const char* aPrefix, |
198 | | nsIntRegion& aOutRegion, |
199 | | NotifySubDocInvalidationFunc aCallback) |
200 | 0 | { |
201 | 0 | // Bug 1251615: This canary is sometimes hit. We're still not sure why. |
202 | 0 | mCanary.Check(); |
203 | 0 | bool transformChanged = !mTransform.FuzzyEqual(GetTransformForInvalidation(mLayer)) || |
204 | 0 | mLayer->GetPostXScale() != mPostXScale || |
205 | 0 | mLayer->GetPostYScale() != mPostYScale; |
206 | 0 | const Maybe<ParentLayerIntRect>& otherClip = mLayer->GetLocalClipRect(); |
207 | 0 | nsIntRegion result; |
208 | 0 |
|
209 | 0 | bool ancestorMaskChanged = mAncestorMaskLayers.Length() != mLayer->GetAncestorMaskLayerCount(); |
210 | 0 | if (!ancestorMaskChanged) { |
211 | 0 | for (size_t i = 0; i < mAncestorMaskLayers.Length(); i++) { |
212 | 0 | if (mLayer->GetAncestorMaskLayerAt(i) != mAncestorMaskLayers[i]->mLayer) { |
213 | 0 | ancestorMaskChanged = true; |
214 | 0 | break; |
215 | 0 | } |
216 | 0 | } |
217 | 0 | } |
218 | 0 |
|
219 | 0 | // Note that we don't bailout early in general since this function |
220 | 0 | // clears some persistent state at the end. Instead we set an overflow |
221 | 0 | // flag and check it right before returning. |
222 | 0 | bool areaOverflowed = false; |
223 | 0 |
|
224 | 0 | Layer* otherMask = mLayer->GetMaskLayer(); |
225 | 0 | if ((mMaskLayer ? mMaskLayer->mLayer : nullptr) != otherMask || |
226 | 0 | ancestorMaskChanged || |
227 | 0 | (mUseClipRect != !!otherClip) || |
228 | 0 | mLayer->GetLocalOpacity() != mOpacity || |
229 | 0 | transformChanged) |
230 | 0 | { |
231 | 0 | Maybe<IntRect> oldBounds = OldTransformedBounds(); |
232 | 0 | Maybe<IntRect> newBounds = NewTransformedBounds(); |
233 | 0 | if (oldBounds && newBounds) { |
234 | 0 | LTI_DUMP(oldBounds.value(), "oldtransform"); |
235 | 0 | LTI_DUMP(newBounds.value(), "newtransform"); |
236 | 0 | result = oldBounds.value(); |
237 | 0 | AddRegion(result, newBounds.value()); |
238 | 0 | } else { |
239 | 0 | areaOverflowed = true; |
240 | 0 | } |
241 | 0 |
|
242 | 0 | // We can't bail out early because we might need to update some internal |
243 | 0 | // layer state. |
244 | 0 | } |
245 | 0 |
|
246 | 0 | nsIntRegion internal; |
247 | 0 | if (!ComputeChangeInternal(aPrefix, internal, aCallback)) { |
248 | 0 | areaOverflowed = true; |
249 | 0 | } |
250 | 0 |
|
251 | 0 | LTI_DUMP(internal, "internal"); |
252 | 0 | AddRegion(result, internal); |
253 | 0 | LTI_DUMP(mLayer->GetInvalidRegion().GetRegion(), "invalid"); |
254 | 0 | AddTransformedRegion(result, mLayer->GetInvalidRegion().GetRegion(), mTransform); |
255 | 0 |
|
256 | 0 | if (mMaskLayer && otherMask) { |
257 | 0 | nsIntRegion mask; |
258 | 0 | if (!mMaskLayer->ComputeChange(aPrefix, mask, aCallback)) { |
259 | 0 | areaOverflowed = true; |
260 | 0 | } |
261 | 0 | LTI_DUMP(mask, "mask"); |
262 | 0 | AddTransformedRegion(result, mask, mTransform); |
263 | 0 | } |
264 | 0 |
|
265 | 0 | for (size_t i = 0; |
266 | 0 | i < std::min(mAncestorMaskLayers.Length(), mLayer->GetAncestorMaskLayerCount()); |
267 | 0 | i++) |
268 | 0 | { |
269 | 0 | nsIntRegion mask; |
270 | 0 | if (!mAncestorMaskLayers[i]->ComputeChange(aPrefix, mask, aCallback)) { |
271 | 0 | areaOverflowed = true; |
272 | 0 | } |
273 | 0 | LTI_DUMP(mask, "ancestormask"); |
274 | 0 | AddTransformedRegion(result, mask, mTransform); |
275 | 0 | } |
276 | 0 |
|
277 | 0 | if (mUseClipRect && otherClip) { |
278 | 0 | if (!mClipRect.IsEqualInterior(*otherClip)) { |
279 | 0 | nsIntRegion tmp; |
280 | 0 | tmp.Xor(mClipRect.ToUnknownRect(), otherClip->ToUnknownRect()); |
281 | 0 | LTI_DUMP(tmp, "clip"); |
282 | 0 | AddRegion(result, tmp); |
283 | 0 | } |
284 | 0 | } |
285 | 0 |
|
286 | 0 | mLayer->ClearInvalidRegion(); |
287 | 0 |
|
288 | 0 | if (areaOverflowed) { |
289 | 0 | return false; |
290 | 0 | } |
291 | 0 | |
292 | 0 | aOutRegion = std::move(result); |
293 | 0 | return true; |
294 | 0 | } |
295 | | |
296 | | void CheckCanary() |
297 | 0 | { |
298 | 0 | mCanary.Check(); |
299 | 0 | mLayer->CheckCanary(); |
300 | 0 | } |
301 | | |
302 | 0 | IntRect NewTransformedBoundsForLeaf() { |
303 | 0 | return TransformRect(mLayer->GetLocalVisibleRegion().GetBounds().ToUnknownRect(), |
304 | 0 | GetTransformForInvalidation(mLayer)); |
305 | 0 | } |
306 | | |
307 | 0 | IntRect OldTransformedBoundsForLeaf() { |
308 | 0 | return TransformRect(mVisibleRegion.GetBounds().ToUnknownRect(), mTransform); |
309 | 0 | } |
310 | | |
311 | | virtual Maybe<IntRect> NewTransformedBounds() |
312 | 0 | { |
313 | 0 | return Some(TransformRect(mLayer->GetLocalVisibleRegion().GetBounds().ToUnknownRect(), |
314 | 0 | GetTransformForInvalidation(mLayer))); |
315 | 0 | } |
316 | | |
317 | | virtual Maybe<IntRect> OldTransformedBounds() |
318 | 0 | { |
319 | 0 | return Some(TransformRect(mVisibleRegion.GetBounds().ToUnknownRect(), mTransform)); |
320 | 0 | } |
321 | | |
322 | | virtual bool ComputeChangeInternal(const char* aPrefix, |
323 | | nsIntRegion& aOutRegion, |
324 | | NotifySubDocInvalidationFunc aCallback) |
325 | 0 | { |
326 | 0 | if (mLayer->AsHostLayer() && !mLayer->GetLocalVisibleRegion().ToUnknownRegion().IsEqual(mVisibleRegion)) { |
327 | 0 | IntRect result = NewTransformedBoundsForLeaf(); |
328 | 0 | result = result.Union(OldTransformedBoundsForLeaf()); |
329 | 0 | aOutRegion = result; |
330 | 0 | } |
331 | 0 | return true; |
332 | 0 | } |
333 | | |
334 | | RefPtr<Layer> mLayer; |
335 | | UniquePtr<LayerPropertiesBase> mMaskLayer; |
336 | | nsTArray<UniquePtr<LayerPropertiesBase>> mAncestorMaskLayers; |
337 | | nsIntRegion mVisibleRegion; |
338 | | Matrix4x4Flagged mTransform; |
339 | | float mPostXScale; |
340 | | float mPostYScale; |
341 | | float mOpacity; |
342 | | ParentLayerIntRect mClipRect; |
343 | | bool mUseClipRect; |
344 | | mozilla::CorruptionCanary mCanary; |
345 | | }; |
346 | | |
347 | | struct ContainerLayerProperties : public LayerPropertiesBase |
348 | | { |
349 | | explicit ContainerLayerProperties(ContainerLayer* aLayer) |
350 | | : LayerPropertiesBase(aLayer) |
351 | | , mPreXScale(aLayer->GetPreXScale()) |
352 | | , mPreYScale(aLayer->GetPreYScale()) |
353 | 0 | { |
354 | 0 | for (Layer* child = aLayer->GetFirstChild(); child; child = child->GetNextSibling()) { |
355 | 0 | child->CheckCanary(); |
356 | 0 | mChildren.AppendElement(CloneLayerTreePropertiesInternal(child)); |
357 | 0 | } |
358 | 0 | } |
359 | | |
360 | | protected: |
361 | | ContainerLayerProperties(const ContainerLayerProperties& a) = delete; |
362 | | ContainerLayerProperties& operator=(const ContainerLayerProperties& a) = delete; |
363 | | |
364 | | public: |
365 | | bool ComputeChangeInternal(const char *aPrefix, |
366 | | nsIntRegion& aOutRegion, |
367 | | NotifySubDocInvalidationFunc aCallback) override |
368 | 0 | { |
369 | 0 | // Make sure we got our virtual call right |
370 | 0 | mSubtypeCanary.Check(); |
371 | 0 | ContainerLayer* container = mLayer->AsContainerLayer(); |
372 | 0 | nsIntRegion invalidOfLayer; // Invalid regions of this layer. |
373 | 0 | nsIntRegion result; // Invliad regions for children only. |
374 | 0 |
|
375 | 0 | container->CheckCanary(); |
376 | 0 |
|
377 | 0 | bool childrenChanged = false; |
378 | 0 | bool invalidateWholeLayer = false; |
379 | 0 | bool areaOverflowed = false; |
380 | 0 | if (mPreXScale != container->GetPreXScale() || |
381 | 0 | mPreYScale != container->GetPreYScale()) |
382 | 0 | { |
383 | 0 | Maybe<IntRect> oldBounds = OldTransformedBounds(); |
384 | 0 | Maybe<IntRect> newBounds = NewTransformedBounds(); |
385 | 0 | if (oldBounds && newBounds) { |
386 | 0 | invalidOfLayer = oldBounds.value(); |
387 | 0 | AddRegion(invalidOfLayer, newBounds.value()); |
388 | 0 | } else { |
389 | 0 | areaOverflowed = true; |
390 | 0 | } |
391 | 0 | childrenChanged = true; |
392 | 0 | invalidateWholeLayer = true; |
393 | 0 |
|
394 | 0 | // Can't bail out early, we need to update the child container layers |
395 | 0 | } |
396 | 0 |
|
397 | 0 | // A low frame rate is especially visible to users when scrolling, so we |
398 | 0 | // particularly want to avoid unnecessary invalidation at that time. For us |
399 | 0 | // here, that means avoiding unnecessary invalidation of child items when |
400 | 0 | // other children are added to or removed from our container layer, since |
401 | 0 | // that may be caused by children being scrolled in or out of view. We are |
402 | 0 | // less concerned with children changing order. |
403 | 0 | // TODO: Consider how we could avoid unnecessary invalidation when children |
404 | 0 | // change order, and whether the overhead would be worth it. |
405 | 0 |
|
406 | 0 | nsDataHashtable<nsPtrHashKey<Layer>, uint32_t> oldIndexMap(mChildren.Length()); |
407 | 0 | for (uint32_t i = 0; i < mChildren.Length(); ++i) { |
408 | 0 | mChildren[i]->CheckCanary(); |
409 | 0 | oldIndexMap.Put(mChildren[i]->mLayer, i); |
410 | 0 | } |
411 | 0 |
|
412 | 0 | uint32_t i = 0; // cursor into the old child list mChildren |
413 | 0 | for (Layer* child = container->GetFirstChild(); child; child = child->GetNextSibling()) { |
414 | 0 | bool invalidateChildsCurrentArea = false; |
415 | 0 | if (i < mChildren.Length()) { |
416 | 0 | uint32_t childsOldIndex; |
417 | 0 | if (oldIndexMap.Get(child, &childsOldIndex)) { |
418 | 0 | if (childsOldIndex >= i) { |
419 | 0 | // Invalidate the old areas of layers that used to be between the |
420 | 0 | // current |child| and the previous |child| that was also in the |
421 | 0 | // old list mChildren (if any of those children have been reordered |
422 | 0 | // rather than removed, we will invalidate their new area when we |
423 | 0 | // encounter them in the new list): |
424 | 0 | for (uint32_t j = i; j < childsOldIndex; ++j) { |
425 | 0 | if (Maybe<IntRect> bounds = mChildren[j]->OldTransformedBounds()) { |
426 | 0 | LTI_DUMP(bounds.value(), "reordered child"); |
427 | 0 | AddRegion(result, bounds.value()); |
428 | 0 | } else { |
429 | 0 | areaOverflowed = true; |
430 | 0 | } |
431 | 0 | childrenChanged |= true; |
432 | 0 | } |
433 | 0 | if (childsOldIndex >= mChildren.Length()) { |
434 | 0 | MOZ_CRASH("Out of bounds"); |
435 | 0 | } |
436 | 0 | // Invalidate any regions of the child that have changed: |
437 | 0 | nsIntRegion region; |
438 | 0 | if (!mChildren[childsOldIndex]->ComputeChange(LTI_DEEPER(aPrefix), region, aCallback)) { |
439 | 0 | areaOverflowed = true; |
440 | 0 | } |
441 | 0 | i = childsOldIndex + 1; |
442 | 0 | if (!region.IsEmpty()) { |
443 | 0 | LTI_LOG("%s%p: child %p produced %s\n", aPrefix, mLayer.get(), |
444 | 0 | mChildren[childsOldIndex]->mLayer.get(), Stringify(region).c_str()); |
445 | 0 | AddRegion(result, region); |
446 | 0 | childrenChanged |= true; |
447 | 0 | } |
448 | 0 | } else { |
449 | 0 | // We've already seen this child in mChildren (which means it must |
450 | 0 | // have been reordered) and invalidated its old area. We need to |
451 | 0 | // invalidate its new area too: |
452 | 0 | invalidateChildsCurrentArea = true; |
453 | 0 | } |
454 | 0 | } else { |
455 | 0 | // |child| is new |
456 | 0 | invalidateChildsCurrentArea = true; |
457 | 0 | } |
458 | 0 | } else { |
459 | 0 | // |child| is new, or was reordered to a higher index |
460 | 0 | invalidateChildsCurrentArea = true; |
461 | 0 | } |
462 | 0 | if (invalidateChildsCurrentArea) { |
463 | 0 | LTI_DUMP(child->GetLocalVisibleRegion().ToUnknownRegion(), "invalidateChildsCurrentArea"); |
464 | 0 | AddTransformedRegion(result, child->GetLocalVisibleRegion().ToUnknownRegion(), |
465 | 0 | GetTransformForInvalidation(child)); |
466 | 0 | if (aCallback) { |
467 | 0 | NotifySubdocumentInvalidation(child, aCallback); |
468 | 0 | } else { |
469 | 0 | ClearInvalidations(child); |
470 | 0 | } |
471 | 0 | } |
472 | 0 | childrenChanged |= invalidateChildsCurrentArea; |
473 | 0 | } |
474 | 0 |
|
475 | 0 | // Process remaining removed children. |
476 | 0 | while (i < mChildren.Length()) { |
477 | 0 | childrenChanged |= true; |
478 | 0 | if (Maybe<IntRect> bounds = mChildren[i]->OldTransformedBounds()) { |
479 | 0 | LTI_DUMP(bounds.value(), "removed child"); |
480 | 0 | AddRegion(result, bounds.value()); |
481 | 0 | } else { |
482 | 0 | areaOverflowed = true; |
483 | 0 | } |
484 | 0 | i++; |
485 | 0 | } |
486 | 0 |
|
487 | 0 | if (aCallback) { |
488 | 0 | aCallback(container, areaOverflowed ? nullptr : &result); |
489 | 0 | } |
490 | 0 |
|
491 | 0 | if (childrenChanged || areaOverflowed) { |
492 | 0 | container->SetChildrenChanged(true); |
493 | 0 | } |
494 | 0 |
|
495 | 0 | if (container->UseIntermediateSurface()) { |
496 | 0 | Maybe<IntRect> bounds; |
497 | 0 | if (!invalidateWholeLayer && !areaOverflowed) { |
498 | 0 | bounds = Some(result.GetBounds()); |
499 | 0 |
|
500 | 0 | // Process changes in the visible region. |
501 | 0 | IntRegion newVisible = mLayer->GetLocalVisibleRegion().ToUnknownRegion(); |
502 | 0 | if (!newVisible.IsEqual(mVisibleRegion)) { |
503 | 0 | newVisible.XorWith(mVisibleRegion); |
504 | 0 | bounds = bounds->SafeUnion(newVisible.GetBounds()); |
505 | 0 | } |
506 | 0 | } |
507 | 0 | container->SetInvalidCompositeRect(bounds ? bounds.ptr() : nullptr); |
508 | 0 | } |
509 | 0 |
|
510 | 0 | // Safe to bail out early now, persistent state has been set. |
511 | 0 | if (areaOverflowed) { |
512 | 0 | return false; |
513 | 0 | } |
514 | 0 | |
515 | 0 | if (!mLayer->Extend3DContext()) { |
516 | 0 | // |result| contains invalid regions only of children. |
517 | 0 | result.Transform(GetTransformForInvalidation(mLayer).GetMatrix()); |
518 | 0 | } |
519 | 0 | // else, effective transforms have applied on children. |
520 | 0 |
|
521 | 0 | LTI_DUMP(invalidOfLayer, "invalidOfLayer"); |
522 | 0 | result.OrWith(invalidOfLayer); |
523 | 0 |
|
524 | 0 | aOutRegion = std::move(result); |
525 | 0 | return true; |
526 | 0 | } |
527 | | |
528 | | Maybe<IntRect> NewTransformedBounds() override |
529 | 0 | { |
530 | 0 | if (mLayer->Extend3DContext()) { |
531 | 0 | IntRect result; |
532 | 0 | for (UniquePtr<LayerPropertiesBase>& child : mChildren) { |
533 | 0 | Maybe<IntRect> childBounds = child->NewTransformedBounds(); |
534 | 0 | if (!childBounds) { |
535 | 0 | return Nothing(); |
536 | 0 | } |
537 | 0 | Maybe<IntRect> combined = result.SafeUnion(childBounds.value()); |
538 | 0 | if (!combined) { |
539 | 0 | LTI_LOG("overflowed bounds of container %p accumulating child %p\n", this, child->mLayer.get()); |
540 | 0 | return Nothing(); |
541 | 0 | } |
542 | 0 | result = combined.value(); |
543 | 0 | } |
544 | 0 | return Some(result); |
545 | 0 | } |
546 | 0 | |
547 | 0 | return LayerPropertiesBase::NewTransformedBounds(); |
548 | 0 | } |
549 | | |
550 | | Maybe<IntRect> OldTransformedBounds() override |
551 | 0 | { |
552 | 0 | if (mLayer->Extend3DContext()) { |
553 | 0 | IntRect result; |
554 | 0 | for (UniquePtr<LayerPropertiesBase>& child : mChildren) { |
555 | 0 | Maybe<IntRect> childBounds = child->OldTransformedBounds(); |
556 | 0 | if (!childBounds) { |
557 | 0 | return Nothing(); |
558 | 0 | } |
559 | 0 | Maybe<IntRect> combined = result.SafeUnion(childBounds.value()); |
560 | 0 | if (!combined) { |
561 | 0 | LTI_LOG("overflowed bounds of container %p accumulating child %p\n", this, child->mLayer.get()); |
562 | 0 | return Nothing(); |
563 | 0 | } |
564 | 0 | result = combined.value(); |
565 | 0 | } |
566 | 0 | return Some(result); |
567 | 0 | } |
568 | 0 | return LayerPropertiesBase::OldTransformedBounds(); |
569 | 0 | } |
570 | | |
571 | | // The old list of children: |
572 | | mozilla::CorruptionCanary mSubtypeCanary; |
573 | | nsTArray<UniquePtr<LayerPropertiesBase>> mChildren; |
574 | | float mPreXScale; |
575 | | float mPreYScale; |
576 | | }; |
577 | | |
578 | | struct ColorLayerProperties : public LayerPropertiesBase |
579 | | { |
580 | | explicit ColorLayerProperties(ColorLayer *aLayer) |
581 | | : LayerPropertiesBase(aLayer) |
582 | | , mColor(aLayer->GetColor()) |
583 | | , mBounds(aLayer->GetBounds()) |
584 | 0 | { } |
585 | | |
586 | | protected: |
587 | | ColorLayerProperties(const ColorLayerProperties& a) = delete; |
588 | | ColorLayerProperties& operator=(const ColorLayerProperties& a) = delete; |
589 | | |
590 | | public: |
591 | | bool ComputeChangeInternal(const char* aPrefix, |
592 | | nsIntRegion& aOutRegion, |
593 | | NotifySubDocInvalidationFunc aCallback) override |
594 | 0 | { |
595 | 0 | ColorLayer* color = static_cast<ColorLayer*>(mLayer.get()); |
596 | 0 |
|
597 | 0 | if (mColor != color->GetColor()) { |
598 | 0 | LTI_DUMP(NewTransformedBoundsForLeaf(), "color"); |
599 | 0 | aOutRegion = NewTransformedBoundsForLeaf(); |
600 | 0 | return true; |
601 | 0 | } |
602 | 0 | |
603 | 0 | nsIntRegion boundsDiff; |
604 | 0 | boundsDiff.Xor(mBounds, color->GetBounds()); |
605 | 0 | LTI_DUMP(boundsDiff, "colorbounds"); |
606 | 0 |
|
607 | 0 | AddTransformedRegion(aOutRegion, boundsDiff, mTransform); |
608 | 0 | return true; |
609 | 0 | } |
610 | | |
611 | | Color mColor; |
612 | | IntRect mBounds; |
613 | | }; |
614 | | |
615 | | static ImageHost* GetImageHost(Layer* aLayer) |
616 | 0 | { |
617 | 0 | HostLayer* compositor = aLayer->AsHostLayer(); |
618 | 0 | if (compositor) { |
619 | 0 | return static_cast<ImageHost*>(compositor->GetCompositableHost()); |
620 | 0 | } |
621 | 0 | return nullptr; |
622 | 0 | } |
623 | | |
624 | | struct ImageLayerProperties : public LayerPropertiesBase |
625 | | { |
626 | | explicit ImageLayerProperties(ImageLayer* aImage, bool aIsMask) |
627 | | : LayerPropertiesBase(aImage) |
628 | | , mContainer(aImage->GetContainer()) |
629 | | , mImageHost(GetImageHost(aImage)) |
630 | | , mSamplingFilter(aImage->GetSamplingFilter()) |
631 | | , mScaleToSize(aImage->GetScaleToSize()) |
632 | | , mScaleMode(aImage->GetScaleMode()) |
633 | | , mLastProducerID(-1) |
634 | | , mLastFrameID(-1) |
635 | | , mIsMask(aIsMask) |
636 | 0 | { |
637 | 0 | if (mImageHost) { |
638 | 0 | mLastProducerID = mImageHost->GetLastProducerID(); |
639 | 0 | mLastFrameID = mImageHost->GetLastFrameID(); |
640 | 0 | } |
641 | 0 | } |
642 | | |
643 | | bool ComputeChangeInternal(const char* aPrefix, |
644 | | nsIntRegion& aOutRegion, |
645 | | NotifySubDocInvalidationFunc aCallback) override |
646 | 0 | { |
647 | 0 | ImageLayer* imageLayer = static_cast<ImageLayer*>(mLayer.get()); |
648 | 0 |
|
649 | 0 | if (!imageLayer->GetLocalVisibleRegion().ToUnknownRegion().IsEqual(mVisibleRegion)) { |
650 | 0 | IntRect result = NewTransformedBoundsForLeaf(); |
651 | 0 | result = result.Union(OldTransformedBoundsForLeaf()); |
652 | 0 | aOutRegion = result; |
653 | 0 | return true; |
654 | 0 | } |
655 | 0 | |
656 | 0 | ImageContainer* container = imageLayer->GetContainer(); |
657 | 0 | ImageHost* host = GetImageHost(imageLayer); |
658 | 0 | if (mContainer != container || |
659 | 0 | mSamplingFilter != imageLayer->GetSamplingFilter() || |
660 | 0 | mScaleToSize != imageLayer->GetScaleToSize() || |
661 | 0 | mScaleMode != imageLayer->GetScaleMode() || |
662 | 0 | host != mImageHost || |
663 | 0 | (host && host->GetProducerID() != mLastProducerID) || |
664 | 0 | (host && host->GetFrameID() != mLastFrameID)) { |
665 | 0 |
|
666 | 0 | if (mIsMask) { |
667 | 0 | // Mask layers have an empty visible region, so we have to |
668 | 0 | // use the image size instead. |
669 | 0 | IntSize size; |
670 | 0 | if (container) { |
671 | 0 | size = container->GetCurrentSize(); |
672 | 0 | } |
673 | 0 | if (host) { |
674 | 0 | size = host->GetImageSize(); |
675 | 0 | } |
676 | 0 | IntRect rect(0, 0, size.width, size.height); |
677 | 0 | LTI_DUMP(rect, "mask"); |
678 | 0 | aOutRegion = TransformRect(rect, GetTransformForInvalidation(mLayer)); |
679 | 0 | return true; |
680 | 0 | } |
681 | 0 | LTI_DUMP(NewTransformedBoundsForLeaf(), "bounds"); |
682 | 0 | aOutRegion = NewTransformedBoundsForLeaf(); |
683 | 0 | return true; |
684 | 0 | } |
685 | 0 | |
686 | 0 | return true; |
687 | 0 | } |
688 | | |
689 | | RefPtr<ImageContainer> mContainer; |
690 | | RefPtr<ImageHost> mImageHost; |
691 | | SamplingFilter mSamplingFilter; |
692 | | gfx::IntSize mScaleToSize; |
693 | | ScaleMode mScaleMode; |
694 | | int32_t mLastProducerID; |
695 | | int32_t mLastFrameID; |
696 | | bool mIsMask; |
697 | | }; |
698 | | |
699 | | struct CanvasLayerProperties : public LayerPropertiesBase |
700 | | { |
701 | | explicit CanvasLayerProperties(CanvasLayer* aCanvas) |
702 | | : LayerPropertiesBase(aCanvas) |
703 | | , mImageHost(GetImageHost(aCanvas)) |
704 | 0 | { |
705 | 0 | mFrameID = mImageHost ? mImageHost->GetFrameID() : -1; |
706 | 0 | } |
707 | | |
708 | | bool ComputeChangeInternal(const char* aPrefix, |
709 | | nsIntRegion& aOutRegion, |
710 | | NotifySubDocInvalidationFunc aCallback) override |
711 | 0 | { |
712 | 0 | CanvasLayer* canvasLayer = static_cast<CanvasLayer*>(mLayer.get()); |
713 | 0 |
|
714 | 0 | ImageHost* host = GetImageHost(canvasLayer); |
715 | 0 | if (host && host->GetFrameID() != mFrameID) { |
716 | 0 | LTI_DUMP(NewTransformedBoundsForLeaf(), "frameId"); |
717 | 0 | aOutRegion = NewTransformedBoundsForLeaf(); |
718 | 0 | return true; |
719 | 0 | } |
720 | 0 | |
721 | 0 | return true; |
722 | 0 | } |
723 | | |
724 | | RefPtr<ImageHost> mImageHost; |
725 | | int32_t mFrameID; |
726 | | }; |
727 | | |
728 | | UniquePtr<LayerPropertiesBase> |
729 | | CloneLayerTreePropertiesInternal(Layer* aRoot, bool aIsMask /* = false */) |
730 | 0 | { |
731 | 0 | if (!aRoot) { |
732 | 0 | return MakeUnique<LayerPropertiesBase>(); |
733 | 0 | } |
734 | 0 | |
735 | 0 | MOZ_ASSERT(!aIsMask || aRoot->GetType() == Layer::TYPE_IMAGE); |
736 | 0 |
|
737 | 0 | aRoot->CheckCanary(); |
738 | 0 |
|
739 | 0 | switch (aRoot->GetType()) { |
740 | 0 | case Layer::TYPE_CONTAINER: |
741 | 0 | case Layer::TYPE_REF: |
742 | 0 | return MakeUnique<ContainerLayerProperties>(aRoot->AsContainerLayer()); |
743 | 0 | case Layer::TYPE_COLOR: |
744 | 0 | return MakeUnique<ColorLayerProperties>(static_cast<ColorLayer*>(aRoot)); |
745 | 0 | case Layer::TYPE_IMAGE: |
746 | 0 | return MakeUnique<ImageLayerProperties>(static_cast<ImageLayer*>(aRoot), aIsMask); |
747 | 0 | case Layer::TYPE_CANVAS: |
748 | 0 | return MakeUnique<CanvasLayerProperties>(static_cast<CanvasLayer*>(aRoot)); |
749 | 0 | case Layer::TYPE_DISPLAYITEM: |
750 | 0 | case Layer::TYPE_READBACK: |
751 | 0 | case Layer::TYPE_SHADOW: |
752 | 0 | case Layer::TYPE_PAINTED: |
753 | 0 | return MakeUnique<LayerPropertiesBase>(aRoot); |
754 | 0 | } |
755 | 0 | |
756 | 0 | MOZ_ASSERT_UNREACHABLE("Unexpected root layer type"); |
757 | 0 | return MakeUnique<LayerPropertiesBase>(aRoot); |
758 | 0 | } |
759 | | |
760 | | /* static */ UniquePtr<LayerProperties> |
761 | | LayerProperties::CloneFrom(Layer* aRoot) |
762 | 0 | { |
763 | 0 | return CloneLayerTreePropertiesInternal(aRoot); |
764 | 0 | } |
765 | | |
766 | | /* static */ void |
767 | | LayerProperties::ClearInvalidations(Layer *aLayer) |
768 | 0 | { |
769 | 0 | ForEachNode<ForwardIterator>( |
770 | 0 | aLayer, |
771 | 0 | [] (Layer* layer) |
772 | 0 | { |
773 | 0 | layer->ClearInvalidRegion(); |
774 | 0 | if (layer->GetMaskLayer()) { |
775 | 0 | ClearInvalidations(layer->GetMaskLayer()); |
776 | 0 | } |
777 | 0 | for (size_t i = 0; i < layer->GetAncestorMaskLayerCount(); i++) { |
778 | 0 | ClearInvalidations(layer->GetAncestorMaskLayerAt(i)); |
779 | 0 | } |
780 | 0 |
|
781 | 0 | } |
782 | 0 | ); |
783 | 0 | } |
784 | | |
785 | | bool |
786 | | LayerPropertiesBase::ComputeDifferences(Layer* aRoot, nsIntRegion& aOutRegion, NotifySubDocInvalidationFunc aCallback) |
787 | 0 | { |
788 | 0 | NS_ASSERTION(aRoot, "Must have a layer tree to compare against!"); |
789 | 0 | if (mLayer != aRoot) { |
790 | 0 | if (aCallback) { |
791 | 0 | NotifySubdocumentInvalidation(aRoot, aCallback); |
792 | 0 | } else { |
793 | 0 | ClearInvalidations(aRoot); |
794 | 0 | } |
795 | 0 | IntRect bounds = TransformRect( |
796 | 0 | aRoot->GetLocalVisibleRegion().GetBounds().ToUnknownRect(), |
797 | 0 | aRoot->GetLocalTransform()); |
798 | 0 | Maybe<IntRect> oldBounds = OldTransformedBounds(); |
799 | 0 | if (!oldBounds) { |
800 | 0 | return false; |
801 | 0 | } |
802 | 0 | Maybe<IntRect> result = bounds.SafeUnion(oldBounds.value()); |
803 | 0 | if (!result) { |
804 | 0 | LTI_LOG("overflowed bounds computing the union of layers %p and %p\n", mLayer.get(), aRoot); |
805 | 0 | return false; |
806 | 0 | } |
807 | 0 | aOutRegion = result.value(); |
808 | 0 | return true; |
809 | 0 | } |
810 | 0 | return ComputeChange(" ", aOutRegion, aCallback); |
811 | 0 | } |
812 | | |
813 | | void |
814 | | LayerPropertiesBase::MoveBy(const IntPoint& aOffset) |
815 | 0 | { |
816 | 0 | mTransform.PostTranslate(aOffset.x, aOffset.y, 0); |
817 | 0 | } |
818 | | |
819 | | } // namespace layers |
820 | | } // namespace mozilla |