Bug 1538969 - Handle non-integer transforms when doing occlusions in PostProcessLayers. r=mstange
authorMatt Woodrow <mwoodrow@mozilla.com>
Wed, 10 Apr 2019 08:08:24 +0000
changeset 468755 3f2ce3a4c4e695b26a2f351a728edb6d33e562e9
parent 468754 4c8055acb67b68829fa037cc2127812d0f9b41a1
child 468756 c2b1753a416b3201552108700ecd3d17686d5d4f
push id112755
push userdvarga@mozilla.com
push dateWed, 10 Apr 2019 22:06:41 +0000
treeherdermozilla-inbound@606f85641d0b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmstange
bugs1538969
milestone68.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 1538969 - Handle non-integer transforms when doing occlusions in PostProcessLayers. r=mstange Differential Revision: https://phabricator.services.mozilla.com/D25186
gfx/layers/composite/LayerManagerComposite.cpp
gfx/layers/composite/LayerManagerComposite.h
testing/web-platform/meta/css/css-transforms/transform3d-backface-visibility-007.html.ini
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -210,26 +210,100 @@ void LayerManagerComposite::BeginTransac
   }
 
   mIsCompositorReady = true;
   mCompositor->SetTargetContext(aTarget, aRect);
   mTarget = aTarget;
   mTargetBounds = aRect;
 }
 
+template <typename Units>
+static IntRectTyped<Units> TransformRect(const IntRectTyped<Units>& aRect,
+                                         const Matrix& aTransform,
+                                         bool aRoundIn = false) {
+  if (aRect.IsEmpty()) {
+    return IntRectTyped<Units>();
+  }
+
+  Rect rect(aRect.X(), aRect.Y(), aRect.Width(), aRect.Height());
+  rect = aTransform.TransformBounds(rect);
+  if (aRoundIn) {
+    MOZ_ASSERT(aTransform.PreservesAxisAlignedRectangles());
+    rect.RoundIn();
+  } else {
+    rect.RoundOut();
+  }
+
+  IntRect intRect;
+  if (!rect.ToIntRect(&intRect)) {
+    intRect = IntRect::MaxIntRect();
+  }
+
+  return ViewAs<Units>(intRect);
+}
+
+template <typename Units>
+static IntRectTyped<Units> TransformRect(const IntRectTyped<Units>& aRect,
+                                         const Matrix4x4& aTransform,
+                                         bool aRoundIn = false) {
+  if (aRect.IsEmpty()) {
+    return IntRectTyped<Units>();
+  }
+
+  Rect rect(aRect.X(), aRect.Y(), aRect.Width(), aRect.Height());
+  rect = aTransform.TransformAndClipBounds(rect, Rect::MaxIntRect());
+  if (aRoundIn) {
+    rect.RoundIn();
+  } else {
+    rect.RoundOut();
+  }
+
+  IntRect intRect;
+  if (!rect.ToIntRect(&intRect)) {
+    intRect = IntRect::MaxIntRect();
+  }
+
+  return ViewAs<Units>(intRect);
+}
+
+template <typename Units, typename MatrixType>
+static IntRectTyped<Units> TransformRectRoundIn(
+    const IntRectTyped<Units>& aRect, const MatrixType& aTransform) {
+  return TransformRect(aRect, aTransform, true);
+}
+
+template <typename Units, typename MatrixType>
+static void AddTransformedRegion(IntRegionTyped<Units>& aDest,
+                                 const IntRegionTyped<Units>& aSource,
+                                 const MatrixType& aTransform) {
+  for (auto iter = aSource.RectIter(); !iter.Done(); iter.Next()) {
+    aDest.Or(aDest, TransformRect(iter.Get(), aTransform));
+  }
+  aDest.SimplifyOutward(20);
+}
+
+template <typename Units, typename MatrixType>
+static void AddTransformedRegionRoundIn(IntRegionTyped<Units>& aDest,
+                                        const IntRegionTyped<Units>& aSource,
+                                        const MatrixType& aTransform) {
+  for (auto iter = aSource.RectIter(); !iter.Done(); iter.Next()) {
+    aDest.Or(aDest, TransformRectRoundIn(iter.Get(), aTransform));
+  }
+}
+
 void LayerManagerComposite::PostProcessLayers(nsIntRegion& aOpaqueRegion) {
   LayerIntRegion visible;
   LayerComposite* rootComposite =
       static_cast<LayerComposite*>(mRoot->AsHostLayer());
   PostProcessLayers(
       mRoot, aOpaqueRegion, visible,
       ViewAs<RenderTargetPixel>(
           rootComposite->GetShadowClipRect(),
           PixelCastJustification::RenderTargetIsParentLayerForRoot),
-      Nothing());
+      Nothing(), true);
 }
 
 // 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.
 static bool ShouldProcessLayer(Layer* aLayer) {
   if (!aLayer->AsContainerLayer()) {
@@ -237,17 +311,18 @@ static bool ShouldProcessLayer(Layer* aL
   }
 
   return aLayer->AsContainerLayer()->UseIntermediateSurface();
 }
 
 void LayerManagerComposite::PostProcessLayers(
     Layer* aLayer, nsIntRegion& aOpaqueRegion, LayerIntRegion& aVisibleRegion,
     const Maybe<RenderTargetIntRect>& aRenderTargetClip,
-    const Maybe<ParentLayerIntRect>& aClipFromAncestors) {
+    const Maybe<ParentLayerIntRect>& aClipFromAncestors,
+    bool aCanContributeOpaque) {
   // 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");
@@ -308,38 +383,42 @@ void LayerManagerComposite::PostProcessL
         RenderTargetIntRect clip = TransformBy(
             ViewAs<ParentLayerToRenderTargetMatrix4x4>(
                 aLayer->GetEffectiveTransform(),
                 PixelCastJustification::RenderTargetIsParentLayerForRoot),
             *childComposite->GetShadowClipRect());
         renderTargetClip = IntersectMaybeRects(renderTargetClip, Some(clip));
       }
 
-      PostProcessLayers(child, opaqueRegion, aVisibleRegion, renderTargetClip,
-                        ancestorClipForChildren);
+      PostProcessLayers(
+          child, opaqueRegion, aVisibleRegion, renderTargetClip,
+          ancestorClipForChildren,
+          aCanContributeOpaque &
+              !(aLayer->GetContentFlags() & Layer::CONTENT_BACKFACE_HIDDEN));
     }
     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;
+  bool canTransformOpaqueRegion = 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 (transform.Is2D(&transform2d)) {
-    if (transform2d.IsIntegerTranslation()) {
-      integerTranslation =
-          Some(IntPoint::Truncate(transform2d.GetTranslation()));
-      localOpaque = opaqueRegion;
-      localOpaque.MoveBy(-*integerTranslation);
-    }
+  if (aCanContributeOpaque &&
+      !(aLayer->GetContentFlags() & Layer::CONTENT_BACKFACE_HIDDEN) &&
+      transform.Is2D(&transform2d) &&
+      transform2d.PreservesAxisAlignedRectangles()) {
+    Matrix inverse = transform2d;
+    inverse.Invert();
+    AddTransformedRegionRoundIn(localOpaque, opaqueRegion, inverse);
+    canTransformOpaqueRegion = true;
   }
 
   // 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.
@@ -353,17 +432,17 @@ void LayerManagerComposite::PostProcessL
     MOZ_ASSERT(aLayer->AsContainerLayer()->UseIntermediateSurface());
     LayerComposite* childComposite =
         static_cast<LayerComposite*>(child->AsHostLayer());
     PostProcessLayers(
         child, localOpaque, descendantsVisibleRegion,
         ViewAs<RenderTargetPixel>(
             childComposite->GetShadowClipRect(),
             PixelCastJustification::RenderTargetIsParentLayerForRoot),
-        ancestorClipForChildren);
+        ancestorClipForChildren, true);
     if (child->Extend3DContext()) {
       hasPreserve3DChild = true;
     }
   }
 
   // Recalculate our visible region.
   LayerIntRegion visible = composite->GetShadowVisibleRegion();
 
@@ -389,26 +468,27 @@ void LayerManagerComposite::PostProcessL
   // for the caller to use.
   ParentLayerIntRegion visibleParentSpace =
       TransformBy(ViewAs<LayerToParentLayerMatrix4x4>(transform), visible);
   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() &&
+  if (canTransformOpaqueRegion && !aLayer->HasMaskLayers() &&
       aLayer->IsOpaqueForVisibility()) {
     if (aLayer->IsOpaque()) {
       localOpaque.OrWith(composite->GetFullyRenderedRegion());
     }
-    localOpaque.MoveBy(*integerTranslation);
+    nsIntRegion parentSpaceOpaque;
+    AddTransformedRegionRoundIn(parentSpaceOpaque, localOpaque, transform2d);
     if (aRenderTargetClip) {
-      localOpaque.AndWith(aRenderTargetClip->ToUnknownRect());
+      parentSpaceOpaque.AndWith(aRenderTargetClip->ToUnknownRect());
     }
-    opaqueRegion.OrWith(localOpaque);
+    opaqueRegion.OrWith(parentSpaceOpaque);
   }
 }
 
 void LayerManagerComposite::EndTransaction(const TimeStamp& aTimeStamp,
                                            EndTransactionFlags aFlags) {
   NS_ASSERTION(mInTransaction, "Didn't call BeginTransaction?");
   NS_ASSERTION(!(aFlags & END_NO_COMPOSITE),
                "Shouldn't get END_NO_COMPOSITE here");
@@ -1371,43 +1451,16 @@ Matrix4x4 HostLayer::GetShadowTransform(
   transform.PostScale(layer->GetPostXScale(), layer->GetPostYScale(), 1.0f);
   if (const ContainerLayer* c = layer->AsContainerLayer()) {
     transform.PreScale(c->GetPreXScale(), c->GetPreYScale(), 1.0f);
   }
 
   return transform;
 }
 
-static LayerIntRect TransformRect(const LayerIntRect& aRect,
-                                  const Matrix4x4& aTransform) {
-  if (aRect.IsEmpty()) {
-    return LayerIntRect();
-  }
-
-  Rect rect(aRect.X(), aRect.Y(), aRect.Width(), aRect.Height());
-  rect = aTransform.TransformAndClipBounds(rect, Rect::MaxIntRect());
-  rect.RoundOut();
-
-  IntRect intRect;
-  if (!rect.ToIntRect(&intRect)) {
-    intRect = IntRect::MaxIntRect();
-  }
-
-  return ViewAs<LayerPixel>(intRect);
-}
-
-static void AddTransformedRegion(LayerIntRegion& aDest,
-                                 const LayerIntRegion& aSource,
-                                 const Matrix4x4& aTransform) {
-  for (auto iter = aSource.RectIter(); !iter.Done(); iter.Next()) {
-    aDest.Or(aDest, TransformRect(iter.Get(), aTransform));
-  }
-  aDest.SimplifyOutward(20);
-}
-
 // Async animations can move child layers without updating our visible region.
 // PostProcessLayers will recompute visible regions for layers with an
 // intermediate surface, but otherwise we need to do it now.
 static void ComputeVisibleRegionForChildren(ContainerLayer* aContainer,
                                             LayerIntRegion& aResult) {
   for (Layer* l = aContainer->GetFirstChild(); l; l = l->GetNextSibling()) {
     if (l->Extend3DContext()) {
       MOZ_ASSERT(l->AsContainerLayer());
--- a/gfx/layers/composite/LayerManagerComposite.h
+++ b/gfx/layers/composite/LayerManagerComposite.h
@@ -326,17 +326,18 @@ class LayerManagerComposite final : publ
    *   - aClipFromAncestors is the approximate combined clip from all
    *     ancestors, in the coordinate space of our parent, but maybe be an
    *     overestimate in the presence of complex transforms.
    */
   void PostProcessLayers(nsIntRegion& aOpaqueRegion);
   void PostProcessLayers(Layer* aLayer, nsIntRegion& aOpaqueRegion,
                          LayerIntRegion& aVisibleRegion,
                          const Maybe<RenderTargetIntRect>& aRenderTargetClip,
-                         const Maybe<ParentLayerIntRect>& aClipFromAncestors);
+                         const Maybe<ParentLayerIntRect>& aClipFromAncestors,
+                         bool aCanContributeOpaque);
 
   /**
    * 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:
deleted file mode 100644
--- a/testing/web-platform/meta/css/css-transforms/transform3d-backface-visibility-007.html.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[transform3d-backface-visibility-007.html]
-  expected:
-    if webrender: PASS
-    if (os == "win") and (processor == "aarch64"): PASS
-    FAIL