Bug 740580 - Add support for Azure DrawTargets to BasicLayers. r=Bas
authorGeorge Wright <gwright@mozilla.com>
Tue, 02 Oct 2012 22:09:12 -0400
changeset 109097 5ff5e81e6de6b5640188f03294be1ec643c3dd11
parent 109096 0cd12dcf7f8f2e5fb55951dec5e5bfa876e177fa
child 109098 0ae09da96f63b1d283822be4de3f5a90e60e9ae7
push id82
push usershu@rfrn.org
push dateFri, 05 Oct 2012 13:20:22 +0000
reviewersBas
bugs740580
milestone18.0a1
Bug 740580 - Add support for Azure DrawTargets to BasicLayers. r=Bas
gfx/layers/ThebesLayerBuffer.cpp
gfx/layers/ThebesLayerBuffer.h
gfx/layers/basic/BasicBuffers.cpp
gfx/layers/basic/BasicBuffers.h
gfx/layers/basic/BasicThebesLayer.cpp
gfx/layers/basic/BasicThebesLayer.h
gfx/layers/ipc/AutoOpenSurface.h
gfx/layers/ipc/ShadowLayers.cpp
gfx/layers/ipc/ShadowLayers.h
gfx/thebes/gfx2DGlue.h
--- a/gfx/layers/ThebesLayerBuffer.cpp
+++ b/gfx/layers/ThebesLayerBuffer.cpp
@@ -8,20 +8,23 @@
 #include "ThebesLayerBuffer.h"
 #include "Layers.h"
 #include "gfxContext.h"
 #include "gfxPlatform.h"
 #include "gfxUtils.h"
 #include "ipc/AutoOpenSurface.h"
 #include "nsDeviceContext.h"
 #include "sampler.h"
+#include "mozilla/gfx/2D.h"
 
 namespace mozilla {
 namespace layers {
 
+using namespace gfx;
+
 nsIntRect
 ThebesLayerBuffer::GetQuadrantRectangle(XSide aXSide, YSide aYSide)
 {
   // quadrantTranslation is the amount we translate the top-left
   // of the quadrant by to get coordinates relative to the layer
   nsIntPoint quadrantTranslation = -mBufferRotation;
   quadrantTranslation.x += aXSide == LEFT ? mBufferRect.width : 0;
   quadrantTranslation.y += aYSide == TOP ? mBufferRect.height : 0;
@@ -55,17 +58,28 @@ ThebesLayerBuffer::DrawBufferQuadrant(gf
     return;
 
   aTarget->NewPath();
   aTarget->Rectangle(gfxRect(fillRect.x, fillRect.y,
                              fillRect.width, fillRect.height),
                      true);
 
   gfxPoint quadrantTranslation(quadrantRect.x, quadrantRect.y);
-  nsRefPtr<gfxPattern> pattern = new gfxPattern(EnsureBuffer());
+  EnsureBuffer();
+  if (!BufferValid()) {
+    return;
+  }
+  nsRefPtr<gfxPattern> pattern;
+
+  if (mBuffer) {
+    pattern = new gfxPattern(mBuffer);
+  } else {
+    RefPtr<SourceSurface> source = mDTBuffer->Snapshot();
+    pattern = new gfxPattern(source, Matrix());
+  }
 
 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
   gfxPattern::GraphicsFilter filter = gfxPattern::FILTER_NEAREST;
   pattern->SetFilter(filter);
 #endif
 
   gfxContextMatrixAutoSaveRestore saveMatrix(aTarget);
 
@@ -111,57 +125,77 @@ ThebesLayerBuffer::DrawBufferWithRotatio
   DrawBufferQuadrant(aTarget, RIGHT, TOP, aOpacity, aMask, aMaskTransform);
   DrawBufferQuadrant(aTarget, LEFT, BOTTOM, aOpacity, aMask, aMaskTransform);
   DrawBufferQuadrant(aTarget, RIGHT, BOTTOM, aOpacity, aMask, aMaskTransform);
 }
 
 already_AddRefed<gfxContext>
 ThebesLayerBuffer::GetContextForQuadrantUpdate(const nsIntRect& aBounds)
 {
-  nsRefPtr<gfxContext> ctx = new gfxContext(EnsureBuffer());
+  EnsureBuffer();
+  if (!BufferValid()) {
+    return nullptr;
+  }
+  nsRefPtr<gfxContext> ctx = mBuffer ? new gfxContext(mBuffer) : new gfxContext(mDTBuffer);
 
   // Figure out which quadrant to draw in
   int32_t xBoundary = mBufferRect.XMost() - mBufferRotation.x;
   int32_t yBoundary = mBufferRect.YMost() - mBufferRotation.y;
   XSide sideX = aBounds.XMost() <= xBoundary ? RIGHT : LEFT;
   YSide sideY = aBounds.YMost() <= yBoundary ? BOTTOM : TOP;
   nsIntRect quadrantRect = GetQuadrantRectangle(sideX, sideY);
   NS_ASSERTION(quadrantRect.Contains(aBounds), "Messed up quadrants");
   ctx->Translate(-gfxPoint(quadrantRect.x, quadrantRect.y));
 
   return ctx.forget();
 }
 
 gfxASurface::gfxContentType
 ThebesLayerBuffer::BufferContentType()
 {
-  return mBuffer ? mBuffer->GetContentType() : mBufferProvider->ContentType();
+  if (mBuffer) {
+    return mBuffer->GetContentType();
+  }
+  if (mDTBuffer) {
+    return ContentForFormat(mDTBuffer->GetFormat());
+  }
+  return mBufferProvider->ContentType();
 }
 
 bool
 ThebesLayerBuffer::BufferSizeOkFor(const nsIntSize& aSize)
 {
   return (aSize == mBufferRect.Size() ||
           (SizedToVisibleBounds != mBufferSizePolicy &&
            aSize < mBufferRect.Size()));
 }
 
-gfxASurface*
+void
 ThebesLayerBuffer::EnsureBuffer()
 {
-  if (!mBuffer && mBufferProvider) {
+  if (mBuffer || mDTBuffer) {
+    return;
+  }
+  if (!mBufferProvider) {
+    NS_ASSERTION(false, "Doesn't have mBuffer, mDTBuffer or mBufferProvider");
+    return;
+  }
+  if (gfxPlatform::GetPlatform()->SupportsAzureContent()) {
+    mDTBuffer = mBufferProvider->GetDrawTarget();
+    NS_ASSERTION(mDTBuffer, "Failed to create DrawTarget");
+  } else {
     mBuffer = mBufferProvider->Get();
+    NS_ASSERTION(mBuffer, "Failed to create buffer");
   }
-  return mBuffer;
 }
 
 bool
 ThebesLayerBuffer::HaveBuffer()
 {
-  return mBuffer || mBufferProvider;
+  return mBuffer || mDTBuffer || mBufferProvider;
 }
 
 static void
 WrapRotationAxis(int32_t* aRotationPoint, int32_t aSize)
 {
   if (*aRotationPoint < 0) {
     *aRotationPoint += aSize;
   } else if (*aRotationPoint >= aSize) {
@@ -248,16 +282,17 @@ ThebesLayerBuffer::BeginPaint(ThebesLaye
                "Destination rect doesn't contain what we need to paint");
 
   result.mRegionToDraw.Sub(neededRegion, validRegion);
   if (result.mRegionToDraw.IsEmpty())
     return result;
 
   nsIntRect drawBounds = result.mRegionToDraw.GetBounds();
   nsRefPtr<gfxASurface> destBuffer;
+  RefPtr<DrawTarget> destDT;
   uint32_t bufferFlags = canHaveRotation ? ALLOW_REPEAT : 0;
   if (canReuseBuffer) {
     nsIntRect keepArea;
     if (keepArea.IntersectRect(destBufferRect, mBufferRect)) {
       // Set mBufferRotation so that the pixels currently in mBuffer
       // will still be rendered in the right place when mBufferRect
       // changes to destBufferRect.
       nsIntPoint newRotation = mBufferRotation +
@@ -269,67 +304,90 @@ ThebesLayerBuffer::BeginPaint(ThebesLaye
       int32_t xBoundary = destBufferRect.XMost() - newRotation.x;
       int32_t yBoundary = destBufferRect.YMost() - newRotation.y;
       if ((drawBounds.x < xBoundary && xBoundary < drawBounds.XMost()) ||
           (drawBounds.y < yBoundary && yBoundary < drawBounds.YMost()) ||
           (newRotation != nsIntPoint(0,0) && !canHaveRotation)) {
         // The stuff we need to redraw will wrap around an edge of the
         // buffer, so move the pixels we can keep into a position that
         // lets us redraw in just one quadrant.
+
         if (mBufferRotation == nsIntPoint(0,0)) {
-          nsIntRect srcRect(nsIntPoint(0, 0), mBufferRect.Size());
+          EnsureBuffer();
+          if (!BufferValid())
+            return result;
+
           nsIntPoint dest = mBufferRect.TopLeft() - destBufferRect.TopLeft();
-          EnsureBuffer()->MovePixels(srcRect, dest);
+          if (mBuffer) {
+            nsIntRect srcRect(nsIntPoint(0, 0), mBufferRect.Size());
+            mBuffer->MovePixels(srcRect, dest);
+          } else {
+            IntRect srcRect(0, 0, mBufferRect.width, mBufferRect.height);
+            RefPtr<SourceSurface> snapshot = mDTBuffer->Snapshot();
+            mDTBuffer->CopySurface(snapshot, srcRect, IntPoint(dest.x, dest.y));
+          }
           result.mDidSelfCopy = true;
           // Don't set destBuffer; we special-case self-copies, and
           // just did the necessary work above.
           mBufferRect = destBufferRect;
         } else {
           // We can't do a real self-copy because the buffer is rotated.
           // So allocate a new buffer for the destination.
           destBufferRect = ComputeBufferRect(neededRegion.GetBounds());
-          destBuffer = CreateBuffer(contentType, destBufferRect.Size(), bufferFlags);
-          if (!destBuffer)
+          if (gfxPlatform::GetPlatform()->SupportsAzureContent()) {
+            destDT = CreateDrawTarget(destBufferRect.Size(), contentType);
+          } else {
+            destBuffer = CreateBuffer(contentType, destBufferRect.Size(), bufferFlags);
+          }
+          if (!destBuffer && !destDT)
             return result;
         }
       } else {
         mBufferRect = destBufferRect;
         mBufferRotation = newRotation;
       }
     } else {
       // No pixels are going to be kept. The whole visible region
       // will be redrawn, so we don't need to copy anything, so we don't
       // set destBuffer.
       mBufferRect = destBufferRect;
       mBufferRotation = nsIntPoint(0,0);
     }
   } else {
     // The buffer's not big enough, so allocate a new one
-    destBuffer = CreateBuffer(contentType, destBufferRect.Size(), bufferFlags);
-    if (!destBuffer)
+    if (gfxPlatform::GetPlatform()->SupportsAzureContent()) {
+      destDT = CreateDrawTarget(destBufferRect.Size(), contentType);
+    } else {
+      destBuffer = CreateBuffer(contentType, destBufferRect.Size(), bufferFlags);
+    }
+    if (!destBuffer && !destDT)
       return result;
   }
   NS_ASSERTION(!(aFlags & PAINT_WILL_RESAMPLE) || destBufferRect == neededRegion.GetBounds(),
                "If we're resampling, we need to validate the entire buffer");
 
   // If we have no buffered data already, then destBuffer will be a fresh buffer
   // and we do not need to clear it below.
-  bool isClear = mBuffer == nullptr;
+  bool isClear = !mBuffer && !mDTBuffer;
 
-  if (destBuffer) {
+  if (destBuffer || destDT) {
     if (HaveBuffer()) {
       // Copy the bits
-      nsRefPtr<gfxContext> tmpCtx = new gfxContext(destBuffer);
+      nsRefPtr<gfxContext> tmpCtx = destBuffer ? new gfxContext(destBuffer) : new gfxContext(destDT);
       nsIntPoint offset = -destBufferRect.TopLeft();
       tmpCtx->SetOperator(gfxContext::OPERATOR_SOURCE);
       tmpCtx->Translate(gfxPoint(offset.x, offset.y));
       DrawBufferWithRotation(tmpCtx, 1.0);
     }
 
-    mBuffer = destBuffer.forget();
+    if (destBuffer) {
+      mBuffer = destBuffer.forget();
+    } else {
+      mDTBuffer = destDT.forget();
+    }
     mBufferRect = destBufferRect;
     mBufferRotation = nsIntPoint(0,0);
   }
   NS_ASSERTION(canHaveRotation || mBufferRotation == nsIntPoint(0,0),
                "Rotation disabled, but we have nonzero rotation?");
 
   nsIntRegion invalidate;
   invalidate.Sub(aLayer->GetValidRegion(), destBufferRect);
--- a/gfx/layers/ThebesLayerBuffer.h
+++ b/gfx/layers/ThebesLayerBuffer.h
@@ -3,16 +3,17 @@
  * 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 THEBESLAYERBUFFER_H_
 #define THEBESLAYERBUFFER_H_
 
 #include "gfxContext.h"
 #include "gfxASurface.h"
+#include "gfxPlatform.h"
 #include "nsRegion.h"
 
 namespace mozilla {
 namespace layers {
 
 class AutoOpenSurface;
 class ThebesLayer;
 
@@ -64,16 +65,17 @@ public:
 
   /**
    * Wipe out all retained contents. Call this when the entire
    * buffer becomes invalid.
    */
   void Clear()
   {
     mBuffer = nullptr;
+    mDTBuffer = nullptr;
     mBufferProvider = nullptr;
     mBufferRect.SetEmpty();
   }
 
   /**
    * This is returned by BeginPaint. The caller should draw into mContext.
    * mRegionToDraw must be drawn. mRegionToInvalidate has been invalidated
    * by ThebesLayerBuffer and must be redrawn on the screen.
@@ -123,20 +125,41 @@ public:
    * Return a new surface of |aSize| and |aType|.
    * @param aFlags if ALLOW_REPEAT is set, then the buffer should be configured
    * to allow repeat-mode, otherwise it should be in pad (clamp) mode
    */
   virtual already_AddRefed<gfxASurface>
   CreateBuffer(ContentType aType, const nsIntSize& aSize, uint32_t aFlags) = 0;
 
   /**
+   * Return a new DrawTarget of |aSize| with SurfaceFormat |aFormat|.
+   */
+  virtual TemporaryRef<gfx::DrawTarget>
+  CreateDrawTarget(const gfx::IntSize& aSize, gfx::SurfaceFormat aFormat)
+  {
+    return gfxPlatform::GetPlatform()->CreateOffscreenDrawTarget(aSize, aFormat);
+  }
+
+  /**
+   * Return a new DrawTarget of |aSize| with ContentType |aContent|.
+   */
+  TemporaryRef<gfx::DrawTarget>
+  CreateDrawTarget(const nsIntSize& aSize, ContentType aContent) 
+  {
+    gfx::SurfaceFormat format = gfx::SurfaceFormatForImageFormat(
+      gfxPlatform::GetPlatform()->OptimalFormatForContent(aContent));
+    return CreateDrawTarget(gfx::IntSize(aSize.width, aSize.height), format);
+  }
+
+  /**
    * Get the underlying buffer, if any. This is useful because we can pass
    * in the buffer as the default "reference surface" if there is one.
    * Don't use it for anything else!
    */
+  gfx::DrawTarget* GetDT() { return mDTBuffer; }
   gfxASurface* GetBuffer() { return mBuffer; }
 
 protected:
   enum XSide {
     LEFT, RIGHT
   };
   enum YSide {
     TOP, BOTTOM
@@ -169,32 +192,44 @@ protected:
   {
     nsRefPtr<gfxASurface> tmp = mBuffer.forget();
     mBuffer = aBuffer;
     mBufferRect = aBufferRect;
     mBufferRotation = aBufferRotation;
     return tmp.forget();
   }
 
+  TemporaryRef<gfx::DrawTarget>
+  SetDT(gfx::DrawTarget* aBuffer,
+            const nsIntRect& aBufferRect, const nsIntPoint& aBufferRotation)
+  {
+    RefPtr<gfx::DrawTarget> tmp = mDTBuffer.forget();
+    mDTBuffer = aBuffer;
+    mBufferRect = aBufferRect;
+    mBufferRotation = aBufferRotation;
+    return tmp.forget();
+  }
+
   /**
    * Set the buffer provider only.  This is used with surfaces that
    * require explicit map/unmap, which |aProvider| is used to do on
    * demand in this code.
    *
    * It's the caller's responsibility to ensure |aProvider| is valid
    * for the duration of operations it requests of this
    * ThebesLayerBuffer.  It's also the caller's responsibility to
    * unset the provider when inactive, by calling
    * SetBufferProvider(nullptr).
    */
   void SetBufferProvider(AutoOpenSurface* aProvider)
   {
     mBufferProvider = aProvider;
     if (!mBufferProvider) {
       mBuffer = nullptr;
+      mDTBuffer = nullptr;
     } else {
       // Only this buffer provider can give us a buffer.  If we
       // already have one, something has gone wrong.
       MOZ_ASSERT(!mBuffer);
     }
   }
 
   /**
@@ -210,25 +245,34 @@ private:
 
   /**
    * Return the buffer's content type.  Requires a valid buffer or
    * buffer provider.
    */
   gfxASurface::gfxContentType BufferContentType();
   bool BufferSizeOkFor(const nsIntSize& aSize);
   /**
-   * If the buffer hasn't been mapped, map it and return it.
+   * If the buffer hasn't been mapped, map it.
    */
-  gfxASurface* EnsureBuffer();
+  void EnsureBuffer();
+
+  /**
+   * Check if the buffer is valid
+   */
+  bool BufferValid() {
+    return mBuffer || mDTBuffer;
+  }
+
   /**
    * True if we have a buffer where we can get it (but not necessarily
    * mapped currently).
    */
   bool HaveBuffer();
 
+  RefPtr<gfx::DrawTarget> mDTBuffer;
   nsRefPtr<gfxASurface> mBuffer;
   /**
    * This member is only set transiently.  It's used to map mBuffer
    * when we're using surfaces that require explicit map/unmap.
    */
   AutoOpenSurface* mBufferProvider;
   /** The area of the ThebesLayer that is covered by the buffer as a whole */
   nsIntRect             mBufferRect;
--- a/gfx/layers/basic/BasicBuffers.cpp
+++ b/gfx/layers/basic/BasicBuffers.cpp
@@ -59,25 +59,52 @@ BasicThebesLayerBuffer::DrawTo(ThebesLay
 
 already_AddRefed<gfxASurface>
 BasicThebesLayerBuffer::CreateBuffer(ContentType aType, 
                                      const nsIntSize& aSize, uint32_t aFlags)
 {
   return mLayer->CreateBuffer(aType, aSize);
 }
 
+TemporaryRef<DrawTarget>
+BasicThebesLayerBuffer::CreateDrawTarget(const IntSize& aSize, SurfaceFormat aFormat)
+{
+  return mLayer->CreateDrawTarget(aSize, aFormat);
+}
+
 void
 BasicThebesLayerBuffer::SetBackingBufferAndUpdateFrom(
   gfxASurface* aBuffer,
   gfxASurface* aSource, const nsIntRect& aRect, const nsIntPoint& aRotation,
   const nsIntRegion& aUpdateRegion)
 {
   SetBackingBuffer(aBuffer, aRect, aRotation);
   nsRefPtr<gfxContext> destCtx =
     GetContextForQuadrantUpdate(aUpdateRegion.GetBounds());
+  if (!destCtx) {
+    return;
+  }
+  destCtx->SetOperator(gfxContext::OPERATOR_SOURCE);
+  if (IsClippingCheap(destCtx, aUpdateRegion)) {
+    gfxUtils::ClipToRegion(destCtx, aUpdateRegion);
+  }
+
+  BasicThebesLayerBuffer srcBuffer(aSource, aRect, aRotation);
+  srcBuffer.DrawBufferWithRotation(destCtx, 1.0);
+}
+
+void
+BasicThebesLayerBuffer::SetBackingBufferAndUpdateFrom(
+  DrawTarget* aBuffer,
+  DrawTarget* aSource, const nsIntRect& aRect, const nsIntPoint& aRotation,
+  const nsIntRegion& aUpdateRegion)
+{
+  SetBackingBuffer(aBuffer, aRect, aRotation);
+  nsRefPtr<gfxContext> destCtx =
+    GetContextForQuadrantUpdate(aUpdateRegion.GetBounds());
   destCtx->SetOperator(gfxContext::OPERATOR_SOURCE);
   if (IsClippingCheap(destCtx, aUpdateRegion)) {
     gfxUtils::ClipToRegion(destCtx, aUpdateRegion);
   }
 
   BasicThebesLayerBuffer srcBuffer(aSource, aRect, aRotation);
   srcBuffer.DrawBufferWithRotation(destCtx, 1.0);
 }
--- a/gfx/layers/basic/BasicBuffers.h
+++ b/gfx/layers/basic/BasicBuffers.h
@@ -3,16 +3,18 @@
  * 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 GFX_BASICBUFFERS_H
 #define GFX_BASICBUFFERS_H
 
 #include "BasicLayersImpl.h"
 
+#include "mozilla/gfx/2D.h"
+
 namespace mozilla {
 namespace layers {
 
 class BasicThebesLayer;
 class BasicThebesLayerBuffer : public ThebesLayerBuffer {
   typedef ThebesLayerBuffer Base;
 
 public:
@@ -34,37 +36,57 @@ public:
    * to aTarget.
    */
   void DrawTo(ThebesLayer* aLayer, gfxContext* aTarget, float aOpacity,
               Layer* aMaskLayer);
 
   virtual already_AddRefed<gfxASurface>
   CreateBuffer(ContentType aType, const nsIntSize& aSize, uint32_t aFlags);
 
+  virtual TemporaryRef<mozilla::gfx::DrawTarget>
+  CreateDrawTarget(const mozilla::gfx::IntSize& aSize,
+                   mozilla::gfx::SurfaceFormat aFormat);
+
   /**
    * Swap out the old backing buffer for |aBuffer| and attributes.
    */
   void SetBackingBuffer(gfxASurface* aBuffer,
                         const nsIntRect& aRect, const nsIntPoint& aRotation)
   {
 #ifdef DEBUG
     gfxIntSize prevSize = gfxIntSize(BufferRect().width, BufferRect().height);
     gfxIntSize newSize = aBuffer->GetSize();
     NS_ABORT_IF_FALSE(newSize == prevSize,
                       "Swapped-in buffer size doesn't match old buffer's!");
 #endif
     nsRefPtr<gfxASurface> oldBuffer;
     oldBuffer = SetBuffer(aBuffer, aRect, aRotation);
   }
 
+  void SetBackingBuffer(mozilla::gfx::DrawTarget* aBuffer,
+                        const nsIntRect& aRect, const nsIntPoint& aRotation)
+  {
+    mozilla::gfx::IntSize prevSize = mozilla::gfx::IntSize(BufferRect().width, BufferRect().height);
+    mozilla::gfx::IntSize newSize = aBuffer->GetSize();
+    NS_ABORT_IF_FALSE(newSize == prevSize,
+                      "Swapped-in buffer size doesn't match old buffer's!");
+    RefPtr<mozilla::gfx::DrawTarget> oldBuffer;
+    oldBuffer = SetDT(aBuffer, aRect, aRotation);
+  }
+
   void SetBackingBufferAndUpdateFrom(
     gfxASurface* aBuffer,
     gfxASurface* aSource, const nsIntRect& aRect, const nsIntPoint& aRotation,
     const nsIntRegion& aUpdateRegion);
 
+  void SetBackingBufferAndUpdateFrom(
+    mozilla::gfx::DrawTarget* aBuffer,
+    mozilla::gfx::DrawTarget* aSource, const nsIntRect& aRect, const nsIntPoint& aRotation,
+    const nsIntRegion& aUpdateRegion);
+
   /**
    * When BasicThebesLayerBuffer is used with layers that hold
    * SurfaceDescriptor, this buffer only has a valid gfxASurface in
    * the scope of an AutoOpenSurface for that SurfaceDescriptor.  That
    * is, it's sort of a "virtual buffer" that's only mapped and
    * unmapped within the scope of AutoOpenSurface.  None of the
    * underlying buffer attributes (rect, rotation) are affected by
    * mapping/unmapping.
@@ -86,16 +108,24 @@ private:
                          const nsIntRect& aRect, const nsIntPoint& aRotation)
     // The size policy doesn't really matter here; this constructor is
     // intended to be used for creating temporaries
     : ThebesLayerBuffer(ContainsVisibleBounds)
   {
     SetBuffer(aBuffer, aRect, aRotation);
   }
 
+  BasicThebesLayerBuffer(mozilla::gfx::DrawTarget* aBuffer,
+                         const nsIntRect& aRect, const nsIntPoint& aRotation)
+    : ThebesLayerBuffer(ContainsVisibleBounds)
+  {
+    SetDT(aBuffer, aRect, aRotation);
+  }
+
+
   BasicThebesLayer* mLayer;
 };
 
 class ShadowThebesLayerBuffer : public BasicThebesLayerBuffer
 {
   typedef BasicThebesLayerBuffer Base;
 
 public:
@@ -133,14 +163,21 @@ public:
 
 protected:
   virtual already_AddRefed<gfxASurface>
   CreateBuffer(ContentType, const nsIntSize&, uint32_t)
   {
     NS_RUNTIMEABORT("ShadowThebesLayer can't paint content");
     return nullptr;
   }
+
+  virtual TemporaryRef<mozilla::gfx::DrawTarget>
+  CreateDrawTarget(const mozilla::gfx::IntSize&, mozilla::gfx::SurfaceFormat)
+  {
+    NS_RUNTIMEABORT("ShadowThebesLayer can't paint content");
+    return nullptr;
+  }
 };
 
 }
 }
 
 #endif
--- a/gfx/layers/basic/BasicThebesLayer.cpp
+++ b/gfx/layers/basic/BasicThebesLayer.cpp
@@ -3,16 +3,17 @@
  * 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/. */
 
 #include "BasicThebesLayer.h"
 #include "gfxUtils.h"
 #include "nsIWidget.h"
 #include "RenderTrace.h"
 #include "sampler.h"
+#include "gfxSharedImageSurface.h"
 
 #include "prprf.h"
 
 using namespace mozilla::gfx;
 
 namespace mozilla {
 namespace layers {
 
@@ -30,16 +31,23 @@ BasicThebesLayer::CreateBuffer(Buffer::C
         referenceSurface = BasicManager()->GetTarget()->CurrentSurface();
       }
     }
   }
   return referenceSurface->CreateSimilarSurface(
     aType, gfxIntSize(aSize.width, aSize.height));
 }
 
+TemporaryRef<DrawTarget>
+BasicThebesLayer::CreateDrawTarget(const IntSize& aSize, SurfaceFormat aFormat)
+{
+  return gfxPlatform::GetPlatform()->CreateOffscreenDrawTarget(aSize, aFormat);
+}
+
+
 static nsIntRegion
 IntersectWithClip(const nsIntRegion& aRegion, gfxContext* aContext)
 {
   gfxRect clip = aContext->GetClipExtents();
   clip.RoundOut();
   nsIntRect r(clip.X(), clip.Y(), clip.Width(), clip.Height());
   nsIntRegion result;
   result.And(aRegion, r);
@@ -260,16 +268,20 @@ struct NS_STACK_CLASS AutoBufferTracker 
     mLayer->mBufferTracker = nullptr;
     mLayer->mBuffer.RevokeBuffer();
     // mInitialBuffer and mNewBuffer will clean up after themselves if
     // they were constructed.
   }
 
   gfxASurface* GetInitialBuffer() { return mInitialBuffer.ref().Get(); }
 
+  DrawTarget* GetInitialDrawTarget() {
+    return mInitialBuffer.ref().GetDrawTarget();
+  }
+
   gfxASurface*
   CreatedBuffer(const SurfaceDescriptor& aDescriptor) {
     Maybe<AutoOpenSurface>* surface = mNewBuffers.AppendElement();
     surface->construct(OPEN_READ_WRITE, aDescriptor);
     return surface->ref().Get();
   }
 
   Maybe<AutoOpenSurface> mInitialBuffer;
@@ -341,52 +353,71 @@ BasicShadowableThebesLayer::SyncFrontBuf
   }
 
   // We temporarily map our back buffer here in order to copy from the
   // front buffer.  We need a live buffer tracker in order to unmap
   // that buffer when appropriate.
   MOZ_ASSERT(mBufferTracker);
 
   nsRefPtr<gfxASurface> backBuffer = mBuffer.GetBuffer();
+  DrawTarget* backDT = mBuffer.GetDT();
+
   if (!IsSurfaceDescriptorValid(mBackBuffer)) {
-    MOZ_ASSERT(!backBuffer);
+    MOZ_ASSERT(!backBuffer && !backDT);
     MOZ_ASSERT(mROFrontBuffer.type() == OptionalThebesBuffer::TThebesBuffer);
     const ThebesBuffer roFront = mROFrontBuffer.get_ThebesBuffer();
     AutoOpenSurface roFrontBuffer(OPEN_READ_ONLY, roFront.buffer());
     backBuffer = CreateBuffer(roFrontBuffer.ContentType(), roFrontBuffer.Size());
   }
   mFrontAndBackBufferDiffer = false;
 
-  if (!backBuffer) {
-    backBuffer = mBufferTracker->GetInitialBuffer();
+  if (!backBuffer && !backDT) {
+    if (gfxPlatform::GetPlatform()->SupportsAzureContent()) {
+      backDT = mBufferTracker->GetInitialDrawTarget();
+    } else {
+      backBuffer = mBufferTracker->GetInitialBuffer();
+    }
   }
 
   if (OptionalThebesBuffer::Tnull_t == mROFrontBuffer.type()) {
     // We didn't get back a read-only ref to our old back buffer (the
     // parent's new front buffer).  If the parent is pushing updates
     // to a texture it owns, then we probably got back the same buffer
     // we pushed in the update and all is well.  If not, ...
     mValidRegion = mFrontValidRegion;
-    mBuffer.SetBackingBuffer(backBuffer, mBackBufferRect, mBackBufferRectRotation);
+    if (backDT) {
+      mBuffer.SetBackingBuffer(backDT, mBackBufferRect, mBackBufferRectRotation);
+    } else {
+      mBuffer.SetBackingBuffer(backBuffer, mBackBufferRect, mBackBufferRectRotation);
+    }
     return;
   }
 
   MOZ_LAYERS_LOG(("BasicShadowableThebes(%p): reading back <x=%d,y=%d,w=%d,h=%d>",
                   this,
                   mFrontUpdatedRegion.GetBounds().x,
                   mFrontUpdatedRegion.GetBounds().y,
                   mFrontUpdatedRegion.GetBounds().width,
                   mFrontUpdatedRegion.GetBounds().height));
 
   const ThebesBuffer roFront = mROFrontBuffer.get_ThebesBuffer();
   AutoOpenSurface autoROFront(OPEN_READ_ONLY, roFront.buffer());
-  mBuffer.SetBackingBufferAndUpdateFrom(
-    backBuffer,
-    autoROFront.Get(), roFront.rect(), roFront.rotation(),
-    mFrontUpdatedRegion);
+
+  if (backDT) {
+    mBuffer.SetBackingBufferAndUpdateFrom(
+      backDT,
+      autoROFront.GetDrawTarget(), roFront.rect(), roFront.rotation(),
+      mFrontUpdatedRegion);
+  } else {
+    mBuffer.SetBackingBufferAndUpdateFrom(
+      backBuffer,
+      autoROFront.Get(), roFront.rect(), roFront.rotation(),
+      mFrontUpdatedRegion);
+  }
+
   mIsNewBuffer = false;
   // Now the new back buffer has the same (interesting) pixels as the
   // new front buffer, and mValidRegion et al. are correct wrt the new
   // back buffer (i.e. as they were for the old back buffer)
 }
 
 void
 BasicShadowableThebesLayer::PaintBuffer(gfxContext* aContext,
@@ -464,16 +495,60 @@ BasicShadowableThebesLayer::CreateBuffer
                     "Bad! Did we create a buffer twice without painting?");
 
   mIsNewBuffer = true;
 
   nsRefPtr<gfxASurface> buffer = mBufferTracker->CreatedBuffer(mBackBuffer);
   return buffer.forget();
 }
 
+TemporaryRef<DrawTarget>
+BasicShadowableThebesLayer::CreateDrawTarget(const IntSize& aSize,
+                                             SurfaceFormat aFormat)
+{
+  if (!HasShadow()) {
+    return BasicThebesLayer::CreateDrawTarget(aSize, aFormat);
+  }
+
+  MOZ_LAYERS_LOG(("BasicShadowableThebes(%p): creating %d x %d buffer(x2)",
+                  this,
+                  aSize.width, aSize.height));
+
+  if (IsSurfaceDescriptorValid(mBackBuffer)) {
+    BasicManager()->DestroyedThebesBuffer(BasicManager()->Hold(this),
+                                          mBackBuffer);
+    mBackBuffer = SurfaceDescriptor();
+  }
+
+  // XXX error handling
+  if (!BasicManager()->AllocBuffer(gfxIntSize(aSize.width, aSize.height),
+                                   mozilla::gfx::ContentForFormat(aFormat),
+                                   &mBackBuffer)) {
+    NS_RUNTIMEABORT("creating ThebesLayer 'back buffer' failed!");
+  }
+
+  NS_ABORT_IF_FALSE(!mIsNewBuffer,
+                    "Bad! Did we create a buffer twice without painting?");
+
+  mIsNewBuffer = true;
+
+  mozilla::ipc::Shmem shm = mBackBuffer.get_Shmem();
+  if (!shm.IsWritable()) {
+    NS_RUNTIMEABORT("shmem not writable!");
+    return nullptr;
+  }
+  SharedImageInfo* shmInfo = gfxSharedImageSurface::GetShmInfoPtr(shm);
+  unsigned char* data = shm.get<unsigned char>();
+
+  int stride = gfxASurface::FormatStrideForWidth(
+      static_cast<gfxASurface::gfxImageFormat>(shmInfo->format), aSize.width);
+
+  return gfxPlatform::GetPlatform()->CreateDrawTargetForData(data, aSize, stride, aFormat);
+}
+
 void
 BasicShadowableThebesLayer::Disconnect()
 {
   mBackBuffer = SurfaceDescriptor();
   BasicShadowableLayer::Disconnect();
 }
 
 
--- a/gfx/layers/basic/BasicThebesLayer.h
+++ b/gfx/layers/basic/BasicThebesLayer.h
@@ -2,16 +2,17 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * 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 GFX_BASICTHEBESLAYER_H
 #define GFX_BASICTHEBESLAYER_H
 
 #include "mozilla/layers/PLayersParent.h"
+#include "mozilla/gfx/2D.h"
 #include "BasicBuffers.h"
 
 namespace mozilla {
 namespace layers {
 
 class BasicThebesLayer : public ThebesLayer, public BasicImplData {
 public:
   typedef BasicThebesLayerBuffer Buffer;
@@ -48,16 +49,21 @@ public:
                            void* aCallbackData,
                            ReadbackProcessor* aReadback);
 
   virtual void ClearCachedResources() { mBuffer.Clear(); mValidRegion.SetEmpty(); }
   
   virtual already_AddRefed<gfxASurface>
   CreateBuffer(Buffer::ContentType aType, const nsIntSize& aSize);
 
+  virtual TemporaryRef<mozilla::gfx::DrawTarget>
+  CreateDrawTarget(const mozilla::gfx::IntSize& aSize,
+                   mozilla::gfx::SurfaceFormat aFormat);
+
+
   virtual void ComputeEffectiveTransforms(const gfx3DMatrix& aTransformToSurface)
   {
     if (!BasicManager()->IsRetained()) {
       // Don't do any snapping of our transform, since we're just going to
       // draw straight through without intermediate buffers.
       mEffectiveTransform = GetLocalTransform()*aTransformToSurface;
       if (gfxPoint(0,0) != mResidualTranslation) {
         mResidualTranslation = gfxPoint(0,0);
@@ -163,16 +169,21 @@ private:
               const nsIntRegion& aRegionToInvalidate,
               bool aDidSelfCopy,
               LayerManager::DrawThebesLayerCallback aCallback,
               void* aCallbackData) MOZ_OVERRIDE;
 
   virtual already_AddRefed<gfxASurface>
   CreateBuffer(Buffer::ContentType aType, const nsIntSize& aSize) MOZ_OVERRIDE;
 
+  virtual TemporaryRef<mozilla::gfx::DrawTarget>
+  CreateDrawTarget(const mozilla::gfx::IntSize& aSize,
+                   mozilla::gfx::SurfaceFormat aFormat) MOZ_OVERRIDE;
+
+
   void DestroyBackBuffer()
   {
     if (IsSurfaceDescriptorValid(mBackBuffer)) {
       BasicManager()->ShadowLayerForwarder::DestroySharedSurface(&mBackBuffer);
     }
   }
 
   // This describes the gfxASurface we hand to mBuffer.  We keep a
--- a/gfx/layers/ipc/AutoOpenSurface.h
+++ b/gfx/layers/ipc/AutoOpenSurface.h
@@ -6,16 +6,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_layers_AutoOpenSurface_h
 #define mozilla_layers_AutoOpenSurface_h 1
 
 #include "base/basictypes.h"
 
 #include "gfxASurface.h"
+#include "mozilla/gfx/2D.h"
 #include "mozilla/layers/PLayers.h"
 #include "ShadowLayers.h"
 
 namespace mozilla {
 namespace layers {
 
 /**
  * Some surface types can be fairly expensive to open.  This helper
@@ -39,32 +40,35 @@ public:
    * return an answer.
    */
   gfxContentType ContentType();
   gfxIntSize Size();
 
   /** This can't escape the scope of AutoOpenSurface. */
   gfxASurface* Get();
 
+  mozilla::gfx::DrawTarget* GetDrawTarget();
+
   /**
    * This can't escape the scope of AutoOpenSurface.
    *
    * This method is currently just a convenience wrapper around
    * gfxASurface::GetAsImageSurface() --- it returns a valid surface
    * exactly when this->Get()->GetAsImageSurface() would.  Clients
    * that need guaranteed (fast) ImageSurfaces should allocate the
    * underlying descriptor with capability MAP_AS_IMAGE_SURFACE, in
    * which case this helper is guaranteed to succeed.
    */
   gfxImageSurface* GetAsImage();
 
 
 private:
   SurfaceDescriptor mDescriptor;
   nsRefPtr<gfxASurface> mSurface;
+  RefPtr<mozilla::gfx::DrawTarget> mDrawTarget;
   nsRefPtr<gfxImageSurface> mSurfaceAsImage;
   OpenMode mMode;
 
   AutoOpenSurface(const AutoOpenSurface&) MOZ_DELETE;
   AutoOpenSurface& operator=(const AutoOpenSurface&) MOZ_DELETE;
 };
 
 } // namespace layers
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -481,16 +481,50 @@ ShadowLayerForwarder::OpenDescriptor(Ope
     return surf.forget();
   }
   default:
     NS_RUNTIMEABORT("unexpected SurfaceDescriptor type!");
     return nullptr;
   }
 }
 
+/*static*/ TemporaryRef<mozilla::gfx::DrawTarget>
+ShadowLayerForwarder::OpenDescriptorForDrawTarget(OpenMode aMode,
+                                                  const SurfaceDescriptor& aSurface)
+{
+  switch (aSurface.type()) {
+  case SurfaceDescriptor::TShmem: {
+    mozilla::ipc::Shmem shm = aSurface.get_Shmem();
+    if (!shm.IsWritable()) {
+      NS_RUNTIMEABORT("shmem not writable!");
+      return nullptr;
+    }
+
+    SharedImageInfo* shmInfo = gfxSharedImageSurface::GetShmInfoPtr(shm);
+    unsigned char* data = shm.get<unsigned char>();
+
+    gfxASurface::gfxImageFormat imgFormat =
+        static_cast<gfxASurface::gfxImageFormat>(shmInfo->format);
+
+    mozilla::gfx::SurfaceFormat surfFormat =
+      mozilla::gfx::SurfaceFormatForImageFormat(imgFormat);
+
+    mozilla::gfx::IntSize size(shmInfo->width, shmInfo->height);
+
+    int stride = gfxASurface::FormatStrideForWidth(imgFormat, size.width);
+
+    return gfxPlatform::GetPlatform()->CreateDrawTargetForData(data, size,
+                                                               stride, surfFormat);
+  }
+  default:
+    NS_RUNTIMEABORT("unexpected SurfaceDescriptor type!");
+    return nullptr;
+  }
+}
+
 /*static*/ gfxContentType
 ShadowLayerForwarder::GetDescriptorSurfaceContentType(
   const SurfaceDescriptor& aDescriptor, OpenMode aMode,
   gfxASurface** aSurface)
 {
   gfxContentType content;
   if (PlatformGetDescriptorSurfaceContentType(aDescriptor, aMode,
                                               &content, aSurface)) {
@@ -705,16 +739,25 @@ gfxASurface*
 AutoOpenSurface::Get()
 {
   if (!mSurface) {
     mSurface = ShadowLayerForwarder::OpenDescriptor(mMode, mDescriptor);
   }
   return mSurface.get();
 }
 
+mozilla::gfx::DrawTarget*
+AutoOpenSurface::GetDrawTarget()
+{
+  if (!mDrawTarget) {
+    mDrawTarget = ShadowLayerForwarder::OpenDescriptorForDrawTarget(mMode, mDescriptor);
+  }
+  return mDrawTarget.get();
+}
+
 gfxImageSurface*
 AutoOpenSurface::GetAsImage()
 {
   if (!mSurfaceAsImage) {
     mSurfaceAsImage = Get()->GetAsImageSurface();
   }
   return mSurfaceAsImage.get();
 }
--- a/gfx/layers/ipc/ShadowLayers.h
+++ b/gfx/layers/ipc/ShadowLayers.h
@@ -4,16 +4,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * 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_layers_ShadowLayers_h
 #define mozilla_layers_ShadowLayers_h 1
 
 #include "gfxASurface.h"
+#include "mozilla/gfx/2D.h"
 #include "GLDefs.h"
 
 #include "ImageLayers.h"
 #include "mozilla/ipc/SharedMemory.h"
 #include "mozilla/WidgetUtils.h"
 
 class gfxSharedImageSurface;
 
@@ -363,16 +364,19 @@ private:
   PlatformGetDescriptorSurfaceSize(const SurfaceDescriptor& aDescriptor,
                                    OpenMode aMode,
                                    gfxIntSize* aSize,
                                    gfxASurface** aSurface);
 
   static already_AddRefed<gfxASurface>
   OpenDescriptor(OpenMode aMode, const SurfaceDescriptor& aSurface);
 
+  static TemporaryRef<mozilla::gfx::DrawTarget>
+  OpenDescriptorForDrawTarget(OpenMode aMode, const SurfaceDescriptor& aSurface);
+
   static already_AddRefed<gfxASurface>
   PlatformOpenDescriptor(OpenMode aMode, const SurfaceDescriptor& aDescriptor);
 
   /** Make this descriptor unusable for gfxASurface clients.  A
    * private interface with AutoOpenSurface. */
   static void
   CloseDescriptor(const SurfaceDescriptor& aDescriptor);
 
--- a/gfx/thebes/gfx2DGlue.h
+++ b/gfx/thebes/gfx2DGlue.h
@@ -222,16 +222,50 @@ inline gfxASurface::gfxContentType Conte
   case FORMAT_A8:
     return gfxASurface::CONTENT_ALPHA;
   case FORMAT_B8G8R8A8:
   default:
     return gfxASurface::CONTENT_COLOR_ALPHA;
   }
 }
 
+inline SurfaceFormat SurfaceFormatForImageFormat(const gfxASurface::gfxImageFormat &aImageFormat)
+{
+  switch (aImageFormat) {
+  case gfxASurface::ImageFormatARGB32:
+    return FORMAT_B8G8R8A8;
+  case gfxASurface::ImageFormatRGB24:
+    return FORMAT_B8G8R8X8;
+  case gfxASurface::ImageFormatA8:
+  case gfxASurface::ImageFormatA1:
+    return FORMAT_A8;
+  case gfxASurface::ImageFormatRGB16_565:
+    return FORMAT_R5G6B5;
+  case gfxASurface::ImageFormatUnknown:
+  default:
+    return FORMAT_B8G8R8A8;
+  }
+}
+
+inline gfxASurface::gfxImageFormat ImageFormatForSurfaceFormat(const SurfaceFormat &aFormat)
+{
+  switch (aFormat) {
+  case FORMAT_B8G8R8A8:
+    return gfxASurface::ImageFormatARGB32;
+  case FORMAT_B8G8R8X8:
+    return gfxASurface::ImageFormatRGB24;
+  case FORMAT_A8:
+    return gfxASurface::ImageFormatA8;
+  case FORMAT_R5G6B5:
+    return gfxASurface::ImageFormatRGB16_565;
+  default:
+    return gfxASurface::ImageFormatARGB32;
+  }
+}
+
 inline CompositionOp CompositionOpForOp(gfxContext::GraphicsOperator aOp)
 {
   switch (aOp) {
   case gfxContext::OPERATOR_ADD:
     return OP_ADD;
   case gfxContext::OPERATOR_ATOP:
     return OP_ATOP;
   case gfxContext::OPERATOR_IN: