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
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "mozilla/layers/AsyncCompositionManager.h"
8 #include <stdint.h> // for uint32_t
9 #include "apz/src/AsyncPanZoomController.h"
10 #include "FrameMetrics.h" // for FrameMetrics
11 #include "LayerManagerComposite.h" // for LayerManagerComposite, etc
12 #include "Layers.h" // for Layer, ContainerLayer, etc
13 #include "gfxPoint.h" // for gfxPoint, gfxSize
14 #include "gfxPrefs.h" // for gfxPrefs
15 #include "mozilla/StyleAnimationValue.h" // for StyleAnimationValue, etc
16 #include "mozilla/WidgetUtils.h" // for ComputeTransformForRotation
17 #include "mozilla/gfx/BaseRect.h" // for BaseRect
18 #include "mozilla/gfx/Point.h" // for RoundedToInt, PointTyped
19 #include "mozilla/gfx/Rect.h" // for RoundedToInt, RectTyped
20 #include "mozilla/gfx/ScaleFactor.h" // for ScaleFactor
21 #include "mozilla/layers/AnimationHelper.h"
22 #include "mozilla/layers/APZUtils.h" // for CompleteAsyncTransform
23 #include "mozilla/layers/Compositor.h" // for Compositor
24 #include "mozilla/layers/CompositorBridgeParent.h" // for CompositorBridgeParent, etc
25 #include "mozilla/layers/CompositorThread.h"
26 #include "mozilla/layers/LayerAnimationUtils.h" // for TimingFunctionToComputedTimingFunction
27 #include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper
28 #include "nsCoord.h" // for NSAppUnitsToFloatPixels, etc
29 #include "nsDebug.h" // for NS_ASSERTION, etc
30 #include "nsDeviceContext.h" // for nsDeviceContext
31 #include "nsDisplayList.h" // for nsDisplayTransform, etc
32 #include "nsMathUtils.h" // for NS_round
33 #include "nsPoint.h" // for nsPoint
34 #include "nsRect.h" // for mozilla::gfx::IntRect
35 #include "nsRegion.h" // for nsIntRegion
36 #include "nsTArray.h" // for nsTArray, nsTArray_Impl, etc
37 #include "nsTArrayForwardDeclare.h" // for InfallibleTArray
38 #include "UnitTransforms.h" // for TransformTo
39 #include "gfxPrefs.h"
40 #if defined(MOZ_WIDGET_ANDROID)
41 #include <android/log.h>
42 #include "mozilla/layers/UiCompositorControllerParent.h"
43 #include "mozilla/widget/AndroidCompositorWidget.h"
44 #endif
45 #include "GeckoProfiler.h"
46 #include "FrameUniformityData.h"
47 #include "TreeTraversal.h" // for ForEachNode, BreadthFirstSearch
48 #include "VsyncSource.h"
49
50 struct nsCSSValueSharedList;
51
52 namespace mozilla {
53 namespace layers {
54
55 using namespace mozilla::gfx;
56
IsSameDimension(dom::ScreenOrientationInternal o1,dom::ScreenOrientationInternal o2)57 static bool IsSameDimension(dom::ScreenOrientationInternal o1,
58 dom::ScreenOrientationInternal o2) {
59 bool isO1portrait = (o1 == dom::eScreenOrientation_PortraitPrimary ||
60 o1 == dom::eScreenOrientation_PortraitSecondary);
61 bool isO2portrait = (o2 == dom::eScreenOrientation_PortraitPrimary ||
62 o2 == dom::eScreenOrientation_PortraitSecondary);
63 return !(isO1portrait ^ isO2portrait);
64 }
65
ContentMightReflowOnOrientationChange(const IntRect & rect)66 static bool ContentMightReflowOnOrientationChange(const IntRect& rect) {
67 return rect.Width() != rect.Height();
68 }
69
AsyncCompositionManager(CompositorBridgeParent * aParent,HostLayerManager * aManager)70 AsyncCompositionManager::AsyncCompositionManager(
71 CompositorBridgeParent* aParent, HostLayerManager* aManager)
72 : mLayerManager(aManager),
73 mIsFirstPaint(true),
74 mLayersUpdated(false),
75 mReadyForCompose(true),
76 mCompositorBridge(aParent) {}
77
~AsyncCompositionManager()78 AsyncCompositionManager::~AsyncCompositionManager() {}
79
ResolveRefLayers(CompositorBridgeParent * aCompositor,bool * aHasRemoteContent,bool * aResolvePlugins)80 void AsyncCompositionManager::ResolveRefLayers(
81 CompositorBridgeParent* aCompositor, bool* aHasRemoteContent,
82 bool* aResolvePlugins) {
83 if (aHasRemoteContent) {
84 *aHasRemoteContent = false;
85 }
86
87 #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
88 // If valid *aResolvePlugins indicates if we need to update plugin geometry
89 // when we walk the tree.
90 bool resolvePlugins = (aCompositor && aResolvePlugins && *aResolvePlugins);
91 #endif
92
93 if (!mLayerManager->GetRoot()) {
94 // Updated the return value since this result controls completing
95 // composition.
96 if (aResolvePlugins) {
97 *aResolvePlugins = false;
98 }
99 return;
100 }
101
102 mReadyForCompose = true;
103 bool hasRemoteContent = false;
104 bool didResolvePlugins = false;
105
106 ForEachNode<ForwardIterator>(mLayerManager->GetRoot(), [&](Layer* layer) {
107 RefLayer* refLayer = layer->AsRefLayer();
108 if (!refLayer) {
109 return;
110 }
111
112 hasRemoteContent = true;
113 const CompositorBridgeParent::LayerTreeState* state =
114 CompositorBridgeParent::GetIndirectShadowTree(
115 refLayer->GetReferentId());
116 if (!state) {
117 return;
118 }
119
120 Layer* referent = state->mRoot;
121 if (!referent) {
122 return;
123 }
124
125 if (!refLayer->GetLocalVisibleRegion().IsEmpty()) {
126 dom::ScreenOrientationInternal chromeOrientation =
127 mTargetConfig.orientation();
128 dom::ScreenOrientationInternal contentOrientation =
129 state->mTargetConfig.orientation();
130 if (!IsSameDimension(chromeOrientation, contentOrientation) &&
131 ContentMightReflowOnOrientationChange(
132 mTargetConfig.naturalBounds())) {
133 mReadyForCompose = false;
134 }
135 }
136
137 refLayer->ConnectReferentLayer(referent);
138
139 #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
140 if (resolvePlugins) {
141 didResolvePlugins |=
142 aCompositor->UpdatePluginWindowState(refLayer->GetReferentId());
143 }
144 #endif
145 });
146
147 if (aHasRemoteContent) {
148 *aHasRemoteContent = hasRemoteContent;
149 }
150 if (aResolvePlugins) {
151 *aResolvePlugins = didResolvePlugins;
152 }
153 }
154
DetachRefLayers()155 void AsyncCompositionManager::DetachRefLayers() {
156 if (!mLayerManager->GetRoot()) {
157 return;
158 }
159
160 mReadyForCompose = false;
161
162 ForEachNodePostOrder<ForwardIterator>(
163 mLayerManager->GetRoot(), [&](Layer* layer) {
164 RefLayer* refLayer = layer->AsRefLayer();
165 if (!refLayer) {
166 return;
167 }
168
169 const CompositorBridgeParent::LayerTreeState* state =
170 CompositorBridgeParent::GetIndirectShadowTree(
171 refLayer->GetReferentId());
172 if (!state) {
173 return;
174 }
175
176 Layer* referent = state->mRoot;
177 if (referent) {
178 refLayer->DetachReferentLayer(referent);
179 }
180 });
181 }
182
ComputeRotation()183 void AsyncCompositionManager::ComputeRotation() {
184 if (!mTargetConfig.naturalBounds().IsEmpty()) {
185 mWorldTransform = ComputeTransformForRotation(mTargetConfig.naturalBounds(),
186 mTargetConfig.rotation());
187 }
188 }
189
190 #ifdef DEBUG
GetBaseTransform(Layer * aLayer,Matrix4x4 * aTransform)191 static void GetBaseTransform(Layer* aLayer, Matrix4x4* aTransform) {
192 // Start with the animated transform if there is one
193 *aTransform = (aLayer->AsHostLayer()->GetShadowTransformSetByAnimation()
194 ? aLayer->GetLocalTransform()
195 : aLayer->GetTransform());
196 }
197 #endif
198
TransformClipRect(Layer * aLayer,const ParentLayerToParentLayerMatrix4x4 & aTransform)199 static void TransformClipRect(
200 Layer* aLayer, const ParentLayerToParentLayerMatrix4x4& aTransform) {
201 MOZ_ASSERT(aTransform.Is2D());
202 const Maybe<ParentLayerIntRect>& clipRect =
203 aLayer->AsHostLayer()->GetShadowClipRect();
204 if (clipRect) {
205 ParentLayerIntRect transformed = TransformBy(aTransform, *clipRect);
206 aLayer->AsHostLayer()->SetShadowClipRect(Some(transformed));
207 }
208 }
209
210 // Similar to TransformFixedClip(), but only transforms the fixed part of the
211 // clip.
TransformFixedClip(Layer * aLayer,const ParentLayerToParentLayerMatrix4x4 & aTransform,AsyncCompositionManager::ClipParts & aClipParts)212 static void TransformFixedClip(
213 Layer* aLayer, const ParentLayerToParentLayerMatrix4x4& aTransform,
214 AsyncCompositionManager::ClipParts& aClipParts) {
215 MOZ_ASSERT(aTransform.Is2D());
216 if (aClipParts.mFixedClip) {
217 *aClipParts.mFixedClip = TransformBy(aTransform, *aClipParts.mFixedClip);
218 aLayer->AsHostLayer()->SetShadowClipRect(aClipParts.Intersect());
219 }
220 }
221
222 /**
223 * Set the given transform as the shadow transform on the layer, assuming
224 * that the given transform already has the pre- and post-scales applied.
225 * That is, this function cancels out the pre- and post-scales from aTransform
226 * before setting it as the shadow transform on the layer, so that when
227 * the layer's effective transform is computed, the pre- and post-scales will
228 * only be applied once.
229 */
SetShadowTransform(Layer * aLayer,LayerToParentLayerMatrix4x4 aTransform)230 static void SetShadowTransform(Layer* aLayer,
231 LayerToParentLayerMatrix4x4 aTransform) {
232 if (ContainerLayer* c = aLayer->AsContainerLayer()) {
233 aTransform.PreScale(1.0f / c->GetPreXScale(), 1.0f / c->GetPreYScale(), 1);
234 }
235 aTransform.PostScale(1.0f / aLayer->GetPostXScale(),
236 1.0f / aLayer->GetPostYScale(), 1);
237 aLayer->AsHostLayer()->SetShadowBaseTransform(aTransform.ToUnknownMatrix());
238 }
239
TranslateShadowLayer(Layer * aLayer,const ParentLayerPoint & aTranslation,bool aAdjustClipRect,AsyncCompositionManager::ClipPartsCache * aClipPartsCache)240 static void TranslateShadowLayer(
241 Layer* aLayer, const ParentLayerPoint& aTranslation, bool aAdjustClipRect,
242 AsyncCompositionManager::ClipPartsCache* aClipPartsCache) {
243 // This layer might also be a scrollable layer and have an async transform.
244 // To make sure we don't clobber that, we start with the shadow transform.
245 // (i.e. GetLocalTransform() instead of GetTransform()).
246 // Note that the shadow transform is reset on every frame of composition so
247 // we don't have to worry about the adjustments compounding over successive
248 // frames.
249 LayerToParentLayerMatrix4x4 layerTransform = aLayer->GetLocalTransformTyped();
250
251 // Apply the translation to the layer transform.
252 layerTransform.PostTranslate(aTranslation);
253
254 SetShadowTransform(aLayer, layerTransform);
255 aLayer->AsHostLayer()->SetShadowTransformSetByAnimation(false);
256
257 if (aAdjustClipRect) {
258 auto transform =
259 ParentLayerToParentLayerMatrix4x4::Translation(aTranslation);
260 // If we're passed a clip parts cache, only transform the fixed part of
261 // the clip.
262 if (aClipPartsCache) {
263 auto iter = aClipPartsCache->find(aLayer);
264 MOZ_ASSERT(iter != aClipPartsCache->end());
265 TransformFixedClip(aLayer, transform, iter->second);
266 } else {
267 TransformClipRect(aLayer, transform);
268 }
269
270 // If a fixed- or sticky-position layer has a mask layer, that mask should
271 // move along with the layer, so apply the translation to the mask layer
272 // too.
273 if (Layer* maskLayer = aLayer->GetMaskLayer()) {
274 TranslateShadowLayer(maskLayer, aTranslation, false, aClipPartsCache);
275 }
276 }
277 }
278
279 #ifdef DEBUG
AccumulateLayerTransforms(Layer * aLayer,Layer * aAncestor,Matrix4x4 & aMatrix)280 static void AccumulateLayerTransforms(Layer* aLayer, Layer* aAncestor,
281 Matrix4x4& aMatrix) {
282 // Accumulate the transforms between this layer and the subtree root layer.
283 for (Layer* l = aLayer; l && l != aAncestor; l = l->GetParent()) {
284 Matrix4x4 transform;
285 GetBaseTransform(l, &transform);
286 aMatrix *= transform;
287 }
288 }
289 #endif
290
GetLayerFixedMarginsOffset(Layer * aLayer,const ScreenMargin & aFixedLayerMargins)291 static LayerPoint GetLayerFixedMarginsOffset(
292 Layer* aLayer, const ScreenMargin& aFixedLayerMargins) {
293 // Work out the necessary translation, in root scrollable layer space.
294 // Because fixed layer margins are stored relative to the root scrollable
295 // layer, we can just take the difference between these values.
296 LayerPoint translation;
297 int32_t sides = aLayer->GetFixedPositionSides();
298
299 if ((sides & eSideBitsLeftRight) == eSideBitsLeftRight) {
300 translation.x += (aFixedLayerMargins.left - aFixedLayerMargins.right) / 2;
301 } else if (sides & eSideBitsRight) {
302 translation.x -= aFixedLayerMargins.right;
303 } else if (sides & eSideBitsLeft) {
304 translation.x += aFixedLayerMargins.left;
305 }
306
307 if ((sides & eSideBitsTopBottom) == eSideBitsTopBottom) {
308 translation.y += (aFixedLayerMargins.top - aFixedLayerMargins.bottom) / 2;
309 } else if (sides & eSideBitsBottom) {
310 translation.y -= aFixedLayerMargins.bottom;
311 } else if (sides & eSideBitsTop) {
312 translation.y += aFixedLayerMargins.top;
313 }
314
315 return translation;
316 }
317
IntervalOverlap(gfxFloat aTranslation,gfxFloat aMin,gfxFloat aMax)318 static gfxFloat IntervalOverlap(gfxFloat aTranslation, gfxFloat aMin,
319 gfxFloat aMax) {
320 // Determine the amount of overlap between the 1D vector |aTranslation|
321 // and the interval [aMin, aMax].
322 if (aTranslation > 0) {
323 return std::max(0.0, std::min(aMax, aTranslation) - std::max(aMin, 0.0));
324 } else {
325 return std::min(0.0, std::max(aMin, aTranslation) - std::min(aMax, 0.0));
326 }
327 }
328
329 /**
330 * Finds the metrics on |aLayer| with scroll id |aScrollId|, and returns a
331 * LayerMetricsWrapper representing the (layer, metrics) pair, or the null
332 * LayerMetricsWrapper if no matching metrics could be found.
333 */
FindMetricsWithScrollId(Layer * aLayer,FrameMetrics::ViewID aScrollId)334 static LayerMetricsWrapper FindMetricsWithScrollId(
335 Layer* aLayer, FrameMetrics::ViewID aScrollId) {
336 for (uint64_t i = 0; i < aLayer->GetScrollMetadataCount(); ++i) {
337 if (aLayer->GetFrameMetrics(i).GetScrollId() == aScrollId) {
338 return LayerMetricsWrapper(aLayer, i);
339 }
340 }
341 return LayerMetricsWrapper();
342 }
343
344 /**
345 * Checks whether the (layer, metrics) pair (aTransformedLayer,
346 * aTransformedMetrics) is on the path from |aFixedLayer| to the metrics with
347 * scroll id |aFixedWithRespectTo|, inclusive.
348 */
AsyncTransformShouldBeUnapplied(Layer * aFixedLayer,FrameMetrics::ViewID aFixedWithRespectTo,Layer * aTransformedLayer,FrameMetrics::ViewID aTransformedMetrics)349 static bool AsyncTransformShouldBeUnapplied(
350 Layer* aFixedLayer, FrameMetrics::ViewID aFixedWithRespectTo,
351 Layer* aTransformedLayer, FrameMetrics::ViewID aTransformedMetrics) {
352 LayerMetricsWrapper transformed =
353 FindMetricsWithScrollId(aTransformedLayer, aTransformedMetrics);
354 if (!transformed.IsValid()) {
355 return false;
356 }
357 // It's important to start at the bottom, because the fixed layer itself
358 // could have the transformed metrics, and they can be at the bottom.
359 LayerMetricsWrapper current(aFixedLayer,
360 LayerMetricsWrapper::StartAt::BOTTOM);
361 bool encounteredTransformedLayer = false;
362 // The transformed layer is on the path from |aFixedLayer| to the fixed-to
363 // layer if as we walk up the (layer, metrics) tree starting from
364 // |aFixedLayer|, we *first* encounter the transformed layer, and *then* (or
365 // at the same time) the fixed-to layer.
366 while (current) {
367 if (!encounteredTransformedLayer && current == transformed) {
368 encounteredTransformedLayer = true;
369 }
370 if (current.Metrics().GetScrollId() == aFixedWithRespectTo) {
371 return encounteredTransformedLayer;
372 }
373 current = current.GetParent();
374 // It's possible that we reach a layers id boundary before we reach an
375 // ancestor with the scroll id |aFixedWithRespectTo| (this could happen
376 // e.g. if the scroll frame with that scroll id uses containerless
377 // scrolling). In such a case, stop the walk, as a new layers id could
378 // have a different layer with scroll id |aFixedWithRespectTo| which we
379 // don't intend to match.
380 if (current && current.AsRefLayer() != nullptr) {
381 break;
382 }
383 }
384 return false;
385 }
386
387 // If |aLayer| is fixed or sticky, returns the scroll id of the scroll frame
388 // that it's fixed or sticky to. Otherwise, returns Nothing().
IsFixedOrSticky(Layer * aLayer)389 static Maybe<FrameMetrics::ViewID> IsFixedOrSticky(Layer* aLayer) {
390 bool isRootOfFixedSubtree = aLayer->GetIsFixedPosition() &&
391 !aLayer->GetParent()->GetIsFixedPosition();
392 if (isRootOfFixedSubtree) {
393 return Some(aLayer->GetFixedPositionScrollContainerId());
394 }
395 if (aLayer->GetIsStickyPosition()) {
396 return Some(aLayer->GetStickyScrollContainerId());
397 }
398 return Nothing();
399 }
400
AlignFixedAndStickyLayers(Layer * aTransformedSubtreeRoot,Layer * aStartTraversalAt,FrameMetrics::ViewID aTransformScrollId,const LayerToParentLayerMatrix4x4 & aPreviousTransformForRoot,const LayerToParentLayerMatrix4x4 & aCurrentTransformForRoot,const ScreenMargin & aFixedLayerMargins,ClipPartsCache * aClipPartsCache)401 void AsyncCompositionManager::AlignFixedAndStickyLayers(
402 Layer* aTransformedSubtreeRoot, Layer* aStartTraversalAt,
403 FrameMetrics::ViewID aTransformScrollId,
404 const LayerToParentLayerMatrix4x4& aPreviousTransformForRoot,
405 const LayerToParentLayerMatrix4x4& aCurrentTransformForRoot,
406 const ScreenMargin& aFixedLayerMargins, ClipPartsCache* aClipPartsCache) {
407 // We're going to be inverting |aCurrentTransformForRoot|.
408 // If it's singular, there's nothing we can do.
409 if (aCurrentTransformForRoot.IsSingular()) {
410 return;
411 }
412
413 Layer* layer = aStartTraversalAt;
414 bool needsAsyncTransformUnapplied = false;
415 if (Maybe<FrameMetrics::ViewID> fixedTo = IsFixedOrSticky(layer)) {
416 needsAsyncTransformUnapplied = AsyncTransformShouldBeUnapplied(
417 layer, *fixedTo, aTransformedSubtreeRoot, aTransformScrollId);
418 }
419
420 // We want to process all the fixed and sticky descendants of
421 // aTransformedSubtreeRoot. Once we do encounter such a descendant, we don't
422 // need to recurse any deeper because the adjustment to the fixed or sticky
423 // layer will apply to its subtree.
424 if (!needsAsyncTransformUnapplied) {
425 for (Layer* child = layer->GetFirstChild(); child;
426 child = child->GetNextSibling()) {
427 AlignFixedAndStickyLayers(aTransformedSubtreeRoot, child,
428 aTransformScrollId, aPreviousTransformForRoot,
429 aCurrentTransformForRoot, aFixedLayerMargins,
430 aClipPartsCache);
431 }
432 return;
433 }
434
435 // Insert a translation so that the position of the anchor point is the same
436 // before and after the change to the transform of aTransformedSubtreeRoot.
437
438 // A transform creates a containing block for fixed-position descendants,
439 // so there shouldn't be a transform in between the fixed layer and
440 // the subtree root layer.
441 #ifdef DEBUG
442 Matrix4x4 ancestorTransform;
443 if (layer != aTransformedSubtreeRoot) {
444 AccumulateLayerTransforms(layer->GetParent(), aTransformedSubtreeRoot,
445 ancestorTransform);
446 }
447 ancestorTransform.NudgeToIntegersFixedEpsilon();
448 MOZ_ASSERT(ancestorTransform.IsIdentity());
449 #endif
450
451 // Since we create container layers for fixed layers, there shouldn't
452 // a local CSS or OMTA transform on the fixed layer, either (any local
453 // transform would go onto a descendant layer inside the container
454 // layer).
455 #ifdef DEBUG
456 Matrix4x4 localTransform;
457 GetBaseTransform(layer, &localTransform);
458 localTransform.NudgeToIntegersFixedEpsilon();
459 MOZ_ASSERT(localTransform.IsIdentity());
460 #endif
461
462 // Now work out the translation necessary to make sure the layer doesn't
463 // move given the new sub-tree root transform.
464
465 // Get the layer's fixed anchor point, in the layer's local coordinate space
466 // (before any transform is applied).
467 LayerPoint anchor = layer->GetFixedPositionAnchor();
468
469 // Offset the layer's anchor point to make sure fixed position content
470 // respects content document fixed position margins.
471 LayerPoint offsetAnchor =
472 anchor + GetLayerFixedMarginsOffset(layer, aFixedLayerMargins);
473
474 // Additionally transform the anchor to compensate for the change
475 // from the old transform to the new transform. We do
476 // this by using the old transform to take the offset anchor back into
477 // subtree root space, and then the inverse of the new transform
478 // to bring it back to layer space.
479 ParentLayerPoint offsetAnchorInSubtreeRootSpace =
480 aPreviousTransformForRoot.TransformPoint(offsetAnchor);
481 LayerPoint transformedAnchor =
482 aCurrentTransformForRoot.Inverse().TransformPoint(
483 offsetAnchorInSubtreeRootSpace);
484
485 // We want to translate the layer by the difference between
486 // |transformedAnchor| and |anchor|.
487 LayerPoint translation = transformedAnchor - anchor;
488
489 // A fixed layer will "consume" (be unadjusted by) the entire translation
490 // calculated above. A sticky layer may consume all, part, or none of it,
491 // depending on where we are relative to its sticky scroll range.
492 // The remainder of the translation (the unconsumed portion) needs to
493 // be propagated to descendant fixed/sticky layers.
494 LayerPoint unconsumedTranslation;
495
496 if (layer->GetIsStickyPosition()) {
497 // For sticky positioned layers, the difference between the two rectangles
498 // defines a pair of translation intervals in each dimension through which
499 // the layer should not move relative to the scroll container. To
500 // accomplish this, we limit each dimension of the |translation| to that
501 // part of it which overlaps those intervals.
502 const LayerRectAbsolute& stickyOuter = layer->GetStickyScrollRangeOuter();
503 const LayerRectAbsolute& stickyInner = layer->GetStickyScrollRangeInner();
504
505 LayerPoint originalTranslation = translation;
506 translation.y =
507 IntervalOverlap(translation.y, stickyOuter.Y(), stickyOuter.YMost()) -
508 IntervalOverlap(translation.y, stickyInner.Y(), stickyInner.YMost());
509 translation.x =
510 IntervalOverlap(translation.x, stickyOuter.X(), stickyOuter.XMost()) -
511 IntervalOverlap(translation.x, stickyInner.X(), stickyInner.XMost());
512 unconsumedTranslation = translation - originalTranslation;
513 }
514
515 // Finally, apply the translation to the layer transform. Note that in cases
516 // where the async transform on |aTransformedSubtreeRoot| affects this layer's
517 // clip rect, we need to apply the same translation to said clip rect, so
518 // that the effective transform on the clip rect takes it back to where it was
519 // originally, had there been no async scroll.
520 TranslateShadowLayer(
521 layer,
522 ViewAs<ParentLayerPixel>(translation,
523 PixelCastJustification::NoTransformOnLayer),
524 true, aClipPartsCache);
525
526 // Propragate the unconsumed portion of the translation to descendant
527 // fixed/sticky layers.
528 if (unconsumedTranslation != LayerPoint()) {
529 // Take the computations we performed to derive |translation| from
530 // |aCurrentTransformForRoot|, and perform them in reverse, keeping other
531 // quantities fixed, to come up with a new transform |newTransform| that
532 // would produce |unconsumedTranslation|.
533 LayerPoint newTransformedAnchor = unconsumedTranslation + anchor;
534 ParentLayerPoint newTransformedAnchorInSubtreeRootSpace =
535 aPreviousTransformForRoot.TransformPoint(newTransformedAnchor);
536 LayerToParentLayerMatrix4x4 newTransform = aPreviousTransformForRoot;
537 newTransform.PostTranslate(newTransformedAnchorInSubtreeRootSpace -
538 offsetAnchorInSubtreeRootSpace);
539
540 // Propagate this new transform to our descendants as the new value of
541 // |aCurrentTransformForRoot|. This allows them to consume the unconsumed
542 // translation.
543 for (Layer* child = layer->GetFirstChild(); child;
544 child = child->GetNextSibling()) {
545 AlignFixedAndStickyLayers(aTransformedSubtreeRoot, child,
546 aTransformScrollId, aPreviousTransformForRoot,
547 newTransform, aFixedLayerMargins,
548 aClipPartsCache);
549 }
550 }
551 }
552
ApplyAnimatedValue(Layer * aLayer,CompositorAnimationStorage * aStorage,nsCSSPropertyID aProperty,const AnimationData & aAnimationData,const AnimationValue & aValue)553 static void ApplyAnimatedValue(Layer* aLayer,
554 CompositorAnimationStorage* aStorage,
555 nsCSSPropertyID aProperty,
556 const AnimationData& aAnimationData,
557 const AnimationValue& aValue) {
558 if (aValue.IsNull()) {
559 // Return gracefully if we have no valid AnimationValue.
560 return;
561 }
562
563 HostLayer* layerCompositor = aLayer->AsHostLayer();
564 switch (aProperty) {
565 case eCSSProperty_opacity: {
566 layerCompositor->SetShadowOpacity(aValue.GetOpacity());
567 layerCompositor->SetShadowOpacitySetByAnimation(true);
568 aStorage->SetAnimatedValue(aLayer->GetCompositorAnimationsId(),
569 aValue.GetOpacity());
570
571 break;
572 }
573 case eCSSProperty_transform: {
574 RefPtr<const nsCSSValueSharedList> list = aValue.GetTransformList();
575 const TransformData& transformData = aAnimationData.get_TransformData();
576 nsPoint origin = transformData.origin();
577 // we expect all our transform data to arrive in device pixels
578 Point3D transformOrigin = transformData.transformOrigin();
579 nsDisplayTransform::FrameTransformProperties props(Move(list),
580 transformOrigin);
581
582 Matrix4x4 transform = nsDisplayTransform::GetResultingTransformMatrix(
583 props, origin, transformData.appUnitsPerDevPixel(), 0,
584 &transformData.bounds());
585 Matrix4x4 frameTransform = transform;
586
587 // If our parent layer is a perspective layer, then the offset into
588 // reference frame coordinates is already on that layer. If not, then we
589 // need to ask for it to be added here.
590 if (!aLayer->GetParent() ||
591 !aLayer->GetParent()->GetTransformIsPerspective()) {
592 nsLayoutUtils::PostTranslate(transform, origin,
593 transformData.appUnitsPerDevPixel(), true);
594 }
595
596 if (ContainerLayer* c = aLayer->AsContainerLayer()) {
597 transform.PostScale(c->GetInheritedXScale(), c->GetInheritedYScale(),
598 1);
599 }
600
601 layerCompositor->SetShadowBaseTransform(transform);
602 layerCompositor->SetShadowTransformSetByAnimation(true);
603 aStorage->SetAnimatedValue(aLayer->GetCompositorAnimationsId(),
604 Move(transform), Move(frameTransform),
605 transformData);
606 break;
607 }
608 default:
609 MOZ_ASSERT_UNREACHABLE("Unhandled animated property");
610 }
611 }
612
SampleAnimations(Layer * aLayer,CompositorAnimationStorage * aStorage,TimeStamp aTime,uint64_t * aLayerAreaAnimated)613 static AnimationProcessTypes SampleAnimations(
614 Layer* aLayer, CompositorAnimationStorage* aStorage, TimeStamp aTime,
615 uint64_t* aLayerAreaAnimated) {
616 // This tracks the first-encountered RefLayer in the layer tree. Since we are
617 // doing a depth-first traversal, it is set to a non-null value if and only if
618 // the currently-being-traversed node has a RefLayer ancestor. In the case of
619 // nested RefLayers it points to the rootmost RefLayer.
620 RefLayer* ancestorRefLayer = nullptr;
621
622 // This bitfield-enum tracks which processes have active animations. Anything
623 // "above" the |ancestorRefLayer| in the layer tree is assumed to be the
624 // chrome process, and anything "below" is assumed to be the content process.
625 AnimationProcessTypes animProcess = AnimationProcessTypes::eNone;
626
627 ForEachNode<ForwardIterator>(
628 aLayer,
629 [&](Layer* layer) {
630 if (!ancestorRefLayer) {
631 ancestorRefLayer = layer->AsRefLayer();
632 }
633
634 bool hasInEffectAnimations = false;
635 AnimationValue animationValue = layer->GetBaseAnimationStyle();
636 if (AnimationHelper::SampleAnimationForEachNode(
637 aTime, layer->GetAnimations(), layer->GetAnimationData(),
638 animationValue, hasInEffectAnimations)) {
639 animProcess |= (ancestorRefLayer ? AnimationProcessTypes::eContent
640 : AnimationProcessTypes::eChrome);
641 }
642 if (hasInEffectAnimations) {
643 Animation& animation = layer->GetAnimations().LastElement();
644 ApplyAnimatedValue(layer, aStorage, animation.property(),
645 animation.data(), animationValue);
646 if (aLayerAreaAnimated) {
647 *aLayerAreaAnimated += (layer->GetVisibleRegion().Area());
648 }
649 }
650 },
651 [&ancestorRefLayer](Layer* aLayer) {
652 // If we're unwinding up past the rootmost RefLayer, clear our pointer
653 if (ancestorRefLayer && aLayer->AsRefLayer() == ancestorRefLayer) {
654 ancestorRefLayer = nullptr;
655 }
656 });
657
658 return animProcess;
659 }
660
SampleAPZAnimations(const LayerMetricsWrapper & aLayer,TimeStamp aSampleTime)661 static bool SampleAPZAnimations(const LayerMetricsWrapper& aLayer,
662 TimeStamp aSampleTime) {
663 bool activeAnimations = false;
664
665 ForEachNodePostOrder<ForwardIterator>(
666 aLayer,
667 [&activeAnimations, &aSampleTime](LayerMetricsWrapper aLayerMetrics) {
668 if (AsyncPanZoomController* apzc = aLayerMetrics.GetApzc()) {
669 apzc->ReportCheckerboard(aSampleTime);
670 activeAnimations |= apzc->AdvanceAnimations(aSampleTime);
671 }
672 });
673
674 return activeAnimations;
675 }
676
RecordShadowTransforms(Layer * aLayer)677 void AsyncCompositionManager::RecordShadowTransforms(Layer* aLayer) {
678 MOZ_ASSERT(gfxPrefs::CollectScrollTransforms());
679 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
680
681 ForEachNodePostOrder<ForwardIterator>(aLayer, [this](Layer* layer) {
682 for (uint32_t i = 0; i < layer->GetScrollMetadataCount(); i++) {
683 AsyncPanZoomController* apzc = layer->GetAsyncPanZoomController(i);
684 if (!apzc) {
685 continue;
686 }
687 gfx::Matrix4x4 shadowTransform =
688 layer->AsHostLayer()->GetShadowBaseTransform();
689 if (!shadowTransform.Is2D()) {
690 continue;
691 }
692
693 Matrix transform = shadowTransform.As2D();
694 if (transform.IsTranslation() && !shadowTransform.IsIdentity()) {
695 Point translation = transform.GetTranslation();
696 mLayerTransformRecorder.RecordTransform(layer, translation);
697 return;
698 }
699 }
700 });
701 }
702
AdjustForClip(const AsyncTransformComponentMatrix & asyncTransform,Layer * aLayer)703 static AsyncTransformComponentMatrix AdjustForClip(
704 const AsyncTransformComponentMatrix& asyncTransform, Layer* aLayer) {
705 AsyncTransformComponentMatrix result = asyncTransform;
706
707 // Container layers start at the origin, but they are clipped to where they
708 // actually have content on the screen. The tree transform is meant to apply
709 // to the clipped area. If the tree transform includes a scale component,
710 // then applying it to container as-is will produce incorrect results. To
711 // avoid this, translate the layer so that the clip rect starts at the origin,
712 // apply the tree transform, and translate back.
713 if (const Maybe<ParentLayerIntRect>& shadowClipRect =
714 aLayer->AsHostLayer()->GetShadowClipRect()) {
715 if (shadowClipRect->TopLeft() !=
716 ParentLayerIntPoint()) { // avoid a gratuitous change of basis
717 result.ChangeBasis(shadowClipRect->X(), shadowClipRect->Y(), 0);
718 }
719 }
720 return result;
721 }
722
ExpandRootClipRect(Layer * aLayer,const ScreenMargin & aFixedLayerMargins)723 static void ExpandRootClipRect(Layer* aLayer,
724 const ScreenMargin& aFixedLayerMargins) {
725 // For Fennec we want to expand the root scrollable layer clip rect based on
726 // the fixed position margins. In particular, we want this while the dynamic
727 // toolbar is in the process of sliding offscreen and the area of the
728 // LayerView visible to the user is larger than the viewport size that Gecko
729 // knows about (and therefore larger than the clip rect). We could also just
730 // clear the clip rect on aLayer entirely but this seems more precise.
731 Maybe<ParentLayerIntRect> rootClipRect =
732 aLayer->AsHostLayer()->GetShadowClipRect();
733 if (rootClipRect && aFixedLayerMargins != ScreenMargin()) {
734 #ifndef MOZ_WIDGET_ANDROID
735 // We should never enter here on anything other than Fennec, since
736 // aFixedLayerMargins should be empty everywhere else.
737 MOZ_ASSERT(false);
738 #endif
739 ParentLayerRect rect(rootClipRect.value());
740 rect.Deflate(ViewAs<ParentLayerPixel>(
741 aFixedLayerMargins,
742 PixelCastJustification::ScreenIsParentLayerForRoot));
743 aLayer->AsHostLayer()->SetShadowClipRect(Some(RoundedOut(rect)));
744 }
745 }
746
747 #ifdef MOZ_WIDGET_ANDROID
MoveScrollbarForLayerMargin(Layer * aRoot,FrameMetrics::ViewID aRootScrollId,const ScreenMargin & aFixedLayerMargins)748 static void MoveScrollbarForLayerMargin(
749 Layer* aRoot, FrameMetrics::ViewID aRootScrollId,
750 const ScreenMargin& aFixedLayerMargins) {
751 // See bug 1223928 comment 9 - once we can detect the RCD with just the
752 // isRootContent flag on the metrics, we can probably move this code into
753 // ApplyAsyncTransformToScrollbar rather than having it as a separate
754 // adjustment on the layer tree.
755 Layer* scrollbar =
756 BreadthFirstSearch<ReverseIterator>(aRoot, [aRootScrollId](Layer* aNode) {
757 return (aNode->GetScrollThumbData().mDirection.isSome() &&
758 *aNode->GetScrollThumbData().mDirection ==
759 ScrollDirection::eHorizontal &&
760 aNode->GetScrollbarTargetContainerId() == aRootScrollId);
761 });
762 if (scrollbar) {
763 // Shift the horizontal scrollbar down into the new space exposed by the
764 // dynamic toolbar hiding. Technically we should also scale the vertical
765 // scrollbar a bit to expand into the new space but it's not as noticeable
766 // and it would add a lot more complexity, so we're going with the "it's not
767 // worth it" justification.
768 TranslateShadowLayer(scrollbar,
769 ParentLayerPoint(0, -aFixedLayerMargins.bottom), true,
770 nullptr);
771 if (scrollbar->GetParent()) {
772 // The layer that has the HORIZONTAL direction sits inside another
773 // ContainerLayer. This ContainerLayer also has a clip rect that causes
774 // the scrollbar to get clipped. We need to expand that clip rect to
775 // prevent that from happening. This is kind of ugly in that we're
776 // assuming a particular layer tree structure but short of adding more
777 // flags to the layer there doesn't appear to be a good way to do this.
778 ExpandRootClipRect(scrollbar->GetParent(), aFixedLayerMargins);
779 }
780 }
781 }
782 #endif
783
ApplyAsyncContentTransformToTree(Layer * aLayer,bool * aOutFoundRoot)784 bool AsyncCompositionManager::ApplyAsyncContentTransformToTree(
785 Layer* aLayer, bool* aOutFoundRoot) {
786 bool appliedTransform = false;
787 std::stack<Maybe<ParentLayerIntRect>> stackDeferredClips;
788
789 // Maps layers to their ClipParts. The parts are not stored individually
790 // on the layer, but during AlignFixedAndStickyLayers we need access to
791 // the individual parts for descendant layers.
792 ClipPartsCache clipPartsCache;
793
794 ForEachNode<ForwardIterator>(
795 aLayer,
796 [&stackDeferredClips](Layer* layer) {
797 stackDeferredClips.push(Maybe<ParentLayerIntRect>());
798 },
799 [this, &aOutFoundRoot, &stackDeferredClips, &appliedTransform,
800 &clipPartsCache](Layer* layer) {
801 Maybe<ParentLayerIntRect> clipDeferredFromChildren =
802 stackDeferredClips.top();
803 stackDeferredClips.pop();
804 LayerToParentLayerMatrix4x4 oldTransform =
805 layer->GetTransformTyped() * AsyncTransformMatrix();
806
807 AsyncTransformComponentMatrix combinedAsyncTransform;
808 bool hasAsyncTransform = false;
809 // Only set on the root layer for Android.
810 ScreenMargin fixedLayerMargins;
811
812 // Each layer has multiple clips:
813 // - Its local clip, which is fixed to the layer contents, i.e. it
814 // moves with those async transforms which the layer contents move
815 // with.
816 // - Its scrolled clip, which moves with all async transforms.
817 // - For each ScrollMetadata on the layer, a scroll clip. This
818 // includes the composition bounds and any other clips induced by
819 // layout. This moves with async transforms from ScrollMetadatas
820 // above it.
821 // In this function, these clips are combined into two shadow clip
822 // parts:
823 // - The fixed clip, which consists of the local clip only, initially
824 // transformed by all async transforms.
825 // - The scrolled clip, which consists of the other clips, transformed
826 // by the appropriate transforms.
827 // These two parts are kept separate for now, because for fixed layers,
828 // we need to adjust the fixed clip (to cancel out some async
829 // transforms). The parts are kept in a cache which is cleared at the
830 // beginning of every composite. The final shadow clip for the layer is
831 // the intersection of the (possibly adjusted) fixed clip and the
832 // scrolled clip.
833 ClipParts& clipParts = clipPartsCache[layer];
834 clipParts.mFixedClip = layer->GetClipRect();
835 clipParts.mScrolledClip = layer->GetScrolledClipRect();
836
837 // If we are a perspective transform ContainerLayer, apply the clip
838 // deferred from our child (if there is any) before we iterate over our
839 // frame metrics, because this clip is subject to all async transforms
840 // of this layer. Since this clip came from the a scroll clip on the
841 // child, it becomes part of our scrolled clip.
842 clipParts.mScrolledClip = IntersectMaybeRects(clipDeferredFromChildren,
843 clipParts.mScrolledClip);
844
845 // The transform of a mask layer is relative to the masked layer's
846 // parent layer. So whenever we apply an async transform to a layer, we
847 // need to apply that same transform to the layer's own mask layer. A
848 // layer can also have "ancestor" mask layers for any rounded clips from
849 // its ancestor scroll frames. A scroll frame mask layer only needs to
850 // be async transformed for async scrolls of this scroll frame's
851 // ancestor scroll frames, not for async scrolls of this scroll frame
852 // itself. In the loop below, we iterate over scroll frames from inside
853 // to outside. At each iteration, this array contains the layer's
854 // ancestor mask layers of all scroll frames inside the current one.
855 nsTArray<Layer*> ancestorMaskLayers;
856
857 // The layer's scrolled clip can have an ancestor mask layer as well,
858 // which is moved by all async scrolls on this layer.
859 if (const Maybe<LayerClip>& scrolledClip = layer->GetScrolledClip()) {
860 if (scrolledClip->GetMaskLayerIndex()) {
861 ancestorMaskLayers.AppendElement(layer->GetAncestorMaskLayerAt(
862 *scrolledClip->GetMaskLayerIndex()));
863 }
864 }
865
866 for (uint32_t i = 0; i < layer->GetScrollMetadataCount(); i++) {
867 AsyncPanZoomController* controller =
868 layer->GetAsyncPanZoomController(i);
869 if (!controller) {
870 continue;
871 }
872
873 hasAsyncTransform = true;
874
875 AsyncTransform asyncTransformWithoutOverscroll =
876 controller->GetCurrentAsyncTransform(
877 AsyncPanZoomController::eForCompositing);
878 AsyncTransformComponentMatrix overscrollTransform =
879 controller->GetOverscrollTransform(
880 AsyncPanZoomController::eForCompositing);
881 AsyncTransformComponentMatrix asyncTransform =
882 AsyncTransformComponentMatrix(asyncTransformWithoutOverscroll) *
883 overscrollTransform;
884
885 if (!layer->IsScrollableWithoutContent()) {
886 controller->MarkAsyncTransformAppliedToContent();
887 }
888
889 const ScrollMetadata& scrollMetadata = layer->GetScrollMetadata(i);
890 const FrameMetrics& metrics = scrollMetadata.GetMetrics();
891
892 #if defined(MOZ_WIDGET_ANDROID)
893 // If we find a metrics which is the root content doc, use that. If
894 // not, use the root layer. Since this function recurses on children
895 // first we should only end up using the root layer if the entire tree
896 // was devoid of a root content metrics. This is a temporary solution;
897 // in the long term we should not need the root content metrics at
898 // all. See bug 1201529 comment 6 for details.
899 if (!(*aOutFoundRoot)) {
900 *aOutFoundRoot =
901 metrics.IsRootContent() || /* RCD */
902 (layer->GetParent() == nullptr && /* rootmost metrics */
903 i + 1 >= layer->GetScrollMetadataCount());
904 if (*aOutFoundRoot) {
905 mRootScrollableId = metrics.GetScrollId();
906 Compositor* compositor = mLayerManager->GetCompositor();
907 if (CompositorBridgeParent* bridge =
908 compositor->GetCompositorBridgeParent()) {
909 AndroidDynamicToolbarAnimator* animator =
910 bridge->GetAndroidDynamicToolbarAnimator();
911 MOZ_ASSERT(animator);
912 if (mIsFirstPaint) {
913 animator->UpdateRootFrameMetrics(metrics);
914 animator->FirstPaint();
915 mIsFirstPaint = false;
916 }
917 if (mLayersUpdated) {
918 animator->NotifyLayersUpdated();
919 mLayersUpdated = false;
920 }
921 // If this is not actually the root content then the animator is
922 // not getting updated in
923 // AsyncPanZoomController::NotifyLayersUpdated because the root
924 // content document is not scrollable. So update it here so it
925 // knows if the root composition size has changed.
926 if (!metrics.IsRootContent()) {
927 animator->MaybeUpdateCompositionSizeAndRootFrameMetrics(
928 metrics);
929 }
930 }
931 fixedLayerMargins = mFixedLayerMargins;
932 }
933 }
934 #else
935 *aOutFoundRoot = false;
936 // Non-Android platforms still care about this flag being cleared
937 // after the first call to TransformShadowTree().
938 mIsFirstPaint = false;
939 #endif
940
941 // Transform the current local clips by this APZC's async transform.
942 // If we're using containerful scrolling, then the clip is not part of
943 // the scrolled frame and should not be transformed.
944 if (!scrollMetadata.UsesContainerScrolling()) {
945 MOZ_ASSERT(asyncTransform.Is2D());
946 if (clipParts.mFixedClip) {
947 *clipParts.mFixedClip =
948 TransformBy(asyncTransform, *clipParts.mFixedClip);
949 }
950 if (clipParts.mScrolledClip) {
951 *clipParts.mScrolledClip =
952 TransformBy(asyncTransform, *clipParts.mScrolledClip);
953 }
954 }
955 // Note: we don't set the layer's shadow clip rect property yet;
956 // AlignFixedAndStickyLayers will use the clip parts from the clip
957 // parts cache.
958
959 combinedAsyncTransform *= asyncTransform;
960
961 // For the purpose of aligning fixed and sticky layers, we disregard
962 // the overscroll transform as well as any OMTA transform when
963 // computing the 'aCurrentTransformForRoot' parameter. This ensures
964 // that the overscroll and OMTA transforms are not unapplied, and
965 // therefore that the visual effects apply to fixed and sticky layers.
966 // We do this by using GetTransform() as the base transform rather
967 // than GetLocalTransform(), which would include those factors.
968 LayerToParentLayerMatrix4x4 transformWithoutOverscrollOrOmta =
969 layer->GetTransformTyped() *
970 CompleteAsyncTransform(
971 AdjustForClip(asyncTransformWithoutOverscroll, layer));
972
973 AlignFixedAndStickyLayers(layer, layer, metrics.GetScrollId(),
974 oldTransform,
975 transformWithoutOverscrollOrOmta,
976 fixedLayerMargins, &clipPartsCache);
977
978 // Combine the local clip with the ancestor scrollframe clip. This is
979 // not included in the async transform above, since the ancestor clip
980 // should not move with this APZC.
981 if (scrollMetadata.HasScrollClip()) {
982 ParentLayerIntRect clip = scrollMetadata.ScrollClip().GetClipRect();
983 if (layer->GetParent() &&
984 layer->GetParent()->GetTransformIsPerspective()) {
985 // If our parent layer has a perspective transform, we want to
986 // apply our scroll clip to it instead of to this layer (see bug
987 // 1168263). A layer with a perspective transform shouldn't have
988 // multiple children with FrameMetrics, nor a child with multiple
989 // FrameMetrics. (A child with multiple FrameMetrics would mean
990 // that there's *another* scrollable element between the one with
991 // the CSS perspective and the transformed element. But you'd have
992 // to use preserve-3d on the inner scrollable element in order to
993 // have the perspective apply to the transformed child, and
994 // preserve-3d is not supported on scrollable elements, so this
995 // case can't occur.)
996 MOZ_ASSERT(!stackDeferredClips.top());
997 stackDeferredClips.top().emplace(clip);
998 } else {
999 clipParts.mScrolledClip =
1000 IntersectMaybeRects(Some(clip), clipParts.mScrolledClip);
1001 }
1002 }
1003
1004 // Do the same for the ancestor mask layers: ancestorMaskLayers
1005 // contains the ancestor mask layers for scroll frames *inside* the
1006 // current scroll frame, so these are the ones we need to shift by our
1007 // async transform.
1008 for (Layer* ancestorMaskLayer : ancestorMaskLayers) {
1009 SetShadowTransform(
1010 ancestorMaskLayer,
1011 ancestorMaskLayer->GetLocalTransformTyped() * asyncTransform);
1012 }
1013
1014 // Append the ancestor mask layer for this scroll frame to
1015 // ancestorMaskLayers.
1016 if (scrollMetadata.HasScrollClip()) {
1017 const LayerClip& scrollClip = scrollMetadata.ScrollClip();
1018 if (scrollClip.GetMaskLayerIndex()) {
1019 size_t maskLayerIndex = scrollClip.GetMaskLayerIndex().value();
1020 Layer* ancestorMaskLayer =
1021 layer->GetAncestorMaskLayerAt(maskLayerIndex);
1022 ancestorMaskLayers.AppendElement(ancestorMaskLayer);
1023 }
1024 }
1025 }
1026
1027 bool clipChanged = (hasAsyncTransform || clipDeferredFromChildren ||
1028 layer->GetScrolledClipRect());
1029 if (clipChanged) {
1030 // Intersect the two clip parts and apply them to the layer.
1031 // During ApplyAsyncContentTransformTree on an ancestor layer,
1032 // AlignFixedAndStickyLayers may overwrite this with a new clip it
1033 // computes from the clip parts, but if that doesn't happen, this
1034 // is the layer's final clip rect.
1035 layer->AsHostLayer()->SetShadowClipRect(clipParts.Intersect());
1036 }
1037
1038 if (hasAsyncTransform) {
1039 // Apply the APZ transform on top of GetLocalTransform() here (rather
1040 // than GetTransform()) in case the OMTA code in SampleAnimations
1041 // already set a shadow transform; in that case we want to apply ours
1042 // on top of that one rather than clobber it.
1043 SetShadowTransform(layer,
1044 layer->GetLocalTransformTyped() *
1045 AdjustForClip(combinedAsyncTransform, layer));
1046
1047 // Do the same for the layer's own mask layer, if it has one.
1048 if (Layer* maskLayer = layer->GetMaskLayer()) {
1049 SetShadowTransform(maskLayer, maskLayer->GetLocalTransformTyped() *
1050 combinedAsyncTransform);
1051 }
1052
1053 appliedTransform = true;
1054 }
1055
1056 ExpandRootClipRect(layer, fixedLayerMargins);
1057
1058 if (layer->GetScrollThumbData().mDirection.isSome()) {
1059 ApplyAsyncTransformToScrollbar(layer);
1060 }
1061 });
1062
1063 return appliedTransform;
1064 }
1065
LayerIsScrollbarTarget(const LayerMetricsWrapper & aTarget,Layer * aScrollbar)1066 static bool LayerIsScrollbarTarget(const LayerMetricsWrapper& aTarget,
1067 Layer* aScrollbar) {
1068 AsyncPanZoomController* apzc = aTarget.GetApzc();
1069 if (!apzc) {
1070 return false;
1071 }
1072 const FrameMetrics& metrics = aTarget.Metrics();
1073 if (metrics.GetScrollId() != aScrollbar->GetScrollbarTargetContainerId()) {
1074 return false;
1075 }
1076 return !metrics.IsScrollInfoLayer();
1077 }
1078
ApplyAsyncTransformToScrollbarForContent(Layer * aScrollbar,const LayerMetricsWrapper & aContent,bool aScrollbarIsDescendant)1079 static void ApplyAsyncTransformToScrollbarForContent(
1080 Layer* aScrollbar, const LayerMetricsWrapper& aContent,
1081 bool aScrollbarIsDescendant) {
1082 AsyncTransformComponentMatrix clipTransform;
1083
1084 LayerToParentLayerMatrix4x4 transform =
1085 AsyncCompositionManager::ComputeTransformForScrollThumb(
1086 aScrollbar->GetLocalTransformTyped(), aContent.GetTransform(),
1087 aContent.GetApzc(), aContent.Metrics(),
1088 aScrollbar->GetScrollThumbData(), aScrollbarIsDescendant,
1089 &clipTransform);
1090
1091 if (aScrollbarIsDescendant) {
1092 // We also need to make a corresponding change on the clip rect of all the
1093 // layers on the ancestor chain from the scrollbar layer up to but not
1094 // including the layer with the async transform. Otherwise the scrollbar
1095 // shifts but gets clipped and so appears to flicker.
1096 for (Layer* ancestor = aScrollbar; ancestor != aContent.GetLayer();
1097 ancestor = ancestor->GetParent()) {
1098 TransformClipRect(ancestor, clipTransform);
1099 }
1100 }
1101
1102 SetShadowTransform(aScrollbar, transform);
1103 }
1104
1105 /* static */ LayerToParentLayerMatrix4x4
ComputeTransformForScrollThumb(const LayerToParentLayerMatrix4x4 & aCurrentTransform,const Matrix4x4 & aScrollableContentTransform,AsyncPanZoomController * aApzc,const FrameMetrics & aMetrics,const ScrollThumbData & aThumbData,bool aScrollbarIsDescendant,AsyncTransformComponentMatrix * aOutClipTransform)1106 AsyncCompositionManager::ComputeTransformForScrollThumb(
1107 const LayerToParentLayerMatrix4x4& aCurrentTransform,
1108 const Matrix4x4& aScrollableContentTransform, AsyncPanZoomController* aApzc,
1109 const FrameMetrics& aMetrics, const ScrollThumbData& aThumbData,
1110 bool aScrollbarIsDescendant,
1111 AsyncTransformComponentMatrix* aOutClipTransform) {
1112 // We only apply the transform if the scroll-target layer has non-container
1113 // children (i.e. when it has some possibly-visible content). This is to
1114 // avoid moving scroll-bars in the situation that only a scroll information
1115 // layer has been built for a scroll frame, as this would result in a
1116 // disparity between scrollbars and visible content.
1117 if (aMetrics.IsScrollInfoLayer()) {
1118 return LayerToParentLayerMatrix4x4{};
1119 }
1120
1121 MOZ_RELEASE_ASSERT(aApzc);
1122
1123 AsyncTransformComponentMatrix asyncTransform =
1124 aApzc->GetCurrentAsyncTransform(AsyncPanZoomController::eForCompositing);
1125
1126 // |asyncTransform| represents the amount by which we have scrolled and
1127 // zoomed since the last paint. Because the scrollbar was sized and positioned
1128 // based on the painted content, we need to adjust it based on asyncTransform
1129 // so that it reflects what the user is actually seeing now.
1130 AsyncTransformComponentMatrix scrollbarTransform;
1131 if (*aThumbData.mDirection == ScrollDirection::eVertical) {
1132 const ParentLayerCoord asyncScrollY = asyncTransform._42;
1133 const float asyncZoomY = asyncTransform._22;
1134
1135 // The scroll thumb needs to be scaled in the direction of scrolling by the
1136 // inverse of the async zoom. This is because zooming in decreases the
1137 // fraction of the whole srollable rect that is in view.
1138 const float yScale = 1.f / asyncZoomY;
1139
1140 // Note: |metrics.GetZoom()| doesn't yet include the async zoom.
1141 const CSSToParentLayerScale effectiveZoom(aMetrics.GetZoom().yScale *
1142 asyncZoomY);
1143
1144 // Here we convert the scrollbar thumb ratio into a true unitless ratio by
1145 // dividing out the conversion factor from the scrollframe's parent's space
1146 // to the scrollframe's space.
1147 const float ratio = aThumbData.mThumbRatio /
1148 (aMetrics.GetPresShellResolution() * asyncZoomY);
1149 // The scroll thumb needs to be translated in opposite direction of the
1150 // async scroll. This is because scrolling down, which translates the layer
1151 // content up, should result in moving the scroll thumb down.
1152 ParentLayerCoord yTranslation = -asyncScrollY * ratio;
1153
1154 // The scroll thumb additionally needs to be translated to compensate for
1155 // the scale applied above. The origin with respect to which the scale is
1156 // applied is the origin of the entire scrollbar, rather than the origin of
1157 // the scroll thumb (meaning, for a vertical scrollbar it's at the top of
1158 // the composition bounds). This means that empty space above the thumb
1159 // is scaled too, effectively translating the thumb. We undo that
1160 // translation here.
1161 // (One can think of the adjustment being done to the translation here as
1162 // a change of basis. We have a method to help with that,
1163 // Matrix4x4::ChangeBasis(), but it wouldn't necessarily make the code
1164 // cleaner in this case).
1165 const CSSCoord thumbOrigin = (aMetrics.GetScrollOffset().y * ratio);
1166 const CSSCoord thumbOriginScaled = thumbOrigin * yScale;
1167 const CSSCoord thumbOriginDelta = thumbOriginScaled - thumbOrigin;
1168 const ParentLayerCoord thumbOriginDeltaPL =
1169 thumbOriginDelta * effectiveZoom;
1170 yTranslation -= thumbOriginDeltaPL;
1171
1172 if (aMetrics.IsRootContent()) {
1173 // Scrollbar for the root are painted at the same resolution as the
1174 // content. Since the coordinate space we apply this transform in includes
1175 // the resolution, we need to adjust for it as well here. Note that in
1176 // another metrics.IsRootContent() hunk below we apply a
1177 // resolution-cancelling transform which ensures the scroll thumb isn't
1178 // actually rendered at a larger scale.
1179 yTranslation *= aMetrics.GetPresShellResolution();
1180 }
1181
1182 scrollbarTransform.PostScale(1.f, yScale, 1.f);
1183 scrollbarTransform.PostTranslate(0, yTranslation, 0);
1184 }
1185 if (*aThumbData.mDirection == ScrollDirection::eHorizontal) {
1186 // See detailed comments under the VERTICAL case.
1187
1188 const ParentLayerCoord asyncScrollX = asyncTransform._41;
1189 const float asyncZoomX = asyncTransform._11;
1190
1191 const float xScale = 1.f / asyncZoomX;
1192
1193 const CSSToParentLayerScale effectiveZoom(aMetrics.GetZoom().xScale *
1194 asyncZoomX);
1195
1196 const float ratio = aThumbData.mThumbRatio /
1197 (aMetrics.GetPresShellResolution() * asyncZoomX);
1198 ParentLayerCoord xTranslation = -asyncScrollX * ratio;
1199
1200 const CSSCoord thumbOrigin = (aMetrics.GetScrollOffset().x * ratio);
1201 const CSSCoord thumbOriginScaled = thumbOrigin * xScale;
1202 const CSSCoord thumbOriginDelta = thumbOriginScaled - thumbOrigin;
1203 const ParentLayerCoord thumbOriginDeltaPL =
1204 thumbOriginDelta * effectiveZoom;
1205 xTranslation -= thumbOriginDeltaPL;
1206
1207 if (aMetrics.IsRootContent()) {
1208 xTranslation *= aMetrics.GetPresShellResolution();
1209 }
1210
1211 scrollbarTransform.PostScale(xScale, 1.f, 1.f);
1212 scrollbarTransform.PostTranslate(xTranslation, 0, 0);
1213 }
1214
1215 LayerToParentLayerMatrix4x4 transform =
1216 aCurrentTransform * scrollbarTransform;
1217
1218 AsyncTransformComponentMatrix compensation;
1219 // If the scrollbar layer is for the root then the content's resolution
1220 // applies to the scrollbar as well. Since we don't actually want the scroll
1221 // thumb's size to vary with the zoom (other than its length reflecting the
1222 // fraction of the scrollable length that's in view, which is taken care of
1223 // above), we apply a transform to cancel out this resolution.
1224 if (aMetrics.IsRootContent()) {
1225 compensation = AsyncTransformComponentMatrix::Scaling(
1226 aMetrics.GetPresShellResolution(),
1227 aMetrics.GetPresShellResolution(), 1.0f)
1228 .Inverse();
1229 }
1230 // If the scrollbar layer is a child of the content it is a scrollbar for,
1231 // then we need to adjust for any async transform (including an overscroll
1232 // transform) on the content. This needs to be cancelled out because layout
1233 // positions and sizes the scrollbar on the assumption that there is no async
1234 // transform, and without this adjustment the scrollbar will end up in the
1235 // wrong place.
1236 //
1237 // Note that since the async transform is applied on top of the content's
1238 // regular transform, we need to make sure to unapply the async transform in
1239 // the same coordinate space. This requires applying the content transform
1240 // and then unapplying it after unapplying the async transform.
1241 if (aScrollbarIsDescendant) {
1242 AsyncTransformComponentMatrix overscroll =
1243 aApzc->GetOverscrollTransform(AsyncPanZoomController::eForCompositing);
1244 Matrix4x4 asyncUntransform =
1245 (asyncTransform * overscroll).Inverse().ToUnknownMatrix();
1246 Matrix4x4 contentTransform = aScrollableContentTransform;
1247 Matrix4x4 contentUntransform = contentTransform.Inverse();
1248
1249 compensation *= ViewAs<AsyncTransformComponentMatrix>(
1250 contentTransform * asyncUntransform * contentUntransform);
1251
1252 // Pass the total compensation out to the caller so that it can use it
1253 // to transform clip transforms as needed.
1254 if (aOutClipTransform) {
1255 *aOutClipTransform = compensation;
1256 }
1257 }
1258 transform = transform * compensation;
1259
1260 return transform;
1261 }
1262
FindScrolledLayerForScrollbar(Layer * aScrollbar,bool * aOutIsAncestor)1263 static LayerMetricsWrapper FindScrolledLayerForScrollbar(Layer* aScrollbar,
1264 bool* aOutIsAncestor) {
1265 // First check if the scrolled layer is an ancestor of the scrollbar layer.
1266 LayerMetricsWrapper root(aScrollbar->Manager()->GetRoot());
1267 LayerMetricsWrapper prevAncestor(aScrollbar);
1268 LayerMetricsWrapper scrolledLayer;
1269
1270 for (LayerMetricsWrapper ancestor(aScrollbar); ancestor;
1271 ancestor = ancestor.GetParent()) {
1272 // Don't walk into remote layer trees; the scrollbar will always be in
1273 // the same layer space.
1274 if (ancestor.AsRefLayer()) {
1275 root = prevAncestor;
1276 break;
1277 }
1278 prevAncestor = ancestor;
1279
1280 if (LayerIsScrollbarTarget(ancestor, aScrollbar)) {
1281 *aOutIsAncestor = true;
1282 return ancestor;
1283 }
1284 }
1285
1286 // Search the entire layer space of the scrollbar.
1287 ForEachNode<ForwardIterator>(root, [&root, &scrolledLayer, &aScrollbar](
1288 LayerMetricsWrapper aLayerMetrics) {
1289 // Do not recurse into RefLayers, since our initial aSubtreeRoot is the
1290 // root (or RefLayer root) of a single layer space to search.
1291 if (root != aLayerMetrics && aLayerMetrics.AsRefLayer()) {
1292 return TraversalFlag::Skip;
1293 }
1294 if (LayerIsScrollbarTarget(aLayerMetrics, aScrollbar)) {
1295 scrolledLayer = aLayerMetrics;
1296 return TraversalFlag::Abort;
1297 }
1298 return TraversalFlag::Continue;
1299 });
1300 return scrolledLayer;
1301 }
1302
ApplyAsyncTransformToScrollbar(Layer * aLayer)1303 void AsyncCompositionManager::ApplyAsyncTransformToScrollbar(Layer* aLayer) {
1304 // If this layer corresponds to a scrollbar, then there should be a layer that
1305 // is a previous sibling or a parent that has a matching ViewID on its
1306 // FrameMetrics. That is the content that this scrollbar is for. We pick up
1307 // the transient async transform from that layer and use it to update the
1308 // scrollbar position. Note that it is possible that the content layer is no
1309 // longer there; in this case we don't need to do anything because there can't
1310 // be an async transform on the content.
1311 bool isAncestor = false;
1312 const LayerMetricsWrapper& scrollTarget =
1313 FindScrolledLayerForScrollbar(aLayer, &isAncestor);
1314 if (scrollTarget) {
1315 ApplyAsyncTransformToScrollbarForContent(aLayer, scrollTarget, isAncestor);
1316 }
1317 }
1318
GetFrameUniformity(FrameUniformityData * aOutData)1319 void AsyncCompositionManager::GetFrameUniformity(
1320 FrameUniformityData* aOutData) {
1321 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
1322 mLayerTransformRecorder.EndTest(aOutData);
1323 }
1324
TransformShadowTree(TimeStamp aCurrentFrame,TimeDuration aVsyncRate,TransformsToSkip aSkip)1325 bool AsyncCompositionManager::TransformShadowTree(TimeStamp aCurrentFrame,
1326 TimeDuration aVsyncRate,
1327 TransformsToSkip aSkip) {
1328 AUTO_PROFILER_LABEL("AsyncCompositionManager::TransformShadowTree", GRAPHICS);
1329
1330 Layer* root = mLayerManager->GetRoot();
1331 if (!root) {
1332 return false;
1333 }
1334
1335 CompositorAnimationStorage* storage =
1336 mCompositorBridge->GetAnimationStorage();
1337 // First, compute and set the shadow transforms from OMT animations.
1338 // NB: we must sample animations *before* sampling pan/zoom
1339 // transforms.
1340 // Use a previous vsync time to make main thread animations and compositor
1341 // more in sync with each other.
1342 // On the initial frame we use aVsyncTimestamp here so the timestamp on the
1343 // second frame are the same as the initial frame, but it does not matter.
1344 uint64_t layerAreaAnimated = 0;
1345 AnimationProcessTypes animationProcess = SampleAnimations(
1346 root, storage,
1347 !mPreviousFrameTimeStamp.IsNull() ? mPreviousFrameTimeStamp
1348 : aCurrentFrame,
1349 &layerAreaAnimated);
1350 bool wantNextFrame = (animationProcess != AnimationProcessTypes::eNone);
1351
1352 mAnimationMetricsTracker.UpdateAnimationInProgress(
1353 animationProcess, layerAreaAnimated, aVsyncRate);
1354
1355 if (!wantNextFrame) {
1356 // Clean up the CompositorAnimationStorage because
1357 // there are no active animations running
1358 storage->Clear();
1359 }
1360
1361 // Advance animations to the next expected vsync timestamp, if we can
1362 // get it.
1363 TimeStamp nextFrame = aCurrentFrame;
1364
1365 MOZ_ASSERT(aVsyncRate != TimeDuration::Forever());
1366 if (aVsyncRate != TimeDuration::Forever()) {
1367 nextFrame += aVsyncRate;
1368 }
1369
1370 #if defined(MOZ_WIDGET_ANDROID)
1371 Compositor* compositor = mLayerManager->GetCompositor();
1372 if (CompositorBridgeParent* bridge =
1373 compositor->GetCompositorBridgeParent()) {
1374 AndroidDynamicToolbarAnimator* animator =
1375 bridge->GetAndroidDynamicToolbarAnimator();
1376 MOZ_ASSERT(animator);
1377 wantNextFrame |= animator->UpdateAnimation(nextFrame);
1378 }
1379 #endif // defined(MOZ_WIDGET_ANDROID)
1380
1381 // Reset the previous time stamp if we don't already have any running
1382 // animations to avoid using the time which is far behind for newly
1383 // started animations.
1384 mPreviousFrameTimeStamp = wantNextFrame ? aCurrentFrame : TimeStamp();
1385
1386 if (!(aSkip & TransformsToSkip::APZ)) {
1387 // FIXME/bug 775437: unify this interface with the ~native-fennec
1388 // derived code
1389 //
1390 // Attempt to apply an async content transform to any layer that has
1391 // an async pan zoom controller (which means that it is rendered
1392 // async using Gecko). If this fails, fall back to transforming the
1393 // primary scrollable layer. "Failing" here means that we don't
1394 // find a frame that is async scrollable. Note that the fallback
1395 // code also includes Fennec which is rendered async. Fennec uses
1396 // its own platform-specific async rendering that is done partially
1397 // in Gecko and partially in Java.
1398 bool foundRoot = false;
1399 if (ApplyAsyncContentTransformToTree(root, &foundRoot)) {
1400 #if defined(MOZ_WIDGET_ANDROID)
1401 MOZ_ASSERT(foundRoot);
1402 if (foundRoot && mFixedLayerMargins != ScreenMargin()) {
1403 MoveScrollbarForLayerMargin(root, mRootScrollableId,
1404 mFixedLayerMargins);
1405 }
1406 #endif
1407 }
1408
1409 bool apzAnimating =
1410 SampleAPZAnimations(LayerMetricsWrapper(root), nextFrame);
1411 mAnimationMetricsTracker.UpdateApzAnimationInProgress(apzAnimating,
1412 aVsyncRate);
1413 wantNextFrame |= apzAnimating;
1414 }
1415
1416 HostLayer* rootComposite = root->AsHostLayer();
1417
1418 gfx::Matrix4x4 trans = rootComposite->GetShadowBaseTransform();
1419 trans *= gfx::Matrix4x4::From2D(mWorldTransform);
1420 rootComposite->SetShadowBaseTransform(trans);
1421
1422 if (gfxPrefs::CollectScrollTransforms()) {
1423 RecordShadowTransforms(root);
1424 }
1425
1426 return wantNextFrame;
1427 }
1428
1429 #if defined(MOZ_WIDGET_ANDROID)
SetFixedLayerMargins(ScreenIntCoord aTop,ScreenIntCoord aBottom)1430 void AsyncCompositionManager::SetFixedLayerMargins(ScreenIntCoord aTop,
1431 ScreenIntCoord aBottom) {
1432 mFixedLayerMargins.top = aTop;
1433 mFixedLayerMargins.bottom = aBottom;
1434 }
1435 #endif // defined(MOZ_WIDGET_ANDROID)
1436
1437 } // namespace layers
1438 } // namespace mozilla
1439