mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 05:41:12 +00:00
Bug 1592026 - Move AsyncCATransaction suspension into NativeLayerRootCA. r=jrmuizel
Differential Revision: https://phabricator.services.mozilla.com/D57061 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
a090f2ff17
commit
c96a136f94
@ -41,6 +41,10 @@ class NativeLayerRoot {
|
||||
virtual void RemoveLayer(NativeLayer* aLayer) = 0;
|
||||
virtual void SetLayers(const nsTArray<RefPtr<NativeLayer>>& aLayers) = 0;
|
||||
|
||||
// Publish the layer changes to the screen. Returns whether the commit was
|
||||
// successful.
|
||||
virtual bool CommitToScreen() = 0;
|
||||
|
||||
protected:
|
||||
virtual ~NativeLayerRoot() {}
|
||||
};
|
||||
|
@ -41,13 +41,36 @@ class SurfacePoolHandleCA;
|
||||
// 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.
|
||||
// underlying CoreAnimation layers until CommitToScreen() is called. This
|
||||
// ensures that the modifications happen on the right thread.
|
||||
//
|
||||
// More specifically: During normal operation, screen updates are driven from a
|
||||
// compositing thread. On this thread, the layers are created / destroyed, their
|
||||
// contents are painted, and the result is committed to the screen. However,
|
||||
// there are some scenarios that need to involve the main thread, most notably
|
||||
// window resizing: During a window resize, we still need the drawing part to
|
||||
// happen on the compositing thread, but the modifications to the underlying
|
||||
// CALayers need to happen on the main thread, once compositing is done.
|
||||
class NativeLayerRootCA : public NativeLayerRoot {
|
||||
public:
|
||||
static already_AddRefed<NativeLayerRootCA> CreateForCALayer(CALayer* aLayer);
|
||||
|
||||
// Can be called on any thread at any point. Returns whether comitting was
|
||||
// successful. Will return false if called off the main thread while
|
||||
// off-main-thread commits are suspended.
|
||||
bool CommitToScreen() override;
|
||||
|
||||
// Enters a mode during which CommitToScreen(), when called on a non-main
|
||||
// thread, will not apply any updates to the CALayer tree.
|
||||
void SuspendOffMainThreadCommits();
|
||||
|
||||
// Exits the mode entered by SuspendOffMainThreadCommits().
|
||||
// Returns true if the last CommitToScreen() was canceled due to suspension,
|
||||
// indicating that another call to CommitToScreen() is needed.
|
||||
bool UnsuspendOffMainThreadCommits();
|
||||
|
||||
bool AreOffMainThreadCommitsSuspended();
|
||||
|
||||
// Overridden methods
|
||||
already_AddRefed<NativeLayer> CreateLayer(
|
||||
const gfx::IntSize& aSize, bool aIsOpaque,
|
||||
@ -59,9 +82,6 @@ class NativeLayerRootCA : public NativeLayerRoot {
|
||||
void SetBackingScale(float aBackingScale);
|
||||
float BackingScale();
|
||||
|
||||
// Must be called within a current CATransaction on the transaction's thread.
|
||||
void ApplyChanges();
|
||||
|
||||
protected:
|
||||
explicit NativeLayerRootCA(CALayer* aLayer);
|
||||
~NativeLayerRootCA() override;
|
||||
@ -71,6 +91,18 @@ class NativeLayerRootCA : public NativeLayerRoot {
|
||||
CALayer* mRootCALayer = nullptr; // strong
|
||||
float mBackingScale = 1.0f;
|
||||
bool mMutated = false;
|
||||
|
||||
// While mOffMainThreadCommitsSuspended is true, no commits
|
||||
// should happen on a non-main thread, because they might race with
|
||||
// main-thread driven updates such as window shape changes, and cause
|
||||
// glitches.
|
||||
bool mOffMainThreadCommitsSuspended = false;
|
||||
|
||||
// Set to true if CommitToScreen() was aborted because of commit suspension.
|
||||
// Set to false when CommitToScreen() completes successfully. When true,
|
||||
// indicates that CommitToScreen() needs to be called at the next available
|
||||
// opportunity.
|
||||
bool mCommitPending = false;
|
||||
};
|
||||
|
||||
// NativeLayerCA wraps a CALayer and lets you draw to it. It ensures that only
|
||||
|
@ -5,8 +5,9 @@
|
||||
|
||||
#include "mozilla/layers/NativeLayerCA.h"
|
||||
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
#import <AppKit/NSAnimationContext.h>
|
||||
#import <AppKit/NSColor.h>
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
@ -123,10 +124,32 @@ float NativeLayerRootCA::BackingScale() {
|
||||
return mBackingScale;
|
||||
}
|
||||
|
||||
// Must be called within a current CATransaction on the transaction's thread.
|
||||
void NativeLayerRootCA::ApplyChanges() {
|
||||
void NativeLayerRootCA::SuspendOffMainThreadCommits() {
|
||||
MutexAutoLock lock(mMutex);
|
||||
mOffMainThreadCommitsSuspended = true;
|
||||
}
|
||||
|
||||
bool NativeLayerRootCA::UnsuspendOffMainThreadCommits() {
|
||||
MutexAutoLock lock(mMutex);
|
||||
mOffMainThreadCommitsSuspended = false;
|
||||
return mCommitPending;
|
||||
}
|
||||
|
||||
bool NativeLayerRootCA::AreOffMainThreadCommitsSuspended() {
|
||||
MutexAutoLock lock(mMutex);
|
||||
return mOffMainThreadCommitsSuspended;
|
||||
}
|
||||
|
||||
bool NativeLayerRootCA::CommitToScreen() {
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
if (!NS_IsMainThread() && mOffMainThreadCommitsSuspended) {
|
||||
mCommitPending = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Force a CoreAnimation layer tree update from this thread.
|
||||
[NSAnimationContext beginGrouping];
|
||||
[CATransaction setDisableActions:YES];
|
||||
|
||||
// Call ApplyChanges on our sublayers first, and then update the root layer's
|
||||
@ -144,6 +167,11 @@ void NativeLayerRootCA::ApplyChanges() {
|
||||
mRootCALayer.sublayers = sublayers;
|
||||
mMutated = false;
|
||||
}
|
||||
[NSAnimationContext endGrouping];
|
||||
|
||||
mCommitPending = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
NativeLayerCA::NativeLayerCA(const IntSize& aSize, bool aIsOpaque,
|
||||
|
@ -593,22 +593,6 @@ class nsChildView final : public nsBaseWidget {
|
||||
RefPtr<mozilla::SwipeTracker> mSwipeTracker;
|
||||
mozilla::UniquePtr<mozilla::SwipeEventQueue> mSwipeEventQueue;
|
||||
|
||||
// Coordinates the triggering of CoreAnimation transactions between the main
|
||||
// thread and the compositor thread in order to avoid glitches during window
|
||||
// resizing and window focus changes.
|
||||
struct WidgetCompositingState {
|
||||
// While mAsyncCATransactionsSuspended is true, no CoreAnimation transaction
|
||||
// should be triggered on a non-main thread, because they might race with
|
||||
// main-thread driven updates such as window shape changes, and cause glitches.
|
||||
bool mAsyncCATransactionsSuspended = false;
|
||||
|
||||
// Set to true if mNativeLayerRoot->ApplyChanges() needs to be called at the
|
||||
// next available opportunity. Set to false whenever ApplyChanges does get
|
||||
// called.
|
||||
bool mNativeLayerChangesPending = false;
|
||||
};
|
||||
mozilla::DataMutex<WidgetCompositingState> mCompositingState;
|
||||
|
||||
RefPtr<mozilla::CancelableRunnable> mUnsuspendAsyncCATransactionsRunnable;
|
||||
|
||||
// This flag is only used when APZ is off. It indicates that the current pan
|
||||
|
@ -236,7 +236,6 @@ nsChildView::nsChildView()
|
||||
mDrawing(false),
|
||||
mIsDispatchPaint(false),
|
||||
mPluginFocused{false},
|
||||
mCompositingState("nsChildView::mCompositingState"),
|
||||
mCurrentPanGestureBelongsToSwipe{false} {}
|
||||
|
||||
nsChildView::~nsChildView() {
|
||||
@ -849,13 +848,12 @@ void nsChildView::SuspendAsyncCATransactions() {
|
||||
// accidentally stay suspended indefinitely.
|
||||
[mView markLayerForDisplay];
|
||||
|
||||
auto compositingState = mCompositingState.Lock();
|
||||
compositingState->mAsyncCATransactionsSuspended = true;
|
||||
mNativeLayerRoot->SuspendOffMainThreadCommits();
|
||||
}
|
||||
|
||||
void nsChildView::MaybeScheduleUnsuspendAsyncCATransactions() {
|
||||
auto compositingState = mCompositingState.Lock();
|
||||
if (compositingState->mAsyncCATransactionsSuspended && !mUnsuspendAsyncCATransactionsRunnable) {
|
||||
if (mNativeLayerRoot->AreOffMainThreadCommitsSuspended() &&
|
||||
!mUnsuspendAsyncCATransactionsRunnable) {
|
||||
mUnsuspendAsyncCATransactionsRunnable =
|
||||
NewCancelableRunnableMethod("nsChildView::MaybeScheduleUnsuspendAsyncCATransactions", this,
|
||||
&nsChildView::UnsuspendAsyncCATransactions);
|
||||
@ -866,14 +864,12 @@ void nsChildView::MaybeScheduleUnsuspendAsyncCATransactions() {
|
||||
void nsChildView::UnsuspendAsyncCATransactions() {
|
||||
mUnsuspendAsyncCATransactionsRunnable = nullptr;
|
||||
|
||||
auto compositingState = mCompositingState.Lock();
|
||||
compositingState->mAsyncCATransactionsSuspended = false;
|
||||
if (compositingState->mNativeLayerChangesPending) {
|
||||
// We need to call mNativeLayerRoot->ApplyChanges() at the next available
|
||||
// opportunity, and it needs to happen during a CoreAnimation transaction.
|
||||
if (mNativeLayerRoot->UnsuspendOffMainThreadCommits()) {
|
||||
// We need to call mNativeLayerRoot->CommitToScreen() at the next available
|
||||
// opportunity.
|
||||
// The easiest way to handle this request is to mark the layer as needing
|
||||
// display, because this will schedule a main thread CATransaction, during
|
||||
// which HandleMainThreadCATransaction will call ApplyChanges().
|
||||
// which HandleMainThreadCATransaction will call CommitToScreen().
|
||||
[mView markLayerForDisplay];
|
||||
}
|
||||
}
|
||||
@ -1378,11 +1374,7 @@ void nsChildView::HandleMainThreadCATransaction() {
|
||||
// Apply the changes inside mNativeLayerRoot to the underlying CALayers. Now is a
|
||||
// good time to call this because we know we're currently inside a main thread
|
||||
// CATransaction.
|
||||
{
|
||||
auto compositingState = mCompositingState.Lock();
|
||||
mNativeLayerRoot->ApplyChanges();
|
||||
compositingState->mNativeLayerChangesPending = false;
|
||||
}
|
||||
mNativeLayerRoot->CommitToScreen();
|
||||
|
||||
MaybeScheduleUnsuspendAsyncCATransactions();
|
||||
}
|
||||
@ -1717,20 +1709,8 @@ bool nsChildView::PreRender(WidgetRenderingContext* aContext) {
|
||||
}
|
||||
|
||||
void nsChildView::PostRender(WidgetRenderingContext* aContext) {
|
||||
{ // scope for lock
|
||||
auto compositingState = mCompositingState.Lock();
|
||||
if (compositingState->mAsyncCATransactionsSuspended) {
|
||||
// We should not trigger a CATransactions on this thread. Instead, let the
|
||||
// main thread take care of calling ApplyChanges at an appropriate time.
|
||||
compositingState->mNativeLayerChangesPending = true;
|
||||
} else {
|
||||
// Force a CoreAnimation layer tree update from this thread.
|
||||
[NSAnimationContext beginGrouping];
|
||||
mNativeLayerRoot->ApplyChanges();
|
||||
compositingState->mNativeLayerChangesPending = false;
|
||||
[NSAnimationContext endGrouping];
|
||||
}
|
||||
}
|
||||
mNativeLayerRoot->CommitToScreen();
|
||||
|
||||
mViewTearDownLock.Unlock();
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user