Bug 1373335 - Make sure we clip the opaque region in render target coordinate space, since complex transforms mean that we can't always have a rectangular representation of clips in layer space. r?mstange draft
authorMatt Woodrow <mwoodrow@mozilla.com>
Wed, 21 Jun 2017 10:50:46 +1200
changeset 597756 389071d9f85f1844f278149f0114ae490f906a80
parent 596219 95543bdc59bd038a3d5d084b85a4fec493c349ee
child 597855 7961c09ab361a169d750940a1c5c220e4a05799c
push id65020
push usermwoodrow@mozilla.com
push dateTue, 20 Jun 2017 22:51:08 +0000
reviewersmstange
bugs1373335
milestone56.0a1
Bug 1373335 - Make sure we clip the opaque region in render target coordinate space, since complex transforms mean that we can't always have a rectangular representation of clips in layer space. r?mstange MozReview-Commit-ID: EqLgeXgohqH
gfx/layers/composite/LayerManagerComposite.cpp
gfx/layers/composite/LayerManagerComposite.h
layout/base/UnitTransforms.h
layout/base/Units.h
layout/reftests/transform-3d/perspective-clipping-2-ref.html
layout/reftests/transform-3d/perspective-clipping-2.html
layout/reftests/transform-3d/reftest.list
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -236,27 +236,30 @@ LayerManagerComposite::BeginTransactionW
   mTarget = aTarget;
   mTargetBounds = aRect;
 }
 
 void
 LayerManagerComposite::PostProcessLayers(nsIntRegion& aOpaqueRegion)
 {
   LayerIntRegion visible;
-  PostProcessLayers(mRoot, aOpaqueRegion, visible, Nothing());
+  LayerComposite* rootComposite = static_cast<LayerComposite*>(mRoot->AsHostLayer());
+  PostProcessLayers(mRoot, aOpaqueRegion, visible,
+                    ViewAs<RenderTargetPixel>(rootComposite->GetShadowClipRect(),
+                                              PixelCastJustification::RenderTargetIsParentLayerForRoot),
+                    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()) {
+  if (!aLayer->AsContainerLayer()) {
     return true;
   }
 
   return aLayer->AsContainerLayer()->UseIntermediateSurface();
 }
 
 /**
  * Get accumulated transform of from the context creating layer to the
@@ -272,16 +275,17 @@ GetAccTransformIn3DContext(Layer* aLayer
   }
   return transform;
 }
 
 void
 LayerManagerComposite::PostProcessLayers(Layer* aLayer,
                                          nsIntRegion& aOpaqueRegion,
                                          LayerIntRegion& aVisibleRegion,
+                                         const Maybe<RenderTargetIntRect>& aRenderTargetClip,
                                          const Maybe<ParentLayerIntRect>& aClipFromAncestors)
 {
 
   // 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(),
@@ -289,17 +293,19 @@ LayerManagerComposite::PostProcessLayers
              "a 3D rendering context");
   Maybe<ParentLayerIntRect> outsideClip =
     IntersectMaybeRects(layerClip, aClipFromAncestors);
 
   Maybe<LayerIntRect> insideClip;
   if (aLayer->Extend3DContext()) {
     // If we're preserve-3d just pass the clip rect down directly, and we'll do the
     // conversion at the preserve-3d leaf Layer.
-    insideClip = Some(ViewAs<LayerPixel>(*outsideClip, PixelCastJustification::MovingDownToChildren));
+    if (outsideClip) {
+      insideClip = Some(ViewAs<LayerPixel>(*outsideClip, PixelCastJustification::MovingDownToChildren));
+    }
   } else if (outsideClip) {
     // 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).
     Matrix4x4 localTransform = GetAccTransformIn3DContext(aLayer);
     if (!localTransform.HasPerspectiveComponent() && localTransform.Invert()) {
       LayerRect insideClipFloat =
@@ -316,25 +322,35 @@ LayerManagerComposite::PostProcessLayers
 
   Maybe<ParentLayerIntRect> ancestorClipForChildren;
   if (insideClip) {
     ancestorClipForChildren =
       Some(ViewAs<ParentLayerPixel>(*insideClip, PixelCastJustification::MovingDownToChildren));
   }
 
   if (!ShouldProcessLayer(aLayer)) {
-    MOZ_ASSERT(!aLayer->AsContainerLayer() || !aLayer->AsContainerLayer()->UseIntermediateSurface());
+    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()) {
+      LayerComposite* childComposite = static_cast<LayerComposite*>(child->AsHostLayer());
+      Maybe<RenderTargetIntRect> renderTargetClip = aRenderTargetClip;
+      if (childComposite->GetShadowClipRect()) {
+        RenderTargetIntRect clip = TransformBy(ViewAs<ParentLayerToRenderTargetMatrix4x4>(
+          aLayer->GetEffectiveTransform(),
+          PixelCastJustification::RenderTargetIsParentLayerForRoot),
+                                               *childComposite->GetShadowClipRect());
+        renderTargetClip = IntersectMaybeRects(renderTargetClip, Some(clip));
+      }
+
       PostProcessLayers(child, aOpaqueRegion, aVisibleRegion,
-                        ancestorClipForChildren);
+                        renderTargetClip, 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();
@@ -358,17 +374,22 @@ LayerManagerComposite::PostProcessLayers
   // 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;
 
   bool hasPreserve3DChild = false;
   for (Layer* child = aLayer->GetLastChild(); child; child = child->GetPrevSibling()) {
-    PostProcessLayers(child, localOpaque, descendantsVisibleRegion, ancestorClipForChildren);
+    MOZ_ASSERT(aLayer->AsContainerLayer()->UseIntermediateSurface());
+    LayerComposite* childComposite = static_cast<LayerComposite*>(child->AsHostLayer());
+    PostProcessLayers(child, localOpaque, descendantsVisibleRegion,
+                      ViewAs<RenderTargetPixel>(childComposite->GetShadowClipRect(),
+                                                PixelCastJustification::RenderTargetIsParentLayerForRoot),
+                      ancestorClipForChildren);
     if (child->Extend3DContext()) {
       hasPreserve3DChild = true;
     }
   }
 
   // Recalculate our visible region.
   LayerIntRegion visible = composite->GetShadowVisibleRegion();
 
@@ -400,20 +421,20 @@ LayerManagerComposite::PostProcessLayers
   // 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());
     }
-    if (insideClip) {
-      localOpaque.AndWith(insideClip->ToUnknownRect());
+    localOpaque.MoveBy(*integerTranslation);
+    if (aRenderTargetClip) {
+      localOpaque.AndWith(aRenderTargetClip->ToUnknownRect());
     }
-    localOpaque.MoveBy(*integerTranslation);
     aOpaqueRegion.OrWith(localOpaque);
   }
 }
 
 void
 LayerManagerComposite::EndTransaction(const TimeStamp& aTimeStamp,
                                       EndTransactionFlags aFlags)
 {
--- a/gfx/layers/composite/LayerManagerComposite.h
+++ b/gfx/layers/composite/LayerManagerComposite.h
@@ -299,21 +299,29 @@ public:
    *   - Applies occlusion culling. This 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.
    *
    *   - Recomputes visible regions to account for async transforms.
    *     Each layer accumulates into |aVisibleRegion| its post-transform
    *     (including async transforms) visible region.
+   *
+   *   - aRenderTargetClip is the exact clip required for aLayer, in the coordinates
+   *     of the nearest render target (the same as GetEffectiveTransform).
+   *
+   *   - 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);
 
   /**
    * 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
   {
--- a/layout/base/UnitTransforms.h
+++ b/layout/base/UnitTransforms.h
@@ -104,16 +104,23 @@ gfx::IntRegionTyped<TargetUnits> ViewAs(
   return gfx::IntRegionTyped<TargetUnits>::FromUnknownRegion(aRegion.ToUnknownRegion());
 }
 template <class NewTargetUnits, class OldTargetUnits, class SourceUnits>
 gfx::ScaleFactor<SourceUnits, NewTargetUnits> ViewTargetAs(
     const gfx::ScaleFactor<SourceUnits, OldTargetUnits>& aScaleFactor,
     PixelCastJustification) {
   return gfx::ScaleFactor<SourceUnits, NewTargetUnits>(aScaleFactor.scale);
 }
+template <class TargetUnits, class SourceUnits>
+Maybe<gfx::IntRectTyped<TargetUnits>> ViewAs(const Maybe<gfx::IntRectTyped<SourceUnits>>& aRect, PixelCastJustification aJustification) {
+  if (aRect.isSome()) {
+    return Some(ViewAs<TargetUnits>(aRect.value(), aJustification));
+  }
+  return Nothing();
+}
 // Unlike the other functions in this category, this function takes the
 // target matrix type, rather than its source and target unit types, as
 // the explicit template argument, so an example invocation is:
 //    ViewAs<ScreenToLayerMatrix4x4>(otherTypedMatrix, justification)
 // The reason is that if it took the source and target unit types as two
 // template arguments, there may be some confusion as to which is the
 // source and which is the target.
 template <class TargetMatrix, class SourceMatrixSourceUnits, class SourceMatrixTargetUnits>
--- a/layout/base/Units.h
+++ b/layout/base/Units.h
@@ -181,16 +181,17 @@ typedef gfx::ScaleFactors2D<ParentLayerP
 
 typedef gfx::Matrix4x4Typed<LayoutDevicePixel, LayoutDevicePixel> LayoutDeviceToLayoutDeviceMatrix4x4;
 typedef gfx::Matrix4x4Typed<LayerPixel, ParentLayerPixel> LayerToParentLayerMatrix4x4;
 typedef gfx::Matrix4x4Typed<ScreenPixel, ScreenPixel> ScreenToScreenMatrix4x4;
 typedef gfx::Matrix4x4Typed<ScreenPixel, ParentLayerPixel> ScreenToParentLayerMatrix4x4;
 typedef gfx::Matrix4x4Typed<ParentLayerPixel, LayerPixel> ParentLayerToLayerMatrix4x4;
 typedef gfx::Matrix4x4Typed<ParentLayerPixel, ScreenPixel> ParentLayerToScreenMatrix4x4;
 typedef gfx::Matrix4x4Typed<ParentLayerPixel, ParentLayerPixel> ParentLayerToParentLayerMatrix4x4;
+typedef gfx::Matrix4x4Typed<ParentLayerPixel, RenderTargetPixel> ParentLayerToRenderTargetMatrix4x4;
 
 /*
  * The pixels that content authors use to specify sizes in.
  */
 struct CSSPixel {
 
   // Conversions from app units
 
new file mode 100644
--- /dev/null
+++ b/layout/reftests/transform-3d/perspective-clipping-2-ref.html
@@ -0,0 +1,6 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+    <div style="width:100px; height:200px; background-color:green"></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/transform-3d/perspective-clipping-2.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+  <div style="width:100px; height:100px; background-color:green"></div>
+  <div style="width:100px; height:100px; overflow:hidden">
+    <div style="transform: translateY(-100px) perspective(1px); will-change:transform">
+        <div style="background-color:red; width:100px; height:100px"></div>
+        <div style="background-color:green; width:100px; height:100px"></div>
+    </div>
+  </div>
+</body>
+</html>
--- a/layout/reftests/transform-3d/reftest.list
+++ b/layout/reftests/transform-3d/reftest.list
@@ -34,16 +34,17 @@ fuzzy-if(skiaContent,1,4) == matrix3d-1a
 == rotate3d-1a.html rotatex-1-ref.html
 == rotate3d-2a.html rotatey-1-ref.html
 != backface-visibility-1a.html about:blank
 == backface-visibility-1b.html about:blank
 == backface-visibility-1c.html about:blank
 fuzzy-if(winWidget&&!layersGPUAccelerated,1,251) == backface-visibility-2.html backface-visibility-2-ref.html
 fails-if(webrender) == backface-visibility-3.html backface-visibility-3-ref.html
 == perspective-clipping-1.html perspective-clipping-1-ref.html
+== perspective-clipping-2.html perspective-clipping-2-ref.html
 != perspective-origin-1a.html rotatex-perspective-1a.html
 == perspective-origin-1b.html perspective-origin-1a.html
 fuzzy(3,99) random-if(Android&&!browserIsRemote) == perspective-origin-2a.html perspective-origin-2-ref.html # subpixel AA, bug 732568
 fuzzy-if(winWidget&&!layersGPUAccelerated,1,61) == perspective-origin-3a.html perspective-origin-3-ref.html
 == perspective-origin-4a.html perspective-origin-4-ref.html
 fails-if(webrender) == perspective-zindex.html green-rect.html
 fails-if(webrender) == perspective-zindex-2.html green-rect.html
 fails-if(webrender) != sorting-1a.html sorting-1-ref.html