Bug 1257641 - Use empty transactions to carry scroll offset updates to APZ that don't require a repaint. r=mattwoodrow,mstange,botond
authorKartikaya Gupta <kgupta@mozilla.com>
Wed, 13 Apr 2016 10:21:13 -0400
changeset 330909 e0ceca98966dd5eeb0c5319e3e3e4e62a940b08e
parent 330908 f33e7e19f318f1498210bed55aa69e5e0b2ff1fb
child 330910 97daab2fd2319acc7698648269b7ce099740f6bc
push id6048
push userkmoir@mozilla.com
push dateMon, 06 Jun 2016 19:02:08 +0000
treeherdermozilla-beta@46d72a56c57d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow, mstange, botond
bugs1257641
milestone48.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 1257641 - Use empty transactions to carry scroll offset updates to APZ that don't require a repaint. r=mattwoodrow,mstange,botond MozReview-Commit-ID: KNeGSKldmp7
gfx/layers/FrameMetrics.h
gfx/layers/Layers.cpp
gfx/layers/Layers.h
gfx/layers/LayersLogging.cpp
gfx/layers/apz/src/AsyncPanZoomController.cpp
layout/generic/nsGfxScrollFrame.cpp
--- a/gfx/layers/FrameMetrics.h
+++ b/gfx/layers/FrameMetrics.h
@@ -22,16 +22,25 @@
 namespace IPC {
 template <typename T> struct ParamTraits;
 } // namespace IPC
 
 namespace mozilla {
 namespace layers {
 
 /**
+ * Helper struct to hold a couple of fields that can be updated as part of
+ * an empty transaction.
+ */
+struct ScrollUpdateInfo {
+  uint32_t mScrollGeneration;
+  CSSPoint mScrollOffset;
+};
+
+/**
  * The viewport and displayport metrics for the painted frame at the
  * time of a layer-tree transaction.  These metrics are especially
  * useful for shadow layers, because the metrics values are updated
  * atomically with new pixels.
  */
 struct FrameMetrics {
   friend struct IPC::ParamTraits<mozilla::layers::FrameMetrics>;
 public:
@@ -39,16 +48,19 @@ public:
   typedef uint64_t ViewID;
   static const ViewID NULL_SCROLL_ID;   // This container layer does not scroll.
   static const ViewID START_SCROLL_ID = 2;  // This is the ID that scrolling subframes
                                         // will begin at.
 
   enum ScrollOffsetUpdateType : uint8_t {
     eNone,          // The default; the scroll offset was not updated
     eMainThread,    // The scroll offset was updated by the main thread.
+    ePending,       // The scroll offset was updated on the main thread, but not
+                    // painted, so the layer texture data is still at the old
+                    // offset.
 
     eSentinel       // For IPC use only
   };
 
   FrameMetrics()
     : mScrollId(NULL_SCROLL_ID)
     , mScrollParentId(NULL_SCROLL_ID)
     , mPresShellResolution(1)
@@ -236,16 +248,23 @@ public:
 
   void CopySmoothScrollInfoFrom(const FrameMetrics& aOther)
   {
     mSmoothScrollOffset = aOther.mSmoothScrollOffset;
     mScrollGeneration = aOther.mScrollGeneration;
     mDoSmoothScroll = aOther.mDoSmoothScroll;
   }
 
+  void UpdatePendingScrollInfo(const ScrollUpdateInfo& aInfo)
+  {
+    mScrollOffset = aInfo.mScrollOffset;
+    mScrollGeneration = aInfo.mScrollGeneration;
+    mScrollUpdateType = ePending;
+  }
+
   void UpdateScrollInfo(uint32_t aScrollGeneration, const CSSPoint& aScrollOffset)
   {
     mScrollOffset = aScrollOffset;
     mScrollGeneration = aScrollGeneration;
   }
 
   // Make a copy of this FrameMetrics object which does not have any pointers
   // to heap-allocated memory (i.e. is Plain Old Data, or 'POD'), and is
@@ -375,16 +394,21 @@ public:
   }
 
   void SetSmoothScrollOffsetUpdated(int32_t aScrollGeneration)
   {
     mDoSmoothScroll = true;
     mScrollGeneration = aScrollGeneration;
   }
 
+  ScrollOffsetUpdateType GetScrollUpdateType() const
+  {
+    return mScrollUpdateType;
+  }
+
   bool GetScrollOffsetUpdated() const
   {
     return mScrollUpdateType != eNone;
   }
 
   bool GetDoSmoothScroll() const
   {
     return mDoSmoothScroll;
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -562,16 +562,21 @@ Layer::ScrollMetadataChanged()
 
 void
 Layer::ApplyPendingUpdatesToSubtree()
 {
   ApplyPendingUpdatesForThisTransaction();
   for (Layer* child = GetFirstChild(); child; child = child->GetNextSibling()) {
     child->ApplyPendingUpdatesToSubtree();
   }
+  if (!GetParent()) {
+    // Once we're done recursing through the whole tree, clear the pending
+    // updates from the manager.
+    Manager()->ClearPendingScrollInfoUpdate();
+  }
 }
 
 bool
 Layer::IsOpaqueForVisibility()
 {
   return GetLocalOpacity() == 1.0f &&
          GetEffectiveMixBlendMode() == CompositionOp::OP_OVER;
 }
@@ -941,16 +946,25 @@ Layer::ApplyPendingUpdatesForThisTransac
   mPendingTransform = nullptr;
 
   if (mPendingAnimations) {
     MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) PendingUpdatesForThisTransaction", this));
     mPendingAnimations->SwapElements(mAnimations);
     mPendingAnimations = nullptr;
     Mutated();
   }
+
+  for (size_t i = 0; i < mScrollMetadata.Length(); i++) {
+    FrameMetrics& fm = mScrollMetadata[i].GetMetrics();
+    Maybe<ScrollUpdateInfo> update = Manager()->GetPendingScrollInfoUpdate(fm.GetScrollId());
+    if (update) {
+      fm.UpdatePendingScrollInfo(update.value());
+      Mutated();
+    }
+  }
 }
 
 float
 Layer::GetLocalOpacity()
 {
   float opacity = mOpacity;
   if (LayerComposite* shadow = AsLayerComposite())
     opacity = shadow->GetShadowOpacity();
@@ -2442,16 +2456,39 @@ LayerManager::DumpPacket(layerscope::Lay
 
 /*static*/ bool
 LayerManager::IsLogEnabled()
 {
   return MOZ_LOG_TEST(GetLog(), LogLevel::Debug);
 }
 
 void
+LayerManager::SetPendingScrollUpdateForNextTransaction(FrameMetrics::ViewID aScrollId,
+                                                       const ScrollUpdateInfo& aUpdateInfo)
+{
+  mPendingScrollUpdates[aScrollId] = aUpdateInfo;
+}
+
+Maybe<ScrollUpdateInfo>
+LayerManager::GetPendingScrollInfoUpdate(FrameMetrics::ViewID aScrollId)
+{
+  auto it = mPendingScrollUpdates.find(aScrollId);
+  if (it != mPendingScrollUpdates.end()) {
+    return Some(it->second);
+  }
+  return Nothing();
+}
+
+void
+LayerManager::ClearPendingScrollInfoUpdate()
+{
+  mPendingScrollUpdates.clear();
+}
+
+void
 PrintInfo(std::stringstream& aStream, LayerComposite* aLayerComposite)
 {
   if (!aLayerComposite) {
     return;
   }
   if (const Maybe<ParentLayerIntRect>& clipRect = aLayerComposite->GetShadowClipRect()) {
     AppendToString(aStream, *clipRect, " [shadow-clip=", "]");
   }
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * 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_LAYERS_H
 #define GFX_LAYERS_H
 
+#include <map>
 #include <stdint.h>                     // for uint32_t, uint64_t, uint8_t
 #include <stdio.h>                      // for FILE
 #include <sys/types.h>                  // for int32_t, int64_t
 #include "FrameMetrics.h"               // for FrameMetrics
 #include "Units.h"                      // for LayerMargin, LayerPoint, ParentLayerIntRect
 #include "gfxContext.h"
 #include "gfxTypes.h"
 #include "gfxPoint.h"                   // for gfxPoint
@@ -693,16 +694,29 @@ private:
     uint32_t mNextIndex;
     uint32_t mLatestStartIndex;
     uint32_t mCurrentRunStartIndex;
     bool mIsPaused;
   };
   FramesTimingRecording mRecording;
 
   TimeStamp mTabSwitchStart;
+
+public:
+  /*
+   * Methods to store/get/clear a "pending scroll info update" object on a
+   * per-scrollid basis. This is used for empty transactions that push over
+   * scroll position updates to the APZ code.
+   */
+  void SetPendingScrollUpdateForNextTransaction(FrameMetrics::ViewID aScrollId,
+                                                const ScrollUpdateInfo& aUpdateInfo);
+  Maybe<ScrollUpdateInfo> GetPendingScrollInfoUpdate(FrameMetrics::ViewID aScrollId);
+  void ClearPendingScrollInfoUpdate();
+private:
+  std::map<FrameMetrics::ViewID,ScrollUpdateInfo> mPendingScrollUpdates;
 };
 
 typedef InfallibleTArray<Animation> AnimationArray;
 
 struct AnimData {
   InfallibleTArray<mozilla::StyleAnimationValue> mStartValues;
   InfallibleTArray<mozilla::StyleAnimationValue> mEndValues;
   InfallibleTArray<Maybe<mozilla::ComputedTimingFunction>> mFunctions;
@@ -852,16 +866,17 @@ public:
    * Set the (sub)document metrics used to render the Layer subtree
    * rooted at this. Note that a layer may have multiple FrameMetrics
    * objects; calling this function will remove all of them and replace
    * them with the provided FrameMetrics. See the documentation for
    * SetFrameMetrics(const nsTArray<FrameMetrics>&) for more details.
    */
   void SetScrollMetadata(const ScrollMetadata& aScrollMetadata)
   {
+    Manager()->ClearPendingScrollInfoUpdate();
     if (mScrollMetadata.Length() != 1 || mScrollMetadata[0] != aScrollMetadata) {
       MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) FrameMetrics", this));
       mScrollMetadata.ReplaceElementsAt(0, mScrollMetadata.Length(), aScrollMetadata);
       ScrollMetadataChanged();
       Mutated();
     }
   }
 
@@ -879,16 +894,17 @@ public:
    * Note also that there is actually a many-to-many relationship between
    * Layers and ScrollMetadata, because multiple Layers may have identical
    * ScrollMetadata objects. This happens when those layers belong to the
    * same scrolling subdocument and therefore end up with the same async
    * transform when they are scrolled by the APZ code.
    */
   void SetScrollMetadata(const nsTArray<ScrollMetadata>& aMetadataArray)
   {
+    Manager()->ClearPendingScrollInfoUpdate();
     if (mScrollMetadata != aMetadataArray) {
       MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) FrameMetrics", this));
       mScrollMetadata = aMetadataArray;
       ScrollMetadataChanged();
       Mutated();
     }
   }
 
--- a/gfx/layers/LayersLogging.cpp
+++ b/gfx/layers/LayersLogging.cpp
@@ -186,17 +186,17 @@ AppendToString(std::stringstream& aStrea
     AppendToString(aStream, m.GetViewport(), "] [v=");
     aStream << nsPrintfCString("] [z=(ld=%.3f r=%.3f",
             m.GetDevPixelsPerCSSPixel().scale,
             m.GetPresShellResolution()).get();
     AppendToString(aStream, m.GetCumulativeResolution(), " cr=");
     AppendToString(aStream, m.GetZoom(), " z=");
     AppendToString(aStream, m.GetExtraResolution(), " er=");
     aStream << nsPrintfCString(")] [u=(%d %d %lu)",
-            m.GetScrollOffsetUpdated(), m.GetDoSmoothScroll(),
+            m.GetScrollUpdateType(), m.GetDoSmoothScroll(),
             m.GetScrollGeneration()).get();
     AppendToString(aStream, m.GetScrollParentId(), "] [p=");
     aStream << nsPrintfCString("] [i=(%ld %lld %d)] }",
             m.GetPresShellId(), m.GetScrollId(), m.IsRootContent()).get();
   }
   aStream << sfx;
 }
 
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -3383,17 +3383,19 @@ void AsyncPanZoomController::NotifyLayer
     // No new information here, skip it. Note that this is not just an
     // optimization; it's correctness too. In the case where we get one of these
     // stale aLayerMetrics *after* a call to NotifyScrollUpdated, processing the
     // stale aLayerMetrics would clobber the more up-to-date information from
     // NotifyScrollUpdated.
     APZC_LOG("%p NotifyLayersUpdated short-circuit\n", this);
     return;
   }
-  mLastContentPaintMetrics = aLayerMetrics;
+  if (aLayerMetrics.GetScrollUpdateType() != FrameMetrics::ScrollOffsetUpdateType::ePending) {
+    mLastContentPaintMetrics = aLayerMetrics;
+  }
 
   mFrameMetrics.SetScrollParentId(aLayerMetrics.GetScrollParentId());
   APZC_LOG_FM(aLayerMetrics, "%p got a NotifyLayersUpdated with aIsFirstPaint=%d, aThisLayerTreeUpdated=%d",
     this, aIsFirstPaint, aThisLayerTreeUpdated);
 
   { // scope lock
     MutexAutoLock lock(mCheckerboardEventLock);
     if (mCheckerboardEvent && mCheckerboardEvent->IsRecordingTrace()) {
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -61,17 +61,16 @@
 #include "AsyncScrollBase.h"
 #include "ScrollSnap.h"
 #include "UnitTransforms.h"
 #include "nsPluginFrame.h"
 #include <mozilla/layers/AxisPhysicsModel.h>
 #include <mozilla/layers/AxisPhysicsMSDModel.h>
 #include "mozilla/layers/LayerTransactionChild.h"
 #include "mozilla/layers/ScrollLinkedEffectDetector.h"
-#include "mozilla/layers/ShadowLayers.h"
 #include "mozilla/unused.h"
 #include "LayersLogging.h"  // for Stringify
 #include <algorithm>
 #include <cstdlib> // for std::abs(int/long)
 #include <cmath> // for std::abs(float/double)
 
 #define PAINT_SKIP_LOG(...)
 // #define PAINT_SKIP_LOG(...) printf_stderr("PSKIP: " __VA_ARGS__)
@@ -2747,24 +2746,28 @@ ScrollFrameHelper::ScrollToImpl(nsPoint 
     if (usingDisplayPort && displayPort.IsEqualEdges(oldDisplayPort)) {
       if (LastScrollOrigin() == nsGkAtoms::apz) {
         schedulePaint = false;
         PAINT_SKIP_LOG("Skipping due to APZ scroll\n");
       } else if (mScrollableByAPZ && !HasPluginFrames() &&
                  !content->GetComposedDoc()->HasScrollLinkedEffect()) {
         nsIWidget* widget = presContext->GetNearestWidget();
         LayerManager* manager = widget ? widget->GetLayerManager() : nullptr;
-        ShadowLayerForwarder* forwarder = manager ? manager->AsShadowForwarder() : nullptr;
-        if (forwarder && forwarder->HasShadowManager()) {
+        if (manager) {
           mozilla::layers::FrameMetrics::ViewID id;
           DebugOnly<bool> success = nsLayoutUtils::FindIDFor(content, &id);
           MOZ_ASSERT(success); // we have a displayport, we better have an ID
-          forwarder->GetShadowManager()->SendUpdateScrollOffset(id,
-              mScrollGeneration, CSSPoint::FromAppUnits(GetScrollPosition()));
+
+          // Schedule an empty transaction to carry over the scroll offset update,
+          // instead of a full transaction. This empty transaction might still get
+          // squashed into a full transaction if something happens to trigger one.
           schedulePaint = false;
+          manager->SetPendingScrollUpdateForNextTransaction(id,
+              { mScrollGeneration, CSSPoint::FromAppUnits(GetScrollPosition()) });
+          mOuter->SchedulePaint(nsIFrame::PAINT_COMPOSITE_ONLY);
           PAINT_SKIP_LOG("Skipping due to APZ-forwarded main-thread scroll\n");
         }
       }
     }
   }
 
   if (schedulePaint) {
     mOuter->SchedulePaint();