Bug 1491442 - Call SuspendAsyncCATransactions on window focus changes. r=mattwoodrow
authorMarkus Stange <mstange@themasta.com>
Fri, 16 Aug 2019 01:15:34 +0000
changeset 488415 706ff8a1badcd0c796e151b9e015bbfed0b20ddf
parent 488414 e6e88a4fc6b95571d04db7841603bea7148df02f
child 488416 ae86a277bd72b7a3c9b5e2ae40fcb74c33b796da
push id36443
push userccoroiu@mozilla.com
push dateFri, 16 Aug 2019 09:48:15 +0000
treeherdermozilla-central@5d4cbfe103bb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow
bugs1491442
milestone70.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 1491442 - Call SuspendAsyncCATransactions on window focus changes. r=mattwoodrow Without this, in windows with title bars, such as the bookmark library window, the title bar and the content would update at different times. The title bar paint is done as part of a main thread CoreAnimation transaction. However, by default, we don't get notified of all main thread CA transactions; our only notification mechanism is the updateLayer handler on the PixelHostingView, and that handler is only invoked (the layer is only displayed) if the layer has been marked as needing display. And by default, window focus changes do not mark random views' backing layers as needing display. Usually, what this means is that the window will be painted twice: Once in the main thread transaction, and then another time on the compositor thread once Gecko has noticed a state change and triggered its own composite in response. (Often, Gecko's compositor-side paint will actually happen *before* the main thread paint, because the main thread is often busy with repainting the system menu bar during window focus changes.) Such non-atomic window repaints look glitchy. Calling SuspendAsyncCATransactions will result in a call to updateLayer in the upcoming CoreAnimation transaction and lets us update the entire window in one atomic paint, and it will avoid updating the window early if the compositor thread gets ahead of the main thread. Differential Revision: https://phabricator.services.mozilla.com/D38759
widget/cocoa/nsChildView.h
widget/cocoa/nsChildView.mm
widget/cocoa/nsCocoaWindow.mm
--- a/widget/cocoa/nsChildView.h
+++ b/widget/cocoa/nsChildView.h
@@ -256,16 +256,24 @@ class WidgetRenderingContext;
                          exitFrom:(mozilla::WidgetMouseEvent::ExitFrom)aExitFrom;
 
 - (void)updateGLContext;
 - (void)_surfaceNeedsUpdate:(NSNotification*)notification;
 
 - (bool)preRender:(NSOpenGLContext*)aGLContext;
 - (void)postRender:(NSOpenGLContext*)aGLContext;
 
+// Call this during operations that will likely trigger a main thread
+// CoreAnimation paint of the window, during which Gecko should do its own
+// painting and present the results atomically with that main thread transaction.
+// This method will suspend off-thread window updates so that the upcoming paint
+// can be atomic, and mark the layer as needing display so that
+// HandleMainThreadCATransaction gets called and Gecko gets a chance to paint.
+- (void)ensureNextCompositeIsAtomicWithMainThreadPaint;
+
 - (NSView*)vibrancyViewsContainer;
 - (NSView*)nonDraggableViewsContainer;
 - (NSView*)pixelHostingView;
 
 - (BOOL)isCoveringTitlebar;
 
 - (void)viewWillStartLiveResize;
 - (void)viewDidEndLiveResize;
--- a/widget/cocoa/nsChildView.mm
+++ b/widget/cocoa/nsChildView.mm
@@ -3725,16 +3725,23 @@ NSEvent* gLastDragMouseDownEvent = nil;
   if (StaticPrefs::gfx_core_animation_enabled_AtStartup()) {
     // This call will cause updateRootCALayer to be called during the upcoming
     // main thread CoreAnimation transaction. It will also trigger a transaction
     // if no transaction is currently pending.
     [[mPixelHostingView layer] setNeedsDisplay];
   }
 }
 
+- (void)ensureNextCompositeIsAtomicWithMainThreadPaint {
+  MOZ_RELEASE_ASSERT(NS_IsMainThread());
+  if (mGeckoChild) {
+    mGeckoChild->SuspendAsyncCATransactions();
+  }
+}
+
 - (void)updateRootCALayer {
   if (NS_IsMainThread() && mGeckoChild) {
     mGeckoChild->HandleMainThreadCATransaction();
   }
 }
 
 - (CALayer*)rootCALayer {
   return [mPixelHostingView layer];
--- a/widget/cocoa/nsCocoaWindow.mm
+++ b/widget/cocoa/nsCocoaWindow.mm
@@ -3304,16 +3304,17 @@ static const NSString* kStateWantsTitleD
     [mTitlebarGradientView removeFromSuperview];
     [mTitlebarGradientView release];
     mTitlebarGradientView = nil;
   }
 }
 
 - (void)windowMainStateChanged {
   [self setTitlebarNeedsDisplay];
+  [[self mainChildView] ensureNextCompositeIsAtomicWithMainThreadPaint];
 }
 
 - (void)setTitlebarNeedsDisplay {
   [mTitlebarGradientView setNeedsDisplay:YES];
 }
 
 - (NSRect)titlebarRect {
   CGFloat titlebarHeight = [self titlebarHeight];