Bug 1638152 - Jank partial prerender transform animations and report the janked animations to the main-thread in each process on WebRender. r=botond,kats

Differential Revision: https://phabricator.services.mozilla.com/D83202
This commit is contained in:
Hiroyuki Ikezoe 2020-07-21 10:03:34 +00:00
Родитель c746275523
Коммит ba018e2355
11 изменённых файлов: 203 добавлений и 29 удалений

Просмотреть файл

@ -13,6 +13,7 @@
#include "mozilla/layers/CompositorThread.h" // for CompositorThreadHolder
#include "mozilla/layers/LayerManagerComposite.h" // for LayerComposite, etc
#include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper
#include "mozilla/layers/OMTAController.h" // for OMTAController
#include "mozilla/ServoStyleConsts.h"
#include "mozilla/webrender/WebRenderTypes.h" // for ToWrTransformProperty, etc
#include "nsDeviceContext.h" // for AppUnitsPerCSSPixel
@ -174,8 +175,22 @@ void CompositorAnimationStorage::SetAnimations(uint64_t aId,
}
}
bool CompositorAnimationStorage::SampleAnimations(TimeStamp aPreviousFrameTime,
TimeStamp aCurrentFrameTime) {
// Returns clip rect in the scroll frame's coordinate space.
static ParentLayerRect GetClipRectForPartialPrerender(
const LayersId aLayersId, const PartialPrerenderData& aPartialPrerenderData,
const RefPtr<APZSampler>& aSampler) {
if (aSampler &&
aPartialPrerenderData.scrollId() != ScrollableLayerGuid::NULL_SCROLL_ID) {
return aSampler->GetCompositionBounds(aLayersId,
aPartialPrerenderData.scrollId());
}
return aPartialPrerenderData.clipRect();
}
bool CompositorAnimationStorage::SampleAnimations(
const OMTAController* aOMTAController, TimeStamp aPreviousFrameTime,
TimeStamp aCurrentFrameTime) {
MutexAutoLock lock(mLock);
bool isAnimating = false;
@ -186,6 +201,10 @@ bool CompositorAnimationStorage::SampleAnimations(TimeStamp aPreviousFrameTime,
return isAnimating;
}
std::unordered_map<LayersId, nsTArray<uint64_t>, LayersId::HashFn> janked;
RefPtr<APZSampler> apzSampler = mCompositorBridge->GetAPZSampler();
for (const auto& iter : mAnimations) {
const auto& animationStorageData = iter.second;
if (animationStorageData->mAnimation.IsEmpty()) {
@ -242,6 +261,40 @@ bool CompositorAnimationStorage::SampleAnimations(TimeStamp aPreviousFrameTime,
AnimationHelper::ServoAnimationValueToMatrix4x4(
animationValues, transformData,
animationStorageData->mCachedMotionPath);
if (const Maybe<PartialPrerenderData>& partialPrerenderData =
transformData.partialPrerenderData()) {
gfx::Matrix4x4 transform = frameTransform;
transform.PostTranslate(
partialPrerenderData->position().ToUnknownPoint());
gfx::Matrix4x4 transformInClip =
partialPrerenderData->transformInClip();
if (apzSampler && partialPrerenderData->scrollId() !=
ScrollableLayerGuid::NULL_SCROLL_ID) {
AsyncTransform asyncTransform =
apzSampler->GetCurrentAsyncTransform(
animationStorageData->mLayersId,
partialPrerenderData->scrollId(), LayoutAndVisual);
transformInClip.PostTranslate(
asyncTransform.mTranslation.ToUnknownPoint());
}
transformInClip = transform * transformInClip;
ParentLayerRect clipRect =
GetClipRectForPartialPrerender(animationStorageData->mLayersId,
*partialPrerenderData, apzSampler);
if (AnimationHelper::ShouldBeJank(
partialPrerenderData->rect(),
partialPrerenderData->overflowedSides(), transformInClip,
clipRect)) {
if (previousValue) {
frameTransform = previousValue->Transform().mFrameTransform;
}
janked[animationStorageData->mLayersId].AppendElement(iter.first);
}
}
SetAnimatedValueForWebRender(iter.first, previousValue, frameTransform,
transformData);
break;
@ -251,6 +304,10 @@ bool CompositorAnimationStorage::SampleAnimations(TimeStamp aPreviousFrameTime,
}
}
if (!janked.empty() && aOMTAController) {
aOMTAController->NotifyJankedAnimations(std::move(janked));
}
return isAnimating;
}
@ -355,19 +412,6 @@ static Matrix4x4 GetTransformForPartialPrerender(
return transform;
}
// Returns clip rect in the scroll frame's coordinate space.
static ParentLayerRect GetClipRectForPartialPrerender(
const LayersId aLayersId, const PartialPrerenderData& aPartialPrerenderData,
const RefPtr<APZSampler>& aSampler) {
if (aSampler &&
aPartialPrerenderData.scrollId() != ScrollableLayerGuid::NULL_SCROLL_ID) {
return aSampler->GetCompositionBounds(aLayersId,
aPartialPrerenderData.scrollId());
}
return aPartialPrerenderData.clipRect();
}
bool CompositorAnimationStorage::ApplyAnimatedValue(
CompositorBridgeParent* aCompositorBridge, Layer* aLayer,
nsCSSPropertyID aProperty, AnimatedValue* aPreviousValue,

Просмотреть файл

@ -18,8 +18,9 @@
namespace mozilla {
namespace layers {
class Animation;
class Layer;
class CompositorBridgeParent;
class Layer;
class OMTAController;
typedef nsTArray<layers::Animation> AnimationArray;
@ -121,7 +122,9 @@ class CompositorAnimationStorage final {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorAnimationStorage)
public:
CompositorAnimationStorage() : mLock("CompositorAnimationStorage::mLock") {}
explicit CompositorAnimationStorage(CompositorBridgeParent* aCompositorBridge)
: mLock("CompositorAnimationStorage::mLock"),
mCompositorBridge(aCompositorBridge) {}
OMTAValue GetOMTAValue(const uint64_t& aId) const;
@ -148,7 +151,8 @@ class CompositorAnimationStorage final {
*
* Note: This is called only by WebRender.
*/
bool SampleAnimations(TimeStamp aPreviousFrameTime,
bool SampleAnimations(const OMTAController* aOMTAController,
TimeStamp aPreviousFrameTime,
TimeStamp aCurrentFrameTime);
/**
@ -168,7 +172,7 @@ class CompositorAnimationStorage final {
void ClearById(const uint64_t& aId);
private:
~CompositorAnimationStorage(){};
~CompositorAnimationStorage() = default;
/**
* Return the animated value if a given id can map to its animated value
@ -220,6 +224,8 @@ class CompositorAnimationStorage final {
AnimationsTable mAnimations;
std::unordered_set<uint64_t> mNewAnimations;
mutable Mutex mLock;
// CompositorBridgeParent owns this CompositorAnimationStorage instance.
CompositorBridgeParent* MOZ_NON_OWNING_REF mCompositorBridge;
};
} // namespace layers

Просмотреть файл

@ -98,6 +98,15 @@ class APZSampler {
void MarkAsyncTransformAppliedToContent(const LayerMetricsWrapper& aLayer);
bool HasUnusedAsyncTransform(const LayerMetricsWrapper& aLayer);
/**
* Similar to above GetCurrentAsyncTransform, but get the current transform
* with LayersId and ViewID.
* NOTE: This function should NOT be called on the compositor thread.
*/
AsyncTransform GetCurrentAsyncTransform(
const LayersId& aLayersId, const ScrollableLayerGuid::ViewID& aScrollId,
AsyncTransformComponents aComponents) const;
/**
* Returns the composition bounds of the APZC correspoinding to the pair of
* |aLayersId| and |aScrollId|.

Просмотреть файл

@ -147,6 +147,27 @@ AsyncTransform APZSampler::GetCurrentAsyncTransform(
AsyncPanZoomController::eForCompositing, aComponents);
}
AsyncTransform APZSampler::GetCurrentAsyncTransform(
const LayersId& aLayersId, const ScrollableLayerGuid::ViewID& aScrollId,
AsyncTransformComponents aComponents) const {
MOZ_ASSERT(!CompositorThreadHolder::IsInCompositorThread());
AssertOnSamplerThread();
RefPtr<AsyncPanZoomController> apzc =
mApz->GetTargetAPZC(aLayersId, aScrollId);
if (!apzc) {
// It's possible that this function can get called even after the target
// APZC has been already destroyed because destroying the animation which
// triggers this function call is basically processed later than the APZC,
// i.e. queue mCompositorAnimationsToDelete in WebRenderBridgeParent and
// then remove in WebRenderBridgeParent::RemoveEpochDataPriorTo.
return AsyncTransform{};
}
return apzc->GetCurrentAsyncTransform(AsyncPanZoomController::eForCompositing,
aComponents);
}
Maybe<CompositionPayload> APZSampler::NotifyScrollSampling(
const LayerMetricsWrapper& aLayer) {
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());

Просмотреть файл

@ -392,7 +392,8 @@ void CompositorBridgeParent::Initialize() {
}
if (mOptions.UseWebRender()) {
mOMTASampler = new OMTASampler(GetAnimationStorage());
CompositorAnimationStorage* animationStorage = GetAnimationStorage();
mOMTASampler = new OMTASampler(animationStorage, mRootLayerTreeID);
}
mPaused = mOptions.InitiallyPaused();
@ -1370,7 +1371,7 @@ void CompositorBridgeParent::ApplyAsyncProperties(
CompositorAnimationStorage* CompositorBridgeParent::GetAnimationStorage() {
if (!mAnimationStorage) {
mAnimationStorage = new CompositorAnimationStorage();
mAnimationStorage = new CompositorAnimationStorage(this);
}
return mAnimationStorage;
}

Просмотреть файл

@ -258,6 +258,7 @@ EXPORTS.mozilla.layers += [
'wr/ClipManager.h',
'wr/DisplayItemCache.h',
'wr/IpcResourceUpdateQueue.h',
'wr/OMTAController.h',
'wr/OMTASampler.h',
'wr/RenderRootStateManager.h',
'wr/RenderRootTypes.h',
@ -528,6 +529,7 @@ UNIFIED_SOURCES += [
'wr/ClipManager.cpp',
'wr/DisplayItemCache.cpp',
'wr/IpcResourceUpdateQueue.cpp',
'wr/OMTAController.cpp',
'wr/OMTASampler.cpp',
'wr/RenderRootStateManager.cpp',
'wr/RenderRootTypes.cpp',

Просмотреть файл

@ -0,0 +1,40 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/layers/OMTAController.h"
#include "mozilla/layers/CompositorBridgeParent.h"
#include "mozilla/layers/CompositorThread.h"
namespace mozilla {
namespace layers {
void OMTAController::NotifyJankedAnimations(
JankedAnimations&& aJankedAnimations) const {
if (StaticPrefs::layout_animation_prerender_partial_jank()) {
return;
}
if (!CompositorThread()) {
return;
}
if (!CompositorThread()->IsOnCurrentThread()) {
CompositorThread()->Dispatch(NewRunnableMethod<JankedAnimations&&>(
"layers::OMTAController::NotifyJankedAnimations", this,
&OMTAController::NotifyJankedAnimations, std::move(aJankedAnimations)));
return;
}
if (CompositorBridgeParent* bridge =
CompositorBridgeParent::GetCompositorBridgeParentFromLayersId(
mRootLayersId)) {
bridge->NotifyJankedAnimations(aJankedAnimations);
}
}
} // namespace layers
} // namespace mozilla

Просмотреть файл

@ -0,0 +1,44 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_layers_OMTAController_h
#define mozilla_layers_OMTAController_h
#include <unordered_map>
#include "mozilla/layers/LayersTypes.h" // for LayersId
#include "nsISerialEventTarget.h"
#include "nsISupportsImpl.h"
#include "nsTArrayForwardDeclare.h"
namespace mozilla {
namespace layers {
/**
* This class just delegates the jank animations notification to the compositor
* thread from the sampler thread.
*/
class OMTAController final {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(OMTAController)
public:
explicit OMTAController(LayersId aRootLayersId)
: mRootLayersId(aRootLayersId) {}
using JankedAnimations =
std::unordered_map<LayersId, nsTArray<uint64_t>, LayersId::HashFn>;
void NotifyJankedAnimations(JankedAnimations&& aJankedAnimations) const;
private:
~OMTAController() = default;
LayersId mRootLayersId;
};
} // namespace layers
} // namespace mozilla
#endif // mozilla_layers_OMTAController_h

Просмотреть файл

@ -9,6 +9,7 @@
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/layers/CompositorAnimationStorage.h"
#include "mozilla/layers/CompositorThread.h"
#include "mozilla/layers/OMTAController.h"
#include "mozilla/layers/SynchronousTask.h"
#include "mozilla/layers/WebRenderBridgeParent.h"
#include "mozilla/webrender/WebRenderAPI.h"
@ -20,11 +21,14 @@ StaticMutex OMTASampler::sWindowIdLock;
StaticAutoPtr<std::unordered_map<uint64_t, RefPtr<OMTASampler>>>
OMTASampler::sWindowIdMap;
OMTASampler::OMTASampler(const RefPtr<CompositorAnimationStorage>& aAnimStorage)
OMTASampler::OMTASampler(const RefPtr<CompositorAnimationStorage>& aAnimStorage,
LayersId aRootLayersId)
: mAnimStorage(aAnimStorage),
mStorageLock("OMTASampler::mStorageLock"),
mThreadIdLock("OMTASampler::mThreadIdLock"),
mSampleTimeLock("OMTASampler::mSampleTimeLock") {}
mSampleTimeLock("OMTASampler::mSampleTimeLock") {
mController = new OMTAController(aRootLayersId);
}
void OMTASampler::Destroy() {
StaticMutexAutoLock lock(sWindowIdLock);
@ -116,7 +120,7 @@ WrAnimations OMTASampler::SampleAnimations(const TimeStamp& aPreviousSampleTime,
MutexAutoLock lock(mStorageLock);
mAnimStorage->SampleAnimations(aPreviousSampleTime, aSampleTime);
mAnimStorage->SampleAnimations(mController, aPreviousSampleTime, aSampleTime);
return mAnimStorage->CollectWebRenderAnimations();
}
@ -150,7 +154,7 @@ void OMTASampler::SampleForTesting(const Maybe<TimeStamp>& aTestingSampleTime) {
}
MutexAutoLock storageLock(mStorageLock);
mAnimStorage->SampleAnimations(previousSampleTime, sampleTime);
mAnimStorage->SampleAnimations(mController, previousSampleTime, sampleTime);
}
void OMTASampler::SetAnimations(

Просмотреть файл

@ -11,6 +11,7 @@
#include <queue>
#include "base/platform_thread.h" // for PlatformThreadId
#include "mozilla/layers/OMTAController.h" // for OMTAController
#include "mozilla/Maybe.h"
#include "mozilla/StaticMutex.h"
#include "mozilla/StaticPtr.h"
@ -42,7 +43,8 @@ class OMTASampler final {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(OMTASampler)
public:
explicit OMTASampler(const RefPtr<CompositorAnimationStorage>& aAnimStorage);
OMTASampler(const RefPtr<CompositorAnimationStorage>& aAnimStorage,
LayersId aRootLayersId);
// Whoever creates this sampler is responsible for calling Destroy() on it
// before releasing the owning refptr.
@ -110,6 +112,7 @@ class OMTASampler final {
WrAnimations SampleAnimations(const TimeStamp& aPreviousSampleTime,
const TimeStamp& aSampleTime);
RefPtr<OMTAController> mController;
// Can only be accessed or modified while holding mStorageLock.
RefPtr<CompositorAnimationStorage> mAnimStorage;
mutable Mutex mStorageLock;

Просмотреть файл

@ -158,14 +158,14 @@ test-pref(layout.css.zoom-transform-hack.enabled,true) == zoom-hack-2.html zoom-
== transform-anon-block-1.html transform-anon-block-1-ref.html
test-pref(layout.animation.prerender.partial.jank,true) test-pref(layout.animation.prerender.partial,true) == partial-prerender-expansion-translate.html partial-prerender-expansion-ref.html
test-pref(layout.animation.prerender.partial.jank,true) test-pref(layout.animation.prerender.partial,true) == partial-prerender-translate-1.html about:blank
skip-if(webrender) test-pref(layout.animation.prerender.partial.jank,true) test-pref(layout.animation.prerender.partial,true) test-pref(layout.animation.prerender.viewport-ratio-limit,"1.125") == partial-prerender-translate-2.html partial-prerender-translate-2-ref.html # bug 1638152 for WebRender
test-pref(layout.animation.prerender.partial.jank,true) test-pref(layout.animation.prerender.partial,true) test-pref(layout.animation.prerender.viewport-ratio-limit,"1.125") == partial-prerender-translate-2.html partial-prerender-translate-2-ref.html
fails test-pref(layout.animation.prerender.partial.jank,true) test-pref(layout.animation.prerender.partial,true) test-pref(layout.animation.prerender.viewport-ratio-limit,"1.125") == partial-prerender-translate-3.html partial-prerender-translate-3-ref.html # bug 1642575
# This reftest doesn't fail on WebRender, this reftest fails only if there is a jank mechanism and the mechanism doesn't properly handle ancestor's transform values
test-pref(layout.animation.prerender.partial.jank,true) test-pref(layout.animation.prerender.partial,true) test-pref(layout.animation.prerender.viewport-ratio-limit,"1.125") == partial-prerender-translate-4.html partial-prerender-expansion-ref.html
# This reftest doesn't fail on WebRender, this reftest fails only if there is a jank mechanism and the mechanism does inproperly handle position:fixed scroll target
test-pref(layout.animation.prerender.partial.jank,true) test-pref(layout.animation.prerender.partial,true) test-pref(layout.animation.prerender.viewport-ratio-limit,"1.125") == partial-prerender-translate-5.html partial-prerender-translate-5-ref.html
fails-if(webrender) test-pref(layout.animation.prerender.partial.jank,true) test-pref(layout.animation.prerender.partial,true) test-pref(layout.animation.prerender.viewport-ratio-limit,"1.125") == partial-prerender-translate-6.html partial-prerender-translate-6-ref.html # bug 1638152 for WebRender
fails-if(webrender) test-pref(layout.animation.prerender.partial.jank,true) test-pref(layout.animation.prerender.partial,true) test-pref(layout.animation.prerender.viewport-ratio-limit,"1.125") == partial-prerender-translate-7.html partial-prerender-translate-2-ref.html # bug 1638152 for WebRender
test-pref(layout.animation.prerender.partial.jank,true) test-pref(layout.animation.prerender.partial,true) test-pref(layout.animation.prerender.viewport-ratio-limit,"1.125") == partial-prerender-translate-6.html partial-prerender-translate-6-ref.html
test-pref(layout.animation.prerender.partial.jank,true) test-pref(layout.animation.prerender.partial,true) test-pref(layout.animation.prerender.viewport-ratio-limit,"1.125") == partial-prerender-translate-7.html partial-prerender-translate-2-ref.html
# This reftest doesn't fail on WebRender, this reftest fails only if there is a jank mechanism and the mechanism doesn't properly clip transform in iframes.
test-pref(layout.animation.prerender.partial.jank,true) test-pref(layout.animation.prerender.partial,true) test-pref(layout.animation.prerender.viewport-ratio-limit,"1.125") == partial-prerender-translate-8.html partial-prerender-translate-8-ref.html
# This reftest doesn't fail on WebRender, this reftest fails only if there is a jank mechanism and the mechanism does inproperly handle position:fixed scroll target