Bug 663776. Part 0.5: Mark layers that could have their transforms changed via off-main-thread animations or empty transactions, and treat all ThebesLayerOGL descendants of such layers as potentially resampled so that their buffers are put into the correct state and size. r=mattwoodrow
authorRobert O'Callahan <robert@ocallahan.org>
Wed, 12 Dec 2012 18:21:23 +1300
changeset 121346 febf7b3ad73179d24167b7413d67b4d4a5420e89
parent 121345 f0951ef3bc4d86b761b3725e7219c2f2752337af
child 121347 4b11f6769b278cd9c9a165e643df832a67a77b46
push idunknown
push userunknown
push dateunknown
reviewersmattwoodrow
bugs663776
milestone20.0a1
Bug 663776. Part 0.5: Mark layers that could have their transforms changed via off-main-thread animations or empty transactions, and treat all ThebesLayerOGL descendants of such layers as potentially resampled so that their buffers are put into the correct state and size. r=mattwoodrow
gfx/layers/Layers.cpp
gfx/layers/Layers.h
gfx/layers/basic/BasicThebesLayer.cpp
gfx/layers/d3d10/ThebesLayerD3D10.cpp
gfx/layers/d3d9/ThebesLayerD3D9.cpp
gfx/layers/opengl/ThebesLayerOGL.cpp
layout/base/nsDisplayList.cpp
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -589,16 +589,36 @@ Layer::SnapTransform(const gfx3DMatrix& 
       *aResidualTransform = matrix2D * snappedMatrixInverse;
     }
   } else {
     result = aTransform;
   }
   return result;
 }
 
+static bool
+AncestorLayerMayChangeTransform(Layer* aLayer)
+{
+  for (Layer* l = aLayer; l; l = l->GetParent()) {
+    if (l->GetContentFlags() & Layer::CONTENT_MAY_CHANGE_TRANSFORM) {
+      return true;
+    }
+  }
+  return false;
+}
+
+bool
+Layer::MayResample()
+{
+  gfxMatrix transform2d;
+  return !GetEffectiveTransform().Is2D(&transform2d) ||
+         transform2d.HasNonIntegerTranslation() ||
+         AncestorLayerMayChangeTransform(this);
+}
+
 nsIntRect
 Layer::CalculateScissorRect(const nsIntRect& aCurrentScissorRect,
                             const gfxMatrix* aWorldTransform)
 {
   ContainerLayer* container = GetParent();
   NS_ASSERTION(container, "This can't be called on the root!");
 
   // Establish initial clip rect: it's either the one passed in, or
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -571,17 +571,23 @@ public:
      * This should never be set at the same time as CONTENT_OPAQUE.
      */
     CONTENT_COMPONENT_ALPHA = 0x02,
 
     /**
      * If this is set then this layer is part of a preserve-3d group, and should
      * be sorted with sibling layers that are also part of the same group.
      */
-    CONTENT_PRESERVE_3D = 0x04
+    CONTENT_PRESERVE_3D = 0x04,
+    /**
+     * This indicates that the transform may be changed on during an empty
+     * transaction where there is no possibility of redrawing the content, so the
+     * implementation should be ready for that.
+     */
+    CONTENT_MAY_CHANGE_TRANSFORM = 0x08
   };
   /**
    * CONSTRUCTION PHASE ONLY
    * This lets layout make some promises about what will be drawn into the
    * visible region of the ThebesLayer. This enables internal quality
    * and performance optimizations.
    */
   void SetContentFlags(uint32_t aFlags)
@@ -1078,16 +1084,24 @@ protected:
    * transform matrix
    * @param aResidualTransform a transform to apply before mEffectiveTransform
    * in order to get the results to completely match aTransform
    */
   gfx3DMatrix SnapTransform(const gfx3DMatrix& aTransform,
                             const gfxRect& aSnapRect,
                             gfxMatrix* aResidualTransform);
 
+  /**
+   * Returns true if this layer's effective transform is not just
+   * a translation by integers, or if this layer or some ancestor layer
+   * is marked as having a transform that may change without a full layer
+   * transaction.
+   */
+  bool MayResample();
+
   LayerManager* mManager;
   ContainerLayer* mParent;
   Layer* mNextSibling;
   Layer* mPrevSibling;
   void* mImplData;
   nsRefPtr<Layer> mMaskLayer;
   gfx::UserData mUserData;
   nsIntRegion mVisibleRegion;
--- a/gfx/layers/basic/BasicThebesLayer.cpp
+++ b/gfx/layers/basic/BasicThebesLayer.cpp
@@ -155,19 +155,17 @@ BasicThebesLayer::PaintThebes(gfxContext
 
   {
     uint32_t flags = 0;
 #ifndef MOZ_WIDGET_ANDROID
     if (BasicManager()->CompositorMightResample()) {
       flags |= ThebesLayerBuffer::PAINT_WILL_RESAMPLE;
     }
     if (!(flags & ThebesLayerBuffer::PAINT_WILL_RESAMPLE)) {
-      gfxMatrix transform;
-      if (!GetEffectiveTransform().CanDraw2D(&transform) ||
-          transform.HasNonIntegerTranslation()) {
+      if (MayResample()) {
         flags |= ThebesLayerBuffer::PAINT_WILL_RESAMPLE;
       }
     }
 #endif
     if (mDrawAtomically) {
       flags |= ThebesLayerBuffer::PAINT_NO_ROTATION;
     }
     Buffer::PaintState state =
--- a/gfx/layers/d3d10/ThebesLayerD3D10.cpp
+++ b/gfx/layers/d3d10/ThebesLayerD3D10.cpp
@@ -160,19 +160,17 @@ ThebesLayerD3D10::Validate(ReadbackProce
   // If we have a transform that requires resampling of our texture, then
   // we need to make sure we don't sample pixels that haven't been drawn.
   // We clamp sample coordinates to the texture rect, but when the visible region
   // doesn't fill the entire texture rect we need to make sure we draw all the
   // pixels in the texture rect anyway in case they get sampled.
   nsIntRegion neededRegion = mVisibleRegion;
   if (!neededRegion.GetBounds().IsEqualInterior(newTextureRect) ||
       neededRegion.GetNumRects() > 1) {
-    gfxMatrix transform2d;
-    if (!GetEffectiveTransform().Is2D(&transform2d) ||
-        transform2d.HasNonIntegerTranslation()) {
+    if (MayResample()) {
       neededRegion = newTextureRect;
       if (mode == SURFACE_OPAQUE) {
         // We're going to paint outside the visible region, but layout hasn't
         // promised that it will paint opaquely there, so we'll have to
         // treat this layer as transparent.
         mode = SURFACE_SINGLE_CHANNEL_ALPHA;
       }
     }
--- a/gfx/layers/d3d9/ThebesLayerD3D9.cpp
+++ b/gfx/layers/d3d9/ThebesLayerD3D9.cpp
@@ -187,19 +187,17 @@ ThebesLayerD3D9::RenderThebesLayer(Readb
   // If we have a transform that requires resampling of our texture, then
   // we need to make sure we don't sample pixels that haven't been drawn.
   // We clamp sample coordinates to the texture rect, but when the visible region
   // doesn't fill the entire texture rect we need to make sure we draw all the
   // pixels in the texture rect anyway in case they get sampled.
   nsIntRegion neededRegion = mVisibleRegion;
   if (!neededRegion.GetBounds().IsEqualInterior(newTextureRect) ||
       neededRegion.GetNumRects() > 1) {
-    gfxMatrix transform2d;
-    if (!GetEffectiveTransform().Is2D(&transform2d) ||
-        transform2d.HasNonIntegerTranslation()) {
+    if (MayResample()) {
       neededRegion = newTextureRect;
       if (mode == SURFACE_OPAQUE) {
         // We're going to paint outside the visible region, but layout hasn't
         // promised that it will paint opaquely there, so we'll have to
         // treat this layer as transparent.
         mode = SURFACE_SINGLE_CHANNEL_ALPHA;
       }
     }
--- a/gfx/layers/opengl/ThebesLayerOGL.cpp
+++ b/gfx/layers/opengl/ThebesLayerOGL.cpp
@@ -852,22 +852,17 @@ ThebesLayerOGL::RenderLayer(int aPreviou
   gl()->fActiveTexture(LOCAL_GL_TEXTURE0);
 
   TextureImage::ContentType contentType =
     CanUseOpaqueSurface() ? gfxASurface::CONTENT_COLOR :
                             gfxASurface::CONTENT_COLOR_ALPHA;
 
   uint32_t flags = 0;
 #ifndef MOZ_GFX_OPTIMIZE_MOBILE
-  gfxMatrix transform2d;
-  if (GetEffectiveTransform().Is2D(&transform2d)) {
-    if (transform2d.HasNonIntegerTranslation()) {
-      flags |= ThebesLayerBufferOGL::PAINT_WILL_RESAMPLE;
-    }
-  } else {
+  if (MayResample()) {
     flags |= ThebesLayerBufferOGL::PAINT_WILL_RESAMPLE;
   }
 #endif
 
   Buffer::PaintState state = mBuffer->BeginPaint(contentType, flags);
   mValidRegion.Sub(mValidRegion, state.mRegionToInvalidate);
 
   if (state.mContext) {
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -3917,25 +3917,29 @@ already_AddRefed<Layer> nsDisplayTransfo
   nsRefPtr<ContainerLayer> container = aManager->GetLayerBuilder()->
     BuildContainerLayerFor(aBuilder, aManager, mFrame, this, *mStoredList.GetChildren(),
                            aContainerParameters, &newTransformMatrix);
 
   // Add the preserve-3d flag for this layer, BuildContainerLayerFor clears all flags,
   // so we never need to explicitely unset this flag.
   if (mFrame->Preserves3D() || mFrame->Preserves3DChildren()) {
     container->SetContentFlags(container->GetContentFlags() | Layer::CONTENT_PRESERVE_3D);
+  } else {
+    container->SetContentFlags(container->GetContentFlags() & ~Layer::CONTENT_PRESERVE_3D);
   }
 
   AddAnimationsAndTransitionsToLayer(container, aBuilder,
                                      this, eCSSProperty_transform);
   if (ShouldPrerenderTransformedContent(aBuilder, mFrame, false)) {
     container->SetUserData(nsIFrame::LayerIsPrerenderedDataKey(),
                            /*the value is irrelevant*/nullptr);
+    container->SetContentFlags(container->GetContentFlags() | Layer::CONTENT_MAY_CHANGE_TRANSFORM);
   } else {
     container->RemoveUserData(nsIFrame::LayerIsPrerenderedDataKey());
+    container->SetContentFlags(container->GetContentFlags() & ~Layer::CONTENT_MAY_CHANGE_TRANSFORM);
   }
   return container.forget();
 }
 
 nsDisplayItem::LayerState
 nsDisplayTransform::GetLayerState(nsDisplayListBuilder* aBuilder,
                                   LayerManager* aManager,
                                   const ContainerParameters& aParameters) {