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
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 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!");
     }