Only composite changed areas in the software compositor (bug 882447, r=mattwoodrow,nrc).
authorDavid Anderson <danderson@mozilla.com>
Thu, 21 Nov 2013 11:25:16 -0800
changeset 156914 f5120348a27f02067b4ea070bc429fe5377686f6
parent 156913 e5dd91e7d90fa5680b32600839f5c35d795b277f
child 156915 f7e67f38813418daeef5d69bc9d24c74380fe179
push id2650
push userkwierso@gmail.com
push dateFri, 22 Nov 2013 03:24:50 +0000
treeherderb2g-inbound@cff1826bfddb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow, nrc
bugs882447
milestone28.0a1
Only composite changed areas in the software compositor (bug 882447, r=mattwoodrow,nrc).
gfx/layers/Compositor.h
gfx/layers/LayerTreeInvalidation.cpp
gfx/layers/Layers.h
gfx/layers/basic/BasicCompositor.cpp
gfx/layers/basic/BasicCompositor.h
gfx/layers/client/ClientLayerManager.cpp
gfx/layers/client/ClientLayerManager.h
gfx/layers/composite/LayerManagerComposite.cpp
gfx/layers/composite/LayerManagerComposite.h
gfx/layers/d3d11/CompositorD3D11.cpp
gfx/layers/d3d11/CompositorD3D11.h
gfx/layers/d3d9/CompositorD3D9.cpp
gfx/layers/d3d9/CompositorD3D9.h
gfx/layers/ipc/CompositorParent.cpp
gfx/layers/ipc/CompositorParent.h
gfx/layers/ipc/LayerTransactionParent.cpp
gfx/layers/ipc/LayersMessages.ipdlh
gfx/layers/ipc/PCompositor.ipdl
gfx/layers/ipc/ShadowLayers.cpp
gfx/layers/opengl/CompositorOGL.cpp
gfx/layers/opengl/CompositorOGL.h
widget/gtk/nsWindow.cpp
widget/gtk/nsWindow.h
widget/windows/nsWindow.h
widget/windows/nsWindowGfx.cpp
--- a/gfx/layers/Compositor.h
+++ b/gfx/layers/Compositor.h
@@ -10,16 +10,17 @@
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
 #include "mozilla/RefPtr.h"             // for TemporaryRef, RefCounted
 #include "mozilla/gfx/Point.h"          // for IntSize, Point
 #include "mozilla/gfx/Rect.h"           // for Rect, IntRect
 #include "mozilla/gfx/Types.h"          // for Float
 #include "mozilla/layers/CompositorTypes.h"  // for DiagnosticTypes, etc
 #include "mozilla/layers/LayersTypes.h"  // for LayersBackend
 #include "nsTraceRefcnt.h"              // for MOZ_COUNT_CTOR, etc
+#include "nsRegion.h"
 
 /**
  * Different elements of a web pages are rendered into separate "layers" before
  * they are flattened into the final image that is brought to the screen.
  * See Layers.h for more informations about layers and why we use retained
  * structures.
  * Most of the documentation for layers is directly in the source code in the
  * form of doc comments. An overview can also be found in the the wiki:
@@ -286,26 +287,34 @@ public:
    * required, these will be in the primary effect in the effect chain.
    */
   virtual void DrawQuad(const gfx::Rect& aRect, const gfx::Rect& aClipRect,
                         const EffectChain& aEffectChain,
                         gfx::Float aOpacity, const gfx::Matrix4x4 &aTransform) = 0;
 
   /**
    * Start a new frame.
+   *
+   * aInvalidRect is the invalid region of the screen; it can be ignored for
+   * compositors where the performance for compositing the entire window is
+   * sufficient.
+   *
    * aClipRectIn is the clip rect for the window in window space (optional).
    * aTransform is the transform from user space to window space.
    * aRenderBounds bounding rect for rendering, in user space.
+   *
    * If aClipRectIn is null, this method sets *aClipRectOut to the clip rect
    * actually used for rendering (if aClipRectIn is non-null, we will use that
    * for the clip rect).
+   *
    * If aRenderBoundsOut is non-null, it will be set to the render bounds
    * actually used by the compositor in window space.
    */
-  virtual void BeginFrame(const gfx::Rect* aClipRectIn,
+  virtual void BeginFrame(const nsIntRegion& aInvalidRegion,
+                          const gfx::Rect* aClipRectIn,
                           const gfxMatrix& aTransform,
                           const gfx::Rect& aRenderBounds,
                           gfx::Rect* aClipRectOut = nullptr,
                           gfx::Rect* aRenderBoundsOut = nullptr) = 0;
 
   /**
    * Flush the current frame to the screen and tidy up.
    */
--- a/gfx/layers/LayerTreeInvalidation.cpp
+++ b/gfx/layers/LayerTreeInvalidation.cpp
@@ -105,16 +105,17 @@ NotifySubdocumentInvalidationRecursive(L
 }
 
 struct LayerPropertiesBase : public LayerProperties
 {
   LayerPropertiesBase(Layer* aLayer)
     : mLayer(aLayer)
     , mMaskLayer(nullptr)
     , mVisibleRegion(aLayer->GetVisibleRegion())
+    , mInvalidRegion(aLayer->GetInvalidRegion())
     , mTransform(aLayer->GetTransform())
     , mOpacity(aLayer->GetOpacity())
     , mUseClipRect(!!aLayer->GetClipRect())
   {
     MOZ_COUNT_CTOR(LayerPropertiesBase);
     if (aLayer->GetMaskLayer()) {
       mMaskLayer = CloneLayerTreePropertiesInternal(aLayer->GetMaskLayer());
     }
@@ -196,16 +197,17 @@ struct LayerPropertiesBase : public Laye
     return TransformRect(mVisibleRegion.GetBounds(), mTransform);
   }
 
   virtual nsIntRegion ComputeChangeInternal(NotifySubDocInvalidationFunc aCallback) { return nsIntRect(); }
 
   nsRefPtr<Layer> mLayer;
   nsAutoPtr<LayerPropertiesBase> mMaskLayer;
   nsIntRegion mVisibleRegion;
+  nsIntRegion mInvalidRegion;
   gfx3DMatrix mTransform;
   float mOpacity;
   nsIntRect mClipRect;
   bool mUseClipRect;
 };
 
 struct ContainerLayerProperties : public LayerPropertiesBase
 {
@@ -315,17 +317,16 @@ struct ColorLayerProperties : public Lay
 
   gfxRGBA mColor;
 };
 
 struct ImageLayerProperties : public LayerPropertiesBase
 {
   ImageLayerProperties(ImageLayer* aImage)
     : LayerPropertiesBase(aImage)
-    , mVisibleRegion(aImage->GetVisibleRegion())
     , mContainer(aImage->GetContainer())
     , mFilter(aImage->GetFilter())
     , mScaleToSize(aImage->GetScaleToSize())
     , mScaleMode(aImage->GetScaleMode())
   {
   }
 
   virtual nsIntRegion ComputeChangeInternal(NotifySubDocInvalidationFunc aCallback)
@@ -343,17 +344,16 @@ struct ImageLayerProperties : public Lay
         mScaleToSize != imageLayer->GetScaleToSize() ||
         mScaleMode != imageLayer->GetScaleMode()) {
       return NewTransformedBounds();
     }
 
     return nsIntRect();
   }
 
-  nsIntRegion mVisibleRegion;
   nsRefPtr<ImageContainer> mContainer;
   GraphicsFilter mFilter;
   gfxIntSize mScaleToSize;
   ScaleMode mScaleMode;
 };
 
 LayerPropertiesBase*
 CloneLayerTreePropertiesInternal(Layer* aRoot)
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -1218,16 +1218,17 @@ public:
 
   static bool IsLogEnabled() { return LayerManager::IsLogEnabled(); }
 
   /**
    * Returns the current area of the layer (in layer-space coordinates)
    * marked as needed to be recomposited.
    */
   const nsIntRegion& GetInvalidRegion() { return mInvalidRegion; }
+  const void SetInvalidRegion(const nsIntRegion& aRect) { mInvalidRegion = aRect; }
 
   /**
    * Mark the entirety of the layer's visible region as being invalid.
    */
   void SetInvalidRectToVisibleRegion() { mInvalidRegion = GetVisibleRegion(); }
 
   /**
    * Adds to the current invalid rect.
--- a/gfx/layers/basic/BasicCompositor.cpp
+++ b/gfx/layers/basic/BasicCompositor.cpp
@@ -413,19 +413,17 @@ RoundOut(Rect r)
 
 void
 BasicCompositor::DrawQuad(const gfx::Rect& aRect,
                           const gfx::Rect& aClipRect,
                           const EffectChain &aEffectChain,
                           gfx::Float aOpacity,
                           const gfx::Matrix4x4 &aTransform)
 {
-  RefPtr<DrawTarget> buffer = mRenderTarget
-                              ? mRenderTarget->mDrawTarget
-                              : mDrawTarget;
+  RefPtr<DrawTarget> buffer = mRenderTarget->mDrawTarget;
 
   // For 2D drawing, |dest| and |buffer| are the same surface. For 3D drawing,
   // |dest| is a temporary surface.
   RefPtr<DrawTarget> dest = buffer;
 
   buffer->PushClipRect(aClipRect);
   AutoSaveTransform autoSaveTransform(dest);
 
@@ -546,46 +544,64 @@ BasicCompositor::DrawQuad(const gfx::Rec
     EffectMask *effectMask = static_cast<EffectMask*>(aEffectChain.mSecondaryEffects[EFFECT_MASK].get());
     static_cast<DeprecatedTextureHost*>(effectMask->mMaskTexture)->Unlock();
   }
 
   buffer->PopClip();
 }
 
 void
-BasicCompositor::BeginFrame(const gfx::Rect *aClipRectIn,
+BasicCompositor::BeginFrame(const nsIntRegion& aInvalidRegion,
+                            const gfx::Rect *aClipRectIn,
                             const gfxMatrix& aTransform,
                             const gfx::Rect& aRenderBounds,
                             gfx::Rect *aClipRectOut /* = nullptr */,
                             gfx::Rect *aRenderBoundsOut /* = nullptr */)
 {
   nsIntRect intRect;
   mWidget->GetClientBounds(intRect);
   Rect rect = Rect(0, 0, intRect.width, intRect.height);
   mWidgetSize = intRect.Size();
 
+  nsIntRect invalidRect = aInvalidRegion.GetBounds();
+  mInvalidRect = IntRect(invalidRect.x, invalidRect.y, invalidRect.width, invalidRect.height);
+  mInvalidRegion = aInvalidRegion;
+
+  if (aRenderBoundsOut) {
+    *aRenderBoundsOut = Rect();
+  }
+
+  if (mInvalidRect.width <= 0 || mInvalidRect.height <= 0) {
+    return;
+  }
+
   if (mCopyTarget) {
     // If we have a copy target, then we don't have a widget-provided mDrawTarget (currently). Create a dummy
     // placeholder so that CreateRenderTarget() works.
     mDrawTarget = gfxPlatform::GetPlatform()->CreateOffscreenCanvasDrawTarget(IntSize(1,1), FORMAT_B8G8R8A8);
   } else {
     mDrawTarget = mWidget->StartRemoteDrawing();
   }
   if (!mDrawTarget) {
-    if (aRenderBoundsOut) {
-      *aRenderBoundsOut = Rect();
-    }
     return;
   }
 
   // Setup an intermediate render target to buffer all compositing. We will
   // copy this into mDrawTarget (the widget), and/or mCopyTarget in EndFrame()
-  RefPtr<CompositingRenderTarget> target = CreateRenderTarget(IntRect(0, 0, intRect.width, intRect.height), INIT_MODE_CLEAR);
+  RefPtr<CompositingRenderTarget> target = CreateRenderTarget(mInvalidRect, INIT_MODE_CLEAR);
   SetRenderTarget(target);
 
+  // We only allocate a surface sized to the invalidated region, so we need to
+  // translate future coordinates.
+  Matrix transform;
+  transform.Translate(-invalidRect.x, -invalidRect.y);
+  mRenderTarget->mDrawTarget->SetTransform(transform);
+
+  gfxUtils::ClipToRegion(mRenderTarget->mDrawTarget, aInvalidRegion);
+
   if (aRenderBoundsOut) {
     *aRenderBoundsOut = rect;
   }
 
   if (aClipRectIn) {
     mRenderTarget->mDrawTarget->PushClipRect(*aClipRectIn);
   } else {
     mRenderTarget->mDrawTarget->PushClipRect(rect);
@@ -594,36 +610,43 @@ BasicCompositor::BeginFrame(const gfx::R
     }
   }
 }
 
 void
 BasicCompositor::EndFrame()
 {
   mRenderTarget->mDrawTarget->PopClip();
+  mRenderTarget->mDrawTarget->PopClip();
 
+  // Note: Most platforms require us to buffer drawing to the widget surface.
+  // That's why we don't draw to mDrawTarget directly.
   RefPtr<SourceSurface> source = mRenderTarget->mDrawTarget->Snapshot();
-  if (mCopyTarget) {
-    mCopyTarget->CopySurface(source,
-                             IntRect(0, 0, mWidgetSize.width, mWidgetSize.height),
-                             IntPoint(0, 0));
-  } else {
-    // Most platforms require us to buffer drawing to the widget surface.
-    // That's why we don't draw to mDrawTarget directly.
-    mDrawTarget->CopySurface(source,
-	                           IntRect(0, 0, mWidgetSize.width, mWidgetSize.height),
-			                       IntPoint(0, 0));
+  RefPtr<DrawTarget> dest(mCopyTarget ? mCopyTarget : mDrawTarget);
+  
+  // The source DrawTarget is clipped to the invalidation region, so we have
+  // to copy the individual rectangles in the region or else we'll draw blank
+  // pixels.
+  nsIntRegionRectIterator iter(mInvalidRegion);
+  for (const nsIntRect *r = iter.Next(); r; r = iter.Next()) {
+    dest->CopySurface(source,
+                      IntRect(r->x - mInvalidRect.x, r->y - mInvalidRect.y, r->width, r->height),
+                      IntPoint(r->x, r->y));
+  }
+  if (!mCopyTarget) {
     mWidget->EndRemoteDrawing();
   }
+
   mDrawTarget = nullptr;
   mRenderTarget = nullptr;
 }
 
 void
 BasicCompositor::AbortFrame()
 {
   mRenderTarget->mDrawTarget->PopClip();
+  mRenderTarget->mDrawTarget->PopClip();
   mDrawTarget = nullptr;
   mRenderTarget = nullptr;
 }
 
 }
 }
--- a/gfx/layers/basic/BasicCompositor.h
+++ b/gfx/layers/basic/BasicCompositor.h
@@ -79,17 +79,18 @@ public:
   }
 
   virtual void DrawQuad(const gfx::Rect& aRect,
                         const gfx::Rect& aClipRect,
                         const EffectChain &aEffectChain,
                         gfx::Float aOpacity,
                         const gfx::Matrix4x4 &aTransform) MOZ_OVERRIDE;
 
-  virtual void BeginFrame(const gfx::Rect *aClipRectIn,
+  virtual void BeginFrame(const nsIntRegion& aInvalidRegion,
+                          const gfx::Rect *aClipRectIn,
                           const gfxMatrix& aTransform,
                           const gfx::Rect& aRenderBounds,
                           gfx::Rect *aClipRectOut = nullptr,
                           gfx::Rect *aRenderBoundsOut = nullptr) MOZ_OVERRIDE;
   virtual void EndFrame() MOZ_OVERRIDE;
   virtual void EndFrameForExternalComposition(const gfxMatrix& aTransform) MOZ_OVERRIDE
   {
     NS_RUNTIMEABORT("We shouldn't ever hit this");
@@ -132,14 +133,17 @@ private:
 
   // The final destination surface
   RefPtr<gfx::DrawTarget> mDrawTarget;
   // The current render target for drawing
   RefPtr<BasicCompositingRenderTarget> mRenderTarget;
   // An optional destination target to copy the results
   // to after drawing is completed.
   RefPtr<gfx::DrawTarget> mCopyTarget;
+
+  gfx::IntRect mInvalidRect;
+  nsIntRegion mInvalidRegion;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif /* MOZILLA_GFX_BASICCOMPOSITOR_H */
--- a/gfx/layers/client/ClientLayerManager.cpp
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -290,16 +290,26 @@ ClientLayerManager::FlushRendering()
   if (mWidget) {
     if (CompositorChild* remoteRenderer = mWidget->GetRemoteRenderer()) {
       remoteRenderer->SendFlushRendering();
     }
   }
 }
 
 void
+ClientLayerManager::SendInvalidRegion(const nsIntRegion& aRegion)
+{
+  if (mWidget) {
+    if (CompositorChild* remoteRenderer = mWidget->GetRemoteRenderer()) {
+      remoteRenderer->SendNotifyRegionInvalidated(aRegion);
+    }
+  }
+}
+
+void
 ClientLayerManager::ForwardTransaction()
 {
   mPhase = PHASE_FORWARD;
 
   // forward this transaction's changeset to our LayerManagerComposite
   bool sent;
   AutoInfallibleTArray<EditReply, 10> replies;
   if (HasShadowManager() && ShadowLayerForwarder::EndTransaction(&replies, &sent)) {
--- a/gfx/layers/client/ClientLayerManager.h
+++ b/gfx/layers/client/ClientLayerManager.h
@@ -74,16 +74,17 @@ public:
   virtual already_AddRefed<RefLayer> CreateRefLayer();
 
   virtual TextureFactoryIdentifier GetTextureFactoryIdentifier() MOZ_OVERRIDE
   {
     return mTextureFactoryIdentifier;
   }
 
   virtual void FlushRendering() MOZ_OVERRIDE;
+  void SendInvalidRegion(const nsIntRegion& aRegion);
 
   virtual bool NeedsWidgetInvalidation() MOZ_OVERRIDE { return false; }
 
   ShadowableLayer* Hold(Layer* aLayer);
 
   bool HasShadowManager() const { return ShadowLayerForwarder::HasShadowManager(); }
 
   virtual bool IsCompositingCheap();
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -140,16 +140,19 @@ LayerManagerComposite::UpdateRenderBound
 {
   mRenderBounds = aRect;
 }
 
 void
 LayerManagerComposite::BeginTransaction()
 {
   mInTransaction = true;
+  if (Compositor::GetBackend() == LAYERS_BASIC) {
+    mClonedLayerTreeProperties = LayerProperties::CloneFrom(GetRoot());
+  }
 }
 
 void
 LayerManagerComposite::BeginTransactionWithDrawTarget(DrawTarget* aTarget)
 {
   mInTransaction = true;
 
 #ifdef MOZ_LAYERS_HAVE_LOG
@@ -191,16 +194,25 @@ LayerManagerComposite::EndTransaction(Dr
   Log();
 #endif
 
   if (mDestroyed) {
     NS_WARNING("Call on destroyed layer manager");
     return;
   }
 
+  if (mRoot && mClonedLayerTreeProperties) {
+    nsIntRegion invalid = mClonedLayerTreeProperties->ComputeDifferences(mRoot, nullptr);
+    mClonedLayerTreeProperties = nullptr;
+
+    mInvalidRegion.Or(mInvalidRegion, invalid);
+  } else {
+    mInvalidRegion.Or(mInvalidRegion, mRenderBounds);
+  }
+
   if (mRoot && !(aFlags & END_NO_IMMEDIATE_REDRAW)) {
     if (aFlags & END_NO_COMPOSITE) {
       // Apply pending tree updates before recomputing effective
       // properties.
       mRoot->ApplyPendingUpdatesToSubtree();
     }
 
     // The results of our drawing always go directly into a pixel buffer,
@@ -338,23 +350,26 @@ LayerManagerComposite::Render()
 
   nsIntRect clipRect;
   Rect bounds(mRenderBounds.x, mRenderBounds.y, mRenderBounds.width, mRenderBounds.height);
   Rect actualBounds;
   if (mRoot->GetClipRect()) {
     clipRect = *mRoot->GetClipRect();
     WorldTransformRect(clipRect);
     Rect rect(clipRect.x, clipRect.y, clipRect.width, clipRect.height);
-    mCompositor->BeginFrame(&rect, mWorldMatrix, bounds, nullptr, &actualBounds);
+    mCompositor->BeginFrame(mInvalidRegion, &rect, mWorldMatrix, bounds, nullptr, &actualBounds);
   } else {
     gfx::Rect rect;
-    mCompositor->BeginFrame(nullptr, mWorldMatrix, bounds, &rect, &actualBounds);
+    mCompositor->BeginFrame(mInvalidRegion, nullptr, mWorldMatrix, bounds, &rect, &actualBounds);
     clipRect = nsIntRect(rect.x, rect.y, rect.width, rect.height);
   }
 
+  // Reset the invalid region now that we've begun compositing.
+  mInvalidRegion.SetEmpty();
+
   if (actualBounds.IsEmpty()) {
     mCompositor->GetWidget()->PostRender(this);
     return;
   }
 
   // Allow widget to render a custom background.
   mCompositor->SaveState();
   mCompositor->GetWidget()->DrawWindowUnderlay(this, nsIntRect(actualBounds.x,
--- a/gfx/layers/composite/LayerManagerComposite.h
+++ b/gfx/layers/composite/LayerManagerComposite.h
@@ -23,16 +23,17 @@
 #include "nsAString.h"
 #include "nsAutoPtr.h"                  // for nsRefPtr
 #include "nsCOMPtr.h"                   // for already_AddRefed
 #include "nsDebug.h"                    // for NS_ASSERTION
 #include "nsISupportsImpl.h"            // for Layer::AddRef, etc
 #include "nsRect.h"                     // for nsIntRect
 #include "nsRegion.h"                   // for nsIntRegion
 #include "nscore.h"                     // for nsAString, etc
+#include "LayerTreeInvalidation.h"
 
 class gfxASurface;
 class gfxContext;
 struct nsIntPoint;
 struct nsIntSize;
 
 #ifdef XP_WIN
 #include <windows.h>
@@ -223,16 +224,21 @@ public:
    * direct texturing
    */
   static bool SupportsDirectTexturing();
 
   static void PlatformSyncBeforeReplyUpdate();
 
   void SetCompositorID(uint32_t aID);
 
+  void AddInvalidRegion(const nsIntRegion& aRegion)
+  {
+    mInvalidRegion.Or(mInvalidRegion, aRegion);
+  }
+
   Compositor* GetCompositor() const
   {
     return mCompositor;
   }
 
   bool PlatformDestroySharedSurface(SurfaceDescriptor* aSurface);
   RefPtr<Compositor> mCompositor;
 
@@ -270,17 +276,20 @@ private:
   /** Our more efficient but less powerful alter ego, if one is available. */
   nsRefPtr<Composer2D> mComposer2D;
 
   /* Thebes layer callbacks; valid at the end of a transaciton,
    * while rendering */
   DrawThebesLayerCallback mThebesLayerCallback;
   void *mThebesLayerCallbackData;
   gfxMatrix mWorldMatrix;
+
   bool mInTransaction;
+  nsIntRegion mInvalidRegion;
+  nsAutoPtr<LayerProperties> mClonedLayerTreeProperties;
 };
 
 /**
  * Composite layers are for use with OMTC on the compositor thread only. There
  * must be corresponding Basic layers on the content thread. For composite
  * layers, the layer manager only maintains the layer tree, all rendering is
  * done by a Compositor (see Compositor.h). As such, composite layers are
  * platform-independent and can be used on any platform for which there is a
--- a/gfx/layers/d3d11/CompositorD3D11.cpp
+++ b/gfx/layers/d3d11/CompositorD3D11.cpp
@@ -618,17 +618,18 @@ CompositorD3D11::DrawQuad(const gfx::Rec
 
   mContext->Draw(4, 0);
   if (restoreBlendMode) {
     mContext->OMSetBlendState(mAttachments->mPremulBlendState, sBlendFactor, 0xFFFFFFFF);
   }
 }
 
 void
-CompositorD3D11::BeginFrame(const Rect* aClipRectIn,
+CompositorD3D11::BeginFrame(const nsIntRegion& aInvalidRegion,
+                            const Rect* aClipRectIn,
                             const gfxMatrix& aTransform,
                             const Rect& aRenderBounds,
                             Rect* aClipRectOut,
                             Rect* aRenderBoundsOut)
 {
   // Don't composite if we are minimised. Other than for the sake of efficency,
   // this is important because resizing our buffers when mimised will fail and
   // cause a crash when we're restored.
--- a/gfx/layers/d3d11/CompositorD3D11.h
+++ b/gfx/layers/d3d11/CompositorD3D11.h
@@ -95,17 +95,18 @@ public:
                         const EffectChain &aEffectChain,
                         gfx::Float aOpacity,
                         const gfx::Matrix4x4 &aTransform) MOZ_OVERRIDE;
 
   /**
    * Start a new frame. If aClipRectIn is null, sets *aClipRectOut to the
    * screen dimensions. 
    */
-  virtual void BeginFrame(const gfx::Rect *aClipRectIn,
+  virtual void BeginFrame(const nsIntRegion& aInvalidRegion,
+                          const gfx::Rect *aClipRectIn,
                           const gfxMatrix& aTransform,
                           const gfx::Rect& aRenderBounds,
                           gfx::Rect *aClipRectOut = nullptr,
                           gfx::Rect *aRenderBoundsOut = nullptr) MOZ_OVERRIDE;
 
   /**
    * Flush the current frame to the screen.
    */
--- a/gfx/layers/d3d9/CompositorD3D9.cpp
+++ b/gfx/layers/d3d9/CompositorD3D9.cpp
@@ -434,17 +434,18 @@ CompositorD3D9::SetMask(const EffectChai
                                      ShaderConstantRect(bounds.x,
                                                         bounds.y,
                                                         bounds.width,
                                                         bounds.height),
                                      1);
 }
 
 void
-CompositorD3D9::BeginFrame(const Rect *aClipRectIn,
+CompositorD3D9::BeginFrame(const nsIntRegion& aInvalidRegion,
+                           const Rect *aClipRectIn,
                            const gfxMatrix& aTransform,
                            const Rect& aRenderBounds,
                            Rect *aClipRectOut,
                            Rect *aRenderBoundsOut)
 {
   if (!mSwapChain->PrepareForRendering()) {
     return;
   }
--- a/gfx/layers/d3d9/CompositorD3D9.h
+++ b/gfx/layers/d3d9/CompositorD3D9.h
@@ -56,17 +56,18 @@ public:
   virtual void SetDestinationSurfaceSize(const gfx::IntSize& aSize) MOZ_OVERRIDE {}
 
   virtual void DrawQuad(const gfx::Rect &aRect,
                         const gfx::Rect &aClipRect,
                         const EffectChain &aEffectChain,
                         gfx::Float aOpacity,
                         const gfx::Matrix4x4 &aTransform) MOZ_OVERRIDE;
 
-  virtual void BeginFrame(const gfx::Rect *aClipRectIn,
+  virtual void BeginFrame(const nsIntRegion& aInvalidRegion,
+                          const gfx::Rect *aClipRectIn,
                           const gfxMatrix& aTransform,
                           const gfx::Rect& aRenderBounds,
                           gfx::Rect *aClipRectOut = nullptr,
                           gfx::Rect *aRenderBoundsOut = nullptr) MOZ_OVERRIDE;
 
   virtual void EndFrame() MOZ_OVERRIDE;
 
   virtual void EndFrameForExternalComposition(const gfxMatrix& aTransform) MOZ_OVERRIDE {}
--- a/gfx/layers/ipc/CompositorParent.cpp
+++ b/gfx/layers/ipc/CompositorParent.cpp
@@ -317,16 +317,25 @@ CompositorParent::RecvFlushRendering()
   // and do it immediately instead.
   if (mCurrentCompositeTask) {
     mCurrentCompositeTask->Cancel();
     ComposeToTarget(nullptr);
   }
   return true;
 }
 
+bool
+CompositorParent::RecvNotifyRegionInvalidated(const nsIntRegion& aRegion)
+{
+  if (mLayerManager) {
+    mLayerManager->AddInvalidRegion(aRegion);
+  }
+  return true;
+}
+
 void
 CompositorParent::ActorDestroy(ActorDestroyReason why)
 {
   mPaused = true;
   RemoveCompositor(mCompositorID);
 
   if (mLayerManager) {
     mLayerManager->Destroy();
@@ -891,16 +900,17 @@ public:
   virtual bool RecvStop() MOZ_OVERRIDE { return true; }
   virtual bool RecvPause() MOZ_OVERRIDE { return true; }
   virtual bool RecvResume() MOZ_OVERRIDE { return true; }
   virtual bool RecvNotifyChildCreated(const uint64_t& child) MOZ_OVERRIDE;
   virtual bool RecvMakeSnapshot(const SurfaceDescriptor& aInSnapshot,
                                 SurfaceDescriptor* aOutSnapshot)
   { return true; }
   virtual bool RecvFlushRendering() MOZ_OVERRIDE { return true; }
+  virtual bool RecvNotifyRegionInvalidated(const nsIntRegion& aRegion) { return true; }
 
   virtual PLayerTransactionParent*
     AllocPLayerTransactionParent(const nsTArray<LayersBackend>& aBackendHints,
                                  const uint64_t& aId,
                                  TextureFactoryIdentifier* aTextureFactoryIdentifier,
                                  bool *aSuccess) MOZ_OVERRIDE;
 
   virtual bool DeallocPLayerTransactionParent(PLayerTransactionParent* aLayers) MOZ_OVERRIDE;
--- a/gfx/layers/ipc/CompositorParent.h
+++ b/gfx/layers/ipc/CompositorParent.h
@@ -83,16 +83,18 @@ public:
   virtual bool RecvStop() MOZ_OVERRIDE;
   virtual bool RecvPause() MOZ_OVERRIDE;
   virtual bool RecvResume() MOZ_OVERRIDE;
   virtual bool RecvNotifyChildCreated(const uint64_t& child) MOZ_OVERRIDE;
   virtual bool RecvMakeSnapshot(const SurfaceDescriptor& aInSnapshot,
                                 SurfaceDescriptor* aOutSnapshot);
   virtual bool RecvFlushRendering() MOZ_OVERRIDE;
 
+  virtual bool RecvNotifyRegionInvalidated(const nsIntRegion& aRegion) MOZ_OVERRIDE;
+
   virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
 
   virtual void ShadowLayersUpdated(LayerTransactionParent* aLayerTree,
                                    const TargetConfig& aTargetConfig,
                                    bool isFirstPaint) MOZ_OVERRIDE;
   /**
    * This forces the is-first-paint flag to true. This is intended to
    * be called by the widget code when it loses its viewport information
--- a/gfx/layers/ipc/LayerTransactionParent.cpp
+++ b/gfx/layers/ipc/LayerTransactionParent.cpp
@@ -279,16 +279,17 @@ LayerTransactionParent::RecvUpdate(const
                                      common.stickyScrollRangeInner());
       }
       if (PLayerParent* maskLayer = common.maskLayerParent()) {
         layer->SetMaskLayer(cast(maskLayer)->AsLayer());
       } else {
         layer->SetMaskLayer(nullptr);
       }
       layer->SetAnimations(common.animations());
+      layer->SetInvalidRegion(common.invalidRegion());
 
       typedef SpecificLayerAttributes Specific;
       const SpecificLayerAttributes& specific = attrs.specific();
       switch (specific.type()) {
       case Specific::Tnull_t:
         break;
 
       case Specific::TThebesLayerAttributes: {
--- a/gfx/layers/ipc/LayersMessages.ipdlh
+++ b/gfx/layers/ipc/LayersMessages.ipdlh
@@ -195,17 +195,18 @@ struct CommonLayerAttributes {
   LayerMargin fixedPositionMargin;
   bool isStickyPosition;
   uint64_t stickyScrollContainerId;
   LayerRect stickyScrollRangeOuter;
   LayerRect stickyScrollRangeInner;
   nullable PLayer maskLayer;
   // Animated colors will only honored for ColorLayers.
   Animation[] animations;
- };
+  nsIntRegion invalidRegion;
+};
 
 struct ThebesLayerAttributes {
   nsIntRegion validRegion;
 };
 struct ContainerLayerAttributes {
   FrameMetrics metrics;
   float preXScale;
   float preYScale;
--- a/gfx/layers/ipc/PCompositor.ipdl
+++ b/gfx/layers/ipc/PCompositor.ipdl
@@ -4,16 +4,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include LayersSurfaces;
 include protocol PGrallocBuffer;
 include protocol PLayerTransaction;
 include "mozilla/GfxMessageUtils.h";
+include "nsRegion.h";
 
 using struct mozilla::null_t from "ipc/IPCMessageUtils.h";
 using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.h";
 using mozilla::layers::LayersBackend from "mozilla/layers/LayersTypes.h";
 
 namespace mozilla {
 namespace layers {
 
@@ -59,12 +60,15 @@ parent:
   // block until they are completed.
   sync FlushRendering();
 
   // layersBackendHints is an ordered list of preffered backends where
   // layersBackendHints[0] is the best backend. If any hints are LAYERS_NONE
   // that hint is ignored.
   sync PLayerTransaction(LayersBackend[] layersBackendHints, uint64_t id)
     returns (TextureFactoryIdentifier textureFactoryIdentifier, bool success);
+
+  // Notify the compositor that a region of the screen has been invalidated.
+  async NotifyRegionInvalidated(nsIntRegion region);
 };
 
 } // layers
 } // mozilla
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -531,16 +531,17 @@ ShadowLayerForwarder::EndTransaction(Inf
     }
     if (Layer* maskLayer = mutant->GetMaskLayer()) {
       common.maskLayerChild() = Shadow(maskLayer->AsShadowableLayer());
     } else {
       common.maskLayerChild() = nullptr;
     }
     common.maskLayerParent() = nullptr;
     common.animations() = mutant->GetAnimations();
+    common.invalidRegion() = mutant->GetInvalidRegion();
     attrs.specific() = null_t();
     mutant->FillSpecificAttributes(attrs.specific());
 
     MOZ_LAYERS_LOG(("[LayersForwarder] OpSetLayerAttributes(%p)\n", mutant));
 
     mTxn->AddEdit(OpSetLayerAttributes(nullptr, Shadow(shadow), attrs));
   }
 
--- a/gfx/layers/opengl/CompositorOGL.cpp
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -750,18 +750,21 @@ CalculatePOTSize(const IntSize& aSize, G
 {
   if (gl->CanUploadNonPowerOfTwo())
     return aSize;
 
   return IntSize(NextPowerOfTwo(aSize.width), NextPowerOfTwo(aSize.height));
 }
 
 void
-CompositorOGL::BeginFrame(const Rect *aClipRectIn, const gfxMatrix& aTransform,
-                          const Rect& aRenderBounds, Rect *aClipRectOut,
+CompositorOGL::BeginFrame(const nsIntRegion& aInvalidRegion,
+                          const Rect *aClipRectIn,
+                          const gfxMatrix& aTransform,
+                          const Rect& aRenderBounds,
+                          Rect *aClipRectOut,
                           Rect *aRenderBoundsOut)
 {
   PROFILER_LABEL("CompositorOGL", "BeginFrame");
   MOZ_ASSERT(!mFrameInProgress, "frame still in progress (should have called EndFrame or AbortFrame");
 
   mVBOs.Reset();
 
   mFrameInProgress = true;
--- a/gfx/layers/opengl/CompositorOGL.h
+++ b/gfx/layers/opengl/CompositorOGL.h
@@ -238,17 +238,18 @@ private:
   /**
    * Have we had DrawQuad calls since the last frame was rendered?
    */
   bool mFrameInProgress;
 
   /* Start a new frame. If aClipRectIn is null and aClipRectOut is non-null,
    * sets *aClipRectOut to the screen dimensions.
    */
-  virtual void BeginFrame(const gfx::Rect *aClipRectIn,
+  virtual void BeginFrame(const nsIntRegion& aInvalidRegion,
+                          const gfx::Rect *aClipRectIn,
                           const gfxMatrix& aTransform,
                           const gfx::Rect& aRenderBounds, 
                           gfx::Rect *aClipRectOut = nullptr,
                           gfx::Rect *aRenderBoundsOut = nullptr) MOZ_OVERRIDE;
 
   ShaderProgramType GetProgramTypeForEffect(Effect* aEffect) const;
 
   /**
--- a/widget/gtk/nsWindow.cpp
+++ b/widget/gtk/nsWindow.cpp
@@ -1954,16 +1954,81 @@ gdk_window_flash(GdkWindow *    aGdkWind
   gdk_gc_destroy(gc);
 
   gdk_region_offset(aRegion, -x, -y);
 }
 #endif /* MOZ_X11 */
 #endif // DEBUG
 #endif
 
+struct ExposeRegion
+{
+    nsIntRegion mRegion;
+
+#if (MOZ_WIDGET_GTK == 2)
+    GdkRectangle *mRects;
+    GdkRectangle *mRectsEnd;
+
+    ExposeRegion() : mRects(nullptr)
+    {
+    }
+    ~ExposeRegion()
+    {
+        g_free(mRects);
+    }
+    bool Init(GdkEventExpose *aEvent)
+    {
+        gint nrects;
+        gdk_region_get_rectangles(aEvent->region, &mRects, &nrects);
+
+        if (nrects > MAX_RECTS_IN_REGION) {
+            // Just use the bounding box
+            mRects[0] = aEvent->area;
+            nrects = 1;
+        }
+
+        mRectsEnd = mRects + nrects;
+
+        for (GdkRectangle *r = mRects; r < mRectsEnd; r++) {
+            mRegion.Or(mRegion, nsIntRect(r->x, r->y, r->width, r->height));
+            LOGDRAW(("\t%d %d %d %d\n", r->x, r->y, r->width, r->height));
+        }
+        return true;
+    }
+
+#else
+# ifdef cairo_copy_clip_rectangle_list
+#  error "Looks like we're including Mozilla's cairo instead of system cairo"
+# endif
+    cairo_rectangle_list_t *mRects;
+
+    ExposeRegion() : mRects(nullptr)
+    {
+    }
+    ~ExposeRegion()
+    {
+        cairo_rectangle_list_destroy(mRects);
+    }
+    bool Init(cairo_t* cr)
+    {
+        if (mRects->status != CAIRO_STATUS_SUCCESS) {
+            NS_WARNING("Failed to obtain cairo rectangle list.");
+            return false;
+        }
+
+        for (int i = 0; i < mRects->num_rectangles; i++)  {
+            const cairo_rectangle_t& r = mRects->rectangles[i];
+            mRegion.Or(mRegion, nsIntRect(r.x, r.y, r.width, r.height));
+            LOGDRAW(("\t%d %d %d %d\n", r.x, r.y, r.width, r.height));
+        }
+        return true;
+    }
+#endif
+};
+
 #if (MOZ_WIDGET_GTK == 2)
 gboolean
 nsWindow::OnExposeEvent(GdkEventExpose *aEvent)
 #else
 gboolean
 nsWindow::OnExposeEvent(cairo_t *cr)
 #endif
 {
@@ -1975,27 +2040,39 @@ nsWindow::OnExposeEvent(cairo_t *cr)
     if (!mGdkWindow || mIsFullyObscured || !mHasMappedToplevel)
         return FALSE;
 
     nsIWidgetListener *listener =
         mAttachedWidgetListener ? mAttachedWidgetListener : mWidgetListener;
     if (!listener)
         return FALSE;
 
+    ExposeRegion exposeRegion;
+#if (MOZ_WIDGET_GTK == 2)
+    if (!exposeRegion.Init(aEvent)) {
+#else
+    if (!exposeRegion.Init(cr)) {
+#endif
+        return FALSE;
+    }
+
+    nsIntRegion &region = exposeRegion.mRegion;
+
     ClientLayerManager *clientLayers =
         (GetLayerManager()->GetBackendType() == LAYERS_CLIENT)
         ? static_cast<ClientLayerManager*>(GetLayerManager())
         : nullptr;
 
     if (clientLayers && mCompositorParent &&
         !gdk_screen_is_composited(gdk_window_get_screen(mGdkWindow)))
     {
         // We need to paint to the screen even if nothing changed, since if we
         // don't have a compositing window manager, our pixels could be stale.
         clientLayers->SetNeedsComposite(true);
+        clientLayers->SendInvalidRegion(region);
     }
 
     // Dispatch WillPaintWindow notification to allow scripts etc. to run
     // before we paint
     {
         listener->WillPaintWindow(this);
 
         // If the window has been destroyed during the will paint notification,
@@ -2011,62 +2088,20 @@ nsWindow::OnExposeEvent(cairo_t *cr)
             return FALSE;
     }
 
     if (clientLayers && mCompositorParent && clientLayers->NeedsComposite()) {
         mCompositorParent->ScheduleRenderOnCompositorThread();
         clientLayers->SetNeedsComposite(false);
     }
 
-#if (MOZ_WIDGET_GTK == 2)
-    GdkRectangle *rects;
-    gint nrects;
-    gdk_region_get_rectangles(aEvent->region, &rects, &nrects);
-    if (MOZ_UNLIKELY(!rects)) // OOM
-        return FALSE;
-#else
-#ifdef cairo_copy_clip_rectangle_list
-#error "Looks like we're including Mozilla's cairo instead of system cairo"
-#else
-    cairo_rectangle_list_t *rects;
-    rects = cairo_copy_clip_rectangle_list(cr);  
-    if (MOZ_UNLIKELY(rects->status != CAIRO_STATUS_SUCCESS)) {
-       NS_WARNING("Failed to obtain cairo rectangle list.");
-       return FALSE;
-    }
-#endif
-#endif
-
-// GTK3 TODO?
-#if (MOZ_WIDGET_GTK == 2)
-    if (nrects > MAX_RECTS_IN_REGION) {
-        // Just use the bounding box
-        rects[0] = aEvent->area;
-        nrects = 1;
-    }
-#endif
-
     LOGDRAW(("sending expose event [%p] %p 0x%lx (rects follow):\n",
              (void *)this, (void *)mGdkWindow,
              gdk_x11_window_get_xid(mGdkWindow)));
 
-    nsIntRegion region;
-  
-#if (MOZ_WIDGET_GTK == 2)
-    GdkRectangle *r = rects;
-    GdkRectangle *r_end = rects + nrects;
-#else
-    cairo_rectangle_t *r = rects->rectangles;
-    cairo_rectangle_t *r_end = r + rects->num_rectangles;
-#endif
-    for (; r < r_end; ++r) {
-        region.Or(region, nsIntRect(r->x, r->y, r->width, r->height));
-        LOGDRAW(("\t%d %d %d %d\n", r->x, r->y, r->width, r->height));
-    }
-
     // Our bounds may have changed after calling WillPaintWindow.  Clip
     // to the new bounds here.  The region is relative to this
     // window.
     region.And(region, nsIntRect(0, 0, mBounds.width, mBounds.height));
 
     bool shaped = false;
     if (eTransparencyTransparent == GetTransparencyMode()) {
         GdkScreen *screen = gdk_window_get_screen(mGdkWindow);
@@ -2098,38 +2133,30 @@ nsWindow::OnExposeEvent(cairo_t *cr)
                     region.Sub(region, r);
                 }
             }
             children = children->next;
         }
     }
 
     if (region.IsEmpty()) {
-#if (MOZ_WIDGET_GTK == 2)
-        g_free(rects);
-#else
-        cairo_rectangle_list_destroy(rects);
-#endif
         return TRUE;
     }
+
     // If this widget uses OMTC...
     if (GetLayerManager()->GetBackendType() == LAYERS_CLIENT) {
         listener->PaintWindow(this, region);
         listener->DidPaintWindow();
-
-        g_free(rects);
         return TRUE;
     } else if (GetLayerManager()->GetBackendType() == mozilla::layers::LAYERS_OPENGL) {
         LayerManagerOGL *manager = static_cast<LayerManagerOGL*>(GetLayerManager());
         manager->SetClippingRegion(region);
 
         listener->PaintWindow(this, region);
         listener->DidPaintWindow();
-
-        g_free(rects);
         return TRUE;
     }
 
     gfxASurface* surf;
 #if (MOZ_WIDGET_GTK == 2)
     surf = GetThebesSurface();
 #else
     surf = GetThebesSurface(cr);
@@ -2222,30 +2249,24 @@ nsWindow::OnExposeEvent(cairo_t *cr)
                 ctx->SetPattern(pattern);
                 ctx->Paint();
             }
         }
     }
 #  ifdef MOZ_HAVE_SHMIMAGE
     if (nsShmImage::UseShm() && MOZ_LIKELY(!mIsDestroyed)) {
 #if (MOZ_WIDGET_GTK == 2)
-        mShmImage->Put(mGdkWindow, rects, r_end);
+        mShmImage->Put(mGdkWindow, exposeRegion.mRects, exposeRegion.mRectsEnd);
 #else
-        mShmImage->Put(mGdkWindow, rects);
+        mShmImage->Put(mGdkWindow, exposeRegion.mRects);
 #endif
     }
 #  endif  // MOZ_HAVE_SHMIMAGE
 #endif // MOZ_X11
 
-#if (MOZ_WIDGET_GTK == 2)
-    g_free(rects);
-#else
-    cairo_rectangle_list_destroy(rects);
-#endif
-
     listener->DidPaintWindow();
 
     // Synchronously flush any new dirty areas
 #if (MOZ_WIDGET_GTK == 2)
     GdkRegion* dirtyArea = gdk_window_get_update_area(mGdkWindow);
 #else
     cairo_region_t* dirtyArea = gdk_window_get_update_area(mGdkWindow);
 #endif
@@ -5966,33 +5987,31 @@ nsWindow::GetSurfaceForGdkDrawable(GdkDr
         result = new gfxXlibSurface(xScreen, xDrawable, pf,
                                     gfxIntSize(aSize.width, aSize.height));
     }
 
     return result.forget();
 }
 #endif
 
-#if (MOZ_WIDGET_GTK == 2)
 TemporaryRef<DrawTarget>
 nsWindow::StartRemoteDrawing()
 {
   gfxASurface *surf = GetThebesSurface();
   if (!surf) {
     return nullptr;
   }
 
   IntSize size(surf->GetSize().width, surf->GetSize().height);
   if (size.width <= 0 || size.height <= 0) {
     return nullptr;
   }
 
   return gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(surf, size);
 }
-#endif
 
 // return the gfxASurface for rendering to this widget
 gfxASurface*
 #if (MOZ_WIDGET_GTK == 2)
 nsWindow::GetThebesSurface()
 #else
 nsWindow::GetThebesSurface(cairo_t *cr)
 #endif
--- a/widget/gtk/nsWindow.h
+++ b/widget/gtk/nsWindow.h
@@ -192,19 +192,17 @@ public:
                                                GdkDragContext  *aDragContext,
                                                gint             aX,
                                                gint             aY,
                                                GtkSelectionData*aSelectionData,
                                                guint            aInfo,
                                                guint            aTime,
                                                gpointer         aData);
 
-#if (MOZ_WIDGET_GTK == 2)
   mozilla::TemporaryRef<mozilla::gfx::DrawTarget> StartRemoteDrawing() MOZ_OVERRIDE;
-#endif
 
 private:
     void               UpdateAlpha(gfxPattern* aPattern, nsIntRect aBoundsRect);
 
     void               NativeResize(int32_t aWidth,
                                     int32_t aHeight,
                                     bool    aRepaint);
 
--- a/widget/windows/nsWindow.h
+++ b/widget/windows/nsWindow.h
@@ -523,16 +523,18 @@ protected:
   static LONG           sLastMouseDownTime;
   static LONG           sLastClickCount;
   static BYTE           sLastMouseButton;
 
   // Graphics
   HDC                   mPaintDC; // only set during painting
   HDC                   mCompositeDC; // only set during StartRemoteDrawing
 
+  nsIntRect             mLastPaintBounds;
+
 #ifdef CAIRO_HAS_D2D_SURFACE
   nsRefPtr<gfxD2DSurface>    mD2DWindowSurface; // Surface for this window.
 #endif
 
   // Transparency
 #ifdef MOZ_XUL
   // Use layered windows to support full 256 level alpha translucency
   nsRefPtr<gfxASurface> mTransparentSurface;
--- a/widget/windows/nsWindowGfx.cpp
+++ b/widget/windows/nsWindowGfx.cpp
@@ -40,16 +40,17 @@ using mozilla::plugins::PluginInstancePa
 #include "LayerManagerOGL.h"
 #ifdef MOZ_ENABLE_D3D9_LAYER
 #include "LayerManagerD3D9.h"
 #endif
 #ifdef MOZ_ENABLE_D3D10_LAYER
 #include "LayerManagerD3D10.h"
 #endif
 #include "mozilla/layers/CompositorParent.h"
+#include "ClientLayerManager.h"
 
 #include "nsUXThemeData.h"
 #include "nsUXThemeConstants.h"
 #include "mozilla/gfx/2D.h"
 
 extern "C" {
 #define PIXMAN_DONT_DEFINE_STDINT
 #include "pixman.h"
@@ -208,33 +209,30 @@ bool nsWindow::OnPaint(HDC aDC, uint32_t
       // could fail. Return without asserting since it's not our fault.
       NS_WARNING("Plugin failed to subclass our window");
     }
 
     ValidateRect(mWnd, nullptr);
     return true;
   }
 
-  // Do an early async composite so that we at least have something on screen
-  // in the right place, even if the content is out of date.
-  if (GetLayerManager()->GetBackendType() == LAYERS_CLIENT &&
-      mCompositorParent) {
+  ClientLayerManager *clientLayerManager =
+      (GetLayerManager()->GetBackendType() == LAYERS_CLIENT)
+      ? static_cast<ClientLayerManager*>(GetLayerManager())
+      : nullptr;
+
+  if (clientLayerManager && mCompositorParent &&
+      !mBounds.IsEqualEdges(mLastPaintBounds))
+  {
+    // Do an early async composite so that we at least have something on the
+    // screen in the right place, even if the content is out of date.
     mCompositorParent->ScheduleRenderOnCompositorThread();
   }
+  mLastPaintBounds = mBounds;
 
-  nsIWidgetListener* listener = GetPaintListener();
-  if (listener) {
-    listener->WillPaintWindow(this);
-  }
-  // Re-get the listener since the will paint notification may have killed it.
-  listener = GetPaintListener();
-  if (!listener)
-    return false;
-
-  bool result = true;
   PAINTSTRUCT ps;
 
 #ifdef MOZ_XUL
   if (!aDC && (eTransparencyTransparent == mTransparencyMode))
   {
     // For layered translucent windows all drawing should go to memory DC and no
     // WM_PAINT messages are normally generated. To support asynchronous painting
     // we force generation of WM_PAINT messages by invalidating window areas with
@@ -268,16 +266,40 @@ bool nsWindow::OnPaint(HDC aDC, uint32_t
   }
 
 #ifdef MOZ_XUL
   bool forceRepaint = aDC || (eTransparencyTransparent == mTransparencyMode);
 #else
   bool forceRepaint = nullptr != aDC;
 #endif
   nsIntRegion region = GetRegionToPaint(forceRepaint, ps, hDC);
+
+  if (clientLayerManager && mCompositorParent) {
+    // We need to paint to the screen even if nothing changed, since if we
+    // don't have a compositing window manager, our pixels could be stale.
+    clientLayerManager->SetNeedsComposite(true);
+    clientLayerManager->SendInvalidRegion(region);
+  }
+
+  nsIWidgetListener* listener = GetPaintListener();
+  if (listener) {
+    listener->WillPaintWindow(this);
+  }
+  // Re-get the listener since the will paint notification may have killed it.
+  listener = GetPaintListener();
+  if (!listener) {
+    return false;
+  }
+
+  if (clientLayerManager && mCompositorParent && clientLayerManager->NeedsComposite()) {
+    mCompositorParent->ScheduleRenderOnCompositorThread();
+    clientLayerManager->SetNeedsComposite(false);
+  }
+
+  bool result = true;
   if (!region.IsEmpty() && listener)
   {
     // Should probably pass in a real region here, using GetRandomRgn
     // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/clipping_4q0e.asp
 
 #ifdef WIDGET_DEBUG_OUTPUT
     debug_DumpPaintEvent(stdout,
                          this,