Bug 1594950 - Bake the layer size and its opaqueness into the layer, don't allow mutating it. r=jrmuizel
authorMarkus Stange <mstange@themasta.com>
Wed, 13 Nov 2019 18:46:02 +0000
changeset 501814 aa9475c27a6d5c4359f3a6d1c058ef9b1b440f5b
parent 501813 d791bfa31f08ec478b2ef6ca4f89b3a8849d723b
child 501815 2a2c01a5ce9a25fd52d55b56a1b238eebfc7d752
push id114172
push userdluca@mozilla.com
push dateTue, 19 Nov 2019 11:31:10 +0000
treeherdermozilla-inbound@b5c5ba07d3db [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel
bugs1594950
milestone72.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 1594950 - Bake the layer size and its opaqueness into the layer, don't allow mutating it. r=jrmuizel These settings are now supplied during layer creation and never change. Consumers must now create new NativeLayer objects if they want to change size or toggle opaqueness. This aligns the NativeLayer API with DirectComposition's capabilities. It also simplifies swap chain management. Differential Revision: https://phabricator.services.mozilla.com/D51757
gfx/layers/NativeLayer.h
gfx/layers/NativeLayerCA.h
gfx/layers/NativeLayerCA.mm
gfx/layers/composite/LayerManagerComposite.cpp
gfx/webrender_bindings/RenderCompositorOGL.cpp
widget/cocoa/nsChildView.mm
--- a/gfx/layers/NativeLayer.h
+++ b/gfx/layers/NativeLayer.h
@@ -28,17 +28,18 @@ class NativeLayerCA;
 // platform's native layers, such as CoreAnimation layers on macOS.
 // Every layer has a rectangle that describes its position and size in the
 // window. The native layer root is usually be created by the window, and then
 // the compositing subsystem uses it to create and place the actual layers.
 class NativeLayerRoot {
  public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(NativeLayerRoot)
 
-  virtual already_AddRefed<NativeLayer> CreateLayer() = 0;
+  virtual already_AddRefed<NativeLayer> CreateLayer(const gfx::IntSize& aSize,
+                                                    bool aIsOpaque) = 0;
   virtual void AppendLayer(NativeLayer* aLayer) = 0;
   virtual void RemoveLayer(NativeLayer* aLayer) = 0;
   virtual void SetLayers(const nsTArray<RefPtr<NativeLayer>>& aLayers) = 0;
 
  protected:
   virtual ~NativeLayerRoot() {}
 };
 
@@ -61,69 +62,69 @@ class NativeLayerRoot {
 // will reduce the window manager's work for that frame because it'll only copy
 // the pixels of the small layer to the screen.
 class NativeLayer {
  public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(NativeLayer)
 
   virtual NativeLayerCA* AsNativeLayerCA() { return nullptr; }
 
-  // The location and size of the layer, in integer device pixels. This also
-  // determines the size of the surface that should be returned from the next
-  // call to NextSurface.
-  virtual void SetRect(const gfx::IntRect& aRect) = 0;
+  // The size and opaqueness of a layer are supplied during layer creation and
+  // never change.
+  virtual gfx::IntSize GetSize() = 0;
+  virtual bool IsOpaque() = 0;
+
+  // The location of the layer, in integer device pixels.
+  virtual void SetPosition(const gfx::IntPoint& aPosition) = 0;
+  virtual gfx::IntPoint GetPosition() = 0;
+
   virtual gfx::IntRect GetRect() = 0;
 
-  // Set whether the layer is fully opaque.
-  virtual void SetIsOpaque(bool aIsOpaque) = 0;
-  virtual bool IsOpaque() = 0;
-
   // Set an optional clip rect on the layer. The clip rect is in the same
   // coordinate space as the layer rect.
   virtual void SetClipRect(const Maybe<gfx::IntRect>& aClipRect) = 0;
   virtual Maybe<gfx::IntRect> ClipRect() = 0;
 
   // Whether the surface contents are flipped vertically compared to this
   // layer's coordinate system. Can be set on any thread at any time.
   virtual void SetSurfaceIsFlipped(bool aIsFlipped) = 0;
   virtual bool SurfaceIsFlipped() = 0;
 
   // Invalidates the specified region in all surfaces that are tracked by this
   // layer.
   virtual void InvalidateRegionThroughoutSwapchain(
       const gfx::IntRegion& aRegion) = 0;
 
-  // Returns a DrawTarget. The size of the DrawTarget will be the size of the
-  // rect that has been passed to SetRect. The caller should draw to that
-  // DrawTarget, then drop its reference to the DrawTarget, and then call
-  // NotifySurfaceReady(). It can limit its drawing to
-  // CurrentSurfaceInvalidRegion() (which is in the DrawTarget's device space).
-  // After a call to NextSurface*, NextSurface* must not be called again until
-  // after NotifySurfaceReady has been called. Can be called on any thread. When
-  // used from multiple threads, callers need to make sure that they still only
-  // call NextSurface and NotifySurfaceReady alternatingly and not in any other
-  // order.
+  // Returns a DrawTarget. The size of the DrawTarget will be the same as the
+  // size of this layer. The caller should draw to that DrawTarget, then drop
+  // its reference to the DrawTarget, and then call NotifySurfaceReady(). It can
+  // limit its drawing to CurrentSurfaceInvalidRegion() (which is in the
+  // DrawTarget's device space). After a call to NextSurface*, NextSurface* must
+  // not be called again until after NotifySurfaceReady has been called. Can be
+  // called on any thread. When used from multiple threads, callers need to make
+  // sure that they still only call NextSurface* and NotifySurfaceReady
+  // alternatingly and not in any other order.
   virtual RefPtr<gfx::DrawTarget> NextSurfaceAsDrawTarget(
       gfx::BackendType aBackendType) = 0;
 
   // Set the GLContext to use for the MozFramebuffer that are returned from
   // NextSurfaceAsFramebuffer. If changed to a different value, all
   // MozFramebuffers tracked by this layer will be discarded.
   // It's a good idea to call SetGLContext(nullptr) before destroying this
   // layer so that GL resource destruction happens at a good time and on the
   // right thread.
   virtual void SetGLContext(gl::GLContext* aGLContext) = 0;
   virtual gl::GLContext* GetGLContext() = 0;
 
   // Must only be called if a non-null GLContext is set on this layer.
   // Returns a GLuint for a framebuffer that can be used for drawing to the
-  // surface. The size of the framebuffer will be the size of the rect that has
-  // been passed to SetRect. If aNeedsDepth is true, the framebuffer is created
-  // with a depth buffer. The caller should draw to the framebuffer, unbind
-  // it, and then call NotifySurfaceReady(). It can limit its drawing to
+  // surface. The size of the framebuffer will be the same as the size of this
+  // layer. If aNeedsDepth is true, the framebuffer is created with a depth
+  // buffer. The caller should draw to the framebuffer, unbind it, and then call
+  // NotifySurfaceReady(). It can limit its drawing to
   // CurrentSurfaceInvalidRegion() (which is in the framebuffer's device space,
   // possibly "upside down" if SurfaceIsFlipped()). The framebuffer will be
   // created using the GLContext that was set on this layer with a call to
   // SetGLContext. The NativeLayer will keep a reference to the MozFramebuffer
   // so that it can reuse the same MozFramebuffer whenever it uses the same
   // underlying surface. Calling SetGLContext with a different context will
   // release that reference. After a call to NextSurface*, NextSurface* must not
   // be called again until after NotifySurfaceReady has been called. Can be
--- a/gfx/layers/NativeLayerCA.h
+++ b/gfx/layers/NativeLayerCA.h
@@ -58,17 +58,18 @@ class NativeLayerRootCA : public NativeL
   static already_AddRefed<NativeLayerRootCA> CreateForCALayer(CALayer* aLayer);
 
   // Must be called within a current CATransaction on the transaction's thread.
   void ApplyChanges();
 
   void SetBackingScale(float aBackingScale);
 
   // Overridden methods
-  already_AddRefed<NativeLayer> CreateLayer() override;
+  already_AddRefed<NativeLayer> CreateLayer(const gfx::IntSize& aSize,
+                                            bool aIsOpaque) override;
   void AppendLayer(NativeLayer* aLayer) override;
   void RemoveLayer(NativeLayer* aLayer) override;
   void SetLayers(const nsTArray<RefPtr<NativeLayer>>& aLayers) override;
 
  protected:
   explicit NativeLayerRootCA(CALayer* aLayer);
   ~NativeLayerRootCA() override;
 
@@ -97,28 +98,29 @@ class NativeLayerRootCA : public NativeL
 // maintain one object per IOSurface in this layer's swap chain, and those
 // objects will be reused in different frames as the layer cycles through the
 // surfaces in its swap chain.
 class NativeLayerCA : public NativeLayer {
  public:
   virtual NativeLayerCA* AsNativeLayerCA() override { return this; }
 
   // Overridden methods
-  void SetRect(const gfx::IntRect& aRect) override;
+  gfx::IntSize GetSize() override;
+  void SetPosition(const gfx::IntPoint& aPosition) override;
+  gfx::IntPoint GetPosition() override;
   gfx::IntRect GetRect() override;
   void InvalidateRegionThroughoutSwapchain(
       const gfx::IntRegion& aRegion) override;
   RefPtr<gfx::DrawTarget> NextSurfaceAsDrawTarget(
       gfx::BackendType aBackendType) override;
   void SetGLContext(gl::GLContext* aGLContext) override;
   gl::GLContext* GetGLContext() override;
   Maybe<GLuint> NextSurfaceAsFramebuffer(bool aNeedsDepth) override;
   gfx::IntRegion CurrentSurfaceInvalidRegion() override;
   void NotifySurfaceReady() override;
-  void SetIsOpaque(bool aIsOpaque) override;
   bool IsOpaque() override;
   void SetClipRect(const Maybe<gfx::IntRect>& aClipRect) override;
   Maybe<gfx::IntRect> ClipRect() override;
   void SetSurfaceIsFlipped(bool aIsFlipped) override;
   bool SurfaceIsFlipped() override;
 
   // Consumers may provide an object that implements the IOSurfaceRegistry
   // interface.
@@ -136,21 +138,21 @@ class NativeLayerCA : public NativeLayer
   // before destroying the NativeLayer so that the UnregisterSurface calls
   // happen at a deterministic time and on the right thread.
   void SetSurfaceRegistry(RefPtr<IOSurfaceRegistry> aSurfaceRegistry);
   RefPtr<IOSurfaceRegistry> GetSurfaceRegistry();
 
  protected:
   friend class NativeLayerRootCA;
 
-  NativeLayerCA();
+  NativeLayerCA(const gfx::IntSize& aSize, bool aIsOpaque);
   ~NativeLayerCA() override;
 
   // Returns an IOSurface that can be drawn to. The size of the IOSurface will
-  // be the size of the rect that has been passed to SetRect.
+  // be the same as the size of this layer.
   // The returned surface is guaranteed to be not in use by the window server.
   // After a call to NextSurface, NextSurface must not be called again until
   // after NotifySurfaceReady has been called. Can be called on any thread. When
   // used from multiple threads, callers need to make sure that they still only
   // call NextSurface and NotifySurfaceReady alternatingly and not in any other
   // order.
   CFTypeRefPtr<IOSurfaceRef> NextSurface(const MutexAutoLock&);
 
@@ -161,35 +163,32 @@ class NativeLayerCA : public NativeLayer
 
   GLuint GetOrCreateFramebufferForSurface(const MutexAutoLock&,
                                           CFTypeRefPtr<IOSurfaceRef> aSurface,
                                           bool aNeedsDepth);
 
   struct SurfaceWithInvalidRegion {
     CFTypeRefPtr<IOSurfaceRef> mSurface;
     gfx::IntRegion mInvalidRegion;
-    gfx::IntSize mSize;
   };
 
   std::vector<SurfaceWithInvalidRegion> RemoveExcessUnusedSurfaces(
       const MutexAutoLock&);
 
   // Controls access to all fields of this class.
   Mutex mMutex;
 
   RefPtr<IOSurfaceRegistry> mSurfaceRegistry;  // can be null
 
   // Each IOSurface is initially created inside NextSurface.
   // The surface stays alive until the recycling mechanism in NextSurface
-  // determines it is no longer needed, for example because the layer size
-  // changed or because the swap chain has grown too long, or until the layer
-  // is destroyed.
-  // During the surface's lifetime, it will continuously move through the fields
-  // mInProgressSurface, mReadySurface, and back to front through the
-  // mSurfaces queue:
+  // determines it is no longer needed (because the swap chain has grown too
+  // long) or until the layer is destroyed. During the surface's lifetime, it
+  // will continuously move through the fields mInProgressSurface,
+  // mReadySurface, and back to front through the mSurfaces queue:
   //
   //  mSurfaces.front()
   //  ------[NextSurface()]-----> mInProgressSurface
   //  --[NotifySurfaceReady()]--> mReadySurface
   //  ----[ApplyChanges()]------> mSurfaces.back()  --> .... -->
   //  mSurfaces.front()
   //
   // We mark an IOSurface as "in use" as long as it is either in
@@ -246,31 +245,31 @@ class NativeLayerCA : public NativeLayer
   RefPtr<MacIOSurface> mInProgressLockedIOSurface;
 
   RefPtr<gl::GLContextCGL> mGLContext;
 
   std::unordered_map<CFTypeRefPtr<IOSurfaceRef>, UniquePtr<gl::MozFramebuffer>>
       mFramebuffers;
 
   gfx::IntPoint mPosition;
-  gfx::IntSize mSize;
+  const gfx::IntSize mSize;
   Maybe<gfx::IntRect> mClipRect;
 
   // Lazily initialized by first call to ApplyChanges. mWrappingLayer is the
   // layer that applies mClipRect (if set), and mContentCALayer is the layer
   // that hosts the IOSurface. We do not share clip layers between consecutive
   // NativeLayerCA objects with the same clip rect.
   CALayer* mWrappingCALayer = nullptr;  // strong
   CALayer* mContentCALayer = nullptr;   // strong
 
   float mBackingScale = 1.0f;
   bool mSurfaceIsFlipped = false;
-  bool mIsOpaque = false;
+  const bool mIsOpaque = false;
+  bool mMutatedBackingScale = false;
+  bool mMutatedSurfaceIsFlipped = false;
   bool mMutatedPosition = false;
-  bool mMutatedSize = false;
-  bool mMutatedIsOpaque = false;
   bool mMutatedClipRect = false;
 };
 
 }  // namespace layers
 }  // namespace mozilla
 
 #endif  // mozilla_layers_NativeLayerCA_h
--- a/gfx/layers/NativeLayerCA.mm
+++ b/gfx/layers/NativeLayerCA.mm
@@ -49,18 +49,18 @@ NativeLayerRootCA::~NativeLayerRootCA() 
   // But we also don't want to trigger a transaction just for clearing the
   // window's layers. And we wouldn't expect a NativeLayerRootCA to be destroyed
   // while the window is still open and visible. Are layer tree modifications
   // outside of CATransactions allowed while the window is closed? Who knows.
 
   [mRootCALayer release];
 }
 
-already_AddRefed<NativeLayer> NativeLayerRootCA::CreateLayer() {
-  RefPtr<NativeLayer> layer = new NativeLayerCA();
+already_AddRefed<NativeLayer> NativeLayerRootCA::CreateLayer(const IntSize& aSize, bool aIsOpaque) {
+  RefPtr<NativeLayer> layer = new NativeLayerCA(aSize, aIsOpaque);
   return layer.forget();
 }
 
 void NativeLayerRootCA::AppendLayer(NativeLayer* aLayer) {
   MutexAutoLock lock(mMutex);
 
   RefPtr<NativeLayerCA> layerCA = aLayer->AsNativeLayerCA();
   MOZ_RELEASE_ASSERT(layerCA);
@@ -130,17 +130,18 @@ void NativeLayerRootCA::SetBackingScale(
   MutexAutoLock lock(mMutex);
 
   mBackingScale = aBackingScale;
   for (auto layer : mSublayers) {
     layer->SetBackingScale(aBackingScale);
   }
 }
 
-NativeLayerCA::NativeLayerCA() : mMutex("NativeLayerCA") {}
+NativeLayerCA::NativeLayerCA(const IntSize& aSize, bool aIsOpaque)
+    : mMutex("NativeLayerCA"), mSize(aSize), mIsOpaque(aIsOpaque) {}
 
 NativeLayerCA::~NativeLayerCA() {
   SetSurfaceRegistry(nullptr);  // or maybe MOZ_RELEASE_ASSERT(!mSurfaceRegistry) would be better?
 
   if (mInProgressLockedIOSurface) {
     mInProgressLockedIOSurface->Unlock(false);
     mInProgressLockedIOSurface = nullptr;
   }
@@ -189,61 +190,55 @@ RefPtr<IOSurfaceRegistry> NativeLayerCA:
   return mSurfaceRegistry;
 }
 
 void NativeLayerCA::SetSurfaceIsFlipped(bool aIsFlipped) {
   MutexAutoLock lock(mMutex);
 
   if (aIsFlipped != mSurfaceIsFlipped) {
     mSurfaceIsFlipped = aIsFlipped;
-    mMutatedSize = true;
+    mMutatedSurfaceIsFlipped = true;
   }
 }
 
 bool NativeLayerCA::SurfaceIsFlipped() {
   MutexAutoLock lock(mMutex);
 
   return mSurfaceIsFlipped;
 }
 
-void NativeLayerCA::SetRect(const IntRect& aRect) {
+IntSize NativeLayerCA::GetSize() {
+  MutexAutoLock lock(mMutex);
+  return mSize;
+}
+void NativeLayerCA::SetPosition(const IntPoint& aPosition) {
   MutexAutoLock lock(mMutex);
 
-  if (aRect.TopLeft() != mPosition) {
-    mPosition = aRect.TopLeft();
+  if (aPosition != mPosition) {
+    mPosition = aPosition;
     mMutatedPosition = true;
   }
-  if (aRect.Size() != mSize) {
-    mSize = aRect.Size();
-    mMutatedSize = true;
-  }
+}
+
+IntPoint NativeLayerCA::GetPosition() {
+  MutexAutoLock lock(mMutex);
+  return mPosition;
 }
 
 IntRect NativeLayerCA::GetRect() {
   MutexAutoLock lock(mMutex);
   return IntRect(mPosition, mSize);
 }
 
 void NativeLayerCA::SetBackingScale(float aBackingScale) {
   MutexAutoLock lock(mMutex);
 
   if (aBackingScale != mBackingScale) {
     mBackingScale = aBackingScale;
-    mMutatedClipRect = true;
-    mMutatedPosition = true;
-    mMutatedSize = true;
-  }
-}
-
-void NativeLayerCA::SetIsOpaque(bool aIsOpaque) {
-  MutexAutoLock lock(mMutex);
-
-  if (aIsOpaque != mIsOpaque) {
-    mIsOpaque = aIsOpaque;
-    mMutatedIsOpaque = true;
+    mMutatedBackingScale = true;
   }
 }
 
 bool NativeLayerCA::IsOpaque() {
   MutexAutoLock lock(mMutex);
   return mIsOpaque;
 }
 
@@ -283,60 +278,54 @@ void NativeLayerCA::InvalidateRegionThro
     mReadySurface->mInvalidRegion.OrWith(r);
   }
   for (auto& surf : mSurfaces) {
     surf.mInvalidRegion.OrWith(r);
   }
 }
 
 CFTypeRefPtr<IOSurfaceRef> NativeLayerCA::NextSurface(const MutexAutoLock& aLock) {
-  IntSize surfaceSize = mSize;
-  if (surfaceSize.IsEmpty()) {
-    NSLog(@"NextSurface returning nullptr because of invalid surfaceSize (%d, %d).",
-          surfaceSize.width, surfaceSize.height);
+  if (mSize.IsEmpty()) {
+    NSLog(@"NextSurface returning nullptr because of invalid mSize (%d, %d).", mSize.width,
+          mSize.height);
     return nullptr;
   }
 
   MOZ_RELEASE_ASSERT(
       !mInProgressSurface,
       "ERROR: Do not call NextSurface twice in sequence. Call NotifySurfaceReady before the "
       "next call to NextSurface.");
 
-  // Find the last surface in unusedSurfaces which has the right size. If such
+  // Find the last surface in unusedSurfaces. If such
   // a surface exists, it is the surface we will recycle.
   std::vector<SurfaceWithInvalidRegion> unusedSurfaces = RemoveExcessUnusedSurfaces(aLock);
-  auto surfIter = std::find_if(
-      unusedSurfaces.rbegin(), unusedSurfaces.rend(),
-      [surfaceSize](const SurfaceWithInvalidRegion& s) { return s.mSize == surfaceSize; });
 
   Maybe<SurfaceWithInvalidRegion> surf;
-  if (surfIter != unusedSurfaces.rend()) {
+  if (!unusedSurfaces.empty()) {
     // We found the surface we want to recycle.
-    surf = Some(*surfIter);
+    surf = Some(unusedSurfaces.back());
 
     // Remove surf from unusedSurfaces.
-    // The reverse iterator makes this a bit cumbersome.
-    unusedSurfaces.erase(std::next(surfIter).base());
+    unusedSurfaces.pop_back();
   } else {
     CFTypeRefPtr<IOSurfaceRef> newSurf = CFTypeRefPtr<IOSurfaceRef>::WrapUnderCreateRule(
         IOSurfaceCreate((__bridge CFDictionaryRef) @{
-          (__bridge NSString*)kIOSurfaceWidth : @(surfaceSize.width),
-          (__bridge NSString*)kIOSurfaceHeight : @(surfaceSize.height),
+          (__bridge NSString*)kIOSurfaceWidth : @(mSize.width),
+          (__bridge NSString*)kIOSurfaceHeight : @(mSize.height),
           (__bridge NSString*)kIOSurfacePixelFormat : @(kCVPixelFormatType_32BGRA),
           (__bridge NSString*)kIOSurfaceBytesPerElement : @(4),
         }));
     if (!newSurf) {
       NSLog(@"NextSurface returning nullptr because IOSurfaceCreate failed to create the surface.");
       return nullptr;
     }
     if (mSurfaceRegistry) {
       mSurfaceRegistry->RegisterSurface(newSurf);
     }
-    surf =
-        Some(SurfaceWithInvalidRegion{newSurf, IntRect(IntPoint(0, 0), surfaceSize), surfaceSize});
+    surf = Some(SurfaceWithInvalidRegion{newSurf, IntRect({}, mSize)});
   }
 
   // Delete all other unused surfaces.
   for (auto unusedSurf : unusedSurfaces) {
     if (mSurfaceRegistry) {
       mSurfaceRegistry->UnregisterSurface(unusedSurf.mSurface);
     }
     mFramebuffers.erase(unusedSurf.mSurface);
@@ -440,18 +429,27 @@ void NativeLayerCA::ApplyChanges() {
 
   if (!mWrappingCALayer) {
     mWrappingCALayer = [[CALayer layer] retain];
     mWrappingCALayer.position = NSZeroPoint;
     mWrappingCALayer.bounds = NSZeroRect;
     mWrappingCALayer.anchorPoint = NSZeroPoint;
     mWrappingCALayer.contentsGravity = kCAGravityTopLeft;
     mContentCALayer = [[CALayer layer] retain];
+    mContentCALayer.position = NSZeroPoint;
     mContentCALayer.anchorPoint = NSZeroPoint;
     mContentCALayer.contentsGravity = kCAGravityTopLeft;
+    mContentCALayer.contentsScale = 1;
+    mContentCALayer.bounds = CGRectMake(0, 0, mSize.width, mSize.height);
+    mContentCALayer.opaque = mIsOpaque;
+    if ([mContentCALayer respondsToSelector:@selector(setContentsOpaque:)]) {
+      // The opaque property seems to not be enough when using IOSurface contents.
+      // Additionally, call the private method setContentsOpaque.
+      [mContentCALayer setContentsOpaque:mIsOpaque];
+    }
     [mWrappingCALayer addSublayer:mContentCALayer];
   }
 
   // CALayers have a position and a size, specified through the position and the bounds properties.
   // layer.bounds.origin must always be (0, 0).
   // A layer's position affects the layer's entire layer subtree. In other words, each layer's
   // position is relative to its superlayer's position. We implement the clip rect using
   // masksToBounds on mWrappingCALayer. So mContentCALayer's position is relative to the clip rect
@@ -459,57 +457,51 @@ void NativeLayerCA::ApplyChanges() {
   // Note: The Core Animation docs on "Positioning and Sizing Sublayers" say:
   //  Important: Always use integral numbers for the width and height of your layer.
   // We hope that this refers to integral physical pixels, and not to integral logical coordinates.
 
   auto globalClipOrigin = mClipRect ? mClipRect->TopLeft() : gfx::IntPoint{};
   auto globalLayerOrigin = mPosition;
   auto clipToLayerOffset = globalLayerOrigin - globalClipOrigin;
 
-  if (mMutatedClipRect) {
+  if (mMutatedBackingScale) {
+    mContentCALayer.bounds =
+        CGRectMake(0, 0, mSize.width / mBackingScale, mSize.height / mBackingScale);
+    mContentCALayer.contentsScale = mBackingScale;
+  }
+
+  if (mMutatedBackingScale || mMutatedClipRect) {
     mWrappingCALayer.position =
         CGPointMake(globalClipOrigin.x / mBackingScale, globalClipOrigin.y / mBackingScale);
     if (mClipRect) {
       mWrappingCALayer.masksToBounds = YES;
       mWrappingCALayer.bounds =
           CGRectMake(0, 0, mClipRect->Width() / mBackingScale, mClipRect->Height() / mBackingScale);
     } else {
       mWrappingCALayer.masksToBounds = NO;
     }
   }
 
-  if (mMutatedPosition || mMutatedClipRect) {
+  if (mMutatedBackingScale || mMutatedPosition || mMutatedClipRect) {
     mContentCALayer.position =
         CGPointMake(clipToLayerOffset.x / mBackingScale, clipToLayerOffset.y / mBackingScale);
   }
 
-  if (mMutatedSize) {
-    mContentCALayer.bounds =
-        CGRectMake(0, 0, mSize.width / mBackingScale, mSize.height / mBackingScale);
-    mContentCALayer.contentsScale = mBackingScale;
+  if (mMutatedBackingScale || mMutatedSurfaceIsFlipped) {
     if (mSurfaceIsFlipped) {
       CGFloat height = mSize.height / mBackingScale;
       mContentCALayer.affineTransform = CGAffineTransformMake(1.0, 0.0, 0.0, -1.0, 0.0, height);
     } else {
       mContentCALayer.affineTransform = CGAffineTransformIdentity;
     }
   }
 
-  if (mMutatedIsOpaque) {
-    mContentCALayer.opaque = mIsOpaque;
-    if ([mContentCALayer respondsToSelector:@selector(setContentsOpaque:)]) {
-      // The opaque property seems to not be enough when using IOSurface contents.
-      // Additionally, call the private method setContentsOpaque.
-      [mContentCALayer setContentsOpaque:mIsOpaque];
-    }
-  }
-
   mMutatedPosition = false;
-  mMutatedSize = false;
-  mMutatedIsOpaque = false;
+  mMutatedBackingScale = false;
+  mMutatedSurfaceIsFlipped = false;
   mMutatedClipRect = false;
 
   if (mReadySurface) {
     mContentCALayer.contents = (id)mReadySurface->mSurface.get();
     IOSurfaceDecrementUseCount(mReadySurface->mSurface.get());
     mSurfaces.push_back(*mReadySurface);
     mReadySurface = Nothing();
   }
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -829,77 +829,75 @@ void LayerManagerComposite::UpdateDebugO
   }
   if (mDisabledApzWarningLayer) {
     mNativeLayerRoot->RemoveLayer(mDisabledApzWarningLayer);
   }
 
   bool drawFps = StaticPrefs::layers_acceleration_draw_fps();
 
   if (drawFps) {
-    if (!mGPUStatsLayer) {
-      mGPUStatsLayer = mNativeLayerRoot->CreateLayer();
-    }
-
     GPUStats stats;
     stats.mScreenPixels = mRenderBounds.Area();
     mCompositor->GetFrameStats(&stats);
 
     std::string text = mDiagnostics->GetFrameOverlayString(stats);
     IntSize size = mTextRenderer->ComputeSurfaceSize(
         text, 600, TextRenderer::FontType::FixedWidth);
 
-    mGPUStatsLayer->SetRect(IntRect(IntPoint(2, 5), size));
+    if (!mGPUStatsLayer || mGPUStatsLayer->GetSize() != size) {
+      mGPUStatsLayer = mNativeLayerRoot->CreateLayer(size, false);
+    }
+
+    mGPUStatsLayer->SetPosition(IntPoint(2, 5));
     RefPtr<DrawTarget> dt =
         mGPUStatsLayer->NextSurfaceAsDrawTarget(BackendType::SKIA);
     mTextRenderer->RenderTextToDrawTarget(dt, text, 600,
                                           TextRenderer::FontType::FixedWidth);
     mGPUStatsLayer->NotifySurfaceReady();
     mNativeLayerRoot->AppendLayer(mGPUStatsLayer);
 
     // The two warning layers are created on demand and their content is only
     // drawn once. After that, they only get moved (if the window size changes)
     // and conditionally shown.
     // The drawing would be unnecessary if we had native "color layers".
     if (mUnusedApzTransformWarning) {
       // If we have an unused APZ transform on this composite, draw a 20x20 red
       // box in the top-right corner.
       if (!mUnusedTransformWarningLayer) {
-        mUnusedTransformWarningLayer = mNativeLayerRoot->CreateLayer();
-        mUnusedTransformWarningLayer->SetRect(IntRect(0, 0, 20, 20));
-        mUnusedTransformWarningLayer->SetIsOpaque(true);
+        mUnusedTransformWarningLayer =
+            mNativeLayerRoot->CreateLayer(IntSize(20, 20), true);
         RefPtr<DrawTarget> dt =
             mUnusedTransformWarningLayer->NextSurfaceAsDrawTarget(
                 BackendType::SKIA);
         dt->FillRect(Rect(0, 0, 20, 20), ColorPattern(Color(1, 0, 0, 1)));
         mUnusedTransformWarningLayer->NotifySurfaceReady();
       }
-      mUnusedTransformWarningLayer->SetRect(
-          IntRect(mRenderBounds.XMost() - 20, mRenderBounds.Y(), 20, 20));
+      mUnusedTransformWarningLayer->SetPosition(
+          IntPoint(mRenderBounds.XMost() - 20, mRenderBounds.Y()));
       mNativeLayerRoot->AppendLayer(mUnusedTransformWarningLayer);
 
       mUnusedApzTransformWarning = false;
       SetDebugOverlayWantsNextFrame(true);
     }
 
     if (mDisabledApzWarning) {
       // If we have a disabled APZ on this composite, draw a 20x20 yellow box
       // in the top-right corner, to the left of the unused-apz-transform
       // warning box.
       if (!mDisabledApzWarningLayer) {
-        mDisabledApzWarningLayer = mNativeLayerRoot->CreateLayer();
-        mDisabledApzWarningLayer->SetRect(IntRect(0, 0, 20, 20));
-        mDisabledApzWarningLayer->SetIsOpaque(true);
+        mDisabledApzWarningLayer =
+            mNativeLayerRoot->CreateLayer(IntSize(20, 20), true);
         RefPtr<DrawTarget> dt =
             mDisabledApzWarningLayer->NextSurfaceAsDrawTarget(
                 BackendType::SKIA);
         dt->FillRect(Rect(0, 0, 20, 20), ColorPattern(Color(1, 1, 0, 1)));
         mDisabledApzWarningLayer->NotifySurfaceReady();
       }
-      mDisabledApzWarningLayer->SetRect(
-          IntRect(mRenderBounds.XMost() - 40, mRenderBounds.Y(), 20, 20));
+      mDisabledApzWarningLayer->SetPosition(
+          IntPoint(mRenderBounds.XMost() - 40, mRenderBounds.Y()));
       mNativeLayerRoot->AppendLayer(mDisabledApzWarningLayer);
 
       mDisabledApzWarning = false;
       SetDebugOverlayWantsNextFrame(true);
     }
   } else {
     mGPUStatsLayer = nullptr;
     mUnusedTransformWarningLayer = nullptr;
@@ -1003,30 +1001,32 @@ void LayerManagerComposite::PlaceNativeL
   }
 }
 
 void LayerManagerComposite::PlaceNativeLayer(
     const IntRect& aRect, bool aOpaque,
     std::deque<RefPtr<NativeLayer>>* aLayersToRecycle,
     IntRegion* aWindowInvalidRegion) {
   RefPtr<NativeLayer> layer;
-  if (aLayersToRecycle->empty()) {
-    layer = mNativeLayerRoot->CreateLayer();
+  if (aLayersToRecycle->empty() ||
+      aLayersToRecycle->front()->GetSize() != aRect.Size() ||
+      aLayersToRecycle->front()->IsOpaque() != aOpaque) {
+    layer = mNativeLayerRoot->CreateLayer(aRect.Size(), aOpaque);
     mNativeLayerRoot->AppendLayer(layer);
+    aWindowInvalidRegion->OrWith(aRect);
   } else {
     layer = aLayersToRecycle->front();
     aLayersToRecycle->pop_front();
+    IntRect oldRect = layer->GetRect();
+    if (!aRect.IsEqualInterior(oldRect)) {
+      aWindowInvalidRegion->OrWith(oldRect);
+      aWindowInvalidRegion->OrWith(aRect);
+    }
   }
-  IntRect oldRect = layer->GetRect();
-  if (!aRect.IsEqualInterior(oldRect)) {
-    aWindowInvalidRegion->OrWith(oldRect);
-    aWindowInvalidRegion->OrWith(aRect);
-  }
-  layer->SetRect(aRect);
-  layer->SetIsOpaque(aOpaque);
+  layer->SetPosition(aRect.TopLeft());
   mNativeLayers.push_back(layer);
 }
 
 // Used to clear the 'mLayerComposited' flag at the beginning of each Render().
 static void ClearLayerFlags(Layer* aLayer) {
   ForEachNode<ForwardIterator>(aLayer, [](Layer* layer) {
     if (layer->AsHostLayer()) {
       static_cast<LayerComposite*>(layer->AsHostLayer())
--- a/gfx/webrender_bindings/RenderCompositorOGL.cpp
+++ b/gfx/webrender_bindings/RenderCompositorOGL.cpp
@@ -31,23 +31,16 @@ UniquePtr<RenderCompositor> RenderCompos
 RenderCompositorOGL::RenderCompositorOGL(
     RefPtr<gl::GLContext>&& aGL, RefPtr<widget::CompositorWidget>&& aWidget)
     : RenderCompositor(std::move(aWidget)),
       mGL(aGL),
       mNativeLayerRoot(GetWidget()->GetNativeLayerRoot()),
       mPreviousFrameDoneSync(nullptr),
       mThisFrameDoneSync(nullptr) {
   MOZ_ASSERT(mGL);
-
-  if (mNativeLayerRoot && !ShouldUseNativeCompositor()) {
-    mNativeLayerForEntireWindow = mNativeLayerRoot->CreateLayer();
-    mNativeLayerForEntireWindow->SetSurfaceIsFlipped(true);
-    mNativeLayerForEntireWindow->SetGLContext(mGL);
-    mNativeLayerRoot->AppendLayer(mNativeLayerForEntireWindow);
-  }
 }
 
 RenderCompositorOGL::~RenderCompositorOGL() {
   if (mNativeLayerRoot) {
     mNativeLayerRoot->SetLayers({});
     mNativeLayerForEntireWindow = nullptr;
     mNativeLayerRoot = nullptr;
   }
@@ -70,19 +63,32 @@ RenderCompositorOGL::~RenderCompositorOG
 }
 
 bool RenderCompositorOGL::BeginFrame() {
   if (!mGL->MakeCurrent()) {
     gfxCriticalNote << "Failed to make render context current, can't draw.";
     return false;
   }
 
+  if (mNativeLayerRoot && !ShouldUseNativeCompositor()) {
+    gfx::IntSize bufferSize = GetBufferSize().ToUnknownSize();
+    if (mNativeLayerForEntireWindow &&
+        mNativeLayerForEntireWindow->GetSize() != bufferSize) {
+      mNativeLayerRoot->RemoveLayer(mNativeLayerForEntireWindow);
+      mNativeLayerForEntireWindow = nullptr;
+    }
+    if (!mNativeLayerForEntireWindow) {
+      mNativeLayerForEntireWindow =
+          mNativeLayerRoot->CreateLayer(bufferSize, false);
+      mNativeLayerForEntireWindow->SetSurfaceIsFlipped(true);
+      mNativeLayerForEntireWindow->SetGLContext(mGL);
+      mNativeLayerRoot->AppendLayer(mNativeLayerForEntireWindow);
+    }
+  }
   if (mNativeLayerForEntireWindow) {
-    gfx::IntRect bounds({}, GetBufferSize().ToUnknownSize());
-    mNativeLayerForEntireWindow->SetRect(bounds);
     Maybe<GLuint> fbo =
         mNativeLayerForEntireWindow->NextSurfaceAsFramebuffer(true);
     if (!fbo) {
       return false;
     }
     mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, *fbo);
   } else {
     mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mGL->GetDefaultFramebuffer());
@@ -198,20 +204,19 @@ void RenderCompositorOGL::Unbind() {
   mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
   mCurrentlyBoundNativeLayer->NotifySurfaceReady();
   mCurrentlyBoundNativeLayer = nullptr;
 }
 
 void RenderCompositorOGL::CreateSurface(wr::NativeSurfaceId aId,
                                         wr::DeviceIntSize aSize,
                                         bool aIsOpaque) {
-  RefPtr<layers::NativeLayer> layer = mNativeLayerRoot->CreateLayer();
-  layer->SetRect(gfx::IntRect(0, 0, aSize.width, aSize.height));
+  RefPtr<layers::NativeLayer> layer = mNativeLayerRoot->CreateLayer(
+      IntSize(aSize.width, aSize.height), aIsOpaque);
   layer->SetGLContext(mGL);
-  layer->SetIsOpaque(aIsOpaque);
   mNativeLayers.insert({wr::AsUint64(aId), layer});
 }
 
 void RenderCompositorOGL::DestroySurface(NativeSurfaceId aId) {
   auto layerCursor = mNativeLayers.find(wr::AsUint64(aId));
   if (layerCursor == mNativeLayers.end()) {
     printf("deleting non-existent layer %" PRId64 "\n", wr::AsUint64(aId));
     return;
@@ -227,22 +232,22 @@ void RenderCompositorOGL::AddSurface(wr:
                                      wr::DeviceIntPoint aPosition,
                                      wr::DeviceIntRect aClipRect) {
   MOZ_RELEASE_ASSERT(!mCurrentlyBoundNativeLayer);
 
   auto layerCursor = mNativeLayers.find(wr::AsUint64(aId));
   MOZ_RELEASE_ASSERT(layerCursor != mNativeLayers.end());
   RefPtr<layers::NativeLayer> layer = layerCursor->second;
 
-  gfx::IntSize layerSize = layer->GetRect().Size();
+  gfx::IntSize layerSize = layer->GetSize();
   gfx::IntRect layerRect(aPosition.x, aPosition.y, layerSize.width,
                          layerSize.height);
   gfx::IntRect clipRect(aClipRect.origin.x, aClipRect.origin.y,
                         aClipRect.size.width, aClipRect.size.height);
-  layer->SetRect(layerRect);
+  layer->SetPosition(layerRect.TopLeft());
   layer->SetClipRect(Some(clipRect));
   mAddedLayers.AppendElement(layer);
 
   mAddedPixelCount += layerRect.Area();
   mAddedClippedPixelCount += clipRect.Intersect(layerRect).Area();
 }
 
 }  // namespace wr
--- a/widget/cocoa/nsChildView.mm
+++ b/widget/cocoa/nsChildView.mm
@@ -1483,29 +1483,34 @@ bool nsChildView::PaintWindowInContext(C
   CGContextRestoreGState(aContext);
 
   mBackingSurface->ReleaseBits(data);
 
   return painted;
 }
 
 void nsChildView::EnsureContentLayerForMainThreadPainting() {
+  // Ensure we have an mContentLayer of the correct size.
+  // The content layer gets created on demand for BasicLayers windows. We do
+  // not create it during widget creation because, for non-BasicLayers windows,
+  // the compositing layer manager will create any layers it needs.
+  gfx::IntSize size = GetBounds().Size().ToUnknownSize();
+  if (mContentLayer && mContentLayer->GetSize() != size) {
+    mNativeLayerRoot->RemoveLayer(mContentLayer);
+    mContentLayer = nullptr;
+  }
   if (!mContentLayer) {
-    // The content layer gets created on demand for BasicLayers windows. We do
-    // not create it during widget creation because, for non-BasicLayers windows,
-    // the compositing layer manager will create any layers it needs.
-    RefPtr<NativeLayer> contentLayer = mNativeLayerRoot->CreateLayer();
+    RefPtr<NativeLayer> contentLayer = mNativeLayerRoot->CreateLayer(size, false);
     mNativeLayerRoot->AppendLayer(contentLayer);
     mContentLayer = contentLayer->AsNativeLayerCA();
   }
 }
 
 void nsChildView::PaintWindowInContentLayer() {
   EnsureContentLayerForMainThreadPainting();
-  mContentLayer->SetRect(GetBounds().ToUnknownRect());
   mContentLayer->SetSurfaceIsFlipped(false);
   RefPtr<DrawTarget> dt = mContentLayer->NextSurfaceAsDrawTarget(gfx::BackendType::SKIA);
   if (!dt) {
     return;
   }
 
   PaintWindowInDrawTarget(
       dt, LayoutDeviceIntRegion::FromUnknownRegion(mContentLayer->CurrentSurfaceInvalidRegion()),