Bug 773440 - Remove one video frame copy when using async-video. r=roc
authorNicolas Silva <nsilva@mozilla.com>
Thu, 23 Aug 2012 10:56:36 -0400
changeset 105208 00ba8683d7c941db5035fbbc68f5a41540261e5f
parent 105207 9e98e33cb80df128010a11f20bb6eab2f3bd85c2
child 105209 78d6548c9c2340e6d974ca03911a894be412bc29
push id55
push usershu@rfrn.org
push dateThu, 30 Aug 2012 01:33:09 +0000
reviewersroc
bugs773440
milestone17.0a1
Bug 773440 - Remove one video frame copy when using async-video. r=roc
gfx/layers/ImageContainer.cpp
gfx/layers/ImageContainer.h
gfx/layers/basic/BasicImageLayer.cpp
gfx/layers/ipc/ImageContainerChild.cpp
gfx/layers/ipc/ImageContainerChild.h
gfx/layers/ipc/LayersSurfaces.ipdlh
--- a/gfx/layers/ImageContainer.cpp
+++ b/gfx/layers/ImageContainer.cpp
@@ -134,21 +134,55 @@ ImageContainer::ImageContainer(int flag)
 
 ImageContainer::~ImageContainer()
 {
   if (mImageContainerChild) {
     mImageContainerChild->DispatchStop();
   }
 }
 
+void ImageContainer::CopyPlane(uint8_t *aDst, uint8_t *aSrc,
+                               const gfxIntSize &aSize,
+                               int32_t aSrcStride, int32_t aDstStride,
+                               int32_t aOffset, int32_t aSkip)
+{
+  if (!aOffset && !aSkip && aSrcStride == aDstStride) {
+    // Fast path: planar input.
+    memcpy(aDst, aSrc, aSize.height * aSrcStride);
+  } else {
+    int32_t height = aSize.height;
+    int32_t width = aSize.width;
+    for (int y = 0; y < height; ++y) {
+      uint8_t *src = aSrc + aOffset;
+      uint8_t *dst = aDst;
+      if (!aSkip) {
+        // Fast path: offset only, no per-pixel skip.
+        memcpy(dst, src, width);
+      } else {
+        // Slow path
+        for (int x = 0; x < width; ++x) {
+          *dst++ = *src++;
+          src += aSkip;
+        }
+      }
+      aSrc += aSrcStride;
+      aDst += aDstStride;
+    }
+  }
+}
+
+
 already_AddRefed<Image>
 ImageContainer::CreateImage(const ImageFormat *aFormats,
                             uint32_t aNumFormats)
 {
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+  if (IsAsync()) {
+    return mImageContainerChild->CreateImage(aFormats, aNumFormats);
+  }
   return mImageFactory->CreateImage(aFormats, aNumFormats, mScaleHint, mRecycleBin);
 }
 
 void 
 ImageContainer::SetCurrentImageInternal(Image *aImage)
 {
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
 
@@ -406,81 +440,49 @@ PlanarYCbCrImage::~PlanarYCbCrImage()
 }
 
 uint8_t* 
 PlanarYCbCrImage::AllocateBuffer(uint32_t aSize)
 {
   return mRecycleBin->GetBuffer(aSize); 
 }
 
-static void
-CopyPlane(uint8_t *aDst, uint8_t *aSrc,
-          const gfxIntSize &aSize, int32_t aStride,
-          int32_t aOffset, int32_t aSkip)
+void PlanarYCbCrImage::Allocate(Data& aData)
 {
-  if (!aOffset && !aSkip) {
-    // Fast path: planar input.
-    memcpy(aDst, aSrc, aSize.height * aStride);
-  } else {
-    int32_t height = aSize.height;
-    int32_t width = aSize.width;
-    for (int y = 0; y < height; ++y) {
-      uint8_t *src = aSrc + aOffset;
-      uint8_t *dst = aDst;
-      if (!aSkip) {
-        // Fast path: offset only, no per-pixel skip.
-        memcpy(dst, src, width);
-      } else {
-        // Slow path
-        for (int x = 0; x < width; ++x) {
-          *dst++ = *src++;
-          src += aSkip;
-        }
-      }
-      aSrc += aStride;
-      aDst += aStride;
-    }
-  }
-}
-
-void
-PlanarYCbCrImage::CopyData(const Data& aData)
-{
-  mData = aData;
-
   // update buffer size
-  mBufferSize = mData.mCbCrStride * mData.mCbCrSize.height * 2 +
-                mData.mYStride * mData.mYSize.height;
-
+  int bufferSize = aData.mCbCrStride * aData.mCbCrSize.height * 2 +
+                aData.mYStride * aData.mYSize.height;
   // get new buffer
-  mBuffer = AllocateBuffer(mBufferSize); 
-  if (!mBuffer)
+  uint8_t* buffer = AllocateBuffer(bufferSize); 
+  if (!buffer)
     return;
 
-  mData.mYChannel = mBuffer;
-  mData.mCbChannel = mData.mYChannel + mData.mYStride * mData.mYSize.height;
-  mData.mCrChannel = mData.mCbChannel + mData.mCbCrStride * mData.mCbCrSize.height;
-
-  CopyPlane(mData.mYChannel, aData.mYChannel,
-            mData.mYSize, mData.mYStride,
-            mData.mYOffset, mData.mYSkip);
-  CopyPlane(mData.mCbChannel, aData.mCbChannel,
-            mData.mCbCrSize, mData.mCbCrStride,
-            mData.mCbOffset, mData.mCbSkip);
-  CopyPlane(mData.mCrChannel, aData.mCrChannel,
-            mData.mCbCrSize, mData.mCbCrStride,
-            mData.mCrOffset, mData.mCrSkip);
-
-  mSize = aData.mPicSize;
+  aData.mYChannel = buffer;
+  aData.mCbChannel = aData.mYChannel + aData.mYStride * aData.mYSize.height;
+  aData.mCrChannel = aData.mCbChannel + aData.mCbCrStride * aData.mCbCrSize.height;
+  mBuffer = buffer;
 }
 
 void
 PlanarYCbCrImage::SetData(const Data &aData)
 {
-  CopyData(aData);
+  mData = aData;
+  mBufferSize = mData.mCbCrStride * mData.mCbCrSize.height * 2 +
+                 mData.mYStride * mData.mYSize.height;
+  mSize = mData.mPicSize;
+  // If mBuffer has not been allocated (through Allocate(aData)), allocate it.
+  // This code path is slower than the one used when Allocate has been called
+  // since it will trigger a full copy.
+  if (!mBuffer) {
+    mBuffer = AllocateBuffer(mBufferSize);
+    NS_ASSERTION(mBuffer, "Failed to Allocate!");
+  }
+  if (aData.mYChannel != mBuffer) {
+    memcpy(mBuffer.get(), aData.mYChannel, mBufferSize);
+  }
 }
 
 already_AddRefed<gfxASurface>
 PlanarYCbCrImage::GetAsSurface()
 {
   if (mSurface) {
     nsRefPtr<gfxASurface> result = mSurface.get();
     return result.forget();
--- a/gfx/layers/ImageContainer.h
+++ b/gfx/layers/ImageContainer.h
@@ -34,16 +34,17 @@ namespace mozilla {
 class CrossProcessMutex;
 namespace ipc {
 class Shmem;
 }
     
 namespace layers {
 
 class ImageContainerChild;
+class SharedPlanarYCbCrImage;
 
 struct ImageBackendData
 {
   virtual ~ImageBackendData() {}
 
 protected:
   ImageBackendData() {}
 };
@@ -478,16 +479,24 @@ public:
    */
   void SetRemoteImageData(RemoteImageData *aRemoteData,
                           CrossProcessMutex *aRemoteDataMutex);
   /**
    * This can be used to check if the container has RemoteData set.
    */
   RemoteImageData *GetRemoteImageData() { return mRemoteData; }
 
+  /**
+   * Helper function to copy an image buffer respecting stride, offset and skip.
+   */
+  static void CopyPlane(uint8_t *aDst, uint8_t *aSrc,
+                        const gfxIntSize &aSize,
+                        int32_t aSrcStride, int32_t aDstStride,
+                        int32_t aOffset, int32_t aSkip);
+
 protected:
   typedef mozilla::ReentrantMonitor ReentrantMonitor;
 
   void SetCurrentImageInternal(Image* aImage);
 
   // This is called to ensure we have an active image, this may not be true
   // when we're storing image information in a RemoteImageData structure.
   // NOTE: If we have remote data mRemoteDataMutex should be locked when
@@ -646,35 +655,58 @@ public:
   };
 
   enum {
     MAX_DIMENSION = 16384
   };
 
   virtual ~PlanarYCbCrImage();
 
+  virtual SharedPlanarYCbCrImage* AsSharedPlanarYCbCrImage()
+  {
+    return nullptr;
+  }
+
   /**
-   * This makes a copy of the data buffers, in order to support functioning
-   * in all different layer managers.
+   * If Allocate has been invoked on aData, simply acquire aData's buffers.
+   * Otherwise make a copy of the data buffers, in order to support 
+   * functioning in all different layer managers.
    */
   virtual void SetData(const Data& aData);
 
   /**
    * Ask this Image to not convert YUV to RGB during SetData, and make
    * the original data available through GetData. This is optional,
    * and not all PlanarYCbCrImages will support it.
    */
   virtual void SetDelayedConversion(bool aDelayed) { }
 
   /**
    * Grab the original YUV data. This is optional.
    */
   virtual const Data* GetData() { return &mData; }
 
   /**
+   * Allocate image buffer(s) and give aData ownership of the image by filling
+   * the pointers in aData.
+   * The information about the size, stride, etc. must be set in aData
+   * prior to calling this method.
+   *
+   * This method is meant to be used with SetData in the following order:
+   * - The caller first fills the necessary information in aData for the data to
+   * be allocated properly.
+   * - The caller invoke Allocate(aData) which will fill the remaining pointers
+   * in aData with allocated memory.
+   * - The caller can fill the allocated buffer with actual image data.
+   * - The caller then invoke SetData(aData) with the same aData that was 
+   * passed in Allocate.
+   */
+  virtual void Allocate(Data& aData);
+
+  /**
    * Return the number of bytes of heap memory used to store this image.
    */
   virtual uint32_t GetDataSize() { return mBufferSize; }
 
   virtual bool IsValid() { return !!mBufferSize; }
 
   virtual gfxIntSize GetSize() { return mSize; }
 
--- a/gfx/layers/basic/BasicImageLayer.cpp
+++ b/gfx/layers/basic/BasicImageLayer.cpp
@@ -340,17 +340,17 @@ BasicShadowableImageLayer::Paint(gfxCont
              data->mCbChannel + i * data->mCbCrStride,
              data->mCbCrSize.width);
       memcpy(dv->Data() + i * dv->Stride(),
              data->mCrChannel + i * data->mCbCrStride,
              data->mCbCrSize.width);
     }
 
     YUVImage yuv(mBackBufferY, mBackBufferU, mBackBufferV,
-                 data->GetPictureRect());
+                 data->GetPictureRect(), 0);
 
     BasicManager()->PaintedImage(BasicManager()->Hold(this),
                                  yuv);
     return;
   }
 
   gfxIntSize oldSize = mSize;
   nsRefPtr<gfxPattern> pat = GetAndPaintCurrentImage
--- a/gfx/layers/ipc/ImageContainerChild.cpp
+++ b/gfx/layers/ipc/ImageContainerChild.cpp
@@ -7,16 +7,18 @@
 #include "ImageContainerChild.h"
 #include "gfxSharedImageSurface.h"
 #include "ShadowLayers.h"
 #include "mozilla/layers/PLayers.h"
 #include "mozilla/layers/SharedImageUtils.h"
 #include "ImageContainer.h"
 #include "GonkIOSurfaceImage.h"
 #include "GrallocImages.h"
+#include "mozilla/ReentrantMonitor.h"
+#include "nsThreadUtils.h"
 
 namespace mozilla {
 namespace layers {
 
 /*
  * - POOL_MAX_SHARED_IMAGES is the maximum number number of shared images to
  * store in the ImageContainerChild's pool.
  *
@@ -34,16 +36,145 @@ namespace layers {
  * (see ImageToSharedImage)
  *
  * The values for the two constants are arbitrary and should be tweaked if it 
  * happens that we run into shared memory problems.
  */
 static const unsigned int POOL_MAX_SHARED_IMAGES = 5;
 static const unsigned int MAX_ACTIVE_SHARED_IMAGES = 10;
 
+/**
+ * A YCbCr image that stores its data in shared memory directly so that it's
+ * data can be sent to the compositor without being copied.
+ */
+class SharedPlanarYCbCrImage : public PlanarYCbCrImage
+{
+  friend class mozilla::layers::ImageContainerChild;
+public:
+  SharedPlanarYCbCrImage(ImageContainerChild* aProtocol)
+  : PlanarYCbCrImage(nullptr), mImageDataAllocator(aProtocol), mSent(false)
+  {
+    MOZ_COUNT_CTOR(SharedPlanarYCbCrImage);
+  }
+
+  ~SharedPlanarYCbCrImage()
+  {
+    if (mYSurface) {
+      SharedImage* sharedImg = new SharedImage();
+      ToSharedImage(sharedImg);
+      mImageDataAllocator->RecycleSharedImage(sharedImg);
+    }
+    MOZ_COUNT_DTOR(SharedPlanarYCbCrImage);
+  }
+
+  /**
+   * To prevent a SharedPlanarYCbCrImage from being sent several times we
+   * store a boolean state telling wether the image has already been sent to
+   * the compositor.
+   */
+  bool IsSent() const
+  {
+    return mSent;
+  }
+
+  SharedPlanarYCbCrImage* AsSharedPlanarYCbCrImage() MOZ_OVERRIDE
+  {
+    return this;
+  }
+
+  void ToSharedImage(SharedImage* aImage)
+  {
+    NS_ABORT_IF_FALSE(aImage, "aImage must be allocated");
+    *aImage = YUVImage(mYSurface->GetShmem(),
+                      mCbSurface->GetShmem(),
+                      mCrSurface->GetShmem(),
+                      mData.GetPictureRect(),
+                      reinterpret_cast<uintptr_t>(this));
+  }
+
+  /**
+   * Returns nullptr because in most cases the YCbCrImage has lost ownership of
+   * its data when this is called.
+   */
+  already_AddRefed<gfxASurface> GetAsSurface()
+  {
+    if (!mYSurface) {
+      return nullptr;
+    }
+    return PlanarYCbCrImage::GetAsSurface();
+  }
+
+  /**
+   * See PlanarYCbCrImage::SeData.
+   * This proxies a synchronous call to the ImageBridgeChild thread.
+   */
+  virtual void Allocate(Data& aData) MOZ_OVERRIDE
+  {
+    // copy aData into mData to have the sizes and strides info
+    // that will be needed by the ImageContainerChild to allocate
+    // shared buffers.
+    mData = aData;
+    // mImageDataAllocator will allocate the shared buffers and
+    // place them into 'this'
+    mImageDataAllocator->AllocateSharedBufferForImage(this);
+    // give aData the resulting buffers
+    aData.mYChannel  = mYSurface->Data();
+    aData.mCbChannel = mCbSurface->Data();
+    aData.mCrChannel = mCrSurface->Data();
+  }
+
+  /**
+   * See PlanarYCbCrImage::SeData.
+   */
+  virtual void SetData(const Data& aData)
+  {
+    // do not set mBuffer like in PlanarYCbCrImage because the later
+    // will try to manage this memory without knowing it belongs to a
+    // shmem.
+    mData = aData;
+    mBufferSize = mData.mCbCrStride * mData.mCbCrSize.height * 2 +
+                   mData.mYStride * mData.mYSize.height;
+    mSize = mData.mPicSize;
+
+    // If m*Surface has not been allocated (through Allocate(aData)), allocate it.
+    // This code path is slower than the one used when Allocate has been called
+    // since it will trigger a full copy.
+    if (!mYSurface) {
+      Data data = aData;
+      Allocate(data);
+    }
+    if (mYSurface->Data() != aData.mYChannel) {
+      ImageContainer::CopyPlane(mYSurface->Data(), aData.mYChannel,
+                                aData.mYSize,
+                                aData.mYStride, aData.mYSize.width,
+                                0,0);
+      ImageContainer::CopyPlane(mCbSurface->Data(), aData.mCbChannel,
+                                aData.mCbCrSize,
+                                aData.mCbCrStride, aData.mCbCrSize.width,
+                                0,0);
+      ImageContainer::CopyPlane(mCrSurface->Data(), aData.mCrChannel,
+                                aData.mCbCrSize,
+                                aData.mCbCrStride, aData.mCbCrSize.width,
+                                0,0);
+    }
+  }
+
+  void MarkAsSent() {
+    mSent = true;
+  }
+
+protected:
+  nsRefPtr<gfxSharedImageSurface> mYSurface;
+  nsRefPtr<gfxSharedImageSurface> mCbSurface;
+  nsRefPtr<gfxSharedImageSurface> mCrSurface;
+  nsRefPtr<ImageContainerChild>   mImageDataAllocator;
+  bool mSent;
+};
+
+
 ImageContainerChild::ImageContainerChild()
 : mImageContainerID(0), mActiveImageCount(0),
   mStop(false), mDispatchedDestroy(false) 
 {
   MOZ_COUNT_CTOR(ImageContainerChild);
   // the Release corresponding to this AddRef is in 
   // ImageBridgeChild::DeallocPImageContainer
   AddRef();
@@ -87,32 +218,40 @@ void ImageContainerChild::StopChildAndPa
   DispatchDestroy();
 }
 
 void ImageContainerChild::StopChild()
 {
   if (mStop) {
     return;
   }
-  mStop = true;    
+  mStop = true;
 
   DispatchDestroy();
 }
 
 bool ImageContainerChild::RecvReturnImage(const SharedImage& aImage)
 {
-  SharedImage* img = new SharedImage(aImage);
   // Remove oldest image from the queue.
   if (mImageQueue.Length() > 0) {
     mImageQueue.RemoveElementAt(0);
   }
-  if (!AddSharedImageToPool(img) || mStop) {
-    DestroySharedImage(*img);
-    delete img;
+
+  if (aImage.type() == SharedImage::TYUVImage &&
+      aImage.get_YUVImage().imageID() != 0) {
+    // if the imageID is non-zero, it means that this image's buffers
+    // are used in a SharedPlanarYCbCrImage that is maybe used in the
+    // main thread. In this case we let ref counting take care of 
+    // deciding when to recycle the shared memory.
+    return true;
   }
+
+  SharedImage* img = new SharedImage(aImage);
+  RecycleSharedImageNow(img);
+
   return true;
 }
 
 void ImageContainerChild::DestroySharedImage(const SharedImage& aImage)
 {
   NS_ABORT_IF_FALSE(InImageBridgeChildThread(),
                     "Should be in ImageBridgeChild thread.");
 
@@ -193,17 +332,18 @@ SharedImage* ImageContainerChild::Create
              data->mCrChannel + i * data->mCbCrStride,
              data->mCbCrSize.width);
     }
 
     SharedImage* result = new SharedImage( 
               YUVImage(tempBufferY->GetShmem(),
                        tempBufferU->GetShmem(),
                        tempBufferV->GetShmem(),
-                       data->GetPictureRect()));
+                       data->GetPictureRect(),
+                       0));
     NS_ABORT_IF_FALSE(result->type() == SharedImage::TYUVImage,
                       "SharedImage type not set correctly");
     return result;
 #ifdef MOZ_WIDGET_GONK
   } else if (image->GetFormat() == GONK_IO_SURFACE) {
     GonkIOSurfaceImage* gonkImage = static_cast<GonkIOSurfaceImage*>(image);
     SharedImage* result = new SharedImage(gonkImage->GetSurfaceDescriptor());
     return result;
@@ -316,24 +456,42 @@ public:
   nsRefPtr<Image> mImage;
 };
 
 SharedImage* ImageContainerChild::ImageToSharedImage(Image* aImage)
 {
   if (mStop) {
     return nullptr;
   }
+
+  NS_ABORT_IF_FALSE(InImageBridgeChildThread(),
+                    "Should be in ImageBridgeChild thread.");
+  if (aImage->GetFormat() == ImageFormat::PLANAR_YCBCR) {
+    SharedPlanarYCbCrImage* sharedYCbCr =
+      static_cast<PlanarYCbCrImage*>(aImage)->AsSharedPlanarYCbCrImage();
+    if (sharedYCbCr) {
+      if (sharedYCbCr->IsSent()) {
+        // don't send the same image twice
+        return nullptr;
+      }
+      // the image is already using shared memory, this means that we don't 
+      // need to copy it
+      SharedImage* result = new SharedImage();
+      sharedYCbCr->ToSharedImage(result);
+      sharedYCbCr->MarkAsSent();
+      return result;
+    }
+  }
+
   if (mActiveImageCount > (int)MAX_ACTIVE_SHARED_IMAGES) {
     // Too many active shared images, perhaps the compositor is hanging.
     // Skipping current image
     return nullptr;
   }
 
-  NS_ABORT_IF_FALSE(InImageBridgeChildThread(),
-                    "Should be in ImageBridgeChild thread.");
   SharedImage *img = GetSharedImageFor(aImage);
   if (img) {
     CopyDataIntoSharedImage(aImage, img);  
   } else {
     img = CreateSharedImageFromData(aImage);
   }
   // Keep a reference to the image we sent to compositor to maintain a
   // correct reference count.
@@ -389,11 +547,118 @@ void ImageContainerChild::DispatchDestro
     return;
   }
   mDispatchedDestroy = true;
   AddRef(); // corresponds to the Release in DestroyNow
   GetMessageLoop()->PostTask(FROM_HERE, 
                     NewRunnableMethod(this, &ImageContainerChild::DestroyNow));
 }
 
+
+already_AddRefed<Image> ImageContainerChild::CreateImage(const ImageFormat *aFormats,
+                                                         uint32_t aNumFormats)
+{
+  // TODO: Add more image formats
+  nsRefPtr<Image> img = new SharedPlanarYCbCrImage(this);
+  return img.forget();
+}
+
+void ImageContainerChild::AllocateSharedBufferForImageNow(Image* aImage)
+{
+  NS_ABORT_IF_FALSE(InImageBridgeChildThread(),
+                    "Should be in ImageBridgeChild thread.");
+  NS_ABORT_IF_FALSE(aImage && aImage->GetFormat() == ImageFormat::PLANAR_YCBCR,
+                    "Only YUV images supported now");
+
+  SharedPlanarYCbCrImage* sharedYCbCr =
+    static_cast<PlanarYCbCrImage*>(aImage)->AsSharedPlanarYCbCrImage();
+
+  // try to reuse shared images from the pool first...
+  SharedImage* fromPool = GetSharedImageFor(aImage);
+  if (fromPool) {
+    YUVImage yuv = fromPool->get_YUVImage();
+    nsRefPtr<gfxSharedImageSurface> surfY =
+      gfxSharedImageSurface::Open(yuv.Ydata());
+    nsRefPtr<gfxSharedImageSurface> surfU =
+      gfxSharedImageSurface::Open(yuv.Udata());
+    nsRefPtr<gfxSharedImageSurface> surfV =
+      gfxSharedImageSurface::Open(yuv.Vdata());
+    sharedYCbCr->mYSurface  = surfY;
+    sharedYCbCr->mCbSurface = surfU;
+    sharedYCbCr->mCrSurface = surfV;
+  } else {
+    // ...else Allocate a shared image
+    nsRefPtr<gfxSharedImageSurface> surfY;
+    nsRefPtr<gfxSharedImageSurface> surfU;
+    nsRefPtr<gfxSharedImageSurface> surfV;
+    const PlanarYCbCrImage::Data* data = sharedYCbCr->GetData();
+    if (!AllocateSharedBuffer(this, data->mYSize, gfxASurface::CONTENT_ALPHA,
+                              getter_AddRefs(surfY)) ||
+        !AllocateSharedBuffer(this, data->mCbCrSize, gfxASurface::CONTENT_ALPHA,
+                              getter_AddRefs(surfU)) ||
+        !AllocateSharedBuffer(this, data->mCbCrSize, gfxASurface::CONTENT_ALPHA,
+                              getter_AddRefs(surfV))) {
+      NS_RUNTIMEABORT("creating SharedImage failed!");
+    }
+    sharedYCbCr->mYSurface  = surfY;
+    sharedYCbCr->mCbSurface = surfU;
+    sharedYCbCr->mCrSurface = surfV;
+  }
+
+}
+
+void ImageContainerChild::AllocateSharedBufferForImageSync(ReentrantMonitor* aBarrier,
+                                                           bool* aDone,
+                                                           Image* aImage)
+{
+  AllocateSharedBufferForImageNow(aImage);
+  ReentrantMonitorAutoEnter autoMon(*aBarrier);
+  *aDone = true;
+  aBarrier->NotifyAll();
+}
+
+
+void ImageContainerChild::AllocateSharedBufferForImage(Image* aImage)
+{
+  
+  if (InImageBridgeChildThread()) {
+    AllocateSharedBufferForImageNow(aImage);
+    return;
+  }
+
+  bool done = false;
+  ReentrantMonitor barrier("ImageBridgeChild::AllocateSharedBufferForImage");
+  ReentrantMonitorAutoEnter autoMon(barrier);
+  GetMessageLoop()->PostTask(FROM_HERE, 
+                             NewRunnableMethod(this,
+                                               &ImageContainerChild::AllocateSharedBufferForImageSync,
+                                               &barrier,
+                                               &done,
+                                               aImage));
+  while (!done) {
+    barrier.Wait();
+  }
+}
+
+void ImageContainerChild::RecycleSharedImageNow(SharedImage* aImage)
+{
+  NS_ABORT_IF_FALSE(InImageBridgeChildThread(),"Must be in the ImageBridgeChild Thread.");
+  
+  if (mStop || !AddSharedImageToPool(aImage)) {
+    DestroySharedImage(*aImage);
+    delete aImage;
+  }
+}
+
+void ImageContainerChild::RecycleSharedImage(SharedImage* aImage)
+{
+  if (InImageBridgeChildThread()) {
+    RecycleSharedImageNow(aImage);
+    return;
+  }
+  GetMessageLoop()->PostTask(FROM_HERE, 
+                             NewRunnableMethod(this,
+                                               &ImageContainerChild::RecycleSharedImageNow,
+                                               aImage));
+}
+
 } // namespace
 } // namespace
-
--- a/gfx/layers/ipc/ImageContainerChild.h
+++ b/gfx/layers/ipc/ImageContainerChild.h
@@ -3,18 +3,22 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_GFX_IMAGECONTAINERCHILD_H
 #define MOZILLA_GFX_IMAGECONTAINERCHILD_H
 
 #include "mozilla/layers/PImageContainerChild.h"
 #include "mozilla/layers/ImageBridgeChild.h"
+#include "ImageTypes.h"
 
 namespace mozilla {
+
+class ReentrantMonitor;
+
 namespace layers {
 
 class ImageBridgeCopyAndSendTask;
 class ImageContainer;
 
 /**
  * ImageContainerChild is the content-side part of the ImageBridge IPDL protocol
  * acting at the ImageContainer level.
@@ -110,17 +114,42 @@ public:
    * Can be called on any thread.
    */
   void DispatchSetIdle();
 
   /**
    * Must be called on the ImageBridgeChild's thread.
    */
   void SetIdleNow();
-  
+
+  /**
+   * Returns an instance of Image that should be of a Shared* subclass of Image
+   * and that should not need to be copied in order to be sent to the compositor.
+   */
+  already_AddRefed<Image> CreateImage(const ImageFormat *aFormats,
+                                      uint32_t aNumFormats);
+
+  /**
+   * Allocate shared buffers for the image.
+   *
+   * Can be called from any thread. If not called from the ImageBridge thread,
+   * it will synchronously proxy the call to the ImageBirdgeThread since shared
+   * memory can only be allocated in the IPDL thread. 
+   */
+  void AllocateSharedBufferForImage(Image* aImage);
+
+  /**
+   * Can be called from any thread.
+   * Deallocates or places aImage in the pool.
+   * If this method is not called from the ImageBridgeChild thread,
+   * a task is dispatched and the recycling is done asynchronously on
+   * the ImageBridgeChild thread.
+   */
+  void RecycleSharedImage(SharedImage* aImage);
+
 protected:
   virtual PGrallocBufferChild*
   AllocPGrallocBuffer(const gfxIntSize&, const gfxContentType&,
                       MaybeMagicGrallocBufferHandle*) MOZ_OVERRIDE
 
   { return nullptr; }
 
   virtual bool
@@ -183,16 +212,38 @@ protected:
    */
   bool CopyDataIntoSharedImage(Image* src, SharedImage* dest);
 
   /**
    * Allocates a SharedImage and copy aImage's data into it.
    * Called by ImageToSharedImage.
    */
   SharedImage * CreateSharedImageFromData(Image* aImage);
+  /**
+   * Allocate shared buffers for the image.
+   * Must be called on the ImageBridgeChild thread.
+   */
+  void AllocateSharedBufferForImageNow(Image* aImage);
+
+  /**
+   * Calls AllocateSharedBufferForImageNow and notify the barrier monitor.
+   * This is meant to be called by AllocateSharedBufferForImage if the
+   * call must be proxied synchronously to the ImageBridgeChild's thread.
+   */
+  void AllocateSharedBufferForImageSync(ReentrantMonitor* aBarrier,
+                                        bool* aDone,
+                                        Image* aImage);
+
+  /**
+   * Either deallocate or place aImage in the shared image pool.
+   * Must be called on the ImageBridgeChild thread.
+   * aImage should not be used after this method is called.
+   */
+  void RecycleSharedImageNow(SharedImage* aImage);
+
 
 private:
   uint64_t mImageContainerID;
   nsTArray<SharedImage*> mSharedImagePool;
 
   /**
    * Save a reference to the outgoing images and remove the reference
    * once the image is returned from the compositor.
--- a/gfx/layers/ipc/LayersSurfaces.ipdlh
+++ b/gfx/layers/ipc/LayersSurfaces.ipdlh
@@ -68,16 +68,24 @@ union SurfaceDescriptor {
   SharedTextureDescriptor;
 };
 
 struct YUVImage {
   Shmem Ydata;
   Shmem Udata;
   Shmem Vdata;
   nsIntRect picture;
+  /**
+   * An ID meant to tell if the shared buffers are being reference counted
+   * on the content side and making it possible to find the reference counted
+   * object on the content side if needed.
+   * If this ID is zero, it means the data is not reference counted and can 
+   * be recycled right after being sent back to the content side.
+   */
+  uintptr_t imageID;
 };
 
 union SharedImage {
   SurfaceDescriptor;
   YUVImage;
   SharedImageID;
   null_t;
 };