From 32ec0f33417a39d9344436d9e122fd0fec0f288d Mon Sep 17 00:00:00 2001 From: Markus Stange Date: Mon, 19 Aug 2019 22:54:26 +0000 Subject: [PATCH] Bug 1491448 - Add NativeLayer::SetOpaqueRegion and implement it for NativeLayerCA by assembling opaque and transparent sublayers to cover the regions. r=jrmuizel Differential Revision: https://phabricator.services.mozilla.com/D40553 --HG-- extra : moz-landing-system : lando --- gfx/layers/NativeLayer.h | 4 + gfx/layers/NativeLayerCA.h | 20 ++++- gfx/layers/NativeLayerCA.mm | 147 ++++++++++++++++++++++++++++-------- 3 files changed, 136 insertions(+), 35 deletions(-) diff --git a/gfx/layers/NativeLayer.h b/gfx/layers/NativeLayer.h index 3611ae74f2e0..85426648d57e 100644 --- a/gfx/layers/NativeLayer.h +++ b/gfx/layers/NativeLayer.h @@ -63,6 +63,10 @@ class NativeLayer { virtual void SetRect(const gfx::IntRect& aRect) = 0; virtual gfx::IntRect GetRect() = 0; + // Define which parts of the layer are opaque.. + virtual void SetOpaqueRegion(const gfx::IntRegion& aRegion) = 0; + virtual gfx::IntRegion OpaqueRegion() = 0; + protected: virtual ~NativeLayer() {} }; diff --git a/gfx/layers/NativeLayerCA.h b/gfx/layers/NativeLayerCA.h index 8a61648f5e32..c7da163dcd91 100644 --- a/gfx/layers/NativeLayerCA.h +++ b/gfx/layers/NativeLayerCA.h @@ -150,6 +150,12 @@ class NativeLayerCA : public NativeLayer { void SetSurfaceIsFlipped(bool aIsFlipped); bool SurfaceIsFlipped(); + // Set an opaque region on the layer. Internally, this causes the creation + // of opaque and transparent sublayers to cover the regions. + // The coordinates in aRegion are relative to mPosition. + void SetOpaqueRegion(const gfx::IntRegion& aRegion) override; + gfx::IntRegion OpaqueRegion() override; + protected: friend class NativeLayerRootCA; @@ -157,7 +163,7 @@ class NativeLayerCA : public NativeLayer { ~NativeLayerCA() override; // To be called by NativeLayerRootCA: - CALayer* UnderlyingCALayer() { return mCALayer; } + CALayer* UnderlyingCALayer() { return mWrappingCALayer; } void ApplyChanges(); void SetBackingScale(float aBackingScale); @@ -169,6 +175,8 @@ class NativeLayerCA : public NativeLayer { std::vector RemoveExcessUnusedSurfaces( const MutexAutoLock&); + void PlaceContentLayers(const MutexAutoLock&, const gfx::IntRegion& aRegion, + bool aOpaque, std::deque* aLayersToRecycle); // Controls access to all fields of this class. Mutex mMutex; @@ -240,14 +248,18 @@ class NativeLayerCA : public NativeLayer { // mSurfaces.back() is the one we submitted most recently. std::deque mSurfaces; - gfx::IntRect mRect; + gfx::IntPoint mPosition; + gfx::IntSize mSize; + gfx::IntRegion mOpaqueRegion; // coordinates relative to mPosition // Lazily initialized by first call to ApplyChanges. - CALayer* mCALayer = nullptr; // strong + CALayer* mWrappingCALayer = nullptr; // strong + std::deque mContentCALayers; // strong float mBackingScale = 1.0f; bool mSurfaceIsFlipped = false; - bool mMutated = false; + bool mMutatedPosition = false; + bool mMutatedGeometry = false; }; } // namespace layers diff --git a/gfx/layers/NativeLayerCA.mm b/gfx/layers/NativeLayerCA.mm index 4a9a891469c6..9ef2cc375761 100644 --- a/gfx/layers/NativeLayerCA.mm +++ b/gfx/layers/NativeLayerCA.mm @@ -11,6 +11,10 @@ #include #include +@interface CALayer (PrivateSetContentsOpaque) +- (void)setContentsOpaque:(BOOL)opaque; +@end + namespace mozilla { namespace layers { @@ -116,7 +120,10 @@ NativeLayerCA::~NativeLayerCA() { IOSurfaceDecrementUseCount(mReadySurface->mSurface.get()); } - [mCALayer release]; + for (CALayer* contentLayer : mContentCALayers) { + [contentLayer release]; + } + [mWrappingCALayer release]; } void NativeLayerCA::SetSurfaceRegistry(RefPtr aSurfaceRegistry) { @@ -156,8 +163,10 @@ RefPtr NativeLayerCA::GetSurfaceRegistry() { void NativeLayerCA::SetSurfaceIsFlipped(bool aIsFlipped) { MutexAutoLock lock(mMutex); - mSurfaceIsFlipped = aIsFlipped; - mMutated = true; + if (aIsFlipped != mSurfaceIsFlipped) { + mSurfaceIsFlipped = aIsFlipped; + mMutatedGeometry = true; + } } bool NativeLayerCA::SurfaceIsFlipped() { @@ -169,20 +178,42 @@ bool NativeLayerCA::SurfaceIsFlipped() { void NativeLayerCA::SetRect(const IntRect& aRect) { MutexAutoLock lock(mMutex); - mRect = aRect; - mMutated = true; + if (aRect.TopLeft() != mPosition) { + mPosition = aRect.TopLeft(); + mMutatedPosition = true; + } + if (aRect.Size() != mSize) { + mSize = aRect.Size(); + mMutatedGeometry = true; + } } IntRect NativeLayerCA::GetRect() { MutexAutoLock lock(mMutex); - return mRect; + return IntRect(mPosition, mSize); } void NativeLayerCA::SetBackingScale(float aBackingScale) { MutexAutoLock lock(mMutex); - mBackingScale = aBackingScale; - mMutated = true; + if (aBackingScale != mBackingScale) { + mBackingScale = aBackingScale; + mMutatedGeometry = true; + } +} + +void NativeLayerCA::SetOpaqueRegion(const gfx::IntRegion& aRegion) { + MutexAutoLock lock(mMutex); + + if (aRegion != mOpaqueRegion) { + mOpaqueRegion = aRegion; + mMutatedGeometry = true; + } +} + +gfx::IntRegion NativeLayerCA::OpaqueRegion() { + MutexAutoLock lock(mMutex); + return mOpaqueRegion; } IntRegion NativeLayerCA::CurrentSurfaceInvalidRegion() { @@ -199,7 +230,7 @@ void NativeLayerCA::InvalidateRegionThroughoutSwapchain(const IntRegion& aRegion MutexAutoLock lock(mMutex); IntRegion r = aRegion; - r.AndWith(IntRect(IntPoint(0, 0), mRect.Size())); + r.AndWith(IntRect(IntPoint(0, 0), mSize)); if (mInProgressSurface) { mInProgressSurface->mInvalidRegion.OrWith(r); } @@ -214,7 +245,7 @@ void NativeLayerCA::InvalidateRegionThroughoutSwapchain(const IntRegion& aRegion CFTypeRefPtr NativeLayerCA::NextSurface() { MutexAutoLock lock(mMutex); - IntSize surfaceSize = mRect.Size(); + IntSize surfaceSize = mSize; if (surfaceSize.IsEmpty()) { NSLog(@"NextSurface returning nullptr because of invalid surfaceSize (%d, %d).", surfaceSize.width, surfaceSize.height); @@ -286,44 +317,98 @@ void NativeLayerCA::NotifySurfaceReady() { } mReadySurface = std::move(mInProgressSurface); mReadySurface->mInvalidRegion = IntRect(); - mMutated = true; } void NativeLayerCA::ApplyChanges() { MutexAutoLock lock(mMutex); - if (!mCALayer) { - mCALayer = [[CALayer layer] retain]; - mCALayer.position = NSZeroPoint; - mCALayer.bounds = NSZeroRect; - mCALayer.anchorPoint = NSZeroPoint; - mCALayer.contentsGravity = kCAGravityTopLeft; + if (!mWrappingCALayer) { + mWrappingCALayer = [[CALayer layer] retain]; + mWrappingCALayer.position = NSZeroPoint; + mWrappingCALayer.bounds = NSZeroRect; + mWrappingCALayer.anchorPoint = NSZeroPoint; + mWrappingCALayer.contentsGravity = kCAGravityTopLeft; } - if (!mMutated) { - return; + if (mMutatedPosition || mMutatedGeometry) { + mWrappingCALayer.position = + CGPointMake(mPosition.x / mBackingScale, mPosition.y / mBackingScale); + mMutatedPosition = false; } - mCALayer.contentsScale = mBackingScale; - mCALayer.position = NSMakePoint(mRect.x / mBackingScale, mRect.y / mBackingScale); - mCALayer.bounds = NSMakeRect(0, 0, mRect.width / mBackingScale, mRect.height / mBackingScale); + if (mMutatedGeometry) { + mWrappingCALayer.bounds = + CGRectMake(0, 0, mSize.width / mBackingScale, mSize.height / mBackingScale); + + // Assemble opaque and transparent sublayers to cover the respective regions. + // mContentCALayers has the current sublayers. We will try to re-use layers + // as much as possible. + IntRegion opaqueRegion; + opaqueRegion.And(IntRect(IntPoint(), mSize), mOpaqueRegion); + IntRegion transparentRegion; + transparentRegion.Sub(IntRect(IntPoint(), mSize), opaqueRegion); + std::deque layersToRecycle = std::move(mContentCALayers); + PlaceContentLayers(lock, opaqueRegion, true, &layersToRecycle); + PlaceContentLayers(lock, transparentRegion, false, &layersToRecycle); + for (CALayer* unusedLayer : layersToRecycle) { + [unusedLayer release]; + } + NSMutableArray* sublayers = + [NSMutableArray arrayWithCapacity:mContentCALayers.size()]; + for (auto layer : mContentCALayers) { + [sublayers addObject:layer]; + } + mWrappingCALayer.sublayers = sublayers; + mMutatedGeometry = false; + } if (mReadySurface) { - mCALayer.contents = (id)mReadySurface->mSurface.get(); + for (CALayer* layer : mContentCALayers) { + layer.contents = (id)mReadySurface->mSurface.get(); + } IOSurfaceDecrementUseCount(mReadySurface->mSurface.get()); mSurfaces.push_back(*mReadySurface); mReadySurface = Nothing(); } +} - if (mSurfaceIsFlipped) { - CGFloat height = mCALayer.bounds.size.height; - // FIXME: this probably needs adjusting if mCALayer.position is non-zero. - mCALayer.affineTransform = CGAffineTransformMake(1.0, 0.0, 0.0, -1.0, 0.0, height); - } else { - mCALayer.affineTransform = CGAffineTransformIdentity; +void NativeLayerCA::PlaceContentLayers(const MutexAutoLock&, const IntRegion& aRegion, bool aOpaque, + std::deque* aLayersToRecycle) { + for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) { + IntRect r = iter.Get(); + + CALayer* layer; + if (aLayersToRecycle->empty()) { + layer = [[CALayer layer] retain]; + layer.anchorPoint = NSZeroPoint; + layer.contentsGravity = kCAGravityTopLeft; + } else { + layer = aLayersToRecycle->front(); + aLayersToRecycle->pop_front(); + } + layer.position = CGPointMake(r.x / mBackingScale, r.y / mBackingScale); + layer.bounds = CGRectMake(0, 0, r.width / mBackingScale, r.height / mBackingScale); + layer.contentsScale = mBackingScale; + CGRect unitContentsRect = + CGRectMake(CGFloat(r.x) / mSize.width, CGFloat(r.y) / mSize.height, + CGFloat(r.width) / mSize.width, CGFloat(r.height) / mSize.height); + if (mSurfaceIsFlipped) { + CGFloat height = r.height / mBackingScale; + layer.affineTransform = CGAffineTransformMake(1.0, 0.0, 0.0, -1.0, 0.0, height); + unitContentsRect.origin.y = 1.0 - (unitContentsRect.origin.y + unitContentsRect.size.height); + layer.contentsRect = unitContentsRect; + } else { + layer.affineTransform = CGAffineTransformIdentity; + layer.contentsRect = unitContentsRect; + } + layer.opaque = aOpaque; + if ([layer respondsToSelector:@selector(setContentsOpaque:)]) { + // The opaque property seems to not be enough when using IOSurface contents. + // Additionally, call the private method setContentsOpaque. + [layer setContentsOpaque:aOpaque]; + } + mContentCALayers.push_back(layer); } - - mMutated = false; } // Called when mMutex is already being held by the current thread.