Bug 735378 - Cache temporary composite surfaces - r=bgirard
authorJeff Gilbert <jgilbert@mozilla.com>
Sun, 25 Mar 2012 12:50:26 -0700
changeset 93591 5574b6eadb139dbd0b714edc98e7cf5945aa79dc
parent 93590 fe40e7c2d790fee9c7c937cd9bf984b5651d775d
child 93592 c4a4b0171b99299ebd17218fe38fa68f6e7d4a31
push id886
push userlsblakk@mozilla.com
push dateMon, 04 Jun 2012 19:57:52 +0000
treeherdermozilla-beta@bbd8d5efd6d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbgirard
bugs735378
milestone14.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 735378 - Cache temporary composite surfaces - r=bgirard
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/opengl/CanvasLayerOGL.cpp
gfx/layers/opengl/CanvasLayerOGL.h
--- a/gfx/layers/basic/BasicLayers.cpp
+++ b/gfx/layers/basic/BasicLayers.cpp
@@ -1076,16 +1076,40 @@ protected:
   nsRefPtr<gfxASurface> mSurface;
   nsRefPtr<mozilla::gl::GLContext> mGLContext;
   mozilla::RefPtr<mozilla::gfx::DrawTarget> mDrawTarget;
   
   PRUint32 mCanvasFramebuffer;
 
   bool mGLBufferIsPremultiplied;
   bool mNeedsYFlip;
+
+  nsRefPtr<gfxImageSurface> mCachedTempSurface;
+  gfxIntSize mCachedSize;
+  gfxImageFormat mCachedFormat;
+
+  gfxImageSurface* GetTempSurface(const gfxIntSize& aSize, const gfxImageFormat aFormat)
+  {
+    if (!mCachedTempSurface ||
+        aSize.width != mCachedSize.width ||
+        aSize.height != mCachedSize.height ||
+        aFormat != mCachedFormat)
+    {
+      mCachedTempSurface = new gfxImageSurface(aSize, aFormat);
+      mCachedSize = aSize;
+      mCachedFormat = aFormat;
+    }
+
+    return mCachedTempSurface;
+  }
+
+  void DiscardTempSurface()
+  {
+    mCachedTempSurface = nsnull;
+  }
 };
 
 void
 BasicCanvasLayer::Initialize(const Data& aData)
 {
   NS_ASSERTION(mSurface == nsnull, "BasicCanvasLayer::Initialize called twice!");
 
   if (aData.mSurface) {
@@ -1134,34 +1158,40 @@ BasicCanvasLayer::UpdateSurface(gfxASurf
                    "Destination surface must be ImageSurface type");
       return;
     }
 
     // We need to read from the GLContext
     mGLContext->MakeCurrent();
 
 #if defined (MOZ_X11) && defined (MOZ_EGL_XRENDER_COMPOSITE)
-    mGLContext->fFinish();
+    mGLContext->GuaranteeResolve();
     gfxASurface* offscreenSurface = mGLContext->GetOffscreenPixmapSurface();
 
     // XRender can only blend premuliplied alpha, so only allow xrender
     // path if we have premultiplied alpha or opaque content.
     if (offscreenSurface && (mGLBufferIsPremultiplied || (GetContentFlags() & CONTENT_OPAQUE))) {  
         mSurface = offscreenSurface;
         mNeedsYFlip = false;
+        return;
     }
-    else
 #endif
-    {
-    nsRefPtr<gfxImageSurface> isurf = aDestSurface ?
-        static_cast<gfxImageSurface*>(aDestSurface) :
-        new gfxImageSurface(gfxIntSize(mBounds.width, mBounds.height),
-                            (GetContentFlags() & CONTENT_OPAQUE)
-                              ? gfxASurface::ImageFormatRGB24
-                              : gfxASurface::ImageFormatARGB32);
+    nsRefPtr<gfxImageSurface> isurf;
+    if (aDestSurface) {
+      DiscardTempSurface();
+      isurf = static_cast<gfxImageSurface*>(aDestSurface);
+    } else {
+      nsIntSize size(mBounds.width, mBounds.height);
+      gfxImageFormat format = (GetContentFlags() & CONTENT_OPAQUE)
+                                ? gfxASurface::ImageFormatRGB24
+                                : gfxASurface::ImageFormatARGB32;
+
+      isurf = GetTempSurface(size, format);
+    }
+
 
     if (!isurf || isurf->CairoStatus() != 0) {
       return;
     }
 
     NS_ASSERTION(isurf->Stride() == mBounds.width * 4, "gfxImageSurface stride isn't what we expect!");
 
     PRUint32 currentFramebuffer = 0;
@@ -1189,17 +1219,16 @@ BasicCanvasLayer::UpdateSurface(gfxASurf
       gfxUtils::PremultiplyImageSurface(isurf);
 
     // stick our surface into mSurface, so that the Paint() path is the same
     if (!aDestSurface) {
       mSurface = isurf;
     }
   }
 }
-}
 
 void
 BasicCanvasLayer::Paint(gfxContext* aContext)
 {
   if (IsHidden())
     return;
   UpdateSurface();
   FireDidTransactionCallback();
--- a/gfx/layers/d3d10/CanvasLayerD3D10.cpp
+++ b/gfx/layers/d3d10/CanvasLayerD3D10.cpp
@@ -167,20 +167,23 @@ CanvasLayerD3D10::UpdateSurface()
     
     HRESULT hr = mTexture->Map(0, D3D10_MAP_WRITE_DISCARD, 0, &map);
 
     if (FAILED(hr)) {
       NS_WARNING("Failed to map CanvasLayer texture.");
       return;
     }
 
+    const bool stridesMatch = map.RowPitch == mBounds.width * 4;
+
     PRUint8 *destination;
-    if (map.RowPitch != mBounds.width * 4) {
-      destination = new PRUint8[mBounds.width * mBounds.height * 4];
+    if (!stridesMatch) {
+      destination = GetTempBlob(mBounds.width * mBounds.height * 4);
     } else {
+      DiscardTempBlob();
       destination = (PRUint8*)map.pData;
     }
 
     mGLContext->MakeCurrent();
 
     PRUint32 currentFramebuffer = 0;
 
     mGLContext->fGetIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING, (GLint*)&currentFramebuffer);
@@ -199,23 +202,22 @@ CanvasLayerD3D10::UpdateSurface()
                                            mBounds.width, mBounds.height,
                                            tmpSurface);
     tmpSurface = nsnull;
 
     // Put back the previous framebuffer binding.
     if (currentFramebuffer != mCanvasFramebuffer)
       mGLContext->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, currentFramebuffer);
 
-    if (map.RowPitch != mBounds.width * 4) {
+    if (!stridesMatch) {
       for (int y = 0; y < mBounds.height; y++) {
         memcpy((PRUint8*)map.pData + map.RowPitch * y,
                destination + mBounds.width * 4 * y,
                mBounds.width * 4);
       }
-      delete [] destination;
     }
     mTexture->Unmap(0);
   } else if (mSurface) {
     RECT r;
     r.left = 0;
     r.top = 0;
     r.right = mBounds.width;
     r.bottom = mBounds.height;
--- a/gfx/layers/d3d10/CanvasLayerD3D10.h
+++ b/gfx/layers/d3d10/CanvasLayerD3D10.h
@@ -82,13 +82,31 @@ private:
 
   PRUint32 mCanvasFramebuffer;
 
   bool mDataIsPremultiplied;
   bool mNeedsYFlip;
   bool mIsD2DTexture;
   bool mUsingSharedTexture;
   bool mHasAlpha;
+
+  nsAutoArrayPtr<PRUint8> mCachedTempBlob;
+  PRUint32 mCachedTempBlob_Size;
+
+  PRUint8* GetTempBlob(const PRUint32 aSize)
+  {
+      if (!mCachedTempBlob || aSize != mCachedTempBlob_Size) {
+          mCachedTempBlob = new PRUint8[aSize];
+          mCachedTempBlob_Size = aSize;
+      }
+
+      return mCachedTempBlob;
+  }
+
+  void DiscardTempBlob()
+  {
+      mCachedTempBlob = nsnull;
+  }
 };
 
 } /* layers */
 } /* mozilla */
 #endif /* GFX_CANVASLAYERD3D10_H */
--- a/gfx/layers/d3d9/CanvasLayerD3D9.cpp
+++ b/gfx/layers/d3d9/CanvasLayerD3D9.cpp
@@ -108,20 +108,23 @@ CanvasLayerD3D9::UpdateSurface()
     LockTextureRectD3D9 textureLock(mTexture);
     if (!textureLock.HasLock()) {
       NS_WARNING("Failed to lock CanvasLayer texture.");
       return;
     }
 
     D3DLOCKED_RECT r = textureLock.GetLockRect();
 
+    const bool stridesMatch = r.Pitch == mBounds.width * 4;
+
     PRUint8 *destination;
-    if (r.Pitch != mBounds.width * 4) {
-      destination = new PRUint8[mBounds.width * mBounds.height * 4];
+    if (!stridesMatch) {
+      destination = GetTempBlob(mBounds.width * mBounds.height * 4);
     } else {
+      DiscardTempBlob();
       destination = (PRUint8*)r.pBits;
     }
 
     mGLContext->MakeCurrent();
 
     PRUint32 currentFramebuffer = 0;
 
     mGLContext->fGetIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING, (GLint*)&currentFramebuffer);
@@ -140,23 +143,22 @@ CanvasLayerD3D9::UpdateSurface()
                                            mBounds.width, mBounds.height,
                                            tmpSurface);
     tmpSurface = nsnull;
 
     // Put back the previous framebuffer binding.
     if (currentFramebuffer != mCanvasFramebuffer)
       mGLContext->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, currentFramebuffer);
 
-    if (r.Pitch != mBounds.width * 4) {
+    if (!stridesMatch) {
       for (int y = 0; y < mBounds.height; y++) {
         memcpy((PRUint8*)r.pBits + r.Pitch * y,
                destination + mBounds.width * 4 * y,
                mBounds.width * 4);
       }
-      delete [] destination;
     }
   } else {
     RECT r;
     r.left = mBounds.x;
     r.top = mBounds.y;
     r.right = mBounds.XMost();
     r.bottom = mBounds.YMost();
 
--- a/gfx/layers/d3d9/CanvasLayerD3D9.h
+++ b/gfx/layers/d3d9/CanvasLayerD3D9.h
@@ -87,16 +87,34 @@ protected:
   nsRefPtr<IDirect3DTexture9> mTexture;
   RefPtr<gfx::DrawTarget> mDrawTarget;
 
   PRUint32 mCanvasFramebuffer;
 
   bool mDataIsPremultiplied;
   bool mNeedsYFlip;
   bool mHasAlpha;
+
+  nsAutoArrayPtr<PRUint8> mCachedTempBlob;
+  PRUint32 mCachedTempBlob_Size;
+
+  PRUint8* GetTempBlob(const PRUint32 aSize)
+  {
+      if (!mCachedTempBlob || aSize != mCachedTempBlob_Size) {
+          mCachedTempBlob = new PRUint8[aSize];
+          mCachedTempBlob_Size = aSize;
+      }
+
+      return mCachedTempBlob;
+  }
+
+  void DiscardTempBlob()
+  {
+      mCachedTempBlob = nsnull;
+  }
 };
 
 // NB: eventually we'll have separate shadow canvas2d and shadow
 // canvas3d layers, but currently they look the same from the
 // perspective of the compositor process
 class ShadowCanvasLayerD3D9 : public ShadowCanvasLayer,
                              public LayerD3D9
 {
--- a/gfx/layers/opengl/CanvasLayerOGL.cpp
+++ b/gfx/layers/opengl/CanvasLayerOGL.cpp
@@ -173,52 +173,56 @@ CanvasLayerOGL::UpdateSurface()
   if (mPixmap) {
     return;
   }
 #endif
 
   if (mCanvasGLContext &&
       mCanvasGLContext->GetContextType() == gl()->GetContextType())
   {
+    DiscardTempSurface();
+
     // Can texture share, just make sure it's resolved first
     mCanvasGLContext->MakeCurrent();
     mCanvasGLContext->GuaranteeResolve();
 
     if (gl()->BindOffscreenNeedsTexture(mCanvasGLContext) &&
         mTexture == 0)
     {
       mOGLManager->MakeCurrent();
       MakeTexture();
     }
   } else {
     nsRefPtr<gfxASurface> updatedAreaSurface;
+
     if (mDrawTarget) {
       // TODO: This is suboptimal - We should have direct handling for the surface types instead of
       // going via a gfxASurface.
       updatedAreaSurface = gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(mDrawTarget);
     } else if (mCanvasSurface) {
       updatedAreaSurface = mCanvasSurface;
     } else if (mCanvasGLContext) {
+      gfxIntSize size(mBounds.width, mBounds.height);
       nsRefPtr<gfxImageSurface> updatedAreaImageSurface =
-        new gfxImageSurface(gfxIntSize(mBounds.width, mBounds.height),
-                            gfxASurface::ImageFormatARGB32);
+        GetTempSurface(size, gfxASurface::ImageFormatARGB32);
+
       mCanvasGLContext->ReadPixelsIntoImageSurface(0, 0,
                                                    mBounds.width,
                                                    mBounds.height,
                                                    updatedAreaImageSurface);
+
       updatedAreaSurface = updatedAreaImageSurface;
     }
 
     mOGLManager->MakeCurrent();
-    mLayerProgram =
-      gl()->UploadSurfaceToTexture(updatedAreaSurface,
-                                   mBounds,
-                                   mTexture,
-                                   false,
-                                   nsIntPoint(0, 0));
+    mLayerProgram = gl()->UploadSurfaceToTexture(updatedAreaSurface,
+                                                 mBounds,
+                                                 mTexture,
+                                                 false,
+                                                 nsIntPoint(0, 0));
   }
 }
 
 void
 CanvasLayerOGL::RenderLayer(int aPreviousDestination,
                             const nsIntPoint& aOffset)
 {
   UpdateSurface();
--- a/gfx/layers/opengl/CanvasLayerOGL.h
+++ b/gfx/layers/opengl/CanvasLayerOGL.h
@@ -89,16 +89,40 @@ protected:
   GLuint mTexture;
 
   bool mDelayedUpdates;
   bool mGLBufferIsPremultiplied;
   bool mNeedsYFlip;
 #if defined(MOZ_WIDGET_GTK2) && !defined(MOZ_PLATFORM_MAEMO)
   GLXPixmap mPixmap;
 #endif
+
+  nsRefPtr<gfxImageSurface> mCachedTempSurface;
+  gfxIntSize mCachedSize;
+  gfxImageFormat mCachedFormat;
+
+  gfxImageSurface* GetTempSurface(const gfxIntSize& aSize, const gfxImageFormat aFormat)
+  {
+    if (!mCachedTempSurface ||
+        aSize.width != mCachedSize.width ||
+        aSize.height != mCachedSize.height ||
+        aFormat != mCachedFormat)
+    {
+      mCachedTempSurface = new gfxImageSurface(aSize, aFormat);
+      mCachedSize = aSize;
+      mCachedFormat = aFormat;
+    }
+
+    return mCachedTempSurface;
+  }
+
+  void DiscardTempSurface()
+  {
+    mCachedTempSurface = nsnull;
+  }
 };
 
 // NB: eventually we'll have separate shadow canvas2d and shadow
 // canvas3d layers, but currently they look the same from the
 // perspective of the compositor process
 class ShadowCanvasLayerOGL : public ShadowCanvasLayer,
                              public LayerOGL
 {