Bug 1491442 - When gfx.core-animation.enabled is true, use CoreAnimation for all windows and create an empty layer. r=mattwoodrow
authorMarkus Stange <mstange@themasta.com>
Fri, 16 Aug 2019 01:13:19 +0000
changeset 488408 372c720b6ce81374cfca793c8ee4e0c9cade03b2
parent 488407 e89c3a6b2cd8eb8c8c328b86433765217ee7684e
child 488409 cf2547ffbfd4d065c691e00255ce8fdda5396cb3
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 - When gfx.core-animation.enabled is true, use CoreAnimation for all windows and create an empty layer. r=mattwoodrow This makes mPixelHostingView layer-backed, and that layer will be empty. This patch also causes all windows (including context menus, tooltips, arrow panels etc.) to use CoreAnimation layers for the window frame. This is achieved by calling setWantsLayer:YES on every window's content view. After this changeset, all windows will still be empty. Differential Revision: https://phabricator.services.mozilla.com/D38755
widget/cocoa/nsChildView.h
widget/cocoa/nsChildView.mm
widget/cocoa/nsCocoaWindow.mm
--- a/widget/cocoa/nsChildView.h
+++ b/widget/cocoa/nsChildView.h
@@ -212,16 +212,19 @@ class WidgetRenderingContext;
   // Subviews of self, which act as container views for vibrancy views and
   // non-draggable views.
   NSView* mVibrancyViewsContainer;      // [STRONG]
   NSView* mNonDraggableViewsContainer;  // [STRONG]
 
   // The view that does our drawing. Always non-null.
   // This is a subview of self so that it can be ordered on top of mVibrancyViewsContainer.
   // Drawing in this view can be performed in different ways:
+  // If StaticPrefs::gfx_core_animation_enabled_AtStartup() is true, mPixelHostingView
+  // will be layer-backed and all Gecko rendering will be performed into sublayers of
+  // that view's layer.
   // If StaticPrefs::gfx_core_animation_enabled_AtStartup() is false, there are two cases:
   // If mUsingOMTCompositor is false, drawing is performed on the main thread
   // inside the view's drawRect handler. If mUsingOMTCompositor is true,
   // mGLContext will be non-null and will be associated with mPixelHostingView,
   // and rendering will be performed on the compositor thread into mGLContext's
   // primary framebuffer.
   PixelHostingView* mPixelHostingView;
 
@@ -452,16 +455,17 @@ class nsChildView final : public nsBaseW
   virtual bool DispatchWindowEvent(mozilla::WidgetGUIEvent& event);
 
   void WillPaintWindow();
   bool PaintWindow(LayoutDeviceIntRegion aRegion);
   bool PaintWindowInDrawTarget(mozilla::gfx::DrawTarget* aDT, const LayoutDeviceIntRegion& aRegion,
                                const mozilla::gfx::IntSize& aSurfaceSize);
   bool PaintWindowInContext(CGContextRef aContext, const LayoutDeviceIntRegion& aRegion,
                             mozilla::gfx::IntSize aSurfaceSize);
+  void HandleMainThreadCATransaction();
 
 #ifdef ACCESSIBILITY
   already_AddRefed<mozilla::a11y::Accessible> GetDocumentAccessible();
 #endif
 
   virtual void CreateCompositor() override;
   virtual void PrepareWindowEffects() override;
   virtual void CleanupWindowEffects() override;
--- a/widget/cocoa/nsChildView.mm
+++ b/widget/cocoa/nsChildView.mm
@@ -163,16 +163,17 @@ static bool sIsTabletPointerActivated = 
 
 static uint32_t sUniqueKeyEventId = 0;
 
 static NSMutableDictionary* sNativeKeyEventsMap = [NSMutableDictionary dictionary];
 
 // The view that will do our drawing or host our NSOpenGLContext or Core Animation layer.
 @interface PixelHostingView : NSView {
 }
+
 @end
 
 @interface ChildView (Private)
 
 // sets up our view, attaching it to its owning gecko view
 - (id)initWithFrame:(NSRect)inFrame geckoChild:(nsChildView*)inChild;
 
 // set up a gecko mouse event based on a cocoa mouse event
@@ -188,16 +189,20 @@ static NSMutableDictionary* sNativeKeyEv
 - (LayoutDeviceIntRegion)nativeDirtyRegionWithBoundingRect:(NSRect)aRect;
 
 - (BOOL)hasRoundedBottomCorners;
 - (CGFloat)cornerRadius;
 - (void)clearCorners;
 
 - (void)setGLOpaque:(BOOL)aOpaque;
 
+- (void)markLayerForDisplay;
+- (CALayer*)rootCALayer;
+- (void)updateRootCALayer;
+
 // Overlay drawing functions for traditional CGContext drawing
 - (void)drawTitleString;
 - (void)maskTopCornersInContext:(CGContextRef)aContext;
 
 #ifdef ACCESSIBILITY
 - (id<mozAccessible>)accessible;
 #endif
 
@@ -1388,16 +1393,18 @@ bool nsChildView::PaintWindowInContext(C
   CGDataProviderRelease(provider);
   CGContextRestoreGState(aContext);
 
   mBackingSurface->ReleaseBits(data);
 
   return painted;
 }
 
+void nsChildView::HandleMainThreadCATransaction() {}
+
 #pragma mark -
 
 void nsChildView::ReportMoveEvent() { NotifyWindowMoved(mBounds.x, mBounds.y); }
 
 void nsChildView::ReportSizeEvent() {
   if (mWidgetListener) mWidgetListener->WindowResized(this, mBounds.width, mBounds.height);
 }
 
@@ -3414,16 +3421,18 @@ NSEvent* gLastDragMouseDownEvent = nil;
   if (!NS_IsMainThread()) {
     // In the presence of CoreAnimation, this method can sometimes be called on
     // a non-main thread. Ignore those calls because Gecko can only react to
     // them on the main thread.
     return;
   }
 
   if (StaticPrefs::gfx_core_animation_enabled_AtStartup()) {
+    // If we use CALayers for display, we will call WillPaintWindow during
+    // nsChildView::HandleMainThreadCATransaction, and not here.
     return;
   }
 
   nsAutoRetainCocoaObject kungFuDeathGrip(self);
 
   if (mGeckoChild) {
     // The OS normally *will* draw our NSWindow, no matter what we do here.
     // But Gecko can delete our parent widget(s) (along with mGeckoChild)
@@ -3455,16 +3464,36 @@ NSEvent* gLastDragMouseDownEvent = nil;
       }
     }
 
     mGeckoChild->WillPaintWindow();
   }
   [super viewWillDraw];
 }
 
+- (void)markLayerForDisplay {
+  MOZ_RELEASE_ASSERT(NS_IsMainThread());
+  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)updateRootCALayer {
+  if (NS_IsMainThread() && mGeckoChild) {
+    mGeckoChild->HandleMainThreadCATransaction();
+  }
+}
+
+- (CALayer*)rootCALayer {
+  return [mPixelHostingView layer];
+}
+
 // If we've just created a non-native context menu, we need to mark it as
 // such and let the OS (and other programs) know when it opens and closes
 // (this is how the OS knows to close other programs' context menus when
 // ours open).  We send the initial notification here, but others are sent
 // in nsCocoaWindow::Show().
 - (void)maybeInitContextMenuTracking {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
@@ -5919,31 +5948,52 @@ nsresult nsChildView::GetSelectionAsPlai
 + (NSMutableDictionary*)sNativeKeyEventsMap {
   return sNativeKeyEventsMap;
 }
 
 @end
 
 @implementation PixelHostingView
 
+- (id)initWithFrame:(NSRect)aRect {
+  self = [super initWithFrame:aRect];
+
+  if (StaticPrefs::gfx_core_animation_enabled_AtStartup()) {
+    self.wantsLayer = YES;
+    self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawDuringViewResize;
+  }
+
+  return self;
+}
+
 - (BOOL)isFlipped {
   return YES;
 }
 
 - (NSView*)hitTest:(NSPoint)aPoint {
   return nil;
 }
 
 - (void)drawRect:(NSRect)aRect {
   if (StaticPrefs::gfx_core_animation_enabled_AtStartup()) {
+    NS_WARNING("Unexpected call to drawRect: This view returns YES from wantsUpdateLayer, so "
+               "drawRect should not be called.");
     return;
   }
   [(ChildView*)[self superview] doDrawRect:aRect];
 }
 
+- (BOOL)wantsUpdateLayer {
+  return YES;
+}
+
+- (void)updateLayer {
+  [(ChildView*)[self superview] updateRootCALayer];
+}
+
 - (BOOL)wantsBestResolutionOpenGLSurface {
   return nsCocoaUtils::HiDPIEnabled() ? YES : NO;
 }
 
 @end
 
 #pragma mark -
 
--- a/widget/cocoa/nsCocoaWindow.mm
+++ b/widget/cocoa/nsCocoaWindow.mm
@@ -480,16 +480,22 @@ nsresult nsCocoaWindow::CreateNativeWind
     // Make sure that regular windows are opaque from the start, so that
     // nsChildView::WidgetTypeSupportsAcceleration returns true for them.
     [mWindow setOpaque:YES];
   }
 
   [mWindow setContentMinSize:NSMakeSize(60, 60)];
   [mWindow disableCursorRects];
 
+  if (StaticPrefs::gfx_core_animation_enabled_AtStartup()) {
+    // Make the window use CoreAnimation from the start, so that we don't
+    // switch from a non-CA window to a CA-window in the middle.
+    [[mWindow contentView] setWantsLayer:YES];
+  }
+
   // Make sure the window starts out not draggable by the background.
   // We will turn it on as necessary.
   [mWindow setMovableByWindowBackground:NO];
 
   [[WindowDataMap sharedWindowDataMap] ensureDataForWindow:mWindow];
   mWindowMadeHere = true;
 
   return NS_OK;
@@ -2819,16 +2825,19 @@ static NSImage* GetMenuMaskImage() {
     if ([effectView respondsToSelector:@selector(setMaskImage:)]) {
       [effectView setMaskImage:GetMenuMaskImage()];
     }
     [self swapOutChildViewWrapper:effectView];
     [effectView release];
   } else if (mUseMenuStyle && !aValue) {
     // Turn off rounded corner masking.
     NSView* wrapper = [[NSView alloc] initWithFrame:NSZeroRect];
+    if (StaticPrefs::gfx_core_animation_enabled_AtStartup()) {
+      [wrapper setWantsLayer:YES];
+    }
     [self swapOutChildViewWrapper:wrapper];
     [wrapper release];
   }
   mUseMenuStyle = aValue;
 }
 
 - (NSTouchBar*)makeTouchBar {
   mTouchBar = [[nsTouchBar alloc] init];