Bug 1745996 - Send a memory pressure event when deactivating sessions. r=jnicol,calu

WebRender retains about 50MBs of memory for every window. When switching
between lots of tabs on Android (where 1 tab = 1 window), this can cause
problems as the app will consume a significant amount of memory.

To avoid this problem, we send a memory pressure event whenever a session is
deactivated, which signals to WebRender that it should deallocate the memory.

This message is sent on a delay of 10s to avoid interfering with tab switching,
and we cancel the message if the tab becomes active again before we fire the
memory pressure event.

Co-Authored-By: Cathy Lu <calu@mozilla.com>
Co-Authored-By: Jonathan Almeida [:jonalmeida] <jonalmeida942@gmail.com>

Differential Revision: https://phabricator.services.mozilla.com/D136965
This commit is contained in:
Agi Sferro 2022-01-26 16:40:56 +00:00
Родитель 16da0fc374
Коммит b1f93026e6
8 изменённых файлов: 60 добавлений и 0 удалений

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

@ -520,6 +520,11 @@ mozilla::ipc::IPCResult CompositorBridgeParent::RecvFlushRendering(
return IPC_OK();
}
mozilla::ipc::IPCResult CompositorBridgeParent::RecvNotifyMemoryPressure() {
NotifyMemoryPressure();
return IPC_OK();
}
mozilla::ipc::IPCResult CompositorBridgeParent::RecvFlushRenderingAsync(
const wr::RenderReasons& aReasons) {
if (mWrBridge) {

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

@ -242,6 +242,7 @@ class CompositorBridgeParentBase : public PCompositorBridgeParent,
const LayersId& id, CompositorOptions* compositorOptions) = 0;
virtual mozilla::ipc::IPCResult RecvFlushRendering(
const wr::RenderReasons& aReasons) = 0;
virtual mozilla::ipc::IPCResult RecvNotifyMemoryPressure() = 0;
virtual mozilla::ipc::IPCResult RecvWaitOnTransactionProcessed() = 0;
virtual mozilla::ipc::IPCResult RecvStartFrameTimeRecording(
const int32_t& bufferSize, uint32_t* startIndex) = 0;
@ -324,6 +325,7 @@ class CompositorBridgeParent final : public CompositorBridgeParentBase,
return IPC_OK();
}
mozilla::ipc::IPCResult RecvNotifyMemoryPressure() override;
mozilla::ipc::IPCResult RecvBeginRecording(
const TimeStamp& aRecordingStart,
BeginRecordingResolver&& aResolve) override;

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

@ -242,6 +242,12 @@ ContentCompositorBridgeParent::RecvMapAndNotifyChildCreated(
return IPC_FAIL_NO_REASON(this);
}
mozilla::ipc::IPCResult
ContentCompositorBridgeParent::RecvNotifyMemoryPressure() {
// This can only be called from the browser process.
return IPC_FAIL_NO_REASON(this);
}
mozilla::ipc::IPCResult ContentCompositorBridgeParent::RecvCheckContentOnlyTDR(
const uint32_t& sequenceNum, bool* isContentOnlyTDR) {
*isContentOnlyTDR = false;

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

@ -84,6 +84,8 @@ class ContentCompositorBridgeParent final : public CompositorBridgeParentBase {
return IPC_OK();
}
mozilla::ipc::IPCResult RecvNotifyMemoryPressure() override;
mozilla::ipc::IPCResult RecvCheckContentOnlyTDR(
const uint32_t& sequenceNum, bool* isContentOnlyTDR) override;

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

@ -173,6 +173,8 @@ parent:
sync NotifyChildRecreated(LayersId id)
returns (CompositorOptions compositorOptions);
async NotifyMemoryPressure();
// Make sure any pending composites are started immediately and
// block until they are completed.
sync FlushRendering(RenderReasons aReasons);

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

@ -60,6 +60,14 @@ public final class ThreadUtils {
sUiHandler.post(runnable);
}
public static void postToUiThreadDelayed(final Runnable runnable, final long delayMillis) {
sUiHandler.postDelayed(runnable, delayMillis);
}
public static void removeUiThreadCallbacks(final Runnable runnable) {
sUiHandler.removeCallbacks(runnable);
}
public static Handler getBackgroundHandler() {
return GeckoBackgroundThread.getHandler();
}

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

@ -90,6 +90,19 @@ public class GeckoSession {
private static final int DATA_URI_MAX_LENGTH = 2 * 1024 * 1024;
// Delay running compositor memory pressure by 10s to avoid interfering with tab switching.
private static final int NOTIFY_MEMORY_PRESSURE_DELAY_MS = 10 * 1000;
private final Runnable mNotifyMemoryPressure =
new Runnable() {
@Override
public void run() {
if (mCompositorReady) {
mCompositor.notifyMemoryPressure();
}
}
};
private enum State implements NativeQueue.State {
INITIAL(0),
READY(1);
@ -195,6 +208,9 @@ public class GeckoSession {
@WrapForJNI(calledFrom = "ui", dispatchTo = "gecko")
public native void setDynamicToolbarMaxHeight(int height);
@WrapForJNI(calledFrom = "ui", dispatchTo = "gecko")
public native void notifyMemoryPressure();
// Gecko thread pauses compositor; blocks UI thread.
@WrapForJNI(calledFrom = "ui", dispatchTo = "current")
public native void syncPauseCompositor();
@ -2090,6 +2106,10 @@ public class GeckoSession {
if (!active) {
mEventDispatcher.dispatch("GeckoView:FlushSessionState", null);
ThreadUtils.postToUiThreadDelayed(mNotifyMemoryPressure, NOTIFY_MEMORY_PRESSURE_DELAY_MS);
} else {
// Delete any pending memory pressure events since we're active again.
ThreadUtils.removeUiThreadCallbacks(mNotifyMemoryPressure);
}
ThreadUtils.runOnUiThread(() -> getAutofillSupport().onActiveChanged(active));

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

@ -1117,6 +1117,21 @@ class LayerViewSupport final
gkWindow->Resize(aLeft, aTop, aWidth, aHeight, /* repaint */ false);
}
void NotifyMemoryPressure() {
MOZ_ASSERT(NS_IsMainThread());
auto acc = mWindow.Access();
if (!acc) {
return; // Already shut down.
}
nsWindow* gkWindow = acc->GetNsWindow();
if (!gkWindow) {
return;
}
gkWindow->mCompositorBridgeChild->SendNotifyMemoryPressure();
}
void SetDynamicToolbarMaxHeight(int32_t aHeight) {
MOZ_ASSERT(NS_IsMainThread());
auto acc = mWindow.Access();