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