Source code

Revision control

Other Tools

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) \
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
47
48
using namespace mozilla::gfx;
49
50
namespace mozilla {
51
namespace layers {
52
53
struct LayerPropertiesBase;
54
UniquePtr<LayerPropertiesBase> CloneLayerTreePropertiesInternal(
55
Layer* aRoot, bool aIsMask = false);
56
57
/**
58
* Get accumulated transform of from the context creating layer to the
59
* given layer.
60
*/
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();
66
}
67
return transform;
68
}
69
70
/**
71
* Get a transform for the given layer depending on extending 3D
72
* context.
73
*
74
* @return local transform for layers not participating 3D rendering
75
* context, or the accmulated transform in the context for else.
76
*/
77
static Matrix4x4Flagged GetTransformForInvalidation(Layer* aLayer) {
78
return (!aLayer->Is3DContextLeaf() && !aLayer->Extend3DContext()
79
? aLayer->GetLocalTransform()
80
: GetTransformIn3DContext(aLayer));
81
}
82
83
static IntRect TransformRect(const IntRect& aRect,
84
const Matrix4x4Flagged& aTransform) {
85
if (aRect.IsEmpty()) {
86
return IntRect();
87
}
88
89
Rect rect(aRect.X(), aRect.Y(), aRect.Width(), aRect.Height());
90
rect = aTransform.TransformAndClipBounds(rect, Rect::MaxIntRect());
91
rect.RoundOut();
92
93
IntRect intRect;
94
if (!rect.ToIntRect(&intRect)) {
95
intRect = IntRect::MaxIntRect();
96
}
97
98
return intRect;
99
}
100
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));
105
}
106
aDest.SimplifyOutward(20);
107
}
108
109
static void AddRegion(nsIntRegion& aDest, const nsIntRegion& aSource) {
110
aDest.Or(aDest, aSource);
111
aDest.SimplifyOutward(20);
112
}
113
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();
124
}
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();
130
}
131
result = combined.value();
132
}
133
return Some(result);
134
}
135
136
return Some(
137
TransformRect(aLayer->GetLocalVisibleRegion().GetBounds().ToUnknownRect(),
138
GetTransformForInvalidation(aLayer)));
139
}
140
141
/**
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.
145
*/
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);
154
}
155
for (size_t i = 0; i < layer->GetAncestorMaskLayerCount(); i++) {
156
Layer* maskLayer = layer->GetAncestorMaskLayerAt(i);
157
NotifySubdocumentInvalidation(maskLayer, aCallback);
158
}
159
},
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);
166
}
167
});
168
}
169
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);
176
}
177
});
178
}
179
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);
195
}
196
for (size_t i = 0; i < aLayer->GetAncestorMaskLayerCount(); i++) {
197
Layer* maskLayer = aLayer->GetAncestorMaskLayerAt(i);
198
mAncestorMaskLayers.AppendElement(
199
CloneLayerTreePropertiesInternal(maskLayer, true));
200
}
201
if (mUseClipRect) {
202
mClipRect = *aLayer->GetLocalClipRect();
203
}
204
mTransform = GetTransformForInvalidation(aLayer);
205
}
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);
214
}
215
MOZ_COUNTED_DTOR_OVERRIDE(LayerPropertiesBase)
216
217
protected:
218
LayerPropertiesBase(const LayerPropertiesBase& a) = delete;
219
LayerPropertiesBase& operator=(const LayerPropertiesBase& a) = delete;
220
221
public:
222
bool ComputeDifferences(Layer* aRoot, nsIntRegion& aOutRegion,
223
NotifySubDocInvalidationFunc aCallback) override;
224
225
void MoveBy(const IntPoint& aOffset) override;
226
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;
237
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;
246
}
247
}
248
}
249
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;
254
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;
268
}
269
270
// We can't bail out early because we might need to update some internal
271
// layer state.
272
}
273
274
nsIntRegion internal;
275
if (!ComputeChangeInternal(aPrefix, internal, aCallback)) {
276
areaOverflowed = true;
277
}
278
279
LTI_DUMP(internal, "internal");
280
AddRegion(result, internal);
281
LTI_DUMP(mLayer->GetInvalidRegion().GetRegion(), "invalid");
282
AddTransformedRegion(result, mLayer->GetInvalidRegion().GetRegion(),
283
mTransform);
284
285
if (mMaskLayer && otherMask) {
286
nsIntRegion mask;
287
if (!mMaskLayer->ComputeChange(aPrefix, mask, aCallback)) {
288
areaOverflowed = true;
289
}
290
LTI_DUMP(mask, "mask");
291
AddRegion(result, mask);
292
}
293
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;
300
}
301
LTI_DUMP(mask, "ancestormask");
302
AddRegion(result, mask);
303
}
304
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);
311
}
312
}
313
314
mLayer->ClearInvalidRegion();
315
316
if (areaOverflowed) {
317
return false;
318
}
319
320
aOutRegion = std::move(result);
321
return true;
322
}
323
324
void CheckCanary() {
325
mCanary.Check();
326
mLayer->CheckCanary();
327
}
328
329
IntRect NewTransformedBoundsForLeaf() {
330
return TransformRect(
331
mLayer->GetLocalVisibleRegion().GetBounds().ToUnknownRect(),
332
GetTransformForInvalidation(mLayer));
333
}
334
335
IntRect OldTransformedBoundsForLeaf() {
336
return TransformRect(mVisibleRegion.GetBounds().ToUnknownRect(),
337
mTransform);
338
}
339
340
Maybe<IntRect> NewTransformedBounds() { return TransformedBounds(mLayer); }
341
342
virtual Maybe<IntRect> OldTransformedBounds() {
343
return Some(
344
TransformRect(mVisibleRegion.GetBounds().ToUnknownRect(), mTransform));
345
}
346
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;
356
}
357
return true;
358
}
359
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;
371
};
372
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));
382
}
383
}
384
385
protected:
386
ContainerLayerProperties(const ContainerLayerProperties& a) = delete;
387
ContainerLayerProperties& operator=(const ContainerLayerProperties& a) =
388
delete;
389
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.
398
399
container->CheckCanary();
400
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;
413
}
414
childrenChanged = true;
415
invalidateWholeLayer = true;
416
417
// Can't bail out early, we need to update the child container layers
418
}
419
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.
428
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);
434
}
435
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;
456
}
457
childrenChanged |= true;
458
}
459
if (childsOldIndex >= mChildren.Length()) {
460
MOZ_CRASH("Out of bounds");
461
}
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;
467
}
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;
475
}
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;
481
}
482
} else {
483
// |child| is new
484
invalidateChildsCurrentArea = true;
485
SetChildrenChangedRecursive(child);
486
}
487
} else {
488
// |child| is new, or was reordered to a higher index
489
invalidateChildsCurrentArea = true;
490
if (!oldIndexMap.Contains(child)) {
491
SetChildrenChangedRecursive(child);
492
}
493
}
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;
501
}
502
if (aCallback) {
503
NotifySubdocumentInvalidation(child, aCallback);
504
} else {
505
ClearInvalidations(child);
506
}
507
}
508
childrenChanged |= invalidateChildsCurrentArea;
509
}
510
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;
519
}
520
i++;
521
}
522
523
if (aCallback) {
524
aCallback(container, areaOverflowed ? nullptr : &result);
525
}
526
527
if (childrenChanged || areaOverflowed) {
528
container->SetChildrenChanged(true);
529
}
530
531
if (container->UseIntermediateSurface()) {
532
Maybe<IntRect> bounds;
533
if (!invalidateWholeLayer && !areaOverflowed) {
534
bounds = Some(result.GetBounds());
535
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());
542
}
543
}
544
container->SetInvalidCompositeRect(bounds ? bounds.ptr() : nullptr);
545
}
546
547
// Safe to bail out early now, persistent state has been set.
548
if (areaOverflowed) {
549
return false;
550
}
551
552
if (!mLayer->Extend3DContext()) {
553
// |result| contains invalid regions only of children.
554
result.Transform(GetTransformForInvalidation(mLayer).GetMatrix());
555
}
556
// else, effective transforms have applied on children.
557
558
LTI_DUMP(invalidOfLayer, "invalidOfLayer");
559
result.OrWith(invalidOfLayer);
560
561
aOutRegion = std::move(result);
562
return true;
563
}
564
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();
572
}
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();
578
}
579
result = combined.value();
580
}
581
return Some(result);
582
}
583
return LayerPropertiesBase::OldTransformedBounds();
584
}
585
586
// The old list of children:
587
mozilla::CorruptionCanary mSubtypeCanary;
588
nsTArray<UniquePtr<LayerPropertiesBase>> mChildren;
589
float mPreXScale;
590
float mPreYScale;
591
};
592
593
struct ColorLayerProperties : public LayerPropertiesBase {
594
explicit ColorLayerProperties(ColorLayer* aLayer)
595
: LayerPropertiesBase(aLayer),
596
mColor(aLayer->GetColor()),
597
mBounds(aLayer->GetBounds()) {}
598
599
protected:
600
ColorLayerProperties(const ColorLayerProperties& a) = delete;
601
ColorLayerProperties& operator=(const ColorLayerProperties& a) = delete;
602
603
public:
604
bool ComputeChangeInternal(const char* aPrefix, nsIntRegion& aOutRegion,
605
NotifySubDocInvalidationFunc aCallback) override {
606
ColorLayer* color = static_cast<ColorLayer*>(mLayer.get());
607
608
if (mColor != color->GetColor()) {
609
LTI_DUMP(NewTransformedBoundsForLeaf(), "color");
610
aOutRegion = NewTransformedBoundsForLeaf();
611
return true;
612
}
613
614
nsIntRegion boundsDiff;
615
boundsDiff.Xor(mBounds, color->GetBounds());
616
LTI_DUMP(boundsDiff, "colorbounds");
617
618
AddTransformedRegion(aOutRegion, boundsDiff, mTransform);
619
return true;
620
}
621
622
DeviceColor mColor;
623
IntRect mBounds;
624
};
625
626
static ImageHost* GetImageHost(Layer* aLayer) {
627
HostLayer* compositor = aLayer->AsHostLayer();
628
if (compositor) {
629
return static_cast<ImageHost*>(compositor->GetCompositableHost());
630
}
631
return nullptr;
632
}
633
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();
655
}
656
}
657
}
658
659
bool ComputeChangeInternal(const char* aPrefix, nsIntRegion& aOutRegion,
660
NotifySubDocInvalidationFunc aCallback) override {
661
ImageLayer* imageLayer = static_cast<ImageLayer*>(mLayer.get());
662
663
if (!imageLayer->GetLocalVisibleRegion().ToUnknownRegion().IsEqual(
664
mVisibleRegion)) {
665
IntRect result = NewTransformedBoundsForLeaf();
666
result = result.Union(OldTransformedBoundsForLeaf());
667
aOutRegion = result;
668
return true;
669
}
670
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();
685
}
686
if (host) {
687
size = host->GetImageSize();
688
}
689
IntRect rect(0, 0, size.width, size.height);
690
LTI_DUMP(rect, "mask");
691
aOutRegion = TransformRect(rect, GetTransformForInvalidation(mLayer));
692
return true;
693
}
694
LTI_DUMP(NewTransformedBoundsForLeaf(), "bounds");
695
aOutRegion = NewTransformedBoundsForLeaf();
696
return true;
697
}
698
699
return true;
700
}
701
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;
710
};
711
712
struct CanvasLayerProperties : public LayerPropertiesBase {
713
explicit CanvasLayerProperties(CanvasLayer* aCanvas)
714
: LayerPropertiesBase(aCanvas), mImageHost(GetImageHost(aCanvas)) {
715
mFrameID = mImageHost ? mImageHost->GetFrameID() : -1;
716
}
717
718
bool ComputeChangeInternal(const char* aPrefix, nsIntRegion& aOutRegion,
719
NotifySubDocInvalidationFunc aCallback) override {
720
CanvasLayer* canvasLayer = static_cast<CanvasLayer*>(mLayer.get());
721
722
ImageHost* host = GetImageHost(canvasLayer);
723
if (host && host->GetFrameID() != mFrameID) {
724
LTI_DUMP(NewTransformedBoundsForLeaf(), "frameId");
725
aOutRegion = NewTransformedBoundsForLeaf();
726
return true;
727
}
728
729
return true;
730
}
731
732
RefPtr<ImageHost> mImageHost;
733
int32_t mFrameID;
734
};
735
736
UniquePtr<LayerPropertiesBase> CloneLayerTreePropertiesInternal(
737
Layer* aRoot, bool aIsMask /* = false */) {
738
if (!aRoot) {
739
return MakeUnique<LayerPropertiesBase>();
740
}
741
742
MOZ_ASSERT(!aIsMask || aRoot->GetType() == Layer::TYPE_IMAGE);
743
744
aRoot->CheckCanary();
745
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);
763
}
764
765
MOZ_ASSERT_UNREACHABLE("Unexpected root layer type");
766
return MakeUnique<LayerPropertiesBase>(aRoot);
767
}
768
769
/* static */
770
UniquePtr<LayerProperties> LayerProperties::CloneFrom(Layer* aRoot) {
771
return CloneLayerTreePropertiesInternal(aRoot);
772
}
773
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());
780
}
781
for (size_t i = 0; i < layer->GetAncestorMaskLayerCount(); i++) {
782
ClearInvalidations(layer->GetAncestorMaskLayerAt(i));
783
}
784
});
785
}
786
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);
796
}
797
IntRect bounds = TransformRect(
798
aRoot->GetLocalVisibleRegion().GetBounds().ToUnknownRect(),
799
aRoot->GetLocalTransform());
800
Maybe<IntRect> oldBounds = OldTransformedBounds();
801
if (!oldBounds) {
802
return false;
803
}
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;
809
}
810
aOutRegion = result.value();
811
return true;
812
}
813
return ComputeChange(" ", aOutRegion, aCallback);
814
}
815
816
void LayerPropertiesBase::MoveBy(const IntPoint& aOffset) {
817
mTransform.PostTranslate(aOffset.x, aOffset.y, 0);
818
}
819
820
} // namespace layers
821
} // namespace mozilla