Bug 1504929 - Further optimizations for RestyleManager::AddLayerChangesForAnimations.. r=birtles,sotaro
authorHiroyuki Ikezoe <hikezoe@mozilla.com>
Tue, 13 Nov 2018 10:23:20 +0000
changeset 502434 f82c7330018b3ad8cf263e1b7fa7eef311d90870
parent 502433 c1bad90a5650591a56adaf809d60baa926442a90
child 502435 33ac76a8930d4e0591975acdaad31e50207e3929
push id10290
push userffxbld-merge
push dateMon, 03 Dec 2018 16:23:23 +0000
treeherdermozilla-beta@700bed2445e6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbirtles, sotaro
bugs1504929
milestone65.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1504929 - Further optimizations for RestyleManager::AddLayerChangesForAnimations.. r=birtles,sotaro This change eliminates - nsLayoutUtils::LastContinuationOrIBSplitSibling calls for each CSS properties on WebRender - iterating over each display item for each compositor runnable CSS properties - a bunch of stuff in the case where the layer manager has not yet created, i.e. the compositor thread is not ready to receive animations Depends on D11425 Differential Revision: https://phabricator.services.mozilla.com/D11426
gfx/layers/AnimationInfo.cpp
gfx/layers/AnimationInfo.h
gfx/layers/wr/WebRenderUserData.h
layout/base/RestyleManager.cpp
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
layout/generic/nsIFrame.h
layout/painting/FrameLayerBuilder.cpp
layout/painting/FrameLayerBuilder.h
--- a/gfx/layers/AnimationInfo.cpp
+++ b/gfx/layers/AnimationInfo.cpp
@@ -1,18 +1,21 @@
 /* -*- 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 "AnimationInfo.h"
+#include "mozilla/LayerAnimationInfo.h"
 #include "mozilla/layers/WebRenderLayerManager.h"
 #include "mozilla/layers/AnimationHelper.h"
 #include "mozilla/dom/Animation.h"
+#include "nsIContent.h"
+#include "PuppetWidget.h"
 
 namespace mozilla {
 namespace layers {
 
 AnimationInfo::AnimationInfo() :
   mCompositorAnimationsId(0),
   mMutated(false)
 {
@@ -184,10 +187,69 @@ AnimationInfo::GetGenerationFromFrame(ns
       GetWebRenderUserData<WebRenderAnimationData>(aFrame, (uint32_t)aDisplayItemKey);
   if (animationData) {
     return animationData->GetAnimationInfo().GetAnimationGeneration();
   }
 
   return Nothing();
 }
 
+/* static */ void
+AnimationInfo::EnumerateGenerationOnFrame(
+  const nsIFrame* aFrame,
+  const nsIContent* aContent,
+  const CompositorAnimatableDisplayItemTypes& aDisplayItemTypes,
+  const AnimationGenerationCallback& aCallback)
+{
+  if (XRE_IsContentProcess()) {
+    if (nsIWidget* widget = nsContentUtils::WidgetForContent(aContent)) {
+      // In case of child processes, we might not have yet created the layer
+      // manager.  That means there is no animation generation we have, thus
+      // we call the callback function with |Nothing()| for the generation.
+      //
+      // Note that we need to use nsContentUtils::WidgetForContent() instead of
+      // TabChild::GetFrom(aFrame->PresShell())->WebWidget() because in the case
+      // of child popup content PuppetWidget::mTabChild is the same as the
+      // parent's one, which means mTabChild->IsLayersConnected() check in
+      // PuppetWidget::GetLayerManager queries the parent state, it results the
+      // assertion in the function failure.
+      if (widget->GetOwningTabChild() &&
+          !static_cast<widget::PuppetWidget*>(widget)->HasLayerManager()) {
+        for (auto displayItem : LayerAnimationInfo::sDisplayItemTypes) {
+          aCallback(Nothing(), displayItem);
+        }
+        return;
+      }
+    }
+  }
+
+  RefPtr<LayerManager> layerManager =
+    nsContentUtils::LayerManagerForContent(aContent);
+
+  if (layerManager &&
+      layerManager->GetBackendType() == layers::LayersBackend::LAYERS_WR) {
+    // In case of continuation, nsDisplayItem uses its last continuation, so we
+    // have to use the last continuation frame here.
+    if (nsLayoutUtils::IsFirstContinuationOrIBSplitSibling(aFrame)) {
+      aFrame = nsLayoutUtils::LastContinuationOrIBSplitSibling(aFrame);
+    }
+
+    for (auto displayItem : LayerAnimationInfo::sDisplayItemTypes) {
+      RefPtr<WebRenderAnimationData> animationData =
+        GetWebRenderUserData<WebRenderAnimationData>(aFrame,
+                                                     (uint32_t)displayItem);
+      Maybe<uint64_t> generation;
+      if (animationData) {
+        generation = animationData->GetAnimationInfo().GetAnimationGeneration();
+      }
+      aCallback(generation, displayItem);
+    }
+    return;
+  }
+
+  FrameLayerBuilder::EnumerateGenerationForDedicatedLayers(
+    aFrame,
+    LayerAnimationInfo::sDisplayItemTypes,
+    aCallback);
+}
+
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/AnimationInfo.h
+++ b/gfx/layers/AnimationInfo.h
@@ -3,19 +3,22 @@
 /* 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 GFX_ANIMATIONINFO_H
 #define GFX_ANIMATIONINFO_H
 
 #include "nsAutoPtr.h"
+#include "nsCSSPropertyIDSet.h"
 #include "nsDisplayItemTypes.h"
+#include "mozilla/Array.h"
 
 struct RawServoAnimationValue;
+class nsIContent;
 class nsIFrame;
 
 namespace mozilla {
 namespace layers {
 
 class Animation;
 class CompositorAnimations;
 class Layer;
@@ -70,16 +73,31 @@ public:
   bool ApplyPendingUpdatesForThisTransaction();
   bool HasTransformAnimation() const;
 
   // In case of continuation, |aFrame| must be the first or the last
   // continuation frame, otherwise this function might return Nothing().
   static Maybe<uint64_t> GetGenerationFromFrame(nsIFrame* aFrame,
                                                 DisplayItemType aDisplayItemKey);
 
+  using CompositorAnimatableDisplayItemTypes =
+    Array<DisplayItemType, nsCSSPropertyIDSet::CompositorAnimatableCount()>;
+  using AnimationGenerationCallback =
+    std::function<bool(const Maybe<uint64_t>& aGeneration,
+                       DisplayItemType aDisplayItemType)>;
+  // Enumerates animation generations on |aFrame| for the given display item
+  // types and calls |aCallback| with the animation generation.
+  //
+  // The enumeration stops if |aCallback| returns false.
+  static void EnumerateGenerationOnFrame(
+    const nsIFrame* aFrame,
+    const nsIContent* aContent,
+    const CompositorAnimatableDisplayItemTypes& aDisplayItemTypes,
+    const AnimationGenerationCallback& aCallback);
+
 protected:
   AnimationArray mAnimations;
   uint64_t mCompositorAnimationsId;
   nsAutoPtr<AnimationArray> mPendingAnimations;
   InfallibleTArray<AnimData> mAnimationData;
   // If this layer is used for OMTA, then this counter is used to ensure we
   // stay in sync with the animation manager
   Maybe<uint64_t> mAnimationGeneration;
--- a/gfx/layers/wr/WebRenderUserData.h
+++ b/gfx/layers/wr/WebRenderUserData.h
@@ -227,17 +227,17 @@ protected:
 
 extern void DestroyWebRenderUserDataTable(WebRenderUserDataTable* aTable);
 
 struct WebRenderUserDataProperty {
   NS_DECLARE_FRAME_PROPERTY_WITH_DTOR(Key, WebRenderUserDataTable, DestroyWebRenderUserDataTable)
 };
 
 template<class T> already_AddRefed<T>
-GetWebRenderUserData(nsIFrame* aFrame, uint32_t aPerFrameKey)
+GetWebRenderUserData(const nsIFrame* aFrame, uint32_t aPerFrameKey)
 {
   MOZ_ASSERT(aFrame);
   WebRenderUserDataTable* userDataTable =
     aFrame->GetProperty(WebRenderUserDataProperty::Key());
   if (!userDataTable) {
     return nullptr;
   }
 
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -1792,21 +1792,21 @@ RestyleManager::AddLayerChangesForAnimat
     return;
   }
 
   uint64_t frameGeneration =
     RestyleManager::GetAnimationGenerationForFrame(aFrame);
 
   Maybe<nsCSSPropertyIDSet> effectiveAnimationProperties;
 
+  Maybe<uint64_t> generation;
   nsChangeHint hint = nsChangeHint(0);
-  for (auto displayItemType : LayerAnimationInfo::sDisplayItemTypes) {
-    Maybe<uint64_t> generation =
-      layers::AnimationInfo::GetGenerationFromFrame(aFrame, displayItemType);
-    if (generation && frameGeneration != *generation) {
+  auto maybeApplyChangeHint = [&](const Maybe<uint64_t>& aGeneration,
+                                  DisplayItemType aDisplayItemType) -> bool {
+    if (aGeneration && frameGeneration != *aGeneration) {
       // If we have a transform layer bug don't have any transform style, we
       // probably just removed the transform but haven't destroyed the layer
       // yet. In this case we will typically add the appropriate change hint
       // (nsChangeHint_UpdateContainingBlock) when we compare styles so in
       // theory we could skip adding any change hint here.
       //
       // However, sometimes when we compare styles we'll get no change. For
       // example, if the transform style was 'none' when we sent the transform
@@ -1816,28 +1816,28 @@ RestyleManager::AddLayerChangesForAnimat
       // on the compositor. To handle this case we simply set all the change
       // hints relevant to removing transform style (since we don't know exactly
       // what changes happened while the animation was running on the
       // compositor).
       //
       // Note that we *don't* add nsChangeHint_UpdateTransformLayer since if we
       // did, ApplyRenderingChangeToTree would complain that we're updating a
       // transform layer without a transform.
-      if (displayItemType == DisplayItemType::TYPE_TRANSFORM &&
+      if (aDisplayItemType == DisplayItemType::TYPE_TRANSFORM &&
           !aFrame->StyleDisplay()->HasTransformStyle()) {
         // Add all the hints for a removing a transform if they are not already
         // set for this frame.
         if (!(NS_IsHintSubset(
                 nsChangeHint_ComprehensiveAddOrRemoveTransform,
                 aHintForThisFrame))) {
           hint |= nsChangeHint_ComprehensiveAddOrRemoveTransform;
         }
-        continue;
+        return true;
       }
-      hint |= LayerAnimationInfo::GetChangeHintFor(displayItemType);
+      hint |= LayerAnimationInfo::GetChangeHintFor(aDisplayItemType);
     }
 
     // We consider it's the first paint for the frame if we have an animation
     // for the property but have no layer, for the case of WebRender,  no
     // corresponding animation info.
     // Note that in case of animations which has properties preventing running
     // on the compositor, e.g., width or height, corresponding layer is not
     // created at all, but even in such cases, we normally set valid change
@@ -1849,23 +1849,30 @@ RestyleManager::AddLayerChangesForAnimat
     // setKeyframes or changing target element from other target which prevents
     // running on the compositor, etc.
     if (!generation) {
       if (!effectiveAnimationProperties) {
         effectiveAnimationProperties.emplace(
           nsLayoutUtils::GetAnimationPropertiesForCompositor(aFrame));
       }
       const nsCSSPropertyIDSet& propertiesForDisplayItem =
-        LayerAnimationInfo::GetCSSPropertiesFor(displayItemType);
+        LayerAnimationInfo::GetCSSPropertiesFor(aDisplayItemType);
       if (effectiveAnimationProperties->Intersects(propertiesForDisplayItem)) {
         hint |=
-          LayerAnimationInfo::GetChangeHintFor(displayItemType);
+          LayerAnimationInfo::GetChangeHintFor(aDisplayItemType);
       }
     }
-  }
+    return true;
+  };
+
+  AnimationInfo::EnumerateGenerationOnFrame(
+    aFrame,
+    aContent,
+    LayerAnimationInfo::sDisplayItemTypes,
+    maybeApplyChangeHint);
 
   if (hint) {
     aChangeListToProcess.AppendChange(aFrame, aContent, hint);
   }
 }
 
 RestyleManager::AnimationsWithDestroyedFrame::AnimationsWithDestroyedFrame(
                                                 RestyleManager* aRestyleManager)
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -4831,17 +4831,17 @@ nsLayoutUtils::LastContinuationOrIBSplit
       result = f;
     }
   }
 
   return result->LastContinuation();
 }
 
 bool
-nsLayoutUtils::IsFirstContinuationOrIBSplitSibling(nsIFrame *aFrame)
+nsLayoutUtils::IsFirstContinuationOrIBSplitSibling(const nsIFrame *aFrame)
 {
   if (aFrame->GetPrevContinuation()) {
     return false;
   }
   if ((aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) &&
       aFrame->GetProperty(nsIFrame::IBSplitPrevSibling())) {
     return false;
   }
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -1417,18 +1417,17 @@ public:
    */
   static nsIFrame*
   LastContinuationOrIBSplitSibling(const nsIFrame *aFrame);
 
   /**
    * Is FirstContinuationOrIBSplitSibling(aFrame) going to return
    * aFrame?
    */
-  static bool
-  IsFirstContinuationOrIBSplitSibling(nsIFrame *aFrame);
+  static bool IsFirstContinuationOrIBSplitSibling(const nsIFrame *aFrame);
 
   /**
    * Check whether aFrame is a part of the scrollbar or scrollcorner of
    * the root content.
    * @param aFrame the checking frame.
    * @return true if the frame is a part of the scrollbar or scrollcorner of
    *         the root content.
    */
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -4086,16 +4086,20 @@ public:
   nscoord ComputeISizeValue(gfxContext*         aRenderingContext,
                             nscoord             aContainingBlockISize,
                             nscoord             aContentEdgeToBoxSizing,
                             nscoord             aBoxSizingToMarginEdge,
                             const nsStyleCoord& aCoord,
                             ComputeSizeFlags    aFlags = eDefault);
 
   DisplayItemDataArray& DisplayItemData() { return mDisplayItemData; }
+  const DisplayItemDataArray& DisplayItemData() const
+  {
+    return mDisplayItemData;
+  }
 
   void AddDisplayItem(nsDisplayItem* aItem);
   bool RemoveDisplayItem(nsDisplayItem* aItem);
   void RemoveDisplayItemDataForDeletion();
   bool HasDisplayItems();
   bool HasDisplayItem(nsDisplayItem* aItem);
 
   bool ForceDescendIntoIfVisible() { return mForceDescendIntoIfVisible; }
--- a/layout/painting/FrameLayerBuilder.cpp
+++ b/layout/painting/FrameLayerBuilder.cpp
@@ -6898,16 +6898,78 @@ FrameLayerBuilder::GetDedicatedLayer(nsI
           !layer->HasUserData(&gPaintedDisplayItemLayerUserData)) {
         return layer;
       }
     }
   }
   return nullptr;
 }
 
+/* static */ void
+FrameLayerBuilder::EnumerateGenerationForDedicatedLayers(
+  const nsIFrame* aFrame,
+  const CompositorAnimatableDisplayItemTypes& aDisplayItemTypes,
+  const AnimationGenerationCallback& aCallback)
+{
+  std::bitset<static_cast<uint32_t>(DisplayItemType::TYPE_MAX)> notFoundTypes;
+  for (auto displayItem : aDisplayItemTypes) {
+    notFoundTypes.set(static_cast<uint32_t>(displayItem));
+  }
+
+  const SmallPointerArray<DisplayItemData>& array = aFrame->DisplayItemData();
+
+  for (uint32_t i = 0; i < array.Length(); i++) {
+    DisplayItemData* element =
+      DisplayItemData::AssertDisplayItemData(array.ElementAt(i));
+    if (!element->mParent->mLayerManager->IsWidgetLayerManager()) {
+      continue;
+    }
+
+    DisplayItemType foundType = DisplayItemType::TYPE_ZERO;
+    for (auto displayItem : aDisplayItemTypes) {
+      if (GetDisplayItemTypeFromKey(element->mDisplayItemKey) == displayItem) {
+        foundType = displayItem;
+        notFoundTypes.reset(static_cast<uint32_t>(displayItem));
+        break;
+      }
+    }
+    if (foundType == DisplayItemType::TYPE_ZERO) {
+      continue;
+    }
+
+    Maybe<uint64_t> generation;
+    if (element->mOptLayer) {
+      generation = element->mOptLayer->GetAnimationGeneration();
+    } else if (!element->mLayer->HasUserData(&gColorLayerUserData) &&
+               !element->mLayer->HasUserData(&gImageLayerUserData) &&
+               !element->mLayer->HasUserData(&gPaintedDisplayItemLayerUserData)) {
+      generation = element->mLayer->GetAnimationGeneration();
+    }
+
+    if (!aCallback(generation, foundType)) {
+      return;
+    }
+  }
+
+  // Bail out if we have already enumerated all possible layers for the given
+  // display item types.
+  if (notFoundTypes.none()) {
+    return;
+  }
+
+  // If there are any display item types that the nsIFrame doesn't have, we need
+  // to call the callback function for them respectively.
+  for (auto displayItem : aDisplayItemTypes) {
+    if (notFoundTypes[static_cast<uint32_t>(displayItem)] &&
+        !aCallback(Nothing(), displayItem)) {
+      return;
+    }
+  }
+}
+
 gfxSize
 FrameLayerBuilder::GetPaintedLayerScaleForFrame(nsIFrame* aFrame)
 {
   MOZ_ASSERT(aFrame, "need a frame");
 
   nsPresContext* presCtx = aFrame->PresContext()->GetRootPresContext();
 
   if (!presCtx) {
--- a/layout/painting/FrameLayerBuilder.h
+++ b/layout/painting/FrameLayerBuilder.h
@@ -3,16 +3,17 @@
 /* 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 FRAMELAYERBUILDER_H_
 #define FRAMELAYERBUILDER_H_
 
 #include "nsAutoPtr.h"
+#include "nsCSSPropertyIDSet.h"
 #include "nsTHashtable.h"
 #include "nsHashKeys.h"
 #include "nsTArray.h"
 #include "nsRegion.h"
 #include "nsIFrame.h"
 #include "DisplayItemClip.h"
 #include "mozilla/gfx/MatrixFwd.h"
 #include "mozilla/layers/LayersTypes.h"
@@ -504,16 +505,34 @@ public:
   /**
    * Call this to determine if a frame has a dedicated (non-Painted) layer
    * for the given display item key. If there isn't one, we return null,
    * otherwise we return the layer.
    */
   static Layer* GetDedicatedLayer(nsIFrame* aFrame,
                                   DisplayItemType aDisplayItemType);
 
+  using CompositorAnimatableDisplayItemTypes =
+    Array<DisplayItemType, nsCSSPropertyIDSet::CompositorAnimatableCount()>;
+  using AnimationGenerationCallback =
+    std::function<bool(const Maybe<uint64_t>& aGeneration,
+                       DisplayItemType aDisplayItemType)>;
+  /**
+   * Enumerates layers for the given display item types and calls |aCallback|
+   * with the animation generation for the layer.  If there is no corresponding
+   * layer for the display item or the layer has no animation, the animation
+   * generation is Nothing().
+   *
+   * The enumeration stops if |aCallback| returns false.
+   */
+  static void EnumerateGenerationForDedicatedLayers(
+    const nsIFrame* aFrame,
+    const CompositorAnimatableDisplayItemTypes& aDisplayItemTypes,
+    const AnimationGenerationCallback& aCallback);
+
   /**
    * This callback must be provided to EndTransaction. The callback data
    * must be the nsDisplayListBuilder containing this FrameLayerBuilder.
    * This function can be called multiple times in a row to draw
    * different regions. This will occur when, for example, progressive paint is
    * enabled. In these cases aDirtyRegion can be used to specify a larger region
    * than aRegionToDraw that will be drawn during the transaction, possibly
    * allowing the callback to make optimizations.