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 id18641
push usercjones@mozilla.com
push dateWed, 09 Feb 2011 00:44:41 +0000
treeherdermozilla-central@fa1a4b6abff0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscjones, jrmuizel
bugs626602
milestone2.0b12pre
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 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;