Bug 1744524: part 2) Add `WindowContext::GetUserGestureStart` and remove `WindowConte...
[gecko.git] / gfx / layers / CompositorAnimationStorage.cpp
blob0ca34f32d2d1d53e8fb04075ea046f530b766e8c
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/. */
7 #include "CompositorAnimationStorage.h"
9 #include "AnimationHelper.h"
10 #include "mozilla/gfx/MatrixFwd.h"
11 #include "mozilla/layers/APZSampler.h" // for APZSampler
12 #include "mozilla/layers/CompositorBridgeParent.h" // for CompositorBridgeParent
13 #include "mozilla/layers/CompositorThread.h" // for CompositorThreadHolder
14 #include "mozilla/layers/OMTAController.h" // for OMTAController
15 #include "mozilla/ProfilerMarkers.h"
16 #include "mozilla/ScopeExit.h"
17 #include "mozilla/ServoStyleConsts.h"
18 #include "mozilla/webrender/WebRenderTypes.h" // for ToWrTransformProperty, etc
19 #include "nsDeviceContext.h" // for AppUnitsPerCSSPixel
20 #include "nsDisplayList.h" // for nsDisplayTransform, etc
21 #include "nsLayoutUtils.h"
22 #include "TreeTraversal.h" // for ForEachNode, BreadthFirstSearch
24 namespace geckoprofiler::markers {
26 using namespace mozilla;
28 struct CompositorAnimationMarker {
29 static constexpr Span<const char> MarkerTypeName() {
30 return MakeStringSpan("CompositorAnimation");
32 static void StreamJSONMarkerData(baseprofiler::SpliceableJSONWriter& aWriter,
33 uint64_t aId, nsCSSPropertyID aProperty) {
34 aWriter.IntProperty("pid", int64_t(aId >> 32));
35 aWriter.IntProperty("id", int64_t(aId & 0xffffffff));
36 aWriter.StringProperty("property", nsCSSProps::GetStringValue(aProperty));
38 static MarkerSchema MarkerTypeDisplay() {
39 using MS = MarkerSchema;
40 MS schema{MS::Location::MarkerChart, MS::Location::MarkerTable};
41 schema.AddKeyLabelFormat("pid", "Process Id", MS::Format::Integer);
42 schema.AddKeyLabelFormat("id", "Animation Id", MS::Format::Integer);
43 schema.AddKeyLabelFormat("property", "Animated Property",
44 MS::Format::String);
45 schema.SetTableLabel("{marker.name} - {marker.data.property}");
46 return schema;
50 } // namespace geckoprofiler::markers
52 namespace mozilla {
53 namespace layers {
55 using gfx::Matrix4x4;
57 void CompositorAnimationStorage::ClearById(const uint64_t& aId) {
58 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
59 MutexAutoLock lock(mLock);
61 const auto& animationStorageData = mAnimations[aId];
62 if (animationStorageData) {
63 PROFILER_MARKER("ClearAnimation", GRAPHICS,
64 MarkerInnerWindowId(mCompositorBridge->GetInnerWindowId()),
65 CompositorAnimationMarker, aId,
66 animationStorageData->mAnimation.LastElement().mProperty);
69 mAnimatedValues.Remove(aId);
70 mAnimations.erase(aId);
73 bool CompositorAnimationStorage::HasAnimations() const {
74 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
75 MutexAutoLock lock(mLock);
77 return !mAnimations.empty();
80 AnimatedValue* CompositorAnimationStorage::GetAnimatedValue(
81 const uint64_t& aId) const {
82 mLock.AssertCurrentThreadOwns();
84 return mAnimatedValues.Get(aId);
87 OMTAValue CompositorAnimationStorage::GetOMTAValue(const uint64_t& aId) const {
88 MutexAutoLock lock(mLock);
90 OMTAValue omtaValue = mozilla::null_t();
91 auto animatedValue = GetAnimatedValue(aId);
92 if (!animatedValue) {
93 return omtaValue;
96 animatedValue->Value().match(
97 [&](const AnimationTransform& aTransform) {
98 gfx::Matrix4x4 transform = aTransform.mFrameTransform;
99 const TransformData& data = aTransform.mData;
100 float scale = data.appUnitsPerDevPixel();
101 gfx::Point3D transformOrigin = data.transformOrigin();
103 // Undo the rebasing applied by
104 // nsDisplayTransform::GetResultingTransformMatrixInternal
105 transform.ChangeBasis(-transformOrigin);
107 // Convert to CSS pixels (this undoes the operations performed by
108 // nsStyleTransformMatrix::ProcessTranslatePart which is called from
109 // nsDisplayTransform::GetResultingTransformMatrix)
110 double devPerCss = double(scale) / double(AppUnitsPerCSSPixel());
111 transform._41 *= devPerCss;
112 transform._42 *= devPerCss;
113 transform._43 *= devPerCss;
114 omtaValue = transform;
116 [&](const float& aOpacity) { omtaValue = aOpacity; },
117 [&](const nscolor& aColor) { omtaValue = aColor; });
118 return omtaValue;
121 void CompositorAnimationStorage::SetAnimatedValue(
122 uint64_t aId, AnimatedValue* aPreviousValue,
123 const gfx::Matrix4x4& aFrameTransform, const TransformData& aData) {
124 mLock.AssertCurrentThreadOwns();
126 if (!aPreviousValue) {
127 MOZ_ASSERT(!mAnimatedValues.Contains(aId));
128 mAnimatedValues.InsertOrUpdate(
129 aId,
130 MakeUnique<AnimatedValue>(gfx::Matrix4x4(), aFrameTransform, aData));
131 return;
133 MOZ_ASSERT(aPreviousValue->Is<AnimationTransform>());
134 MOZ_ASSERT(aPreviousValue == GetAnimatedValue(aId));
136 aPreviousValue->SetTransform(aFrameTransform, aData);
139 void CompositorAnimationStorage::SetAnimatedValue(uint64_t aId,
140 AnimatedValue* aPreviousValue,
141 nscolor aColor) {
142 mLock.AssertCurrentThreadOwns();
144 if (!aPreviousValue) {
145 MOZ_ASSERT(!mAnimatedValues.Contains(aId));
146 mAnimatedValues.InsertOrUpdate(aId, MakeUnique<AnimatedValue>(aColor));
147 return;
150 MOZ_ASSERT(aPreviousValue->Is<nscolor>());
151 MOZ_ASSERT(aPreviousValue == GetAnimatedValue(aId));
152 aPreviousValue->SetColor(aColor);
155 void CompositorAnimationStorage::SetAnimatedValue(uint64_t aId,
156 AnimatedValue* aPreviousValue,
157 float aOpacity) {
158 mLock.AssertCurrentThreadOwns();
160 if (!aPreviousValue) {
161 MOZ_ASSERT(!mAnimatedValues.Contains(aId));
162 mAnimatedValues.InsertOrUpdate(aId, MakeUnique<AnimatedValue>(aOpacity));
163 return;
166 MOZ_ASSERT(aPreviousValue->Is<float>());
167 MOZ_ASSERT(aPreviousValue == GetAnimatedValue(aId));
168 aPreviousValue->SetOpacity(aOpacity);
171 void CompositorAnimationStorage::SetAnimations(uint64_t aId,
172 const LayersId& aLayersId,
173 const AnimationArray& aValue) {
174 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
175 MutexAutoLock lock(mLock);
177 mAnimations[aId] = std::make_unique<AnimationStorageData>(
178 AnimationHelper::ExtractAnimations(aLayersId, aValue));
180 PROFILER_MARKER("SetAnimation", GRAPHICS,
181 MarkerInnerWindowId(mCompositorBridge->GetInnerWindowId()),
182 CompositorAnimationMarker, aId,
183 mAnimations[aId]->mAnimation.LastElement().mProperty);
185 // If there is the last animated value, then we need to store the id to remove
186 // the value if the new animation doesn't produce any animated data (i.e. in
187 // the delay phase) when we sample this new animation.
188 if (mAnimatedValues.Contains(aId)) {
189 mNewAnimations.insert(aId);
193 // Returns clip rect in the scroll frame's coordinate space.
194 static ParentLayerRect GetClipRectForPartialPrerender(
195 const LayersId aLayersId, const PartialPrerenderData& aPartialPrerenderData,
196 const RefPtr<APZSampler>& aSampler, const MutexAutoLock& aProofOfMapLock) {
197 if (aSampler &&
198 aPartialPrerenderData.scrollId() != ScrollableLayerGuid::NULL_SCROLL_ID) {
199 return aSampler->GetCompositionBounds(
200 aLayersId, aPartialPrerenderData.scrollId(), aProofOfMapLock);
203 return aPartialPrerenderData.clipRect();
206 bool CompositorAnimationStorage::SampleAnimations(
207 const OMTAController* aOMTAController, TimeStamp aPreviousFrameTime,
208 TimeStamp aCurrentFrameTime) {
209 MutexAutoLock lock(mLock);
211 bool isAnimating = false;
212 auto cleanup = MakeScopeExit([&] { mNewAnimations.clear(); });
214 // Do nothing if there are no compositor animations
215 if (mAnimations.empty()) {
216 return isAnimating;
219 std::unordered_map<LayersId, nsTArray<uint64_t>, LayersId::HashFn> janked;
221 RefPtr<APZSampler> apzSampler = mCompositorBridge->GetAPZSampler();
223 auto callback = [&](const MutexAutoLock& aProofOfMapLock) {
224 for (const auto& iter : mAnimations) {
225 const auto& animationStorageData = iter.second;
226 if (animationStorageData->mAnimation.IsEmpty()) {
227 continue;
230 isAnimating = true;
231 AutoTArray<RefPtr<RawServoAnimationValue>, 1> animationValues;
232 AnimatedValue* previousValue = GetAnimatedValue(iter.first);
233 AnimationHelper::SampleResult sampleResult =
234 AnimationHelper::SampleAnimationForEachNode(
235 apzSampler, animationStorageData->mLayersId, aProofOfMapLock,
236 aPreviousFrameTime, aCurrentFrameTime, previousValue,
237 animationStorageData->mAnimation, animationValues);
239 if (sampleResult != AnimationHelper::SampleResult::Sampled) {
240 if (mNewAnimations.find(iter.first) != mNewAnimations.end()) {
241 mAnimatedValues.Remove(iter.first);
243 continue;
246 const PropertyAnimationGroup& lastPropertyAnimationGroup =
247 animationStorageData->mAnimation.LastElement();
249 PROFILER_MARKER(
250 "SampleAnimation", GRAPHICS,
251 MarkerOptions(
252 MarkerThreadId(CompositorThreadHolder::GetThreadId()),
253 MarkerInnerWindowId(mCompositorBridge->GetInnerWindowId())),
254 CompositorAnimationMarker, iter.first,
255 lastPropertyAnimationGroup.mProperty);
257 // Store the AnimatedValue
258 switch (lastPropertyAnimationGroup.mProperty) {
259 case eCSSProperty_background_color: {
260 SetAnimatedValue(iter.first, previousValue,
261 Servo_AnimationValue_GetColor(animationValues[0],
262 NS_RGBA(0, 0, 0, 0)));
263 break;
265 case eCSSProperty_opacity: {
266 MOZ_ASSERT(animationValues.Length() == 1);
267 SetAnimatedValue(iter.first, previousValue,
268 Servo_AnimationValue_GetOpacity(animationValues[0]));
269 break;
271 case eCSSProperty_rotate:
272 case eCSSProperty_scale:
273 case eCSSProperty_translate:
274 case eCSSProperty_transform:
275 case eCSSProperty_offset_path:
276 case eCSSProperty_offset_distance:
277 case eCSSProperty_offset_rotate:
278 case eCSSProperty_offset_anchor: {
279 MOZ_ASSERT(animationStorageData->mTransformData);
281 const TransformData& transformData =
282 *animationStorageData->mTransformData;
283 MOZ_ASSERT(transformData.origin() == nsPoint());
285 gfx::Matrix4x4 frameTransform =
286 AnimationHelper::ServoAnimationValueToMatrix4x4(
287 animationValues, transformData,
288 animationStorageData->mCachedMotionPath);
290 if (const Maybe<PartialPrerenderData>& partialPrerenderData =
291 transformData.partialPrerenderData()) {
292 gfx::Matrix4x4 transform = frameTransform;
293 transform.PostTranslate(
294 partialPrerenderData->position().ToUnknownPoint());
296 gfx::Matrix4x4 transformInClip =
297 partialPrerenderData->transformInClip();
298 if (apzSampler && partialPrerenderData->scrollId() !=
299 ScrollableLayerGuid::NULL_SCROLL_ID) {
300 AsyncTransform asyncTransform =
301 apzSampler->GetCurrentAsyncTransform(
302 animationStorageData->mLayersId,
303 partialPrerenderData->scrollId(), LayoutAndVisual,
304 aProofOfMapLock);
305 transformInClip.PostTranslate(
306 asyncTransform.mTranslation.ToUnknownPoint());
308 transformInClip = transform * transformInClip;
310 ParentLayerRect clipRect = GetClipRectForPartialPrerender(
311 animationStorageData->mLayersId, *partialPrerenderData,
312 apzSampler, aProofOfMapLock);
313 if (AnimationHelper::ShouldBeJank(
314 partialPrerenderData->rect(),
315 partialPrerenderData->overflowedSides(), transformInClip,
316 clipRect)) {
317 if (previousValue) {
318 frameTransform = previousValue->Transform().mFrameTransform;
320 janked[animationStorageData->mLayersId].AppendElement(iter.first);
324 SetAnimatedValue(iter.first, previousValue, frameTransform,
325 transformData);
326 break;
328 default:
329 MOZ_ASSERT_UNREACHABLE("Unhandled animated property");
334 if (apzSampler) {
335 // Hold APZCTreeManager::mMapLock for all the animations inside this
336 // CompositorBridgeParent. This ensures that APZ cannot process a
337 // transaction on the updater thread in between sampling different
338 // animations.
339 apzSampler->CallWithMapLock(callback);
340 } else {
341 // A fallback way if we don't have |apzSampler|. We don't care about
342 // APZCTreeManager::mMapLock in this case because we don't use any APZ
343 // interface.
344 mozilla::Mutex dummy("DummyAPZMapLock");
345 MutexAutoLock lock(dummy);
346 callback(lock);
349 if (!janked.empty() && aOMTAController) {
350 aOMTAController->NotifyJankedAnimations(std::move(janked));
353 return isAnimating;
356 WrAnimations CompositorAnimationStorage::CollectWebRenderAnimations() const {
357 MutexAutoLock lock(mLock);
359 WrAnimations animations;
361 for (const auto& animatedValueEntry : mAnimatedValues) {
362 AnimatedValue* value = animatedValueEntry.GetWeak();
363 value->Value().match(
364 [&](const AnimationTransform& aTransform) {
365 animations.mTransformArrays.AppendElement(wr::ToWrTransformProperty(
366 animatedValueEntry.GetKey(), aTransform.mFrameTransform));
368 [&](const float& aOpacity) {
369 animations.mOpacityArrays.AppendElement(
370 wr::ToWrOpacityProperty(animatedValueEntry.GetKey(), aOpacity));
372 [&](const nscolor& aColor) {
373 animations.mColorArrays.AppendElement(wr::ToWrColorProperty(
374 animatedValueEntry.GetKey(),
375 ToDeviceColor(gfx::sRGBColor::FromABGR(aColor))));
379 return animations;
382 } // namespace layers
383 } // namespace mozilla