mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-19 16:25:38 +00:00
91b071ed14
gfx::Color is currently misused in many places. The DrawTargets expect the color space to be in device space, e.g. what we are actually going to draw using. Everything sitting above generally deals with sRGB, as specified in CSS. Sometimes we missed the conversion from sRGB to device space when issuing draw calls, and similarly sometimes we converted the color to device space twice. This patch splits the type in two. sRGBColor and DeviceColor now represent sRGB and device color spaces respectively. DrawTarget only accepts DeviceColor, and one can get a DeviceColor from an sRGBColor via the ToDeviceColor helper API. The reftests now pass with color management enabled for everything (e.g. CSS) instead of just tagged raster images. There will be a follow up patch to enable color management everywhere by default on all supported platforms. Differential Revision: https://phabricator.services.mozilla.com/D64771 --HG-- extra : moz-landing-system : lando
822 lines
28 KiB
C++
822 lines
28 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "LayerTreeInvalidation.h"
|
|
|
|
#include <stdint.h> // for uint32_t
|
|
#include "ImageContainer.h" // for ImageContainer
|
|
#include "ImageLayers.h" // for ImageLayer, etc
|
|
#include "Layers.h" // for Layer, ContainerLayer, etc
|
|
#include "Units.h" // for ParentLayerIntRect
|
|
#include "gfxRect.h" // for gfxRect
|
|
#include "gfxUtils.h" // for gfxUtils
|
|
#include "mozilla/ArrayUtils.h" // for ArrayEqual
|
|
#include "mozilla/gfx/BaseSize.h" // for BaseSize
|
|
#include "mozilla/gfx/Point.h" // for IntSize
|
|
#include "mozilla/mozalloc.h" // for operator new, etc
|
|
#include "nsDataHashtable.h" // for nsDataHashtable
|
|
#include "nsDebug.h" // for NS_ASSERTION
|
|
#include "nsHashKeys.h" // for nsPtrHashKey
|
|
#include "nsISupportsImpl.h" // for Layer::AddRef, etc
|
|
#include "nsRect.h" // for IntRect
|
|
#include "nsTArray.h" // for AutoTArray, nsTArray_Impl
|
|
#include "mozilla/Poison.h"
|
|
#include "mozilla/layers/ImageHost.h"
|
|
#include "mozilla/layers/LayerManagerComposite.h"
|
|
#include "TreeTraversal.h" // for ForEachNode
|
|
#include "LayersLogging.h"
|
|
|
|
// LayerTreeInvalidation debugging
|
|
#define LTI_DEBUG 0
|
|
|
|
#if LTI_DEBUG
|
|
# define LTI_DEEPER(aPrefix) nsPrintfCString("%s ", aPrefix).get()
|
|
# define LTI_DUMP(rgn, label) \
|
|
if (!(rgn).IsEmpty()) \
|
|
printf_stderr("%s%p: " label " portion is %s\n", aPrefix, mLayer.get(), \
|
|
Stringify(rgn).c_str());
|
|
# define LTI_LOG(...) printf_stderr(__VA_ARGS__)
|
|
#else
|
|
# define LTI_DEEPER(aPrefix) nullptr
|
|
# define LTI_DUMP(rgn, label)
|
|
# define LTI_LOG(...)
|
|
#endif
|
|
|
|
using namespace mozilla::gfx;
|
|
|
|
namespace mozilla {
|
|
namespace layers {
|
|
|
|
struct LayerPropertiesBase;
|
|
UniquePtr<LayerPropertiesBase> CloneLayerTreePropertiesInternal(
|
|
Layer* aRoot, bool aIsMask = false);
|
|
|
|
/**
|
|
* Get accumulated transform of from the context creating layer to the
|
|
* given layer.
|
|
*/
|
|
static Matrix4x4 GetTransformIn3DContext(Layer* aLayer) {
|
|
Matrix4x4 transform = aLayer->GetLocalTransform();
|
|
for (Layer* layer = aLayer->GetParent(); layer && layer->Extend3DContext();
|
|
layer = layer->GetParent()) {
|
|
transform = transform * layer->GetLocalTransform();
|
|
}
|
|
return transform;
|
|
}
|
|
|
|
/**
|
|
* Get a transform for the given layer depending on extending 3D
|
|
* context.
|
|
*
|
|
* @return local transform for layers not participating 3D rendering
|
|
* context, or the accmulated transform in the context for else.
|
|
*/
|
|
static Matrix4x4Flagged GetTransformForInvalidation(Layer* aLayer) {
|
|
return (!aLayer->Is3DContextLeaf() && !aLayer->Extend3DContext()
|
|
? aLayer->GetLocalTransform()
|
|
: GetTransformIn3DContext(aLayer));
|
|
}
|
|
|
|
static IntRect TransformRect(const IntRect& aRect,
|
|
const Matrix4x4Flagged& aTransform) {
|
|
if (aRect.IsEmpty()) {
|
|
return IntRect();
|
|
}
|
|
|
|
Rect rect(aRect.X(), aRect.Y(), aRect.Width(), aRect.Height());
|
|
rect = aTransform.TransformAndClipBounds(rect, Rect::MaxIntRect());
|
|
rect.RoundOut();
|
|
|
|
IntRect intRect;
|
|
if (!rect.ToIntRect(&intRect)) {
|
|
intRect = IntRect::MaxIntRect();
|
|
}
|
|
|
|
return intRect;
|
|
}
|
|
|
|
static void AddTransformedRegion(nsIntRegion& aDest, const nsIntRegion& aSource,
|
|
const Matrix4x4Flagged& aTransform) {
|
|
for (auto iter = aSource.RectIter(); !iter.Done(); iter.Next()) {
|
|
aDest.Or(aDest, TransformRect(iter.Get(), aTransform));
|
|
}
|
|
aDest.SimplifyOutward(20);
|
|
}
|
|
|
|
static void AddRegion(nsIntRegion& aDest, const nsIntRegion& aSource) {
|
|
aDest.Or(aDest, aSource);
|
|
aDest.SimplifyOutward(20);
|
|
}
|
|
|
|
Maybe<IntRect> TransformedBounds(Layer* aLayer) {
|
|
if (aLayer->Extend3DContext()) {
|
|
ContainerLayer* container = aLayer->AsContainerLayer();
|
|
MOZ_ASSERT(container);
|
|
IntRect result;
|
|
for (Layer* child = container->GetFirstChild(); child;
|
|
child = child->GetNextSibling()) {
|
|
Maybe<IntRect> childBounds = TransformedBounds(child);
|
|
if (!childBounds) {
|
|
return Nothing();
|
|
}
|
|
Maybe<IntRect> combined = result.SafeUnion(childBounds.value());
|
|
if (!combined) {
|
|
LTI_LOG("overflowed bounds of container %p accumulating child %p\n",
|
|
container, child);
|
|
return Nothing();
|
|
}
|
|
result = combined.value();
|
|
}
|
|
return Some(result);
|
|
}
|
|
|
|
return Some(
|
|
TransformRect(aLayer->GetLocalVisibleRegion().GetBounds().ToUnknownRect(),
|
|
GetTransformForInvalidation(aLayer)));
|
|
}
|
|
|
|
/**
|
|
* Walks over this layer, and all descendant layers.
|
|
* If any of these are a ContainerLayer that reports invalidations to a
|
|
* PresShell, then report that the entire bounds have changed.
|
|
*/
|
|
static void NotifySubdocumentInvalidation(
|
|
Layer* aLayer, NotifySubDocInvalidationFunc aCallback) {
|
|
ForEachNode<ForwardIterator>(
|
|
aLayer,
|
|
[aCallback](Layer* layer) {
|
|
layer->ClearInvalidRegion();
|
|
if (layer->GetMaskLayer()) {
|
|
NotifySubdocumentInvalidation(layer->GetMaskLayer(), aCallback);
|
|
}
|
|
for (size_t i = 0; i < layer->GetAncestorMaskLayerCount(); i++) {
|
|
Layer* maskLayer = layer->GetAncestorMaskLayerAt(i);
|
|
NotifySubdocumentInvalidation(maskLayer, aCallback);
|
|
}
|
|
},
|
|
[aCallback](Layer* layer) {
|
|
ContainerLayer* container = layer->AsContainerLayer();
|
|
if (container && !container->Extend3DContext()) {
|
|
nsIntRegion region =
|
|
container->GetLocalVisibleRegion().ToUnknownRegion();
|
|
aCallback(container, ®ion);
|
|
}
|
|
});
|
|
}
|
|
|
|
static void SetChildrenChangedRecursive(Layer* aLayer) {
|
|
ForEachNode<ForwardIterator>(aLayer, [](Layer* layer) {
|
|
ContainerLayer* container = layer->AsContainerLayer();
|
|
if (container) {
|
|
container->SetChildrenChanged(true);
|
|
container->SetInvalidCompositeRect(nullptr);
|
|
}
|
|
});
|
|
}
|
|
|
|
struct LayerPropertiesBase : public LayerProperties {
|
|
explicit LayerPropertiesBase(Layer* aLayer)
|
|
: mLayer(aLayer),
|
|
mMaskLayer(nullptr),
|
|
mVisibleRegion(mLayer->Extend3DContext()
|
|
? nsIntRegion()
|
|
: mLayer->GetLocalVisibleRegion().ToUnknownRegion()),
|
|
mPostXScale(aLayer->GetPostXScale()),
|
|
mPostYScale(aLayer->GetPostYScale()),
|
|
mOpacity(aLayer->GetLocalOpacity()),
|
|
mUseClipRect(!!aLayer->GetLocalClipRect()) {
|
|
MOZ_COUNT_CTOR(LayerPropertiesBase);
|
|
if (aLayer->GetMaskLayer()) {
|
|
mMaskLayer =
|
|
CloneLayerTreePropertiesInternal(aLayer->GetMaskLayer(), true);
|
|
}
|
|
for (size_t i = 0; i < aLayer->GetAncestorMaskLayerCount(); i++) {
|
|
Layer* maskLayer = aLayer->GetAncestorMaskLayerAt(i);
|
|
mAncestorMaskLayers.AppendElement(
|
|
CloneLayerTreePropertiesInternal(maskLayer, true));
|
|
}
|
|
if (mUseClipRect) {
|
|
mClipRect = *aLayer->GetLocalClipRect();
|
|
}
|
|
mTransform = GetTransformForInvalidation(aLayer);
|
|
}
|
|
LayerPropertiesBase()
|
|
: mLayer(nullptr),
|
|
mMaskLayer(nullptr),
|
|
mPostXScale(0.0),
|
|
mPostYScale(0.0),
|
|
mOpacity(0.0),
|
|
mUseClipRect(false) {
|
|
MOZ_COUNT_CTOR(LayerPropertiesBase);
|
|
}
|
|
MOZ_COUNTED_DTOR_OVERRIDE(LayerPropertiesBase)
|
|
|
|
protected:
|
|
LayerPropertiesBase(const LayerPropertiesBase& a) = delete;
|
|
LayerPropertiesBase& operator=(const LayerPropertiesBase& a) = delete;
|
|
|
|
public:
|
|
bool ComputeDifferences(Layer* aRoot, nsIntRegion& aOutRegion,
|
|
NotifySubDocInvalidationFunc aCallback) override;
|
|
|
|
void MoveBy(const IntPoint& aOffset) override;
|
|
|
|
bool ComputeChange(const char* aPrefix, nsIntRegion& aOutRegion,
|
|
NotifySubDocInvalidationFunc aCallback) {
|
|
// Bug 1251615: This canary is sometimes hit. We're still not sure why.
|
|
mCanary.Check();
|
|
bool transformChanged =
|
|
!mTransform.FuzzyEqual(GetTransformForInvalidation(mLayer)) ||
|
|
mLayer->GetPostXScale() != mPostXScale ||
|
|
mLayer->GetPostYScale() != mPostYScale;
|
|
const Maybe<ParentLayerIntRect>& otherClip = mLayer->GetLocalClipRect();
|
|
nsIntRegion result;
|
|
|
|
bool ancestorMaskChanged =
|
|
mAncestorMaskLayers.Length() != mLayer->GetAncestorMaskLayerCount();
|
|
if (!ancestorMaskChanged) {
|
|
for (size_t i = 0; i < mAncestorMaskLayers.Length(); i++) {
|
|
if (mLayer->GetAncestorMaskLayerAt(i) !=
|
|
mAncestorMaskLayers[i]->mLayer) {
|
|
ancestorMaskChanged = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Note that we don't bailout early in general since this function
|
|
// clears some persistent state at the end. Instead we set an overflow
|
|
// flag and check it right before returning.
|
|
bool areaOverflowed = false;
|
|
|
|
Layer* otherMask = mLayer->GetMaskLayer();
|
|
if ((mMaskLayer ? mMaskLayer->mLayer : nullptr) != otherMask ||
|
|
ancestorMaskChanged || (mUseClipRect != !!otherClip) ||
|
|
mLayer->GetLocalOpacity() != mOpacity || transformChanged) {
|
|
Maybe<IntRect> oldBounds = OldTransformedBounds();
|
|
Maybe<IntRect> newBounds = NewTransformedBounds();
|
|
if (oldBounds && newBounds) {
|
|
LTI_DUMP(oldBounds.value(), "oldtransform");
|
|
LTI_DUMP(newBounds.value(), "newtransform");
|
|
result = oldBounds.value();
|
|
AddRegion(result, newBounds.value());
|
|
} else {
|
|
areaOverflowed = true;
|
|
}
|
|
|
|
// We can't bail out early because we might need to update some internal
|
|
// layer state.
|
|
}
|
|
|
|
nsIntRegion internal;
|
|
if (!ComputeChangeInternal(aPrefix, internal, aCallback)) {
|
|
areaOverflowed = true;
|
|
}
|
|
|
|
LTI_DUMP(internal, "internal");
|
|
AddRegion(result, internal);
|
|
LTI_DUMP(mLayer->GetInvalidRegion().GetRegion(), "invalid");
|
|
AddTransformedRegion(result, mLayer->GetInvalidRegion().GetRegion(),
|
|
mTransform);
|
|
|
|
if (mMaskLayer && otherMask) {
|
|
nsIntRegion mask;
|
|
if (!mMaskLayer->ComputeChange(aPrefix, mask, aCallback)) {
|
|
areaOverflowed = true;
|
|
}
|
|
LTI_DUMP(mask, "mask");
|
|
AddRegion(result, mask);
|
|
}
|
|
|
|
for (size_t i = 0; i < std::min(mAncestorMaskLayers.Length(),
|
|
mLayer->GetAncestorMaskLayerCount());
|
|
i++) {
|
|
nsIntRegion mask;
|
|
if (!mAncestorMaskLayers[i]->ComputeChange(aPrefix, mask, aCallback)) {
|
|
areaOverflowed = true;
|
|
}
|
|
LTI_DUMP(mask, "ancestormask");
|
|
AddRegion(result, mask);
|
|
}
|
|
|
|
if (mUseClipRect && otherClip) {
|
|
if (!mClipRect.IsEqualInterior(*otherClip)) {
|
|
nsIntRegion tmp;
|
|
tmp.Xor(mClipRect.ToUnknownRect(), otherClip->ToUnknownRect());
|
|
LTI_DUMP(tmp, "clip");
|
|
AddRegion(result, tmp);
|
|
}
|
|
}
|
|
|
|
mLayer->ClearInvalidRegion();
|
|
|
|
if (areaOverflowed) {
|
|
return false;
|
|
}
|
|
|
|
aOutRegion = std::move(result);
|
|
return true;
|
|
}
|
|
|
|
void CheckCanary() {
|
|
mCanary.Check();
|
|
mLayer->CheckCanary();
|
|
}
|
|
|
|
IntRect NewTransformedBoundsForLeaf() {
|
|
return TransformRect(
|
|
mLayer->GetLocalVisibleRegion().GetBounds().ToUnknownRect(),
|
|
GetTransformForInvalidation(mLayer));
|
|
}
|
|
|
|
IntRect OldTransformedBoundsForLeaf() {
|
|
return TransformRect(mVisibleRegion.GetBounds().ToUnknownRect(),
|
|
mTransform);
|
|
}
|
|
|
|
Maybe<IntRect> NewTransformedBounds() { return TransformedBounds(mLayer); }
|
|
|
|
virtual Maybe<IntRect> OldTransformedBounds() {
|
|
return Some(
|
|
TransformRect(mVisibleRegion.GetBounds().ToUnknownRect(), mTransform));
|
|
}
|
|
|
|
virtual bool ComputeChangeInternal(const char* aPrefix,
|
|
nsIntRegion& aOutRegion,
|
|
NotifySubDocInvalidationFunc aCallback) {
|
|
if (mLayer->AsHostLayer() &&
|
|
!mLayer->GetLocalVisibleRegion().ToUnknownRegion().IsEqual(
|
|
mVisibleRegion)) {
|
|
IntRect result = NewTransformedBoundsForLeaf();
|
|
result = result.Union(OldTransformedBoundsForLeaf());
|
|
aOutRegion = result;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
RefPtr<Layer> mLayer;
|
|
UniquePtr<LayerPropertiesBase> mMaskLayer;
|
|
nsTArray<UniquePtr<LayerPropertiesBase>> mAncestorMaskLayers;
|
|
nsIntRegion mVisibleRegion;
|
|
Matrix4x4Flagged mTransform;
|
|
float mPostXScale;
|
|
float mPostYScale;
|
|
float mOpacity;
|
|
ParentLayerIntRect mClipRect;
|
|
bool mUseClipRect;
|
|
mozilla::CorruptionCanary mCanary;
|
|
};
|
|
|
|
struct ContainerLayerProperties : public LayerPropertiesBase {
|
|
explicit ContainerLayerProperties(ContainerLayer* aLayer)
|
|
: LayerPropertiesBase(aLayer),
|
|
mPreXScale(aLayer->GetPreXScale()),
|
|
mPreYScale(aLayer->GetPreYScale()) {
|
|
for (Layer* child = aLayer->GetFirstChild(); child;
|
|
child = child->GetNextSibling()) {
|
|
child->CheckCanary();
|
|
mChildren.AppendElement(CloneLayerTreePropertiesInternal(child));
|
|
}
|
|
}
|
|
|
|
protected:
|
|
ContainerLayerProperties(const ContainerLayerProperties& a) = delete;
|
|
ContainerLayerProperties& operator=(const ContainerLayerProperties& a) =
|
|
delete;
|
|
|
|
public:
|
|
bool ComputeChangeInternal(const char* aPrefix, nsIntRegion& aOutRegion,
|
|
NotifySubDocInvalidationFunc aCallback) override {
|
|
// Make sure we got our virtual call right
|
|
mSubtypeCanary.Check();
|
|
ContainerLayer* container = mLayer->AsContainerLayer();
|
|
nsIntRegion invalidOfLayer; // Invalid regions of this layer.
|
|
nsIntRegion result; // Invliad regions for children only.
|
|
|
|
container->CheckCanary();
|
|
|
|
bool childrenChanged = false;
|
|
bool invalidateWholeLayer = false;
|
|
bool areaOverflowed = false;
|
|
if (mPreXScale != container->GetPreXScale() ||
|
|
mPreYScale != container->GetPreYScale()) {
|
|
Maybe<IntRect> oldBounds = OldTransformedBounds();
|
|
Maybe<IntRect> newBounds = NewTransformedBounds();
|
|
if (oldBounds && newBounds) {
|
|
invalidOfLayer = oldBounds.value();
|
|
AddRegion(invalidOfLayer, newBounds.value());
|
|
} else {
|
|
areaOverflowed = true;
|
|
}
|
|
childrenChanged = true;
|
|
invalidateWholeLayer = true;
|
|
|
|
// Can't bail out early, we need to update the child container layers
|
|
}
|
|
|
|
// A low frame rate is especially visible to users when scrolling, so we
|
|
// particularly want to avoid unnecessary invalidation at that time. For us
|
|
// here, that means avoiding unnecessary invalidation of child items when
|
|
// other children are added to or removed from our container layer, since
|
|
// that may be caused by children being scrolled in or out of view. We are
|
|
// less concerned with children changing order.
|
|
// TODO: Consider how we could avoid unnecessary invalidation when children
|
|
// change order, and whether the overhead would be worth it.
|
|
|
|
nsDataHashtable<nsPtrHashKey<Layer>, uint32_t> oldIndexMap(
|
|
mChildren.Length());
|
|
for (uint32_t i = 0; i < mChildren.Length(); ++i) {
|
|
mChildren[i]->CheckCanary();
|
|
oldIndexMap.Put(mChildren[i]->mLayer, i);
|
|
}
|
|
|
|
uint32_t i = 0; // cursor into the old child list mChildren
|
|
for (Layer* child = container->GetFirstChild(); child;
|
|
child = child->GetNextSibling()) {
|
|
bool invalidateChildsCurrentArea = false;
|
|
if (i < mChildren.Length()) {
|
|
uint32_t childsOldIndex;
|
|
if (oldIndexMap.Get(child, &childsOldIndex)) {
|
|
if (childsOldIndex >= i) {
|
|
// Invalidate the old areas of layers that used to be between the
|
|
// current |child| and the previous |child| that was also in the
|
|
// old list mChildren (if any of those children have been reordered
|
|
// rather than removed, we will invalidate their new area when we
|
|
// encounter them in the new list):
|
|
for (uint32_t j = i; j < childsOldIndex; ++j) {
|
|
if (Maybe<IntRect> bounds =
|
|
mChildren[j]->OldTransformedBounds()) {
|
|
LTI_DUMP(bounds.value(), "reordered child");
|
|
AddRegion(result, bounds.value());
|
|
} else {
|
|
areaOverflowed = true;
|
|
}
|
|
childrenChanged |= true;
|
|
}
|
|
if (childsOldIndex >= mChildren.Length()) {
|
|
MOZ_CRASH("Out of bounds");
|
|
}
|
|
// Invalidate any regions of the child that have changed:
|
|
nsIntRegion region;
|
|
if (!mChildren[childsOldIndex]->ComputeChange(LTI_DEEPER(aPrefix),
|
|
region, aCallback)) {
|
|
areaOverflowed = true;
|
|
}
|
|
i = childsOldIndex + 1;
|
|
if (!region.IsEmpty()) {
|
|
LTI_LOG("%s%p: child %p produced %s\n", aPrefix, mLayer.get(),
|
|
mChildren[childsOldIndex]->mLayer.get(),
|
|
Stringify(region).c_str());
|
|
AddRegion(result, region);
|
|
childrenChanged |= true;
|
|
}
|
|
} else {
|
|
// We've already seen this child in mChildren (which means it must
|
|
// have been reordered) and invalidated its old area. We need to
|
|
// invalidate its new area too:
|
|
invalidateChildsCurrentArea = true;
|
|
}
|
|
} else {
|
|
// |child| is new
|
|
invalidateChildsCurrentArea = true;
|
|
SetChildrenChangedRecursive(child);
|
|
}
|
|
} else {
|
|
// |child| is new, or was reordered to a higher index
|
|
invalidateChildsCurrentArea = true;
|
|
if (!oldIndexMap.Contains(child)) {
|
|
SetChildrenChangedRecursive(child);
|
|
}
|
|
}
|
|
if (invalidateChildsCurrentArea) {
|
|
LTI_DUMP(child->GetLocalVisibleRegion().ToUnknownRegion(),
|
|
"invalidateChildsCurrentArea");
|
|
if (Maybe<IntRect> bounds = TransformedBounds(child)) {
|
|
AddRegion(result, bounds.value());
|
|
} else {
|
|
areaOverflowed = true;
|
|
}
|
|
if (aCallback) {
|
|
NotifySubdocumentInvalidation(child, aCallback);
|
|
} else {
|
|
ClearInvalidations(child);
|
|
}
|
|
}
|
|
childrenChanged |= invalidateChildsCurrentArea;
|
|
}
|
|
|
|
// Process remaining removed children.
|
|
while (i < mChildren.Length()) {
|
|
childrenChanged |= true;
|
|
if (Maybe<IntRect> bounds = mChildren[i]->OldTransformedBounds()) {
|
|
LTI_DUMP(bounds.value(), "removed child");
|
|
AddRegion(result, bounds.value());
|
|
} else {
|
|
areaOverflowed = true;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
if (aCallback) {
|
|
aCallback(container, areaOverflowed ? nullptr : &result);
|
|
}
|
|
|
|
if (childrenChanged || areaOverflowed) {
|
|
container->SetChildrenChanged(true);
|
|
}
|
|
|
|
if (container->UseIntermediateSurface()) {
|
|
Maybe<IntRect> bounds;
|
|
if (!invalidateWholeLayer && !areaOverflowed) {
|
|
bounds = Some(result.GetBounds());
|
|
|
|
// Process changes in the visible region.
|
|
IntRegion newVisible =
|
|
mLayer->GetLocalVisibleRegion().ToUnknownRegion();
|
|
if (!newVisible.IsEqual(mVisibleRegion)) {
|
|
newVisible.XorWith(mVisibleRegion);
|
|
bounds = bounds->SafeUnion(newVisible.GetBounds());
|
|
}
|
|
}
|
|
container->SetInvalidCompositeRect(bounds ? bounds.ptr() : nullptr);
|
|
}
|
|
|
|
// Safe to bail out early now, persistent state has been set.
|
|
if (areaOverflowed) {
|
|
return false;
|
|
}
|
|
|
|
if (!mLayer->Extend3DContext()) {
|
|
// |result| contains invalid regions only of children.
|
|
result.Transform(GetTransformForInvalidation(mLayer).GetMatrix());
|
|
}
|
|
// else, effective transforms have applied on children.
|
|
|
|
LTI_DUMP(invalidOfLayer, "invalidOfLayer");
|
|
result.OrWith(invalidOfLayer);
|
|
|
|
aOutRegion = std::move(result);
|
|
return true;
|
|
}
|
|
|
|
Maybe<IntRect> OldTransformedBounds() override {
|
|
if (mLayer->Extend3DContext()) {
|
|
IntRect result;
|
|
for (UniquePtr<LayerPropertiesBase>& child : mChildren) {
|
|
Maybe<IntRect> childBounds = child->OldTransformedBounds();
|
|
if (!childBounds) {
|
|
return Nothing();
|
|
}
|
|
Maybe<IntRect> combined = result.SafeUnion(childBounds.value());
|
|
if (!combined) {
|
|
LTI_LOG("overflowed bounds of container %p accumulating child %p\n",
|
|
this, child->mLayer.get());
|
|
return Nothing();
|
|
}
|
|
result = combined.value();
|
|
}
|
|
return Some(result);
|
|
}
|
|
return LayerPropertiesBase::OldTransformedBounds();
|
|
}
|
|
|
|
// The old list of children:
|
|
mozilla::CorruptionCanary mSubtypeCanary;
|
|
nsTArray<UniquePtr<LayerPropertiesBase>> mChildren;
|
|
float mPreXScale;
|
|
float mPreYScale;
|
|
};
|
|
|
|
struct ColorLayerProperties : public LayerPropertiesBase {
|
|
explicit ColorLayerProperties(ColorLayer* aLayer)
|
|
: LayerPropertiesBase(aLayer),
|
|
mColor(aLayer->GetColor()),
|
|
mBounds(aLayer->GetBounds()) {}
|
|
|
|
protected:
|
|
ColorLayerProperties(const ColorLayerProperties& a) = delete;
|
|
ColorLayerProperties& operator=(const ColorLayerProperties& a) = delete;
|
|
|
|
public:
|
|
bool ComputeChangeInternal(const char* aPrefix, nsIntRegion& aOutRegion,
|
|
NotifySubDocInvalidationFunc aCallback) override {
|
|
ColorLayer* color = static_cast<ColorLayer*>(mLayer.get());
|
|
|
|
if (mColor != color->GetColor()) {
|
|
LTI_DUMP(NewTransformedBoundsForLeaf(), "color");
|
|
aOutRegion = NewTransformedBoundsForLeaf();
|
|
return true;
|
|
}
|
|
|
|
nsIntRegion boundsDiff;
|
|
boundsDiff.Xor(mBounds, color->GetBounds());
|
|
LTI_DUMP(boundsDiff, "colorbounds");
|
|
|
|
AddTransformedRegion(aOutRegion, boundsDiff, mTransform);
|
|
return true;
|
|
}
|
|
|
|
DeviceColor mColor;
|
|
IntRect mBounds;
|
|
};
|
|
|
|
static ImageHost* GetImageHost(Layer* aLayer) {
|
|
HostLayer* compositor = aLayer->AsHostLayer();
|
|
if (compositor) {
|
|
return static_cast<ImageHost*>(compositor->GetCompositableHost());
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
struct ImageLayerProperties : public LayerPropertiesBase {
|
|
explicit ImageLayerProperties(ImageLayer* aImage, bool aIsMask)
|
|
: LayerPropertiesBase(aImage),
|
|
mContainer(aImage->GetContainer()),
|
|
mImageHost(GetImageHost(aImage)),
|
|
mSamplingFilter(aImage->GetSamplingFilter()),
|
|
mScaleToSize(aImage->GetScaleToSize()),
|
|
mScaleMode(aImage->GetScaleMode()),
|
|
mLastProducerID(-1),
|
|
mLastFrameID(-1),
|
|
mIsMask(aIsMask) {
|
|
if (mImageHost) {
|
|
if (aIsMask) {
|
|
// Mask layers never set the 'last' producer/frame
|
|
// id, since they never get composited as their own
|
|
// layer.
|
|
mLastProducerID = mImageHost->GetProducerID();
|
|
mLastFrameID = mImageHost->GetFrameID();
|
|
} else {
|
|
mLastProducerID = mImageHost->GetLastProducerID();
|
|
mLastFrameID = mImageHost->GetLastFrameID();
|
|
}
|
|
}
|
|
}
|
|
|
|
bool ComputeChangeInternal(const char* aPrefix, nsIntRegion& aOutRegion,
|
|
NotifySubDocInvalidationFunc aCallback) override {
|
|
ImageLayer* imageLayer = static_cast<ImageLayer*>(mLayer.get());
|
|
|
|
if (!imageLayer->GetLocalVisibleRegion().ToUnknownRegion().IsEqual(
|
|
mVisibleRegion)) {
|
|
IntRect result = NewTransformedBoundsForLeaf();
|
|
result = result.Union(OldTransformedBoundsForLeaf());
|
|
aOutRegion = result;
|
|
return true;
|
|
}
|
|
|
|
ImageContainer* container = imageLayer->GetContainer();
|
|
ImageHost* host = GetImageHost(imageLayer);
|
|
if (mContainer != container ||
|
|
mSamplingFilter != imageLayer->GetSamplingFilter() ||
|
|
mScaleToSize != imageLayer->GetScaleToSize() ||
|
|
mScaleMode != imageLayer->GetScaleMode() || host != mImageHost ||
|
|
(host && host->GetProducerID() != mLastProducerID) ||
|
|
(host && host->GetFrameID() != mLastFrameID)) {
|
|
if (mIsMask) {
|
|
// Mask layers have an empty visible region, so we have to
|
|
// use the image size instead.
|
|
IntSize size;
|
|
if (container) {
|
|
size = container->GetCurrentSize();
|
|
}
|
|
if (host) {
|
|
size = host->GetImageSize();
|
|
}
|
|
IntRect rect(0, 0, size.width, size.height);
|
|
LTI_DUMP(rect, "mask");
|
|
aOutRegion = TransformRect(rect, GetTransformForInvalidation(mLayer));
|
|
return true;
|
|
}
|
|
LTI_DUMP(NewTransformedBoundsForLeaf(), "bounds");
|
|
aOutRegion = NewTransformedBoundsForLeaf();
|
|
return true;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
RefPtr<ImageContainer> mContainer;
|
|
RefPtr<ImageHost> mImageHost;
|
|
SamplingFilter mSamplingFilter;
|
|
gfx::IntSize mScaleToSize;
|
|
ScaleMode mScaleMode;
|
|
int32_t mLastProducerID;
|
|
int32_t mLastFrameID;
|
|
bool mIsMask;
|
|
};
|
|
|
|
struct CanvasLayerProperties : public LayerPropertiesBase {
|
|
explicit CanvasLayerProperties(CanvasLayer* aCanvas)
|
|
: LayerPropertiesBase(aCanvas), mImageHost(GetImageHost(aCanvas)) {
|
|
mFrameID = mImageHost ? mImageHost->GetFrameID() : -1;
|
|
}
|
|
|
|
bool ComputeChangeInternal(const char* aPrefix, nsIntRegion& aOutRegion,
|
|
NotifySubDocInvalidationFunc aCallback) override {
|
|
CanvasLayer* canvasLayer = static_cast<CanvasLayer*>(mLayer.get());
|
|
|
|
ImageHost* host = GetImageHost(canvasLayer);
|
|
if (host && host->GetFrameID() != mFrameID) {
|
|
LTI_DUMP(NewTransformedBoundsForLeaf(), "frameId");
|
|
aOutRegion = NewTransformedBoundsForLeaf();
|
|
return true;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
RefPtr<ImageHost> mImageHost;
|
|
int32_t mFrameID;
|
|
};
|
|
|
|
UniquePtr<LayerPropertiesBase> CloneLayerTreePropertiesInternal(
|
|
Layer* aRoot, bool aIsMask /* = false */) {
|
|
if (!aRoot) {
|
|
return MakeUnique<LayerPropertiesBase>();
|
|
}
|
|
|
|
MOZ_ASSERT(!aIsMask || aRoot->GetType() == Layer::TYPE_IMAGE);
|
|
|
|
aRoot->CheckCanary();
|
|
|
|
switch (aRoot->GetType()) {
|
|
case Layer::TYPE_CONTAINER:
|
|
case Layer::TYPE_REF:
|
|
return MakeUnique<ContainerLayerProperties>(aRoot->AsContainerLayer());
|
|
case Layer::TYPE_COLOR:
|
|
return MakeUnique<ColorLayerProperties>(static_cast<ColorLayer*>(aRoot));
|
|
case Layer::TYPE_IMAGE:
|
|
return MakeUnique<ImageLayerProperties>(static_cast<ImageLayer*>(aRoot),
|
|
aIsMask);
|
|
case Layer::TYPE_CANVAS:
|
|
return MakeUnique<CanvasLayerProperties>(
|
|
static_cast<CanvasLayer*>(aRoot));
|
|
case Layer::TYPE_DISPLAYITEM:
|
|
case Layer::TYPE_READBACK:
|
|
case Layer::TYPE_SHADOW:
|
|
case Layer::TYPE_PAINTED:
|
|
return MakeUnique<LayerPropertiesBase>(aRoot);
|
|
}
|
|
|
|
MOZ_ASSERT_UNREACHABLE("Unexpected root layer type");
|
|
return MakeUnique<LayerPropertiesBase>(aRoot);
|
|
}
|
|
|
|
/* static */
|
|
UniquePtr<LayerProperties> LayerProperties::CloneFrom(Layer* aRoot) {
|
|
return CloneLayerTreePropertiesInternal(aRoot);
|
|
}
|
|
|
|
/* static */
|
|
void LayerProperties::ClearInvalidations(Layer* aLayer) {
|
|
ForEachNode<ForwardIterator>(aLayer, [](Layer* layer) {
|
|
layer->ClearInvalidRegion();
|
|
if (layer->GetMaskLayer()) {
|
|
ClearInvalidations(layer->GetMaskLayer());
|
|
}
|
|
for (size_t i = 0; i < layer->GetAncestorMaskLayerCount(); i++) {
|
|
ClearInvalidations(layer->GetAncestorMaskLayerAt(i));
|
|
}
|
|
});
|
|
}
|
|
|
|
bool LayerPropertiesBase::ComputeDifferences(
|
|
Layer* aRoot, nsIntRegion& aOutRegion,
|
|
NotifySubDocInvalidationFunc aCallback) {
|
|
NS_ASSERTION(aRoot, "Must have a layer tree to compare against!");
|
|
if (mLayer != aRoot) {
|
|
if (aCallback) {
|
|
NotifySubdocumentInvalidation(aRoot, aCallback);
|
|
} else {
|
|
ClearInvalidations(aRoot);
|
|
}
|
|
IntRect bounds = TransformRect(
|
|
aRoot->GetLocalVisibleRegion().GetBounds().ToUnknownRect(),
|
|
aRoot->GetLocalTransform());
|
|
Maybe<IntRect> oldBounds = OldTransformedBounds();
|
|
if (!oldBounds) {
|
|
return false;
|
|
}
|
|
Maybe<IntRect> result = bounds.SafeUnion(oldBounds.value());
|
|
if (!result) {
|
|
LTI_LOG("overflowed bounds computing the union of layers %p and %p\n",
|
|
mLayer.get(), aRoot);
|
|
return false;
|
|
}
|
|
aOutRegion = result.value();
|
|
return true;
|
|
}
|
|
return ComputeChange(" ", aOutRegion, aCallback);
|
|
}
|
|
|
|
void LayerPropertiesBase::MoveBy(const IntPoint& aOffset) {
|
|
mTransform.PostTranslate(aOffset.x, aOffset.y, 0);
|
|
}
|
|
|
|
} // namespace layers
|
|
} // namespace mozilla
|