fix: disable background throttling also in the `viz::DisplayScheduler`

`viz::DisplayScheduler` is responsible for drawing and swapping frames
in the `DisplayScheduler::DrawAndSwap` which is called from the
`DisplayScheduler::AttemptDrawAndSwap` if the `DisplayScheduler::ShouldDraw`
returns true. `ShouldDraw` depends on the `DisplayScheduler` visibility
and when it is not visible then it returns false.

In order to keep producing frames, disabling `backgroundThrottling`
should also prevent changing `DisplayScheduler` visibility to false.

`DisplayScheduler` lives in the `ui::Compositor` where every
`electron::NativewWindow` has its own `Compositor`. `electron::NativewWindow`
may be host of the multiple `electron::api::WebContents` instances which may
have different `WebPreferences` settings. Therefore if at least one
of the `WebContents` requires disabling throttling then all other `WebContents`
using the same window will have it disabled in the `ui::Compositor`.

BREAKING CHANGE:
`backgroundThrottling` set to false will disable frames throttling
in the `BrowserWindow` for all `WebContents` displayed by it.

Close: [#31016](https://github.com/electron/electron/issues/31016)
pr/38924
Michał Pichliński 2023-06-26 11:14:51 +02:00
parent 9226cc662b
commit 931baa8519
9 changed files with 174 additions and 3 deletions

View File

@ -74,7 +74,11 @@
* `defaultEncoding` string (optional) - Defaults to `ISO-8859-1`.
* `backgroundThrottling` boolean (optional) - Whether to throttle animations and timers
when the page becomes background. This also affects the
[Page Visibility API](../browser-window.md#page-visibility). Defaults to `true`.
[Page Visibility API](../browser-window.md#page-visibility). When at least one
[webContents](../web-contents.md) displayed in the single
[browserWindow](../browser-window.md) has dsiabled `backgroundThrottling` then
frames will be drawn and swapped for the whole window and other
[webContents](../web-contents.md) displayed by it. Defaults to `true`.
* `offscreen` boolean (optional) - Whether to enable offscreen rendering for the browser
window. Defaults to `false`. See the
[offscreen rendering tutorial](../../tutorial/offscreen-rendering.md) for

View File

@ -335,6 +335,7 @@ filenames = {
"shell/browser/api/ui_event.h",
"shell/browser/auto_updater.cc",
"shell/browser/auto_updater.h",
"shell/browser/background_throttling_source.h",
"shell/browser/badging/badge_manager.cc",
"shell/browser/badging/badge_manager.h",
"shell/browser/badging/badge_manager_factory.cc",

View File

@ -128,3 +128,4 @@ fix_remove_profiles_from_spellcheck_service.patch
chore_patch_out_profile_methods_in_chrome_browser_pdf.patch
chore_patch_out_profile_methods_in_titlebar_config.patch
fix_crash_on_nativetheme_change_during_context_menu_close.patch
fix_disabling_background_throttling_in_compositor.patch

View File

@ -0,0 +1,83 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Michal Pichlinski <michal.pichlinski@openfin.co>
Date: Thu, 15 Jun 2023 23:04:48 +0200
Subject: allow disabling throttling in the `viz::DisplayScheduler` per
`ui::Compositor`
In Chromium when the `viz::DisplayScheduler` is invisible it throttles
its work by dropping frame draws and swaps.
This patch allows disbling this throttling by preventing transition to
invisible state of the `viz::DisplayScheduler` owned
by the `ui::Compositor`.
diff --git a/ui/compositor/compositor.cc b/ui/compositor/compositor.cc
index e52e5b74146d0709925e87c6f4d5dc551ec8eec8..bea5ac54c007672ac6cc91dd34ab552a82579ffb 100644
--- a/ui/compositor/compositor.cc
+++ b/ui/compositor/compositor.cc
@@ -342,7 +342,8 @@ void Compositor::SetLayerTreeFrameSink(
if (display_private_) {
disabled_swap_until_resize_ = false;
display_private_->Resize(size());
- display_private_->SetDisplayVisible(host_->IsVisible());
+ // Invisible display is throttling itself.
+ display_private_->SetDisplayVisible(background_throttling_ ? host_->IsVisible() : true);
display_private_->SetDisplayColorSpaces(display_color_spaces_);
display_private_->SetDisplayColorMatrix(
gfx::SkM44ToTransform(display_color_matrix_));
@@ -534,8 +535,11 @@ void Compositor::SetVisible(bool visible) {
host_->SetVisible(visible);
// Visibility is reset when the output surface is lost, so this must also be
// updated then.
- if (display_private_)
- display_private_->SetDisplayVisible(visible);
+ if (display_private_) {
+ // Invisible display is throttling itself.
+ display_private_->SetDisplayVisible(
+ background_throttling_ ? visible : true);
+ }
}
bool Compositor::IsVisible() {
@@ -960,4 +964,13 @@ const cc::LayerTreeSettings& Compositor::GetLayerTreeSettings() const {
return host_->GetSettings();
}
+void Compositor::SetBackgroundThrottling(bool background_throttling_enabled) {
+ background_throttling_ = background_throttling_enabled;
+ if (display_private_) {
+ // Invisible display is throttling itself.
+ display_private_->SetDisplayVisible(
+ background_throttling_ ? host_->IsVisible() : true);
+ }
+}
+
} // namespace ui
diff --git a/ui/compositor/compositor.h b/ui/compositor/compositor.h
index ddb28fd6f7549759df7e3b7d6b309cd982e199af..7473f3af9bc8cac8db3c30dfbc9b7140f832ac15 100644
--- a/ui/compositor/compositor.h
+++ b/ui/compositor/compositor.h
@@ -511,6 +511,10 @@ class COMPOSITOR_EXPORT Compositor : public base::PowerSuspendObserver,
const cc::LayerTreeSettings& GetLayerTreeSettings() const;
+ // Sets |background_throttling_| responsible for suspending drawing
+ // and switching frames.
+ void SetBackgroundThrottling(bool background_throttling_enabled);
+
private:
friend class base::RefCounted<Compositor>;
friend class TotalAnimationThroughputReporter;
@@ -617,6 +621,12 @@ class COMPOSITOR_EXPORT Compositor : public base::PowerSuspendObserver,
// See go/report-ux-metrics-at-painting for details.
bool animation_started_ = false;
+ // Background throttling is a default Chromium behaviour. It occurs
+ // when the |display_private_| is not visible by prevent drawing and swapping
+ // frames. When it is disabled we are keeping |display_private_| always
+ // visible in order to keep generating frames.
+ bool background_throttling_ = true;
+
TrackerId next_throughput_tracker_id_ = 1u;
struct TrackerState {
TrackerState();

View File

@ -1035,6 +1035,9 @@ void WebContents::InitWithWebContents(
}
WebContents::~WebContents() {
if (owner_window_) {
owner_window_->RemoveBackgroundThrottlingSource(this);
}
if (web_contents()) {
content::RenderViewHost* host = web_contents()->GetRenderViewHost();
if (host)
@ -2253,10 +2256,15 @@ void WebContents::SetOwnerWindow(NativeWindow* owner_window) {
void WebContents::SetOwnerWindow(content::WebContents* web_contents,
NativeWindow* owner_window) {
if (owner_window_) {
owner_window_->RemoveBackgroundThrottlingSource(this);
}
if (owner_window) {
owner_window_ = owner_window->GetWeakPtr();
NativeWindowRelay::CreateForWebContents(web_contents,
owner_window->GetWeakPtr());
owner_window_->AddBackgroundThrottlingSource(this);
} else {
owner_window_ = nullptr;
web_contents->RemoveUserData(NativeWindowRelay::UserDataKey());
@ -2310,6 +2318,10 @@ bool WebContents::GetBackgroundThrottling() const {
void WebContents::SetBackgroundThrottling(bool allowed) {
background_throttling_ = allowed;
if (owner_window_) {
owner_window_->UpdateBackgroundThrottlingState();
}
auto* rfh = web_contents()->GetPrimaryMainFrame();
if (!rfh)
return;

View File

@ -36,6 +36,7 @@
#include "printing/buildflags/buildflags.h"
#include "shell/browser/api/frame_subscriber.h"
#include "shell/browser/api/save_page_handler.h"
#include "shell/browser/background_throttling_source.h"
#include "shell/browser/event_emitter_mixin.h"
#include "shell/browser/extended_web_contents_observer.h"
#include "shell/browser/ui/inspectable_web_contents.h"
@ -107,7 +108,8 @@ class WebContents : public ExclusiveAccessContext,
public content::WebContentsDelegate,
public content::RenderWidgetHost::InputEventObserver,
public InspectableWebContentsDelegate,
public InspectableWebContentsViewDelegate {
public InspectableWebContentsViewDelegate,
public BackgroundThrottlingSource {
public:
enum class Type {
kBackgroundPage, // An extension background page.
@ -156,7 +158,7 @@ class WebContents : public ExclusiveAccessContext,
void Close(absl::optional<gin_helper::Dictionary> options);
base::WeakPtr<WebContents> GetWeakPtr() { return weak_factory_.GetWeakPtr(); }
bool GetBackgroundThrottling() const;
bool GetBackgroundThrottling() const override;
void SetBackgroundThrottling(bool allowed);
int GetProcessID() const;
base::ProcessId GetOSProcessID() const;

View File

@ -0,0 +1,18 @@
// Copyright 2023 OpenFin. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ELECTRON_SHELL_BROWSER_BACKGROUND_THROTTLING_SOURCE
#define ELECTRON_SHELL_BROWSER_BACKGROUND_THROTTLING_SOURCE
namespace electron {
class BackgroundThrottlingSource {
public:
virtual ~BackgroundThrottlingSource() = default;
virtual bool GetBackgroundThrottling() const = 0;
};
} // namespace electron
#endif // ELECTRON_SHELL_BROWSER_BACKGROUND_THROTTLING_SOURCE

View File

@ -13,6 +13,7 @@
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "content/public/browser/web_contents_user_data.h"
#include "shell/browser/background_throttling_source.h"
#include "shell/browser/browser.h"
#include "shell/browser/native_window_features.h"
#include "shell/browser/ui/drag_util.h"
@ -23,6 +24,7 @@
#include "shell/common/options_switches.h"
#include "third_party/skia/include/core/SkRegion.h"
#include "ui/base/hit_test.h"
#include "ui/compositor/compositor.h"
#include "ui/views/widget/widget.h"
#if !BUILDFLAG(IS_MAC)
@ -737,6 +739,40 @@ void NativeWindow::RemoveDraggableRegionProvider(
[&provider](DraggableRegionProvider* p) { return p == provider; });
}
void NativeWindow::AddBackgroundThrottlingSource(
BackgroundThrottlingSource* source) {
auto result = background_throttling_sources_.insert(source);
DCHECK(result.second) << "Added already stored BackgroundThrottlingSource.";
UpdateBackgroundThrottlingState();
}
void NativeWindow::RemoveBackgroundThrottlingSource(
BackgroundThrottlingSource* source) {
auto result = background_throttling_sources_.erase(source);
DCHECK(result == 1)
<< "Tried to remove non existing BackgroundThrottlingSource.";
UpdateBackgroundThrottlingState();
}
void NativeWindow::UpdateBackgroundThrottlingState() {
if (!GetWidget()) {
return;
}
if (!GetWidget()->GetCompositor()) {
return;
}
bool enable_background_throttling = true;
for (const auto* background_throttling_source :
background_throttling_sources_) {
if (!background_throttling_source->GetBackgroundThrottling()) {
enable_background_throttling = false;
break;
}
}
GetWidget()->GetCompositor()->SetBackgroundThrottling(
enable_background_throttling);
}
views::Widget* NativeWindow::GetWidget() {
return widget();
}

View File

@ -47,6 +47,7 @@ class PersistentDictionary;
namespace electron {
class ElectronMenuModel;
class BackgroundThrottlingSource;
class NativeBrowserView;
namespace api {
@ -393,6 +394,17 @@ class NativeWindow : public base::SupportsUserData,
void AddDraggableRegionProvider(DraggableRegionProvider* provider);
void RemoveDraggableRegionProvider(DraggableRegionProvider* provider);
// Adds |source| to |background_throttling_sources_|, triggers update of
// background throttling state.
void AddBackgroundThrottlingSource(BackgroundThrottlingSource* source);
// Removes |source| to |background_throttling_sources_|, triggers update of
// background throttling state.
void RemoveBackgroundThrottlingSource(BackgroundThrottlingSource* source);
// Updates `ui::Compositor` background throttling state based on
// |background_throttling_sources_|. If at least one of the sources disables
// throttling, then throttling in the `ui::Compositor` will be disabled.
void UpdateBackgroundThrottlingState();
protected:
friend class api::BrowserView;
@ -483,6 +495,8 @@ class NativeWindow : public base::SupportsUserData,
// Observers of this window.
base::ObserverList<NativeWindowObserver> observers_;
std::set<BackgroundThrottlingSource*> background_throttling_sources_;
// Accessible title.
std::u16string accessible_title_;