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