Bug 626602, part 6: Implement asynchronous D3D10 readback. r=cjones,jrmuizel
authorBas Schouten <bas.schouten@live.nl>
Fri, 04 Feb 2011 20:30:00 -0600
changeset 62204 9bb82d1b62a807d462d4fcfcc839277df9f63a04
parent 62203 d9d905c76a3efced7d4b7ed0fa20cb5adb487c81
child 62205 580f6ff0b8249874f551545f00867621807e462c
push idunknown
push userunknown
push dateunknown
reviewerscjones, jrmuizel
bugs626602
milestone2.0b12pre
Bug 626602, part 6: Implement asynchronous D3D10 readback. r=cjones,jrmuizel
gfx/layers/Makefile.in
gfx/layers/d3d10/ContainerLayerD3D10.cpp
gfx/layers/d3d10/LayerManagerD3D10.cpp
gfx/layers/d3d10/LayerManagerD3D10.h
gfx/layers/d3d10/ReadbackLayerD3D10.h
gfx/layers/d3d10/ReadbackManagerD3D10.cpp
gfx/layers/d3d10/ReadbackManagerD3D10.h
gfx/layers/d3d10/ThebesLayerD3D10.cpp
gfx/layers/d3d10/ThebesLayerD3D10.h
--- a/gfx/layers/Makefile.in
+++ b/gfx/layers/Makefile.in
@@ -101,25 +101,27 @@ CPPSRCS += \
         CanvasLayerD3D9.cpp \
         DeviceManagerD3D9.cpp \
         Nv3DVUtils.cpp \
         $(NULL)
 endif
 ifdef MOZ_ENABLE_D3D10_LAYER
 EXPORTS += \
         LayerManagerD3D10.h \
+        ReadbackManagerD3D10.h \
         $(NULL)
 
 CPPSRCS += \
         LayerManagerD3D10.cpp \
         ThebesLayerD3D10.cpp \
         ContainerLayerD3D10.cpp \
         ImageLayerD3D10.cpp \
         ColorLayerD3D10.cpp \
         CanvasLayerD3D10.cpp \
+        ReadbackManagerD3D10.cpp \
         $(NULL)
 endif
 endif
 
 ifdef MOZ_IPC #{
 EXPORTS_NAMESPACES = IPC mozilla/layers
 EXPORTS_IPC = ShadowLayerUtils.h
 EXPORTS_mozilla/layers =\
--- a/gfx/layers/d3d10/ContainerLayerD3D10.cpp
+++ b/gfx/layers/d3d10/ContainerLayerD3D10.cpp
@@ -35,16 +35,19 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "ContainerLayerD3D10.h"
 #include "nsAlgorithm.h"
 #include "gfxUtils.h"
 #include "nsRect.h"
 
+#include "ThebesLayerD3D10.h"
+#include "ReadbackProcessor.h"
+
 namespace mozilla {
 namespace layers {
 
 ContainerLayerD3D10::ContainerLayerD3D10(LayerManagerD3D10 *aManager)
   : ContainerLayer(aManager, NULL)
   , LayerD3D10(aManager)
 {
   mImplData = static_cast<LayerD3D10*>(this);
@@ -397,17 +400,24 @@ ContainerLayerD3D10::Validate()
         mSupportsComponentAlphaChildren = PR_TRUE;
       }
     }
   } else {
     mSupportsComponentAlphaChildren = (GetContentFlags() & CONTENT_OPAQUE) ||
         (mParent && mParent->SupportsComponentAlphaChildren());
   }
 
+  ReadbackProcessor readback;
+  readback.BuildUpdates(this);
+
   Layer *layer = GetFirstChild();
   while (layer) {
-    static_cast<LayerD3D10*>(layer->ImplData())->Validate();
+    if (layer->GetType() == TYPE_THEBES) {
+      static_cast<ThebesLayerD3D10*>(layer)->Validate(&readback);
+    } else {
+      static_cast<LayerD3D10*>(layer->ImplData())->Validate();
+    }
     layer = layer->GetNextSibling();
   }
 }
 
 } /* layers */
 } /* mozilla */
--- a/gfx/layers/d3d10/LayerManagerD3D10.cpp
+++ b/gfx/layers/d3d10/LayerManagerD3D10.cpp
@@ -34,23 +34,25 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "LayerManagerD3D10.h"
 #include "LayerManagerD3D10Effect.h"
 #include "gfxWindowsPlatform.h"
 #include "gfxD2DSurface.h"
+#include "gfxFailure.h"
 #include "cairo-win32.h"
 #include "dxgi.h"
 
 #include "ContainerLayerD3D10.h"
 #include "ThebesLayerD3D10.h"
 #include "ColorLayerD3D10.h"
 #include "CanvasLayerD3D10.h"
+#include "ReadbackLayerD3D10.h"
 #include "ImageLayerD3D10.h"
 
 #include "../d3d9/Nv3DVUtils.h"
 
 namespace mozilla {
 namespace layers {
 
 typedef HRESULT (WINAPI*D3D10CreateEffectFromMemoryFunc)(
@@ -62,35 +64,60 @@ typedef HRESULT (WINAPI*D3D10CreateEffec
     ID3D10Effect **ppEffect
 );
 
 struct Vertex
 {
     float position[2];
 };
 
-// {17F88CCB-1F49-4c08-8002-ADA7BD44856D}
-static const GUID sEffect = 
-{ 0x17f88ccb, 0x1f49, 0x4c08, { 0x80, 0x2, 0xad, 0xa7, 0xbd, 0x44, 0x85, 0x6d } };
-// {19599D91-912C-4C2F-A8C5-299DE85FBD34}
-static const GUID sInputLayout = 
-{ 0x19599d91, 0x912c, 0x4c2f, { 0xa8, 0xc5, 0x29, 0x9d, 0xe8, 0x5f, 0xbd, 0x34 } };
-// {293157D2-09C7-4680-AE27-C28E370E418B}
-static const GUID sVertexBuffer = 
-{ 0x293157d2, 0x9c7, 0x4680, { 0xae, 0x27, 0xc2, 0x8e, 0x37, 0xe, 0x41, 0x8b } };
+// {592BF306-0EED-4F76-9D03-A0846450F472}
+static const GUID sDeviceAttachments = 
+{ 0x592bf306, 0xeed, 0x4f76, { 0x9d, 0x3, 0xa0, 0x84, 0x64, 0x50, 0xf4, 0x72 } };
+// {716AEDB1-C9C3-4B4D-8332-6F65D44AF6A8}
+static const GUID sLayerManagerCount = 
+{ 0x716aedb1, 0xc9c3, 0x4b4d, { 0x83, 0x32, 0x6f, 0x65, 0xd4, 0x4a, 0xf6, 0xa8 } };
 
 cairo_user_data_key_t gKeyD3D10Texture;
 
 LayerManagerD3D10::LayerManagerD3D10(nsIWidget *aWidget)
   : mWidget(aWidget)
 {
 }
 
+struct DeviceAttachments
+{
+  nsRefPtr<ID3D10Effect> mEffect;
+  nsRefPtr<ID3D10InputLayout> mInputLayout;
+  nsRefPtr<ID3D10Buffer> mVertexBuffer;
+  nsRefPtr<ReadbackManagerD3D10> mReadbackManager;
+};
+
 LayerManagerD3D10::~LayerManagerD3D10()
 {
+  if (mDevice) {
+    int referenceCount = 0;
+    UINT size = sizeof(referenceCount);
+    HRESULT hr = mDevice->GetPrivateData(sLayerManagerCount, &size, &referenceCount);
+    NS_ASSERTION(SUCCEEDED(hr), "Reference count not found on device.");
+    referenceCount--;
+    mDevice->SetPrivateData(sLayerManagerCount, sizeof(referenceCount), &referenceCount);
+
+    if (!referenceCount) {
+      DeviceAttachments *attachments;
+      size = sizeof(attachments);
+      mDevice->GetPrivateData(sDeviceAttachments, &size, &attachments);
+      // No LayerManagers left for this device. Clear out interfaces stored which
+      // hold a reference to the device.
+      mDevice->SetPrivateData(sDeviceAttachments, 0, NULL);
+
+      delete attachments;
+    }
+  }
+
   Destroy();
 }
 
 static bool
 IsOptimus()
 {
   return GetModuleHandleA("nvumdshim.dll");
 }
@@ -124,18 +151,29 @@ LayerManagerD3D10::Initialize()
   if (mNv3DVUtils) {
     IUnknown* devUnknown = NULL;
     if (mDevice) {
       mDevice->QueryInterface(IID_IUnknown, (void **)&devUnknown);
     }
     mNv3DVUtils->SetDeviceInfo(devUnknown);
   }
 
-  UINT size = sizeof(ID3D10Effect*);
-  if (FAILED(mDevice->GetPrivateData(sEffect, &size, mEffect.StartAssignment()))) {
+  int referenceCount = 0;
+  UINT size = sizeof(referenceCount);
+  // If this isn't there yet it'll fail, count will remain 0, which is correct.
+  mDevice->GetPrivateData(sLayerManagerCount, &size, &referenceCount);
+  referenceCount++;
+  mDevice->SetPrivateData(sLayerManagerCount, sizeof(referenceCount), &referenceCount);
+
+  DeviceAttachments *attachments;
+  size = sizeof(DeviceAttachments*);
+  if (FAILED(mDevice->GetPrivateData(sDeviceAttachments, &size, &attachments))) {
+    attachments = new DeviceAttachments;
+    mDevice->SetPrivateData(sDeviceAttachments, sizeof(attachments), &attachments);
+
     D3D10CreateEffectFromMemoryFunc createEffect = (D3D10CreateEffectFromMemoryFunc)
 	GetProcAddress(LoadLibraryA("d3d10_1.dll"), "D3D10CreateEffectFromMemory");
 
     if (!createEffect) {
       return false;
     }
 
     hr = createEffect((void*)g_main,
@@ -144,21 +182,18 @@ LayerManagerD3D10::Initialize()
                       mDevice,
                       NULL,
                       getter_AddRefs(mEffect));
     
     if (FAILED(hr)) {
       return false;
     }
 
-    mDevice->SetPrivateDataInterface(sEffect, mEffect);
-  }
-
-  size = sizeof(ID3D10InputLayout*);
-  if (FAILED(mDevice->GetPrivateData(sInputLayout, &size, mInputLayout.StartAssignment()))) {
+    attachments->mEffect = mEffect;
+  
     D3D10_INPUT_ELEMENT_DESC layout[] =
     {
       { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 },
     };
     D3D10_PASS_DESC passDesc;
     mEffect->GetTechniqueByName("RenderRGBLayerPremul")->GetPassByIndex(0)->
       GetDesc(&passDesc);
 
@@ -167,33 +202,34 @@ LayerManagerD3D10::Initialize()
                                     passDesc.pIAInputSignature,
                                     passDesc.IAInputSignatureSize,
                                     getter_AddRefs(mInputLayout));
     
     if (FAILED(hr)) {
       return false;
     }
 
-    mDevice->SetPrivateDataInterface(sInputLayout, mInputLayout);
-  }
-
-  size = sizeof(ID3D10Buffer*);
-  if (FAILED(mDevice->GetPrivateData(sVertexBuffer, &size, mVertexBuffer.StartAssignment()))) {
+    attachments->mInputLayout = mInputLayout;
+  
     Vertex vertices[] = { {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}, {1.0, 1.0} };
     CD3D10_BUFFER_DESC bufferDesc(sizeof(vertices), D3D10_BIND_VERTEX_BUFFER);
     D3D10_SUBRESOURCE_DATA data;
     data.pSysMem = (void*)vertices;
 
     hr = mDevice->CreateBuffer(&bufferDesc, &data, getter_AddRefs(mVertexBuffer));
 
     if (FAILED(hr)) {
       return false;
     }
 
-    mDevice->SetPrivateDataInterface(sVertexBuffer, mVertexBuffer);
+    attachments->mVertexBuffer = mVertexBuffer;
+  } else {
+    mEffect = attachments->mEffect;
+    mVertexBuffer = attachments->mVertexBuffer;
+    mInputLayout = attachments->mInputLayout;
   }
 
   nsRefPtr<IDXGIDevice> dxgiDevice;
   nsRefPtr<IDXGIAdapter> dxgiAdapter;
   nsRefPtr<IDXGIFactory> dxgiFactory;
 
   mDevice->QueryInterface(dxgiDevice.StartAssignment());
   dxgiDevice->GetAdapter(getter_AddRefs(dxgiAdapter));
@@ -328,16 +364,23 @@ LayerManagerD3D10::CreateColorLayer()
 
 already_AddRefed<CanvasLayer>
 LayerManagerD3D10::CreateCanvasLayer()
 {
   nsRefPtr<CanvasLayer> layer = new CanvasLayerD3D10(this);
   return layer.forget();
 }
 
+already_AddRefed<ReadbackLayer>
+LayerManagerD3D10::CreateReadbackLayer()
+{
+  nsRefPtr<ReadbackLayer> layer = new ReadbackLayerD3D10(this);
+  return layer.forget();
+}
+
 already_AddRefed<ImageContainer>
 LayerManagerD3D10::CreateImageContainer()
 {
   nsRefPtr<ImageContainer> layer = new ImageContainerD3D10(mDevice);
   return layer.forget();
 }
 
 static void ReleaseTexture(void *texture)
@@ -377,16 +420,23 @@ LayerManagerD3D10::CreateOptimalSurface(
 
   surface->SetData(&gKeyD3D10Texture,
                    texture.forget().get(),
                    ReleaseTexture);
 
   return surface.forget();
 }
 
+ReadbackManagerD3D10*
+LayerManagerD3D10::readbackManager()
+{
+  EnsureReadbackManager();
+  return mReadbackManager;
+}
+
 void
 LayerManagerD3D10::SetViewport(const nsIntSize &aViewport)
 {
   mViewport = aViewport;
 
   D3D10_VIEWPORT viewport;
   viewport.MaxDepth = 1.0f;
   viewport.MinDepth = 0;
@@ -491,16 +541,42 @@ LayerManagerD3D10::VerifyBufferSize()
     mSwapChain->ResizeBuffers(1, rect.width, rect.height,
                               DXGI_FORMAT_B8G8R8A8_UNORM,
                               DXGI_SWAP_CHAIN_FLAG_GDI_COMPATIBLE);
   }
 
 }
 
 void
+LayerManagerD3D10::EnsureReadbackManager()
+{
+  if (mReadbackManager) {
+    return;
+  }
+
+  DeviceAttachments *attachments;
+  UINT size = sizeof(DeviceAttachments*);
+  if (FAILED(mDevice->GetPrivateData(sDeviceAttachments, &size, &attachments))) {
+    // Strange! This shouldn't happen ... return a readback manager for this
+    // layer manager only.
+    mReadbackManager = new ReadbackManagerD3D10();
+    gfx::LogFailure(NS_LITERAL_CSTRING("Couldn't get device attachments for device."));
+    return;
+  }
+
+  if (attachments->mReadbackManager) {
+    mReadbackManager = attachments->mReadbackManager;
+    return;
+  }
+
+  mReadbackManager = new ReadbackManagerD3D10();
+  attachments->mReadbackManager = mReadbackManager;
+}
+
+void
 LayerManagerD3D10::Render()
 {
   if (mRoot) {
     static_cast<LayerD3D10*>(mRoot->ImplData())->Validate();
   }
 
   SetupPipeline();
 
--- a/gfx/layers/d3d10/LayerManagerD3D10.h
+++ b/gfx/layers/d3d10/LayerManagerD3D10.h
@@ -41,16 +41,18 @@
 #include "Layers.h"
 
 #include <windows.h>
 #include <d3d10_1.h>
 
 #include "gfxContext.h"
 #include "nsIWidget.h"
 
+#include "ReadbackManagerD3D10.h"
+
 namespace mozilla {
 namespace layers {
 
 class Nv3DVUtils;
 
 /**
  * This structure is used to pass rectangles to our shader constant. We can use
  * this for passing rectangular areas to SetVertexShaderConstant. In the format
@@ -118,16 +120,18 @@ public:
   virtual already_AddRefed<ContainerLayer> CreateContainerLayer();
 
   virtual already_AddRefed<ImageLayer> CreateImageLayer();
 
   virtual already_AddRefed<ColorLayer> CreateColorLayer();
 
   virtual already_AddRefed<CanvasLayer> CreateCanvasLayer();
 
+  virtual already_AddRefed<ReadbackLayer> CreateReadbackLayer();
+
   virtual already_AddRefed<ImageContainer> CreateImageContainer();
 
   virtual already_AddRefed<gfxASurface>
     CreateOptimalSurface(const gfxIntSize &aSize,
                          gfxASurface::gfxImageFormat imageFormat);
 
   virtual LayersBackend GetBackendType() { return LAYERS_D3D10; }
   virtual void GetBackendName(nsAString& name) { name.AssignLiteral("Direct3D 10"); }
@@ -137,38 +141,42 @@ public:
 #endif // MOZ_LAYERS_HAVE_LOG
 
   // Public helpers
 
   ID3D10Device1 *device() const { return mDevice; }
 
   ID3D10Effect *effect() const { return mEffect; }
 
+  ReadbackManagerD3D10 *readbackManager();
+
   void SetViewport(const nsIntSize &aViewport);
   const nsIntSize &GetViewport() { return mViewport; }
 
   /**
    * Return pointer to the Nv3DVUtils instance
    */
   Nv3DVUtils *GetNv3DVUtils()  { return mNv3DVUtils; }
 
   static void LayerManagerD3D10::ReportFailure(const nsACString &aMsg, HRESULT aCode);
 
 private:
   void SetupPipeline();
   void UpdateRenderTarget();
   void VerifyBufferSize();
+  void EnsureReadbackManager();
 
   void Render();
 
   nsRefPtr<ID3D10Device1> mDevice;
 
   nsRefPtr<ID3D10Effect> mEffect;
   nsRefPtr<ID3D10InputLayout> mInputLayout;
   nsRefPtr<ID3D10Buffer> mVertexBuffer;
+  nsRefPtr<ReadbackManagerD3D10> mReadbackManager;
 
   nsRefPtr<ID3D10RenderTargetView> mRTView;
 
   nsRefPtr<IDXGISwapChain> mSwapChain;
 
   nsIWidget *mWidget;
 
   CallbackInfo mCurrentCallbackInfo;
new file mode 100644
--- /dev/null
+++ b/gfx/layers/d3d10/ReadbackLayerD3D10.h
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Corporation code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Bas Schouten <bschouten@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef GFX_READBACKLAYERD3D10_H
+#define GFX_READBACKLAYERD3D10_H
+
+#include "LayerManagerD3D10.h"
+#include "ReadbackLayer.h"
+
+namespace mozilla {
+namespace layers {
+
+class THEBES_API ReadbackLayerD3D10 :
+  public ReadbackLayer,
+  public LayerD3D10
+{
+public:
+    ReadbackLayerD3D10(LayerManagerD3D10 *aManager)
+    : ReadbackLayer(aManager, NULL),
+      LayerD3D10(aManager)
+  {
+      mImplData = static_cast<LayerD3D10*>(this);
+  }
+
+  virtual Layer* GetLayer() { return this; }
+  virtual void RenderLayer() {}
+};
+
+} /* layers */
+} /* mozilla */
+#endif /* GFX_READBACKLAYERD3D10_H */
new file mode 100644
--- /dev/null
+++ b/gfx/layers/d3d10/ReadbackManagerD3D10.cpp
@@ -0,0 +1,250 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Corporation code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Bas Schouten <bschouten@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "ReadbackManagerD3D10.h"
+#include "ReadbackProcessor.h"
+
+#include "nsIThread.h"
+#include "nsThreadUtils.h"
+#include "gfxImageSurface.h"
+
+namespace mozilla {
+namespace layers {
+
+// Structure that contains the information required to execute a readback task,
+// the only member accessed off the main thread here is mReadbackTexture. Since
+// mLayer may be released only on the main thread this object should always be
+// destroyed on the main thread!
+struct ReadbackTask {
+  // The texture that we copied the contents of the thebeslayer to.
+  nsRefPtr<ID3D10Texture2D> mReadbackTexture;
+  // This exists purely to keep the ReadbackLayer alive for the lifetime of
+  // mUpdate. Note that this addref and release should occur -solely- on the
+  // main thread.
+  nsRefPtr<ReadbackLayer> mLayer;
+  ReadbackProcessor::Update mUpdate;
+  // The origin in ThebesLayer coordinates of mReadbackTexture.
+  gfxPoint mOrigin;
+  // mLayer->GetBackgroundOffset() when the task is created.  We have
+  // to save this in the ReadbackTask because it might change before
+  // the update is delivered to the readback sink.
+  nsIntPoint mBackgroundOffset;
+};
+
+// This class is created and dispatched from the Readback thread but it must be
+// destroyed by the main thread.
+class ReadbackResultWriter : public nsIRunnable
+{
+  NS_DECL_ISUPPORTS
+public:
+  ReadbackResultWriter(ReadbackTask *aTask) : mTask(aTask) {}
+
+  NS_IMETHODIMP Run()
+  {
+    ReadbackProcessor::Update *update = &mTask->mUpdate;
+
+    if (!update->mLayer->GetSink()) {
+      // This can happen when a plugin is destroyed.
+      return NS_OK;
+    }
+
+    nsIntPoint offset = mTask->mBackgroundOffset;
+
+    D3D10_TEXTURE2D_DESC desc;
+    mTask->mReadbackTexture->GetDesc(&desc);
+
+    D3D10_MAPPED_TEXTURE2D mappedTex;
+    // We know this map will immediately succeed, as we've already mapped this
+    // copied data on our task thread.
+    HRESULT hr = mTask->mReadbackTexture->Map(0, D3D10_MAP_READ, 0, &mappedTex);
+
+    if (FAILED(hr)) {
+      // If this fails we're never going to get our ThebesLayer content.
+      update->mLayer->GetSink()->SetUnknown(update->mSequenceCounter);
+      return NS_OK;
+    }
+
+    nsRefPtr<gfxImageSurface> sourceSurface =
+      new gfxImageSurface((unsigned char*)mappedTex.pData,
+                          gfxIntSize(desc.Width, desc.Height),
+                          mappedTex.RowPitch,
+                          gfxASurface::ImageFormatRGB24);
+
+    nsRefPtr<gfxContext> ctx =
+      update->mLayer->GetSink()->BeginUpdate(update->mUpdateRect + offset,
+                                             update->mSequenceCounter);
+
+    if (ctx) {
+      ctx->Translate(gfxPoint(offset.x, offset.y));
+      ctx->SetSource(sourceSurface, gfxPoint(mTask->mOrigin.x,
+                                             mTask->mOrigin.y));
+      ctx->Paint();
+
+      update->mLayer->GetSink()->EndUpdate(ctx, update->mUpdateRect + offset);
+    }
+
+    mTask->mReadbackTexture->Unmap(0);
+
+    return NS_OK;
+  }
+
+private:
+  nsAutoPtr<ReadbackTask> mTask;
+};
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(ReadbackResultWriter, nsIRunnable)
+
+DWORD WINAPI StartTaskThread(void *aManager)
+{
+  static_cast<ReadbackManagerD3D10*>(aManager)->ProcessTasks();
+
+  return 0;
+}
+
+ReadbackManagerD3D10::ReadbackManagerD3D10()
+  : mRefCnt(0)
+{
+  ::InitializeCriticalSection(&mTaskMutex);
+  mShutdownEvent = ::CreateEventA(NULL, FALSE, FALSE, "ReadbackShutdownEvent");
+  mTaskSemaphore = ::CreateSemaphoreA(NULL, 0, 1000000, "ReadbackTaskSemaphore");
+  mTaskThread = ::CreateThread(NULL, 0, StartTaskThread, this, 0, 0);
+}
+
+ReadbackManagerD3D10::~ReadbackManagerD3D10()
+{
+  ::SetEvent(mShutdownEvent);
+
+  // This shouldn't take longer than 5 seconds, if it does we're going to choose
+  // to leak the thread and its synchronisation in favor of crashing or freezing
+  DWORD result = ::WaitForSingleObject(mTaskThread, 5000);
+  if (result != WAIT_TIMEOUT) {
+    ::DeleteCriticalSection(&mTaskMutex);
+    ::CloseHandle(mShutdownEvent);
+    ::CloseHandle(mTaskSemaphore);
+    ::CloseHandle(mTaskThread);
+  } else {
+    NS_WARNING("ReadbackManager: Task thread did not shutdown in 5 seconds. Leaking.");
+  }
+}
+
+void
+ReadbackManagerD3D10::PostTask(ID3D10Texture2D *aTexture, void *aUpdate, const gfxPoint &aOrigin)
+{
+  ReadbackTask *task = new ReadbackTask;
+  task->mReadbackTexture = aTexture;
+  task->mUpdate = *static_cast<ReadbackProcessor::Update*>(aUpdate);
+  task->mOrigin = aOrigin;
+  task->mLayer = task->mUpdate.mLayer;
+  task->mBackgroundOffset = task->mLayer->GetBackgroundLayerOffset();
+
+  ::EnterCriticalSection(&mTaskMutex);
+  mPendingReadbackTasks.AppendElement(task);
+  ::LeaveCriticalSection(&mTaskMutex);
+
+  ::ReleaseSemaphore(mTaskSemaphore, 1, NULL);
+}
+
+HRESULT
+ReadbackManagerD3D10::QueryInterface(REFIID riid, void **ppvObject)
+{
+  if (!ppvObject) {
+    return E_POINTER;
+  }
+
+  if (riid == IID_IUnknown) {
+    *ppvObject = this;
+  } else {
+    return E_NOINTERFACE;
+  }
+
+  return S_OK;
+}
+
+ULONG
+ReadbackManagerD3D10::AddRef()
+{
+  NS_ASSERTION(NS_IsMainThread(),
+    "ReadbackManagerD3D10 should only be refcounted on main thread.");
+  return ++mRefCnt;
+}
+
+ULONG
+ReadbackManagerD3D10::Release()
+{
+  NS_ASSERTION(NS_IsMainThread(),
+    "ReadbackManagerD3D10 should only be refcounted on main thread.");
+  ULONG newRefCnt = --mRefCnt;
+  if (!newRefCnt) {
+    mRefCnt++;
+    delete this;
+  }
+  return newRefCnt;
+}
+
+void
+ReadbackManagerD3D10::ProcessTasks()
+{
+  HANDLE handles[] = { mTaskSemaphore, mShutdownEvent };
+  
+  while (true) {
+    DWORD result = ::WaitForMultipleObjects(2, handles, FALSE, INFINITE);
+    if (result != WAIT_OBJECT_0) {
+      return;
+    }
+
+    ::EnterCriticalSection(&mTaskMutex);
+    ReadbackTask *nextReadbackTask = mPendingReadbackTasks[0].forget();
+    mPendingReadbackTasks.RemoveElementAt(0);
+    ::LeaveCriticalSection(&mTaskMutex);
+
+    // We want to block here until the texture contents are available, the
+    // easiest thing is to simply map and unmap.
+    D3D10_MAPPED_TEXTURE2D mappedTex;
+    nextReadbackTask->mReadbackTexture->Map(0, D3D10_MAP_READ, 0, &mappedTex);
+    nextReadbackTask->mReadbackTexture->Unmap(0);
+
+    // We can only send the update to the sink on the main thread, so post an
+    // event there to do so. Ownership of the task is passed from
+    // mPendingReadbackTasks to ReadbackResultWriter here.
+    nsCOMPtr<nsIThread> thread = do_GetMainThread();
+    thread->Dispatch(new ReadbackResultWriter(nextReadbackTask),
+                     nsIEventTarget::DISPATCH_NORMAL);
+  }
+}
+
+}
+}
new file mode 100644
--- /dev/null
+++ b/gfx/layers/d3d10/ReadbackManagerD3D10.h
@@ -0,0 +1,107 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Corporation code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Bas Schouten <bschouten@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef GFX_READBACKMANAGERD3D10_H
+#define GFX_READBACKMANAGERD3D10_H
+
+#include <windows.h>
+#include <d3d10_1.h>
+
+#include "nsTArray.h"
+#include "nsAutoPtr.h"
+#include "gfxPoint.h"
+
+namespace mozilla {
+namespace layers {
+
+DWORD WINAPI StartTaskThread(void *aManager);
+
+struct ReadbackTask;
+
+class ReadbackManagerD3D10 : public IUnknown
+{
+public:
+  ReadbackManagerD3D10();
+  ~ReadbackManagerD3D10();
+
+  /**
+   * Tell the readback manager to post a readback task.
+   *
+   * @param aTexture D3D10_USAGE_STAGING texture that will contain the data that
+   *                 was readback.
+   * @param aUpdate  ReadbackProcessor::Update object. This is a void pointer
+   *                 since we cannot forward declare a nested class, and do not
+   *                 export ReadbackProcessor.h
+   * @param aOrigin  Origin of the aTexture surface in the ThebesLayer
+   *                 coordinate system.
+   */
+  void PostTask(ID3D10Texture2D *aTexture, void *aUpdate, const gfxPoint &aOrigin);
+
+  virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid,
+                                                   void **ppvObject);
+  virtual ULONG STDMETHODCALLTYPE AddRef(void);
+  virtual ULONG STDMETHODCALLTYPE Release(void);
+
+private:
+  friend DWORD WINAPI StartTaskThread(void *aManager);
+
+  void ProcessTasks();
+
+  // The invariant maintained by |mTaskSemaphore| is that the readback thread
+  // will awaken from WaitForMultipleObjects() at least once per readback 
+  // task enqueued by the main thread.  Since the readback thread processes
+  // exactly one task per wakeup (with one exception), no tasks are lost.  The
+  // exception is when the readback thread is shut down, which orphans the
+  // remaining tasks, on purpose.
+  HANDLE mTaskSemaphore;
+  // Event signaled when the task thread should shutdown
+  HANDLE mShutdownEvent;
+  // Handle to the task thread
+  HANDLE mTaskThread;
+
+  // FiFo list of readback tasks that are to be executed. Access is synchronized
+  // by mTaskMutex.
+  CRITICAL_SECTION mTaskMutex;
+  nsTArray<nsAutoPtr<ReadbackTask>> mPendingReadbackTasks;
+
+  ULONG mRefCnt;
+};
+
+}
+}
+
+#endif /* GFX_READBACKMANAGERD3D10_H */
\ No newline at end of file
--- a/gfx/layers/d3d10/ThebesLayerD3D10.cpp
+++ b/gfx/layers/d3d10/ThebesLayerD3D10.cpp
@@ -40,16 +40,18 @@
 
 #include "gfxWindowsPlatform.h"
 #ifdef CAIRO_HAS_D2D_SURFACE
 #include "gfxD2DSurface.h"
 #endif
 
 #include "gfxTeeSurface.h"
 #include "gfxUtils.h"
+#include "ReadbackLayer.h"
+#include "ReadbackProcessor.h"
 
 namespace mozilla {
 namespace layers {
 
 ThebesLayerD3D10::ThebesLayerD3D10(LayerManagerD3D10 *aManager)
   : ThebesLayer(aManager, NULL)
   , LayerD3D10(aManager)
 {
@@ -163,17 +165,17 @@ ThebesLayerD3D10::RenderLayer()
   }
 
   // Set back to default.
   effect()->GetVariableByName("vTextureCoords")->AsVector()->
     SetFloatVector(ShaderConstantRectD3D10(0, 0, 1.0f, 1.0f));
 }
 
 void
-ThebesLayerD3D10::Validate()
+ThebesLayerD3D10::Validate(ReadbackProcessor *aReadback)
 {
   if (mVisibleRegion.IsEmpty()) {
     return;
   }
 
   SurfaceMode mode = GetSurfaceMode();
   if (mode == SURFACE_COMPONENT_ALPHA &&
       (!mParent || !mParent->SupportsComponentAlphaChildren())) {
@@ -186,16 +188,22 @@ ThebesLayerD3D10::Validate()
   GetDesiredResolutions(xres, yres);
 
   // If our resolution changed, we need new sized textures, delete the old ones.
   if (ResolutionChanged(xres, yres)) {
       mTexture = nsnull;
       mTextureOnWhite = nsnull;
   }
 
+  nsTArray<ReadbackProcessor::Update> readbackUpdates;
+  nsIntRegion readbackRegion;
+  if (aReadback && UsedForReadback()) {
+    aReadback->GetThebesLayerUpdates(this, &readbackUpdates, &readbackRegion);
+  }
+
   nsIntRect visibleRect = mVisibleRegion.GetBounds();
 
   if (mTexture) {
     if (!mTextureRegion.IsEqual(mVisibleRegion)) {
       nsRefPtr<ID3D10Texture2D> oldTexture = mTexture;
       mTexture = nsnull;
       nsRefPtr<ID3D10Texture2D> oldTextureOnWhite = mTextureOnWhite;
       mTextureOnWhite = nsnull;
@@ -259,16 +267,33 @@ ThebesLayerD3D10::Validate()
      * This is an issue for opaque surfaces, which otherwise won't get their
      * background painted.
      */
     nsIntRegion region;
     region.Sub(mVisibleRegion, mValidRegion);
 
     DrawRegion(region, mode);
 
+    if (readbackUpdates.Length() > 0) {
+      CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM,
+                                 visibleRect.width, visibleRect.height,
+                                 1, 1, 0, D3D10_USAGE_STAGING,
+                                 D3D10_CPU_ACCESS_READ);
+
+      nsRefPtr<ID3D10Texture2D> readbackTexture;
+      device()->CreateTexture2D(&desc, NULL, getter_AddRefs(readbackTexture));
+      device()->CopyResource(readbackTexture, mTexture);
+
+      for (int i = 0; i < readbackUpdates.Length(); i++) {
+        mD3DManager->readbackManager()->PostTask(readbackTexture,
+                                                 &readbackUpdates[i],
+                                                 gfxPoint(visibleRect.x, visibleRect.y));
+      }
+    }
+
     mValidRegion = mVisibleRegion;
   }
 }
 
 void
 ThebesLayerD3D10::LayerManagerDestroyed()
 {
   mD3DManager = nsnull;
--- a/gfx/layers/d3d10/ThebesLayerD3D10.h
+++ b/gfx/layers/d3d10/ThebesLayerD3D10.h
@@ -46,23 +46,25 @@ namespace layers {
 
 class ThebesLayerD3D10 : public ThebesLayer,
                          public LayerD3D10
 {
 public:
   ThebesLayerD3D10(LayerManagerD3D10 *aManager);
   virtual ~ThebesLayerD3D10();
 
+  void Validate(ReadbackProcessor *aReadback);
+
   /* ThebesLayer implementation */
   void InvalidateRegion(const nsIntRegion& aRegion);
 
   /* LayerD3D10 implementation */
   virtual Layer* GetLayer();
   virtual void RenderLayer();
-  virtual void Validate();
+  virtual void Validate() { Validate(nsnull); }
   virtual void LayerManagerDestroyed();
 
 private:
   /* Texture with our surface data */
   nsRefPtr<ID3D10Texture2D> mTexture;
 
   /* Shader resource view for our texture */
   nsRefPtr<ID3D10ShaderResourceView> mSRView;