Bug 794200 - Progressive draw tiles in the direction of scrolling. r=bgirard
authorChris Lord <chrislord.net@gmail.com>
Wed, 26 Sep 2012 21:41:56 +0100
changeset 111776 49a7067b35fe4ce11e2994e18867f2383b58a20d
parent 111775 d4fc54501277097b71a60620dbc9155436e6aef3
child 111777 2786b171b97fb91ed5bb66c70469f0b520f5dee5
push idunknown
push userunknown
push dateunknown
reviewersbgirard
bugs794200
milestone18.0a1
Bug 794200 - Progressive draw tiles in the direction of scrolling. r=bgirard When scrolling downwards, make sure to fill in tiles from the top, upwards from the bottom, left from the right and right from the left.
gfx/layers/Layers.cpp
gfx/layers/Layers.h
gfx/layers/basic/BasicTiledThebesLayer.cpp
gfx/layers/basic/BasicTiledThebesLayer.h
gfx/layers/ipc/CompositorParent.cpp
gfx/layers/ipc/CompositorParent.h
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -181,16 +181,47 @@ AppendToString(nsACString& s, const Fram
 
 } // namespace <anon>
 
 namespace mozilla {
 namespace layers {
 
 //--------------------------------------------------
 // LayerManager
+Layer*
+LayerManager::GetPrimaryScrollableLayer()
+{
+  if (!mRoot) {
+    return nullptr;
+  }
+
+  nsTArray<Layer*> queue;
+  queue.AppendElement(mRoot);
+  while (queue.Length()) {
+    ContainerLayer* containerLayer = queue[0]->AsContainerLayer();
+    queue.RemoveElementAt(0);
+    if (!containerLayer) {
+      continue;
+    }
+
+    const FrameMetrics& frameMetrics = containerLayer->GetFrameMetrics();
+    if (frameMetrics.IsScrollable()) {
+      return containerLayer;
+    }
+
+    Layer* child = containerLayer->GetFirstChild();
+    while (child) {
+      queue.AppendElement(child);
+      child = child->GetNextSibling();
+    }
+  }
+
+  return mRoot;
+}
+
 already_AddRefed<gfxASurface>
 LayerManager::CreateOptimalSurface(const gfxIntSize &aSize,
                                    gfxASurface::gfxImageFormat aFormat)
 {
   return gfxPlatform::GetPlatform()->
     CreateOffscreenSurface(aSize, gfxASurface::ContentFromFormat(aFormat));
 }
 
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -289,16 +289,23 @@ public:
    */
   virtual void SetRoot(Layer* aLayer) = 0;
   /**
    * Can be called anytime
    */
   Layer* GetRoot() { return mRoot; }
 
   /**
+   * Does a breadth-first search from the root layer to find the first
+   * scrollable layer.
+   * Can be called any time.
+   */
+  Layer* GetPrimaryScrollableLayer();
+
+  /**
    * CONSTRUCTION PHASE ONLY
    * Called when a managee has mutated.
    * Subclasses overriding this method must first call their
    * superclass's impl
    */
 #ifdef DEBUG
   // In debug builds, we check some properties of |aLayer|.
   virtual void Mutated(Layer* aLayer);
--- a/gfx/layers/basic/BasicTiledThebesLayer.cpp
+++ b/gfx/layers/basic/BasicTiledThebesLayer.cpp
@@ -252,39 +252,79 @@ BasicTiledThebesLayer::PaintThebes(gfxCo
       mTiledBuffer.GetResolution() == resolution) {
     // Paint tiles that have no content before tiles that only have stale content.
     nsIntRegion staleRegion = mTiledBuffer.GetValidRegion();
     staleRegion.And(staleRegion, regionToPaint);
     if (!staleRegion.IsEmpty() && !staleRegion.Contains(regionToPaint)) {
       regionToPaint.Sub(regionToPaint, staleRegion);
     }
 
-    nsIntRegionRectIterator it(regionToPaint);
-    const nsIntRect* rect = it.Next();
-    if (!rect)
-      return;
+    // The following code decides what order to draw tiles in, based on the
+    // current scroll direction of the primary scrollable layer.
+    // XXX While this code is of a reasonable size currently, it is likely
+    //     we'll want to add more comprehensive methods of deciding what
+    //     tiles to draw. This is a good candidate for splitting out into a
+    //     separate function.
+
+    gfx::Point scrollOffset(0, 0);
+    Layer* primaryScrollable = BasicManager()->GetPrimaryScrollableLayer();
+    if (primaryScrollable) {
+      const FrameMetrics& metrics = primaryScrollable->AsContainerLayer()->GetFrameMetrics();
+      scrollOffset = metrics.mViewportScrollOffset;
+    }
 
-    // Currently we start painting from the first rect of the paint
-    // region and convert that into a tile. We should order tiles using
-    // their position from relevant user interaction events.
-    int paintTileStartX = mTiledBuffer.RoundDownToTileEdge(rect->x);
-    int paintTileStartY = mTiledBuffer.RoundDownToTileEdge(rect->y);
+    // First, decide whether to iterate on the region from the beginning or end
+    // of the rect list. This relies on the specific behaviour of nsRegion when
+    // subtracting rects. If we're moving more in the X direction, we draw
+    // tiles by column, otherwise by row.
+    nsIntRegionRectIterator it(regionToPaint);
+    const nsIntRect* rect;
+    int32_t scrollDiffX = scrollOffset.x - mLastScrollOffset.x;
+    int32_t scrollDiffY = scrollOffset.y - mLastScrollOffset.y;
+    if ((NS_ABS(scrollDiffY) > NS_ABS(scrollDiffX) && scrollDiffY >= 0)) {
+      rect = it.Next();
+    } else {
+      const nsIntRect* lastRect;
+      while (lastRect = it.Next()) {
+        rect = lastRect;
+      }
+    }
+
+    // Second, decide what direction to start drawing rects from by checking
+    // the scroll offset difference of the primary scrollable layer. If we're
+    // scrolling to the right, make sure to start from the left, downwards
+    // start from the top, etc.
+    int paintTileStartX, paintTileStartY;
+    if (scrollOffset.x >= mLastScrollOffset.x) {
+      paintTileStartX = mTiledBuffer.RoundDownToTileEdge(rect->x);
+    } else {
+      paintTileStartX = mTiledBuffer.RoundDownToTileEdge(rect->XMost() - 1);
+    }
+
+    if (scrollOffset.y >= mLastScrollOffset.y) {
+      paintTileStartY = mTiledBuffer.RoundDownToTileEdge(rect->y);
+    } else {
+      paintTileStartY = mTiledBuffer.RoundDownToTileEdge(rect->YMost() - 1);
+    }
 
     nsIntRegion maxPaint(
       nsIntRect(paintTileStartX, paintTileStartY,
                 mTiledBuffer.GetTileLength(), mTiledBuffer.GetTileLength()));
 
     if (!maxPaint.Contains(regionToPaint)) {
       // The region needed to paint is larger then our progressive chunk size
       // therefore update what we want to paint and ask for a new paint transaction.
       regionToPaint.And(regionToPaint, maxPaint);
       BasicManager()->SetRepeatTransaction();
 
       // Make sure that tiles that fall outside of the visible region are discarded.
       mValidRegion.And(mValidRegion, mVisibleRegion);
+    } else {
+      // The transaction is completed, store the last scroll offset.
+      mLastScrollOffset = scrollOffset;
     }
 
     // Keep track of what we're about to refresh.
     mValidRegion.Or(mValidRegion, regionToPaint);
   } else {
     mTiledBuffer.SetResolution(resolution);
     mValidRegion = mVisibleRegion;
   }
--- a/gfx/layers/basic/BasicTiledThebesLayer.h
+++ b/gfx/layers/basic/BasicTiledThebesLayer.h
@@ -156,16 +156,17 @@ class BasicTiledThebesLayer : public The
                               public BasicImplData,
                               public BasicShadowableLayer
 {
   typedef ThebesLayer Base;
 
 public:
   BasicTiledThebesLayer(BasicShadowLayerManager* const aManager)
     : ThebesLayer(aManager, static_cast<BasicImplData*>(this))
+    , mLastScrollOffset(0, 0)
   {
     MOZ_COUNT_CTOR(BasicTiledThebesLayer);
   }
 
   ~BasicTiledThebesLayer()
   {
     MOZ_COUNT_DTOR(BasicTiledThebesLayer);
   }
@@ -206,14 +207,15 @@ private:
               const nsIntRegion& aRegionToInvalidate,
               bool aDidSelfCopy,
               LayerManager::DrawThebesLayerCallback aCallback,
               void* aCallbackData)
   { NS_RUNTIMEABORT("Not reached."); }
 
   // Members
   BasicTiledLayerBuffer mTiledBuffer;
+  gfx::Point mLastScrollOffset;
 };
 
 } // layers
 } // mozilla
 
 #endif
--- a/gfx/layers/ipc/CompositorParent.cpp
+++ b/gfx/layers/ipc/CompositorParent.cpp
@@ -525,45 +525,16 @@ CompositorParent::Composite()
     printf_stderr("Compositor: Composite took %i ms.\n",
                   15 + (int)(TimeStamp::Now() - mExpectedComposeTime).ToMilliseconds());
   }
 #endif
 }
 
 // Do a breadth-first search to find the first layer in the tree that is
 // scrollable.
-Layer*
-CompositorParent::GetPrimaryScrollableLayer()
-{
-  Layer* root = mLayerManager->GetRoot();
-
-  nsTArray<Layer*> queue;
-  queue.AppendElement(root);
-  while (queue.Length()) {
-    ContainerLayer* containerLayer = queue[0]->AsContainerLayer();
-    queue.RemoveElementAt(0);
-    if (!containerLayer) {
-      continue;
-    }
-
-    const FrameMetrics& frameMetrics = containerLayer->GetFrameMetrics();
-    if (frameMetrics.IsScrollable()) {
-      return containerLayer;
-    }
-
-    Layer* child = containerLayer->GetFirstChild();
-    while (child) {
-      queue.AppendElement(child);
-      child = child->GetNextSibling();
-    }
-  }
-
-  return root;
-}
-
 static void
 Translate2D(gfx3DMatrix& aTransform, const gfxPoint& aOffset)
 {
   aTransform._41 += aOffset.x;
   aTransform._42 += aOffset.y;
 }
 
 void
@@ -766,17 +737,17 @@ CompositorParent::ApplyAsyncContentTrans
 
   return appliedTransform;
 }
 
 bool
 CompositorParent::TransformShadowTree(TimeStamp aCurrentFrame)
 {
   bool wantNextFrame = false;
-  Layer* layer = GetPrimaryScrollableLayer();
+  Layer* layer = mLayerManager->GetPrimaryScrollableLayer();
   ShadowLayer* shadow = layer->AsShadowLayer();
   ContainerLayer* container = layer->AsContainerLayer();
   Layer* root = mLayerManager->GetRoot();
 
   // NB: we must sample animations *before* sampling pan/zoom
   // transforms.
   wantNextFrame |= SampleAnimations(layer, mLastCompose);
 
--- a/gfx/layers/ipc/CompositorParent.h
+++ b/gfx/layers/ipc/CompositorParent.h
@@ -216,22 +216,16 @@ private:
   /**
    * Remove a compositor from the global compositor map.
    */
   static CompositorParent* RemoveCompositor(uint64_t id);
 
 
   // Platform specific functions
   /**
-   * Does a breadth-first search to find the first layer in the tree with a
-   * displayport set.
-   */
-  Layer* GetPrimaryScrollableLayer();
-
-  /**
    * Recursively applies the given translation to all top-level fixed position
    * layers that are descendants of the given layer.
    * aScaleDiff is considered to be the scale transformation applied when
    * displaying the layers, and is used to make sure the anchor points of
    * fixed position layers remain in the same position.
    */
   void TransformFixedLayers(Layer* aLayer,
                             const gfxPoint& aTranslation,