Bug 1604684 - Make opacity a compositor surface property rather than a tile property. r=sotaro
authorGlenn Watson <git@intuitionlibrary.com>
Mon, 06 Jan 2020 20:11:21 +0000
changeset 508977 f443172cae02e3835bd4f3ca38cb4331271cc86a
parent 508976 033de5b3ae20bfb28da62377abfa8aa3cd1841a6
child 508978 27e947bd421db51086e4cda9e23dd4bea0ba2535
push id36987
push usernerli@mozilla.com
push dateTue, 07 Jan 2020 05:13:50 +0000
treeherdermozilla-central@be6b36c9338e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssotaro
bugs1604684
milestone73.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 1604684 - Make opacity a compositor surface property rather than a tile property. r=sotaro This will allow use of the DirectComposition virtual surface API. If it turns out that some pages recreate surfaces a lot due to opacity changing, we can add some extra logic to avoid recreating surfaces as often, and making use of per-tile opacity in some cases. Differential Revision: https://phabricator.services.mozilla.com/D57592
gfx/webrender_bindings/DCLayerTree.cpp
gfx/webrender_bindings/DCLayerTree.h
gfx/webrender_bindings/RenderCompositor.cpp
gfx/webrender_bindings/RenderCompositor.h
gfx/webrender_bindings/RenderCompositorANGLE.cpp
gfx/webrender_bindings/RenderCompositorANGLE.h
gfx/webrender_bindings/RenderCompositorOGL.cpp
gfx/webrender_bindings/RenderCompositorOGL.h
gfx/webrender_bindings/src/bindings.rs
gfx/wr/example-compositor/compositor-windows/src/lib.cpp
gfx/wr/example-compositor/compositor-windows/src/lib.rs
gfx/wr/example-compositor/compositor/src/main.rs
gfx/wr/webrender/src/composite.rs
gfx/wr/webrender/src/picture.rs
gfx/wr/webrender/src/renderer.rs
gfx/wr/webrender/src/resource_cache.rs
--- a/gfx/webrender_bindings/DCLayerTree.cpp
+++ b/gfx/webrender_bindings/DCLayerTree.cpp
@@ -225,25 +225,25 @@ void DCLayerTree::Unbind() {
   auto surface = GetSurface(id.surface_id);
   auto layer = surface->GetLayer(id.x, id.y);
 
   layer->EndDraw();
   mCurrentId = Nothing();
 }
 
 void DCLayerTree::CreateSurface(wr::NativeSurfaceId aId,
-                                wr::DeviceIntSize aTileSize) {
+                                wr::DeviceIntSize aTileSize, bool aIsOpaque) {
   auto it = mDCSurfaces.find(aId);
   MOZ_RELEASE_ASSERT(it == mDCSurfaces.end());
   if (it != mDCSurfaces.end()) {
     // DCSurface already exists.
     return;
   }
 
-  auto surface = MakeUnique<DCSurface>(aTileSize, this);
+  auto surface = MakeUnique<DCSurface>(aTileSize, aIsOpaque, this);
   if (!surface->Initialize()) {
     gfxCriticalNote << "Failed to initialize DCSurface: " << wr::AsUint64(aId);
     return;
   }
 
   mDCSurfaces[aId] = std::move(surface);
 }
 
@@ -251,20 +251,19 @@ void DCLayerTree::DestroySurface(NativeS
   auto surface_it = mDCSurfaces.find(aId);
   MOZ_RELEASE_ASSERT(surface_it != mDCSurfaces.end());
   auto surface = surface_it->second.get();
 
   mRootVisual->RemoveVisual(surface->GetVisual());
   mDCSurfaces.erase(surface_it);
 }
 
-void DCLayerTree::CreateTile(wr::NativeSurfaceId aId, int aX, int aY,
-                             bool aIsOpaque) {
+void DCLayerTree::CreateTile(wr::NativeSurfaceId aId, int aX, int aY) {
   auto surface = GetSurface(aId);
-  surface->CreateTile(aX, aY, aIsOpaque);
+  surface->CreateTile(aX, aY);
 }
 
 void DCLayerTree::DestroyTile(wr::NativeSurfaceId aId, int aX, int aY) {
   auto surface = GetSurface(aId);
   surface->DestroyTile(aX, aY);
 }
 
 void DCLayerTree::AddSurface(wr::NativeSurfaceId aId,
@@ -330,39 +329,40 @@ GLuint DCLayerTree::GetOrCreateFbo(int a
     frame_buffer_info.fboId = fboId;
     frame_buffer_info.depthRboId = depthRboId;
     mFrameBuffers.push_back(frame_buffer_info);
   }
 
   return fboId;
 }
 
-DCSurface::DCSurface(wr::DeviceIntSize aTileSize, DCLayerTree* aDCLayerTree)
-    : mDCLayerTree(aDCLayerTree), mTileSize(aTileSize) {}
+DCSurface::DCSurface(wr::DeviceIntSize aTileSize, bool aIsOpaque,
+                     DCLayerTree* aDCLayerTree)
+    : mDCLayerTree(aDCLayerTree), mTileSize(aTileSize), mIsOpaque(aIsOpaque) {}
 
 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;
   }
 
   return true;
 }
 
-void DCSurface::CreateTile(int aX, int aY, bool aIsOpaque) {
+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, aIsOpaque)) {
+  if (!layer->Initialize(aX, aY, mTileSize, mIsOpaque)) {
     gfxCriticalNote << "Failed to initialize DCLayer: " << aX << aY;
     return;
   }
 
   mVisual->AddVisual(layer->GetVisual(), FALSE, NULL);
   mDCLayers[key] = std::move(layer);
 }
 
--- a/gfx/webrender_bindings/DCLayerTree.h
+++ b/gfx/webrender_bindings/DCLayerTree.h
@@ -53,20 +53,20 @@ class DCLayerTree {
   void WaitForCommitCompletion();
 
   // Interface for wr::Compositor
   void CompositorBeginFrame();
   void CompositorEndFrame();
   void Bind(wr::NativeTileId aId, wr::DeviceIntPoint* aOffset, uint32_t* aFboId,
             wr::DeviceIntRect aDirtyRect);
   void Unbind();
-  void CreateSurface(wr::NativeSurfaceId aId, wr::DeviceIntSize aTileSize);
+  void CreateSurface(wr::NativeSurfaceId aId, wr::DeviceIntSize aTileSize,
+                     bool aIsOpaque);
   void DestroySurface(NativeSurfaceId aId);
-  void CreateTile(wr::NativeSurfaceId aId, int32_t aX, int32_t aY,
-                  bool aIsOpaque);
+  void CreateTile(wr::NativeSurfaceId aId, int32_t aX, int32_t aY);
   void DestroyTile(wr::NativeSurfaceId aId, int32_t aX, int32_t aY);
   void AddSurface(wr::NativeSurfaceId aId, wr::DeviceIntPoint aPosition,
                   wr::DeviceIntRect aClipRect);
 
   gl::GLContext* GetGLContext() const { return mGL; }
   EGLConfig GetEGLConfig() const { return mEGLConfig; }
   ID3D11Device* GetDevice() const { return mDevice; }
   IDCompositionDevice2* GetCompositionDevice() const {
@@ -130,21 +130,22 @@ class DCLayerTree {
  Represents a single picture cache slice. Each surface contains some
  number of tiles. An implementation may choose to allocate individual
  tiles to render in to (as the current impl does), or allocate a large
  single virtual surface to draw into (e.g. the DirectComposition virtual
  surface API in future).
  */
 class DCSurface {
  public:
-  explicit DCSurface(wr::DeviceIntSize aTileSize, DCLayerTree* aDCLayerTree);
+  explicit DCSurface(wr::DeviceIntSize aTileSize, bool aIsOpaque,
+                     DCLayerTree* aDCLayerTree);
   ~DCSurface();
 
   bool Initialize();
-  void CreateTile(int32_t aX, int32_t aY, bool aIsOpaque);
+  void CreateTile(int32_t aX, int32_t aY);
   void DestroyTile(int32_t aX, int32_t aY);
 
   IDCompositionVisual2* GetVisual() const { return mVisual; }
   DCLayer* GetLayer(int32_t aX, int32_t aY) const;
 
   struct TileKey {
     TileKey(int32_t aX, int32_t aY) : mX(aX), mY(aY) {}
 
@@ -163,16 +164,17 @@ 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;
   std::unordered_map<TileKey, UniquePtr<DCLayer>, TileKeyHashFn> mDCLayers;
 };
 
 /**
  Represents a tile within a surface.
  TODO(gw): We should probably rename this to DCTile as a follow up.
  */
 class DCLayer {
--- a/gfx/webrender_bindings/RenderCompositor.cpp
+++ b/gfx/webrender_bindings/RenderCompositor.cpp
@@ -38,25 +38,25 @@ void wr_compositor_begin_frame(void* aCo
 void wr_compositor_bind(void* aCompositor, wr::NativeTileId aId,
                         wr::DeviceIntPoint* aOffset, uint32_t* aFboId,
                         wr::DeviceIntRect aDirtyRect) {
   RenderCompositor* compositor = static_cast<RenderCompositor*>(aCompositor);
   compositor->Bind(aId, aOffset, aFboId, aDirtyRect);
 }
 
 void wr_compositor_create_surface(void* aCompositor, wr::NativeSurfaceId aId,
-                                  wr::DeviceIntSize aTileSize) {
+                                  wr::DeviceIntSize aTileSize, bool aIsOpaque) {
   RenderCompositor* compositor = static_cast<RenderCompositor*>(aCompositor);
-  compositor->CreateSurface(aId, aTileSize);
+  compositor->CreateSurface(aId, aTileSize, aIsOpaque);
 }
 
 void wr_compositor_create_tile(void* aCompositor, wr::NativeSurfaceId aId,
-                               int32_t aX, int32_t aY, bool aIsOpaque) {
+                               int32_t aX, int32_t aY) {
   RenderCompositor* compositor = static_cast<RenderCompositor*>(aCompositor);
-  compositor->CreateTile(aId, aX, aY, aIsOpaque);
+  compositor->CreateTile(aId, aX, aY);
 }
 
 void wr_compositor_destroy_tile(void* aCompositor, wr::NativeSurfaceId aId,
                                 int32_t aX, int32_t aY) {
   RenderCompositor* compositor = static_cast<RenderCompositor*>(aCompositor);
   compositor->DestroyTile(aId, aX, aY);
 }
 
--- a/gfx/webrender_bindings/RenderCompositor.h
+++ b/gfx/webrender_bindings/RenderCompositor.h
@@ -84,20 +84,19 @@ class RenderCompositor {
 
   // Interface for wr::Compositor
   virtual void CompositorBeginFrame() {}
   virtual void CompositorEndFrame() {}
   virtual void Bind(wr::NativeTileId aId, wr::DeviceIntPoint* aOffset,
                     uint32_t* aFboId, wr::DeviceIntRect aDirtyRect) {}
   virtual void Unbind() {}
   virtual void CreateSurface(wr::NativeSurfaceId aId,
-                             wr::DeviceIntSize aTileSize) {}
+                             wr::DeviceIntSize aTileSize, bool aIsOpaque) {}
   virtual void DestroySurface(NativeSurfaceId aId) {}
-  virtual void CreateTile(wr::NativeSurfaceId, int32_t aX, int32_t aY,
-                          bool aIsOpaque) {}
+  virtual void CreateTile(wr::NativeSurfaceId, int32_t aX, int32_t aY) {}
   virtual void DestroyTile(wr::NativeSurfaceId, int32_t aX, int32_t aY) {}
   virtual void AddSurface(wr::NativeSurfaceId aId, wr::DeviceIntPoint aPosition,
                           wr::DeviceIntRect aClipRect) {}
 
   // Interface for partial present
   virtual bool UsePartialPresent() { return false; }
   virtual bool RequestFullRender() { return false; }
   virtual uint32_t GetMaxPartialPresentRects() { return 0; }
--- a/gfx/webrender_bindings/RenderCompositorANGLE.cpp
+++ b/gfx/webrender_bindings/RenderCompositorANGLE.cpp
@@ -754,27 +754,28 @@ void RenderCompositorANGLE::Bind(wr::Nat
                                  wr::DeviceIntPoint* aOffset, uint32_t* aFboId,
                                  wr::DeviceIntRect aDirtyRect) {
   mDCLayerTree->Bind(aId, aOffset, aFboId, aDirtyRect);
 }
 
 void RenderCompositorANGLE::Unbind() { mDCLayerTree->Unbind(); }
 
 void RenderCompositorANGLE::CreateSurface(wr::NativeSurfaceId aId,
-                                          wr::DeviceIntSize aTileSize) {
-  mDCLayerTree->CreateSurface(aId, aTileSize);
+                                          wr::DeviceIntSize aTileSize,
+                                          bool aIsOpaque) {
+  mDCLayerTree->CreateSurface(aId, aTileSize, aIsOpaque);
 }
 
 void RenderCompositorANGLE::DestroySurface(NativeSurfaceId aId) {
   mDCLayerTree->DestroySurface(aId);
 }
 
-void RenderCompositorANGLE::CreateTile(wr::NativeSurfaceId aId, int aX, int aY,
-                                       bool aIsOpaque) {
-  mDCLayerTree->CreateTile(aId, aX, aY, aIsOpaque);
+void RenderCompositorANGLE::CreateTile(wr::NativeSurfaceId aId, int aX,
+                                       int aY) {
+  mDCLayerTree->CreateTile(aId, aX, aY);
 }
 
 void RenderCompositorANGLE::DestroyTile(wr::NativeSurfaceId aId, int aX,
                                         int aY) {
   mDCLayerTree->DestroyTile(aId, aX, aY);
 }
 
 void RenderCompositorANGLE::AddSurface(wr::NativeSurfaceId aId,
--- a/gfx/webrender_bindings/RenderCompositorANGLE.h
+++ b/gfx/webrender_bindings/RenderCompositorANGLE.h
@@ -68,21 +68,20 @@ class RenderCompositorANGLE : public Ren
   uint32_t GetMaxUpdateRects() override;
 
   // Interface for wr::Compositor
   void CompositorBeginFrame() override;
   void CompositorEndFrame() override;
   void Bind(wr::NativeTileId aId, wr::DeviceIntPoint* aOffset, uint32_t* aFboId,
             wr::DeviceIntRect aDirtyRect) override;
   void Unbind() override;
-  void CreateSurface(wr::NativeSurfaceId aId,
-                     wr::DeviceIntSize aTileSize) override;
+  void CreateSurface(wr::NativeSurfaceId aId, wr::DeviceIntSize aTileSize,
+                     bool aIsOpaque) override;
   void DestroySurface(NativeSurfaceId aId) override;
-  void CreateTile(wr::NativeSurfaceId aId, int32_t aX, int32_t aY,
-                  bool aIsOpaque) override;
+  void CreateTile(wr::NativeSurfaceId aId, int32_t aX, int32_t aY) override;
   void DestroyTile(wr::NativeSurfaceId aId, int32_t aX, int32_t aY) override;
   void AddSurface(wr::NativeSurfaceId aId, wr::DeviceIntPoint aPosition,
                   wr::DeviceIntRect aClipRect) override;
 
   // Interface for partial present
   bool UsePartialPresent() override;
   bool RequestFullRender() override;
   uint32_t GetMaxPartialPresentRects() override;
--- a/gfx/webrender_bindings/RenderCompositorOGL.cpp
+++ b/gfx/webrender_bindings/RenderCompositorOGL.cpp
@@ -271,34 +271,34 @@ void RenderCompositorOGL::Unbind() {
   MOZ_RELEASE_ASSERT(mCurrentlyBoundNativeLayer);
 
   mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
   mCurrentlyBoundNativeLayer->NotifySurfaceReady();
   mCurrentlyBoundNativeLayer = nullptr;
 }
 
 void RenderCompositorOGL::CreateSurface(wr::NativeSurfaceId aId,
-                                        wr::DeviceIntSize aTileSize) {
-  Surface surface(aTileSize);
+                                        wr::DeviceIntSize aTileSize,
+                                        bool aIsOpaque) {
+  Surface surface(aTileSize, aIsOpaque);
   mSurfaces.insert({aId, surface});
 }
 
 void RenderCompositorOGL::DestroySurface(NativeSurfaceId aId) {
   mSurfaces.erase(aId);
 }
 
-void RenderCompositorOGL::CreateTile(wr::NativeSurfaceId aId, int aX, int aY,
-                                     bool aIsOpaque) {
+void RenderCompositorOGL::CreateTile(wr::NativeSurfaceId aId, int aX, int aY) {
   auto surfaceCursor = mSurfaces.find(aId);
   MOZ_RELEASE_ASSERT(surfaceCursor != mSurfaces.end());
   Surface& surface = surfaceCursor->second;
 
   RefPtr<layers::NativeLayer> layer = mNativeLayerRoot->CreateLayer(
-      IntSize(surface.mTileSize.width, surface.mTileSize.height), aIsOpaque,
-      mSurfacePoolHandle);
+      IntSize(surface.mTileSize.width, surface.mTileSize.height),
+      surface.mIsOpaque, mSurfacePoolHandle);
   surface.mNativeLayers.insert({TileKey(aX, aY), layer});
   mTotalPixelCount += gfx::IntRect({}, layer->GetSize()).Area();
 }
 
 void RenderCompositorOGL::DestroyTile(wr::NativeSurfaceId aId, int aX, int aY) {
   auto surfaceCursor = mSurfaces.find(aId);
   MOZ_RELEASE_ASSERT(surfaceCursor != mSurfaces.end());
   Surface& surface = surfaceCursor->second;
--- a/gfx/webrender_bindings/RenderCompositorOGL.h
+++ b/gfx/webrender_bindings/RenderCompositorOGL.h
@@ -51,21 +51,20 @@ class RenderCompositorOGL : public Rende
                      const Range<uint8_t>& aReadbackBuffer) override;
 
   // Interface for wr::Compositor
   void CompositorBeginFrame() override;
   void CompositorEndFrame() override;
   void Bind(wr::NativeTileId aId, wr::DeviceIntPoint* aOffset, uint32_t* aFboId,
             wr::DeviceIntRect aDirtyRect) override;
   void Unbind() override;
-  void CreateSurface(wr::NativeSurfaceId aId,
-                     wr::DeviceIntSize aTileSize) override;
+  void CreateSurface(wr::NativeSurfaceId aId, wr::DeviceIntSize aTileSize,
+                     bool aIsOpaque) override;
   void DestroySurface(NativeSurfaceId aId) override;
-  void CreateTile(wr::NativeSurfaceId aId, int32_t aX, int32_t aY,
-                  bool aIsOpaque) override;
+  void CreateTile(wr::NativeSurfaceId aId, int32_t aX, int32_t aY) override;
   void DestroyTile(wr::NativeSurfaceId aId, int32_t aX, int32_t aY) override;
   void AddSurface(wr::NativeSurfaceId aId, wr::DeviceIntPoint aPosition,
                   wr::DeviceIntRect aClipRect) override;
 
   struct TileKey {
     TileKey(int32_t aX, int32_t aY) : mX(aX), mY(aY) {}
 
     int32_t mX;
@@ -86,19 +85,21 @@ class RenderCompositorOGL : public Rende
   struct TileKeyHashFn {
     std::size_t operator()(const TileKey& aId) const {
       return HashGeneric(aId.mX, aId.mY);
     }
   };
 
   class Surface {
    public:
-    explicit Surface(wr::DeviceIntSize aTileSize) : mTileSize(aTileSize) {}
+    explicit Surface(wr::DeviceIntSize aTileSize, bool aIsOpaque)
+        : mTileSize(aTileSize), mIsOpaque(aIsOpaque) {}
 
     wr::DeviceIntSize mTileSize;
+    bool mIsOpaque;
     std::unordered_map<TileKey, RefPtr<layers::NativeLayer>, TileKeyHashFn>
         mNativeLayers;
   };
 
   struct SurfaceIdHashFn {
     std::size_t operator()(const wr::NativeSurfaceId& aId) const {
       return HashGeneric(wr::AsUint64(aId));
     }
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -1205,27 +1205,27 @@ fn wr_device_new(gl_context: *mut c_void
     )
 }
 
 extern "C" {
     fn wr_compositor_create_surface(
         compositor: *mut c_void,
         id: NativeSurfaceId,
         tile_size: DeviceIntSize,
+        is_opaque: bool,
     );
     fn wr_compositor_destroy_surface(
         compositor: *mut c_void,
         id: NativeSurfaceId,
     );
     fn wr_compositor_create_tile(
         compositor: *mut c_void,
         id: NativeSurfaceId,
         x: i32,
         y: i32,
-        is_opaque: bool,
     );
     fn wr_compositor_destroy_tile(
         compositor: *mut c_void,
         id: NativeSurfaceId,
         x: i32,
         y: i32,
     );
     fn wr_compositor_bind(
@@ -1248,22 +1248,24 @@ extern "C" {
 
 pub struct WrCompositor(*mut c_void);
 
 impl Compositor for WrCompositor {
     fn create_surface(
         &mut self,
         id: NativeSurfaceId,
         tile_size: DeviceIntSize,
+        is_opaque: bool,
     ) {
         unsafe {
             wr_compositor_create_surface(
                 self.0,
                 id,
                 tile_size,
+                is_opaque,
             );
         }
     }
 
     fn destroy_surface(
         &mut self,
         id: NativeSurfaceId,
     ) {
@@ -1273,25 +1275,23 @@ impl Compositor for WrCompositor {
                 id,
             );
         }
     }
 
     fn create_tile(
         &mut self,
         id: NativeTileId,
-        is_opaque: bool,
     ) {
         unsafe {
             wr_compositor_create_tile(
                 self.0,
                 id.surface_id,
                 id.x,
                 id.y,
-                is_opaque,
             );
         }
     }
 
     fn destroy_tile(
         &mut self,
         id: NativeTileId,
     ) {
--- a/gfx/wr/example-compositor/compositor-windows/src/lib.cpp
+++ b/gfx/wr/example-compositor/compositor-windows/src/lib.cpp
@@ -58,16 +58,17 @@ struct TileKeyHasher {
     size_t operator()(const TileKey &key) const {
         return key.x ^ key.y;
     }
 };
 
 struct Surface {
     int tile_width;
     int tile_height;
+    bool is_opaque;
     std::unordered_map<TileKey, Tile, TileKeyHasher> tiles;
     IDCompositionVisual2 *pVisual;
 };
 
 struct CachedFrameBuffer {
     int width;
     int height;
     GLuint fboId;
@@ -460,48 +461,49 @@ extern "C" {
         }
     }
 
     // Create a new DC surface
     void com_dc_create_surface(
         Window *window,
         uint64_t id,
         int tile_width,
-        int tile_height
+        int tile_height,
+        bool is_opaque
     ) {
         assert(window->surfaces.count(id) == 0);
 
         Surface surface;
         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));
 
         window->surfaces[id] = surface;
     }
 
     void com_dc_create_tile(
         Window *window,
         uint64_t id,
         int x,
-        int y,
-        bool is_opaque
+        int y
     ) {
         assert(window->surfaces.count(id) == 1);
         Surface &surface = window->surfaces[id];
 
         TileKey key(x, y);
         assert(surface.tiles.count(key) == 0);
 
         Tile tile;
 
         // Create the video memory surface.
-        DXGI_ALPHA_MODE alpha_mode = is_opaque ? DXGI_ALPHA_MODE_IGNORE : DXGI_ALPHA_MODE_PREMULTIPLIED;
+        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
         );
         assert(SUCCEEDED(hr));
--- a/gfx/wr/example-compositor/compositor-windows/src/lib.rs
+++ b/gfx/wr/example-compositor/compositor-windows/src/lib.rs
@@ -33,24 +33,24 @@ extern {
     fn com_dc_get_proc_address(name: *const c_char) -> *const c_void;
     fn com_dc_swap_buffers(window: *mut Window);
 
     fn com_dc_create_surface(
         window: *mut Window,
         id: u64,
         tile_width: i32,
         tile_height: i32,
+        is_opaque: bool,
     );
 
     fn com_dc_create_tile(
         window: *mut Window,
         id: u64,
         x: i32,
         y: i32,
-        is_opaque: bool,
     );
 
     fn com_dc_destroy_tile(
         window: *mut Window,
         id: u64,
         x: i32,
         y: i32,
     );
@@ -119,41 +119,41 @@ pub fn get_proc_address(name: *const c_c
     }
 }
 
 pub fn create_surface(
     window: *mut Window,
     id: u64,
     tile_width: i32,
     tile_height: i32,
+    is_opaque: bool,
 ) {
     unsafe {
         com_dc_create_surface(
             window,
             id,
             tile_width,
             tile_height,
+            is_opaque,
         )
     }
 }
 
 pub fn create_tile(
     window: *mut Window,
     id: u64,
     x: i32,
     y: i32,
-    is_opaque: bool,
 ) {
     unsafe {
         com_dc_create_tile(
             window,
             id,
             x,
             y,
-            is_opaque,
         )
     }
 }
 
 pub fn destroy_tile(
     window: *mut Window,
     id: u64,
     x: i32,
--- a/gfx/wr/example-compositor/compositor/src/main.rs
+++ b/gfx/wr/example-compositor/compositor/src/main.rs
@@ -38,43 +38,43 @@ impl DirectCompositeInterface {
     }
 }
 
 impl webrender::Compositor for DirectCompositeInterface {
     fn create_surface(
         &mut self,
         id: webrender::NativeSurfaceId,
         tile_size: DeviceIntSize,
+        is_opaque: bool,
     ) {
         compositor::create_surface(
             self.window,
             id.0,
             tile_size.width,
             tile_size.height,
+            is_opaque,
         );
     }
 
     fn destroy_surface(
         &mut self,
         id: webrender::NativeSurfaceId,
     ) {
         compositor::destroy_surface(self.window, id.0);
     }
 
     fn create_tile(
         &mut self,
         id: webrender::NativeTileId,
-        is_opaque: bool,
     ) {
         compositor::create_tile(
             self.window,
             id.surface_id.0,
             id.x,
             id.y,
-            is_opaque,
         );
     }
 
     fn destroy_tile(
         &mut self,
         id: webrender::NativeTileId,
     ) {
         compositor::destroy_tile(
@@ -255,17 +255,17 @@ fn build_display_list(
         builder,
         LayoutRect::new(
             LayoutPoint::new(100.0, 100.0),
             LayoutSize::new(size_factor * 400.0, size_factor * 400.0),
         ),
         ColorF::new(1.0, 0.0, 0.0, 1.0),
         scroll_space_info.spatial_id,
         root_pipeline_id,
-        0.0,
+        time,
         time,
     );
 
     push_rotated_rect(
         builder,
         LayoutRect::new(
             LayoutPoint::new(800.0, 100.0),
             LayoutSize::new(size_factor * 100.0, size_factor * 600.0),
--- a/gfx/wr/webrender/src/composite.rs
+++ b/gfx/wr/webrender/src/composite.rs
@@ -17,23 +17,23 @@ use std::{ops, u64};
 /// Describes details of an operation to apply to a native surface
 #[derive(Debug, Clone)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub enum NativeSurfaceOperationDetails {
     CreateSurface {
         id: NativeSurfaceId,
         tile_size: DeviceIntSize,
+        is_opaque: bool,
     },
     DestroySurface {
         id: NativeSurfaceId,
     },
     CreateTile {
         id: NativeTileId,
-        is_opaque: bool,
     },
     DestroyTile {
         id: NativeTileId,
     }
 }
 
 /// Describes an operation to apply to a native surface
 #[derive(Debug, Clone)]
@@ -428,16 +428,17 @@ pub struct NativeSurfaceInfo {
 /// by the client application, then picture cache slices will be
 /// composited by the OS compositor, rather than drawn via WR batches.
 pub trait Compositor {
     /// Create a new OS compositor surface with the given properties.
     fn create_surface(
         &mut self,
         id: NativeSurfaceId,
         tile_size: DeviceIntSize,
+        is_opaque: bool,
     );
 
     /// Destroy the surface with the specified id. WR may call this
     /// at any time the surface is no longer required (including during
     /// renderer deinit). It's the responsibility of the embedder
     /// to ensure that the surface is only freed once the GPU is
     /// no longer using the surface (if this isn't already handled
     /// by the operating system).
@@ -445,17 +446,16 @@ pub trait Compositor {
         &mut self,
         id: NativeSurfaceId,
     );
 
     /// Create a new OS compositor tile with the given properties.
     fn create_tile(
         &mut self,
         id: NativeTileId,
-        is_opaque: bool,
     );
 
     /// Destroy an existing compositor tile.
     fn destroy_tile(
         &mut self,
         id: NativeTileId,
     );
 
--- a/gfx/wr/webrender/src/picture.rs
+++ b/gfx/wr/webrender/src/picture.rs
@@ -199,16 +199,18 @@ pub struct PictureCacheState {
     /// The current transform of the picture cache root spatial node
     root_transform: TransformKey,
     /// The current tile size in device pixels
     current_tile_size: DeviceIntSize,
     /// Various allocations we want to avoid re-doing.
     allocations: PictureCacheRecycledAllocations,
     /// Currently allocated native compositor surface for this picture cache.
     pub native_surface_id: Option<NativeSurfaceId>,
+    /// True if the entire picture cache is opaque.
+    is_opaque: bool,
 }
 
 pub struct PictureCacheRecycledAllocations {
     old_tiles: FastHashMap<TileOffset, Tile>,
     old_opacity_bindings: FastHashMap<PropertyBindingId, OpacityBindingInfo>,
     compare_cache: FastHashMap<PrimitiveComparisonKey, PrimitiveCompareResult>,
 }
 
@@ -893,19 +895,17 @@ impl Tile {
             }
             CompositorKind::Native { .. } => {
             }
         }
 
         // Check if this tile can be considered opaque. Opacity state must be updated only
         // after all early out checks have been performed. Otherwise, we might miss updating
         // the native surface next time this tile becomes visible.
-        let tile_is_opaque = ctx.backdrop.rect.contains_rect(&self.clipped_rect);
-        let opacity_changed = tile_is_opaque != self.is_opaque;
-        self.is_opaque = tile_is_opaque;
+        self.is_opaque = ctx.backdrop.rect.contains_rect(&self.clipped_rect);
 
         // Check if the selected composite mode supports dirty rect updates. For Draw composite
         // mode, we can always update the content with smaller dirty rects. For native composite
         // mode, we can only use dirty rects if the compositor supports partial surface updates.
         let (supports_dirty_rects, supports_simple_prims) = match state.composite_state.compositor_kind {
             CompositorKind::Draw { .. } => {
                 (true, true)
             }
@@ -973,37 +973,17 @@ impl Tile {
                 }
             }
         } else {
             // If this tile will be backed by a surface, we want to retain
             // the texture handle from the previous frame, if possible. If
             // the tile was previously a color, or not set, then just set
             // up a new texture cache handle.
             match self.surface.take() {
-                Some(TileSurface::Texture { mut descriptor, visibility_mask }) => {
-                    // If opacity changed, and this is a native OS compositor surface,
-                    // it needs to be recreated.
-                    // TODO(gw): This is a limitation of the DirectComposite APIs. It might
-                    //           make sense on other platforms to be able to change this as
-                    //           a property on a surface, if we ever see pages where this
-                    //           is changing frequently.
-                    if opacity_changed {
-                        if let SurfaceTextureDescriptor::Native { ref mut id, .. } = descriptor {
-                            // Reset the dirty rect and tile validity in this case, to
-                            // force the new tile to be completely redrawn.
-                            self.invalidate(None, InvalidationReason::SurfaceOpacityChanged);
-
-                            // If this tile has a currently allocated native surface, destroy it. It
-                            // will be re-allocated next time it's determined to be visible.
-                            if let Some(id) = id.take() {
-                                state.resource_cache.destroy_compositor_tile(id);
-                            }
-                        }
-                    }
-
+                Some(TileSurface::Texture { descriptor, visibility_mask }) => {
                     // Reuse the existing descriptor and vis mask
                     TileSurface::Texture {
                         descriptor,
                         visibility_mask,
                     }
                 }
                 Some(TileSurface::Color { .. }) | Some(TileSurface::Clear) | None => {
                     // This is the case where we are constructing a tile surface that
@@ -1525,16 +1505,18 @@ pub struct TileCacheInstance {
     compare_cache: FastHashMap<PrimitiveComparisonKey, PrimitiveCompareResult>,
     /// The allocated compositor surface for this picture cache. May be None if
     /// not using native compositor, or if the surface was destroyed and needs
     /// to be reallocated next time this surface contains valid tiles.
     pub native_surface_id: Option<NativeSurfaceId>,
     /// The current device position of this cache. Used to set the compositor
     /// offset of the surface when building the visual tree.
     pub device_position: DevicePoint,
+    /// True if the entire picture cache surface is opaque.
+    is_opaque: bool,
 }
 
 impl TileCacheInstance {
     pub fn new(
         slice: usize,
         spatial_node_index: SpatialNodeIndex,
         background_color: Option<ColorF>,
         shared_clips: Vec<ClipDataHandle>,
@@ -1574,16 +1556,17 @@ impl TileCacheInstance {
             shared_clips,
             shared_clip_chain,
             current_tile_size: DeviceIntSize::zero(),
             frames_until_size_eval: 0,
             fract_offset: PictureVector2D::zero(),
             compare_cache: FastHashMap::default(),
             native_surface_id: None,
             device_position: DevicePoint::zero(),
+            is_opaque: true,
         }
     }
 
     /// Returns true if this tile cache is considered opaque.
     pub fn is_opaque(&self) -> bool {
         // If known opaque due to background clear color and being the first slice.
         // The background_color will only be Some(..) if this is the first slice.
         match self.background_color {
@@ -1695,16 +1678,17 @@ impl TileCacheInstance {
         // If there are pending retained state, retrieve it.
         if let Some(prev_state) = frame_state.retained_tiles.caches.remove(&self.slice) {
             self.tiles.extend(prev_state.tiles);
             self.root_transform = prev_state.root_transform;
             self.spatial_nodes = prev_state.spatial_nodes;
             self.opacity_bindings = prev_state.opacity_bindings;
             self.current_tile_size = prev_state.current_tile_size;
             self.native_surface_id = prev_state.native_surface_id;
+            self.is_opaque = prev_state.is_opaque;
 
             fn recycle_map<K: std::cmp::Eq + std::hash::Hash, V>(
                 ideal_len: usize,
                 dest: &mut FastHashMap<K, V>,
                 src: FastHashMap<K, V>,
             ) {
                 if dest.capacity() < src.capacity() {
                     if src.capacity() < 3 * ideal_len {
@@ -2350,23 +2334,52 @@ impl TileCacheInstance {
         };
 
         let mut state = TilePostUpdateState {
             resource_cache: frame_state.resource_cache,
             composite_state: frame_state.composite_state,
             compare_cache: &mut self.compare_cache,
         };
 
-        // Step through each tile and invalidate if the dependencies have changed.
+        // Step through each tile and invalidate if the dependencies have changed. Determine
+        // the current opacity setting and whether it's changed.
+        let mut tile_cache_is_opaque = true;
         for (key, tile) in self.tiles.iter_mut() {
             if tile.post_update(&ctx, &mut state) {
                 self.tiles_to_draw.push(*key);
+                tile_cache_is_opaque &= tile.is_opaque;
             }
         }
 
+        // If opacity changed, the native compositor surface and all tiles get invalidated.
+        // (this does nothing if not using native compositor mode).
+        // TODO(gw): This property probably changes very rarely, so it is OK to invalidate
+        //           everything in this case. If it turns out that this isn't true, we could
+        //           consider other options, such as per-tile opacity (natively supported
+        //           on CoreAnimation, and supported if backed by non-virtual surfaces in
+        //           DirectComposition).
+        if self.is_opaque != tile_cache_is_opaque {
+            if let Some(native_surface_id) = self.native_surface_id.take() {
+                // Since the native surface will be destroyed, need to clear the compositor tile
+                // handle for all tiles. This means the tiles will be reallocated on demand
+                // when the tiles are added to render tasks.
+                for tile in self.tiles.values_mut() {
+                    if let Some(TileSurface::Texture { descriptor: SurfaceTextureDescriptor::Native { ref mut id, .. }, .. }) = tile.surface {
+                        *id = None;
+                    }
+                    // Invalidate the entire tile to force a redraw.
+                    tile.invalidate(None, InvalidationReason::SurfaceOpacityChanged);
+                }
+                // Destroy the compositor surface. It will be reallocated with the correct
+                // opacity flag when render tasks are generated for tiles.
+                frame_state.resource_cache.destroy_compositor_surface(native_surface_id);
+            }
+            self.is_opaque = tile_cache_is_opaque;
+        }
+
         // When under test, record a copy of the dirty region to support
         // invalidation testing in wrench.
         if frame_context.config.testing {
             frame_state.scratch.recorded_dirty_regions.push(self.dirty_region.record());
         }
     }
 }
 
@@ -3178,16 +3191,17 @@ impl PicturePrimitive {
                     tile_cache.slice,
                     PictureCacheState {
                         tiles: tile_cache.tiles,
                         spatial_nodes: tile_cache.spatial_nodes,
                         opacity_bindings: tile_cache.opacity_bindings,
                         root_transform: tile_cache.root_transform,
                         current_tile_size: tile_cache.current_tile_size,
                         native_surface_id: tile_cache.native_surface_id.take(),
+                        is_opaque: tile_cache.is_opaque,
                         allocations: PictureCacheRecycledAllocations {
                             old_tiles: tile_cache.old_tiles,
                             old_opacity_bindings: tile_cache.old_opacity_bindings,
                             compare_cache: tile_cache.compare_cache,
                         },
                     },
                 );
             }
@@ -3715,32 +3729,32 @@ impl PicturePrimitive {
                                     SurfaceTextureDescriptor::Native { id } => {
                                         if id.is_none() {
                                             // Allocate a native surface id if we're in native compositing mode,
                                             // and we don't have a surface yet (due to first frame, or destruction
                                             // due to tile size changing etc).
                                             if tile_cache.native_surface_id.is_none() {
                                                 let surface_id = frame_state
                                                     .resource_cache
-                                                    .create_compositor_surface(tile_cache.current_tile_size);
+                                                    .create_compositor_surface(
+                                                        tile_cache.current_tile_size,
+                                                        tile_cache.is_opaque,
+                                                    );
 
                                                 tile_cache.native_surface_id = Some(surface_id);
                                             }
 
                                             // Create the tile identifier and allocate it.
                                             let tile_id = NativeTileId {
                                                 surface_id: tile_cache.native_surface_id.unwrap(),
                                                 x: key.x,
                                                 y: key.y,
                                             };
 
-                                            frame_state.resource_cache.create_compositor_tile(
-                                                tile_id,
-                                                tile.is_opaque,
-                                            );
+                                            frame_state.resource_cache.create_compositor_tile(tile_id);
 
                                             *id = Some(tile_id);
                                         }
                                     }
                                 }
 
                                 *visibility_mask = PrimitiveVisibilityMask::empty();
                                 let dirty_region_index = tile_cache.dirty_region.dirty_rects.len();
--- a/gfx/wr/webrender/src/renderer.rs
+++ b/gfx/wr/webrender/src/renderer.rs
@@ -2963,20 +2963,20 @@ impl Renderer {
                 }
             }
 
             // Allocate a new surface, if we need it and there isn't one.
             if self.debug_overlay_state.is_enabled && self.debug_overlay_state.current_size.is_none() {
                 compositor.create_surface(
                     NativeSurfaceId::DEBUG_OVERLAY,
                     framebuffer_size,
+                    false,
                 );
                 compositor.create_tile(
                     NativeTileId::DEBUG_OVERLAY,
-                    false,
                 );
                 self.debug_overlay_state.current_size = Some(framebuffer_size);
             }
         }
     }
 
     /// Bind a draw target for the debug / profiler overlays, if required.
     fn bind_debug_overlay(&mut self) {
@@ -5094,28 +5094,28 @@ impl Renderer {
         debug_assert!(self.texture_resolver.prev_pass_color.is_none());
     }
 
     fn update_native_surfaces(&mut self) {
         match self.compositor_config {
             CompositorConfig::Native { ref mut compositor, .. } => {
                 for op in self.pending_native_surface_updates.drain(..) {
                     match op.details {
-                        NativeSurfaceOperationDetails::CreateSurface { id, tile_size } => {
+                        NativeSurfaceOperationDetails::CreateSurface { id, tile_size, is_opaque } => {
                             let _inserted = self.allocated_native_surfaces.insert(id);
                             debug_assert!(_inserted, "bug: creating existing surface");
-                            compositor.create_surface(id, tile_size);
+                            compositor.create_surface(id, tile_size, is_opaque);
                         }
                         NativeSurfaceOperationDetails::DestroySurface { id } => {
                             let _existed = self.allocated_native_surfaces.remove(&id);
                             debug_assert!(_existed, "bug: removing unknown surface");
                             compositor.destroy_surface(id);
                         }
-                        NativeSurfaceOperationDetails::CreateTile { id, is_opaque } => {
-                            compositor.create_tile(id, is_opaque);
+                        NativeSurfaceOperationDetails::CreateTile { id } => {
+                            compositor.create_tile(id);
                         }
                         NativeSurfaceOperationDetails::DestroyTile { id } => {
                             compositor.destroy_tile(id);
                         }
                     }
                 }
             }
             CompositorConfig::Draw { .. } => {
--- a/gfx/wr/webrender/src/resource_cache.rs
+++ b/gfx/wr/webrender/src/resource_cache.rs
@@ -1774,24 +1774,26 @@ impl ResourceCache {
         }
     }
 
     /// Queue up allocation of a new OS native compositor surface with the
     /// specified tile size.
     pub fn create_compositor_surface(
         &mut self,
         tile_size: DeviceIntSize,
+        is_opaque: bool,
     ) -> NativeSurfaceId {
         let id = NativeSurfaceId(NEXT_NATIVE_SURFACE_ID.fetch_add(1, Ordering::Relaxed));
 
         self.pending_native_surface_updates.push(
             NativeSurfaceOperation {
                 details: NativeSurfaceOperationDetails::CreateSurface {
                     id,
                     tile_size,
+                    is_opaque,
                 },
             }
         );
 
         id
     }
 
     /// Queue up destruction of an existing native OS surface. This is used when
@@ -1808,23 +1810,21 @@ impl ResourceCache {
             }
         );
     }
 
     /// Queue construction of a native compositor tile on a given surface.
     pub fn create_compositor_tile(
         &mut self,
         id: NativeTileId,
-        is_opaque: bool,
     ) {
         self.pending_native_surface_updates.push(
             NativeSurfaceOperation {
                 details: NativeSurfaceOperationDetails::CreateTile {
                     id,
-                    is_opaque,
                 },
             }
         );
     }
 
     /// Queue destruction of a native compositor tile.
     pub fn destroy_compositor_tile(
         &mut self,