--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -210,16 +210,30 @@ TransformClipRect(Layer* aLayer,
MOZ_ASSERT(aTransform.Is2D());
const Maybe<ParentLayerIntRect>& clipRect = aLayer->AsLayerComposite()->GetShadowClipRect();
if (clipRect) {
ParentLayerIntRect transformed = TransformBy(aTransform, *clipRect);
aLayer->AsLayerComposite()->SetShadowClipRect(Some(transformed));
}
}
+// Similar to TransformFixedClip(), but only transforms the fixed part of the
+// clip.
+static void
+TransformFixedClip(Layer* aLayer,
+ const ParentLayerToParentLayerMatrix4x4& aTransform,
+ AsyncCompositionManager::ClipParts& aClipParts)
+{
+ MOZ_ASSERT(aTransform.Is2D());
+ if (aClipParts.mFixedClip) {
+ *aClipParts.mFixedClip = TransformBy(aTransform, *aClipParts.mFixedClip);
+ aLayer->AsLayerComposite()->SetShadowClipRect(aClipParts.Intersect());
+ }
+}
+
/**
* Set the given transform as the shadow transform on the layer, assuming
* that the given transform already has the pre- and post-scales applied.
* That is, this function cancels out the pre- and post-scales from aTransform
* before setting it as the shadow transform on the layer, so that when
* the layer's effective transform is computed, the pre- and post-scales will
* only be applied once.
*/
@@ -235,40 +249,49 @@ SetShadowTransform(Layer* aLayer, LayerT
1.0f / aLayer->GetPostYScale(),
1);
aLayer->AsLayerComposite()->SetShadowBaseTransform(aTransform.ToUnknownMatrix());
}
static void
TranslateShadowLayer(Layer* aLayer,
const gfxPoint& aTranslation,
- bool aAdjustClipRect)
+ bool aAdjustClipRect,
+ AsyncCompositionManager::ClipPartsCache* aClipPartsCache = nullptr)
{
// This layer might also be a scrollable layer and have an async transform.
// To make sure we don't clobber that, we start with the shadow transform.
// (i.e. GetLocalTransform() instead of GetTransform()).
// Note that the shadow transform is reset on every frame of composition so
// we don't have to worry about the adjustments compounding over successive
// frames.
LayerToParentLayerMatrix4x4 layerTransform = aLayer->GetLocalTransformTyped();
// Apply the translation to the layer transform.
layerTransform.PostTranslate(aTranslation.x, aTranslation.y, 0);
SetShadowTransform(aLayer, layerTransform);
aLayer->AsLayerComposite()->SetShadowTransformSetByAnimation(false);
if (aAdjustClipRect) {
- TransformClipRect(aLayer,
- ParentLayerToParentLayerMatrix4x4::Translation(aTranslation.x, aTranslation.y, 0));
+ auto transform = ParentLayerToParentLayerMatrix4x4::Translation(aTranslation.x, aTranslation.y, 0);
+ // If we're passed a clip parts cache, only transform the fixed part of
+ // the clip.
+ if (aClipPartsCache) {
+ auto iter = aClipPartsCache->find(aLayer);
+ MOZ_ASSERT(iter != aClipPartsCache->end());
+ TransformFixedClip(aLayer, transform, iter->second);
+ } else {
+ TransformClipRect(aLayer, transform);
+ }
// If a fixed- or sticky-position layer has a mask layer, that mask should
// move along with the layer, so apply the translation to the mask layer too.
if (Layer* maskLayer = aLayer->GetMaskLayer()) {
- TranslateShadowLayer(maskLayer, aTranslation, false);
+ TranslateShadowLayer(maskLayer, aTranslation, false, aClipPartsCache);
}
}
}
static void
AccumulateLayerTransforms(Layer* aLayer,
Layer* aAncestor,
Matrix4x4& aMatrix)
@@ -504,17 +527,18 @@ AsyncCompositionManager::AlignFixedAndSt
// where the async transform on |aTransformedSubtreeRoot| affects this layer's
// clip rect, we need to apply the same translation to said clip rect, so
// that the effective transform on the clip rect takes it back to where it was
// originally, had there been no async scroll.
// Also, some layers want async scrolling to move their clip rect
// (IsClipFixed() = false), so we don't make a compensating adjustment for
// those.
bool adjustClipRect = aTransformAffectsLayerClip && aLayer->IsClipFixed();
- TranslateShadowLayer(aLayer, ThebesPoint(translation.ToUnknownPoint()), adjustClipRect);
+ TranslateShadowLayer(aLayer, ThebesPoint(translation.ToUnknownPoint()),
+ adjustClipRect, &mClipPartsCache);
}
static void
SampleValue(float aPortion, Animation& aAnimation, StyleAnimationValue& aStart,
StyleAnimationValue& aEnd, Animatable* aValue, Layer* aLayer)
{
StyleAnimationValue interpolatedValue;
NS_ASSERTION(aStart.GetUnit() == aEnd.GetUnit() ||
@@ -804,28 +828,42 @@ AsyncCompositionManager::ApplyAsyncConte
LayerToParentLayerMatrix4x4 oldTransform = aLayer->GetTransformTyped() *
AsyncTransformMatrix();
AsyncTransformComponentMatrix combinedAsyncTransform;
bool hasAsyncTransform = false;
ScreenMargin fixedLayerMargins;
- // Each layer has multiple clips. Its local clip, which must move with async
- // transforms, and its scrollframe clips, which are the clips between each
- // scrollframe and its ancestor scrollframe. Scrollframe clips include the
- // composition bounds and any other clips induced by layout.
- //
- // The final clip for the layer is the intersection of these clips.
- Maybe<ParentLayerIntRect> asyncClip = aLayer->GetClipRect();
+ // Each layer has multiple clips:
+ // - Its local clip, which is fixed to the layer contents, i.e. it moves
+ // with those async transforms which the layer contents move with.
+ // - For each ScrollMetadata on the layer, a scroll clip. This includes
+ // the composition bounds and any other clips induced by layout. This
+ // moves with async transforms from ScrollMetadatas above it.
+ // In this function, these clips are combined into two shadow clip parts:
+ // - The fixed clip, which consists of the local clip only, initially
+ // transformed by all async transforms.
+ // - The scrolled clip, which consists of the scroll clips, transformed by
+ // the appropriate transforms.
+ // These two parts are kept separate for now, because for fixed layers, we
+ // need to adjust the fixed clip (to cancel out some async transforms).
+ // The parts are kept in a cache which is cleared at the beginning of every
+ // composite.
+ // The final shadow clip for the layer is the intersection of the (possibly
+ // adjusted) fixed clip and the scrolled clip.
+ ClipParts& clipParts = mClipPartsCache[aLayer];
+ clipParts.mFixedClip = aLayer->GetClipRect();
// If we are a perspective transform ContainerLayer, apply the clip deferred
// from our child (if there is any) before we iterate over our frame metrics,
// because this clip is subject to all async transforms of this layer.
- asyncClip = IntersectMaybeRects(asyncClip, clipDeferredFromChildren);
+ // Since this clip came from the a scroll clip on the child, it becomes part
+ // of our scrolled clip.
+ clipParts.mScrolledClip = clipDeferredFromChildren;
// The transform of a mask layer is relative to the masked layer's parent
// layer. So whenever we apply an async transform to a layer, we need to
// apply that same transform to the layer's own mask layer.
// A layer can also have "ancestor" mask layers for any rounded clips from
// its ancestor scroll frames. A scroll frame mask layer only needs to be
// async transformed for async scrolls of this scroll frame's ancestor
// scroll frames, not for async scrolls of this scroll frame itself.
@@ -897,24 +935,31 @@ AsyncCompositionManager::ApplyAsyncConte
}
}
#else
// Non-Android platforms still care about this flag being cleared after
// the first call to TransformShadowTree().
mIsFirstPaint = false;
#endif
- // Transform the current local clip by this APZC's async transform. If we're
+ // Transform the current local clips by this APZC's async transform. If we're
// using containerful scrolling, then the clip is not part of the scrolled
// frame and should not be transformed.
- if (asyncClip && !scrollMetadata.UsesContainerScrolling()) {
+ if (!scrollMetadata.UsesContainerScrolling()) {
MOZ_ASSERT(asyncTransform.Is2D());
- asyncClip = Some(TransformBy(asyncTransform, *asyncClip));
+ if (clipParts.mFixedClip) {
+ clipParts.mFixedClip = Some(TransformBy(asyncTransform, *clipParts.mFixedClip));
+ }
+ if (clipParts.mScrolledClip) {
+ clipParts.mScrolledClip = Some(TransformBy(asyncTransform, *clipParts.mScrolledClip));
+ }
}
- aLayer->AsLayerComposite()->SetShadowClipRect(asyncClip);
+ // Note: we don't set the layer's shadow clip rect property yet;
+ // AlignFixedAndStickyLayers will use the clip parts from the clip parts
+ // cache.
combinedAsyncTransform *= asyncTransform;
// For the purpose of aligning fixed and sticky layers, we disregard
// the overscroll transform as well as any OMTA transform when computing the
// 'aCurrentTransformForRoot' parameter. This ensures that the overscroll
// and OMTA transforms are not unapplied, and therefore that the visual
// effects apply to fixed and sticky layers. We do this by using
@@ -924,42 +969,38 @@ AsyncCompositionManager::ApplyAsyncConte
aLayer->GetTransformTyped()
* CompleteAsyncTransform(
AdjustForClip(asyncTransformWithoutOverscroll, aLayer));
// Since fixed/sticky layers are relative to their nearest scrolling ancestor,
// we use the ViewID from the bottommost scrollable metrics here.
AlignFixedAndStickyLayers(aLayer, aLayer, metrics.GetScrollId(), oldTransform,
transformWithoutOverscrollOrOmta, fixedLayerMargins,
- asyncClip.isSome());
+ clipParts.IsSome());
- // AlignFixedAndStickyLayers may have changed the clip rect, so we have to
- // read it from the layer again.
- asyncClip = aLayer->AsLayerComposite()->GetShadowClipRect();
-
- // Combine the local clip with the ancestor scrollframe clip. This is not
- // included in the async transform above, since the ancestor clip should not
- // move with this APZC.
+ // Combine the scrolled portion of the local clip with the ancestor
+ // scroll clip. This is not included in the async transform above, since
+ // the ancestor clip should not move with this APZC.
if (scrollMetadata.HasScrollClip()) {
ParentLayerIntRect clip = scrollMetadata.ScrollClip().GetClipRect();
if (aLayer->GetParent() && aLayer->GetParent()->GetTransformIsPerspective()) {
// If our parent layer has a perspective transform, we want to apply
// our scroll clip to it instead of to this layer (see bug 1168263).
// A layer with a perspective transform shouldn't have multiple
// children with FrameMetrics, nor a child with multiple FrameMetrics.
// (A child with multiple FrameMetrics would mean that there's *another*
// scrollable element between the one with the CSS perspective and the
// transformed element. But you'd have to use preserve-3d on the inner
// scrollable element in order to have the perspective apply to the
// transformed child, and preserve-3d is not supported on scrollable
// elements, so this case can't occur.)
MOZ_ASSERT(!aClipDeferredToParent);
aClipDeferredToParent = Some(clip);
} else {
- asyncClip = IntersectMaybeRects(Some(clip), asyncClip);
+ clipParts.mScrolledClip = IntersectMaybeRects(Some(clip), clipParts.mScrolledClip);
}
}
// Do the same for the ancestor mask layers: ancestorMaskLayers contains
// the ancestor mask layers for scroll frames *inside* the current scroll
// frame, so these are the ones we need to shift by our async transform.
for (Layer* ancestorMaskLayer : ancestorMaskLayers) {
SetShadowTransform(ancestorMaskLayer,
@@ -972,18 +1013,24 @@ AsyncCompositionManager::ApplyAsyncConte
if (scrollClip.GetMaskLayerIndex()) {
size_t maskLayerIndex = scrollClip.GetMaskLayerIndex().value();
Layer* ancestorMaskLayer = aLayer->GetAncestorMaskLayerAt(maskLayerIndex);
ancestorMaskLayers.AppendElement(ancestorMaskLayer);
}
}
}
- if (hasAsyncTransform || clipDeferredFromChildren) {
- aLayer->AsLayerComposite()->SetShadowClipRect(asyncClip);
+ bool clipChanged = (hasAsyncTransform || clipDeferredFromChildren);
+ if (clipChanged) {
+ // Intersect the two clip parts and apply them to the layer.
+ // During ApplyAsyncContentTransformTree on an ancestor layer,
+ // AlignFixedAndStickyLayers may overwrite this with a new clip it
+ // computes from the clip parts, but if that doesn't happen, this
+ // is the layer's final clip rect.
+ aLayer->AsLayerComposite()->SetShadowClipRect(clipParts.Intersect());
}
if (hasAsyncTransform) {
// Apply the APZ transform on top of GetLocalTransform() here (rather than
// GetTransform()) in case the OMTA code in SampleAnimations already set a
// shadow transform; in that case we want to apply ours on top of that one
// rather than clobber it.
SetShadowTransform(aLayer,
@@ -1426,16 +1473,19 @@ AsyncCompositionManager::TransformShadow
for (uint32_t i = 0; i < scrollableLayers.Length(); i++) {
if (scrollableLayers[i]) {
TransformScrollableLayer(scrollableLayers[i]);
}
}
}
+ // Clear the cache of clip parts that was used by ApplyAsyncContentTransformToTree().
+ mClipPartsCache.clear();
+
// Advance APZ animations to the next expected vsync timestamp, if we can
// get it.
TimeStamp nextFrame = aCurrentFrame;
TimeDuration vsyncrate = gfxPlatform::GetPlatform()->GetHardwareVsync()->GetGlobalDisplay().GetVsyncRate();
if (vsyncrate != TimeDuration::Forever()) {
nextFrame += vsyncrate;
}
wantNextFrame |= SampleAPZAnimations(LayerMetricsWrapper(root), nextFrame);