Bug 1085223 - Add an occlusion culling pass to compositor layers. r=BenWa
authorMatt Woodrow <mwoodrow@mozilla.com>
Mon, 20 Oct 2014 17:04:39 +1300
changeset 211362 fa9c6845338e2e2be839d1085faf173768aaf5d6
parent 211360 223e2f4b0d47b0b739c2da4dbd7ec2e1602c58ba
child 211363 3fefbf7f0b78bbde23b306d46ff405f8efec88f3
push id27674
push usercbook@mozilla.com
push dateTue, 21 Oct 2014 13:32:11 +0000
treeherdermozilla-central@313e5dcdfcb8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersBenWa
bugs1085223
milestone36.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 1085223 - Add an occlusion culling pass to compositor layers. r=BenWa
gfx/layers/composite/ContainerLayerComposite.cpp
gfx/layers/composite/LayerManagerComposite.cpp
gfx/layers/composite/LayerManagerComposite.h
gfx/layers/ipc/CompositorParent.cpp
--- a/gfx/layers/composite/ContainerLayerComposite.cpp
+++ b/gfx/layers/composite/ContainerLayerComposite.cpp
@@ -61,60 +61,16 @@ LayerHasCheckerboardingAPZC(Layer* aLaye
       }
       return true;
     }
     break;
   }
   return false;
 }
 
-/**
- * Returns a rectangle of content painted opaquely by aLayer. Very consertative;
- * bails by returning an empty rect in any tricky situations.
- */
-static nsIntRect
-GetOpaqueRect(Layer* aLayer)
-{
-  nsIntRect result;
-  gfx::Matrix matrix;
-  bool is2D = aLayer->AsLayerComposite()->GetShadowTransform().Is2D(&matrix);
-
-  // Just bail if there's anything difficult to handle.
-  if (!is2D || aLayer->GetMaskLayer() ||
-    aLayer->GetIsFixedPosition() ||
-    aLayer->GetIsStickyPosition() ||
-    aLayer->GetEffectiveOpacity() != 1.0f ||
-    matrix.HasNonIntegerTranslation()) {
-    return result;
-  }
-
-  if (aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) {
-    result = aLayer->GetEffectiveVisibleRegion().GetLargestRectangle();
-  } else {
-    // Drill down into RefLayers because that's what we particularly care about;
-    // layer construction for aLayer will not have known about the opaqueness
-    // of any RefLayer subtrees.
-    RefLayer* refLayer = aLayer->AsRefLayer();
-    if (refLayer && refLayer->GetFirstChild()) {
-      result = GetOpaqueRect(refLayer->GetFirstChild());
-    }
-  }
-
-  // Translate our opaque region to cover the child
-  gfx::Point point = matrix.GetTranslation();
-  result.MoveBy(static_cast<int>(point.x), static_cast<int>(point.y));
-
-  const nsIntRect* clipRect = aLayer->GetEffectiveClipRect();
-  if (clipRect) {
-    result.IntersectRect(result, *clipRect);
-  }
-
-  return result;
-}
-
 static void DrawLayerInfo(const RenderTargetIntRect& aClipRect,
                           LayerManagerComposite* aManager,
                           Layer* aLayer)
 {
 
   if (aLayer->GetType() == Layer::LayerType::TYPE_CONTAINER) {
     // XXX - should figure out a way to render this, but for now this
     // is hard to do, since it will often get superimposed over the first
@@ -157,22 +113,20 @@ static void PrintUniformityInfo(Layer* a
   LayerTranslationPayload* payload = new LayerTranslationPayload(aLayer, translation);
   PROFILER_MARKER_PAYLOAD("LayerTranslation", payload);
 #endif
 }
 
 /* all of the per-layer prepared data we need to maintain */
 struct PreparedLayer
 {
-  PreparedLayer(LayerComposite *aLayer, RenderTargetIntRect aClipRect, bool aRestoreVisibleRegion, nsIntRegion &aVisibleRegion) :
-    mLayer(aLayer), mClipRect(aClipRect), mRestoreVisibleRegion(aRestoreVisibleRegion), mSavedVisibleRegion(aVisibleRegion) {}
+  PreparedLayer(LayerComposite *aLayer, RenderTargetIntRect aClipRect) :
+    mLayer(aLayer), mClipRect(aClipRect) {}
   LayerComposite* mLayer;
   RenderTargetIntRect mClipRect;
-  bool mRestoreVisibleRegion;
-  nsIntRegion mSavedVisibleRegion;
 };
 
 /* all of the prepared data that we need in RenderLayer() */
 struct PreparedData
 {
   RefPtr<CompositingRenderTarget> mTmpTarget;
   nsAutoTArray<PreparedLayer, 12> mLayers;
   bool mNeedsSurfaceCopy;
@@ -218,48 +172,18 @@ ContainerPrepare(ContainerT* aContainer,
         !quad.Intersects(compositor->ClipRectInLayersCoordinates(layerToRender->GetLayer(), clipRect)) &&
         !LayerHasCheckerboardingAPZC(layerToRender->GetLayer(), nullptr)) {
       CULLING_LOG("Sublayer %p is clipped entirely\n", layerToRender->GetLayer());
       continue;
     }
 
     CULLING_LOG("Preparing sublayer %p\n", layerToRender->GetLayer());
 
-    nsIntRegion savedVisibleRegion;
-    bool restoreVisibleRegion = false;
-    gfx::Matrix matrix;
-    bool is2D = layerToRender->GetLayer()->GetBaseTransform().Is2D(&matrix);
-    if (i + 1 < children.Length() &&
-        is2D && !matrix.HasNonIntegerTranslation()) {
-      LayerComposite* nextLayer = static_cast<LayerComposite*>(children.ElementAt(i + 1)->ImplData());
-      CULLING_LOG("Culling against %p\n", nextLayer->GetLayer());
-      nsIntRect nextLayerOpaqueRect;
-      if (nextLayer && nextLayer->GetLayer()) {
-        nextLayerOpaqueRect = GetOpaqueRect(nextLayer->GetLayer());
-        gfx::Point point = matrix.GetTranslation();
-        nextLayerOpaqueRect.MoveBy(static_cast<int>(-point.x), static_cast<int>(-point.y));
-        CULLING_LOG("  point %i, %i\n", static_cast<int>(-point.x), static_cast<int>(-point.y));
-        CULLING_LOG("  opaque rect %i, %i, %i, %i\n", nextLayerOpaqueRect.x, nextLayerOpaqueRect.y, nextLayerOpaqueRect.width, nextLayerOpaqueRect.height);
-      }
-      if (!nextLayerOpaqueRect.IsEmpty()) {
-        CULLING_LOG("  draw\n");
-        savedVisibleRegion = layerToRender->GetShadowVisibleRegion();
-        nsIntRegion visibleRegion;
-        visibleRegion.Sub(savedVisibleRegion, nextLayerOpaqueRect);
-        if (visibleRegion.IsEmpty()) {
-          continue;
-        }
-        layerToRender->SetShadowVisibleRegion(visibleRegion);
-        restoreVisibleRegion = true;
-      } else {
-        CULLING_LOG("  skip\n");
-      }
-    }
     layerToRender->Prepare(clipRect);
-    aContainer->mPrepared->mLayers.AppendElement(PreparedLayer(layerToRender, clipRect, restoreVisibleRegion, savedVisibleRegion));
+    aContainer->mPrepared->mLayers.AppendElement(PreparedLayer(layerToRender, clipRect));
   }
 
   CULLING_LOG("Preparing container layer %p\n", aContainer->GetLayer());
 
   /**
    * Setup our temporary surface for rendering the contents of this container.
    */
 
@@ -320,21 +244,16 @@ RenderLayers(ContainerT* aContainer,
         gfx::Rect fbRect(clearRect.x, clearRect.y, clearRect.width, clearRect.height);
         compositor->ClearRect(fbRect);
         layerToRender->SetClearRect(nsIntRect(0, 0, 0, 0));
       }
     } else {
       layerToRender->RenderLayer(RenderTargetPixel::ToUntyped(clipRect));
     }
 
-    if (preparedData.mRestoreVisibleRegion) {
-      // Restore the region in case it's not covered by opaque content next time
-      layerToRender->SetShadowVisibleRegion(preparedData.mSavedVisibleRegion);
-    }
-
     if (gfxPrefs::UniformityInfo()) {
       PrintUniformityInfo(layer);
     }
 
     if (gfxPrefs::DrawLayerInfo()) {
       DrawLayerInfo(clipRect, aManager, layer);
     }
 
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -192,16 +192,65 @@ LayerManagerComposite::BeginTransactionW
   }
 
   mIsCompositorReady = true;
   mCompositor->SetTargetContext(aTarget, aRect);
   mTarget = aTarget;
   mTargetBounds = aRect;
 }
 
+void
+LayerManagerComposite::ApplyOcclusionCulling(Layer* aLayer, nsIntRegion& aOpaqueRegion)
+{
+  nsIntRegion localOpaque;
+  Matrix transform2d;
+  bool isTranslation = false;
+  // If aLayer has a simple transform (only an integer translation) then we
+  // can easily convert aOpaqueRegion into pre-transform coordinates and include
+  // that region.
+  if (aLayer->GetLocalTransform().Is2D(&transform2d)) {
+    if (transform2d.IsIntegerTranslation()) {
+      isTranslation = true;
+      localOpaque = aOpaqueRegion;
+      localOpaque.MoveBy(-transform2d._31, -transform2d._32);
+    }
+  }
+
+  // Subtract any areas that we know to be opaque from our
+  // visible region.
+  LayerComposite *composite = aLayer->AsLayerComposite();
+  if (!localOpaque.IsEmpty()) {
+    nsIntRegion visible = composite->GetShadowVisibleRegion();
+    visible.Sub(visible, localOpaque);
+    composite->SetShadowVisibleRegion(visible);
+  }
+
+  // Compute occlusions for our descendants (in front-to-back order) and allow them to
+  // contribute to localOpaque.
+  for (Layer* child = aLayer->GetLastChild(); child; child = child->GetPrevSibling()) {
+    ApplyOcclusionCulling(child, localOpaque);
+  }
+
+  // If we have a simple transform, then we can add our opaque area into
+  // aOpaqueRegion.
+  if (isTranslation &&
+      !aLayer->GetMaskLayer() &&
+      aLayer->GetLocalOpacity() == 1.0f) {
+    if (aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) {
+      localOpaque.Or(localOpaque, composite->GetShadowVisibleRegion());
+    }
+    localOpaque.MoveBy(transform2d._31, transform2d._32);
+    const nsIntRect* clip = aLayer->GetEffectiveClipRect();
+    if (clip) {
+      localOpaque.And(localOpaque, *clip);
+    }
+    aOpaqueRegion.Or(aOpaqueRegion, localOpaque);
+  }
+}
+
 bool
 LayerManagerComposite::EndEmptyTransaction(EndTransactionFlags aFlags)
 {
   NS_ASSERTION(mInTransaction, "Didn't call BeginTransaction?");
   if (!mRoot) {
     mInTransaction = false;
     mIsCompositorReady = false;
     return false;
@@ -252,16 +301,19 @@ LayerManagerComposite::EndTransaction(Dr
       // properties.
       mRoot->ApplyPendingUpdatesToSubtree();
     }
 
     // The results of our drawing always go directly into a pixel buffer,
     // so we don't need to pass any global transform here.
     mRoot->ComputeEffectiveTransforms(gfx::Matrix4x4());
 
+    nsIntRegion opaque;
+    ApplyOcclusionCulling(mRoot, opaque);
+
     Render();
     mGeometryChanged = false;
   } else {
     // Modified layer tree
     mGeometryChanged = true;
   }
 
   mCompositor->ClearTargetContext();
--- a/gfx/layers/composite/LayerManagerComposite.h
+++ b/gfx/layers/composite/LayerManagerComposite.h
@@ -161,16 +161,23 @@ public:
   virtual bool AreComponentAlphaLayersEnabled() MOZ_OVERRIDE;
 
   virtual TemporaryRef<DrawTarget>
     CreateOptimalMaskDrawTarget(const IntSize &aSize) MOZ_OVERRIDE;
 
   virtual const char* Name() const MOZ_OVERRIDE { return ""; }
 
   /**
+   * Restricts the shadow visible region of layers that are covered with
+   * opaque content. aOpaqueRegion is the region already known to be covered
+   * with opaque content, in the post-transform coordinate space of aLayer.
+   */
+  void ApplyOcclusionCulling(Layer* aLayer, nsIntRegion& aOpaqueRegion);
+
+  /**
    * RAII helper class to add a mask effect with the compositable from aMaskLayer
    * to the EffectChain aEffect and notify the compositable when we are done.
    */
   class AutoAddMaskEffect
   {
   public:
     AutoAddMaskEffect(Layer* aMaskLayer,
                       EffectChain& aEffect,
--- a/gfx/layers/ipc/CompositorParent.cpp
+++ b/gfx/layers/ipc/CompositorParent.cpp
@@ -640,16 +640,36 @@ CompositorParent::ScheduleComposition()
 
 void
 CompositorParent::CompositeCallback()
 {
   mCurrentCompositeTask = nullptr;
   CompositeToTarget(nullptr);
 }
 
+// Go down the composite layer tree, setting properties to match their
+// content-side counterparts.
+static void
+SetShadowProperties(Layer* aLayer)
+{
+  // FIXME: Bug 717688 -- Do these updates in LayerTransactionParent::RecvUpdate.
+  LayerComposite* layerComposite = aLayer->AsLayerComposite();
+  // Set the layerComposite's base transform to the layer's base transform.
+  layerComposite->SetShadowTransform(aLayer->GetBaseTransform());
+  layerComposite->SetShadowTransformSetByAnimation(false);
+  layerComposite->SetShadowVisibleRegion(aLayer->GetVisibleRegion());
+  layerComposite->SetShadowClipRect(aLayer->GetClipRect());
+  layerComposite->SetShadowOpacity(aLayer->GetOpacity());
+
+  for (Layer* child = aLayer->GetFirstChild();
+      child; child = child->GetNextSibling()) {
+    SetShadowProperties(child);
+  }
+}
+
 void
 CompositorParent::CompositeToTarget(DrawTarget* aTarget, const nsIntRect* aRect)
 {
   profiler_tracing("Paint", "Composite", TRACING_INTERVAL_START);
   PROFILER_LABEL("CompositorParent", "Composite",
     js::ProfileEntry::Category::GRAPHICS);
 
   MOZ_ASSERT(IsInCompositorThread(),
@@ -667,16 +687,17 @@ CompositorParent::CompositeToTarget(Draw
   mLastCompose = TimeStamp::Now();
 
   if (!CanComposite()) {
     DidComposite();
     return;
   }
 
   AutoResolveRefLayers resolve(mCompositionManager);
+  SetShadowProperties(mLayerManager->GetRoot());
 
   if (aTarget) {
     mLayerManager->BeginTransactionWithDrawTarget(aTarget, *aRect);
   } else {
     mLayerManager->BeginTransaction();
   }
 
   if (mForceCompositionTask && !mOverrideComposeReadiness) {
@@ -754,36 +775,16 @@ CompositorParent::ForceComposeToTarget(D
 bool
 CompositorParent::CanComposite()
 {
   return mLayerManager &&
          mLayerManager->GetRoot() &&
          !mPaused;
 }
 
-// Go down the composite layer tree, setting properties to match their
-// content-side counterparts.
-static void
-SetShadowProperties(Layer* aLayer)
-{
-  // FIXME: Bug 717688 -- Do these updates in LayerTransactionParent::RecvUpdate.
-  LayerComposite* layerComposite = aLayer->AsLayerComposite();
-  // Set the layerComposite's base transform to the layer's base transform.
-  layerComposite->SetShadowTransform(aLayer->GetBaseTransform());
-  layerComposite->SetShadowTransformSetByAnimation(false);
-  layerComposite->SetShadowVisibleRegion(aLayer->GetVisibleRegion());
-  layerComposite->SetShadowClipRect(aLayer->GetClipRect());
-  layerComposite->SetShadowOpacity(aLayer->GetOpacity());
-
-  for (Layer* child = aLayer->GetFirstChild();
-      child; child = child->GetNextSibling()) {
-    SetShadowProperties(child);
-  }
-}
-
 void
 CompositorParent::ScheduleRotationOnCompositorThread(const TargetConfig& aTargetConfig,
                                                      bool aIsFirstPaint)
 {
   MOZ_ASSERT(IsInCompositorThread());
 
   if (!aIsFirstPaint &&
       !mCompositionManager->IsFirstPaint() &&
@@ -821,32 +822,30 @@ CompositorParent::ShadowLayersUpdated(La
     AutoResolveRefLayers resolve(mCompositionManager);
     mApzcTreeManager->UpdatePanZoomControllerTree(this, root, aIsFirstPaint,
         mRootLayerTreeID, aPaintSequenceNumber);
   }
 
   MOZ_ASSERT(aTransactionId > mPendingTransaction);
   mPendingTransaction = aTransactionId;
 
-  if (root) {
-    SetShadowProperties(root);
-  }
   if (aScheduleComposite) {
     ScheduleComposition();
     if (mPaused) {
       DidComposite();
     }
     // When testing we synchronously update the shadow tree with the animated
     // values to avoid race conditions when calling GetAnimationTransform etc.
     // (since the above SetShadowProperties will remove animation effects).
     // However, we only do this update when a composite operation is already
     // scheduled in order to better match the behavior under regular sampling
     // conditions.
     if (mIsTesting && root && mCurrentCompositeTask) {
       AutoResolveRefLayers resolve(mCompositionManager);
+      SetShadowProperties(mLayerManager->GetRoot());
       bool requestNextFrame =
         mCompositionManager->TransformShadowTree(mTestTime);
       if (!requestNextFrame) {
         CancelCurrentCompositeTask();
         // Pretend we composited in case someone is wating for this event.
         DidComposite();
       }
     }
@@ -869,16 +868,17 @@ CompositorParent::SetTestSampleTime(Laye
   }
 
   mIsTesting = true;
   mTestTime = aTime;
 
   // Update but only if we were already scheduled to animate
   if (mCompositionManager && mCurrentCompositeTask) {
     AutoResolveRefLayers resolve(mCompositionManager);
+    SetShadowProperties(mLayerManager->GetRoot());
     bool requestNextFrame = mCompositionManager->TransformShadowTree(aTime);
     if (!requestNextFrame) {
       CancelCurrentCompositeTask();
       // Pretend we composited in case someone is wating for this event.
       DidComposite();
     }
   }
 
@@ -1417,19 +1417,16 @@ CrossProcessCompositorParent::ShadowLaye
   const CompositorParent::LayerTreeState* state = CompositorParent::GetIndirectShadowTree(id);
   if (!state) {
     return;
   }
   MOZ_ASSERT(state->mParent);
   state->mParent->ScheduleRotationOnCompositorThread(aTargetConfig, aIsFirstPaint);
 
   Layer* shadowRoot = aLayerTree->GetRoot();
-  if (shadowRoot) {
-    SetShadowProperties(shadowRoot);
-  }
   UpdateIndirectTree(id, shadowRoot, aTargetConfig);
 
   state->mParent->NotifyShadowTreeTransaction(id, aIsFirstPaint, aScheduleComposite,
       aPaintSequenceNumber, aIsRepeatTransaction);
 
   // Send the 'remote paint ready' message to the content thread if it has already asked.
   if(mNotifyAfterRemotePaint)  {
     unused << SendRemotePaintIsReady();