Bug 622072. Part 1: Remove rect parameter from Updated() and change implementations to defer updates to render time. r=bas
authorRobert O'Callahan <robert@ocallahan.org>
Mon, 28 Mar 2011 12:59:46 +1300
changeset 64540 f1ea5b6264d0a150efe7275a526a1c567dca8f23
parent 64539 bec3f251f53a43a08a13d31f190cf2877bc99f23
child 64541 d3481745c067a9df6c8446079db1aeb26d257b1a
push idunknown
push userunknown
push dateunknown
reviewersbas
bugs622072
milestone2.2a1pre
Bug 622072. Part 1: Remove rect parameter from Updated() and change implementations to defer updates to render time. r=bas The rectangle parameter is currently not used --- all callers always pass the full canvas bounds. In the long term, we probably won't want this parameter since all implementations should be doing accelerated drawing direct to buffers with no intermediate copies, hence there will be no need to optimize the size of those copies. Plus, performance-sensitive testcases tend to paint most or all of the canvas on every frame anyway.
content/canvas/src/WebGLContext.cpp
content/canvas/src/nsCanvasRenderingContext2D.cpp
gfx/layers/Layers.h
gfx/layers/basic/BasicLayers.cpp
gfx/layers/d3d10/CanvasLayerD3D10.cpp
gfx/layers/d3d10/CanvasLayerD3D10.h
gfx/layers/d3d9/CanvasLayerD3D9.cpp
gfx/layers/d3d9/CanvasLayerD3D9.h
gfx/layers/ipc/PLayers.ipdl
gfx/layers/ipc/ShadowLayers.cpp
gfx/layers/ipc/ShadowLayersParent.cpp
gfx/layers/opengl/CanvasLayerOGL.cpp
gfx/layers/opengl/CanvasLayerOGL.h
--- a/content/canvas/src/WebGLContext.cpp
+++ b/content/canvas/src/WebGLContext.cpp
@@ -622,17 +622,17 @@ static PRUint8 gWebGLLayerUserData;
 already_AddRefed<layers::CanvasLayer>
 WebGLContext::GetCanvasLayer(CanvasLayer *aOldLayer,
                              LayerManager *aManager)
 {
     if (!mResetLayer && aOldLayer &&
         aOldLayer->HasUserData(&gWebGLLayerUserData)) {
         NS_ADDREF(aOldLayer);
         if (mInvalidated) {
-            aOldLayer->Updated(nsIntRect(0, 0, mWidth, mHeight));
+            aOldLayer->Updated();
             mInvalidated = PR_FALSE;
             HTMLCanvasElement()->GetPrimaryCanvasFrame()->MarkLayersActive();
         }
         return aOldLayer;
     }
 
     nsRefPtr<CanvasLayer> canvasLayer = aManager->CreateCanvasLayer();
     if (!canvasLayer) {
@@ -656,17 +656,17 @@ WebGLContext::GetCanvasLayer(CanvasLayer
     }
 
     data.mSize = nsIntSize(mWidth, mHeight);
     data.mGLBufferIsPremultiplied = mOptions.premultipliedAlpha ? PR_TRUE : PR_FALSE;
 
     canvasLayer->Initialize(data);
     PRUint32 flags = gl->CreationFormat().alpha == 0 ? Layer::CONTENT_OPAQUE : 0;
     canvasLayer->SetContentFlags(flags);
-    canvasLayer->Updated(nsIntRect(0, 0, mWidth, mHeight));
+    canvasLayer->Updated();
 
     mInvalidated = PR_FALSE;
     mResetLayer = PR_FALSE;
 
     return canvasLayer.forget().get();
 }
 
 NS_IMETHODIMP
--- a/content/canvas/src/nsCanvasRenderingContext2D.cpp
+++ b/content/canvas/src/nsCanvasRenderingContext2D.cpp
@@ -4125,19 +4125,17 @@ nsCanvasRenderingContext2D::GetCanvasLay
 {
     if (!mValid)
         return nsnull;
 
     if (!mResetLayer && aOldLayer &&
         aOldLayer->HasUserData(&g2DContextLayerUserData)) {
         NS_ADDREF(aOldLayer);
         if (mIsEntireFrameInvalid || mInvalidateCount > 0) {
-            // XXX Need to just update the changed area here; we should keep track
-            // of the rectangle based on Redraw args.
-            aOldLayer->Updated(nsIntRect(0, 0, mWidth, mHeight));
+            aOldLayer->Updated();
             MarkContextClean();
             HTMLCanvasElement()->GetPrimaryCanvasFrame()->MarkLayersActive();
         }
 
         return aOldLayer;
     }
 
     nsRefPtr<CanvasLayer> canvasLayer = aManager->CreateCanvasLayer();
@@ -4150,17 +4148,17 @@ nsCanvasRenderingContext2D::GetCanvasLay
     CanvasLayer::Data data;
 
     data.mSurface = mSurface.get();
     data.mSize = nsIntSize(mWidth, mHeight);
 
     canvasLayer->Initialize(data);
     PRUint32 flags = mOpaque ? Layer::CONTENT_OPAQUE : 0;
     canvasLayer->SetContentFlags(flags);
-    canvasLayer->Updated(nsIntRect(0, 0, mWidth, mHeight));
+    canvasLayer->Updated();
 
     mResetLayer = PR_FALSE;
 
     MarkContextClean();
 
     return canvasLayer.forget().get();
 }
 
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -1171,23 +1171,20 @@ public:
    * have either mSurface or mGLContext initialized (but not both), as
    * well as mSize.
    *
    * This must only be called once.
    */
   virtual void Initialize(const Data& aData) = 0;
 
   /**
-   * CONSTRUCTION PHASE ONLY
-   * Notify this CanvasLayer that the rectangle given by aRect
-   * has been updated, and any work that needs to be done
-   * to bring the contents from the Surface/GLContext to the
-   * Layer in preparation for compositing should be performed.
+   * Notify this CanvasLayer that the canvas surface contents have
+   * changed (or will change) before the next transaction.
    */
-  virtual void Updated(const nsIntRect& aRect) = 0;
+  void Updated() { mDirty = PR_TRUE; }
 
   /**
    * CONSTRUCTION PHASE ONLY
    * Set the filter used to resample this image (if necessary).
    */
   void SetFilter(gfxPattern::GraphicsFilter aFilter) { mFilter = aFilter; }
   gfxPattern::GraphicsFilter GetFilter() const { return mFilter; }
 
@@ -1202,23 +1199,27 @@ public:
     mEffectiveTransform =
         SnapTransform(GetLocalTransform(), gfxRect(0, 0, mBounds.width, mBounds.height),
                       nsnull)*
         SnapTransform(aTransformToSurface, gfxRect(0, 0, 0, 0), nsnull);
   }
 
 protected:
   CanvasLayer(LayerManager* aManager, void* aImplData)
-    : Layer(aManager, aImplData), mFilter(gfxPattern::FILTER_GOOD) {}
+    : Layer(aManager, aImplData), mFilter(gfxPattern::FILTER_GOOD), mDirty(PR_FALSE) {}
 
   virtual nsACString& PrintInfo(nsACString& aTo, const char* aPrefix);
 
   /**
    * 0, 0, canvaswidth, canvasheight
    */
   nsIntRect mBounds;
   gfxPattern::GraphicsFilter mFilter;
+  /**
+   * Set to true in Updated(), cleared during a transaction.
+   */
+  PRPackedBool mDirty;
 };
 
 }
 }
 
 #endif /* GFX_LAYERS_H */
--- a/gfx/layers/basic/BasicLayers.cpp
+++ b/gfx/layers/basic/BasicLayers.cpp
@@ -888,45 +888,41 @@ public:
   virtual void SetVisibleRegion(const nsIntRegion& aRegion)
   {
     NS_ASSERTION(BasicManager()->InConstruction(),
                  "Can only set properties in construction phase");
     CanvasLayer::SetVisibleRegion(aRegion);
   }
 
   virtual void Initialize(const Data& aData);
-  virtual void Updated(const nsIntRect& aRect);
   virtual void Paint(gfxContext* aContext);
 
   virtual void PaintWithOpacity(gfxContext* aContext,
                                 float aOpacity);
 
 protected:
   BasicLayerManager* BasicManager()
   {
     return static_cast<BasicLayerManager*>(mManager);
   }
+  void UpdateSurface();
 
   nsRefPtr<gfxASurface> mSurface;
   nsRefPtr<mozilla::gl::GLContext> mGLContext;
   PRUint32 mCanvasFramebuffer;
 
-  nsIntRect mUpdatedRect;
-
   PRPackedBool mGLBufferIsPremultiplied;
   PRPackedBool mNeedsYFlip;
 };
 
 void
 BasicCanvasLayer::Initialize(const Data& aData)
 {
   NS_ASSERTION(mSurface == nsnull, "BasicCanvasLayer::Initialize called twice!");
 
-  mUpdatedRect.Empty();
-
   if (aData.mSurface) {
     mSurface = aData.mSurface;
     NS_ASSERTION(aData.mGLContext == nsnull,
                  "CanvasLayer can't have both surface and GLContext");
     mNeedsYFlip = PR_FALSE;
   } else if (aData.mGLContext) {
     NS_ASSERTION(aData.mGLContext->IsOffscreen(), "canvas gl context isn't offscreen");
     mGLContext = aData.mGLContext;
@@ -936,22 +932,21 @@ BasicCanvasLayer::Initialize(const Data&
   } else {
     NS_ERROR("CanvasLayer created without mSurface or mGLContext?");
   }
 
   mBounds.SetRect(0, 0, aData.mSize.width, aData.mSize.height);
 }
 
 void
-BasicCanvasLayer::Updated(const nsIntRect& aRect)
+BasicCanvasLayer::UpdateSurface()
 {
-  NS_ASSERTION(mUpdatedRect.IsEmpty(),
-               "CanvasLayer::Updated called more than once in a transaction!");
-
-  mUpdatedRect.UnionRect(mUpdatedRect, aRect);
+  if (!mDirty)
+    return;
+  mDirty = PR_FALSE;
 
   if (mGLContext) {
     nsRefPtr<gfxImageSurface> isurf =
       new gfxImageSurface(gfxIntSize(mBounds.width, mBounds.height),
                           (GetContentFlags() & CONTENT_OPAQUE)
                             ? gfxASurface::ImageFormatRGB24
                             : gfxASurface::ImageFormatARGB32);
     if (!isurf || isurf->CairoStatus() != 0) {
@@ -971,19 +966,16 @@ BasicCanvasLayer::Updated(const nsIntRec
 
     mGLContext->fGetIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING, (GLint*)&currentFramebuffer);
 
     // Make sure that we read pixels from the correct framebuffer, regardless
     // of what's currently bound.
     if (currentFramebuffer != mCanvasFramebuffer)
       mGLContext->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mCanvasFramebuffer);
 
-    // For simplicity, we read the entire framebuffer for now -- in
-    // the future we should use mUpdatedRect, though with WebGL we don't
-    // have an easy way to generate one.
     mGLContext->ReadPixelsIntoImageSurface(0, 0,
                                            mBounds.width, mBounds.height,
                                            isurf);
 
     // Put back the previous framebuffer binding.
     if (currentFramebuffer != mCanvasFramebuffer)
       mGLContext->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, currentFramebuffer);
 
@@ -992,25 +984,22 @@ BasicCanvasLayer::Updated(const nsIntRec
     // Note that this is a WebGL attribute; GL itself has no knowledge of
     // premultiplied or unpremultiplied alpha.
     if (!mGLBufferIsPremultiplied)
       gfxUtils::PremultiplyImageSurface(isurf);
 
     // stick our surface into mSurface, so that the Paint() path is the same
     mSurface = isurf;
   }
-
-  // sanity
-  NS_ASSERTION(mUpdatedRect.IsEmpty() || mBounds.Contains(mUpdatedRect),
-               "CanvasLayer: Updated rect bigger than bounds!");
 }
 
 void
 BasicCanvasLayer::Paint(gfxContext* aContext)
 {
+  UpdateSurface();
   PaintWithOpacity(aContext, GetEffectiveOpacity());
 }
 
 void
 BasicCanvasLayer::PaintWithOpacity(gfxContext* aContext,
                                    float aOpacity)
 {
   NS_ASSERTION(BasicManager()->InDrawing(),
@@ -1032,18 +1021,16 @@ BasicCanvasLayer::PaintWithOpacity(gfxCo
   // No need to snap here; our transform is already set up to snap our rect
   aContext->Rectangle(gfxRect(0, 0, mBounds.width, mBounds.height));
   aContext->SetPattern(pat);
   aContext->FillWithOpacity(aOpacity);
 
   if (mNeedsYFlip) {
     aContext->SetMatrix(m);
   }
-
-  mUpdatedRect.Empty();
 }
 
 class BasicReadbackLayer : public ReadbackLayer,
                            BasicImplData
 {
 public:
   BasicReadbackLayer(BasicLayerManager* aLayerManager) :
     ReadbackLayer(aLayerManager, static_cast<BasicImplData*>(this))
@@ -2559,19 +2546,16 @@ public:
   virtual void Disconnect()
   {
     DestroyFrontBuffer();
     ShadowCanvasLayer::Disconnect();
   }
 
   virtual void Initialize(const Data& aData);
 
-  virtual void Updated(const nsIntRect& aRect)
-  {}
-
   virtual already_AddRefed<gfxSharedImageSurface>
   Swap(gfxSharedImageSurface* newFront);
 
   virtual void DestroyFrontBuffer()
   {
     if (mFrontSurface) {
       BasicManager()->ShadowLayerManager::DestroySharedSurface(mFrontSurface, mAllocator);
     }
--- a/gfx/layers/d3d10/CanvasLayerD3D10.cpp
+++ b/gfx/layers/d3d10/CanvasLayerD3D10.cpp
@@ -109,18 +109,22 @@ CanvasLayerD3D10::Initialize(const Data&
       return;
     }
   }
 
   device()->CreateShaderResourceView(mTexture, NULL, getter_AddRefs(mSRView));
 }
 
 void
-CanvasLayerD3D10::Updated(const nsIntRect& aRect)
+CanvasLayerD3D10::UpdateSurface()
 {
+  if (!mDirty)
+    return;
+  mDirty = PR_FALSE;
+
   if (mIsD2DTexture) {
     mSurface->Flush();
     return;
   }
 
   if (mUsingSharedTexture) {
     // need to sync on the d3d9 device
     if (mGLContext) {
@@ -156,19 +160,16 @@ CanvasLayerD3D10::Updated(const nsIntRec
 
     mGLContext->fGetIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING, (GLint*)&currentFramebuffer);
 
     // Make sure that we read pixels from the correct framebuffer, regardless
     // of what's currently bound.
     if (currentFramebuffer != mCanvasFramebuffer)
       mGLContext->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mCanvasFramebuffer);
 
-    // For simplicity, we read the entire framebuffer for now -- in
-    // the future we should use aRect, though with WebGL we don't
-    // have an easy way to generate one.
     nsRefPtr<gfxImageSurface> tmpSurface =
       new gfxImageSurface(destination,
                           gfxIntSize(mBounds.width, mBounds.height),
                           mBounds.width * 4,
                           gfxASurface::ImageFormatARGB32);
     mGLContext->ReadPixelsIntoImageSurface(0, 0,
                                            mBounds.width, mBounds.height,
                                            tmpSurface);
@@ -184,40 +185,36 @@ CanvasLayerD3D10::Updated(const nsIntRec
                destination + mBounds.width * 4 * y,
                mBounds.width * 4);
       }
       delete [] destination;
     }
     mTexture->Unmap(0);
   } else if (mSurface) {
     RECT r;
-    r.left = aRect.x;
-    r.top = aRect.y;
-    r.right = aRect.XMost();
-    r.bottom = aRect.YMost();
+    r.left = 0;
+    r.top = 0;
+    r.right = mBounds.width;
+    r.bottom = mBounds.height;
 
     D3D10_MAPPED_TEXTURE2D map;
     HRESULT hr = mTexture->Map(0, D3D10_MAP_WRITE_DISCARD, 0, &map);
 
     if (FAILED(hr)) {
       NS_WARNING("Failed to lock CanvasLayer texture.");
       return;
     }
 
-    PRUint8 *startBits;
-    PRUint32 sourceStride;
-
     nsRefPtr<gfxImageSurface> dstSurface;
 
     dstSurface = new gfxImageSurface((unsigned char*)map.pData,
-                                     gfxIntSize(aRect.width, aRect.height),
+                                     gfxIntSize(mBounds.width, mBounds.height),
                                      map.RowPitch,
                                      gfxASurface::ImageFormatARGB32);
     nsRefPtr<gfxContext> ctx = new gfxContext(dstSurface);
-    ctx->Translate(gfxPoint(-aRect.x, -aRect.y));
     ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
     ctx->SetSource(mSurface);
     ctx->Paint();
     
     mTexture->Unmap(0);
   }
 }
 
@@ -225,16 +222,18 @@ Layer*
 CanvasLayerD3D10::GetLayer()
 {
   return this;
 }
 
 void
 CanvasLayerD3D10::RenderLayer()
 {
+  UpdateSurface();
+
   if (!mTexture) {
     return;
   }
 
   nsIntRect visibleRect = mVisibleRegion.GetBounds();
 
   SetEffectTransformAndOpacity();
 
--- a/gfx/layers/d3d10/CanvasLayerD3D10.h
+++ b/gfx/layers/d3d10/CanvasLayerD3D10.h
@@ -59,25 +59,26 @@ public:
   {
       mImplData = static_cast<LayerD3D10*>(this);
   }
 
   ~CanvasLayerD3D10();
 
   // CanvasLayer implementation
   virtual void Initialize(const Data& aData);
-  virtual void Updated(const nsIntRect& aRect);
 
   // LayerD3D10 implementation
   virtual Layer* GetLayer();
   virtual void RenderLayer();
 
 private:
   typedef mozilla::gl::GLContext GLContext;
 
+  void UpdateSurface();
+
   nsRefPtr<gfxASurface> mSurface;
   nsRefPtr<GLContext> mGLContext;
   nsRefPtr<ID3D10Texture2D> mTexture;
   nsRefPtr<ID3D10ShaderResourceView> mSRView;
 
   PRUint32 mCanvasFramebuffer;
 
   PRPackedBool mDataIsPremultiplied;
--- a/gfx/layers/d3d9/CanvasLayerD3D9.cpp
+++ b/gfx/layers/d3d9/CanvasLayerD3D9.cpp
@@ -74,18 +74,22 @@ CanvasLayerD3D9::Initialize(const Data& 
   }
 
   mBounds.SetRect(0, 0, aData.mSize.width, aData.mSize.height);
 
   CreateTexture();
 }
 
 void
-CanvasLayerD3D9::Updated(const nsIntRect& aRect)
+CanvasLayerD3D9::UpdateSurface()
 {
+  if (!mDirty)
+    return;
+  mDirty = PR_FALSE;
+
   if (!mTexture) {
     CreateTexture();
     NS_WARNING("CanvasLayerD3D9::Updated called but no texture present!");
     return;
   }
 
   if (mGLContext) {
     // WebGL reads entire surface.
@@ -112,19 +116,16 @@ CanvasLayerD3D9::Updated(const nsIntRect
 
     mGLContext->fGetIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING, (GLint*)&currentFramebuffer);
 
     // Make sure that we read pixels from the correct framebuffer, regardless
     // of what's currently bound.
     if (currentFramebuffer != mCanvasFramebuffer)
       mGLContext->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mCanvasFramebuffer);
 
-    // For simplicity, we read the entire framebuffer for now -- in
-    // the future we should use aRect, though with WebGL we don't
-    // have an easy way to generate one.
     nsRefPtr<gfxImageSurface> tmpSurface =
       new gfxImageSurface(destination,
                           gfxIntSize(mBounds.width, mBounds.height),
                           mBounds.width * 4,
                           gfxASurface::ImageFormatARGB32);
     mGLContext->ReadPixelsIntoImageSurface(0, 0,
                                            mBounds.width, mBounds.height,
                                            tmpSurface);
@@ -140,90 +141,81 @@ CanvasLayerD3D9::Updated(const nsIntRect
                destination + mBounds.width * 4 * y,
                mBounds.width * 4);
       }
       delete [] destination;
     }
     mTexture->UnlockRect(0);
   } else if (mSurface) {
     RECT r;
-    r.left = aRect.x;
-    r.top = aRect.y;
-    r.right = aRect.XMost();
-    r.bottom = aRect.YMost();
+    r.left = mBounds.x;
+    r.top = mBounds.y;
+    r.right = mBounds.XMost();
+    r.bottom = mBounds.YMost();
 
     D3DLOCKED_RECT lockedRect;
     HRESULT hr = mTexture->LockRect(0, &lockedRect, &r, 0);
 
     if (FAILED(hr)) {
       NS_WARNING("Failed to lock CanvasLayer texture.");
       return;
     }
 
-    PRUint8 *startBits;
-    PRUint32 sourceStride;
-
     nsRefPtr<gfxImageSurface> sourceSurface;
 
     if (mSurface->GetType() == gfxASurface::SurfaceTypeWin32) {
       sourceSurface = mSurface->GetAsImageSurface();
-      startBits = sourceSurface->Data() + sourceSurface->Stride() * aRect.y +
-                  aRect.x * 4;
-      sourceStride = sourceSurface->Stride();
     } else if (mSurface->GetType() == gfxASurface::SurfaceTypeImage) {
       sourceSurface = static_cast<gfxImageSurface*>(mSurface.get());
       if (sourceSurface->Format() != gfxASurface::ImageFormatARGB32 &&
           sourceSurface->Format() != gfxASurface::ImageFormatRGB24)
       {
         mTexture->UnlockRect(0);
         return;
       }
-      startBits = sourceSurface->Data() + sourceSurface->Stride() * aRect.y +
-                  aRect.x * 4;
-      sourceStride = sourceSurface->Stride();
     } else {
-      sourceSurface = new gfxImageSurface(gfxIntSize(aRect.width, aRect.height),
+      sourceSurface = new gfxImageSurface(gfxIntSize(mBounds.width, mBounds.height),
                                           gfxASurface::ImageFormatARGB32);
       nsRefPtr<gfxContext> ctx = new gfxContext(sourceSurface);
-      ctx->Translate(gfxPoint(-aRect.x, -aRect.y));
       ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
       ctx->SetSource(mSurface);
       ctx->Paint();
-      startBits = sourceSurface->Data();
-      sourceStride = sourceSurface->Stride();
     }
 
+    PRUint8 *startBits = sourceSurface->Data();
+    PRUint32 sourceStride = sourceSurface->Stride();
+
     if (sourceSurface->Format() != gfxASurface::ImageFormatARGB32) {
       mHasAlpha = false;
     } else {
       mHasAlpha = true;
     }
 
-    for (int y = 0; y < aRect.height; y++) {
+    for (int y = 0; y < mBounds.height; y++) {
       memcpy((PRUint8*)lockedRect.pBits + lockedRect.Pitch * y,
              startBits + sourceStride * y,
-             aRect.width * 4);
+             mBounds.width * 4);
     }
 
     mTexture->UnlockRect(0);
   }
 }
 
 Layer*
 CanvasLayerD3D9::GetLayer()
 {
   return this;
 }
 
 void
 CanvasLayerD3D9::RenderLayer()
 {
-  if (!mTexture) {
-    Updated(mBounds);
-  }
+  UpdateSurface();
+  if (!mTexture)
+    return;
 
   /*
    * We flip the Y axis here, note we can only do this because we are in 
    * CULL_NONE mode!
    */
 
   ShaderConstantRect quad(0, 0, mBounds.width, mBounds.height);
   if (mNeedsYFlip) {
--- a/gfx/layers/d3d9/CanvasLayerD3D9.h
+++ b/gfx/layers/d3d9/CanvasLayerD3D9.h
@@ -61,29 +61,30 @@ public:
       mImplData = static_cast<LayerD3D9*>(this);
       aManager->deviceManager()->mLayersWithResources.AppendElement(this);
   }
 
   ~CanvasLayerD3D9();
 
   // CanvasLayer implementation
   virtual void Initialize(const Data& aData);
-  virtual void Updated(const nsIntRect& aRect);
 
   // LayerD3D9 implementation
   virtual Layer* GetLayer();
   virtual void RenderLayer();
   virtual void CleanResources();
   virtual void LayerManagerDestroyed();
 
   void CreateTexture();
 
 protected:
   typedef mozilla::gl::GLContext GLContext;
 
+  void UpdateSurface();
+
   nsRefPtr<gfxASurface> mSurface;
   nsRefPtr<GLContext> mGLContext;
   nsRefPtr<IDirect3DTexture9> mTexture;
 
   PRUint32 mCanvasFramebuffer;
 
   PRPackedBool mDataIsPremultiplied;
   PRPackedBool mNeedsYFlip;
--- a/gfx/layers/ipc/PLayers.ipdl
+++ b/gfx/layers/ipc/PLayers.ipdl
@@ -167,17 +167,16 @@ struct OpRemoveChild  { PLayer container
 struct OpPaintThebesBuffer {
   PLayer layer;
   ThebesBuffer newFrontBuffer;
   nsIntRegion updatedRegion;
 };
 
 struct OpPaintCanvas  {
   PLayer layer;
-  nsIntRect updated;
   Shmem newFrontBuffer;
 };
 
 struct OpPaintImage  {
   PLayer layer;
   Shmem newFrontBuffer;
 };
 
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -296,17 +296,16 @@ ShadowLayerForwarder::PaintedImage(Shado
   mTxn->AddPaint(OpPaintImage(NULL, Shadow(aImage),
                               aNewFrontSurface->GetShmem()));
 }
 void
 ShadowLayerForwarder::PaintedCanvas(ShadowableLayer* aCanvas,
                                     gfxSharedImageSurface* aNewFrontSurface)
 {
   mTxn->AddPaint(OpPaintCanvas(NULL, Shadow(aCanvas),
-                               nsIntRect(),
                                aNewFrontSurface->GetShmem()));
 }
 
 PRBool
 ShadowLayerForwarder::EndTransaction(InfallibleTArray<EditReply>* aReplies)
 {
   NS_ABORT_IF_FALSE(HasShadowManager(), "no manager to forward to");
   NS_ABORT_IF_FALSE(!mTxn->Finished(), "forgot BeginTransaction?");
--- a/gfx/layers/ipc/ShadowLayersParent.cpp
+++ b/gfx/layers/ipc/ShadowLayersParent.cpp
@@ -417,17 +417,17 @@ ShadowLayersParent::RecvUpdate(const Inf
 
       nsRefPtr<gfxSharedImageSurface> newFront =
         gfxSharedImageSurface::Open(op.newFrontBuffer());
       nsRefPtr<gfxSharedImageSurface> newBack = canvas->Swap(newFront);
       if (newFront == newBack) {
         newFront.forget();
       }
 
-      canvas->Updated(op.updated());
+      canvas->Updated();
 
       replyv.push_back(OpBufferSwap(shadow, NULL,
                                     newBack->GetShmem()));
 
       break;
     }
     case Edit::TOpPaintImage: {
       MOZ_LAYERS_LOG(("[ParentSide] Paint ImageLayer"));
--- a/gfx/layers/opengl/CanvasLayerOGL.cpp
+++ b/gfx/layers/opengl/CanvasLayerOGL.cpp
@@ -130,73 +130,66 @@ CanvasLayerOGL::MakeTexture()
 
   gl()->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_LINEAR);
   gl()->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_LINEAR);
   gl()->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
   gl()->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
 }
 
 void
-CanvasLayerOGL::Updated(const nsIntRect& aRect)
+CanvasLayerOGL::UpdateSurface()
 {
+  if (!mDirty)
+    return;
+  mDirty = PR_FALSE;
+
   if (mDestroyed || mDelayedUpdates) {
     return;
   }
 
-  NS_ASSERTION(mUpdatedRect.IsEmpty(),
-               "CanvasLayer::Updated called more than once during a transaction!");
-
   mOGLManager->MakeCurrent();
 
-  mUpdatedRect.UnionRect(mUpdatedRect, aRect);
-
   if (mCanvasGLContext &&
       mCanvasGLContext->GetContextType() == gl()->GetContextType())
   {
     if (gl()->BindOffscreenNeedsTexture(mCanvasGLContext) &&
         mTexture == 0)
     {
       MakeTexture();
     }
   } else {
-    if (!mTexture) {
-      mUpdatedRect = mBounds;
-    }
-
     nsRefPtr<gfxASurface> updatedAreaSurface;
     if (mCanvasSurface) {
       updatedAreaSurface = mCanvasSurface;
     } else if (mCanvasGLContext) {
       nsRefPtr<gfxImageSurface> updatedAreaImageSurface =
-        new gfxImageSurface(gfxIntSize(mUpdatedRect.width, mUpdatedRect.height),
+        new gfxImageSurface(gfxIntSize(mBounds.width, mBounds.height),
                             gfxASurface::ImageFormatARGB32);
-      mCanvasGLContext->ReadPixelsIntoImageSurface(mUpdatedRect.x, mUpdatedRect.y,
-                                                   mUpdatedRect.width,
-                                                   mUpdatedRect.height,
+      mCanvasGLContext->ReadPixelsIntoImageSurface(0, 0,
+                                                   mBounds.width,
+                                                   mBounds.height,
                                                    updatedAreaImageSurface);
       updatedAreaSurface = updatedAreaImageSurface;
     }
 
     mLayerProgram =
       gl()->UploadSurfaceToTexture(updatedAreaSurface,
-                                   mUpdatedRect,
+                                   mBounds,
                                    mTexture,
                                    false,
-                                   mUpdatedRect.TopLeft());
+                                   nsIntPoint(0, 0));
   }
-
-  // sanity
-  NS_ASSERTION(mBounds.Contains(mUpdatedRect),
-               "CanvasLayer: Updated rect bigger than bounds!");
 }
 
 void
 CanvasLayerOGL::RenderLayer(int aPreviousDestination,
                             const nsIntPoint& aOffset)
 {
+  UpdateSurface();
+
   mOGLManager->MakeCurrent();
 
   // XXX We're going to need a different program depending on if
   // mGLBufferIsPremultiplied is TRUE or not.  The RGBLayerProgram
   // assumes that it's true.
 
   gl()->fActiveTexture(LOCAL_GL_TEXTURE0);
 
@@ -243,18 +236,16 @@ CanvasLayerOGL::RenderLayer(int aPreviou
   program->SetRenderOffset(aOffset);
   program->SetTextureUnit(0);
 
   mOGLManager->BindAndDrawQuad(program, mNeedsYFlip ? true : false);
 
   if (useGLContext) {
     gl()->UnbindTex2DOffscreen(mCanvasGLContext);
   }
-
-  mUpdatedRect.Empty();
 }
 
 
 #ifdef MOZ_IPC
 
 ShadowCanvasLayerOGL::ShadowCanvasLayerOGL(LayerManagerOGL* aManager)
   : ShadowCanvasLayer(aManager, nsnull)
   , LayerOGL(aManager)
--- a/gfx/layers/opengl/CanvasLayerOGL.h
+++ b/gfx/layers/opengl/CanvasLayerOGL.h
@@ -61,34 +61,33 @@ public:
       mDelayedUpdates(PR_FALSE)
   { 
       mImplData = static_cast<LayerOGL*>(this);
   }
   ~CanvasLayerOGL() { Destroy(); }
 
   // CanvasLayer implementation
   virtual void Initialize(const Data& aData);
-  virtual void Updated(const nsIntRect& aRect);
 
   // LayerOGL implementation
   virtual void Destroy();
   virtual Layer* GetLayer() { return this; }
   virtual void RenderLayer(int aPreviousFrameBuffer,
                            const nsIntPoint& aOffset);
 
 protected:
+  void UpdateSurface();
+
   nsRefPtr<gfxASurface> mCanvasSurface;
   nsRefPtr<GLContext> mCanvasGLContext;
   gl::ShaderProgramType mLayerProgram;
 
   void MakeTexture();
   GLuint mTexture;
 
-  nsIntRect mUpdatedRect;
-
   PRPackedBool mDelayedUpdates;
   PRPackedBool mGLBufferIsPremultiplied;
   PRPackedBool mNeedsYFlip;
 };
 
 #ifdef MOZ_IPC
 // NB: eventually we'll have separate shadow canvas2d and shadow
 // canvas3d layers, but currently they look the same from the