Bug 1580117 - Pause composition for occluded windows on macOS. r=mstange

This pauses composition when a window becomes fully occluded.

This is particularly important for platforms like macOS, where minimized /
hidden windows historically would remain compositing, potentially on every vsync
if the hidden window contained animations. This should lead to a nice power
savings by skipping this unnecessary work.

This change affects both the WebRender and legacy compositor paths.

As bug 1580117 is believed to be a macOS specific issue, this is currently
implemented for macOS only, but could in the future be generalized to other
platforms as well (likely using size mode changes since other platforms do not
notify on occlusion change).

Differential Revision: https://phabricator.services.mozilla.com/D85954
This commit is contained in:
J. Ryan Stinnett 2020-08-31 13:54:24 +00:00
Родитель 6db78719c3
Коммит 643a99c8fa
2 изменённых файлов: 78 добавлений и 0 удалений

Просмотреть файл

@ -340,6 +340,9 @@ class nsCocoaWindow final : public nsBaseWidget, public nsPIWidgetCocoa {
bool InFullScreenMode() const { return mInFullScreenMode; }
void PauseCompositor();
void ResumeCompositor();
protected:
virtual ~nsCocoaWindow();

Просмотреть файл

@ -34,6 +34,8 @@
#include "nsIScreenManager.h"
#include "nsIWidgetListener.h"
#include "VibrancyManager.h"
#include "nsPresContext.h"
#include "nsDocShell.h"
#include "gfxPlatform.h"
#include "qcms.h"
@ -44,6 +46,7 @@
#include "mozilla/StaticPrefs_gfx.h"
#include "mozilla/StaticPrefs_widget.h"
#include "mozilla/PresShell.h"
#include "mozilla/layers/CompositorBridgeChild.h"
#include <algorithm>
namespace mozilla {
@ -2090,6 +2093,12 @@ void nsCocoaWindow::DispatchOcclusionEvent() {
if (mWidgetListener) {
mWidgetListener->OcclusionStateChanged(mIsFullyOccluded);
}
if (mIsFullyOccluded) {
PauseCompositor();
} else {
ResumeCompositor();
}
}
void nsCocoaWindow::ReportSizeEvent() {
@ -2105,6 +2114,72 @@ void nsCocoaWindow::ReportSizeEvent() {
NS_OBJC_END_TRY_ABORT_BLOCK;
}
void nsCocoaWindow::PauseCompositor() {
nsIWidget* mainChildView = static_cast<nsIWidget*>([[mWindow mainChildView] widget]);
if (!mainChildView) {
return;
}
CompositorBridgeChild* remoteRenderer = mainChildView->GetRemoteRenderer();
if (!remoteRenderer) {
return;
}
remoteRenderer->SendPause();
// Now that the compositor has paused, we also try to mark the browser window
// docshell inactive to stop any animations. This does not affect docshells
// for browsers in other processes, but browser UI code should be managing
// their active state appropriately.
if (!mWidgetListener) {
return;
}
PresShell* presShell = mWidgetListener->GetPresShell();
if (!presShell) {
return;
}
nsPresContext* presContext = presShell->GetPresContext();
if (!presContext) {
return;
}
nsDocShell* docShell = presContext->GetDocShell();
if (!docShell) {
return;
}
docShell->SetIsActive(false);
}
void nsCocoaWindow::ResumeCompositor() {
nsIWidget* mainChildView = static_cast<nsIWidget*>([[mWindow mainChildView] widget]);
if (!mainChildView) {
return;
}
CompositorBridgeChild* remoteRenderer = mainChildView->GetRemoteRenderer();
if (!remoteRenderer) {
return;
}
remoteRenderer->SendResume();
// Now that the compositor has resumed, we also try to mark the browser window
// docshell active to restart any animations. This does not affect docshells
// for browsers in other processes, but browser UI code should be managing
// their active state appropriately.
if (!mWidgetListener) {
return;
}
PresShell* presShell = mWidgetListener->GetPresShell();
if (!presShell) {
return;
}
nsPresContext* presContext = presShell->GetPresContext();
if (!presContext) {
return;
}
nsDocShell* docShell = presContext->GetDocShell();
if (!docShell) {
return;
}
docShell->SetIsActive(true);
}
void nsCocoaWindow::SetMenuBar(nsMenuBarX* aMenuBar) {
if (mMenuBar) mMenuBar->SetParent(nullptr);
if (!mWindow) {