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