Backed out 4 changesets (bug 1594950) for turning bug 1405083 into permafail.
authorCosmin Sabou <csabou@mozilla.com>
Wed, 13 Nov 2019 06:44:24 +0200
changeset 501700 c351bea716961017d6e4f59cb48a9bcaf6111e9b
parent 501699 1dda54201821a681dcd00b06efb2815d9dd7bdea
child 501701 f644542d11c15980eed8368d5b52539d571c35ce
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)
bugs1594950, 1405083
milestone72.0a1
backs out684a87e91d9444b222a0e2f5e540c06b90becb13
adea6912c3a313742b1662803d48caff5a6a8c3c
aea7108204e950084354be4614349e614ae19166
15baea8520de4503c11b07accc0f0de013336971
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
Backed out 4 changesets (bug 1594950) for turning bug 1405083 into permafail. Backed out changeset 684a87e91d94 (bug 1594950) Backed out changeset adea6912c3a3 (bug 1594950) Backed out changeset aea7108204e9 (bug 1594950) Backed out changeset 15baea8520de (bug 1594950)
gfx/layers/NativeLayer.h
gfx/layers/NativeLayerCA.h
gfx/layers/NativeLayerCA.mm
gfx/layers/basic/BasicCompositor.cpp
gfx/layers/composite/LayerManagerComposite.cpp
gfx/layers/opengl/CompositorOGL.cpp
gfx/webrender_bindings/RenderCompositorOGL.cpp
widget/cocoa/nsChildView.h
widget/cocoa/nsChildView.mm
--- a/gfx/layers/NativeLayer.h
+++ b/gfx/layers/NativeLayer.h
@@ -28,18 +28,17 @@ 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(const gfx::IntSize& aSize,
-                                                    bool aIsOpaque) = 0;
+  virtual already_AddRefed<NativeLayer> CreateLayer() = 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() {}
 };
 
@@ -62,87 +61,89 @@ 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 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 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;
+  virtual gfx::IntRect GetRect() = 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;
 
-  // 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.
-  // aUpdateRegion must not extend beyond the layer size.
+  // 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.
   virtual RefPtr<gfx::DrawTarget> NextSurfaceAsDrawTarget(
-      const gfx::IntRegion& aUpdateRegion, gfx::BackendType aBackendType) = 0;
+      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 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
+  // 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
   // 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
   // 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.
-  // aUpdateRegion must not extend beyond the layer size.
-  virtual Maybe<GLuint> NextSurfaceAsFramebuffer(
-      const gfx::IntRegion& aUpdateRegion, bool aNeedsDepth) = 0;
+  virtual Maybe<GLuint> NextSurfaceAsFramebuffer(bool aNeedsDepth) = 0;
 
   // The invalid region of the surface that has been returned from the most
   // recent call to NextSurface*. Newly-created surfaces are entirely invalid.
   // For surfaces that have been used before, the invalid region is the union of
-  // all invalid regions that have been passed to NextSurface* since the last
-  // time that NotifySurfaceReady was called for this surface. Can only be
-  // called between calls to NextSurface* and NotifySurfaceReady. Can be called
-  // on any thread.
+  // all invalid regions that have been passed to
+  // InvalidateRegionThroughoutSwapchain since the last time that
+  // NotifySurfaceReady was called for this surface. Can only be called between
+  // calls to NextSurface* and NotifySurfaceReady. Can be called on any thread.
   virtual gfx::IntRegion CurrentSurfaceInvalidRegion() = 0;
 
   // Indicates that the surface which has been returned from the most recent
   // call to NextSurface* is now finished being drawn to and can be displayed on
   // the screen. Resets the invalid region on the surface to the empty region.
   virtual void NotifySurfaceReady() = 0;
 
  protected:
--- a/gfx/layers/NativeLayerCA.h
+++ b/gfx/layers/NativeLayerCA.h
@@ -29,16 +29,27 @@ namespace mozilla {
 
 namespace gl {
 class GLContextCGL;
 class MozFramebuffer;
 }  // namespace gl
 
 namespace layers {
 
+class IOSurfaceRegistry {
+ public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(IOSurfaceRegistry)
+
+  virtual void RegisterSurface(CFTypeRefPtr<IOSurfaceRef> aSurface) = 0;
+  virtual void UnregisterSurface(CFTypeRefPtr<IOSurfaceRef> aSurface) = 0;
+
+ protected:
+  virtual ~IOSurfaceRegistry() {}
+};
+
 // NativeLayerRootCA is the CoreAnimation implementation of the NativeLayerRoot
 // interface. A NativeLayerRootCA is created by the widget around an existing
 // CALayer with a call to CreateForCALayer.
 // All methods can be called from any thread, there is internal locking.
 // All effects from mutating methods are buffered locally and don't modify the
 // underlying CoreAnimation layers until ApplyChanges() is called. This ensures
 // that the modifications can be limited to run within a CoreAnimation
 // transaction, and on a thread of the caller's choosing.
@@ -47,18 +58,17 @@ 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(const gfx::IntSize& aSize,
-                                            bool aIsOpaque) override;
+  already_AddRefed<NativeLayer> CreateLayer() 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;
 
@@ -75,87 +85,111 @@ class NativeLayerRootCA : public NativeL
 // All calls to mutating methods are buffered, and don't take effect on the
 // underlying CoreAnimation layers until ApplyChanges() is called.
 // The two most important methods are NextSurface and NotifySurfaceReady:
 // NextSurface takes an available surface from the swap chain or creates a new
 // surface if necessary. This surface can then be drawn to. Once drawing is
 // finished, NotifySurfaceReady marks the surface as ready. This surface is
 // committed to the layer during the next call to ApplyChanges().
 // The swap chain keeps track of invalid areas within the surfaces.
+//
+// Creation and destruction of IOSurface objects is broadcast to an optional
+// "surface registry" so that associated objects such as framebuffer objects
+// don't need to be recreated on every frame: Instead, the surface registry can
+// 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
-  gfx::IntSize GetSize() override;
-  void SetPosition(const gfx::IntPoint& aPosition) override;
-  gfx::IntPoint GetPosition() override;
+  void SetRect(const gfx::IntRect& aRect) override;
   gfx::IntRect GetRect() override;
+  void InvalidateRegionThroughoutSwapchain(
+      const gfx::IntRegion& aRegion) override;
   RefPtr<gfx::DrawTarget> NextSurfaceAsDrawTarget(
-      const gfx::IntRegion& aUpdateRegion,
       gfx::BackendType aBackendType) override;
   void SetGLContext(gl::GLContext* aGLContext) override;
   gl::GLContext* GetGLContext() override;
-  Maybe<GLuint> NextSurfaceAsFramebuffer(const gfx::IntRegion& aUpdateRegion,
-                                         bool aNeedsDepth) 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.
+  // The registry's methods, Register/UnregisterSurface, will be called
+  // synchronously during calls to NextSurface(), SetSurfaceRegistry(), and the
+  // NativeLayer destructor, on the thread that those things happen to run on.
+  // If this layer already owns surfaces when SetSurfaceRegistry gets called
+  // with a non-null surface registry, those surfaces will immediately
+  // (synchronously) be registered with that registry. If the current surface
+  // registry is unset (via a call to SetSurfaceRegistry with a different value,
+  // such as null), and the NativeLayer still owns surfaces, then those surfaces
+  // will immediately be unregistered.
+  // Since NativeLayer objects are reference counted and can be used from
+  // different threads, it is recommended to call SetSurfaceRegistry(nullptr)
+  // 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(const gfx::IntSize& aSize, bool aIsOpaque);
+  NativeLayerCA();
   ~NativeLayerCA() override;
 
   // Returns an IOSurface that can be drawn to. The size of the IOSurface will
-  // be the same as the size of this layer.
+  // be the size of the rect that has been passed to SetRect.
   // 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&);
 
   // To be called by NativeLayerRootCA:
   CALayer* UnderlyingCALayer() { return mWrappingCALayer; }
   void ApplyChanges();
   void SetBackingScale(float aBackingScale);
 
-  // Invalidates the specified region in all surfaces that are tracked by this
-  // layer.
-  void InvalidateRegionThroughoutSwapchain(const MutexAutoLock&,
-                                           const gfx::IntRegion& aRegion);
-
   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 (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, 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:
   //
   //  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
@@ -212,31 +246,31 @@ class NativeLayerCA : public NativeLayer
   RefPtr<MacIOSurface> mInProgressLockedIOSurface;
 
   RefPtr<gl::GLContextCGL> mGLContext;
 
   std::unordered_map<CFTypeRefPtr<IOSurfaceRef>, UniquePtr<gl::MozFramebuffer>>
       mFramebuffers;
 
   gfx::IntPoint mPosition;
-  const gfx::IntSize mSize;
+  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;
-  const bool mIsOpaque = false;
-  bool mMutatedBackingScale = false;
-  bool mMutatedSurfaceIsFlipped = false;
+  bool mIsOpaque = 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(const IntSize& aSize, bool aIsOpaque) {
-  RefPtr<NativeLayer> layer = new NativeLayerCA(aSize, aIsOpaque);
+already_AddRefed<NativeLayer> NativeLayerRootCA::CreateLayer() {
+  RefPtr<NativeLayer> layer = new NativeLayerCA();
   return layer.forget();
 }
 
 void NativeLayerRootCA::AppendLayer(NativeLayer* aLayer) {
   MutexAutoLock lock(mMutex);
 
   RefPtr<NativeLayerCA> layerCA = aLayer->AsNativeLayerCA();
   MOZ_RELEASE_ASSERT(layerCA);
@@ -130,79 +130,120 @@ void NativeLayerRootCA::SetBackingScale(
   MutexAutoLock lock(mMutex);
 
   mBackingScale = aBackingScale;
   for (auto layer : mSublayers) {
     layer->SetBackingScale(aBackingScale);
   }
 }
 
-NativeLayerCA::NativeLayerCA(const IntSize& aSize, bool aIsOpaque)
-    : mMutex("NativeLayerCA"), mSize(aSize), mIsOpaque(aIsOpaque) {}
+NativeLayerCA::NativeLayerCA() : mMutex("NativeLayerCA") {}
 
 NativeLayerCA::~NativeLayerCA() {
+  SetSurfaceRegistry(nullptr);  // or maybe MOZ_RELEASE_ASSERT(!mSurfaceRegistry) would be better?
+
   if (mInProgressLockedIOSurface) {
     mInProgressLockedIOSurface->Unlock(false);
     mInProgressLockedIOSurface = nullptr;
   }
   if (mInProgressSurface) {
     IOSurfaceDecrementUseCount(mInProgressSurface->mSurface.get());
   }
   if (mReadySurface) {
     IOSurfaceDecrementUseCount(mReadySurface->mSurface.get());
   }
 
   [mContentCALayer release];
   [mWrappingCALayer release];
 }
 
+void NativeLayerCA::SetSurfaceRegistry(RefPtr<IOSurfaceRegistry> aSurfaceRegistry) {
+  MutexAutoLock lock(mMutex);
+
+  if (mSurfaceRegistry) {
+    for (auto surf : mSurfaces) {
+      mSurfaceRegistry->UnregisterSurface(surf.mSurface);
+    }
+    if (mInProgressSurface) {
+      mSurfaceRegistry->UnregisterSurface(mInProgressSurface->mSurface);
+    }
+    if (mReadySurface) {
+      mSurfaceRegistry->UnregisterSurface(mReadySurface->mSurface);
+    }
+  }
+  mSurfaceRegistry = aSurfaceRegistry;
+  if (mSurfaceRegistry) {
+    for (auto surf : mSurfaces) {
+      mSurfaceRegistry->RegisterSurface(surf.mSurface);
+    }
+    if (mInProgressSurface) {
+      mSurfaceRegistry->RegisterSurface(mInProgressSurface->mSurface);
+    }
+    if (mReadySurface) {
+      mSurfaceRegistry->RegisterSurface(mReadySurface->mSurface);
+    }
+  }
+}
+
+RefPtr<IOSurfaceRegistry> NativeLayerCA::GetSurfaceRegistry() {
+  MutexAutoLock lock(mMutex);
+
+  return mSurfaceRegistry;
+}
+
 void NativeLayerCA::SetSurfaceIsFlipped(bool aIsFlipped) {
   MutexAutoLock lock(mMutex);
 
   if (aIsFlipped != mSurfaceIsFlipped) {
     mSurfaceIsFlipped = aIsFlipped;
-    mMutatedSurfaceIsFlipped = true;
+    mMutatedSize = true;
   }
 }
 
 bool NativeLayerCA::SurfaceIsFlipped() {
   MutexAutoLock lock(mMutex);
 
   return mSurfaceIsFlipped;
 }
 
-IntSize NativeLayerCA::GetSize() {
-  MutexAutoLock lock(mMutex);
-  return mSize;
-}
-void NativeLayerCA::SetPosition(const IntPoint& aPosition) {
+void NativeLayerCA::SetRect(const IntRect& aRect) {
   MutexAutoLock lock(mMutex);
 
-  if (aPosition != mPosition) {
-    mPosition = aPosition;
+  if (aRect.TopLeft() != mPosition) {
+    mPosition = aRect.TopLeft();
     mMutatedPosition = true;
   }
-}
-
-IntPoint NativeLayerCA::GetPosition() {
-  MutexAutoLock lock(mMutex);
-  return mPosition;
+  if (aRect.Size() != mSize) {
+    mSize = aRect.Size();
+    mMutatedSize = true;
+  }
 }
 
 IntRect NativeLayerCA::GetRect() {
   MutexAutoLock lock(mMutex);
   return IntRect(mPosition, mSize);
 }
 
 void NativeLayerCA::SetBackingScale(float aBackingScale) {
   MutexAutoLock lock(mMutex);
 
   if (aBackingScale != mBackingScale) {
     mBackingScale = aBackingScale;
-    mMutatedBackingScale = true;
+    mMutatedClipRect = true;
+    mMutatedPosition = true;
+    mMutatedSize = true;
+  }
+}
+
+void NativeLayerCA::SetIsOpaque(bool aIsOpaque) {
+  MutexAutoLock lock(mMutex);
+
+  if (aIsOpaque != mIsOpaque) {
+    mIsOpaque = aIsOpaque;
+    mMutatedIsOpaque = true;
   }
 }
 
 bool NativeLayerCA::IsOpaque() {
   MutexAutoLock lock(mMutex);
   return mIsOpaque;
 }
 
@@ -225,91 +266,101 @@ IntRegion NativeLayerCA::CurrentSurfaceI
 
   MOZ_RELEASE_ASSERT(
       mInProgressSurface,
       "Only call currentSurfaceInvalidRegion after a call to NextSurface and before the call "
       "to notifySurfaceIsReady.");
   return mInProgressSurface->mInvalidRegion;
 }
 
-void NativeLayerCA::InvalidateRegionThroughoutSwapchain(const MutexAutoLock&,
-                                                        const IntRegion& aRegion) {
+void NativeLayerCA::InvalidateRegionThroughoutSwapchain(const IntRegion& aRegion) {
+  MutexAutoLock lock(mMutex);
+
   IntRegion r = aRegion;
+  r.AndWith(IntRect(IntPoint(0, 0), mSize));
   if (mInProgressSurface) {
     mInProgressSurface->mInvalidRegion.OrWith(r);
   }
   if (mReadySurface) {
     mReadySurface->mInvalidRegion.OrWith(r);
   }
   for (auto& surf : mSurfaces) {
     surf.mInvalidRegion.OrWith(r);
   }
 }
 
 CFTypeRefPtr<IOSurfaceRef> NativeLayerCA::NextSurface(const MutexAutoLock& aLock) {
-  if (mSize.IsEmpty()) {
-    NSLog(@"NextSurface returning nullptr because of invalid mSize (%d, %d).", mSize.width,
-          mSize.height);
+  IntSize surfaceSize = mSize;
+  if (surfaceSize.IsEmpty()) {
+    NSLog(@"NextSurface returning nullptr because of invalid surfaceSize (%d, %d).",
+          surfaceSize.width, surfaceSize.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. If such
+  // Find the last surface in unusedSurfaces which has the right size. 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 (!unusedSurfaces.empty()) {
+  if (surfIter != unusedSurfaces.rend()) {
     // We found the surface we want to recycle.
-    surf = Some(unusedSurfaces.back());
+    surf = Some(*surfIter);
 
     // Remove surf from unusedSurfaces.
-    unusedSurfaces.pop_back();
+    // The reverse iterator makes this a bit cumbersome.
+    unusedSurfaces.erase(std::next(surfIter).base());
   } else {
     CFTypeRefPtr<IOSurfaceRef> newSurf = CFTypeRefPtr<IOSurfaceRef>::WrapUnderCreateRule(
         IOSurfaceCreate((__bridge CFDictionaryRef) @{
-          (__bridge NSString*)kIOSurfaceWidth : @(mSize.width),
-          (__bridge NSString*)kIOSurfaceHeight : @(mSize.height),
+          (__bridge NSString*)kIOSurfaceWidth : @(surfaceSize.width),
+          (__bridge NSString*)kIOSurfaceHeight : @(surfaceSize.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;
     }
-    surf = Some(SurfaceWithInvalidRegion{newSurf, IntRect({}, mSize)});
+    if (mSurfaceRegistry) {
+      mSurfaceRegistry->RegisterSurface(newSurf);
+    }
+    surf =
+        Some(SurfaceWithInvalidRegion{newSurf, IntRect(IntPoint(0, 0), surfaceSize), surfaceSize});
   }
 
   // Delete all other unused surfaces.
   for (auto unusedSurf : unusedSurfaces) {
+    if (mSurfaceRegistry) {
+      mSurfaceRegistry->UnregisterSurface(unusedSurf.mSurface);
+    }
     mFramebuffers.erase(unusedSurf.mSurface);
   }
   unusedSurfaces.clear();
 
   MOZ_RELEASE_ASSERT(surf);
   mInProgressSurface = std::move(surf);
   IOSurfaceIncrementUseCount(mInProgressSurface->mSurface.get());
   return mInProgressSurface->mSurface;
 }
 
-RefPtr<gfx::DrawTarget> NativeLayerCA::NextSurfaceAsDrawTarget(const gfx::IntRegion& aUpdateRegion,
-                                                               gfx::BackendType aBackendType) {
+RefPtr<gfx::DrawTarget> NativeLayerCA::NextSurfaceAsDrawTarget(gfx::BackendType aBackendType) {
   MutexAutoLock lock(mMutex);
   CFTypeRefPtr<IOSurfaceRef> surface = NextSurface(lock);
   if (!surface) {
     return nullptr;
   }
 
-  MOZ_RELEASE_ASSERT(IntRect({}, mSize).Contains(aUpdateRegion.GetBounds()));
-  InvalidateRegionThroughoutSwapchain(lock, aUpdateRegion);
-
   mInProgressLockedIOSurface = new MacIOSurface(std::move(surface));
   mInProgressLockedIOSurface->Lock(false);
   return mInProgressLockedIOSurface->GetAsDrawTargetLocked(aBackendType);
 }
 
 void NativeLayerCA::SetGLContext(gl::GLContext* aContext) {
   MutexAutoLock lock(mMutex);
 
@@ -322,26 +373,23 @@ void NativeLayerCA::SetGLContext(gl::GLC
   }
 }
 
 gl::GLContext* NativeLayerCA::GetGLContext() {
   MutexAutoLock lock(mMutex);
   return mGLContext;
 }
 
-Maybe<GLuint> NativeLayerCA::NextSurfaceAsFramebuffer(const gfx::IntRegion& aUpdateRegion,
-                                                      bool aNeedsDepth) {
+Maybe<GLuint> NativeLayerCA::NextSurfaceAsFramebuffer(bool aNeedsDepth) {
   MutexAutoLock lock(mMutex);
   CFTypeRefPtr<IOSurfaceRef> surface = NextSurface(lock);
   if (!surface) {
     return Nothing();
   }
 
-  MOZ_RELEASE_ASSERT(IntRect({}, mSize).Contains(aUpdateRegion.GetBounds()));
-  InvalidateRegionThroughoutSwapchain(lock, aUpdateRegion);
   return Some(GetOrCreateFramebufferForSurface(lock, std::move(surface), aNeedsDepth));
 }
 
 GLuint NativeLayerCA::GetOrCreateFramebufferForSurface(const MutexAutoLock&,
                                                        CFTypeRefPtr<IOSurfaceRef> aSurface,
                                                        bool aNeedsDepth) {
   auto fbCursor = mFramebuffers.find(aSurface);
   if (fbCursor != mFramebuffers.end()) {
@@ -394,22 +442,16 @@ void NativeLayerCA::ApplyChanges() {
     mWrappingCALayer = [[CALayer layer] retain];
     mWrappingCALayer.position = NSZeroPoint;
     mWrappingCALayer.bounds = NSZeroRect;
     mWrappingCALayer.anchorPoint = NSZeroPoint;
     mWrappingCALayer.contentsGravity = kCAGravityTopLeft;
     mContentCALayer = [[CALayer layer] retain];
     mContentCALayer.anchorPoint = NSZeroPoint;
     mContentCALayer.contentsGravity = kCAGravityTopLeft;
-    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
@@ -417,51 +459,57 @@ 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 (mMutatedBackingScale) {
-    mContentCALayer.bounds =
-        CGRectMake(0, 0, mSize.width / mBackingScale, mSize.height / mBackingScale);
-    mContentCALayer.contentsScale = mBackingScale;
-  }
-
-  if (mMutatedBackingScale || mMutatedClipRect) {
+  if (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 (mMutatedBackingScale || mMutatedPosition || mMutatedClipRect) {
+  if (mMutatedPosition || mMutatedClipRect) {
     mContentCALayer.position =
         CGPointMake(clipToLayerOffset.x / mBackingScale, clipToLayerOffset.y / mBackingScale);
   }
 
-  if (mMutatedBackingScale || mMutatedSurfaceIsFlipped) {
+  if (mMutatedSize) {
+    mContentCALayer.bounds =
+        CGRectMake(0, 0, mSize.width / mBackingScale, mSize.height / mBackingScale);
+    mContentCALayer.contentsScale = mBackingScale;
     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;
-  mMutatedBackingScale = false;
-  mMutatedSurfaceIsFlipped = false;
+  mMutatedSize = false;
+  mMutatedIsOpaque = false;
   mMutatedClipRect = false;
 
   if (mReadySurface) {
     mContentCALayer.contents = (id)mReadySurface->mSurface.get();
     IOSurfaceDecrementUseCount(mReadySurface->mSurface.get());
     mSurfaces.push_back(*mReadySurface);
     mReadySurface = Nothing();
   }
--- a/gfx/layers/basic/BasicCompositor.cpp
+++ b/gfx/layers/basic/BasicCompositor.cpp
@@ -1029,18 +1029,19 @@ Maybe<gfx::IntRect> BasicCompositor::Beg
 
   if (mInvalidRegion.IsEmpty()) {
     return Nothing();
   }
 
   RefPtr<CompositingRenderTarget> target;
   aNativeLayer->SetSurfaceIsFlipped(false);
   IntRegion invalidRelativeToLayer = mInvalidRegion.MovedBy(-rect.TopLeft());
-  RefPtr<DrawTarget> dt = aNativeLayer->NextSurfaceAsDrawTarget(
-      invalidRelativeToLayer, BackendType::SKIA);
+  aNativeLayer->InvalidateRegionThroughoutSwapchain(invalidRelativeToLayer);
+  RefPtr<DrawTarget> dt =
+      aNativeLayer->NextSurfaceAsDrawTarget(BackendType::SKIA);
   if (!dt) {
     return Nothing();
   }
   invalidRelativeToLayer = aNativeLayer->CurrentSurfaceInvalidRegion();
   mInvalidRegion = invalidRelativeToLayer.MovedBy(rect.TopLeft());
   MOZ_RELEASE_ASSERT(!mInvalidRegion.IsEmpty());
   mCurrentNativeLayer = aNativeLayer;
   IntRegion clearRegion;
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -829,75 +829,77 @@ 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);
 
-    if (!mGPUStatsLayer || mGPUStatsLayer->GetSize() != size) {
-      mGPUStatsLayer = mNativeLayerRoot->CreateLayer(size, false);
-    }
-
-    mGPUStatsLayer->SetPosition(IntPoint(2, 5));
-    RefPtr<DrawTarget> dt = mGPUStatsLayer->NextSurfaceAsDrawTarget(
-        IntRect({}, size), BackendType::SKIA);
+    mGPUStatsLayer->SetRect(IntRect(IntPoint(2, 5), size));
+    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(IntSize(20, 20), true);
+        mUnusedTransformWarningLayer = mNativeLayerRoot->CreateLayer();
+        mUnusedTransformWarningLayer->SetRect(IntRect(0, 0, 20, 20));
+        mUnusedTransformWarningLayer->SetIsOpaque(true);
         RefPtr<DrawTarget> dt =
             mUnusedTransformWarningLayer->NextSurfaceAsDrawTarget(
-                IntRect(0, 0, 20, 20), BackendType::SKIA);
+                BackendType::SKIA);
         dt->FillRect(Rect(0, 0, 20, 20), ColorPattern(Color(1, 0, 0, 1)));
         mUnusedTransformWarningLayer->NotifySurfaceReady();
       }
-      mUnusedTransformWarningLayer->SetPosition(
-          IntPoint(mRenderBounds.XMost() - 20, mRenderBounds.Y()));
+      mUnusedTransformWarningLayer->SetRect(
+          IntRect(mRenderBounds.XMost() - 20, mRenderBounds.Y(), 20, 20));
       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(IntSize(20, 20), true);
+        mDisabledApzWarningLayer = mNativeLayerRoot->CreateLayer();
+        mDisabledApzWarningLayer->SetRect(IntRect(0, 0, 20, 20));
+        mDisabledApzWarningLayer->SetIsOpaque(true);
         RefPtr<DrawTarget> dt =
             mDisabledApzWarningLayer->NextSurfaceAsDrawTarget(
-                IntRect(0, 0, 20, 20), BackendType::SKIA);
+                BackendType::SKIA);
         dt->FillRect(Rect(0, 0, 20, 20), ColorPattern(Color(1, 1, 0, 1)));
         mDisabledApzWarningLayer->NotifySurfaceReady();
       }
-      mDisabledApzWarningLayer->SetPosition(
-          IntPoint(mRenderBounds.XMost() - 40, mRenderBounds.Y()));
+      mDisabledApzWarningLayer->SetRect(
+          IntRect(mRenderBounds.XMost() - 40, mRenderBounds.Y(), 20, 20));
       mNativeLayerRoot->AppendLayer(mDisabledApzWarningLayer);
 
       mDisabledApzWarning = false;
       SetDebugOverlayWantsNextFrame(true);
     }
   } else {
     mGPUStatsLayer = nullptr;
     mUnusedTransformWarningLayer = nullptr;
@@ -1001,32 +1003,30 @@ void LayerManagerComposite::PlaceNativeL
   }
 }
 
 void LayerManagerComposite::PlaceNativeLayer(
     const IntRect& aRect, bool aOpaque,
     std::deque<RefPtr<NativeLayer>>* aLayersToRecycle,
     IntRegion* aWindowInvalidRegion) {
   RefPtr<NativeLayer> layer;
-  if (aLayersToRecycle->empty() ||
-      aLayersToRecycle->front()->GetSize() != aRect.Size() ||
-      aLayersToRecycle->front()->IsOpaque() != aOpaque) {
-    layer = mNativeLayerRoot->CreateLayer(aRect.Size(), aOpaque);
+  if (aLayersToRecycle->empty()) {
+    layer = mNativeLayerRoot->CreateLayer();
     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);
-    }
   }
-  layer->SetPosition(aRect.TopLeft());
+  IntRect oldRect = layer->GetRect();
+  if (!aRect.IsEqualInterior(oldRect)) {
+    aWindowInvalidRegion->OrWith(oldRect);
+    aWindowInvalidRegion->OrWith(aRect);
+  }
+  layer->SetRect(aRect);
+  layer->SetIsOpaque(aOpaque);
   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/layers/opengl/CompositorOGL.cpp
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -764,18 +764,18 @@ CompositorOGL::RenderTargetForNativeLaye
   }
 
   aNativeLayer->SetSurfaceIsFlipped(true);
   aNativeLayer->SetGLContext(mGLContext);
 
   IntRect layerRect = aNativeLayer->GetRect();
   IntRegion invalidRelativeToLayer =
       aInvalidRegion.MovedBy(-layerRect.TopLeft());
-  Maybe<GLuint> fbo =
-      aNativeLayer->NextSurfaceAsFramebuffer(invalidRelativeToLayer, false);
+  aNativeLayer->InvalidateRegionThroughoutSwapchain(invalidRelativeToLayer);
+  Maybe<GLuint> fbo = aNativeLayer->NextSurfaceAsFramebuffer(false);
   if (!fbo) {
     return nullptr;
   }
 
   invalidRelativeToLayer = aNativeLayer->CurrentSurfaceInvalidRegion();
   aInvalidRegion = invalidRelativeToLayer.MovedBy(layerRect.TopLeft());
 
   RefPtr<CompositingRenderTargetOGL> rt =
--- a/gfx/webrender_bindings/RenderCompositorOGL.cpp
+++ b/gfx/webrender_bindings/RenderCompositorOGL.cpp
@@ -31,16 +31,23 @@ 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;
   }
@@ -63,35 +70,21 @@ RenderCompositorOGL::~RenderCompositorOG
 }
 
 bool RenderCompositorOGL::BeginFrame() {
   if (!mGL->MakeCurrent()) {
     gfxCriticalNote << "Failed to make render context current, can't draw.";
     return false;
   }
 
-  gfx::IntSize bufferSize = GetBufferSize().ToUnknownSize();
-  if (mNativeLayerRoot && !ShouldUseNativeCompositor()) {
-    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({}, bufferSize);
+    gfx::IntRect bounds({}, GetBufferSize().ToUnknownSize());
+    mNativeLayerForEntireWindow->SetRect(bounds);
     Maybe<GLuint> fbo =
-        mNativeLayerForEntireWindow->NextSurfaceAsFramebuffer(bounds, true);
+        mNativeLayerForEntireWindow->NextSurfaceAsFramebuffer(true);
     if (!fbo) {
       return false;
     }
     mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, *fbo);
   } else {
     mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mGL->GetDefaultFramebuffer());
   }
 
@@ -184,17 +177,17 @@ void RenderCompositorOGL::Bind(wr::Nativ
   gfx::IntRect layerRect = layer->GetRect();
   gfx::IntRect dirtyRect(aDirtyRect.origin.x, aDirtyRect.origin.y,
                          aDirtyRect.size.width, aDirtyRect.size.height);
   MOZ_RELEASE_ASSERT(
       dirtyRect.IsEqualInterior(layerRect - layerRect.TopLeft()),
       "We currently do not support partial updates (max_update_rects is set to "
       "0), so we expect the dirty rect to always cover the entire layer.");
 
-  Maybe<GLuint> fbo = layer->NextSurfaceAsFramebuffer(dirtyRect, true);
+  Maybe<GLuint> fbo = layer->NextSurfaceAsFramebuffer(true);
   MOZ_RELEASE_ASSERT(fbo);  // TODO: make fallible
   mCurrentlyBoundNativeLayer = layer;
 
   *aFboId = *fbo;
   *aOffset = wr::DeviceIntPoint{0, 0};
 
   mDrawnPixelCount += layerRect.Area();
 }
@@ -205,19 +198,20 @@ 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(
-      IntSize(aSize.width, aSize.height), aIsOpaque);
+  RefPtr<layers::NativeLayer> layer = mNativeLayerRoot->CreateLayer();
+  layer->SetRect(gfx::IntRect(0, 0, aSize.width, aSize.height));
   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;
@@ -233,22 +227,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->GetSize();
+  gfx::IntSize layerSize = layer->GetRect().Size();
   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->SetPosition(layerRect.TopLeft());
+  layer->SetRect(layerRect);
   layer->SetClipRect(Some(clipRect));
   mAddedLayers.AppendElement(layer);
 
   mAddedPixelCount += layerRect.Area();
   mAddedClippedPixelCount += clipRect.Intersect(layerRect).Area();
 }
 
 }  // namespace wr
--- a/widget/cocoa/nsChildView.h
+++ b/widget/cocoa/nsChildView.h
@@ -700,19 +700,16 @@ class nsChildView final : public nsBaseW
 
   // In BasicLayers mode, this is the CoreAnimation layer that contains the
   // rendering from Gecko. It is a sublayer of mNativeLayerRoot's underlying
   // wrapper layer.
   // Lazily created by EnsureContentLayerForMainThreadPainting().
   // Always null if StaticPrefs::gfx_core_animation_enabled_AtStartup() is false.
   RefPtr<mozilla::layers::NativeLayerCA> mContentLayer;
 
-  // In BasicLayers mode, this is the invalid region of mContentLayer.
-  LayoutDeviceIntRegion mContentLayerInvalidRegion;
-
   mozilla::UniquePtr<mozilla::VibrancyManager> mVibrancyManager;
   RefPtr<mozilla::SwipeTracker> mSwipeTracker;
   mozilla::UniquePtr<mozilla::SwipeEventQueue> mSwipeEventQueue;
 
   // Only used for drawRect-based painting in popups.
   // Always null if StaticPrefs::gfx_core_animation_enabled_AtStartup() is true.
   RefPtr<mozilla::gfx::DrawTarget> mBackingSurface;
 
--- a/widget/cocoa/nsChildView.mm
+++ b/widget/cocoa/nsChildView.mm
@@ -1289,17 +1289,17 @@ void nsChildView::Invalidate(const Layou
 
   if (!mView || !mVisible) return;
 
   NS_ASSERTION(GetLayerManager()->GetBackendType() != LayersBackend::LAYERS_CLIENT,
                "Shouldn't need to invalidate with accelerated OMTC layers!");
 
   if (StaticPrefs::gfx_core_animation_enabled_AtStartup()) {
     EnsureContentLayerForMainThreadPainting();
-    mContentLayerInvalidRegion.OrWith(aRect.Intersect(GetBounds()));
+    mContentLayer->InvalidateRegionThroughoutSwapchain(aRect.ToUnknownRect());
     [mView markLayerForDisplay];
   } else {
     [[mView pixelHostingView] setNeedsDisplayInRect:DevPixelsToCocoaPoints(aRect)];
   }
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
@@ -1483,47 +1483,39 @@ 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) {
-    RefPtr<NativeLayer> contentLayer = mNativeLayerRoot->CreateLayer(size, false);
+    // 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();
     mNativeLayerRoot->AppendLayer(contentLayer);
     mContentLayer = contentLayer->AsNativeLayerCA();
-    mContentLayerInvalidRegion = GetBounds();
   }
 }
 
 void nsChildView::PaintWindowInContentLayer() {
   EnsureContentLayerForMainThreadPainting();
+  mContentLayer->SetRect(GetBounds().ToUnknownRect());
   mContentLayer->SetSurfaceIsFlipped(false);
-  RefPtr<DrawTarget> dt = mContentLayer->NextSurfaceAsDrawTarget(
-      mContentLayerInvalidRegion.ToUnknownRegion(), gfx::BackendType::SKIA);
+  RefPtr<DrawTarget> dt = mContentLayer->NextSurfaceAsDrawTarget(gfx::BackendType::SKIA);
   if (!dt) {
     return;
   }
 
   PaintWindowInDrawTarget(
       dt, LayoutDeviceIntRegion::FromUnknownRegion(mContentLayer->CurrentSurfaceInvalidRegion()),
       dt->GetSize());
   mContentLayer->NotifySurfaceReady();
-  mContentLayerInvalidRegion.SetEmpty();
 }
 
 void nsChildView::HandleMainThreadCATransaction() {
   WillPaintWindow();
 
   if (GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_BASIC) {
     // We're in BasicLayers mode, i.e. main thread software compositing.
     // Composite the window into our layer's surface.