Bug 1006084 - Allow ThebesLayers to be recreated when the creation hint changes. r=mattwoodrow
authorChris Lord <chrislord.net@gmail.com>
Fri, 13 Jun 2014 17:11:08 +0100
changeset 188678 e92103cfb78948e7596d0d6843831e320f9354ea
parent 188677 c0efa7f1e54c2dfbc9dfa77c49bf0411bc8b891d
child 188679 264e4791ad72072962dc5b1e156ec44357b7cf2d
push id8024
push userkwierso@gmail.com
push dateSat, 14 Jun 2014 00:50:00 +0000
treeherderb2g-inbound@355973298aab [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow
bugs1006084
milestone33.0a1
Bug 1006084 - Allow ThebesLayers to be recreated when the creation hint changes. r=mattwoodrow
gfx/layers/Layers.h
gfx/layers/client/ClientLayerManager.h
gfx/layers/client/ClientThebesLayer.cpp
gfx/layers/client/ClientThebesLayer.h
gfx/layers/client/ClientTiledThebesLayer.cpp
gfx/layers/client/ClientTiledThebesLayer.h
gfx/layers/client/SimpleTiledContentClient.cpp
gfx/layers/client/SimpleTiledContentClient.h
layout/base/FrameLayerBuilder.cpp
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -353,16 +353,23 @@ public:
    * NONE: No hint.
    * SCROLLABLE: This layer may represent scrollable content.
    */
   enum ThebesLayerCreationHint {
     NONE, SCROLLABLE
   };
 
   /**
+   * Returns true if aLayer is optimized for the given ThebesLayerCreationHint.
+   */
+  virtual bool IsOptimizedFor(ThebesLayer* aLayer,
+                              ThebesLayerCreationHint aCreationHint)
+  { return true; }
+
+  /**
    * CONSTRUCTION PHASE ONLY
    * Create a ThebesLayer for this manager's layer tree.
    */
   virtual already_AddRefed<ThebesLayer> CreateThebesLayer() = 0;
   /**
    * CONSTRUCTION PHASE ONLY
    * Create a ThebesLayer for this manager's layer tree, with a creation hint
    * parameter to help optimise the type of layer created.
@@ -1544,48 +1551,56 @@ public:
       NS_ASSERTION(-0.5 <= mResidualTranslation.x && mResidualTranslation.x < 0.5 &&
                    -0.5 <= mResidualTranslation.y && mResidualTranslation.y < 0.5,
                    "Residual translation out of range");
       mValidRegion.SetEmpty();
     }
     ComputeEffectiveTransformForMaskLayer(aTransformToSurface);
   }
 
+  LayerManager::ThebesLayerCreationHint GetCreationHint() const { return mCreationHint; }
+
   bool UsedForReadback() { return mUsedForReadback; }
   void SetUsedForReadback(bool aUsed) { mUsedForReadback = aUsed; }
   /**
    * Returns the residual translation. Apply this translation when drawing
    * into the ThebesLayer so that when mEffectiveTransform is applied afterwards
    * by layer compositing, the results exactly match the "ideal transform"
    * (the product of the transform of this layer and its ancestors).
    * Returns 0,0 unless SetAllowResidualTranslation(true) has been called.
    * The residual translation components are always in the range [-0.5, 0.5).
    */
   gfxPoint GetResidualTranslation() const { return mResidualTranslation; }
 
 protected:
-  ThebesLayer(LayerManager* aManager, void* aImplData)
+  ThebesLayer(LayerManager* aManager, void* aImplData,
+              LayerManager::ThebesLayerCreationHint aCreationHint = LayerManager::NONE)
     : Layer(aManager, aImplData)
     , mValidRegion()
+    , mCreationHint(aCreationHint)
     , mUsedForReadback(false)
     , mAllowResidualTranslation(false)
   {
     mContentFlags = 0; // Clear NO_TEXT, NO_TEXT_OVER_TRANSPARENT
   }
 
   virtual nsACString& PrintInfo(nsACString& aTo, const char* aPrefix);
 
   /**
    * ComputeEffectiveTransforms snaps the ideal transform to get mEffectiveTransform.
    * mResidualTranslation is the translation that should be applied *before*
    * mEffectiveTransform to get the ideal transform.
    */
   gfxPoint mResidualTranslation;
   nsIntRegion mValidRegion;
   /**
+   * The creation hint that was used when constructing this layer.
+   */
+  const LayerManager::ThebesLayerCreationHint mCreationHint;
+  /**
    * Set when this ThebesLayer is participating in readback, i.e. some
    * ReadbackLayer (may) be getting its background from this layer.
    */
   bool mUsedForReadback;
   /**
    * True when
    */
   bool mAllowResidualTranslation;
--- a/gfx/layers/client/ClientLayerManager.h
+++ b/gfx/layers/client/ClientLayerManager.h
@@ -68,16 +68,18 @@ public:
   }
   virtual void GetBackendName(nsAString& name);
   virtual const char* Name() const { return "Client"; }
 
   virtual void SetRoot(Layer* aLayer);
 
   virtual void Mutated(Layer* aLayer);
 
+  virtual bool IsOptimizedFor(ThebesLayer* aLayer, ThebesLayerCreationHint aHint);
+
   virtual already_AddRefed<ThebesLayer> CreateThebesLayer();
   virtual already_AddRefed<ThebesLayer> CreateThebesLayerWithHint(ThebesLayerCreationHint aHint);
   virtual already_AddRefed<ContainerLayer> CreateContainerLayer();
   virtual already_AddRefed<ImageLayer> CreateImageLayer();
   virtual already_AddRefed<CanvasLayer> CreateCanvasLayer();
   virtual already_AddRefed<ColorLayer> CreateColorLayer();
   virtual already_AddRefed<RefLayer> CreateRefLayer();
 
--- a/gfx/layers/client/ClientThebesLayer.cpp
+++ b/gfx/layers/client/ClientThebesLayer.cpp
@@ -122,16 +122,32 @@ ClientThebesLayer::RenderLayer()
     MOZ_ASSERT(mContentClient->GetForwarder());
   }
 
   mContentClient->BeginPaint();
   PaintThebes();
   mContentClient->EndPaint();
 }
 
+bool
+ClientLayerManager::IsOptimizedFor(ThebesLayer* aLayer, ThebesLayerCreationHint aHint)
+{
+#ifdef MOZ_B2G
+  // The only creation hint is whether the layer is scrollable or not, and this
+  // is only respected on B2G, where it's used to determine whether to use
+  // tiled layers or not.
+  // There are pretty nasty performance consequences for not using tiles on
+  // large, scrollable layers, so we want the layer to be recreated in this
+  // situation.
+  return aHint == aLayer->GetCreationHint();
+#else
+  return LayerManager::IsOptimizedFor(aLayer, aHint);
+#endif
+}
+
 already_AddRefed<ThebesLayer>
 ClientLayerManager::CreateThebesLayer()
 {
   return CreateThebesLayerWithHint(NONE);
 }
 
 already_AddRefed<ThebesLayer>
 ClientLayerManager::CreateThebesLayerWithHint(ThebesLayerCreationHint aHint)
@@ -145,29 +161,29 @@ ClientLayerManager::CreateThebesLayerWit
       (AsShadowForwarder()->GetCompositorBackendType() == LayersBackend::LAYERS_OPENGL ||
        AsShadowForwarder()->GetCompositorBackendType() == LayersBackend::LAYERS_D3D9 ||
        AsShadowForwarder()->GetCompositorBackendType() == LayersBackend::LAYERS_D3D11)) {
     if (!gTilingLog) {
       gTilingLog = PR_NewLogModule("tiling");
     }
     if (gfxPrefs::LayersUseSimpleTiles()) {
       nsRefPtr<SimpleClientTiledThebesLayer> layer =
-        new SimpleClientTiledThebesLayer(this);
+        new SimpleClientTiledThebesLayer(this, aHint);
       CREATE_SHADOW(Thebes);
       return layer.forget();
     } else {
       nsRefPtr<ClientTiledThebesLayer> layer =
-        new ClientTiledThebesLayer(this);
+        new ClientTiledThebesLayer(this, aHint);
       CREATE_SHADOW(Thebes);
       return layer.forget();
     }
   } else
   {
     nsRefPtr<ClientThebesLayer> layer =
-      new ClientThebesLayer(this);
+      new ClientThebesLayer(this, aHint);
     CREATE_SHADOW(Thebes);
     return layer.forget();
   }
 }
 
 
 }
 }
--- a/gfx/layers/client/ClientThebesLayer.h
+++ b/gfx/layers/client/ClientThebesLayer.h
@@ -22,25 +22,27 @@ class gfxContext;
 
 namespace mozilla {
 namespace layers {
 
 class CompositableClient;
 class ShadowableLayer;
 class SpecificLayerAttributes;
 
-class ClientThebesLayer : public ThebesLayer, 
+class ClientThebesLayer : public ThebesLayer,
                           public ClientLayer {
 public:
   typedef RotatedContentBuffer::PaintState PaintState;
   typedef RotatedContentBuffer::ContentType ContentType;
 
-  ClientThebesLayer(ClientLayerManager* aLayerManager) :
+  ClientThebesLayer(ClientLayerManager* aLayerManager,
+                    LayerManager::ThebesLayerCreationHint aCreationHint = LayerManager::NONE) :
     ThebesLayer(aLayerManager,
-                static_cast<ClientLayer*>(MOZ_THIS_IN_INITIALIZER_LIST())),
+                static_cast<ClientLayer*>(MOZ_THIS_IN_INITIALIZER_LIST()),
+                aCreationHint),
     mContentClient(nullptr)
   {
     MOZ_COUNT_CTOR(ClientThebesLayer);
   }
   virtual ~ClientThebesLayer()
   {
     if (mContentClient) {
       mContentClient->OnDetach();
--- a/gfx/layers/client/ClientTiledThebesLayer.cpp
+++ b/gfx/layers/client/ClientTiledThebesLayer.cpp
@@ -24,19 +24,21 @@
 #include "nsISupportsImpl.h"            // for MOZ_COUNT_CTOR, etc
 #include "nsRect.h"                     // for nsIntRect
 #include "LayersLogging.h"
 
 namespace mozilla {
 namespace layers {
 
 
-ClientTiledThebesLayer::ClientTiledThebesLayer(ClientLayerManager* const aManager)
+ClientTiledThebesLayer::ClientTiledThebesLayer(ClientLayerManager* const aManager,
+                                               ClientLayerManager::ThebesLayerCreationHint aCreationHint)
   : ThebesLayer(aManager,
-                static_cast<ClientLayer*>(MOZ_THIS_IN_INITIALIZER_LIST()))
+                static_cast<ClientLayer*>(MOZ_THIS_IN_INITIALIZER_LIST()),
+                aCreationHint)
   , mContentClient()
 {
   MOZ_COUNT_CTOR(ClientTiledThebesLayer);
   mPaintData.mLastScrollOffset = ParentLayerPoint(0, 0);
   mPaintData.mFirstPaint = true;
 }
 
 ClientTiledThebesLayer::~ClientTiledThebesLayer()
--- a/gfx/layers/client/ClientTiledThebesLayer.h
+++ b/gfx/layers/client/ClientTiledThebesLayer.h
@@ -36,17 +36,18 @@ class SpecificLayerAttributes;
  * There is no ContentClient for tiled layers. There is a ContentHost, however.
  */
 class ClientTiledThebesLayer : public ThebesLayer,
                                public ClientLayer
 {
   typedef ThebesLayer Base;
 
 public:
-  ClientTiledThebesLayer(ClientLayerManager* const aManager);
+  ClientTiledThebesLayer(ClientLayerManager* const aManager,
+                         ClientLayerManager::ThebesLayerCreationHint aCreationHint = LayerManager::NONE);
   ~ClientTiledThebesLayer();
 
   // Override name to distinguish it from ClientThebesLayer in layer dumps
   virtual const char* Name() const { return "TiledThebesLayer"; }
 
   // Thebes Layer
   virtual Layer* AsLayer() { return this; }
   virtual void InvalidateRegion(const nsIntRegion& aRegion) {
--- a/gfx/layers/client/SimpleTiledContentClient.cpp
+++ b/gfx/layers/client/SimpleTiledContentClient.cpp
@@ -242,19 +242,21 @@ SimpleTiledContentClient::~SimpleTiledCo
 
 void
 SimpleTiledContentClient::UseTiledLayerBuffer()
 {
   mForwarder->UseTiledLayerBuffer(this, mTiledBuffer.GetSurfaceDescriptorTiles());
   mTiledBuffer.ClearPaintedRegion();
 }
 
-SimpleClientTiledThebesLayer::SimpleClientTiledThebesLayer(ClientLayerManager* aManager)
+SimpleClientTiledThebesLayer::SimpleClientTiledThebesLayer(ClientLayerManager* aManager,
+                                                           ClientLayerManager::ThebesLayerCreationHint aCreationHint)
   : ThebesLayer(aManager,
-                static_cast<ClientLayer*>(MOZ_THIS_IN_INITIALIZER_LIST()))
+                static_cast<ClientLayer*>(MOZ_THIS_IN_INITIALIZER_LIST()),
+                aCreationHint)
   , mContentClient()
 {
   MOZ_COUNT_CTOR(SimpleClientTiledThebesLayer);
 }
 
 SimpleClientTiledThebesLayer::~SimpleClientTiledThebesLayer()
 {
   MOZ_COUNT_DTOR(SimpleClientTiledThebesLayer);
--- a/gfx/layers/client/SimpleTiledContentClient.h
+++ b/gfx/layers/client/SimpleTiledContentClient.h
@@ -154,17 +154,18 @@ private:
 };
 
 class SimpleClientTiledThebesLayer : public ThebesLayer,
                                      public ClientLayer
 {
   typedef ThebesLayer Base;
 
 public:
-  SimpleClientTiledThebesLayer(ClientLayerManager* const aManager);
+  SimpleClientTiledThebesLayer(ClientLayerManager* const aManager,
+                               ClientLayerManager::ThebesLayerCreationHint aCreationHint = LayerManager::NONE);
   ~SimpleClientTiledThebesLayer();
 
   // Thebes Layer
   virtual Layer* AsLayer() { return this; }
   virtual void InvalidateRegion(const nsIntRegion& aRegion) {
     mInvalidRegion.Or(mInvalidRegion, aRegion);
     mValidRegion.Sub(mValidRegion, aRegion);
   }
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -1441,84 +1441,95 @@ InvalidateEntireThebesLayer(ThebesLayer*
 already_AddRefed<ThebesLayer>
 ContainerState::CreateOrRecycleThebesLayer(const nsIFrame* aAnimatedGeometryRoot,
                                            const nsIFrame* aReferenceFrame,
                                            const nsPoint& aTopLeft)
 {
   // We need a new thebes layer
   nsRefPtr<ThebesLayer> layer;
   ThebesDisplayItemLayerUserData* data;
+  bool layerRecycled = false;
 #ifndef MOZ_ANDROID_OMTC
   bool didResetScrollPositionForLayerPixelAlignment = false;
 #endif
+
+  // Check whether the layer will be scrollable. This is used as a hint to
+  // influence whether tiled layers are used or not.
+  LayerManager::ThebesLayerCreationHint creationHint = LayerManager::NONE;
+  nsIFrame* animatedGeometryRootParent = aAnimatedGeometryRoot->GetParent();
+  if (animatedGeometryRootParent &&
+      animatedGeometryRootParent->GetType() == nsGkAtoms::scrollFrame) {
+    creationHint = LayerManager::SCROLLABLE;
+  }
+
   if (mNextFreeRecycledThebesLayer < mRecycledThebesLayers.Length()) {
-    // Recycle a layer
+    // Try to recycle a layer
     layer = mRecycledThebesLayers[mNextFreeRecycledThebesLayer];
     ++mNextFreeRecycledThebesLayer;
-    // Clear clip rect and mask layer so we don't accidentally stay clipped.
-    // We will reapply any necessary clipping.
-    layer->SetMaskLayer(nullptr);
-
-    data = static_cast<ThebesDisplayItemLayerUserData*>
-        (layer->GetUserData(&gThebesDisplayItemLayerUserData));
-    NS_ASSERTION(data, "Recycled ThebesLayers must have user data");
-
-    // This gets called on recycled ThebesLayers that are going to be in the
-    // final layer tree, so it's a convenient time to invalidate the
-    // content that changed where we don't know what ThebesLayer it belonged
-    // to, or if we need to invalidate the entire layer, we can do that.
-    // This needs to be done before we update the ThebesLayer to its new
-    // transform. See nsGfxScrollFrame::InvalidateInternal, where
-    // we ensure that mInvalidThebesContent is updated according to the
-    // scroll position as of the most recent paint.
-    if (!FuzzyEqual(data->mXScale, mParameters.mXScale, 0.00001f) ||
-        !FuzzyEqual(data->mYScale, mParameters.mYScale, 0.00001f) ||
-        data->mAppUnitsPerDevPixel != mAppUnitsPerDevPixel) {
-#ifdef MOZ_DUMP_PAINTING
-    if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
-      printf_stderr("Recycled layer %p changed scale\n", layer.get());
-    }
-#endif
-      InvalidateEntireThebesLayer(layer, aAnimatedGeometryRoot);
-#ifndef MOZ_ANDROID_OMTC
-      didResetScrollPositionForLayerPixelAlignment = true;
-#endif
-    }
-    if (!data->mRegionToInvalidate.IsEmpty()) {
+
+    // Check if the layer hint has changed and whether or not the layer should
+    // be recreated because of it.
+    if (mManager->IsOptimizedFor(layer->AsThebesLayer(), creationHint)) {
+      layerRecycled = true;
+
+      // Clear clip rect and mask layer so we don't accidentally stay clipped.
+      // We will reapply any necessary clipping.
+      layer->SetMaskLayer(nullptr);
+
+      data = static_cast<ThebesDisplayItemLayerUserData*>
+          (layer->GetUserData(&gThebesDisplayItemLayerUserData));
+      NS_ASSERTION(data, "Recycled ThebesLayers must have user data");
+
+      // This gets called on recycled ThebesLayers that are going to be in the
+      // final layer tree, so it's a convenient time to invalidate the
+      // content that changed where we don't know what ThebesLayer it belonged
+      // to, or if we need to invalidate the entire layer, we can do that.
+      // This needs to be done before we update the ThebesLayer to its new
+      // transform. See nsGfxScrollFrame::InvalidateInternal, where
+      // we ensure that mInvalidThebesContent is updated according to the
+      // scroll position as of the most recent paint.
+      if (!FuzzyEqual(data->mXScale, mParameters.mXScale, 0.00001f) ||
+          !FuzzyEqual(data->mYScale, mParameters.mYScale, 0.00001f) ||
+          data->mAppUnitsPerDevPixel != mAppUnitsPerDevPixel) {
 #ifdef MOZ_DUMP_PAINTING
       if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
-        printf_stderr("Invalidating deleted frame content from layer %p\n", layer.get());
-      }
-#endif
-      layer->InvalidateRegion(data->mRegionToInvalidate);
-#ifdef MOZ_DUMP_PAINTING
-      if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
-        nsAutoCString str;
-        AppendToString(str, data->mRegionToInvalidate);
-        printf_stderr("Invalidating layer %p: %s\n", layer.get(), str.get());
+        printf_stderr("Recycled layer %p changed scale\n", layer.get());
       }
 #endif
-      data->mRegionToInvalidate.SetEmpty();
+        InvalidateEntireThebesLayer(layer, aAnimatedGeometryRoot);
+#ifndef MOZ_ANDROID_OMTC
+        didResetScrollPositionForLayerPixelAlignment = true;
+#endif
+      }
+      if (!data->mRegionToInvalidate.IsEmpty()) {
+#ifdef MOZ_DUMP_PAINTING
+        if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
+          printf_stderr("Invalidating deleted frame content from layer %p\n", layer.get());
+        }
+#endif
+        layer->InvalidateRegion(data->mRegionToInvalidate);
+#ifdef MOZ_DUMP_PAINTING
+        if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
+          nsAutoCString str;
+          AppendToString(str, data->mRegionToInvalidate);
+          printf_stderr("Invalidating layer %p: %s\n", layer.get(), str.get());
+        }
+#endif
+        data->mRegionToInvalidate.SetEmpty();
+      }
+
+      // We do not need to Invalidate these areas in the widget because we
+      // assume the caller of InvalidateThebesLayerContents has ensured
+      // the area is invalidated in the widget.
     }
-
-    // We do not need to Invalidate these areas in the widget because we
-    // assume the caller of InvalidateThebesLayerContents has ensured
-    // the area is invalidated in the widget.
-  } else {
-    // Check whether the layer will be scrollable. This is used as a hint to
-    // influence whether tiled layers are used or not.
-    bool canScroll = false;
-    nsIFrame* animatedGeometryRootParent = aAnimatedGeometryRoot->GetParent();
-    if (animatedGeometryRootParent &&
-        animatedGeometryRootParent->GetType() == nsGkAtoms::scrollFrame) {
-      canScroll = true;
-    }
+  }
+
+  if (!layerRecycled) {
     // Create a new thebes layer
-    layer = mManager->CreateThebesLayerWithHint(canScroll ? LayerManager::SCROLLABLE :
-                                                            LayerManager::NONE);
+    layer = mManager->CreateThebesLayerWithHint(creationHint);
     if (!layer)
       return nullptr;
     // Mark this layer as being used for Thebes-painting display items
     data = new ThebesDisplayItemLayerUserData();
     layer->SetUserData(&gThebesDisplayItemLayerUserData, data);
     ResetScrollPositionForLayerPixelAlignment(aAnimatedGeometryRoot);
 #ifndef MOZ_ANDROID_OMTC
     didResetScrollPositionForLayerPixelAlignment = true;