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
This commit is contained in:
Markus Stange 2019-08-19 22:54:26 +00:00
parent c0250f29fb
commit 32ec0f3341
3 changed files with 136 additions and 35 deletions

View File

@ -63,6 +63,10 @@ class NativeLayer {
virtual void SetRect(const gfx::IntRect& aRect) = 0; virtual void SetRect(const gfx::IntRect& aRect) = 0;
virtual gfx::IntRect GetRect() = 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: protected:
virtual ~NativeLayer() {} virtual ~NativeLayer() {}
}; };

View File

@ -150,6 +150,12 @@ class NativeLayerCA : public NativeLayer {
void SetSurfaceIsFlipped(bool aIsFlipped); void SetSurfaceIsFlipped(bool aIsFlipped);
bool SurfaceIsFlipped(); 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: protected:
friend class NativeLayerRootCA; friend class NativeLayerRootCA;
@ -157,7 +163,7 @@ class NativeLayerCA : public NativeLayer {
~NativeLayerCA() override; ~NativeLayerCA() override;
// To be called by NativeLayerRootCA: // To be called by NativeLayerRootCA:
CALayer* UnderlyingCALayer() { return mCALayer; } CALayer* UnderlyingCALayer() { return mWrappingCALayer; }
void ApplyChanges(); void ApplyChanges();
void SetBackingScale(float aBackingScale); void SetBackingScale(float aBackingScale);
@ -169,6 +175,8 @@ class NativeLayerCA : public NativeLayer {
std::vector<SurfaceWithInvalidRegion> RemoveExcessUnusedSurfaces( std::vector<SurfaceWithInvalidRegion> RemoveExcessUnusedSurfaces(
const MutexAutoLock&); const MutexAutoLock&);
void PlaceContentLayers(const MutexAutoLock&, const gfx::IntRegion& aRegion,
bool aOpaque, std::deque<CALayer*>* aLayersToRecycle);
// Controls access to all fields of this class. // Controls access to all fields of this class.
Mutex mMutex; Mutex mMutex;
@ -240,14 +248,18 @@ class NativeLayerCA : public NativeLayer {
// mSurfaces.back() is the one we submitted most recently. // mSurfaces.back() is the one we submitted most recently.
std::deque<SurfaceWithInvalidRegion> mSurfaces; std::deque<SurfaceWithInvalidRegion> mSurfaces;
gfx::IntRect mRect; gfx::IntPoint mPosition;
gfx::IntSize mSize;
gfx::IntRegion mOpaqueRegion; // coordinates relative to mPosition
// Lazily initialized by first call to ApplyChanges. // Lazily initialized by first call to ApplyChanges.
CALayer* mCALayer = nullptr; // strong CALayer* mWrappingCALayer = nullptr; // strong
std::deque<CALayer*> mContentCALayers; // strong
float mBackingScale = 1.0f; float mBackingScale = 1.0f;
bool mSurfaceIsFlipped = false; bool mSurfaceIsFlipped = false;
bool mMutated = false; bool mMutatedPosition = false;
bool mMutatedGeometry = false;
}; };
} // namespace layers } // namespace layers

View File

@ -11,6 +11,10 @@
#include <utility> #include <utility>
#include <algorithm> #include <algorithm>
@interface CALayer (PrivateSetContentsOpaque)
- (void)setContentsOpaque:(BOOL)opaque;
@end
namespace mozilla { namespace mozilla {
namespace layers { namespace layers {
@ -116,7 +120,10 @@ NativeLayerCA::~NativeLayerCA() {
IOSurfaceDecrementUseCount(mReadySurface->mSurface.get()); IOSurfaceDecrementUseCount(mReadySurface->mSurface.get());
} }
[mCALayer release]; for (CALayer* contentLayer : mContentCALayers) {
[contentLayer release];
}
[mWrappingCALayer release];
} }
void NativeLayerCA::SetSurfaceRegistry(RefPtr<IOSurfaceRegistry> aSurfaceRegistry) { void NativeLayerCA::SetSurfaceRegistry(RefPtr<IOSurfaceRegistry> aSurfaceRegistry) {
@ -156,8 +163,10 @@ RefPtr<IOSurfaceRegistry> NativeLayerCA::GetSurfaceRegistry() {
void NativeLayerCA::SetSurfaceIsFlipped(bool aIsFlipped) { void NativeLayerCA::SetSurfaceIsFlipped(bool aIsFlipped) {
MutexAutoLock lock(mMutex); MutexAutoLock lock(mMutex);
mSurfaceIsFlipped = aIsFlipped; if (aIsFlipped != mSurfaceIsFlipped) {
mMutated = true; mSurfaceIsFlipped = aIsFlipped;
mMutatedGeometry = true;
}
} }
bool NativeLayerCA::SurfaceIsFlipped() { bool NativeLayerCA::SurfaceIsFlipped() {
@ -169,20 +178,42 @@ bool NativeLayerCA::SurfaceIsFlipped() {
void NativeLayerCA::SetRect(const IntRect& aRect) { void NativeLayerCA::SetRect(const IntRect& aRect) {
MutexAutoLock lock(mMutex); MutexAutoLock lock(mMutex);
mRect = aRect; if (aRect.TopLeft() != mPosition) {
mMutated = true; mPosition = aRect.TopLeft();
mMutatedPosition = true;
}
if (aRect.Size() != mSize) {
mSize = aRect.Size();
mMutatedGeometry = true;
}
} }
IntRect NativeLayerCA::GetRect() { IntRect NativeLayerCA::GetRect() {
MutexAutoLock lock(mMutex); MutexAutoLock lock(mMutex);
return mRect; return IntRect(mPosition, mSize);
} }
void NativeLayerCA::SetBackingScale(float aBackingScale) { void NativeLayerCA::SetBackingScale(float aBackingScale) {
MutexAutoLock lock(mMutex); MutexAutoLock lock(mMutex);
mBackingScale = aBackingScale; if (aBackingScale != mBackingScale) {
mMutated = true; 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() { IntRegion NativeLayerCA::CurrentSurfaceInvalidRegion() {
@ -199,7 +230,7 @@ void NativeLayerCA::InvalidateRegionThroughoutSwapchain(const IntRegion& aRegion
MutexAutoLock lock(mMutex); MutexAutoLock lock(mMutex);
IntRegion r = aRegion; IntRegion r = aRegion;
r.AndWith(IntRect(IntPoint(0, 0), mRect.Size())); r.AndWith(IntRect(IntPoint(0, 0), mSize));
if (mInProgressSurface) { if (mInProgressSurface) {
mInProgressSurface->mInvalidRegion.OrWith(r); mInProgressSurface->mInvalidRegion.OrWith(r);
} }
@ -214,7 +245,7 @@ void NativeLayerCA::InvalidateRegionThroughoutSwapchain(const IntRegion& aRegion
CFTypeRefPtr<IOSurfaceRef> NativeLayerCA::NextSurface() { CFTypeRefPtr<IOSurfaceRef> NativeLayerCA::NextSurface() {
MutexAutoLock lock(mMutex); MutexAutoLock lock(mMutex);
IntSize surfaceSize = mRect.Size(); IntSize surfaceSize = mSize;
if (surfaceSize.IsEmpty()) { if (surfaceSize.IsEmpty()) {
NSLog(@"NextSurface returning nullptr because of invalid surfaceSize (%d, %d).", NSLog(@"NextSurface returning nullptr because of invalid surfaceSize (%d, %d).",
surfaceSize.width, surfaceSize.height); surfaceSize.width, surfaceSize.height);
@ -286,44 +317,98 @@ void NativeLayerCA::NotifySurfaceReady() {
} }
mReadySurface = std::move(mInProgressSurface); mReadySurface = std::move(mInProgressSurface);
mReadySurface->mInvalidRegion = IntRect(); mReadySurface->mInvalidRegion = IntRect();
mMutated = true;
} }
void NativeLayerCA::ApplyChanges() { void NativeLayerCA::ApplyChanges() {
MutexAutoLock lock(mMutex); MutexAutoLock lock(mMutex);
if (!mCALayer) { if (!mWrappingCALayer) {
mCALayer = [[CALayer layer] retain]; mWrappingCALayer = [[CALayer layer] retain];
mCALayer.position = NSZeroPoint; mWrappingCALayer.position = NSZeroPoint;
mCALayer.bounds = NSZeroRect; mWrappingCALayer.bounds = NSZeroRect;
mCALayer.anchorPoint = NSZeroPoint; mWrappingCALayer.anchorPoint = NSZeroPoint;
mCALayer.contentsGravity = kCAGravityTopLeft; mWrappingCALayer.contentsGravity = kCAGravityTopLeft;
} }
if (!mMutated) { if (mMutatedPosition || mMutatedGeometry) {
return; mWrappingCALayer.position =
CGPointMake(mPosition.x / mBackingScale, mPosition.y / mBackingScale);
mMutatedPosition = false;
} }
mCALayer.contentsScale = mBackingScale; if (mMutatedGeometry) {
mCALayer.position = NSMakePoint(mRect.x / mBackingScale, mRect.y / mBackingScale); mWrappingCALayer.bounds =
mCALayer.bounds = NSMakeRect(0, 0, mRect.width / mBackingScale, mRect.height / mBackingScale); 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<CALayer*> layersToRecycle = std::move(mContentCALayers);
PlaceContentLayers(lock, opaqueRegion, true, &layersToRecycle);
PlaceContentLayers(lock, transparentRegion, false, &layersToRecycle);
for (CALayer* unusedLayer : layersToRecycle) {
[unusedLayer release];
}
NSMutableArray<CALayer*>* sublayers =
[NSMutableArray arrayWithCapacity:mContentCALayers.size()];
for (auto layer : mContentCALayers) {
[sublayers addObject:layer];
}
mWrappingCALayer.sublayers = sublayers;
mMutatedGeometry = false;
}
if (mReadySurface) { if (mReadySurface) {
mCALayer.contents = (id)mReadySurface->mSurface.get(); for (CALayer* layer : mContentCALayers) {
layer.contents = (id)mReadySurface->mSurface.get();
}
IOSurfaceDecrementUseCount(mReadySurface->mSurface.get()); IOSurfaceDecrementUseCount(mReadySurface->mSurface.get());
mSurfaces.push_back(*mReadySurface); mSurfaces.push_back(*mReadySurface);
mReadySurface = Nothing(); mReadySurface = Nothing();
} }
}
if (mSurfaceIsFlipped) { void NativeLayerCA::PlaceContentLayers(const MutexAutoLock&, const IntRegion& aRegion, bool aOpaque,
CGFloat height = mCALayer.bounds.size.height; std::deque<CALayer*>* aLayersToRecycle) {
// FIXME: this probably needs adjusting if mCALayer.position is non-zero. for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
mCALayer.affineTransform = CGAffineTransformMake(1.0, 0.0, 0.0, -1.0, 0.0, height); IntRect r = iter.Get();
} else {
mCALayer.affineTransform = CGAffineTransformIdentity; 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. // Called when mMutex is already being held by the current thread.