Skip to content

Commit

Permalink
Bug 1689203 - Allow fallback from WebRender to Software WebRender. r=…
Browse files Browse the repository at this point in the history
…jrmuizel

We can disable WebRender because the GPU process crashed, or we
encountered a graceful runtime error in WebRender. This patch adds two
new prefs to control how that fallback works.

gfx.webrender.fallback.software-d3d11 controls if WebRender falls back
to Software WebRender + D3D11 compositing. If true, and the user is
allowed to get Software WebRender, we will fallback to Software
WebRender with the D3D11 compositor first.

gfx.webrender.fallback.software controls if WebRender falls back to
Software WebRender. If true, and the user is allowed to get Software
WebRender, we will fallback to Software WebRender without the D3D11
compositor.

gfx.webrender.fallback.basic controls if WebRender or Software
WebRender falls back to Basic. If true, it falls back to Basic.
Otherwise it continues to use Software WebRender without the D3D11
compositor. Note that this means OpenGL on Android.

This patch also means that gfx.webrender.all=true and MOZ_WEBRENDER=1
no longer disables Software WebRender. It will still prefer (Hardware)
WebRender but we want to allow fallback to Software WebRender for
configurations that forced WebRender on.

Differential Revision: https://phabricator.services.mozilla.com/D103491
  • Loading branch information
aosmond committed Feb 1, 2021
1 parent 844d5d5 commit b5f061e
Show file tree
Hide file tree
Showing 7 changed files with 157 additions and 65 deletions.
4 changes: 0 additions & 4 deletions gfx/config/gfxConfigManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,6 @@ void gfxConfigManager::ConfigureWebRenderSoftware() {
// (hardware). See bug 1656811.
if (mWrSoftwareForceEnabled) {
mFeatureWrSoftware->UserForceEnable("Force enabled by pref");
} else if (mWrForceEnabled || mWrEnvForceEnabled) {
mFeatureWrSoftware->UserDisable(
"User force-enabled full WR",
"FEATURE_FAILURE_USER_FORCE_ENABLED_FULL_WR"_ns);
} else if (mWrForceDisabled || mWrEnvForceDisabled) {
// If the user set the pref to force-disable, let's do that. This
// will override all the other enabling prefs
Expand Down
93 changes: 48 additions & 45 deletions gfx/ipc/GPUProcessManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -192,14 +192,27 @@ bool GPUProcessManager::IsGPUProcessLaunching() {
}

void GPUProcessManager::DisableGPUProcess(const char* aMessage) {
MaybeDisableGPUProcess(aMessage, /* aAllowRestart */ false);
}

bool GPUProcessManager::MaybeDisableGPUProcess(const char* aMessage,
bool aAllowRestart) {
if (!gfxConfig::IsEnabled(Feature::GPU_PROCESS)) {
return;
return true;
}

bool wantRestart = gfxPlatform::FallbackFromAcceleration(
FeatureStatus::Unavailable, "GPU Process is disabled",
"FEATURE_FAILURE_GPU_PROCESS_DISABLED"_ns);
if (aAllowRestart && wantRestart) {
// The fallback method can make use of the GPU process.
return false;
}

gfxConfig::SetFailed(Feature::GPU_PROCESS, FeatureStatus::Failed, aMessage);
gfxCriticalNote << aMessage;

gfxPlatform::NotifyGPUProcessDisabled();
gfxPlatform::DisableGPUProcess();

Telemetry::Accumulate(Telemetry::GPU_PROCESS_CRASH_FALLBACKS,
uint32_t(FallbackType::DISABLED));
Expand All @@ -218,12 +231,7 @@ void GPUProcessManager::DisableGPUProcess(const char* aMessage) {
// crash, then we need to tell the content processes again, because they
// need to rebind to the UI process.
HandleProcessLost();

// On Windows and Linux, always fallback to software.
// The assumption is that something in the graphics driver is crashing.
#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
FallbackToSoftware("GPU Process is disabled, fallback to software solution.");
#endif
return true;
}

bool GPUProcessManager::EnsureGPUReady() {
Expand Down Expand Up @@ -454,48 +462,49 @@ bool GPUProcessManager::DisableWebRenderConfig(wr::WebRenderError aError,
return false;
}
// Disable WebRender
bool wantRestart;
if (aError == wr::WebRenderError::INITIALIZE) {
gfxPlatform::DisableWebRender(gfx::FeatureStatus::Unavailable,
"WebRender initialization failed", aMsg);
wantRestart = gfxPlatform::FallbackFromAcceleration(
gfx::FeatureStatus::Unavailable, "WebRender initialization failed",
aMsg);
} else if (aError == wr::WebRenderError::MAKE_CURRENT) {
gfxPlatform::DisableWebRender(gfx::FeatureStatus::Unavailable,
"Failed to make render context current",
"FEATURE_FAILURE_WEBRENDER_MAKE_CURRENT"_ns);
wantRestart = gfxPlatform::FallbackFromAcceleration(
gfx::FeatureStatus::Unavailable,
"Failed to make render context current",
"FEATURE_FAILURE_WEBRENDER_MAKE_CURRENT"_ns);
} else if (aError == wr::WebRenderError::RENDER) {
gfxPlatform::DisableWebRender(gfx::FeatureStatus::Unavailable,
"Failed to render WebRender",
"FEATURE_FAILURE_WEBRENDER_RENDER"_ns);
wantRestart = gfxPlatform::FallbackFromAcceleration(
gfx::FeatureStatus::Unavailable, "Failed to render WebRender",
"FEATURE_FAILURE_WEBRENDER_RENDER"_ns);
} else if (aError == wr::WebRenderError::NEW_SURFACE) {
gfxPlatform::DisableWebRender(gfx::FeatureStatus::Unavailable,
"Failed to create new surface",
"FEATURE_FAILURE_WEBRENDER_NEW_SURFACE"_ns);
wantRestart = gfxPlatform::FallbackFromAcceleration(
gfx::FeatureStatus::Unavailable, "Failed to create new surface",
"FEATURE_FAILURE_WEBRENDER_NEW_SURFACE"_ns);
} else if (aError == wr::WebRenderError::EXCESSIVE_RESETS) {
gfxPlatform::DisableWebRender(
wantRestart = gfxPlatform::FallbackFromAcceleration(
gfx::FeatureStatus::Unavailable, "Device resets exceeded threshold",
"FEATURE_FAILURE_WEBRENDER_EXCESSIVE_RESETS"_ns);
} else {
MOZ_ASSERT_UNREACHABLE("Invalid value");
gfxPlatform::DisableWebRender(gfx::FeatureStatus::Unavailable,
"Unhandled failure reason",
"FEATURE_FAILURE_WEBRENDER_UNHANDLED"_ns);
gfxPlatform::FallbackFromAcceleration(
gfx::FeatureStatus::Unavailable, "Unhandled failure reason",
"FEATURE_FAILURE_WEBRENDER_UNHANDLED"_ns);
}
gfx::gfxVars::SetUseWebRenderDCompVideoOverlayWin(false);

// If we still have the GPU process, and we fallback to a new configuration
// that prefers to have the GPU process, reset the counter.
if (wantRestart && mProcess) {
mNumProcessAttempts = 1;
}

#if defined(MOZ_WIDGET_ANDROID)
// If aError is not wr::WebRenderError::INITIALIZE, nsWindow does not
// re-create LayerManager. Needs to trigger re-creating LayerManager on
// android
if (aError != wr::WebRenderError::INITIALIZE) {
NotifyDisablingWebRender();
}
#elif defined(MOZ_WIDGET_GTK)
// Hardware compositing should be disabled by default if we aren't using
// WebRender. We had to check if it is enabled at all, because it may
// already have been forced disabled (e.g. safe mode, headless). It may
// still be forced on by the user, and if so, this should have no effect.
gfxConfig::SetFailed(Feature::HW_COMPOSITING, FeatureStatus::Blocked,
"Acceleration blocked by platform",
"FEATURE_FAILURE_LOST_WEBRENDER"_ns);
#endif

return true;
Expand All @@ -505,10 +514,11 @@ void GPUProcessManager::DisableWebRender(wr::WebRenderError aError,
const nsCString& aMsg) {
if (DisableWebRenderConfig(aError, aMsg)) {
if (mProcess) {
OnRemoteProcessDeviceReset(mProcess);
RebuildRemoteSessions();
} else {
OnInProcessDeviceReset(/* aTrackThreshold */ false);
RebuildInProcessSessions();
}
NotifyListenersOnCompositeDeviceReset();
}
}

Expand Down Expand Up @@ -584,17 +594,6 @@ void GPUProcessManager::OnRemoteProcessDeviceReset(GPUProcessHost* aHost) {
NotifyListenersOnCompositeDeviceReset();
}

void GPUProcessManager::FallbackToSoftware(const char* aMessage) {
gfxConfig::SetFailed(Feature::HW_COMPOSITING, FeatureStatus::Blocked,
aMessage, "GPU_PROCESS_FALLBACK_TO_SOFTWARE"_ns);
#ifdef XP_WIN
gfxConfig::SetFailed(Feature::D3D11_COMPOSITING, FeatureStatus::Blocked,
aMessage, "GPU_PROCESS_FALLBACK_TO_SOFTWARE"_ns);
gfxConfig::SetFailed(Feature::DIRECT2D, FeatureStatus::Blocked, aMessage,
"GPU_PROCESS_FALLBACK_TO_SOFTWARE"_ns);
#endif
}

void GPUProcessManager::NotifyListenersOnCompositeDeviceReset() {
for (const auto& listener : mListeners) {
listener->OnCompositorDeviceReset();
Expand All @@ -616,7 +615,11 @@ void GPUProcessManager::OnProcessUnexpectedShutdown(GPUProcessHost* aHost) {
char disableMessage[64];
SprintfLiteral(disableMessage, "GPU process disabled after %d attempts",
mNumProcessAttempts);
DisableGPUProcess(disableMessage);
if (!MaybeDisableGPUProcess(disableMessage, /* aAllowRestart */ true)) {
// Fallback wants the GPU process. Reset our counter.
mNumProcessAttempts = 0;
HandleProcessLost();
}
} else if (mNumProcessAttempts >
uint32_t(StaticPrefs::
layers_gpu_process_max_restarts_with_decoder()) &&
Expand Down
5 changes: 5 additions & 0 deletions gfx/ipc/GPUProcessManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,11 @@ class GPUProcessManager final : public GPUProcessHost::Listener {
// Permanently disable the GPU process and record a message why.
void DisableGPUProcess(const char* aMessage);

// May permanently disable the GPU process and record a message why. May
// return false if the fallback process decided we should retry the GPU
// process, but only if aAllowRestart is also true.
bool MaybeDisableGPUProcess(const char* aMessage, bool aAllowRestart);

// Shutdown the GPU process.
void CleanShutdown();
void DestroyProcess();
Expand Down
88 changes: 77 additions & 11 deletions gfx/thebes/gfxPlatform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3367,27 +3367,93 @@ void gfxPlatform::NotifyCompositorCreated(LayersBackend aBackend) {
}

/* static */
void gfxPlatform::DisableWebRender(FeatureStatus aStatus, const char* aMessage,
const nsACString& aFailureId) {
bool gfxPlatform::FallbackFromAcceleration(FeatureStatus aStatus,
const char* aMessage,
const nsACString& aFailureId) {
// We always want to ensure (Hardware) WebRender is disabled.
if (gfxConfig::IsEnabled(Feature::WEBRENDER)) {
gfxConfig::GetFeature(Feature::WEBRENDER)
.ForceDisable(aStatus, aMessage, aFailureId);
}
// TODO(aosmond): When WebRender Software replaces Basic, we must not disable
// it because of GPU process crashes, etc.
if (gfxConfig::IsEnabled(Feature::WEBRENDER_SOFTWARE)) {
gfxConfig::GetFeature(Feature::WEBRENDER_SOFTWARE)

#ifdef XP_WIN
// Before we disable D3D11 and HW_COMPOSITING, we should check if we can
// fallback from WebRender to Software WebRender + D3D11 compositing.
if (StaticPrefs::gfx_webrender_fallback_software_d3d11_AtStartup() &&
gfxConfig::IsEnabled(Feature::WEBRENDER_SOFTWARE) &&
gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING) &&
gfxVars::UseWebRender() && !gfxVars::UseSoftwareWebRender()) {
// Fallback to Software WebRender + D3D11 compositing.
gfxCriticalNote << "Fallback WR to SW-WR + D3D11";
gfxVars::SetUseSoftwareWebRender(true);
return true;
}

// We aren't using Software WebRender + D3D11 compositing, so turn off the
// D3D11 and D2D.
if (gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
gfxConfig::GetFeature(Feature::D3D11_COMPOSITING)
.ForceDisable(aStatus, aMessage, aFailureId);
}
if (gfxConfig::IsEnabled(Feature::DIRECT2D)) {
gfxConfig::GetFeature(Feature::DIRECT2D)
.ForceDisable(aStatus, aMessage, aFailureId);
}
gfxVars::SetUseWebRender(false);
gfxVars::SetUseSoftwareWebRender(false);
#endif

#ifndef MOZ_WIDGET_ANDROID
// Non-Android wants to fallback to Software WebRender or Basic. Android wants
// to fallback to OpenGL.
if (gfxConfig::IsEnabled(Feature::HW_COMPOSITING)) {
gfxConfig::GetFeature(Feature::HW_COMPOSITING)
.ForceDisable(aStatus, aMessage, aFailureId);
}
#endif

if (!gfxVars::UseWebRender()) {
// We were not using WebRender in the first place, and we have disabled
// all forms of accelerated compositing.
return false;
}

if (StaticPrefs::gfx_webrender_fallback_software_AtStartup() &&
gfxConfig::IsEnabled(Feature::WEBRENDER_SOFTWARE) &&
!gfxVars::UseSoftwareWebRender()) {
// Fallback from WebRender to Software WebRender.
gfxCriticalNote << "Fallback WR to SW-WR";
gfxVars::SetUseSoftwareWebRender(true);
return true;
}

if (StaticPrefs::gfx_webrender_fallback_basic_AtStartup()) {
// Fallback from WebRender or Software WebRender to Basic.
gfxCriticalNote << "Fallback (SW-)WR to Basic";
if (gfxConfig::IsEnabled(Feature::WEBRENDER_SOFTWARE)) {
gfxConfig::GetFeature(Feature::WEBRENDER_SOFTWARE)
.ForceDisable(aStatus, aMessage, aFailureId);
}
gfxVars::SetUseWebRender(false);
gfxVars::SetUseSoftwareWebRender(false);
return false;
}

// Continue using Software WebRender.
gfxCriticalNoteOnce << "Fallback remains SW-WR";
MOZ_ASSERT(gfxVars::UseWebRender());
MOZ_ASSERT(gfxVars::UseSoftwareWebRender());
return false;
}

/* static */
void gfxPlatform::NotifyGPUProcessDisabled() {
DisableWebRender(FeatureStatus::Unavailable, "GPU Process is disabled",
"FEATURE_FAILURE_GPU_PROCESS_DISABLED"_ns);
void gfxPlatform::DisableGPUProcess() {
gfxVars::SetRemoteCanvasEnabled(false);

if (gfxVars::UseWebRender()) {
// We need to initialize the parent process to prepare for WebRender if we
// did not end up disabling it, despite losing the GPU process.
wr::RenderThread::Start();
image::ImageMemoryReporter::InitForWebRender();
}
}

void gfxPlatform::FetchAndImportContentDeviceData() {
Expand Down
9 changes: 5 additions & 4 deletions gfx/thebes/gfxPlatform.h
Original file line number Diff line number Diff line change
Expand Up @@ -733,7 +733,7 @@ class gfxPlatform : public mozilla::layers::MemoryPressureListener {
*/
static bool PerfWarnings();

static void NotifyGPUProcessDisabled();
static void DisableGPUProcess();

void NotifyCompositorCreated(mozilla::layers::LayersBackend aBackend);
mozilla::layers::LayersBackend GetCompositorBackend() const {
Expand Down Expand Up @@ -791,9 +791,10 @@ class gfxPlatform : public mozilla::layers::MemoryPressureListener {

static const char* WebRenderResourcePathOverride();

static void DisableWebRender(mozilla::gfx::FeatureStatus aStatus,
const char* aMessage,
const nsACString& aFailureId);
// Returns true if we would like to keep the GPU process if possible.
static bool FallbackFromAcceleration(mozilla::gfx::FeatureStatus aStatus,
const char* aMessage,
const nsACString& aFailureId);

void NotifyFrameStats(nsTArray<mozilla::layers::FrameStats>&& aFrameStats);

Expand Down
3 changes: 2 additions & 1 deletion gfx/webrender_bindings/RenderThread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -234,8 +234,9 @@ void RenderThread::RemoveRenderer(wr::WindowId aWindowId) {

mRenderers.erase(aWindowId);

if (mRenderers.size() == 0 && mHandlingDeviceReset) {
if (mRenderers.empty()) {
mHandlingDeviceReset = false;
mHandlingWebRenderError = false;
}

auto windows = mWindowInfos.Lock();
Expand Down
20 changes: 20 additions & 0 deletions modules/libpref/init/StaticPrefList.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4684,6 +4684,26 @@
value: true
mirror: once

# Whether or not to fallback from WebRender/WebRender Software to Basic.
- name: gfx.webrender.fallback.basic
type: bool
value: true
mirror: once

# Whether or not to fallback from WebRender to Software WebRender.
- name: gfx.webrender.fallback.software
type: bool
value: @IS_NIGHTLY_BUILD@
mirror: once

#ifdef XP_WIN
# Whether or not to fallback from WebRender to Software WebRender + D3D11.
- name: gfx.webrender.fallback.software-d3d11
type: bool
value: @IS_NIGHTLY_BUILD@
mirror: once
#endif

- name: gfx.webrender.use-optimized-shaders
type: bool
value: true
Expand Down

0 comments on commit b5f061e

Please sign in to comment.