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