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
--- 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