Bug 1361970 - Make PostProcessLayers occlusion culling work against the surface we will draw to rather than the parent layer. r=mstange
☠☠ backed out by 7b6507f85ef5 ☠ ☠
authorMatt Woodrow <mwoodrow@mozilla.com>
Thu, 08 Jun 2017 15:26:50 +1200
changeset 413412 14ac21b548cf4f8bbec395429f9b941425265e73
parent 413411 5ea810dbc4affeeffb3694b99609435075a25697
child 413413 4f29bdedb50c5f60d58e88030cb37c0bec6cfab0
push id1490
push usermtabara@mozilla.com
push dateMon, 31 Jul 2017 14:08:16 +0000
treeherdermozilla-release@70e32e6bf15e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmstange
bugs1361970
milestone55.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 1361970 - Make PostProcessLayers occlusion culling work against the surface we will draw to rather than the parent layer. r=mstange
gfx/layers/composite/LayerManagerComposite.cpp
gfx/layers/composite/LayerManagerComposite.h
layout/base/UnitTransforms.h
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -232,94 +232,66 @@ LayerManagerComposite::BeginTransactionW
   }
 
   mIsCompositorReady = true;
   mCompositor->SetTargetContext(aTarget, aRect);
   mTarget = aTarget;
   mTargetBounds = aRect;
 }
 
-/**
- * Get accumulated transform of from the context creating layer to the
- * given layer.
- */
-static Matrix4x4
-GetAccTransformIn3DContext(Layer* aLayer) {
-  Matrix4x4 transform = aLayer->GetLocalTransform();
-  for (Layer* layer = aLayer->GetParent();
-       layer && layer->Extend3DContext();
-       layer = layer->GetParent()) {
-    transform = transform * layer->GetLocalTransform();
+void
+LayerManagerComposite::PostProcessLayers(nsIntRegion& aOpaqueRegion)
+{
+  RenderTargetIntRect bounds(ViewAs<RenderTargetPixel>(mRenderBounds));
+  LayerIntRegion visible;
+  PostProcessLayers(mRoot, bounds, ParentLayerIntPoint(), aOpaqueRegion, visible, Nothing());
+}
+
+// We want to skip directly through ContainerLayers that don't have an intermediate
+// surface. We compute occlusions for leaves and intermediate surfaces against
+// the layer that they actually composite into so that we can use the final (snapped)
+// effective transform.
+bool ShouldProcessLayer(Layer* aLayer)
+{
+  if (!aLayer->GetParent() ||
+      !aLayer->AsContainerLayer()) {
+    return true;
   }
-  return transform;
+
+  return aLayer->AsContainerLayer()->UseIntermediateSurface();
 }
 
 void
 LayerManagerComposite::PostProcessLayers(Layer* aLayer,
+                                         const RenderTargetIntRect& aClipRect,
+                                         const ParentLayerIntPoint& aOffset,
                                          nsIntRegion& aOpaqueRegion,
                                          LayerIntRegion& aVisibleRegion,
                                          const Maybe<ParentLayerIntRect>& aClipFromAncestors)
 {
-  if (aLayer->Extend3DContext()) {
-    // For layers participating 3D rendering context, their visible
-    // region should be empty (invisible), so we pass through them
-    // without doing anything.
-
-    // Direct children of the establisher may have a clip, becaue the
-    // item containing it; ex. of nsHTMLScrollFrame, may give it one.
-    Maybe<ParentLayerIntRect> layerClip =
-      aLayer->AsHostLayer()->GetShadowClipRect();
-    Maybe<ParentLayerIntRect> ancestorClipForChildren =
-      IntersectMaybeRects(layerClip, aClipFromAncestors);
-    MOZ_ASSERT(!layerClip || !aLayer->Combines3DTransformWithAncestors(),
-               "Only direct children of the establisher could have a clip");
-
-    for (Layer* child = aLayer->GetLastChild();
-         child;
-         child = child->GetPrevSibling()) {
-      PostProcessLayers(child, aOpaqueRegion, aVisibleRegion,
-                        ancestorClipForChildren);
-    }
-    return;
-  }
-
-  nsIntRegion localOpaque;
-  // Treat layers on the path to the root of the 3D rendering context as
-  // a giant layer if it is a leaf.
-  Matrix4x4 transform = GetAccTransformIn3DContext(aLayer);
-  Matrix transform2d;
-  Maybe<IntPoint> integerTranslation;
-  // 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 (transform.Is2D(&transform2d)) {
-    if (transform2d.IsIntegerTranslation()) {
-      integerTranslation = Some(IntPoint::Truncate(transform2d.GetTranslation()));
-      localOpaque = aOpaqueRegion;
-      localOpaque.MoveBy(-*integerTranslation);
-    }
-  }
 
   // Compute a clip that's the combination of our layer clip with the clip
   // from our ancestors.
   LayerComposite* composite = static_cast<LayerComposite*>(aLayer->AsHostLayer());
   Maybe<ParentLayerIntRect> layerClip = composite->GetShadowClipRect();
   MOZ_ASSERT(!layerClip || !aLayer->Combines3DTransformWithAncestors(),
              "The layer with a clip should not participate "
              "a 3D rendering context");
   Maybe<ParentLayerIntRect> outsideClip =
     IntersectMaybeRects(layerClip, aClipFromAncestors);
 
+  Matrix4x4 localTransform = aLayer->GetLocalTransform();
+
   // Convert the combined clip into our pre-transform coordinate space, so
   // that it can later be intersected with our visible region.
   // If our transform is a perspective, there's no meaningful insideClip rect
   // we can compute (it would need to be a cone).
   Maybe<LayerIntRect> insideClip;
-  if (outsideClip && !transform.HasPerspectiveComponent()) {
-    Matrix4x4 inverse = transform;
+  if (outsideClip && !localTransform.HasPerspectiveComponent()) {
+    Matrix4x4 inverse = localTransform;
     if (inverse.Invert()) {
       Maybe<LayerRect> insideClipFloat =
         UntransformBy(ViewAs<ParentLayerToLayerMatrix4x4>(inverse),
                       ParentLayerRect(*outsideClip),
                       LayerRect::MaxIntRect());
       if (insideClipFloat) {
         insideClipFloat->RoundOut();
         LayerIntRect insideClipInt;
@@ -331,28 +303,72 @@ LayerManagerComposite::PostProcessLayers
   }
 
   Maybe<ParentLayerIntRect> ancestorClipForChildren;
   if (insideClip) {
     ancestorClipForChildren =
       Some(ViewAs<ParentLayerPixel>(*insideClip, PixelCastJustification::MovingDownToChildren));
   }
 
+  if (!ShouldProcessLayer(aLayer)) {
+    MOZ_ASSERT(!aLayer->AsContainerLayer() || !aLayer->AsContainerLayer()->UseIntermediateSurface());
+    // For layers participating 3D rendering context, their visible
+    // region should be empty (invisible), so we pass through them
+    // without doing anything.
+    for (Layer* child = aLayer->GetLastChild();
+         child;
+         child = child->GetPrevSibling()) {
+      RenderTargetIntRect clipRect = child->CalculateScissorRect(aClipRect);
+      PostProcessLayers(child, clipRect, aOffset, aOpaqueRegion, aVisibleRegion,
+                        ancestorClipForChildren);
+    }
+    return;
+  }
+
+  nsIntRegion localOpaque;
+  // Treat layers on the path to the root of the 3D rendering context as
+  // a giant layer if it is a leaf.
+  Matrix4x4 transform = aLayer->GetEffectiveTransform();
+  Matrix transform2d;
+  Maybe<IntPoint> integerTranslation;
+  // 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 (transform.Is2D(&transform2d)) {
+    if (transform2d.IsIntegerTranslation()) {
+      integerTranslation = Some(IntPoint::Truncate(transform2d.GetTranslation()));
+      localOpaque = aOpaqueRegion;
+      localOpaque.MoveBy(-*integerTranslation);
+    }
+  }
+
   // Save the value of localOpaque, which currently stores the region obscured
   // by siblings (and uncles and such), before our descendants contribute to it.
   nsIntRegion obscured = localOpaque;
 
   // Recurse on our descendants, in front-to-back order. In this process:
   //  - Occlusions are computed for them, and they contribute to localOpaque.
   //  - They recalculate their visible regions, taking ancestorClipForChildren
   //    into account, and accumulate them into descendantsVisibleRegion.
   LayerIntRegion descendantsVisibleRegion;
+
+  // CalculateScissorRect calculates rects that are relative to the origin of the
+  // intermediate surface, not Layer coords. If we create an intermediate surface,
+  // provide the surface offset to our children so they can move the clip into
+  // the right coord space.
+  ParentLayerIntPoint childOffset = aOffset;
+  if (aLayer->AsContainerLayer() && aLayer->AsContainerLayer()->UseIntermediateSurface()) {
+    childOffset = ViewAs<ParentLayerPixel>(aLayer->AsContainerLayer()->GetIntermediateSurfaceRect().TopLeft(),
+                                           PixelCastJustification::MovingDownToChildren);
+  }
+
   bool hasPreserve3DChild = false;
   for (Layer* child = aLayer->GetLastChild(); child; child = child->GetPrevSibling()) {
-    PostProcessLayers(child, localOpaque, descendantsVisibleRegion, ancestorClipForChildren);
+    RenderTargetIntRect clipRect = child->CalculateScissorRect(aClipRect);
+    PostProcessLayers(child, clipRect, childOffset, localOpaque, descendantsVisibleRegion, ancestorClipForChildren);
     if (child->Extend3DContext()) {
       hasPreserve3DChild = true;
     }
   }
 
   // Recalculate our visible region.
   LayerIntRegion visible = composite->GetShadowVisibleRegion();
 
@@ -373,34 +389,35 @@ LayerManagerComposite::PostProcessLayers
   }
   composite->SetShadowVisibleRegion(visible);
 
   // Transform the newly calculated visible region into our parent's space,
   // apply our clip to it (if any), and accumulate it into |aVisibleRegion|
   // for the caller to use.
   ParentLayerIntRegion visibleParentSpace = TransformBy(
       ViewAs<LayerToParentLayerMatrix4x4>(transform), visible);
-  if (const Maybe<ParentLayerIntRect>& clipRect = composite->GetShadowClipRect()) {
-    visibleParentSpace.AndWith(*clipRect);
-  }
+
+  // Convert the clip from being in RenderTarget coords to Layer coords
+  // using the offset provided.
+  ParentLayerIntRect clip = TransformByTranslation(aClipRect, aOffset);
+
+  visibleParentSpace.AndWith(clip);
   aVisibleRegion.OrWith(ViewAs<LayerPixel>(visibleParentSpace,
       PixelCastJustification::MovingDownToChildren));
 
   // If we have a simple transform, then we can add our opaque area into
   // aOpaqueRegion.
   if (integerTranslation &&
       !aLayer->HasMaskLayers() &&
       aLayer->IsOpaqueForVisibility()) {
     if (aLayer->IsOpaque()) {
       localOpaque.OrWith(composite->GetFullyRenderedRegion());
     }
     localOpaque.MoveBy(*integerTranslation);
-    if (layerClip) {
-      localOpaque.AndWith(layerClip->ToUnknownRect());
-    }
+    localOpaque.AndWith(clip.ToUnknownRect());
     aOpaqueRegion.OrWith(localOpaque);
   }
 }
 
 void
 LayerManagerComposite::EndTransaction(const TimeStamp& aTimeStamp,
                                       EndTransactionFlags aFlags)
 {
@@ -444,27 +461,24 @@ LayerManagerComposite::EndTransaction(co
   MOZ_LAYERS_LOG(("]----- EndTransaction"));
 #endif
 }
 
 void
 LayerManagerComposite::UpdateAndRender()
 {
   nsIntRegion invalid;
-  bool didEffectiveTransforms = false;
+  // 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;
-  LayerIntRegion visible;
-  PostProcessLayers(mRoot, opaque, visible, Nothing());
+  PostProcessLayers(opaque);
 
   if (mClonedLayerTreeProperties) {
-    // Effective transforms are needed by ComputeDifferences().
-    mRoot->ComputeEffectiveTransforms(gfx::Matrix4x4());
-    didEffectiveTransforms = true;
-
     // We need to compute layer tree differences even if we're not going to
     // immediately use the resulting damage area, since ComputeDifferences
     // is also responsible for invalidates intermediate surfaces in
     // ContainerLayers.
     nsIntRegion changed = mClonedLayerTreeProperties->ComputeDifferences(mRoot, nullptr);
 
     if (mTarget) {
       // Since we're composing to an external target, we're not going to use
@@ -497,22 +511,16 @@ LayerManagerComposite::UpdateAndRender()
     mClonedLayerTreeProperties = LayerProperties::CloneFrom(GetRoot());
     return;
   }
 
   // We don't want our debug overlay to cause more frames to happen
   // so we will invalidate after we've decided if something changed.
   InvalidateDebugOverlay(invalid, mRenderBounds);
 
-  if (!didEffectiveTransforms) {
-    // 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());
-  }
-
   Render(invalid, opaque);
 #if defined(MOZ_WIDGET_ANDROID)
   RenderToPresentationSurface();
 #endif
   mWindowOverlayChanged = false;
 
   // Update cached layer tree information.
   mClonedLayerTreeProperties = LayerProperties::CloneFrom(GetRoot());
@@ -1104,18 +1112,17 @@ LayerManagerComposite::RenderToPresentat
                                                   rotation);
   viewMatrix.Invert(); // unrotate
   viewMatrix.PostScale(scale, scale);
   viewMatrix.PostTranslate(offset.x, offset.y);
   Matrix4x4 matrix = Matrix4x4::From2D(viewMatrix);
 
   mRoot->ComputeEffectiveTransforms(matrix);
   nsIntRegion opaque;
-  LayerIntRegion visible;
-  PostProcessLayers(mRoot, opaque, visible, Nothing());
+  PostProcessLayers(opaque);
 
   nsIntRegion invalid;
   IntRect bounds = IntRect::Truncate(0, 0, scale * pageWidth, actualHeight);
   IntRect rect, actualBounds;
   MOZ_ASSERT(mRoot->GetOpacity() == 1);
   mCompositor->BeginFrame(invalid, nullptr, bounds, nsIntRegion(), &rect, &actualBounds);
 
   // The Java side of Fennec sets a scissor rect that accounts for
--- a/gfx/layers/composite/LayerManagerComposite.h
+++ b/gfx/layers/composite/LayerManagerComposite.h
@@ -300,17 +300,20 @@ public:
    *     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.
    *
    *   - Recomputes visible regions to account for async transforms.
    *     Each layer accumulates into |aVisibleRegion| its post-transform
    *     (including async transforms) visible region.
    */
+  void PostProcessLayers(nsIntRegion& aOpaqueRegion);
   void PostProcessLayers(Layer* aLayer,
+                         const RenderTargetIntRect& aClipRect,
+                         const ParentLayerIntPoint& aOffset,
                          nsIntRegion& aOpaqueRegion,
                          LayerIntRegion& aVisibleRegion,
                          const Maybe<ParentLayerIntRect>& aClipFromAncestors);
 
   /**
    * 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.
    */
--- a/layout/base/UnitTransforms.h
+++ b/layout/base/UnitTransforms.h
@@ -289,11 +289,22 @@ UntransformVector(const gfx::Matrix4x4Ty
   gfx::Point4DTyped<TargetUnits> projectedAnchor = aTransform.ProjectPoint(aAnchor);
   gfx::Point4DTyped<TargetUnits> projectedTarget = aTransform.ProjectPoint(aAnchor + aVector);
   if (!projectedAnchor.HasPositiveWCoord() || !projectedTarget.HasPositiveWCoord()){
     return Nothing();
   }
   return Some(projectedTarget.As2DPoint() - projectedAnchor.As2DPoint());
 }
 
+
+template <typename TargetUnits, typename SourceUnits>
+static gfx::IntRectTyped<TargetUnits>
+TransformByTranslation(const gfx::IntRectTyped<SourceUnits>& aRect,
+                       const gfx::IntPointTyped<TargetUnits>& aOffset)
+{
+  nsIntRect rect = aRect.ToUnknownRect();
+  rect.MoveBy(aOffset.ToUnknownPoint());
+  return ViewAs<TargetUnits>(rect);
+}
+
 } // namespace mozilla
 
 #endif