Bug 559613 - Reuse the rendering context for tab previews and drop the cache time to 5 seconds. r=robarnold,jimm sr=vlad
authorJim Mathies <jmathies@mozilla.com>, Rob Arnold <robarnold@cs.cmu.edu>
Thu, 19 Aug 2010 08:22:46 -0700
changeset 50903 3be451ad56d79019c9d494b4f0bb6640e36b062d
parent 50902 05028b7e5d6e57455a5d2693cc90532c1d7336d6
child 50904 8f80a2d92cae87904c325921c1b21c4645a3b804
child 51335 718efba8384b19f88371e4132d91dba7412a2845
push id15193
push usersdwilsh@shawnwilsher.com
push dateThu, 19 Aug 2010 15:43:07 +0000
treeherdermozilla-central@3be451ad56d7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrobarnold, jimm, vlad
bugs559613
milestone2.0b5pre
first release with
nightly linux32
3be451ad56d7 / 4.0b5pre / 20100819091741 / files
nightly linux64
3be451ad56d7 / 4.0b5pre / 20100819092221 / files
nightly mac
3be451ad56d7 / 4.0b5pre / 20100819092214 / files
nightly win32
3be451ad56d7 / 4.0b5pre / 20100819102200 / files
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
Bug 559613 - Reuse the rendering context for tab previews and drop the cache time to 5 seconds. r=robarnold,jimm sr=vlad a=blocking2.0
browser/app/profile/firefox.js
content/canvas/public/nsICanvasRenderingContextInternal.h
content/canvas/src/WebGLContext.h
content/canvas/src/nsCanvasRenderingContext2D.cpp
widget/src/windows/TaskbarPreview.cpp
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -920,17 +920,17 @@ pref("dom.ipc.plugins.enabled", true);
 #else
 pref("dom.ipc.plugins.enabled", false);
 #endif
 
 #ifdef XP_WIN
 #ifndef WINCE
 pref("browser.taskbar.previews.enable", true);
 pref("browser.taskbar.previews.max", 20);
-pref("browser.taskbar.previews.cachetime", 20);
+pref("browser.taskbar.previews.cachetime", 5);
 pref("browser.taskbar.lists.enabled", true);
 pref("browser.taskbar.lists.frequent.enabled", true);
 pref("browser.taskbar.lists.recent.enabled", false);
 pref("browser.taskbar.lists.maxListItemCount", 7);
 pref("browser.taskbar.lists.tasks.enabled", true);
 pref("browser.taskbar.lists.refreshInSeconds", 30);
 #endif
 #endif
--- a/content/canvas/public/nsICanvasRenderingContextInternal.h
+++ b/content/canvas/public/nsICanvasRenderingContextInternal.h
@@ -38,20 +38,19 @@
 #ifndef nsICanvasRenderingContextInternal_h___
 #define nsICanvasRenderingContextInternal_h___
 
 #include "nsISupports.h"
 #include "nsIInputStream.h"
 #include "nsIDocShell.h"
 #include "gfxPattern.h"
 
-// {b96168fd-6f13-4ca7-b820-e96f22e71fe5}
+// {EC90F32E-7848-4819-A1E3-02E64C682A72}
 #define NS_ICANVASRENDERINGCONTEXTINTERNAL_IID \
-{ 0xb96168fd, 0x6f13, 0x4ca7, \
-  { 0xb8, 0x20, 0xe9, 0x6f, 0x22, 0xe7, 0x1f, 0xe5 } }
+{ 0xec90f32e, 0x7848, 0x4819, { 0xa1, 0xe3, 0x2, 0xe6, 0x4c, 0x68, 0x2a, 0x72 } }
 
 class nsHTMLCanvasElement;
 class gfxContext;
 class gfxASurface;
 
 namespace mozilla {
 namespace layers {
 class CanvasLayer;
@@ -97,16 +96,20 @@ public:
   NS_IMETHOD GetThebesSurface(gfxASurface **surface) = 0;
 
   // If this context is opaque, the backing store of the canvas should
   // be created as opaque; all compositing operators should assume the
   // dst alpha is always 1.0.  If this is never called, the context
   // defaults to false (not opaque).
   NS_IMETHOD SetIsOpaque(PRBool isOpaque) = 0;
 
+  // Invalidate this context and release any held resources, in preperation
+  // for possibly reinitializing with SetDimensions/InitializeWithSurface.
+  NS_IMETHOD Reset() = 0;
+
   // Return the CanvasLayer for this context, creating
   // one for the given layer manager if not available.
   virtual already_AddRefed<CanvasLayer> GetCanvasLayer(CanvasLayer *aOldLayer,
                                                        LayerManager *aManager) = 0;
 
   virtual void MarkContextClean() = 0;
 
   // Redraw the dirty rectangle of this canvas.
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -274,16 +274,18 @@ public:
 
     NS_DECL_NSICANVASRENDERINGCONTEXTWEBGL
 
     // nsICanvasRenderingContextInternal
     NS_IMETHOD SetCanvasElement(nsHTMLCanvasElement* aParentCanvas);
     NS_IMETHOD SetDimensions(PRInt32 width, PRInt32 height);
     NS_IMETHOD InitializeWithSurface(nsIDocShell *docShell, gfxASurface *surface, PRInt32 width, PRInt32 height)
         { return NS_ERROR_NOT_IMPLEMENTED; }
+    NS_IMETHOD Reset()
+        { /* (InitializeWithSurface) */ return NS_ERROR_NOT_IMPLEMENTED; }
     NS_IMETHOD Render(gfxContext *ctx, gfxPattern::GraphicsFilter f);
     NS_IMETHOD GetInputStream(const char* aMimeType,
                               const PRUnichar* aEncoderOptions,
                               nsIInputStream **aStream);
     NS_IMETHOD GetThebesSurface(gfxASurface **surface);
     NS_IMETHOD SetIsOpaque(PRBool b) { return NS_OK; };
     NS_IMETHOD SetIsIPC(PRBool b) { return NS_ERROR_NOT_IMPLEMENTED; }
     NS_IMETHOD Redraw(const gfxRect&) { return NS_ERROR_NOT_IMPLEMENTED; }
--- a/content/canvas/src/nsCanvasRenderingContext2D.cpp
+++ b/content/canvas/src/nsCanvasRenderingContext2D.cpp
@@ -415,16 +415,17 @@ public:
     NS_IMETHOD SetDimensions(PRInt32 width, PRInt32 height);
     NS_IMETHOD InitializeWithSurface(nsIDocShell *shell, gfxASurface *surface, PRInt32 width, PRInt32 height);
     NS_IMETHOD Render(gfxContext *ctx, gfxPattern::GraphicsFilter aFilter);
     NS_IMETHOD GetInputStream(const char* aMimeType,
                               const PRUnichar* aEncoderOptions,
                               nsIInputStream **aStream);
     NS_IMETHOD GetThebesSurface(gfxASurface **surface);
     NS_IMETHOD SetIsOpaque(PRBool isOpaque);
+    NS_IMETHOD Reset();
     already_AddRefed<CanvasLayer> GetCanvasLayer(CanvasLayer *aOldLayer,
                                                  LayerManager *aManager);
     void MarkContextClean();
     NS_IMETHOD SetIsIPC(PRBool isIPC);
     // this rect is in CSS pixels
     NS_IMETHOD Redraw(const gfxRect &r);
     // Swap this back buffer with the front, and copy its contents to the new back.
     // x, y, w, and h specify the area of |back| that is dirty.
@@ -462,19 +463,16 @@ protected:
      */
     static PRUint8 (*sUnpremultiplyTable)[256];
 
     /**
      * Lookup table used to speed up PutImageData().
      */
     static PRUint8 (*sPremultiplyTable)[256];
 
-    // destroy thebes/image stuff, in preparation for possibly recreating
-    void Destroy();
-
     // Some helpers.  Doesn't modify acolor on failure.
     nsresult SetStyleFromStringOrInterface(const nsAString& aStr, nsISupports *aInterface, Style aWhichStyle);
     nsresult GetStyleAsStringOrInterface(nsAString& aStr, nsISupports **aInterface, PRInt32 *aType, Style aWhichStyle);
 
     void StyleColorToString(const nscolor& aColor, nsAString& aStr);
 
     void DirtyAllStyles();
     /**
@@ -837,17 +835,17 @@ nsCanvasRenderingContext2D::nsCanvasRend
     , mSaveCount(0), mIsEntireFrameInvalid(PR_FALSE), mInvalidateCount(0)
     , mLastStyle(STYLE_MAX), mStyleStack(20)
 {
     sNumLivingContexts++;
 }
 
 nsCanvasRenderingContext2D::~nsCanvasRenderingContext2D()
 {
-    Destroy();
+    Reset();
 
 #ifdef MOZ_IPC
     ContentParent* allocator = ContentParent::GetSingleton(PR_FALSE);
     if (allocator && gfxSharedImageSurface::IsSharedImage(mBackSurface)) {
         Shmem mem = static_cast<gfxSharedImageSurface*>(mBackSurface.get())->GetShmem();
         allocator->DeallocShmem(mem);
     }
     mBackSurface = nsnull;
@@ -857,18 +855,18 @@ nsCanvasRenderingContext2D::~nsCanvasRen
     if (!sNumLivingContexts) {
         delete[] sUnpremultiplyTable;
         delete[] sPremultiplyTable;
         sUnpremultiplyTable = nsnull;
         sPremultiplyTable = nsnull;
     }
 }
 
-void
-nsCanvasRenderingContext2D::Destroy()
+nsresult
+nsCanvasRenderingContext2D::Reset()
 {
 #ifdef MOZ_IPC
     ContentParent* allocator = ContentParent::GetSingleton(PR_FALSE);
     if (allocator && gfxSharedImageSurface::IsSharedImage(mSurface)) {
         Shmem &mem = static_cast<gfxSharedImageSurface*>(mSurface.get())->GetShmem();
         allocator->DeallocShmem(mem);
     }
 #endif
@@ -877,16 +875,17 @@ nsCanvasRenderingContext2D::Destroy()
     // since those are the ones that we created a surface for
     if (mValid && !mDocShell)
         gCanvasMemoryUsed -= mWidth * mHeight * 4;
 
     mSurface = nsnull;
     mThebes = nsnull;
     mValid = PR_FALSE;
     mIsEntireFrameInvalid = PR_FALSE;
+    return NS_OK;
 }
 
 nsresult
 nsCanvasRenderingContext2D::SetStyleFromStringOrInterface(const nsAString& aStr,
                                                           nsISupports *aInterface,
                                                           Style aWhichStyle)
 {
     nsresult rv;
@@ -1078,17 +1077,17 @@ nsCanvasRenderingContext2D::Redraw(const
     HTMLCanvasElement()->InvalidateFrame(&r);
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::SetDimensions(PRInt32 width, PRInt32 height)
 {
-    Destroy();
+    Reset();
 
     nsRefPtr<gfxASurface> surface;
 
     // Check that the dimensions are sane
     gfxIntSize size(width, height);
     if (gfxASurface::CheckSurfaceSize(size, 0xffff)) {
 
         gfxASurface::gfxImageFormat format = GetImageFormat();
@@ -1139,17 +1138,17 @@ nsCanvasRenderingContext2D::SetDimension
         gCanvasMemoryUsed += width * height * 4;
     }
 
     return InitializeWithSurface(NULL, surface, width, height);
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::InitializeWithSurface(nsIDocShell *docShell, gfxASurface *surface, PRInt32 width, PRInt32 height) {
-    Destroy();
+    Reset();
 
     NS_ASSERTION(!docShell ^ !mCanvasElement, "Cannot set both docshell and canvas element");
     mDocShell = docShell;
 
     mWidth = width;
     mHeight = height;
 
     mSurface = surface;
--- a/widget/src/windows/TaskbarPreview.cpp
+++ b/widget/src/windows/TaskbarPreview.cpp
@@ -18,16 +18,17 @@
  *
  * The Initial Developer of the Original Code is
  * Mozilla Foundation.
  * Portions created by the Initial Developer are Copyright (C) 2009
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Rob Arnold <tellrob@gmail.com>
+ *   Jim Mathies <jmathies@mozilla.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -64,65 +65,108 @@
 
 // Defined in dwmapi in a header that needs a higher numbered _WINNT #define
 #define DWM_SIT_DISPLAYFRAME 0x1
 
 namespace mozilla {
 namespace widget {
 
 namespace {
-/* Helper method to create a canvas rendering context backed by the given surface
+
+// Shared by all TaskbarPreviews to avoid the expensive creation process.
+// Manually refcounted (see gInstCount) by the ctor and dtor of TaskbarPreview.
+// This is done because static constructors aren't allowed for perf reasons.
+nsIDOMCanvasRenderingContext2D* gCtx = NULL;
+// Used in tracking the number of previews. Used in freeing
+// the static 2d rendering context on shutdown.
+PRUint32 gInstCount = 0;
+
+/* Helper method to lazily create a canvas rendering context and associate a given
+ * surface with it.
  *
  * @param shell The docShell used by the canvas context for text settings and other
  *              misc things.
  * @param surface The gfxSurface backing the context
  * @param width The width of the given surface
  * @param height The height of the given surface
- * @param aCtx Out-param - a canvas context backed by the given surface
  */
 nsresult
-CreateRenderingContext(nsIDocShell *shell, gfxASurface *surface, PRUint32 width, PRUint32 height, nsICanvasRenderingContextInternal **aCtx) {
+GetRenderingContext(nsIDocShell *shell, gfxASurface *surface,
+                    PRUint32 width, PRUint32 height) {
   nsresult rv;
-  nsCOMPtr<nsICanvasRenderingContextInternal> ctx(do_CreateInstance(
-    "@mozilla.org/content/canvas-rendering-context;1?id=2d", &rv));
-  NS_ENSURE_SUCCESS(rv, rv);
-  rv = ctx->InitializeWithSurface(shell, surface, width, height);
-  NS_ENSURE_SUCCESS(rv, rv);
+  nsCOMPtr<nsIDOMCanvasRenderingContext2D> ctx = gCtx;
+
+  if (!ctx) {
+    // create the canvas rendering context
+    ctx = do_CreateInstance("@mozilla.org/content/canvas-rendering-context;1?id=2d", &rv);
+    if (NS_FAILED(rv)) {
+      NS_WARNING("Could not create nsICanvasRenderingContextInternal for tab previews!");
+      return rv;
+    }
+    NS_ADDREF(ctx);
+    gCtx = ctx;
+  }
+
+  nsCOMPtr<nsICanvasRenderingContextInternal> ctxI = do_QueryInterface(ctx, &rv);
+  if (NS_FAILED(rv))
+    return rv;
 
-  NS_ADDREF(*aCtx = ctx);
-  return NS_OK;
+  // Set the surface we'll use to render.
+  return ctxI->InitializeWithSurface(shell, surface, width, height);
+}
+
+/* Helper method for freeing surface resources associated with the rendering context.
+ */
+void
+ResetRenderingContext() {
+  if (!gCtx)
+    return;
+
+  nsresult rv;
+  nsCOMPtr<nsICanvasRenderingContextInternal> ctxI = do_QueryInterface(gCtx, &rv);
+  if (NS_FAILED(rv))
+    return;
+  if (NS_FAILED(ctxI->Reset())) {
+    NS_RELEASE(gCtx);
+    gCtx = nsnull;
+  }
 }
 
 }
 
 TaskbarPreview::TaskbarPreview(ITaskbarList4 *aTaskbar, nsITaskbarPreviewController *aController, HWND aHWND, nsIDocShell *aShell)
   : mTaskbar(aTaskbar),
     mController(aController),
     mWnd(aHWND),
     mVisible(PR_FALSE),
     mDocShell(do_GetWeakReference(aShell))
 {
   // TaskbarPreview may outlive the WinTaskbar that created it
   ::CoInitialize(NULL);
 
+  gInstCount++;
+
   WindowHook &hook = GetWindowHook();
   hook.AddMonitor(WM_DESTROY, MainWindowHook, this);
 }
 
 TaskbarPreview::~TaskbarPreview() {
   // Avoid dangling pointer
   if (sActivePreview == this)
     sActivePreview = nsnull;
 
   // Our subclass should have invoked DetachFromNSWindow already.
   NS_ASSERTION(!mWnd, "TaskbarPreview::DetachFromNSWindow was not called before destruction");
 
   // Make sure to release before potentially uninitializing COM
   mTaskbar = NULL;
 
+  if (--gInstCount == 0)
+    NS_IF_RELEASE(gCtx);
+
   ::CoUninitialize();
 }
 
 NS_IMETHODIMP
 TaskbarPreview::SetController(nsITaskbarPreviewController *aController) {
   NS_ENSURE_ARG(aController);
 
   mController = aController;
@@ -346,42 +390,40 @@ TaskbarPreview::DrawBitmap(PRUint32 widt
   nsresult rv;
   nsRefPtr<gfxWindowsSurface> surface = new gfxWindowsSurface(gfxIntSize(width, height), gfxASurface::ImageFormatARGB32);
 
   nsCOMPtr<nsIDocShell> shell = do_QueryReferent(mDocShell);
 
   if (!shell)
     return;
 
-  nsCOMPtr<nsICanvasRenderingContextInternal> ctxI;
-  rv = CreateRenderingContext(shell, surface, width, height, getter_AddRefs(ctxI));
-
-  nsCOMPtr<nsIDOMCanvasRenderingContext2D> ctx = do_QueryInterface(ctxI);
+  rv = GetRenderingContext(shell, surface, width, height);
+  if (NS_FAILED(rv))
+    return;
 
   PRBool drawFrame = PR_FALSE;
-  if (NS_SUCCEEDED(rv) && ctx) {
-    if (isPreview)
-      rv = mController->DrawPreview(ctx, &drawFrame);
-    else
-      rv = mController->DrawThumbnail(ctx, width, height, &drawFrame);
-
-  }
+  if (isPreview)
+    rv = mController->DrawPreview(gCtx, &drawFrame);
+  else
+    rv = mController->DrawThumbnail(gCtx, width, height, &drawFrame);
 
   if (NS_FAILED(rv))
     return;
 
   HDC hDC = surface->GetDC();
   HBITMAP hBitmap = (HBITMAP)GetCurrentObject(hDC, OBJ_BITMAP);
 
   DWORD flags = drawFrame ? DWM_SIT_DISPLAYFRAME : 0;
   POINT pptClient = { 0, 0 };
   if (isPreview)
     nsUXThemeData::dwmSetIconicLivePreviewBitmapPtr(PreviewWindow(), hBitmap, &pptClient, flags);
   else
     nsUXThemeData::dwmSetIconicThumbnailPtr(PreviewWindow(), hBitmap, flags);
+
+  ResetRenderingContext();
 }
 
 /* static */
 PRBool
 TaskbarPreview::MainWindowHook(void *aContext,
                                HWND hWnd, UINT nMsg,
                                WPARAM wParam, LPARAM lParam,
                                LRESULT *aResult)