Bug 1607352 - Support DirectComposition virtual surface API. r=sotaro
authorGlenn Watson <git@intuitionlibrary.com>
Tue, 07 Jan 2020 20:42:58 +0000
changeset 509215 1d212c2ddb76f5d54fd884e8e08001fc8475e13c
parent 509214 6cad172ad4bcf77bcace164816a8955802a6770d
child 509216 9f0cfd2aaa21590530f222b71ce648a6bf80707e
push id36993
push userdluca@mozilla.com
push dateWed, 08 Jan 2020 09:41:58 +0000
treeherdermozilla-central@12fb5e522dd3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssotaro
bugs1607352
milestone74.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1607352 - Support DirectComposition virtual surface API. r=sotaro Adds an #ifdef to the DCLayerTree implementation that allows selecting whether to use the virtual surface API (enabled by default) or the regular DC surface API. For now, this is a compile-time switch. As a follow up to this, we will support both options at runtime (for example, using the regular surface API for surfaces that have holes or translucency). Differential Revision: https://phabricator.services.mozilla.com/D58870
gfx/webrender_bindings/DCLayerTree.cpp
gfx/webrender_bindings/DCLayerTree.h
gfx/wr/example-compositor/compositor-windows/src/lib.cpp
--- a/gfx/webrender_bindings/DCLayerTree.cpp
+++ b/gfx/webrender_bindings/DCLayerTree.cpp
@@ -45,21 +45,25 @@ UniquePtr<DCLayerTree> DCLayerTree::Crea
 DCLayerTree::DCLayerTree(gl::GLContext* aGL, EGLConfig aEGLConfig,
                          ID3D11Device* aDevice,
                          IDCompositionDevice2* aCompositionDevice)
     : mGL(aGL),
       mEGLConfig(aEGLConfig),
       mDevice(aDevice),
       mCompositionDevice(aCompositionDevice),
       mDebugCounter(false),
-      mDebugVisualRedrawRegions(false) {}
+      mDebugVisualRedrawRegions(false),
+      mEGLImage(EGL_NO_IMAGE),
+      mColorRBO(0) {}
 
 DCLayerTree::~DCLayerTree() {
   const auto gl = GetGLContext();
 
+  DestroyEGLSurface();
+
   // Delete any cached FBO objects
   for (auto it = mFrameBuffers.begin(); it != mFrameBuffers.end(); ++it) {
     gl->fDeleteRenderbuffers(1, &it->depthRboId);
     gl->fDeleteFramebuffers(1, &it->fboId);
   }
 }
 
 bool DCLayerTree::Initialize(HWND aHwnd) {
@@ -205,33 +209,44 @@ void DCLayerTree::CompositorEndFrame() {
   mCurrentLayers.clear();
 
   mCompositionDevice->Commit();
 }
 
 void DCLayerTree::Bind(wr::NativeTileId aId, wr::DeviceIntPoint* aOffset,
                        uint32_t* aFboId, wr::DeviceIntRect aDirtyRect) {
   auto surface = GetSurface(aId.surface_id);
-  auto layer = surface->GetLayer(aId.x, aId.y);
+  wr::DeviceIntPoint targetOffset{0, 0};
 
-  *aFboId = layer->CreateEGLSurfaceForCompositionSurface(aDirtyRect, aOffset);
-  mCurrentId = Some(aId);
+#ifdef USE_VIRTUAL_SURFACES
+  wr::DeviceIntSize tileSize = surface->GetTileSize();
+  RefPtr<IDCompositionSurface> compositionSurface =
+      surface->GetCompositionSurface();
+  targetOffset.x = VIRTUAL_OFFSET + tileSize.width * aId.x;
+  targetOffset.y = VIRTUAL_OFFSET + tileSize.height * aId.y;
+#else
+  auto layer = surface->GetLayer(aId.x, aId.y);
+  RefPtr<IDCompositionSurface> compositionSurface =
+      layer->GetCompositionSurface();
+#endif
+
+  *aFboId = CreateEGLSurfaceForCompositionSurface(
+      aDirtyRect, aOffset, compositionSurface, targetOffset);
+  mCurrentSurface = Some(compositionSurface);
 }
 
 void DCLayerTree::Unbind() {
-  if (mCurrentId.isNothing()) {
+  if (mCurrentSurface.isNothing()) {
     return;
   }
 
-  const auto id = mCurrentId.ref();
-  auto surface = GetSurface(id.surface_id);
-  auto layer = surface->GetLayer(id.x, id.y);
+  RefPtr<IDCompositionSurface> surface = mCurrentSurface.ref();
+  surface->EndDraw();
 
-  layer->EndDraw();
-  mCurrentId = Nothing();
+  mCurrentSurface = Nothing();
 }
 
 void DCLayerTree::CreateSurface(wr::NativeSurfaceId aId,
                                 wr::DeviceIntSize aTileSize, bool aIsOpaque) {
   auto it = mDCSurfaces.find(aId);
   MOZ_RELEASE_ASSERT(it == mDCSurfaces.end());
   if (it != mDCSurfaces.end()) {
     // DCSurface already exists.
@@ -269,16 +284,23 @@ void DCLayerTree::DestroyTile(wr::Native
 void DCLayerTree::AddSurface(wr::NativeSurfaceId aId,
                              wr::DeviceIntPoint aPosition,
                              wr::DeviceIntRect aClipRect) {
   auto it = mDCSurfaces.find(aId);
   MOZ_RELEASE_ASSERT(it != mDCSurfaces.end());
   const auto layer = it->second.get();
   const auto visual = layer->GetVisual();
 
+#ifdef USE_VIRTUAL_SURFACES
+  layer->UpdateAllocatedRect();
+
+  aPosition.x -= VIRTUAL_OFFSET;
+  aPosition.y -= VIRTUAL_OFFSET;
+#endif
+
   // Place the visual - this changes frame to frame based on scroll position
   // of the slice.
   visual->SetOffsetX(aPosition.x);
   visual->SetOffsetY(aPosition.y);
 
   // Set the clip rect - converting from world space to the pre-offset space
   // that DC requires for rectangle clips.
   D2D_RECT_F clip_rect;
@@ -331,72 +353,124 @@ GLuint DCLayerTree::GetOrCreateFbo(int a
     mFrameBuffers.push_back(frame_buffer_info);
   }
 
   return fboId;
 }
 
 DCSurface::DCSurface(wr::DeviceIntSize aTileSize, bool aIsOpaque,
                      DCLayerTree* aDCLayerTree)
-    : mDCLayerTree(aDCLayerTree), mTileSize(aTileSize), mIsOpaque(aIsOpaque) {}
+    : mDCLayerTree(aDCLayerTree),
+      mTileSize(aTileSize),
+      mIsOpaque(aIsOpaque),
+      mAllocatedRectDirty(true) {}
 
 DCSurface::~DCSurface() {}
 
 bool DCSurface::Initialize() {
   HRESULT hr;
   const auto dCompDevice = mDCLayerTree->GetCompositionDevice();
   hr = dCompDevice->CreateVisual(getter_AddRefs(mVisual));
   if (FAILED(hr)) {
     gfxCriticalNote << "Failed to create DCompositionVisual: " << gfx::hexa(hr);
     return false;
   }
 
+#ifdef USE_VIRTUAL_SURFACES
+  DXGI_ALPHA_MODE alpha_mode =
+      mIsOpaque ? DXGI_ALPHA_MODE_IGNORE : DXGI_ALPHA_MODE_PREMULTIPLIED;
+
+  hr = dCompDevice->CreateVirtualSurface(VIRTUAL_OFFSET * 2, VIRTUAL_OFFSET * 2,
+                                         DXGI_FORMAT_B8G8R8A8_UNORM, alpha_mode,
+                                         getter_AddRefs(mVirtualSurface));
+  MOZ_ASSERT(SUCCEEDED(hr));
+
+  // Bind the surface memory to this visual
+  hr = mVisual->SetContent(mVirtualSurface);
+  MOZ_ASSERT(SUCCEEDED(hr));
+#endif
+
   return true;
 }
 
 void DCSurface::CreateTile(int aX, int aY) {
   TileKey key(aX, aY);
   MOZ_RELEASE_ASSERT(mDCLayers.find(key) == mDCLayers.end());
 
   auto layer = MakeUnique<DCLayer>(mDCLayerTree);
   if (!layer->Initialize(aX, aY, mTileSize, mIsOpaque)) {
     gfxCriticalNote << "Failed to initialize DCLayer: " << aX << aY;
     return;
   }
 
+#ifdef USE_VIRTUAL_SURFACES
+  mAllocatedRectDirty = true;
+#else
   mVisual->AddVisual(layer->GetVisual(), FALSE, NULL);
+#endif
+
   mDCLayers[key] = std::move(layer);
 }
 
 void DCSurface::DestroyTile(int aX, int aY) {
   TileKey key(aX, aY);
+#ifdef USE_VIRTUAL_SURFACES
+  mAllocatedRectDirty = true;
+#else
   auto layer = GetLayer(aX, aY);
   mVisual->RemoveVisual(layer->GetVisual());
+#endif
   mDCLayers.erase(key);
 }
 
+#ifdef USE_VIRTUAL_SURFACES
+void DCSurface::UpdateAllocatedRect() {
+  if (mAllocatedRectDirty) {
+    RECT rect = {1000000, 1000000, -1000000, -1000000};
+
+    for (auto it = mDCLayers.begin(); it != mDCLayers.end(); ++it) {
+      int x = it->first.mX;
+      int y = it->first.mY;
+
+      rect.left = std::min((int)rect.left, x * mTileSize.width);
+      rect.right = std::max((int)rect.right, (x + 1) * mTileSize.width);
+
+      rect.top = std::min((int)rect.top, y * mTileSize.height);
+      rect.bottom = std::max((int)rect.bottom, (y + 1) * mTileSize.height);
+    }
+
+    rect.left += VIRTUAL_OFFSET;
+    rect.top += VIRTUAL_OFFSET;
+    rect.bottom += VIRTUAL_OFFSET;
+    rect.right += VIRTUAL_OFFSET;
+
+    mVirtualSurface->Trim(&rect, 1);
+    mAllocatedRectDirty = false;
+  }
+}
+#endif
+
 DCLayer* DCSurface::GetLayer(int aX, int aY) const {
   TileKey key(aX, aY);
   auto layer_it = mDCLayers.find(key);
   MOZ_RELEASE_ASSERT(layer_it != mDCLayers.end());
   return layer_it->second.get();
 }
 
-DCLayer::DCLayer(DCLayerTree* aDCLayerTree)
-    : mDCLayerTree(aDCLayerTree), mEGLImage(EGL_NO_IMAGE), mColorRBO(0) {}
+DCLayer::DCLayer(DCLayerTree* aDCLayerTree) : mDCLayerTree(aDCLayerTree) {}
 
-DCLayer::~DCLayer() { DestroyEGLSurface(); }
+DCLayer::~DCLayer() {}
 
 bool DCLayer::Initialize(int aX, int aY, wr::DeviceIntSize aSize,
                          bool aIsOpaque) {
   if (aSize.width <= 0 || aSize.height <= 0) {
     return false;
   }
-  mBufferSize = LayoutDeviceIntSize(aSize.width, aSize.height);
 
+#ifndef USE_VIRTUAL_SURFACES
   HRESULT hr;
   const auto dCompDevice = mDCLayerTree->GetCompositionDevice();
   hr = dCompDevice->CreateVisual(getter_AddRefs(mVisual));
   if (FAILED(hr)) {
     gfxCriticalNote << "Failed to create DCompositionVisual: " << gfx::hexa(hr);
     return false;
   }
 
@@ -410,20 +484,22 @@ bool DCLayer::Initialize(int aX, int aY,
     gfxCriticalNote << "SetContent failed: " << gfx::hexa(hr);
     return false;
   }
 
   // Position this tile at a local space offset within the parent visual
   // Scroll offsets get applied to the parent visual only.
   mVisual->SetOffsetX(aX * aSize.width);
   mVisual->SetOffsetY(aY * aSize.height);
+#endif
 
   return true;
 }
 
+#ifndef USE_VIRTUAL_SURFACES
 RefPtr<IDCompositionSurface> DCLayer::CreateCompositionSurface(
     wr::DeviceIntSize aSize, bool aIsOpaque) {
   HRESULT hr;
   const auto dCompDevice = mDCLayerTree->GetCompositionDevice();
   const auto alphaMode =
       aIsOpaque ? DXGI_ALPHA_MODE_IGNORE : DXGI_ALPHA_MODE_PREMULTIPLIED;
   RefPtr<IDCompositionSurface> compositionSurface;
 
@@ -432,55 +508,48 @@ RefPtr<IDCompositionSurface> DCLayer::Cr
                                   getter_AddRefs(compositionSurface));
   if (FAILED(hr)) {
     gfxCriticalNote << "Failed to create DCompositionSurface: "
                     << gfx::hexa(hr);
     return nullptr;
   }
   return compositionSurface;
 }
+#endif
 
-GLuint DCLayer::CreateEGLSurfaceForCompositionSurface(
-    wr::DeviceIntRect aDirtyRect, wr::DeviceIntPoint* aOffset) {
-  MOZ_ASSERT(mCompositionSurface.get());
+GLuint DCLayerTree::CreateEGLSurfaceForCompositionSurface(
+    wr::DeviceIntRect aDirtyRect, wr::DeviceIntPoint* aOffset,
+    RefPtr<IDCompositionSurface> aCompositionSurface,
+    wr::DeviceIntPoint aSurfaceOffset) {
+  MOZ_ASSERT(aCompositionSurface.get());
 
   HRESULT hr;
-  const auto gl = mDCLayerTree->GetGLContext();
+  const auto gl = GetGLContext();
   RefPtr<ID3D11Texture2D> backBuf;
   POINT offset;
 
-  LayoutDeviceIntRect dirtyRect(aDirtyRect.origin.x, aDirtyRect.origin.y,
-                                aDirtyRect.size.width, aDirtyRect.size.height);
+  RECT update_rect;
+  update_rect.left = aSurfaceOffset.x + aDirtyRect.origin.x;
+  update_rect.top = aSurfaceOffset.y + aDirtyRect.origin.y;
+  update_rect.right = update_rect.left + aDirtyRect.size.width;
+  update_rect.bottom = update_rect.top + aDirtyRect.size.height;
 
-  RECT update_rect;
-  update_rect.left = dirtyRect.X();
-  update_rect.top = dirtyRect.Y();
-  update_rect.right = dirtyRect.XMost();
-  update_rect.bottom = dirtyRect.YMost();
-
-  RECT* rect = &update_rect;
-  if (StaticPrefs::gfx_webrender_compositor_max_update_rects_AtStartup() <= 0) {
-    // Update entire surface
-    rect = nullptr;
-  }
-
-  hr = mCompositionSurface->BeginDraw(rect, __uuidof(ID3D11Texture2D),
+  hr = aCompositionSurface->BeginDraw(&update_rect, __uuidof(ID3D11Texture2D),
                                       (void**)getter_AddRefs(backBuf), &offset);
   if (FAILED(hr)) {
     gfxCriticalNote << "DCompositionSurface::BeginDraw failed: "
-                    << gfx::hexa(hr) << "dirtyRect: " << dirtyRect;
+                    << gfx::hexa(hr);
     return false;
   }
 
   // DC includes the origin of the dirty / update rect in the draw offset,
   // undo that here since WR expects it to be an absolute offset.
-  offset.x -= dirtyRect.X();
-  offset.y -= dirtyRect.Y();
+  offset.x -= aDirtyRect.origin.x;
+  offset.y -= aDirtyRect.origin.y;
 
-  // Texture size could be diffrent from mBufferSize.
   D3D11_TEXTURE2D_DESC desc;
   backBuf->GetDesc(&desc);
 
   const auto& gle = gl::GLContextEGL::Cast(gl);
   const auto& egl = gle->mEgl;
 
   const auto buffer = reinterpret_cast<EGLClientBuffer>(backBuf.get());
 
@@ -495,17 +564,17 @@ GLuint DCLayer::CreateEGLSurfaceForCompo
   gl->fGetIntegerv(LOCAL_GL_RENDERBUFFER_BINDING, &currentRboId);
 
   // Create a render buffer object that is backed by the EGL image.
   gl->fGenRenderbuffers(1, &mColorRBO);
   gl->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, mColorRBO);
   gl->fEGLImageTargetRenderbufferStorage(LOCAL_GL_RENDERBUFFER, mEGLImage);
 
   // Get or create an FBO for the specified dimensions
-  GLuint fboId = mDCLayerTree->GetOrCreateFbo(desc.Width, desc.Height);
+  GLuint fboId = GetOrCreateFbo(desc.Width, desc.Height);
 
   // Attach the new renderbuffer to the FBO
   gl->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, fboId);
   gl->fFramebufferRenderbuffer(LOCAL_GL_DRAW_FRAMEBUFFER,
                                LOCAL_GL_COLOR_ATTACHMENT0,
                                LOCAL_GL_RENDERBUFFER, mColorRBO);
 
   // Restore previous FBO and RBO bindings
@@ -513,36 +582,26 @@ GLuint DCLayer::CreateEGLSurfaceForCompo
   gl->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, currentRboId);
 
   aOffset->x = offset.x;
   aOffset->y = offset.y;
 
   return fboId;
 }
 
-void DCLayer::DestroyEGLSurface() {
-  const auto gl = mDCLayerTree->GetGLContext();
+void DCLayerTree::DestroyEGLSurface() {
+  const auto gl = GetGLContext();
 
   if (mColorRBO) {
     gl->fDeleteRenderbuffers(1, &mColorRBO);
     mColorRBO = 0;
   }
 
   if (mEGLImage) {
     const auto& gle = gl::GLContextEGL::Cast(gl);
     const auto& egl = gle->mEgl;
     egl->fDestroyImage(egl->Display(), mEGLImage);
     mEGLImage = EGL_NO_IMAGE;
   }
 }
 
-void DCLayer::EndDraw() {
-  MOZ_ASSERT(mCompositionSurface.get());
-  if (!mCompositionSurface) {
-    return;
-  }
-
-  mCompositionSurface->EndDraw();
-  DestroyEGLSurface();
-}
-
 }  // namespace wr
 }  // namespace mozilla
--- a/gfx/webrender_bindings/DCLayerTree.h
+++ b/gfx/webrender_bindings/DCLayerTree.h
@@ -18,25 +18,35 @@
 
 struct ID3D11Device;
 struct ID3D11DeviceContext;
 struct IDCompositionDevice2;
 struct IDCompositionSurface;
 struct IDCompositionTarget;
 struct IDCompositionVisual2;
 struct IDXGISwapChain1;
+struct IDCompositionVirtualSurface;
 
 namespace mozilla {
 
 namespace gl {
 class GLContext;
 }
 
 namespace wr {
 
+#define USE_VIRTUAL_SURFACES
+
+// DirectComposition virtual surfaces are zero based, but WR picture cache
+// bounds can potentially have a negative origin. Shift all the picture cache
+// coordinates by a large fixed amount, such that we don't need to re-create
+// the surface if the picture cache origin becomes negative due to adding more
+// tiles to the above / left.
+#define VIRTUAL_OFFSET 512 * 1024
+
 class DCLayer;
 class DCSurface;
 
 /**
  * DCLayerTree manages direct composition layers.
  * It does not manage gecko's layers::Layer.
  */
 class DCLayerTree {
@@ -76,31 +86,44 @@ class DCLayerTree {
 
   // Get or create an FBO with depth buffer suitable for specified dimensions
   GLuint GetOrCreateFbo(int aWidth, int aHeight);
 
  protected:
   bool Initialize(HWND aHwnd);
   bool MaybeUpdateDebugCounter();
   bool MaybeUpdateDebugVisualRedrawRegions();
+  void DestroyEGLSurface();
+  GLuint CreateEGLSurfaceForCompositionSurface(
+      wr::DeviceIntRect aDirtyRect, wr::DeviceIntPoint* aOffset,
+      RefPtr<IDCompositionSurface> aCompositionSurface,
+      wr::DeviceIntPoint aSurfaceOffset);
 
   RefPtr<gl::GLContext> mGL;
   EGLConfig mEGLConfig;
 
   RefPtr<ID3D11Device> mDevice;
 
   RefPtr<IDCompositionDevice2> mCompositionDevice;
   RefPtr<IDCompositionTarget> mCompositionTarget;
   RefPtr<IDCompositionVisual2> mRootVisual;
   RefPtr<IDCompositionVisual2> mDefaultSwapChainVisual;
 
   bool mDebugCounter;
   bool mDebugVisualRedrawRegions;
 
-  Maybe<wr::NativeTileId> mCurrentId;
+  Maybe<RefPtr<IDCompositionSurface>> mCurrentSurface;
+
+  // The EGL image that is bound to the D3D texture provided by
+  // DirectComposition.
+  EGLImage mEGLImage;
+
+  // The GL render buffer ID that maps the EGLImage to an RBO for attaching to
+  // an FBO.
+  GLuint mColorRBO;
 
   struct SurfaceIdHashFn {
     std::size_t operator()(const wr::NativeSurfaceId& aId) const {
       return HashGeneric(wr::AsUint64(aId));
     }
   };
 
   std::unordered_map<wr::NativeSurfaceId, UniquePtr<DCSurface>, SurfaceIdHashFn>
@@ -148,16 +171,26 @@ class DCSurface {
 
   struct TileKey {
     TileKey(int32_t aX, int32_t aY) : mX(aX), mY(aY) {}
 
     int32_t mX;
     int32_t mY;
   };
 
+#ifdef USE_VIRTUAL_SURFACES
+  wr::DeviceIntSize GetTileSize() const { return mTileSize; }
+
+  IDCompositionVirtualSurface* GetCompositionSurface() const {
+    return mVirtualSurface;
+  }
+
+  void UpdateAllocatedRect();
+#endif
+
  protected:
   DCLayerTree* mDCLayerTree;
 
   struct TileKeyHashFn {
     std::size_t operator()(const TileKey& aId) const {
       return HashGeneric(aId.mX, aId.mY);
     }
   };
@@ -165,57 +198,49 @@ class DCSurface {
   // The visual for this surface. No content is attached to here, but tiles
   // that belong to this surface are added as children. In this way, we can
   // set the clip and scroll offset once, on this visual, to affect all
   // children.
   RefPtr<IDCompositionVisual2> mVisual;
 
   wr::DeviceIntSize mTileSize;
   bool mIsOpaque;
+  bool mAllocatedRectDirty;
   std::unordered_map<TileKey, UniquePtr<DCLayer>, TileKeyHashFn> mDCLayers;
+
+#ifdef USE_VIRTUAL_SURFACES
+  RefPtr<IDCompositionVirtualSurface> mVirtualSurface;
+#endif
 };
 
 /**
  Represents a tile within a surface.
  TODO(gw): We should probably rename this to DCTile as a follow up.
  */
 class DCLayer {
  public:
   explicit DCLayer(DCLayerTree* aDCLayerTree);
   ~DCLayer();
   bool Initialize(int aX, int aY, wr::DeviceIntSize aSize, bool aIsOpaque);
-  GLuint CreateEGLSurfaceForCompositionSurface(wr::DeviceIntRect aDirtyRect,
-                                               wr::DeviceIntPoint* aOffset);
-  void EndDraw();
 
+#ifndef USE_VIRTUAL_SURFACES
   IDCompositionSurface* GetCompositionSurface() const {
     return mCompositionSurface;
   }
   IDCompositionVisual2* GetVisual() const { return mVisual; }
 
  protected:
   RefPtr<IDCompositionSurface> CreateCompositionSurface(wr::DeviceIntSize aSize,
                                                         bool aIsOpaque);
-  void DestroyEGLSurface();
-
-  DCLayerTree* mDCLayerTree;
 
   RefPtr<IDCompositionSurface> mCompositionSurface;
-
-  // The EGL image that is bound to the D3D texture provided by
-  // DirectComposition.
-  EGLImage mEGLImage;
+  RefPtr<IDCompositionVisual2> mVisual;
+#endif
 
-  // The GL render buffer ID that maps the EGLImage to an RBO for attaching to
-  // an FBO.
-  GLuint mColorRBO;
-
-  LayoutDeviceIntSize mBufferSize;
-
-  RefPtr<IDCompositionVisual2> mVisual;
+  DCLayerTree* mDCLayerTree;
 };
 
 static inline bool operator==(const DCSurface::TileKey& a0,
                               const DCSurface::TileKey& a1) {
   return a0.mX == a1.mX && a0.mY == a1.mY;
 }
 
 }  // namespace wr
--- a/gfx/wr/example-compositor/compositor-windows/src/lib.cpp
+++ b/gfx/wr/example-compositor/compositor-windows/src/lib.cpp
@@ -22,30 +22,35 @@
 #include "EGL/eglext_angle.h"
 #include "GL/gl.h"
 #include "GLES/gl.h"
 #include "GLES/glext.h"
 #include "GLES3/gl3.h"
 
 #define NUM_QUERIES 2
 
+#define USE_VIRTUAL_SURFACES
+#define VIRTUAL_OFFSET 512 * 1024
+
 enum SyncMode {
     None = 0,
     Swap = 1,
     Commit = 2,
     Flush = 3,
     Query = 4,
 };
 
 // The OS compositor representation of a picture cache tile.
 struct Tile {
+#ifndef USE_VIRTUAL_SURFACES
     // Represents the underlying DirectComposition surface texture that gets drawn into.
     IDCompositionSurface *pSurface;
     // Represents the node in the visual tree that defines the properties of this tile (clip, position etc).
     IDCompositionVisual2 *pVisual;
+#endif
 };
 
 struct TileKey {
     int x;
     int y;
 
     TileKey(int ax, int ay) : x(ax), y(ay) {}
 };
@@ -61,16 +66,19 @@ struct TileKeyHasher {
 };
 
 struct Surface {
     int tile_width;
     int tile_height;
     bool is_opaque;
     std::unordered_map<TileKey, Tile, TileKeyHasher> tiles;
     IDCompositionVisual2 *pVisual;
+#ifdef USE_VIRTUAL_SURFACES
+    IDCompositionVirtualSurface *pVirtualSurface;
+#endif
 };
 
 struct CachedFrameBuffer {
     int width;
     int height;
     GLuint fboId;
     GLuint depthRboId;
 };
@@ -211,17 +219,18 @@ extern "C" {
 
         LPCWSTR name;
         if (enable_compositor) {
             name = L"example-compositor (DirectComposition)";
         } else {
             name = L"example-compositor (Simple)";
         }
 
-        window->hWnd = CreateWindow(
+        window->hWnd = CreateWindowEx(
+            WS_EX_NOREDIRECTIONBITMAP,
             CLASS_NAME,
             name,
             WS_OVERLAPPEDWINDOW,
             CW_USEDEFAULT,
             CW_USEDEFAULT,
             window_width,
             window_height,
             NULL,
@@ -374,20 +383,22 @@ extern "C" {
 
         return window;
     }
 
     void com_dc_destroy_window(Window *window) {
         for (auto surface_it=window->surfaces.begin() ; surface_it != window->surfaces.end() ; ++surface_it) {
             Surface &surface = surface_it->second;
 
+#ifndef USE_VIRTUAL_SURFACES
             for (auto tile_it=surface.tiles.begin() ; tile_it != surface.tiles.end() ; ++tile_it) {
                 tile_it->second.pSurface->Release();
                 tile_it->second.pVisual->Release();
             }
+#endif
 
             surface.pVisual->Release();
         }
 
         if (window->fb_surface != EGL_NO_SURFACE) {
             eglDestroySurface(window->EGLDisplay, window->fb_surface);
         }
         eglDestroyContext(window->EGLDisplay, window->EGLContext);
@@ -475,16 +486,33 @@ extern "C" {
         surface.tile_width = tile_width;
         surface.tile_height = tile_height;
         surface.is_opaque = is_opaque;
 
         // Create the visual node in the DC tree that stores properties
         HRESULT hr = window->pDCompDevice->CreateVisual(&surface.pVisual);
         assert(SUCCEEDED(hr));
 
+#ifdef USE_VIRTUAL_SURFACES
+        DXGI_ALPHA_MODE alpha_mode = surface.is_opaque ? DXGI_ALPHA_MODE_IGNORE : DXGI_ALPHA_MODE_PREMULTIPLIED;
+
+        hr = window->pDCompDevice->CreateVirtualSurface(
+            VIRTUAL_OFFSET * 2,
+            VIRTUAL_OFFSET * 2,
+            DXGI_FORMAT_B8G8R8A8_UNORM,
+            alpha_mode,
+            &surface.pVirtualSurface
+        );
+        assert(SUCCEEDED(hr));
+
+        // Bind the surface memory to this visual
+        hr = surface.pVisual->SetContent(surface.pVirtualSurface);
+        assert(SUCCEEDED(hr));
+#endif
+
         window->surfaces[id] = surface;
     }
 
     void com_dc_create_tile(
         Window *window,
         uint64_t id,
         int x,
         int y
@@ -492,16 +520,17 @@ extern "C" {
         assert(window->surfaces.count(id) == 1);
         Surface &surface = window->surfaces[id];
 
         TileKey key(x, y);
         assert(surface.tiles.count(key) == 0);
 
         Tile tile;
 
+#ifndef USE_VIRTUAL_SURFACES
         // Create the video memory surface.
         DXGI_ALPHA_MODE alpha_mode = surface.is_opaque ? DXGI_ALPHA_MODE_IGNORE : DXGI_ALPHA_MODE_PREMULTIPLIED;
         HRESULT hr = window->pDCompDevice->CreateSurface(
             surface.tile_width,
             surface.tile_height,
             DXGI_FORMAT_B8G8R8A8_UNORM,
             alpha_mode,
             &tile.pSurface
@@ -522,16 +551,17 @@ extern "C" {
         tile.pVisual->SetOffsetX(offset_x);
         tile.pVisual->SetOffsetY(offset_y);
 
         surface.pVisual->AddVisual(
             tile.pVisual,
             FALSE,
             NULL
         );
+#endif
 
         surface.tiles[key] = tile;
     }
 
     void com_dc_destroy_tile(
         Window *window,
         uint64_t id,
         int x,
@@ -539,38 +569,44 @@ extern "C" {
     ) {
         assert(window->surfaces.count(id) == 1);
         Surface &surface = window->surfaces[id];
 
         TileKey key(x, y);
         assert(surface.tiles.count(key) == 1);
         Tile &tile = surface.tiles[key];
 
+#ifndef USE_VIRTUAL_SURFACES
         surface.pVisual->RemoveVisual(tile.pVisual);
 
         tile.pVisual->Release();
         tile.pSurface->Release();
+#endif
 
         surface.tiles.erase(key);
     }
 
     void com_dc_destroy_surface(
         Window *window,
         uint64_t id
     ) {
         assert(window->surfaces.count(id) == 1);
         Surface &surface = window->surfaces[id];
 
         window->pRoot->RemoveVisual(surface.pVisual);
 
+#ifdef USE_VIRTUAL_SURFACES
+        surface.pVirtualSurface->Release();
+#else
         // Release the video memory and visual in the tree
         for (auto tile_it=surface.tiles.begin() ; tile_it != surface.tiles.end() ; ++tile_it) {
             tile_it->second.pSurface->Release();
             tile_it->second.pVisual->Release();
         }
+#endif
 
         surface.pVisual->Release();
         window->surfaces.erase(id);
     }
 
     // Bind a DC surface to allow issuing GL commands to it
     GLuint com_dc_bind_surface(
         Window *window,
@@ -586,41 +622,61 @@ extern "C" {
     ) {
         assert(window->surfaces.count(surface_id) == 1);
         Surface &surface = window->surfaces[surface_id];
 
         TileKey key(tile_x, tile_y);
         assert(surface.tiles.count(key) == 1);
         Tile &tile = surface.tiles[key];
 
-        // Store the current surface for unbinding later
-        window->pCurrentSurface = tile.pSurface;
-
         // Inform DC that we want to draw on this surface. DC uses texture
         // atlases when the tiles are small. It returns an offset where the
         // client code must draw into this surface when this happens.
         RECT update_rect;
         update_rect.left = dirty_x0;
         update_rect.top = dirty_y0;
         update_rect.right = dirty_x0 + dirty_width;
         update_rect.bottom = dirty_y0 + dirty_height;
         POINT offset;
         D3D11_TEXTURE2D_DESC desc;
         ID3D11Texture2D *pTexture;
-        HRESULT hr = tile.pSurface->BeginDraw(
+        HRESULT hr;
+
+        // Store the current surface for unbinding later
+#ifdef USE_VIRTUAL_SURFACES
+        LONG tile_offset_x = VIRTUAL_OFFSET + tile_x * surface.tile_width;
+        LONG tile_offset_y = VIRTUAL_OFFSET + tile_y * surface.tile_height;
+
+        update_rect.left += tile_offset_x;
+        update_rect.top += tile_offset_y;
+        update_rect.right += tile_offset_x;
+        update_rect.bottom += tile_offset_y;
+
+        hr = surface.pVirtualSurface->BeginDraw(
             &update_rect,
             __uuidof(ID3D11Texture2D),
             (void **) &pTexture,
             &offset
         );
+        window->pCurrentSurface = surface.pVirtualSurface;
+#else
+        hr = tile.pSurface->BeginDraw(
+            &update_rect,
+            __uuidof(ID3D11Texture2D),
+            (void **) &pTexture,
+            &offset
+        );
+        window->pCurrentSurface = tile.pSurface;
+#endif
+
         // DC includes the origin of the dirty / update rect in the draw offset,
         // undo that here since WR expects it to be an absolute offset.
+        assert(SUCCEEDED(hr));
         offset.x -= dirty_x0;
         offset.y -= dirty_y0;
-        assert(SUCCEEDED(hr));
         pTexture->GetDesc(&desc);
         *x_offset = offset.x;
         *y_offset = offset.y;
 
         // Construct an EGLImage wrapper around the D3D texture for ANGLE.
         const EGLAttrib attribs[] = { EGL_NONE };
         window->mEGLImage = eglCreateImage(
             window->EGLDisplay,
@@ -685,16 +741,20 @@ extern "C" {
     ) {
         Surface surface = window->surfaces[id];
         window->mCurrentLayers.push_back(id);
 
         // Place the visual - this changes frame to frame based on scroll position
         // of the slice.
         float offset_x = (float) (x + window->client_rect.left);
         float offset_y = (float) (y + window->client_rect.top);
+#ifdef USE_VIRTUAL_SURFACES
+        offset_x -= VIRTUAL_OFFSET;
+        offset_y -= VIRTUAL_OFFSET;
+#endif
         surface.pVisual->SetOffsetX(offset_x);
         surface.pVisual->SetOffsetY(offset_y);
 
         // Set the clip rect - converting from world space to the pre-offset space
         // that DC requires for rectangle clips.
         D2D_RECT_F clip_rect;
         clip_rect.left = clip_x - offset_x;
         clip_rect.top = clip_y - offset_y;