b=574481; fix layers lifetime isues; part 3 - add lifetime mgmt to GL layers; r=bas
authorVladimir Vukicevic <vladimir@pobox.com>
Fri, 06 Aug 2010 22:09:18 -0700
changeset 49072 0ebbc02a5c8726cb61224758767200a9d8c21db4
parent 49071 de9137456817269f99751e8bccea7e039ddcb655
child 49073 6636c00d1945d8a0b3f3e3aad3c22ee07cf10502
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbas
bugs574481
milestone2.0b4pre
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
b=574481; fix layers lifetime isues; part 3 - add lifetime mgmt to GL layers; r=bas
gfx/layers/opengl/CanvasLayerOGL.cpp
gfx/layers/opengl/CanvasLayerOGL.h
gfx/layers/opengl/ColorLayerOGL.h
gfx/layers/opengl/ContainerLayerOGL.cpp
gfx/layers/opengl/ContainerLayerOGL.h
gfx/layers/opengl/ImageLayerOGL.cpp
gfx/layers/opengl/ImageLayerOGL.h
gfx/layers/opengl/LayerManagerOGL.cpp
gfx/layers/opengl/LayerManagerOGL.h
gfx/layers/opengl/LayerManagerOGLProgram.h
gfx/layers/opengl/ThebesLayerOGL.cpp
gfx/layers/opengl/ThebesLayerOGL.h
--- a/gfx/layers/opengl/CanvasLayerOGL.cpp
+++ b/gfx/layers/opengl/CanvasLayerOGL.cpp
@@ -49,22 +49,27 @@
 #ifdef XP_MACOSX
 #include <OpenGL/OpenGL.h>
 #endif
 
 using namespace mozilla;
 using namespace mozilla::layers;
 using namespace mozilla::gl;
 
-CanvasLayerOGL::~CanvasLayerOGL()
+void
+CanvasLayerOGL::Destroy()
 {
-  mOGLManager->MakeCurrent();
+  if (!mDestroyed) {
+    if (mTexture) {
+      GLContext *cx = mOGLManager->glForResources();
+      cx->MakeCurrent();
+      cx->fDeleteTextures(1, &mTexture);
+    }
 
-  if (mTexture) {
-    gl()->fDeleteTextures(1, &mTexture);
+    mDestroyed = PR_TRUE;
   }
 }
 
 void
 CanvasLayerOGL::Initialize(const Data& aData)
 {
   NS_ASSERTION(mCanvasSurface == nsnull, "BasicCanvasLayer::Initialize called twice!");
 
@@ -111,16 +116,20 @@ CanvasLayerOGL::MakeTexture()
   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)
 {
+  if (mDestroyed) {
+    return;
+  }
+
   NS_ASSERTION(mUpdatedRect.IsEmpty(),
                "CanvasLayer::Updated called more than once during a transaction!");
 
   mOGLManager->MakeCurrent();
 
   mUpdatedRect.UnionRect(mUpdatedRect, aRect);
 
   if (mCanvasGLContext) {
--- a/gfx/layers/opengl/CanvasLayerOGL.h
+++ b/gfx/layers/opengl/CanvasLayerOGL.h
@@ -51,24 +51,24 @@ class THEBES_API CanvasLayerOGL :
 public:
   CanvasLayerOGL(LayerManagerOGL *aManager)
     : CanvasLayer(aManager, NULL),
       LayerOGL(aManager),
       mTexture(0)
   { 
       mImplData = static_cast<LayerOGL*>(this);
   }
-
-  ~CanvasLayerOGL();
+  ~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:
   nsRefPtr<gfxASurface> mCanvasSurface;
   nsRefPtr<GLContext> mCanvasGLContext;
 
--- a/gfx/layers/opengl/ColorLayerOGL.h
+++ b/gfx/layers/opengl/ColorLayerOGL.h
@@ -48,19 +48,22 @@ class THEBES_API ColorLayerOGL : public 
 {
 public:
   ColorLayerOGL(LayerManagerOGL *aManager)
     : ColorLayer(aManager, NULL)
     , LayerOGL(aManager)
   { 
     mImplData = static_cast<LayerOGL*>(this);
   }
+  ~ColorLayerOGL() { Destroy(); }
 
   // LayerOGL Implementation
   virtual Layer* GetLayer();
 
+  virtual void Destroy() { mDestroyed = PR_TRUE; }
+
   virtual void RenderLayer(int aPreviousFrameBuffer,
                            const nsIntPoint& aOffset);
 };
 
 } /* layers */
 } /* mozilla */
 #endif /* GFX_COLORLAYEROGL_H */
--- a/gfx/layers/opengl/ContainerLayerOGL.cpp
+++ b/gfx/layers/opengl/ContainerLayerOGL.cpp
@@ -44,18 +44,28 @@ ContainerLayerOGL::ContainerLayerOGL(Lay
   : ContainerLayer(aManager, NULL)
   , LayerOGL(aManager)
 {
   mImplData = static_cast<LayerOGL*>(this);
 }
 
 ContainerLayerOGL::~ContainerLayerOGL()
 {
-  while (mFirstChild) {
-    RemoveChild(mFirstChild);
+  Destroy();
+}
+
+void
+ContainerLayerOGL::Destroy()
+{
+  if (!mDestroyed) {
+    while (mFirstChild) {
+      GetFirstChildOGL()->Destroy();
+      RemoveChild(mFirstChild);
+    }
+    mDestroyed = PR_TRUE;
   }
 }
 
 void
 ContainerLayerOGL::InsertAfter(Layer* aChild, Layer* aAfter)
 {
   aChild->SetParent(this);
   if (!aAfter) {
--- a/gfx/layers/opengl/ContainerLayerOGL.h
+++ b/gfx/layers/opengl/ContainerLayerOGL.h
@@ -1,9 +1,9 @@
-/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * ***** BEGIN LICENSE BLOCK *****
  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  *
  * The contents of this file are subject to the Mozilla Public License Version
  * 1.1 (the "License"); you may not use this file except in compliance with
  * the License. You may obtain a copy of the License at
  * http://www.mozilla.org/MPL/
  *
@@ -55,16 +55,18 @@ public:
 
   void InsertAfter(Layer* aChild, Layer* aAfter);
 
   void RemoveChild(Layer* aChild);
 
   /** LayerOGL implementation */
   Layer* GetLayer();
 
+  void Destroy();
+
   LayerOGL* GetFirstChildOGL();
 
   PRBool IsEmpty();
 
   virtual void RenderLayer(int aPreviousFrameBuffer,
                            const nsIntPoint& aOffset);
 };
 
--- a/gfx/layers/opengl/ImageLayerOGL.cpp
+++ b/gfx/layers/opengl/ImageLayerOGL.cpp
@@ -52,39 +52,44 @@ using mozilla::MutexAutoLock;
  * optionally delete a texture associated with that context.
  */
 class TextureDeleter : public nsRunnable {
 public:
   TextureDeleter(already_AddRefed<GLContext> aContext,
                  GLuint aTexture)
       : mContext(aContext), mTexture(aTexture)
   {
+    NS_ASSERTION(aTexture, "TextureDeleter instantiated with nothing to do");
   }
+
   NS_IMETHOD Run() {
-    if (mTexture) {
-      mContext->DestroyTexture(mTexture);
-    }
+    mContext->MakeCurrent();
+    mContext->fDeleteTextures(1, &mTexture);
+
     // Ensure context is released on the main thread
     mContext = nsnull;
     return NS_OK;
   }
 
   nsRefPtr<GLContext> mContext;
   GLuint mTexture;
 };
 
 void
 GLTexture::Allocate(GLContext *aContext)
 {
-  NS_ASSERTION(NS_IsMainThread(), "Can only allocate texture on main thread");
+  NS_ASSERTION(aContext->IsGlobalSharedContext() ||
+               NS_IsMainThread(), "Can only allocate texture on main thread or with cx sharing");
 
   Release();
 
   mContext = aContext;
-  mTexture = mContext->CreateTexture();
+
+  mContext->MakeCurrent();
+  mContext->fGenTextures(1, &mTexture);
 }
 
 void
 GLTexture::TakeFrom(GLTexture *aOther)
 {
   Release();
 
   mContext = aOther->mContext.forget();
@@ -95,29 +100,30 @@ GLTexture::TakeFrom(GLTexture *aOther)
 void
 GLTexture::Release()
 {
   if (!mContext) {
     NS_ASSERTION(!mTexture, "Can't delete texture without a context");
     return;
   }
 
-  if (NS_IsMainThread()) {
-    if (mTexture) {
-      mContext->DestroyTexture(mTexture);
-      mTexture = 0;
+  if (mTexture) {
+    if (NS_IsMainThread() || mContext->IsGlobalSharedContext()) {
+      mContext->MakeCurrent();
+      mContext->fDeleteTextures(1, &mTexture);
+    } else {
+      nsCOMPtr<nsIRunnable> runnable =
+        new TextureDeleter(mContext.forget(), mTexture);
+      NS_DispatchToMainThread(runnable);
     }
-    mContext = nsnull;
-    return;
+
+    mTexture = 0;
   }
 
-  nsCOMPtr<nsIRunnable> runnable =
-    new TextureDeleter(mContext.forget(), mTexture);
-  NS_DispatchToMainThread(runnable);
-  mTexture = 0;
+  mContext = nsnull;
 }
 
 RecycleBin::RecycleBin()
   : mLock("mozilla.layers.RecycleBin.mLock")
 {
 }
 
 void
@@ -128,17 +134,17 @@ RecycleBin::RecycleBuffer(PRUint8* aBuff
   if (!mRecycledBuffers.IsEmpty() && aSize != mRecycledBufferSize) {
     mRecycledBuffers.Clear();
   }
   mRecycledBufferSize = aSize;
   mRecycledBuffers.AppendElement(aBuffer);
 }
 
 PRUint8*
-RecycleBin::TakeBuffer(PRUint32 aSize)
+RecycleBin::GetBuffer(PRUint32 aSize)
 {
   MutexAutoLock lock(mLock);
 
   if (mRecycledBuffers.IsEmpty() || mRecycledBufferSize != aSize)
     return new PRUint8[aSize];
 
   PRUint32 last = mRecycledBuffers.Length() - 1;
   PRUint8* result = mRecycledBuffers[last].forget();
@@ -158,18 +164,18 @@ RecycleBin::RecycleTexture(GLTexture *aT
   if (!mRecycledTextures[aType].IsEmpty() && aSize != mRecycledTextureSizes[aType]) {
     mRecycledTextures[aType].Clear();
   }
   mRecycledTextureSizes[aType] = aSize;
   mRecycledTextures[aType].AppendElement()->TakeFrom(aTexture);
 }
 
 void
-RecycleBin::TakeTexture(TextureType aType, const gfxIntSize& aSize,
-                        GLContext *aContext, GLTexture *aOutTexture)
+RecycleBin::GetTexture(TextureType aType, const gfxIntSize& aSize,
+                       GLContext *aContext, GLTexture *aOutTexture)
 {
   MutexAutoLock lock(mLock);
 
   if (mRecycledTextures[aType].IsEmpty() || mRecycledTextureSizes[aType] != aSize) {
     aOutTexture->Allocate(aContext);
     return;
   }
   PRUint32 last = mRecycledTextures[aType].Length() - 1;
@@ -179,26 +185,36 @@ RecycleBin::TakeTexture(TextureType aTyp
 
 ImageContainerOGL::ImageContainerOGL(LayerManagerOGL *aManager)
   : ImageContainer(aManager)
   , mRecycleBin(new RecycleBin())
   , mActiveImageLock("mozilla.layers.ImageContainerOGL.mActiveImageLock")
 {
 }
 
+ImageContainerOGL::~ImageContainerOGL()
+{
+  if (mManager) {
+    NS_ASSERTION(mManager->GetBackendType() == LayerManager::LAYERS_OPENGL, "Wrong layer manager got assigned to ImageContainerOGL!");
+
+    static_cast<LayerManagerOGL*>(mManager)->ForgetImageContainer(this);
+  }
+}
+
 already_AddRefed<Image>
 ImageContainerOGL::CreateImage(const Image::Format *aFormats,
                                PRUint32 aNumFormats)
 {
   if (!aNumFormats) {
     return nsnull;
   }
   nsRefPtr<Image> img;
   if (aFormats[0] == Image::PLANAR_YCBCR) {
-    img = new PlanarYCbCrImageOGL(mRecycleBin);
+    img = new PlanarYCbCrImageOGL(static_cast<LayerManagerOGL*>(mManager),
+                                  mRecycleBin);
   } else if (aFormats[0] == Image::CAIRO_SURFACE) {
     img = new CairoImageOGL(static_cast<LayerManagerOGL*>(mManager));
   }
   return img.forget();
 }
 
 void
 ImageContainerOGL::SetCurrentImage(Image *aImage)
@@ -223,43 +239,118 @@ ImageContainerOGL::GetCurrentImage()
 
   nsRefPtr<Image> retval = mActiveImage;
   return retval.forget();
 }
 
 already_AddRefed<gfxASurface>
 ImageContainerOGL::GetCurrentAsSurface(gfxIntSize *aSize)
 {
-  return nsnull;
+  MutexAutoLock lock(mActiveImageLock);
+
+  if (!mActiveImage) {
+    *aSize = gfxIntSize(0,0);
+    return nsnull;
+  }
+
+  GLContext *gl = nsnull;
+  // tex1 will be RGBA or Y, tex2 will Cb, tex3 will be Cr
+  GLuint tex1 = 0, tex2 = 0, tex3 = 0;
+  gfxIntSize size;
+
+  if (mActiveImage->GetFormat() == Image::PLANAR_YCBCR) {
+    PlanarYCbCrImageOGL *yuvImage =
+      static_cast<PlanarYCbCrImageOGL*>(mActiveImage.get());
+    if (!yuvImage->HasData() || !yuvImage->HasTextures()) {
+      *aSize = gfxIntSize(0, 0);
+      return nsnull;
+    }
+
+    size = yuvImage->mSize;
+    gl = yuvImage->mTextures[0].GetGLContext();
+    tex1 = yuvImage->mTextures[0].GetTextureID();
+    tex2 = yuvImage->mTextures[1].GetTextureID();
+    tex3 = yuvImage->mTextures[2].GetTextureID();
+  }
+
+  if (mActiveImage->GetFormat() == Image::CAIRO_SURFACE) {
+    CairoImageOGL *cairoImage =
+      static_cast<CairoImageOGL*>(mActiveImage.get());
+    size = cairoImage->mSize;
+    gl = cairoImage->mTexture.GetGLContext();
+    tex1 = cairoImage->mTexture.GetTextureID();
+  }
+
+  // XXX TODO: read all textures in YCbCr case and convert to RGB
+  // XXX Or maybe add a ReadYCbCrTextureImage that will take 3 textures
+  // and return RGB, since we can render YCbCr to the temporary framebuffer.
+  nsRefPtr<gfxImageSurface> s = gl->ReadTextureImage(tex1, size, LOCAL_GL_RGBA);
+  *aSize = size;
+  return s.forget();
 }
 
 gfxIntSize
 ImageContainerOGL::GetCurrentSize()
 {
   MutexAutoLock lock(mActiveImageLock);
   if (!mActiveImage) {
     return gfxIntSize(0,0);
   }
+
   if (mActiveImage->GetFormat() == Image::PLANAR_YCBCR) {
     PlanarYCbCrImageOGL *yuvImage =
       static_cast<PlanarYCbCrImageOGL*>(mActiveImage.get());
     if (!yuvImage->HasData()) {
       return gfxIntSize(0,0);
     }
     return yuvImage->mSize;
 
-  } else if (mActiveImage->GetFormat() == Image::CAIRO_SURFACE) {
+  }
+
+  if (mActiveImage->GetFormat() == Image::CAIRO_SURFACE) {
     CairoImageOGL *cairoImage =
       static_cast<CairoImageOGL*>(mActiveImage.get());
     return cairoImage->mSize;
   }
 
   return gfxIntSize(0,0);
 }
 
+PRBool
+ImageContainerOGL::SetLayerManager(LayerManager *aManager)
+{
+  if (!aManager) {
+    // the layer manager just entirely went away
+
+    // XXX if we don't have context sharing, we should tell our images
+    // that their textures are no longer valid.
+    mManager = nsnull;
+    return PR_TRUE;
+  }
+
+  if (aManager->GetBackendType() != LayerManager::LAYERS_OPENGL) {
+    return PR_FALSE;
+  }
+
+  LayerManagerOGL* lmOld = static_cast<LayerManagerOGL*>(mManager);
+  LayerManagerOGL* lmNew = static_cast<LayerManagerOGL*>(aManager);
+
+  if (lmOld) {
+    NS_ASSERTION(lmNew->glForResources() == lmOld->glForResources(),
+                 "We require GL context sharing here!");
+    lmOld->ForgetImageContainer(this);
+  }
+
+  mManager = aManager;
+
+  lmNew->RememberImageContainer(this);
+
+  return PR_TRUE;
+}
+
 Layer*
 ImageLayerOGL::GetLayer()
 {
   return this;
 }
 
 void
 ImageLayerOGL::RenderLayer(int,
@@ -271,21 +362,25 @@ ImageLayerOGL::RenderLayer(int,
   mOGLManager->MakeCurrent();
 
   nsRefPtr<Image> image = GetContainer()->GetCurrentImage();
 
   if (image->GetFormat() == Image::PLANAR_YCBCR) {
     PlanarYCbCrImageOGL *yuvImage =
       static_cast<PlanarYCbCrImageOGL*>(image.get());
 
-    if (!yuvImage->HasData())
+    if (!yuvImage->HasData()) {
       return;
+    }
+    
+    if (!yuvImage->HasTextures()) {
+      yuvImage->AllocateTextures(gl());
+    }
 
-    if (!yuvImage->HasTextures())
-      yuvImage->AllocateTextures(mOGLManager);
+    yuvImage->UpdateTextures(gl());
 
     gl()->fActiveTexture(LOCAL_GL_TEXTURE0);
     gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, yuvImage->mTextures[0].GetTextureID());
     gl()->fActiveTexture(LOCAL_GL_TEXTURE1);
     gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, yuvImage->mTextures[1].GetTextureID());
     gl()->fActiveTexture(LOCAL_GL_TEXTURE2);
     gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, yuvImage->mTextures[2].GetTextureID());
 
@@ -330,30 +425,62 @@ ImageLayerOGL::RenderLayer(int,
     program->SetTextureUnit(0);
 
     mOGLManager->BindAndDrawQuad(program);
   }
 
   DEBUG_GL_ERROR_CHECK(gl());
 }
 
-PlanarYCbCrImageOGL::PlanarYCbCrImageOGL(RecycleBin *aRecycleBin)
+static void
+InitTexture(GLContext* aGL, GLuint aTexture, GLenum aFormat, const gfxIntSize& aSize)
+{
+  aGL->fBindTexture(LOCAL_GL_TEXTURE_2D, aTexture);
+  aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_LINEAR);
+  aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_LINEAR);
+  aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
+  aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
+
+  aGL->fTexImage2D(LOCAL_GL_TEXTURE_2D,
+                   0,
+                   aFormat,
+                   aSize.width,
+                   aSize.height,
+                   0,
+                   aFormat,
+                   LOCAL_GL_UNSIGNED_BYTE,
+                   NULL);
+}
+
+PlanarYCbCrImageOGL::PlanarYCbCrImageOGL(LayerManagerOGL *aManager,
+                                         RecycleBin *aRecycleBin)
   : PlanarYCbCrImage(nsnull), mRecycleBin(aRecycleBin), mHasData(PR_FALSE)
 {
+#if 0
+  // We really want to allocate this on the decode thread -- but to do that,
+  // we need to create a per-thread shared GL context, and it will only work
+  // if we have context sharing.  For now, create the textures on the main
+  // thread the first time we render.
+  if (aManager) {
+    AllocateTextures(aManager->glForResources());
+  }
+#endif
 }
 
 PlanarYCbCrImageOGL::~PlanarYCbCrImageOGL()
 {
   if (mBuffer) {
     mRecycleBin->RecycleBuffer(mBuffer.forget(), mBufferSize);
   }
 
-  mRecycleBin->RecycleTexture(&mTextures[0], RecycleBin::TEXTURE_Y, mData.mYSize);
-  mRecycleBin->RecycleTexture(&mTextures[1], RecycleBin::TEXTURE_C, mData.mCbCrSize);
-  mRecycleBin->RecycleTexture(&mTextures[2], RecycleBin::TEXTURE_C, mData.mCbCrSize);
+  if (HasTextures()) {
+    mRecycleBin->RecycleTexture(&mTextures[0], RecycleBin::TEXTURE_Y, mData.mYSize);
+    mRecycleBin->RecycleTexture(&mTextures[1], RecycleBin::TEXTURE_C, mData.mCbCrSize);
+    mRecycleBin->RecycleTexture(&mTextures[2], RecycleBin::TEXTURE_C, mData.mCbCrSize);
+  }
 }
 
 void
 PlanarYCbCrImageOGL::SetData(const PlanarYCbCrImage::Data &aData)
 {
   // For now, we copy the data
   int width_shift = 0;
   int height_shift = 0;
@@ -379,17 +506,17 @@ PlanarYCbCrImageOGL::SetData(const Plana
   mData = aData;
   mData.mCbCrStride = mData.mCbCrSize.width = aData.mPicSize.width >> width_shift;
   mData.mCbCrSize.height = aData.mPicSize.height >> height_shift;
   mData.mYSize = aData.mPicSize;
   mData.mYStride = mData.mYSize.width;
 
   mBufferSize = mData.mCbCrStride * mData.mCbCrSize.height * 2 +
                 mData.mYStride * mData.mYSize.height;
-  mBuffer = mRecycleBin->TakeBuffer(mBufferSize);
+  mBuffer = mRecycleBin->GetBuffer(mBufferSize);
   if (!mBuffer)
     return;
 
   mData.mYChannel = mBuffer;
   mData.mCbChannel = mData.mYChannel + mData.mYStride * mData.mYSize.height;
   mData.mCrChannel = mData.mCbChannel + mData.mCbCrStride * mData.mCbCrSize.height;
   int cbcr_x = aData.mPicX >> width_shift;
   int cbcr_y = aData.mPicY >> height_shift;
@@ -412,165 +539,144 @@ PlanarYCbCrImageOGL::SetData(const Plana
 
   // Fix picture rect to be correct
   mData.mPicX = mData.mPicY = 0;
   mSize = aData.mPicSize;
 
   mHasData = PR_TRUE;
 }
 
-static void
-SetupPlaneTexture(GLContext* aGL, const gfxIntSize& aSize, PRUint8* aData, PRBool aIsNew)
+void
+PlanarYCbCrImageOGL::AllocateTextures(mozilla::gl::GLContext *gl)
 {
-  aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_LINEAR);
-  aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_LINEAR);
-  aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
-  aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
+  gl->MakeCurrent();
 
-  if (aIsNew) {
-    aGL->fTexImage2D(LOCAL_GL_TEXTURE_2D,
-                     0,
-                     LOCAL_GL_LUMINANCE,
-                     aSize.width,
-                     aSize.height,
-                     0,
-                     LOCAL_GL_LUMINANCE,
-                     LOCAL_GL_UNSIGNED_BYTE,
-                     aData);
-  } else {
-    aGL->fTexSubImage2D(LOCAL_GL_TEXTURE_2D,
-                        0,
-                        0, 0,
-                        aSize.width,
-                        aSize.height,
-                        LOCAL_GL_LUMINANCE,
-                        LOCAL_GL_UNSIGNED_BYTE,
-                        aData);
-  }
+  mRecycleBin->GetTexture(RecycleBin::TEXTURE_Y, mData.mYSize, gl, &mTextures[0]);
+  InitTexture(gl, mTextures[0].GetTextureID(), LOCAL_GL_LUMINANCE, mData.mYSize);
+
+  mRecycleBin->GetTexture(RecycleBin::TEXTURE_C, mData.mCbCrSize, gl, &mTextures[1]);
+  InitTexture(gl, mTextures[1].GetTextureID(), LOCAL_GL_LUMINANCE, mData.mCbCrSize);
+
+  mRecycleBin->GetTexture(RecycleBin::TEXTURE_C, mData.mCbCrSize, gl, &mTextures[2]);
+  InitTexture(gl, mTextures[2].GetTextureID(), LOCAL_GL_LUMINANCE, mData.mCbCrSize);
 }
 
 void
-PlanarYCbCrImageOGL::AllocateTextures(LayerManagerOGL *aManager)
+PlanarYCbCrImageOGL::UpdateTextures(GLContext *gl)
 {
-  aManager->MakeCurrent();
-
-  mozilla::gl::GLContext *gl = aManager->gl();
-
-  PRPackedBool isNewTexture[3];
-  for (PRUint32 i = 0; i < 3; ++i) {
-    isNewTexture[i] = !mTextures[i].IsAllocated();
-  }
-
-  mRecycleBin->TakeTexture(RecycleBin::TEXTURE_Y, mData.mYSize, gl, &mTextures[0]);
-  mRecycleBin->TakeTexture(RecycleBin::TEXTURE_C, mData.mCbCrSize, gl, &mTextures[1]);
-  mRecycleBin->TakeTexture(RecycleBin::TEXTURE_C, mData.mCbCrSize, gl, &mTextures[2]);
-  if (!HasTextures())
-    return;
-
   GLint alignment;
 
   if (!((ptrdiff_t)mData.mYStride & 0x7) && !((ptrdiff_t)mData.mYChannel & 0x7)) {
     alignment = 8;
   } else if (!((ptrdiff_t)mData.mYStride & 0x3)) {
     alignment = 4;
   } else if (!((ptrdiff_t)mData.mYStride & 0x1)) {
     alignment = 2;
   } else {
     alignment = 1;
   }
 
   // Set texture alignment for Y plane.
   gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, alignment);
 
   gl->fBindTexture(LOCAL_GL_TEXTURE_2D, mTextures[0].GetTextureID());
-
-  SetupPlaneTexture(gl, mData.mYSize, mData.mYChannel, isNewTexture[0]);
+  gl->fTexSubImage2D(LOCAL_GL_TEXTURE_2D, 0,
+                     0, 0, mData.mYSize.width, mData.mYSize.height,
+                     LOCAL_GL_LUMINANCE,
+                     LOCAL_GL_UNSIGNED_BYTE,
+                     mData.mYChannel);
 
   if (!((ptrdiff_t)mData.mCbCrStride & 0x7) && 
       !((ptrdiff_t)mData.mCbChannel & 0x7) &&
-      !((ptrdiff_t)mData.mCrChannel & 0x7)) {
+      !((ptrdiff_t)mData.mCrChannel & 0x7))
+  {
     alignment = 8;
   } else if (!((ptrdiff_t)mData.mCbCrStride & 0x3)) {
     alignment = 4;
   } else if (!((ptrdiff_t)mData.mCbCrStride & 0x1)) {
     alignment = 2;
   } else {
     alignment = 1;
   }
   
   // Set texture alignment for Cb/Cr plane
   gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, alignment);
 
   gl->fBindTexture(LOCAL_GL_TEXTURE_2D, mTextures[1].GetTextureID());
-
-  SetupPlaneTexture(gl, mData.mCbCrSize, mData.mCbChannel, isNewTexture[1]);
+  gl->fTexSubImage2D(LOCAL_GL_TEXTURE_2D, 0,
+                     0, 0, mData.mCbCrSize.width, mData.mCbCrSize.height,
+                     LOCAL_GL_LUMINANCE,
+                     LOCAL_GL_UNSIGNED_BYTE,
+                     mData.mCbChannel);
 
   gl->fBindTexture(LOCAL_GL_TEXTURE_2D, mTextures[2].GetTextureID());
-
-  SetupPlaneTexture(gl, mData.mCbCrSize, mData.mCrChannel, isNewTexture[2]);
+  gl->fTexSubImage2D(LOCAL_GL_TEXTURE_2D, 0,
+                     0, 0, mData.mCbCrSize.width, mData.mCbCrSize.height,
+                     LOCAL_GL_LUMINANCE,
+                     LOCAL_GL_UNSIGNED_BYTE,
+                     mData.mCrChannel);
 
   // Reset alignment to default
   gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4);
 
   // Recycle main-memory buffer now that we've got the data in our textures
   if (mBuffer) {
     mRecycleBin->RecycleBuffer(mBuffer.forget(), mBufferSize);
   }
 }
 
-CairoImageOGL::CairoImageOGL(LayerManagerOGL *aManager) : CairoImage(nsnull)
+CairoImageOGL::CairoImageOGL(LayerManagerOGL *aManager)
+  : CairoImage(nsnull)
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread to create a cairo image");
 
-  // Allocate texture now to grab a reference to the GLContext
-  mTexture.Allocate(aManager->gl());
+  if (aManager) {
+    // Allocate texture now to grab a reference to the GLContext
+    mTexture.Allocate(aManager->glForResources());
+  }
 }
 
 void
 CairoImageOGL::SetData(const CairoImage::Data &aData)
 {
   if (!mTexture.IsAllocated())
     return;
 
   mozilla::gl::GLContext *gl = mTexture.GetGLContext();
   gl->MakeCurrent();
 
-  mSize = aData.mSize;
-
-  gl->fActiveTexture(LOCAL_GL_TEXTURE0);
-  gl->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture.GetTextureID());
-  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);
+  if (mSize != aData.mSize) {
+    gl->fActiveTexture(LOCAL_GL_TEXTURE0);
+    InitTexture(gl, mTexture.GetTextureID(), LOCAL_GL_RGBA, aData.mSize);
+    mSize = aData.mSize;
+  } else {
+    gl->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture.GetTextureID());
+  }
 
   if (!mASurfaceAsGLContext) {
     mASurfaceAsGLContext = GLContextProvider::CreateForNativePixmapSurface(aData.mSurface);
     if (mASurfaceAsGLContext)
       mASurfaceAsGLContext->BindTexImage();
   }
+
   if (mASurfaceAsGLContext)
     return;
 
   // XXX This could be a lot more efficient if we already have an image-compatible
   // surface
   // XXX if we ever create an ImageFormatRGB24 surface, make sure that we use
   // a BGRX program in that case (instead of BGRA)
   nsRefPtr<gfxImageSurface> imageSurface =
     new gfxImageSurface(aData.mSize, gfxASurface::ImageFormatARGB32);
   nsRefPtr<gfxContext> context = new gfxContext(imageSurface);
 
   context->SetSource(aData.mSurface);
   context->Paint();
 
-  gl->fTexImage2D(LOCAL_GL_TEXTURE_2D,
-                  0,
-                  LOCAL_GL_RGBA,
-                  mSize.width,
-                  mSize.height,
-                  0,
-                  LOCAL_GL_RGBA,
-                  LOCAL_GL_UNSIGNED_BYTE,
-                  imageSurface->Data());
+  gl->fTexSubImage2D(LOCAL_GL_TEXTURE_2D, 0,
+                     0, 0, mSize.width, mSize.height,
+                     LOCAL_GL_RGBA,
+                     LOCAL_GL_UNSIGNED_BYTE,
+                     imageSurface->Data());
 }
 
 } /* layers */
 } /* mozilla */
--- a/gfx/layers/opengl/ImageLayerOGL.h
+++ b/gfx/layers/opengl/ImageLayerOGL.h
@@ -1,9 +1,9 @@
-/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * ***** BEGIN LICENSE BLOCK *****
  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  *
  * The contents of this file are subject to the Mozilla Public License Version
  * 1.1 (the "License"); you may not use this file except in compliance with
  * the License. You may obtain a copy of the License at
  * http://www.mozilla.org/MPL/
  *
@@ -97,27 +97,27 @@ class RecycleBin {
 
   typedef mozilla::gl::GLContext GLContext;
 
 public:
   RecycleBin();
 
   void RecycleBuffer(PRUint8* aBuffer, PRUint32 aSize);
   // Returns a recycled buffer of the right size, or allocates a new buffer.
-  PRUint8* TakeBuffer(PRUint32 aSize);
+  PRUint8* GetBuffer(PRUint32 aSize);
 
   enum TextureType {
     TEXTURE_Y,
     TEXTURE_C
   };
 
   void RecycleTexture(GLTexture *aTexture, TextureType aType,
                       const gfxIntSize& aSize);
-  void TakeTexture(TextureType aType, const gfxIntSize& aSize,
-                   GLContext *aContext, GLTexture *aOutTexture);
+  void GetTexture(TextureType aType, const gfxIntSize& aSize,
+                  GLContext *aContext, GLTexture *aOutTexture);
 
 private:
   typedef mozilla::Mutex Mutex;
 
   // This protects mRecycledBuffers, mRecycledBufferSize, mRecycledTextures
   // and mRecycledTextureSizes
   Mutex mLock;
 
@@ -130,29 +130,31 @@ private:
   nsTArray<GLTexture> mRecycledTextures[2];
   gfxIntSize mRecycledTextureSizes[2];
 };
 
 class THEBES_API ImageContainerOGL : public ImageContainer
 {
 public:
   ImageContainerOGL(LayerManagerOGL *aManager);
-  virtual ~ImageContainerOGL() {}
+  virtual ~ImageContainerOGL();
 
   virtual already_AddRefed<Image> CreateImage(const Image::Format* aFormats,
                                               PRUint32 aNumFormats);
 
   virtual void SetCurrentImage(Image* aImage);
 
   virtual already_AddRefed<Image> GetCurrentImage();
 
   virtual already_AddRefed<gfxASurface> GetCurrentAsSurface(gfxIntSize* aSize);
 
   virtual gfxIntSize GetCurrentSize();
 
+  virtual PRBool SetLayerManager(LayerManager *aManager);
+
 private:
   typedef mozilla::Mutex Mutex;
 
   nsRefPtr<RecycleBin> mRecycleBin;
 
   // This protects mActiveImage
   Mutex mActiveImageLock;
 
@@ -164,39 +166,44 @@ class THEBES_API ImageLayerOGL : public 
 {
 public:
   ImageLayerOGL(LayerManagerOGL *aManager)
     : ImageLayer(aManager, NULL)
     , LayerOGL(aManager)
   { 
     mImplData = static_cast<LayerOGL*>(this);
   }
+  ~ImageLayerOGL() { Destroy(); }
 
   // LayerOGL Implementation
+  virtual void Destroy() { mDestroyed = PR_TRUE; }
   virtual Layer* GetLayer();
 
   virtual void RenderLayer(int aPreviousFrameBuffer,
                            const nsIntPoint& aOffset);
 };
 
 class THEBES_API PlanarYCbCrImageOGL : public PlanarYCbCrImage
 {
   typedef mozilla::gl::GLContext GLContext;
 
 public:
-  PlanarYCbCrImageOGL(RecycleBin *aRecycleBin);
+  PlanarYCbCrImageOGL(LayerManagerOGL *aManager,
+                      RecycleBin *aRecycleBin);
   ~PlanarYCbCrImageOGL();
 
   virtual void SetData(const Data &aData);
 
   /**
    * Upload the data from out mData into our textures. For now we use this to
    * make sure the textures are created and filled on the main thread.
    */
-  void AllocateTextures(LayerManagerOGL *aManager);
+  void AllocateTextures(GLContext *gl);
+  void UpdateTextures(GLContext *gl);
+
   PRBool HasData() { return mHasData; }
   PRBool HasTextures()
   {
     return mTextures[0].IsAllocated() && mTextures[1].IsAllocated() &&
            mTextures[2].IsAllocated();
   }
 
   nsAutoArrayPtr<PRUint8> mBuffer;
--- a/gfx/layers/opengl/LayerManagerOGL.cpp
+++ b/gfx/layers/opengl/LayerManagerOGL.cpp
@@ -71,31 +71,53 @@ LayerManagerOGL::LayerManagerOGL(nsIWidg
   , mBackBufferTexture(0)
   , mBackBufferSize(-1, -1)
   , mHasBGRA(0)
 {
 }
 
 LayerManagerOGL::~LayerManagerOGL()
 {
-  mRoot = nsnull;
-  CleanupResources();
+  Destroy();
+}
+
+void
+LayerManagerOGL::Destroy()
+{
+  if (!mDestroyed) {
+    if (mRoot) {
+      RootLayer()->Destroy();
+    }
+    mRoot = nsnull;
+
+    // Make a copy, since SetLayerManager will cause mImageContainers
+    // to get mutated.
+    nsTArray<ImageContainer*> imageContainers(mImageContainers);
+    for (PRUint32 i = 0; i < imageContainers.Length(); ++i) {
+      ImageContainer *c = imageContainers[i];
+      c->SetLayerManager(nsnull);
+    }
+
+    CleanupResources();
+
+    mDestroyed = PR_TRUE;
+  }
 }
 
 void
 LayerManagerOGL::CleanupResources()
 {
   if (!mGLContext)
     return;
 
   nsRefPtr<GLContext> ctx = mGLContext->GetSharedContext();
   if (!ctx) {
     ctx = mGLContext;
   }
-  
+
   ctx->MakeCurrent();
 
   for (unsigned int i = 0; i < mPrograms.Length(); ++i)
     delete mPrograms[i];
   mPrograms.Clear();
 
   ctx->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
 
@@ -120,16 +142,17 @@ LayerManagerOGL::CleanupResources()
 PRBool
 LayerManagerOGL::Initialize(GLContext *aExistingContext)
 {
   if (aExistingContext) {
     mGLContext = aExistingContext;
   } else {
     if (mGLContext)
       CleanupResources();
+
     mGLContext = gl::GLContextProvider::CreateForWindow(mWidget);
 
     if (!mGLContext) {
       NS_WARNING("Failed to create LayerManagerOGL context");
       return PR_FALSE;
     }
   }
 
@@ -322,91 +345,167 @@ LayerManagerOGL::SetClippingRegion(const
 void
 LayerManagerOGL::BeginTransaction()
 {
 }
 
 void
 LayerManagerOGL::BeginTransactionWithTarget(gfxContext *aTarget)
 {
+  if (mDestroyed) {
+    NS_WARNING("Call on destroyed layer manager");
+    return;
+  }
+
   mTarget = aTarget;
 }
 
 void
 LayerManagerOGL::EndTransaction(DrawThebesLayerCallback aCallback,
                                 void* aCallbackData)
 {
+  if (mDestroyed) {
+    NS_WARNING("Call on destroyed layer manager");
+    return;
+  }
+
   mThebesLayerCallback = aCallback;
   mThebesLayerCallbackData = aCallbackData;
 
   Render();
 
   mThebesLayerCallback = nsnull;
   mThebesLayerCallbackData = nsnull;
 
   mTarget = NULL;
 }
 
 already_AddRefed<ThebesLayer>
 LayerManagerOGL::CreateThebesLayer()
 {
+  if (mDestroyed) {
+    NS_WARNING("Call on destroyed layer manager");
+    return nsnull;
+  }
+
   nsRefPtr<ThebesLayer> layer = new ThebesLayerOGL(this);
   return layer.forget();
 }
 
 already_AddRefed<ContainerLayer>
 LayerManagerOGL::CreateContainerLayer()
 {
+  if (mDestroyed) {
+    NS_WARNING("Call on destroyed layer manager");
+    return nsnull;
+  }
+
   nsRefPtr<ContainerLayer> layer = new ContainerLayerOGL(this);
   return layer.forget();
 }
 
 already_AddRefed<ImageContainer>
 LayerManagerOGL::CreateImageContainer()
 {
+  if (mDestroyed) {
+    NS_WARNING("Call on destroyed layer manager");
+    return nsnull;
+  }
+
   nsRefPtr<ImageContainer> container = new ImageContainerOGL(this);
+  RememberImageContainer(container);
   return container.forget();
 }
 
 already_AddRefed<ImageLayer>
 LayerManagerOGL::CreateImageLayer()
 {
+  if (mDestroyed) {
+    NS_WARNING("Call on destroyed layer manager");
+    return nsnull;
+  }
+
   nsRefPtr<ImageLayer> layer = new ImageLayerOGL(this);
   return layer.forget();
 }
 
 already_AddRefed<ColorLayer>
 LayerManagerOGL::CreateColorLayer()
 {
+  if (mDestroyed) {
+    NS_WARNING("Call on destroyed layer manager");
+    return nsnull;
+  }
+
   nsRefPtr<ColorLayer> layer = new ColorLayerOGL(this);
   return layer.forget();
 }
 
 already_AddRefed<CanvasLayer>
 LayerManagerOGL::CreateCanvasLayer()
 {
+  if (mDestroyed) {
+    NS_WARNING("Call on destroyed layer manager");
+    return nsnull;
+  }
+
   nsRefPtr<CanvasLayer> layer = new CanvasLayerOGL(this);
   return layer.forget();
 }
 
 void
+LayerManagerOGL::ForgetImageContainer(ImageContainer *aContainer)
+{
+  NS_ASSERTION(aContainer->Manager() == this,
+               "ForgetImageContainer called on non-owned container!");
+
+  if (!mImageContainers.RemoveElement(aContainer)) {
+    NS_WARNING("ForgetImageContainer couldn't find container it was supposed to forget!");
+    return;
+  }
+}
+
+void
+LayerManagerOGL::RememberImageContainer(ImageContainer *aContainer)
+{
+  NS_ASSERTION(aContainer->Manager() == this,
+               "RememberImageContainer called on non-owned container!");
+  mImageContainers.AppendElement(aContainer);
+}
+
+void
 LayerManagerOGL::MakeCurrent()
 {
+  if (mDestroyed) {
+    NS_WARNING("Call on destroyed layer manager");
+    return;
+  }
+
   mGLContext->MakeCurrent();
 }
 
 LayerOGL*
 LayerManagerOGL::RootLayer() const
 {
+  if (mDestroyed) {
+    NS_WARNING("Call on destroyed layer manager");
+    return nsnull;
+  }
+
   return static_cast<LayerOGL*>(mRoot->ImplData());
 }
 
 void
 LayerManagerOGL::Render()
 {
+  if (mDestroyed) {
+    NS_WARNING("Call on destroyed layer manager");
+    return;
+  }
+
   nsIntRect rect;
   mWidget->GetBounds(rect);
   GLint width = rect.width;
   GLint height = rect.height;
 
   MakeCurrent();
 
   DEBUG_GL_ERROR_CHECK(mGLContext);
@@ -734,11 +833,10 @@ LayerManagerOGL::CreateFBOWithTexture(in
                LOCAL_GL_FRAMEBUFFER_COMPLETE, "Error setting up framebuffer.");
 
   *aFBO = fbo;
   *aTexture = tex;
 
   DEBUG_GL_ERROR_CHECK(gl());
 }
 
-                                     
 } /* layers */
 } /* mozilla */
--- a/gfx/layers/opengl/LayerManagerOGL.h
+++ b/gfx/layers/opengl/LayerManagerOGL.h
@@ -78,16 +78,18 @@ class THEBES_API LayerManagerOGL : publi
   typedef mozilla::gl::GLContext GLContext;
 
 public:
   LayerManagerOGL(nsIWidget *aWidget);
   virtual ~LayerManagerOGL();
 
   void CleanupResources();
 
+  void Destroy();
+
   /**
    * Initializes the layer manager, this is when the layer manager will
    * actually access the device and attempt to create the swap chain used
    * to draw to the window. If this method fails the device cannot be used.
    * This function is not threadsafe.
    *
    * \param aExistingContext an existing GL context to use, instead of creating
    * our own for the widget.
@@ -131,16 +133,26 @@ public:
 
   virtual already_AddRefed<CanvasLayer> CreateCanvasLayer();
 
   virtual already_AddRefed<ImageContainer> CreateImageContainer();
 
   virtual LayersBackend GetBackendType() { return LAYERS_OPENGL; }
 
   /**
+   * Image Container management.
+   */
+
+  /* Forget this image container.  Should be called by ImageContainerOGL
+   * on its current layer manager before switching to a new one.
+   */
+  void ForgetImageContainer(ImageContainer* aContainer);
+  void RememberImageContainer(ImageContainer* aContainer);
+
+  /**
    * Helper methods.
    */
   void MakeCurrent();
 
   ColorTextureLayerProgram *GetRGBALayerProgram() {
     return static_cast<ColorTextureLayerProgram*>(mPrograms[RGBALayerProgramType]);
   }
   ColorTextureLayerProgram *GetBGRALayerProgram() {
@@ -177,16 +189,26 @@ public:
   GLContext *gl() const { return mGLContext; }
 
   DrawThebesLayerCallback GetThebesLayerCallback() const
   { return mThebesLayerCallback; }
 
   void* GetThebesLayerCallbackData() const
   { return mThebesLayerCallbackData; }
 
+  // This is a GLContext that can be used for resource
+  // management (creation, destruction).  It is guaranteed
+  // to be either the same as the gl() context, or a context
+  // that is in the same share pool.
+  GLContext *glForResources() const {
+    if (mGLContext->GetSharedContext())
+      return mGLContext->GetSharedContext();
+    return mGLContext;
+  }
+
   /*
    * Helper functions for our layers
    */
   void CallThebesLayerDrawCallback(ThebesLayer* aLayer,
                                    gfxContext* aContext,
                                    const nsIntRegion& aRegionToDraw)
   {
     NS_ASSERTION(mThebesLayerCallback,
@@ -276,16 +298,21 @@ private:
   nsIWidget *mWidget;
   /** 
    * Context target, NULL when drawing directly to our swap chain.
    */
   nsRefPtr<gfxContext> mTarget;
 
   nsRefPtr<GLContext> mGLContext;
 
+  // The image containers that this layer manager has created.
+  // The destructor will tell the layer manager to remove
+  // it from the list.
+  nsTArray<ImageContainer*> mImageContainers;
+
   enum ProgramType {
     RGBALayerProgramType,
     BGRALayerProgramType,
     RGBXLayerProgramType,
     BGRXLayerProgramType,
     RGBARectLayerProgramType,
     ColorLayerProgramType,
     YCbCrLayerProgramType,
@@ -359,31 +386,39 @@ private:
 
 /**
  * General information and tree management for OGL layers.
  */
 class LayerOGL
 {
 public:
   LayerOGL(LayerManagerOGL *aManager)
-    : mOGLManager(aManager)
+    : mOGLManager(aManager), mDestroyed(PR_FALSE)
   { }
 
+  virtual ~LayerOGL() { }
+
   virtual LayerOGL *GetFirstChildOGL() {
     return nsnull;
   }
 
+  /* Do NOT call this from the generic LayerOGL destructor.  Only from the
+   * concrete class destructor
+   */
+  virtual void Destroy() = 0;
+
   virtual Layer* GetLayer() = 0;
 
   virtual void RenderLayer(int aPreviousFrameBuffer,
                            const nsIntPoint& aOffset) = 0;
 
   typedef mozilla::gl::GLContext GLContext;
 
   GLContext *gl() const { return mOGLManager->gl(); }
 protected:
   LayerManagerOGL *mOGLManager;
+  PRPackedBool mDestroyed;
 };
 
 } /* layers */
 } /* mozilla */
 
 #endif /* GFX_LAYERMANAGEROGL_H */
--- a/gfx/layers/opengl/LayerManagerOGLProgram.h
+++ b/gfx/layers/opengl/LayerManagerOGLProgram.h
@@ -295,29 +295,27 @@ protected:
 
       if (!success) {
         fprintf (stderr, "=== PROGRAM LINKING FAILED ===\n");
       } else {
         fprintf (stderr, "=== PROGRAM LINKING WARNINGS ===\n");
       }
       fprintf (stderr, "=== Log:\n%s\n", nsPromiseFlatCString(log).get());
       fprintf (stderr, "============\n");
-
-      // We can mark the shaders for deletion; they're attached to the program
-      // and will remain attached.
-      mGL->fDeleteShader(vertexShader);
-      mGL->fDeleteShader(fragmentShader);
+    }
 
-      if (!success) {
-        mGL->fDeleteProgram(mProgram);
+    // We can mark the shaders for deletion; they're attached to the program
+    // and will remain attached.
+    mGL->fDeleteShader(vertexShader);
+    mGL->fDeleteShader(fragmentShader);
 
-        mProgram = 0;
-
-        return false;
-      }
+    if (!success) {
+      mGL->fDeleteProgram(mProgram);
+      mProgram = 0;
+      return false;
     }
 
     // Now query uniforms, so that we can initialize mUniformValues
     // note that for simplicity, mUniformLocations is indexed by the
     // uniform -location-, and not the uniform -index-.  This means
     // that it might have a bunch of unused space as locations dense
     // like indices are; however, there are unlikely to be enough for
     // our shaders for this to become a significant memory issue.
--- a/gfx/layers/opengl/ThebesLayerOGL.cpp
+++ b/gfx/layers/opengl/ThebesLayerOGL.cpp
@@ -326,18 +326,28 @@ ThebesLayerOGL::ThebesLayerOGL(LayerMana
   , LayerOGL(aManager)
   , mBuffer(nsnull)
 {
   mImplData = static_cast<LayerOGL*>(this);
 }
 
 ThebesLayerOGL::~ThebesLayerOGL()
 {
-  mBuffer = nsnull;
-  DEBUG_GL_ERROR_CHECK(gl());
+  Destroy();
+}
+
+void
+ThebesLayerOGL::Destroy()
+{
+  if (!mDestroyed) {
+    mBuffer = nsnull;
+    DEBUG_GL_ERROR_CHECK(gl());
+
+    mDestroyed = PR_TRUE;
+  }
 }
 
 PRBool
 ThebesLayerOGL::CreateSurface()
 {
   NS_ASSERTION(!mBuffer, "buffer already created?");
 
   if (mVisibleRegion.IsEmpty()) {
--- a/gfx/layers/opengl/ThebesLayerOGL.h
+++ b/gfx/layers/opengl/ThebesLayerOGL.h
@@ -45,31 +45,32 @@
 
 
 namespace mozilla {
 namespace layers {
 
 class ThebesLayerBufferOGL;
 
 class ThebesLayerOGL : public ThebesLayer, 
-                         public LayerOGL
+                       public LayerOGL
 {
   typedef ThebesLayerBufferOGL Buffer;
 
 public:
   ThebesLayerOGL(LayerManagerOGL *aManager);
   virtual ~ThebesLayerOGL();
 
   /** Layer implementation */
   void SetVisibleRegion(const nsIntRegion& aRegion);
 
   /** ThebesLayer implementation */
   void InvalidateRegion(const nsIntRegion& aRegion);
 
   /** LayerOGL implementation */
+  void Destroy();
   Layer* GetLayer();
   virtual PRBool IsEmpty();
   virtual void RenderLayer(int aPreviousFrameBuffer,
                            const nsIntPoint& aOffset);
 
 private:
   PRBool CreateSurface();