Bug 1873084 - Prefer downgrading acceleration over disabling GPU process with initialization failures. r=jrmuizel

This patch makes it so that if we encounter an issue launching the GPU
process, we attempt to fallback first over disabling the GPU process.
This is because the different acceleration modes we might start with
change how we initialize. It may be possible to have a pure Software
WebRender GPU process with a given users configuration.

Differential Revision: https://phabricator.services.mozilla.com/D197827
This commit is contained in:
Andrew Osmond 2024-01-06 19:41:35 +00:00
parent dd2c3f98ef
commit accf1bcd8f
2 changed files with 85 additions and 35 deletions

View File

@ -276,8 +276,8 @@ bool GPUProcessManager::MaybeDisableGPUProcess(const char* aMessage,
mLastErrorMsg.reset();
} else {
wantRestart = gfxPlatform::FallbackFromAcceleration(
FeatureStatus::Unavailable, "GPU Process is disabled",
"FEATURE_FAILURE_GPU_PROCESS_DISABLED"_ns);
FeatureStatus::Unavailable, aMessage,
"FEATURE_FAILURE_GPU_PROCESS_ERROR"_ns);
}
if (aAllowRestart && wantRestart) {
// The fallback method can make use of the GPU process.
@ -322,7 +322,8 @@ bool GPUProcessManager::MaybeDisableGPUProcess(const char* aMessage,
return true;
}
nsresult GPUProcessManager::EnsureGPUReady() {
nsresult GPUProcessManager::EnsureGPUReady(
bool aRetryAfterFallback /* = true */) {
MOZ_ASSERT(NS_IsMainThread());
// We only wait to fail with NS_ERROR_ILLEGAL_DURING_SHUTDOWN if we would
@ -330,41 +331,63 @@ nsresult GPUProcessManager::EnsureGPUReady() {
// process.
bool inShutdown = AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMShutdown);
// Launch the GPU process if it is enabled but hasn't been (re-)launched yet.
if (!mProcess && gfxConfig::IsEnabled(Feature::GPU_PROCESS)) {
if (NS_WARN_IF(inShutdown)) {
return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
while (true) {
// Launch the GPU process if it is enabled but hasn't been (re-)launched
// yet.
if (!mProcess && gfxConfig::IsEnabled(Feature::GPU_PROCESS)) {
if (NS_WARN_IF(inShutdown)) {
return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
}
if (!LaunchGPUProcess()) {
return NS_ERROR_FAILURE;
}
}
if (!LaunchGPUProcess()) {
return NS_ERROR_NOT_AVAILABLE;
}
}
if (mProcess && !mProcess->IsConnected()) {
if (NS_WARN_IF(inShutdown)) {
return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
}
if (mProcess && !mProcess->IsConnected()) {
if (NS_WARN_IF(inShutdown)) {
return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
if (!mProcess->WaitForLaunch()) {
// If this fails, we should have fired OnProcessLaunchComplete and
// removed the process.
MOZ_ASSERT(!mProcess && !mGPUChild);
return NS_ERROR_FAILURE;
}
}
if (!mProcess->WaitForLaunch()) {
// If this fails, we should have fired OnProcessLaunchComplete and
// removed the process.
MOZ_ASSERT(!mProcess && !mGPUChild);
return NS_ERROR_NOT_AVAILABLE;
// The only scenario this should be possible is if we raced with the
// initialization, which failed, and has already decided to disable the GPU
// process.
if (!mGPUChild) {
MOZ_DIAGNOSTIC_ASSERT(!gfxConfig::IsEnabled(Feature::GPU_PROCESS));
break;
}
}
if (mGPUChild) {
if (mGPUChild->EnsureGPUReady()) {
return NS_OK;
}
// If the initialization above fails, we likely have a GPU process teardown
// waiting in our message queue (or will soon). We need to ensure we don't
// restart it later because if we fail here, our callers assume they should
// fall back to a combined UI/GPU process. This also ensures our internal
// state is consistent (e.g. process token is reset).
DisableGPUProcess("Failed to initialize GPU process");
// waiting in our message queue (or will soon). If the fallback wants us to
// give up on the GPU process, we will exit the loop.
if (MaybeDisableGPUProcess("Failed to initialize GPU process",
/* aAllowRestart */ true)) {
MOZ_DIAGNOSTIC_ASSERT(!gfxConfig::IsEnabled(Feature::GPU_PROCESS));
break;
}
// Otherwise HandleProcessLost will explicitly teardown the process and
// prevent any pending events from triggering our fallback logic again, and
// we will retry with a different configuration.
MOZ_DIAGNOSTIC_ASSERT(gfxConfig::IsEnabled(Feature::GPU_PROCESS));
OnBlockingProcessUnexpectedShutdown();
// Some callers may need to reconfigure if we fellback.
if (!aRetryAfterFallback) {
return NS_ERROR_NOT_AVAILABLE;
}
}
// This is the first time we are trying to use the in-process compositor.
@ -374,7 +397,7 @@ nsresult GPUProcessManager::EnsureGPUReady() {
}
ResetProcessStable();
}
return NS_ERROR_NOT_AVAILABLE;
return NS_ERROR_FAILURE;
}
bool GPUProcessManager::EnsureProtocolsReady() {
@ -783,6 +806,15 @@ void GPUProcessManager::NotifyListenersOnCompositeDeviceReset() {
}
}
void GPUProcessManager::OnBlockingProcessUnexpectedShutdown() {
if (mProcess) {
CompositorManagerChild::OnGPUProcessLost(mProcess->GetProcessToken());
}
DestroyProcess(/* aUnexpectedShutdown */ true);
mUnstableProcessAttempts = 0;
HandleProcessLost();
}
void GPUProcessManager::OnProcessUnexpectedShutdown(GPUProcessHost* aHost) {
MOZ_ASSERT(mProcess && mProcess == aHost);
@ -800,8 +832,11 @@ void GPUProcessManager::OnProcessUnexpectedShutdown(GPUProcessHost* aHost) {
mTotalProcessAttempts);
if (!MaybeDisableGPUProcess(disableMessage, /* aAllowRestart */ true)) {
// Fallback wants the GPU process. Reset our counter.
MOZ_DIAGNOSTIC_ASSERT(gfxConfig::IsEnabled(Feature::GPU_PROCESS));
mUnstableProcessAttempts = 0;
HandleProcessLost();
} else {
MOZ_DIAGNOSTIC_ASSERT(!gfxConfig::IsEnabled(Feature::GPU_PROCESS));
}
} else if (mUnstableProcessAttempts >
uint32_t(StaticPrefs::
@ -1037,15 +1072,22 @@ already_AddRefed<CompositorSession> GPUProcessManager::CreateTopLevelCompositor(
LayersId layerTreeId = AllocateLayerTreeId();
if (!EnsureProtocolsReady()) {
RefPtr<CompositorSession> session;
nsresult rv = EnsureGPUReady(/* aRetryAfterFallback */ false);
if (NS_WARN_IF(rv == NS_ERROR_ILLEGAL_DURING_SHUTDOWN)) {
*aRetryOut = false;
return nullptr;
}
RefPtr<CompositorSession> session;
// If we used fallback, then retry creating the compositor sessions because
// our configuration may have changed.
if (rv == NS_ERROR_NOT_AVAILABLE) {
*aRetryOut = true;
return nullptr;
}
nsresult rv = EnsureGPUReady();
if (NS_WARN_IF(rv == NS_ERROR_ILLEGAL_DURING_SHUTDOWN)) {
if (!EnsureProtocolsReady()) {
*aRetryOut = false;
return nullptr;
}
@ -1054,9 +1096,15 @@ already_AddRefed<CompositorSession> GPUProcessManager::CreateTopLevelCompositor(
session = CreateRemoteSession(aWidget, aLayerManager, layerTreeId, aScale,
aOptions, aUseExternalSurfaceSize,
aSurfaceSize, aInnerWindowId);
if (!session) {
// We couldn't create a remote compositor, so abort the process.
DisableGPUProcess("Failed to create remote compositor");
if (NS_WARN_IF(!session)) {
if (!MaybeDisableGPUProcess("Failed to create remote compositor",
/* aAllowRestart */ true)) {
// Fallback wants the GPU process. Reset our counter.
MOZ_DIAGNOSTIC_ASSERT(gfxConfig::IsEnabled(Feature::GPU_PROCESS));
OnBlockingProcessUnexpectedShutdown();
} else {
MOZ_DIAGNOSTIC_ASSERT(!gfxConfig::IsEnabled(Feature::GPU_PROCESS));
}
*aRetryOut = true;
return nullptr;
}

View File

@ -96,7 +96,7 @@ class GPUProcessManager final : public GPUProcessHost::Listener {
// If the GPU process is enabled but has not yet been launched then this will
// launch the process. If that is not desired then check that return value of
// Process() is non-null before calling.
nsresult EnsureGPUReady();
nsresult EnsureGPUReady(bool aRetryAfterFallback = true);
already_AddRefed<CompositorSession> CreateTopLevelCompositor(
nsBaseWidget* aWidget, WebRenderLayerManager* aLayerManager,
@ -242,6 +242,8 @@ class GPUProcessManager final : public GPUProcessHost::Listener {
void DestroyRemoteCompositorSessions();
void DestroyInProcessCompositorSessions();
void OnBlockingProcessUnexpectedShutdown();
// Returns true if we crossed the threshold such that we should disable
// acceleration.
bool OnDeviceReset(bool aTrackThreshold);