Bug 649417 - Part 2 - Add a YUV option to SharedImage and use it to share YUV data across processes. r=cjones,joe
authorMatt Woodrow <mwoodrow@mozilla.com>
Thu, 21 Apr 2011 16:38:39 +1200
changeset 69407 dd803c39a72b51c67a45309dfe75cd33586ae7dc
parent 69406 8ca0d7969d3d422a5d618218fb6b0a37e01ab55a
child 69408 a66c328760f5e2cff49d3ff705d7beecd951a82a
push idunknown
push userunknown
push dateunknown
reviewerscjones, joe
bugs649417
milestone6.0a1
Bug 649417 - Part 2 - Add a YUV option to SharedImage and use it to share YUV data across processes. r=cjones,joe
content/html/content/src/nsHTMLMediaElement.cpp
gfx/layers/ImageLayers.h
gfx/layers/basic/BasicImages.cpp
gfx/layers/basic/BasicLayers.cpp
gfx/layers/ipc/PLayers.ipdl
gfx/layers/ipc/ShadowLayers.cpp
gfx/layers/ipc/ShadowLayers.h
gfx/layers/ipc/ShadowLayersParent.cpp
gfx/layers/opengl/ImageLayerOGL.cpp
gfx/layers/opengl/ImageLayerOGL.h
gfx/layers/opengl/LayerManagerOGL.cpp
--- a/content/html/content/src/nsHTMLMediaElement.cpp
+++ b/content/html/content/src/nsHTMLMediaElement.cpp
@@ -2229,16 +2229,19 @@ ImageContainer* nsHTMLMediaElement::GetI
     return nsnull;
 
   nsRefPtr<LayerManager> manager =
     nsContentUtils::PersistentLayerManagerForDocument(GetOwnerDoc());
   if (!manager)
     return nsnull;
 
   mImageContainer = manager->CreateImageContainer();
+  if (manager->IsCompositingCheap()) {
+    mImageContainer->SetDelayedConversion(PR_TRUE);
+  }
   return mImageContainer;
 }
 
 nsresult nsHTMLMediaElement::DispatchAudioAvailableEvent(float* aFrameBuffer,
                                                          PRUint32 aFrameBufferLength,
                                                          float aTime)
 {
   // Auto manage the memory for the frame buffer. If we fail and return
--- a/gfx/layers/ImageLayers.h
+++ b/gfx/layers/ImageLayers.h
@@ -158,16 +158,22 @@ public:
    * Can be called on any thread. This method takes mReentrantMonitor
    * when accessing thread-shared state.
    * 
    * The Image data must not be modified after this method is called!
    */
   virtual void SetCurrentImage(Image* aImage) = 0;
 
   /**
+   * Ask any PlanarYCbCr images created by this container to delay
+   * YUV -> RGB conversion until draw time. See PlanarYCbCrImage::SetDelayedConversion.
+   */
+  virtual void SetDelayedConversion(PRBool aDelayed) {}
+
+  /**
    * Get the current Image.
    * This has to add a reference since otherwise there are race conditions
    * where the current image is destroyed before the caller can add
    * a reference.
    * Can be called on any thread. This method takes mReentrantMonitor
    * when accessing thread-shared state.
    * Implementations must call CurrentImageChanged() while holding
    * mReentrantMonitor.
@@ -410,16 +416,28 @@ public:
   /**
    * This makes a copy of the data buffers.
    * XXX Eventually we will change this to not make a copy of the data,
    * Right now it doesn't matter because the BasicLayer implementation
    * does YCbCr conversion here anyway.
    */
   virtual void SetData(const Data& aData) = 0;
 
+  /**
+   * 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(PRBool aDelayed) { }
+
+  /**
+   * Grab the original YUV data. This is optional.
+   */
+  virtual const Data* GetData() { return nsnull; }
+
 protected:
   PlanarYCbCrImage(void* aImplData) : Image(aImplData, PLANAR_YCBCR) {}
 };
 
 /**
  * Currently, the data in a CairoImage surface is treated as being in the
  * device output color space.
  */
--- a/gfx/layers/basic/BasicImages.cpp
+++ b/gfx/layers/basic/BasicImages.cpp
@@ -110,62 +110,125 @@ class BasicPlanarYCbCrImage : public Pla
 public:
    /** 
     * aScaleHint is a size that the image is expected to be rendered at.
     * This is a hint for image backends to optimize scaling.
     */
   BasicPlanarYCbCrImage(const gfxIntSize& aScaleHint) :
     PlanarYCbCrImage(static_cast<BasicImageImplData*>(this)),
     mScaleHint(aScaleHint),
-    mOffscreenFormat(gfxASurface::ImageFormatUnknown)
+    mOffscreenFormat(gfxASurface::ImageFormatUnknown),
+    mDelayedConversion(PR_FALSE)
     {}
 
   virtual void SetData(const Data& aData);
+  virtual void SetDelayedConversion(PRBool aDelayed) { mDelayedConversion = aDelayed; }
 
   virtual already_AddRefed<gfxASurface> GetAsSurface();
 
+  const Data* GetData() { return &mData; }
+
   void SetOffscreenFormat(gfxImageFormat aFormat) { mOffscreenFormat = aFormat; }
   gfxImageFormat GetOffscreenFormat() { return mOffscreenFormat; }
 
 protected:
   nsAutoArrayPtr<PRUint8>              mBuffer;
   nsCountedRef<nsMainThreadSurfaceRef> mSurface;
   gfxIntSize                           mScaleHint;
   PRInt32                              mStride;
   gfxImageFormat                       mOffscreenFormat;
+  Data                                 mData;
+  PRUint32                             mBufferSize;
+  PRPackedBool                         mDelayedConversion;
 };
 
 void
 BasicPlanarYCbCrImage::SetData(const Data& aData)
 {
   // Do some sanity checks to prevent integer overflow
   if (aData.mYSize.width > 16384 || aData.mYSize.height > 16384) {
     NS_ERROR("Illegal width or height");
     return;
   }
-
-  gfxASurface::gfxImageFormat format = GetOffscreenFormat();
-
+  
   gfx::YUVType type = gfx::YV12;
+  int width_shift = 0;
+  int height_shift = 0;
   if (aData.mYSize.width == aData.mCbCrSize.width &&
       aData.mYSize.height == aData.mCbCrSize.height) {
     type = gfx::YV24;
+    width_shift = 0;
+    height_shift = 0;
   }
   else if (aData.mYSize.width / 2 == aData.mCbCrSize.width &&
            aData.mYSize.height == aData.mCbCrSize.height) {
     type = gfx::YV16;
+    width_shift = 1;
+    height_shift = 0;
   }
   else if (aData.mYSize.width / 2 == aData.mCbCrSize.width &&
            aData.mYSize.height / 2 == aData.mCbCrSize.height ) {
     type = gfx::YV12;
+    width_shift = 1;
+    height_shift = 1;
   }
   else {
     NS_ERROR("YCbCr format not supported");
   }
 
+  if (mDelayedConversion) {
+
+    mData = aData;
+    mData.mCbCrStride = mData.mCbCrSize.width = aData.mPicSize.width >> width_shift;
+    // Round up the values for width and height to make sure we sample enough data
+    // for the last pixel - See bug 590735
+    if (width_shift && (aData.mPicSize.width & 1)) {
+      mData.mCbCrStride++;
+      mData.mCbCrSize.width++;
+    }
+    mData.mCbCrSize.height = aData.mPicSize.height >> height_shift;
+    if (height_shift && (aData.mPicSize.height & 1)) {
+        mData.mCbCrSize.height++;
+    }
+    mData.mYSize = aData.mPicSize;
+    mData.mYStride = mData.mYSize.width;
+    mBufferSize = mData.mCbCrStride * mData.mCbCrSize.height * 2 +
+                  mData.mYStride * mData.mYSize.height;
+    mBuffer = new PRUint8[mBufferSize];
+    
+    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;
+
+    for (int i = 0; i < mData.mYSize.height; i++) {
+      memcpy(mData.mYChannel + i * mData.mYStride,
+             aData.mYChannel + ((aData.mPicY + i) * aData.mYStride) + aData.mPicX,
+             mData.mYStride);
+    }
+    for (int i = 0; i < mData.mCbCrSize.height; i++) {
+      memcpy(mData.mCbChannel + i * mData.mCbCrStride,
+             aData.mCbChannel + ((cbcr_y + i) * aData.mCbCrStride) + cbcr_x,
+             mData.mCbCrStride);
+    }
+    for (int i = 0; i < mData.mCbCrSize.height; i++) {
+      memcpy(mData.mCrChannel + i * mData.mCbCrStride,
+             aData.mCrChannel + ((cbcr_y + i) * aData.mCbCrStride) + cbcr_x,
+             mData.mCbCrStride);
+    }
+
+    // Fix picture rect to be correct
+    mData.mPicX = mData.mPicY = 0;
+    mSize = aData.mPicSize;
+    return;
+  }
+
+  gfxASurface::gfxImageFormat format = GetOffscreenFormat();
+
   // 'prescale' is true if the scaling is to be done as part of the
   // YCbCr to RGB conversion rather than on the RGB data when rendered.
   PRBool prescale = mScaleHint.width > 0 && mScaleHint.height > 0 &&
                     mScaleHint != aData.mPicSize;
   if (format == gfxASurface::ImageFormatRGB16_565) {
 #if defined(HAVE_YCBCR_TO_RGB565)
     if (prescale &&
         !gfx::IsScaleYCbCrToRGB565Fast(aData.mPicX,
@@ -291,17 +354,19 @@ BasicPlanarYCbCrImage::GetAsSurface()
 {
   NS_ASSERTION(NS_IsMainThread(), "Must be main thread");
 
   if (mSurface) {
     nsRefPtr<gfxASurface> result = mSurface.get();
     return result.forget();
   }
 
-  if (!mBuffer) {
+  // XXX: If we forced delayed conversion, are we ever going to hit this?
+  // We may need to implement the conversion here.
+  if (!mBuffer || mDelayedConversion) {
     return nsnull;
   }
 
   gfxASurface::gfxImageFormat format = GetOffscreenFormat();
 
   nsRefPtr<gfxImageSurface> imgSurface =
       new gfxImageSurface(mBuffer, mSize, mStride, format);
   if (!imgSurface || imgSurface->CairoStatus() != 0) {
@@ -331,33 +396,36 @@ BasicPlanarYCbCrImage::GetAsSurface()
  */
 class BasicImageContainer : public ImageContainer {
 public:
   typedef gfxASurface::gfxImageFormat gfxImageFormat;
 
   BasicImageContainer() :
     ImageContainer(nsnull),
     mScaleHint(-1, -1),
-    mOffscreenFormat(gfxASurface::ImageFormatUnknown)
+    mOffscreenFormat(gfxASurface::ImageFormatUnknown),
+    mDelayed(PR_FALSE)
   {}
   virtual already_AddRefed<Image> CreateImage(const Image::Format* aFormats,
                                               PRUint32 aNumFormats);
+  virtual void SetDelayedConversion(PRBool aDelayed) { mDelayed = aDelayed; }
   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);
   virtual void SetScaleHint(const gfxIntSize& aScaleHint);
   void SetOffscreenFormat(gfxImageFormat aFormat) { mOffscreenFormat = aFormat; }
   virtual LayerManager::LayersBackend GetBackendType() { return LayerManager::LAYERS_BASIC; }
 
 protected:
   nsRefPtr<Image> mImage;
   gfxIntSize mScaleHint;
   gfxImageFormat mOffscreenFormat;
+  PRPackedBool mDelayed;
 };
 
 /**
  * Returns true if aFormat is in the given format array.
  */
 static PRBool
 FormatInList(const Image::Format* aFormats, PRUint32 aNumFormats,
              Image::Format aFormat)
@@ -377,16 +445,17 @@ BasicImageContainer::CreateImage(const I
   nsRefPtr<Image> image;
   // Prefer cairo surfaces because they're native for us
   if (FormatInList(aFormats, aNumFormats, Image::CAIRO_SURFACE)) {
     image = new BasicCairoImage();
   } else if (FormatInList(aFormats, aNumFormats, Image::PLANAR_YCBCR)) {
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     image = new BasicPlanarYCbCrImage(mScaleHint);
     static_cast<BasicPlanarYCbCrImage*>(image.get())->SetOffscreenFormat(mOffscreenFormat);
+    static_cast<BasicPlanarYCbCrImage*>(image.get())->SetDelayedConversion(mDelayed);
   }
   return image.forget();
 }
 
 void
 BasicImageContainer::SetCurrentImage(Image* aImage)
 {
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
--- a/gfx/layers/basic/BasicLayers.cpp
+++ b/gfx/layers/basic/BasicLayers.cpp
@@ -1626,16 +1626,23 @@ public:
     NS_ABORT_IF_FALSE(!mShadow, "can't have two shadows (yet)");
     mShadow = aShadow;
   }
 
   virtual void SetBackBuffer(const SurfaceDescriptor& aBuffer)
   {
     NS_RUNTIMEABORT("if this default impl is called, |aBuffer| leaks");
   }
+  
+  virtual void SetBackBufferYUVImage(gfxSharedImageSurface* aYBuffer,
+                                     gfxSharedImageSurface* aUBuffer,
+                                     gfxSharedImageSurface* aVBuffer)
+  {
+    NS_RUNTIMEABORT("if this default impl is called, |aBuffer| leaks");
+  }
 
   virtual void Disconnect()
   {
     // This is an "emergency Disconnect()", called when the compositing
     // process has died.  |mShadow| and our Shmem buffers are
     // automatically managed by IPDL, so we don't need to explicitly
     // free them here (it's hard to get that right on emergency
     // shutdown anyway).
@@ -1960,34 +1967,119 @@ public:
   virtual Layer* AsLayer() { return this; }
   virtual ShadowableLayer* AsShadowableLayer() { return this; }
 
   virtual void SetBackBuffer(const SurfaceDescriptor& aBuffer)
   {
     mBackBuffer = aBuffer;
   }
 
+  virtual void SetBackBufferYUVImage(gfxSharedImageSurface* aYBuffer,
+                                     gfxSharedImageSurface* aUBuffer,
+                                     gfxSharedImageSurface* aVBuffer)
+  {
+    mBackBufferY = aYBuffer;
+    mBackBufferU = aUBuffer;
+    mBackBufferV = aVBuffer;
+  }
+
   virtual void Disconnect()
   {
     mBackBuffer = SurfaceDescriptor();
     BasicShadowableLayer::Disconnect();
   }
 
 private:
   BasicShadowLayerManager* BasicManager()
   {
     return static_cast<BasicShadowLayerManager*>(mManager);
   }
 
+  // For YUV Images these are the 3 planes (Y, Cb and Cr),
+  // for RGB images only mBackSurface is used.
   SurfaceDescriptor mBackBuffer;
+  nsRefPtr<gfxSharedImageSurface> mBackBufferY;
+  nsRefPtr<gfxSharedImageSurface> mBackBufferU;
+  nsRefPtr<gfxSharedImageSurface> mBackBufferV;
+  gfxIntSize mCbCrSize;
 };
  
 void
 BasicShadowableImageLayer::Paint(gfxContext* aContext)
 {
+  nsRefPtr<Image> image = mContainer->GetCurrentImage();
+
+  if (image->GetFormat() == Image::PLANAR_YCBCR && BasicManager()->IsCompositingCheap()) {
+    PlanarYCbCrImage *YCbCrImage = static_cast<PlanarYCbCrImage*>(image.get());
+    const PlanarYCbCrImage::Data *data = YCbCrImage->GetData();
+    NS_ASSERTION(data, "Must be able to retrieve yuv data from image!");
+
+    if (mSize != data->mYSize || mCbCrSize != data->mCbCrSize) {
+
+      if (mBackBufferY) {
+        BasicManager()->ShadowLayerForwarder::DestroySharedSurface(mBackBufferY);
+        BasicManager()->ShadowLayerForwarder::DestroySharedSurface(mBackBufferU);
+        BasicManager()->ShadowLayerForwarder::DestroySharedSurface(mBackBufferV);
+        BasicManager()->DestroyedImageBuffer(BasicManager()->Hold(this));
+      }
+      mSize = data->mYSize;
+      mCbCrSize = data->mCbCrSize;
+
+      nsRefPtr<gfxSharedImageSurface> tmpYSurface;
+      nsRefPtr<gfxSharedImageSurface> tmpUSurface;
+      nsRefPtr<gfxSharedImageSurface> tmpVSurface;
+
+      if (!BasicManager()->AllocDoubleBuffer(
+            mSize,
+            gfxASurface::CONTENT_ALPHA,
+            getter_AddRefs(tmpYSurface), getter_AddRefs(mBackBufferY)))
+        NS_RUNTIMEABORT("creating ImageLayer 'front buffer' failed!");
+      
+      if (!BasicManager()->AllocDoubleBuffer(
+            mCbCrSize,
+            gfxASurface::CONTENT_ALPHA,
+            getter_AddRefs(tmpUSurface), getter_AddRefs(mBackBufferU)))
+        NS_RUNTIMEABORT("creating ImageLayer 'front buffer' failed!");
+      
+      if (!BasicManager()->AllocDoubleBuffer(
+            mCbCrSize,
+            gfxASurface::CONTENT_ALPHA,
+            getter_AddRefs(tmpVSurface), getter_AddRefs(mBackBufferV)))
+        NS_RUNTIMEABORT("creating ImageLayer 'front buffer' failed!");
+
+      YUVImage yuv(tmpYSurface->GetShmem(),
+                   tmpUSurface->GetShmem(),
+                   tmpVSurface->GetShmem());
+
+      BasicManager()->CreatedImageBuffer(BasicManager()->Hold(this),
+                                         nsIntSize(mSize.width, mSize.height),
+                                         yuv);
+
+    }
+      
+    memcpy(mBackBufferY->Data(), 
+           data->mYChannel, 
+           data->mYStride * mSize.height);
+    memcpy(mBackBufferU->Data(), 
+           data->mCbChannel, 
+           data->mCbCrStride * mCbCrSize.height);
+    memcpy(mBackBufferV->Data(), 
+           data->mCrChannel, 
+           data->mCbCrStride * mCbCrSize.height);
+      
+    YUVImage yuv(mBackBufferY->GetShmem(),
+                 mBackBufferU->GetShmem(),
+                 mBackBufferV->GetShmem());
+  
+    BasicManager()->PaintedImage(BasicManager()->Hold(this),
+                                 yuv);
+
+    return;
+  }
+
   gfxIntSize oldSize = mSize;
   nsRefPtr<gfxPattern> pat = GetAndPaintCurrentImage(aContext, GetEffectiveOpacity());
   if (!pat || !HasShadow())
     return;
 
   if (oldSize != mSize) {
     if (IsSurfaceDescriptorValid(mBackBuffer)) {
       BasicManager()->DestroyedImageBuffer(BasicManager()->Hold(this));
@@ -2820,16 +2912,36 @@ BasicShadowLayerManager::ForwardTransact
         MOZ_LAYERS_LOG(("[LayersForwarder] BufferSwap"));
 
         const OpBufferSwap& obs = reply.get_OpBufferSwap();
         const SurfaceDescriptor& descr = obs.newBackBuffer();
         GetBasicShadowable(obs)->SetBackBuffer(descr);
         break;
       }
 
+      case EditReply::TOpImageSwap: {
+        MOZ_LAYERS_LOG(("[LayersForwarder] YUVBufferSwap"));
+
+        const OpImageSwap& ois = reply.get_OpImageSwap();
+        BasicShadowableLayer* layer = GetBasicShadowable(ois);
+        const SharedImage& newBack = ois.newBackImage();
+
+        if (newBack.type() == SharedImage::TSurfaceDescriptor) {
+          layer->SetBackBuffer(newBack.get_SurfaceDescriptor());
+        } else {
+          const YUVImage& yuv = newBack.get_YUVImage();
+          nsRefPtr<gfxSharedImageSurface> YSurf = gfxSharedImageSurface::Open(yuv.Ydata());
+          nsRefPtr<gfxSharedImageSurface> USurf = gfxSharedImageSurface::Open(yuv.Udata());
+          nsRefPtr<gfxSharedImageSurface> VSurf = gfxSharedImageSurface::Open(yuv.Vdata());
+          layer->SetBackBufferYUVImage(YSurf, USurf, VSurf);
+        }
+        
+        break;
+      }
+
       default:
         NS_RUNTIMEABORT("not reached");
       }
     }
   } else if (HasShadowManager()) {
     NS_WARNING("failed to forward Layers transaction");
   }
 
--- a/gfx/layers/ipc/PLayers.ipdl
+++ b/gfx/layers/ipc/PLayers.ipdl
@@ -78,18 +78,25 @@ struct OpCreateCanvasLayer     { PLayer 
 // buffer that only contains (transparent) black pixels just so that
 // we can swap it back after the first OpPaint without a special case.
 
 union SurfaceDescriptor {
   Shmem;
   SurfaceDescriptorX11;
 };
 
+struct YUVImage {
+  Shmem Ydata;
+  Shmem Udata;
+  Shmem Vdata;
+};
+
 union SharedImage {
   SurfaceDescriptor;
+  YUVImage;
 };
 
 struct ThebesBuffer {
   SurfaceDescriptor buffer;
   nsIntRect rect;
   nsIntPoint rotation; 
 };
 union OptionalThebesBuffer { ThebesBuffer; null_t; };
@@ -211,16 +218,18 @@ union Edit {
   OpPaintCanvas;
   OpPaintImage;
 };
 
 
 // Replies to operations
 struct OpBufferSwap   { PLayer layer; SurfaceDescriptor newBackBuffer; };
 
+struct OpImageSwap { PLayer layer; SharedImage newBackImage; };
+
 struct OpThebesBufferSwap {
   PLayer layer;
   ThebesBuffer newBackBuffer;
   nsIntRegion newValidRegion;
   float newXResolution;
   float newYResolution;
   // If the parent took the child's old back buffer and returned its
   // old front buffer, |readOnlyFrontBuffer| may (if non-null) contain
@@ -232,16 +241,17 @@ struct OpThebesBufferSwap {
   nsIntRegion frontUpdatedRegion;
 };
 
 // Unit of a "changeset reply".  This is a weird abstraction, probably
 // only to be used for buffer swapping.
 union EditReply {
   OpBufferSwap;
   OpThebesBufferSwap;
+  OpImageSwap;
 };
 
 
 sync protocol PLayers {
   manager PRenderFrame /*or PCompositor or PMedia or PPlugin*/;
   manages PLayer;
 
 parent:
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -202,21 +202,21 @@ ShadowLayerForwarder::CreatedThebesBuffe
                                      aFrontValidRegion,
                                      aXResolution,
                                      aYResolution));
 }
 
 void
 ShadowLayerForwarder::CreatedImageBuffer(ShadowableLayer* aImage,
                                          nsIntSize aSize,
-                                         const SurfaceDescriptor& aTempFrontSurface)
+                                         const SharedImage& aTempFrontImage)
 {
   mTxn->AddEdit(OpCreateImageBuffer(NULL, Shadow(aImage),
                                     aSize,
-                                    aTempFrontSurface));
+                                    aTempFrontImage));
 }
 
 void
 ShadowLayerForwarder::CreatedCanvasBuffer(ShadowableLayer* aCanvas,
                                           nsIntSize aSize,
                                           const SurfaceDescriptor& aTempFrontSurface)
 {
   mTxn->AddEdit(OpCreateCanvasBuffer(NULL, Shadow(aCanvas),
@@ -286,20 +286,20 @@ ShadowLayerForwarder::PaintedThebesBuffe
   mTxn->AddPaint(OpPaintThebesBuffer(NULL, Shadow(aThebes),
                                      ThebesBuffer(aNewFrontBuffer,
                                                   aBufferRect,
                                                   aBufferRotation),
                                      aUpdatedRegion));
 }
 void
 ShadowLayerForwarder::PaintedImage(ShadowableLayer* aImage,
-                                   const SurfaceDescriptor& aNewFrontSurface)
+                                   const SharedImage& aNewFrontImage)
 {
   mTxn->AddPaint(OpPaintImage(NULL, Shadow(aImage),
-                              aNewFrontSurface));
+                              aNewFrontImage));
 }
 void
 ShadowLayerForwarder::PaintedCanvas(ShadowableLayer* aCanvas,
                                     const SurfaceDescriptor& aNewFrontSurface)
 {
   mTxn->AddPaint(OpPaintCanvas(NULL, Shadow(aCanvas),
                                aNewFrontSurface));
 }
--- a/gfx/layers/ipc/ShadowLayers.h
+++ b/gfx/layers/ipc/ShadowLayers.h
@@ -161,17 +161,17 @@ public:
                            const nsIntRect& aBufferRect,
                            const SurfaceDescriptor& aInitialFrontBuffer);
   /**
    * For the next two methods, |aSize| is the size of
    * |aInitialFrontSurface|.
    */
   void CreatedImageBuffer(ShadowableLayer* aImage,
                           nsIntSize aSize,
-                          const SurfaceDescriptor& aInitialFrontSurface);
+                          const SharedImage& aInitialFrontImage);
   void CreatedCanvasBuffer(ShadowableLayer* aCanvas,
                            nsIntSize aSize,
                            const SurfaceDescriptor& aInitialFrontSurface);
 
   /**
    * The specified layer is destroying its buffers.
    * |aBackBufferToDestroy| is deallocated when this transaction is
    * posted to the parent.  During the parent-side transaction, the
@@ -221,17 +221,17 @@ public:
                            const nsIntRect& aBufferRect,
                            const nsIntPoint& aBufferRotation,
                            const SurfaceDescriptor& aNewFrontBuffer);
   /**
    * NB: this initial implementation only forwards RGBA data for
    * ImageLayers.  This is slow, and will be optimized.
    */
   void PaintedImage(ShadowableLayer* aImage,
-                    const SurfaceDescriptor& aNewFrontSurface);
+                    const SharedImage& aNewFrontImage);
   void PaintedCanvas(ShadowableLayer* aCanvas,
                      const SurfaceDescriptor& aNewFrontSurface);
 
   /**
    * End the current transaction and forward it to ShadowLayerManager.
    * |aReplies| are directions from the ShadowLayerManager to the
    * caller of EndTransaction().
    */
--- a/gfx/layers/ipc/ShadowLayersParent.cpp
+++ b/gfx/layers/ipc/ShadowLayersParent.cpp
@@ -433,18 +433,18 @@ ShadowLayersParent::RecvUpdate(const Inf
 
       SurfaceDescriptor newFront = op.newFrontBuffer();
       SharedImage newBack;
       image->Swap(op.newFrontBuffer(), &newBack);
       if (newFront == newBack.get_SurfaceDescriptor()) {
         newFront = SurfaceDescriptor();
       }
 
-      replyv.push_back(OpBufferSwap(shadow, NULL,
-                                    newBack));
+      replyv.push_back(OpImageSwap(shadow, NULL,
+                                   newBack));
 
       break;
     }
 
     default:
       NS_RUNTIMEABORT("not reached");
     }
   }
--- a/gfx/layers/opengl/ImageLayerOGL.cpp
+++ b/gfx/layers/opengl/ImageLayerOGL.cpp
@@ -415,17 +415,17 @@ ImageLayerOGL::RenderLayer(int,
     gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, yuvImage->mTextures[0].GetTextureID());
     ApplyFilter(mFilter);
     gl()->fActiveTexture(LOCAL_GL_TEXTURE1);
     gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, yuvImage->mTextures[1].GetTextureID());
     ApplyFilter(mFilter);
     gl()->fActiveTexture(LOCAL_GL_TEXTURE2);
     gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, yuvImage->mTextures[2].GetTextureID());
     ApplyFilter(mFilter);
-
+    
     YCbCrTextureLayerProgram *program = mOGLManager->GetYCbCrLayerProgram();
 
     program->Activate();
     program->SetLayerQuadRect(nsIntRect(0, 0,
                                         yuvImage->mSize.width,
                                         yuvImage->mSize.height));
     program->SetLayerTransform(GetEffectiveTransform());
     program->SetLayerOpacity(GetEffectiveOpacity());
@@ -664,78 +664,87 @@ PlanarYCbCrImageOGL::AllocateTextures(mo
 
   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::UpdateTextures(GLContext *gl)
+static void
+UploadYUVToTexture(GLContext* gl, const PlanarYCbCrImage::Data& aData, 
+                   GLTexture* aYTexture,
+                   GLTexture* aUTexture,
+                   GLTexture* aVTexture)
 {
-  if (!mBuffer || !mHasData)
-    return;
-
   GLint alignment;
 
-  if (!((ptrdiff_t)mData.mYStride & 0x7) && !((ptrdiff_t)mData.mYChannel & 0x7)) {
+  if (!((ptrdiff_t)aData.mYStride & 0x7) && !((ptrdiff_t)aData.mYChannel & 0x7)) {
     alignment = 8;
-  } else if (!((ptrdiff_t)mData.mYStride & 0x3)) {
+  } else if (!((ptrdiff_t)aData.mYStride & 0x3)) {
     alignment = 4;
-  } else if (!((ptrdiff_t)mData.mYStride & 0x1)) {
+  } else if (!((ptrdiff_t)aData.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());
+  gl->fBindTexture(LOCAL_GL_TEXTURE_2D, aYTexture->GetTextureID());
   gl->fTexSubImage2D(LOCAL_GL_TEXTURE_2D, 0,
-                     0, 0, mData.mYSize.width, mData.mYSize.height,
+                     0, 0, aData.mYSize.width, aData.mYSize.height,
                      LOCAL_GL_LUMINANCE,
                      LOCAL_GL_UNSIGNED_BYTE,
-                     mData.mYChannel);
+                     aData.mYChannel);
 
-  if (!((ptrdiff_t)mData.mCbCrStride & 0x7) && 
-      !((ptrdiff_t)mData.mCbChannel & 0x7) &&
-      !((ptrdiff_t)mData.mCrChannel & 0x7))
+  if (!((ptrdiff_t)aData.mCbCrStride & 0x7) && 
+      !((ptrdiff_t)aData.mCbChannel & 0x7) &&
+      !((ptrdiff_t)aData.mCrChannel & 0x7))
   {
     alignment = 8;
-  } else if (!((ptrdiff_t)mData.mCbCrStride & 0x3)) {
+  } else if (!((ptrdiff_t)aData.mCbCrStride & 0x3)) {
     alignment = 4;
-  } else if (!((ptrdiff_t)mData.mCbCrStride & 0x1)) {
+  } else if (!((ptrdiff_t)aData.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());
+  gl->fBindTexture(LOCAL_GL_TEXTURE_2D, aUTexture->GetTextureID());
   gl->fTexSubImage2D(LOCAL_GL_TEXTURE_2D, 0,
-                     0, 0, mData.mCbCrSize.width, mData.mCbCrSize.height,
+                     0, 0, aData.mCbCrSize.width, aData.mCbCrSize.height,
                      LOCAL_GL_LUMINANCE,
                      LOCAL_GL_UNSIGNED_BYTE,
-                     mData.mCbChannel);
+                     aData.mCbChannel);
 
-  gl->fBindTexture(LOCAL_GL_TEXTURE_2D, mTextures[2].GetTextureID());
+  gl->fBindTexture(LOCAL_GL_TEXTURE_2D, aVTexture->GetTextureID());
   gl->fTexSubImage2D(LOCAL_GL_TEXTURE_2D, 0,
-                     0, 0, mData.mCbCrSize.width, mData.mCbCrSize.height,
+                     0, 0, aData.mCbCrSize.width, aData.mCbCrSize.height,
                      LOCAL_GL_LUMINANCE,
                      LOCAL_GL_UNSIGNED_BYTE,
-                     mData.mCrChannel);
+                     aData.mCrChannel);
 
   // Reset alignment to default
   gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4);
 }
 
+void
+PlanarYCbCrImageOGL::UpdateTextures(GLContext *gl)
+{
+  if (!mBuffer || !mHasData)
+    return;
+
+  UploadYUVToTexture(gl, mData, &mTextures[0], &mTextures[1], &mTextures[2]);
+}
+
 CairoImageOGL::CairoImageOGL(LayerManagerOGL *aManager)
   : CairoImage(nsnull), mSize(0, 0)
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread to create a cairo image");
 
   if (aManager) {
     // Allocate texture now to grab a reference to the GLContext
     mTexture.Allocate(aManager->glForResources());
@@ -788,48 +797,103 @@ ShadowImageLayerOGL::ShadowImageLayerOGL
 
 ShadowImageLayerOGL::~ShadowImageLayerOGL()
 {}
 
 PRBool
 ShadowImageLayerOGL::Init(const SharedImage& aFront,
                           const nsIntSize& aSize)
 {
-  mDeadweight = aFront;
-  nsRefPtr<gfxASurface> surf = 
-    ShadowLayerForwarder::OpenDescriptor(aFront.get_SurfaceDescriptor());
-  gfxSize sz = surf->GetSize();
-  mTexImage = gl()->CreateTextureImage(nsIntSize(sz.width, sz.height),
-                                       surf->GetContentType(),
-                                       LOCAL_GL_CLAMP_TO_EDGE);
-  return PR_TRUE;
+  if (aFront.type() == SharedImage::TSurfaceDescriptor) {
+    SurfaceDescriptor desc = aFront.get_SurfaceDescriptor();
+    nsRefPtr<gfxASurface> surf = 
+      ShadowLayerForwarder::OpenDescriptor(desc);
+    gfxSize sz = surf->GetSize();
+    mTexImage = gl()->CreateTextureImage(nsIntSize(sz.width, sz.height),
+                                         surf->GetContentType(),
+                                         LOCAL_GL_CLAMP_TO_EDGE);
+    mOGLManager->DestroySharedSurface(&desc, mAllocator);
+    return PR_TRUE;
+  } else {
+    YUVImage yuv = aFront.get_YUVImage();
+    
+    nsRefPtr<gfxSharedImageSurface> surfY =
+      gfxSharedImageSurface::Open(yuv.Ydata());
+    nsRefPtr<gfxSharedImageSurface> surfU =
+      gfxSharedImageSurface::Open(yuv.Udata());
+    nsRefPtr<gfxSharedImageSurface> surfV =
+      gfxSharedImageSurface::Open(yuv.Vdata());
+    
+    mSize = gfxIntSize(surfY->GetSize().width, surfY->GetSize().height);
+    gfxIntSize CbCrSize = gfxIntSize(surfU->GetSize().width, surfU->GetSize().height);
+
+    if (!mYUVTexture[0].IsAllocated()) {
+      mYUVTexture[0].Allocate(mOGLManager->glForResources());
+      mYUVTexture[1].Allocate(mOGLManager->glForResources());
+      mYUVTexture[2].Allocate(mOGLManager->glForResources());
+    }
+
+    NS_ASSERTION(mYUVTexture[0].IsAllocated() &&
+                 mYUVTexture[1].IsAllocated() &&
+                 mYUVTexture[2].IsAllocated(),
+                 "Texture allocation failed!");
+
+    gl()->MakeCurrent();
+    InitTexture(gl(), mYUVTexture[0].GetTextureID(), LOCAL_GL_LUMINANCE, mSize);
+    InitTexture(gl(), mYUVTexture[1].GetTextureID(), LOCAL_GL_LUMINANCE, CbCrSize);
+    InitTexture(gl(), mYUVTexture[2].GetTextureID(), LOCAL_GL_LUMINANCE, CbCrSize);
+    
+    mOGLManager->DestroySharedSurface(surfY, mAllocator);
+    mOGLManager->DestroySharedSurface(surfU, mAllocator);
+    mOGLManager->DestroySharedSurface(surfV, mAllocator);
+    return PR_TRUE;
+  }
 }
 
 void
 ShadowImageLayerOGL::Swap(const SharedImage& aNewFront, SharedImage* aNewBack)
 {
-  if (!mDestroyed && mTexImage) {
-    nsRefPtr<gfxASurface> surf = 
-      ShadowLayerForwarder::OpenDescriptor(aNewFront.get_SurfaceDescriptor());
-    // XXX this is always just ridiculously slow
-    gfxSize sz = surf->GetSize();
-    nsIntRegion updateRegion(nsIntRect(0, 0, sz.width, sz.height));
-    mTexImage->DirectUpdate(surf, updateRegion);
+  if (!mDestroyed) {
+    if (aNewFront.type() == SharedImage::TSurfaceDescriptor) {
+      nsRefPtr<gfxASurface> surf = 
+        ShadowLayerForwarder::OpenDescriptor(aNewFront.get_SurfaceDescriptor());
+      // XXX this is always just ridiculously slow
+      gfxSize sz = surf->GetSize();
+      nsIntRegion updateRegion(nsIntRect(0, 0, sz.width, sz.height));
+      mTexImage->DirectUpdate(surf, updateRegion);
+    } else {
+      const YUVImage& yuv = aNewFront.get_YUVImage();
+    
+      nsRefPtr<gfxSharedImageSurface> surfY =
+        gfxSharedImageSurface::Open(yuv.Ydata());
+      nsRefPtr<gfxSharedImageSurface> surfU =
+        gfxSharedImageSurface::Open(yuv.Udata());
+      nsRefPtr<gfxSharedImageSurface> surfV =
+        gfxSharedImageSurface::Open(yuv.Vdata());
+ 
+      PlanarYCbCrImage::Data data;
+      data.mYChannel = surfY->Data();
+      data.mYStride = surfY->Stride();
+      data.mYSize = surfY->GetSize();
+      data.mCbChannel = surfU->Data();
+      data.mCrChannel = surfV->Data();
+      data.mCbCrStride = surfU->Stride();
+      data.mCbCrSize = surfU->GetSize();
+
+      UploadYUVToTexture(gl(), data, &mYUVTexture[0], &mYUVTexture[1], &mYUVTexture[2]);
+    }
   }
 
   *aNewBack = aNewFront;
 }
 
 void
 ShadowImageLayerOGL::DestroyFrontBuffer()
 {
   mTexImage = nsnull;
-  if (IsSurfaceDescriptorValid(mDeadweight)) {
-    mOGLManager->DestroySharedSurface(&mDeadweight, mAllocator);
-  }
 }
 
 void
 ShadowImageLayerOGL::Disconnect()
 {
   Destroy();
 }
 
@@ -849,28 +913,54 @@ ShadowImageLayerOGL::GetLayer()
 }
 
 void
 ShadowImageLayerOGL::RenderLayer(int aPreviousFrameBuffer,
                                  const nsIntPoint& aOffset)
 {
   mOGLManager->MakeCurrent();
 
-  gl()->fActiveTexture(LOCAL_GL_TEXTURE0);
-  gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexImage->Texture());
-  ColorTextureLayerProgram *program =
-    mOGLManager->GetColorTextureLayerProgram(mTexImage->GetShaderProgramType());
+  LayerProgram* program;
+
+  if (mTexImage) {
+    gl()->fActiveTexture(LOCAL_GL_TEXTURE0);
+    gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexImage->Texture());
+    ColorTextureLayerProgram *colorProgram =
+      mOGLManager->GetColorTextureLayerProgram(mTexImage->GetShaderProgramType());
+
+    ApplyFilter(mFilter);
 
-  ApplyFilter(mFilter);
+    colorProgram->Activate();
+    colorProgram->SetTextureUnit(0);
+    colorProgram->SetLayerQuadRect(nsIntRect(nsIntPoint(0, 0), mTexImage->GetSize()));
+    program = colorProgram;
+  } else {
+    gl()->fActiveTexture(LOCAL_GL_TEXTURE0);
+    gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, mYUVTexture[0].GetTextureID());
+    ApplyFilter(mFilter);
+    gl()->fActiveTexture(LOCAL_GL_TEXTURE1);
+    gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, mYUVTexture[1].GetTextureID());
+    ApplyFilter(mFilter);
+    gl()->fActiveTexture(LOCAL_GL_TEXTURE2);
+    gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, mYUVTexture[2].GetTextureID());
+    ApplyFilter(mFilter);
+    
+    YCbCrTextureLayerProgram *yuvProgram = mOGLManager->GetYCbCrLayerProgram();
 
-  program->Activate();
-  program->SetLayerQuadRect(nsIntRect(nsIntPoint(0, 0), mTexImage->GetSize()));
+    yuvProgram->Activate();
+    yuvProgram->SetLayerQuadRect(nsIntRect(0, 0,
+                                           mSize.width,
+                                           mSize.height));
+    yuvProgram->SetYCbCrTextureUnits(0, 1, 2);
+
+    program = yuvProgram;
+  }
+
   program->SetLayerTransform(GetEffectiveTransform());
   program->SetLayerOpacity(GetEffectiveOpacity());
   program->SetRenderOffset(aOffset);
-  program->SetTextureUnit(0);
 
   mOGLManager->BindAndDrawQuad(program);
 }
 
 
 } /* layers */
 } /* mozilla */
--- a/gfx/layers/opengl/ImageLayerOGL.h
+++ b/gfx/layers/opengl/ImageLayerOGL.h
@@ -258,15 +258,15 @@ public:
 
   virtual Layer* GetLayer();
 
   virtual void RenderLayer(int aPreviousFrameBuffer,
                            const nsIntPoint& aOffset);
 
 private:
   nsRefPtr<TextureImage> mTexImage;
-
-  SurfaceDescriptor mDeadweight;
+  GLTexture mYUVTexture[3];
+  gfxIntSize mSize;
 };
 
 } /* layers */
 } /* mozilla */
 #endif /* GFX_IMAGELAYEROGL_H */
--- a/gfx/layers/opengl/LayerManagerOGL.cpp
+++ b/gfx/layers/opengl/LayerManagerOGL.cpp
@@ -1083,17 +1083,18 @@ LayerManagerOGL::CreateFBOWithTexture(co
     mGLContext->fClearColor(0.0, 0.0, 0.0, 0.0);
     mGLContext->fClear(LOCAL_GL_COLOR_BUFFER_BIT);
   }
 
   *aFBO = fbo;
   *aTexture = tex;
 }
 
-void LayerOGL::ApplyFilter(gfxPattern::GraphicsFilter aFilter)
+void 
+LayerOGL::ApplyFilter(gfxPattern::GraphicsFilter aFilter)
 {
   if (aFilter == gfxPattern::FILTER_NEAREST) {
     gl()->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_NEAREST);
     gl()->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_NEAREST);
   } else {
     if (aFilter != gfxPattern::FILTER_GOOD) {
       NS_WARNING("Unsupported filter type!");
     }