Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  GPUProcessManager.cpp

  Sprache: C
 

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "GPUProcessManager.h"

#include "gfxConfig.h"
#include "gfxPlatform.h"
#include "GPUProcessHost.h"
#include "GPUProcessListener.h"
#include "mozilla/AppShutdown.h"
#include "mozilla/MemoryReportingProcess.h"
#include "mozilla/Preferences.h"
#include "mozilla/RDDChild.h"
#include "mozilla/RDDProcessManager.h"
#include "mozilla/RemoteMediaManagerChild.h"
#include "mozilla/RemoteMediaManagerParent.h"
#include "mozilla/Sprintf.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/StaticPrefs_gfx.h"
#include "mozilla/StaticPrefs_layers.h"
#include "mozilla/StaticPrefs_media.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/gfx/gfxVars.h"
#include "mozilla/gfx/GPUChild.h"
#include "mozilla/gfx/GPUParent.h"
#include "mozilla/glean/GfxMetrics.h"
#include "mozilla/ipc/Endpoint.h"
#include "mozilla/ipc/ProcessChild.h"
#include "mozilla/layers/APZCTreeManagerChild.h"
#include "mozilla/layers/APZInputBridgeChild.h"
#include "mozilla/layers/CompositorBridgeChild.h"
#include "mozilla/layers/CompositorBridgeParent.h"
#include "mozilla/layers/CompositorManagerChild.h"
#include "mozilla/layers/CompositorManagerParent.h"
#include "mozilla/layers/CompositorOptions.h"
#include "mozilla/layers/ImageBridgeChild.h"
#include "mozilla/layers/ImageBridgeParent.h"
#include "mozilla/layers/InProcessCompositorSession.h"
#include "mozilla/layers/LayerTreeOwnerTracker.h"
#include "mozilla/layers/RemoteCompositorSession.h"
#include "mozilla/layers/VideoBridgeParent.h"
#include "mozilla/webrender/RenderThread.h"
#include "mozilla/widget/PlatformWidgetTypes.h"
#include "nsAppRunner.h"
#include "mozilla/widget/CompositorWidget.h"
#ifdef MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING
#  include "mozilla/widget/CompositorWidgetChild.h"
#endif
#include "nsIWidget.h"
#include "nsContentUtils.h"
#include "VRManagerChild.h"
#include "VRManagerParent.h"
#include "VsyncBridgeChild.h"
#include "VsyncIOThreadHolder.h"
#include "VsyncSource.h"
#include "nsExceptionHandler.h"
#include "nsPrintfCString.h"

#ifdef MOZ_WMF_MEDIA_ENGINE
#  include "mozilla/ipc/UtilityMediaServiceChild.h"
#endif

#if defined(MOZ_WIDGET_ANDROID)
#  include "mozilla/java/SurfaceControlManagerWrappers.h"
#  include "mozilla/widget/AndroidUiThread.h"
#  include "mozilla/layers/UiCompositorControllerChild.h"
#endif  // defined(MOZ_WIDGET_ANDROID)

#if defined(XP_WIN)
#  include "gfxWindowsPlatform.h"
#  include "mozilla/gfx/DeviceManagerDx.h"
#endif

namespace mozilla {
namespace gfx {

using namespace mozilla::layers;

static StaticAutoPtr<GPUProcessManager> sSingleton;

GPUProcessManager* GPUProcessManager::Get() { return sSingleton; }

void GPUProcessManager::Initialize() {
  MOZ_ASSERT(XRE_IsParentProcess());
  sSingleton = new GPUProcessManager();
}

void GPUProcessManager::Shutdown() {
  if (!sSingleton) {
    return;
  }
  sSingleton->ShutdownInternal();
  sSingleton = nullptr;
}

GPUProcessManager::GPUProcessManager()
    : mTaskFactory(this),
      mNextNamespace(0),
      mIdNamespace(0),
      mResourceId(0),
      mUnstableProcessAttempts(0),
      mTotalProcessAttempts(0),
      mDeviceResetCount(0),
      mAppInForeground(true),
      mProcess(nullptr),
      mProcessToken(0),
      mGPUChild(nullptr) {
  MOZ_COUNT_CTOR(GPUProcessManager);

  mIdNamespace = AllocateNamespace();

  mDeviceResetLastTime = TimeStamp::Now();

  LayerTreeOwnerTracker::Initialize();
  CompositorBridgeParent::InitializeStatics();
}

GPUProcessManager::~GPUProcessManager() {
  MOZ_COUNT_DTOR(GPUProcessManager);

  LayerTreeOwnerTracker::Shutdown();

  // The GPU process should have already been shut down.
  MOZ_ASSERT(!mProcess && !mGPUChild);

  // We should have already removed observers.
  MOZ_DIAGNOSTIC_ASSERT(!mObserver);
  MOZ_DIAGNOSTIC_ASSERT(!mBatteryObserver);
}

NS_IMPL_ISUPPORTS(GPUProcessManager::Observer, nsIObserver);

GPUProcessManager::Observer::Observer() {
  nsContentUtils::RegisterShutdownObserver(this);
  Preferences::AddStrongObserver(this, "");
  if (nsCOMPtr<nsIObserverService> obsServ = services::GetObserverService()) {
    obsServ->AddObserver(this, "application-foreground"false);
    obsServ->AddObserver(this, "application-background"false);
    obsServ->AddObserver(this, "screen-information-changed"false);
    obsServ->AddObserver(this, "xpcom-will-shutdown"false);
  }
}

void GPUProcessManager::Observer::Shutdown() {
  nsContentUtils::UnregisterShutdownObserver(this);
  Preferences::RemoveObserver(this, "");
  if (nsCOMPtr<nsIObserverService> obsServ = services::GetObserverService()) {
    obsServ->RemoveObserver(this, "application-foreground");
    obsServ->RemoveObserver(this, "application-background");
    obsServ->RemoveObserver(this, "screen-information-changed");
    obsServ->RemoveObserver(this, "xpcom-will-shutdown");
  }
}

NS_IMETHODIMP
GPUProcessManager::Observer::Observe(nsISupports* aSubject, const char* aTopic,
                                     const char16_t* aData) {
  if (auto* gpm = GPUProcessManager::Get()) {
    gpm->NotifyObserve(aTopic, aData);
  }
  return NS_OK;
}

void GPUProcessManager::NotifyObserve(const char* aTopic,
                                      const char16_t* aData) {
  if (!strcmp(aTopic, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID)) {
    StopBatteryObserving();
  } else if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
    ShutdownInternal();
  } else if (!strcmp(aTopic, "nsPref:changed")) {
    OnPreferenceChange(aData);
  } else if (!strcmp(aTopic, "application-foreground")) {
    SetAppInForeground(true);
  } else if (!strcmp(aTopic, "application-background")) {
    SetAppInForeground(false);
  } else if (!strcmp(aTopic, "screen-information-changed")) {
    ScreenInformationChanged();
  }
}

GPUProcessManager::BatteryObserver::BatteryObserver() {
  hal::RegisterBatteryObserver(this);
}

void GPUProcessManager::BatteryObserver::Notify(
    const hal::BatteryInformation& aBatteryInfo) {
  if (auto* gpm = GPUProcessManager::Get()) {
    gpm->NotifyBatteryInfo(aBatteryInfo);
  }
}

void GPUProcessManager::BatteryObserver::Shutdown() {
  hal::UnregisterBatteryObserver(this);
}

void GPUProcessManager::OnPreferenceChange(const char16_t* aData) {
  if (!mGPUChild && !IsGPUProcessLaunching()) {
    return;
  }

  // We know prefs are ASCII here.
  NS_LossyConvertUTF16toASCII strData(aData);

  mozilla::dom::Pref pref(strData, /* isLocked */ false,
                          /* isSanitized */ false, Nothing(), Nothing());

  Preferences::GetPreference(&pref, GeckoProcessType_GPU,
                             /* remoteType */ ""_ns);
  if (mGPUChild) {
    MOZ_ASSERT(mQueuedPrefs.IsEmpty());
    mGPUChild->SendPreferenceUpdate(pref);
  } else {
    mQueuedPrefs.AppendElement(pref);
  }
}

void GPUProcessManager::ScreenInformationChanged() {
#if defined(XP_WIN)
  if (!!mGPUChild) {
    mGPUChild->SendScreenInformationChanged();
  }
#endif
}

void GPUProcessManager::NotifyBatteryInfo(
    const hal::BatteryInformation& aBatteryInfo) {
  if (mGPUChild) {
    mGPUChild->SendNotifyBatteryInfo(aBatteryInfo);
  }
}

void GPUProcessManager::MaybeCrashIfGpuProcessOnceStable() {
  if (StaticPrefs::layers_gpu_process_allow_fallback_to_parent_AtStartup()) {
    return;
  }
  MOZ_RELEASE_ASSERT(!gfxConfig::IsEnabled(Feature::GPU_PROCESS));
  if (!mProcessStableOnce) {
    return;
  }
  // If the last launch error was suspected to be an OOM failure, let's annotate
  // this as an OOM crash.
  if (mProcess && mProcess->IsLaunchOomError()) {
    CrashReporter::AnnotateOOMAllocationSize(1);
  }
  MOZ_CRASH("Fallback to parent process not allowed!");
}

void GPUProcessManager::ResetProcessStable() {
  mTotalProcessAttempts++;
  mProcessStable = false;
  mProcessAttemptLastTime = TimeStamp::Now();
}

bool GPUProcessManager::IsProcessStable(const TimeStamp& aNow) {
  if (mTotalProcessAttempts > 0) {
    auto delta = (int32_t)(aNow - mProcessAttemptLastTime).ToMilliseconds();
    if (delta < StaticPrefs::layers_gpu_process_stable_min_uptime_ms()) {
      return false;
    }
  }
  return mProcessStable;
}

nsresult GPUProcessManager::LaunchGPUProcess() {
  if (mProcess) {
    return NS_OK;
  }

  if (AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMShutdown)) {
    return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
  }

  // Start listening for pref changes so we can
  // forward them to the process once it is running.
  if (!mObserver) {
    mObserver = new Observer();
  }

  // Start the Vsync I/O thread so can use it as soon as the process launches.
  EnsureVsyncIOThread();

  mTotalProcessAttempts++;
  mozilla::glean::gpu_process::total_launch_attempts.Set(mTotalProcessAttempts);
  mProcessAttemptLastTime = TimeStamp::Now();
  mProcessStable = false;

  geckoargs::ChildProcessArgs extraArgs;
  ipc::ProcessChild::AddPlatformBuildID(extraArgs);

  // The subprocess is launched asynchronously, so we wait for a callback to
  // acquire the IPDL actor.
  mProcess = new GPUProcessHost(this);
  if (!mProcess->Launch(std::move(extraArgs))) {
    DisableGPUProcess("Failed to launch GPU process");
    return NS_ERROR_FAILURE;
  }

  return NS_OK;
}

bool GPUProcessManager::IsGPUProcessLaunching() {
  MOZ_ASSERT(NS_IsMainThread());
  return !!mProcess && !mGPUChild;
}

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 true;
  }

  bool wantRestart;
  {
    // Collect the gfxVar updates into a single message.
    gfxVarsCollectUpdates collect;

    if (!aAllowRestart) {
      gfxConfig::SetFailed(Feature::GPU_PROCESS, FeatureStatus::Failed,
                           aMessage);
      gfxVars::SetGPUProcessEnabled(false);
    }

    if (mLastError) {
      wantRestart =
          FallbackFromAcceleration(mLastError.value(), mLastErrorMsg.ref());
      mLastError.reset();
      mLastErrorMsg.reset();
    } else {
      wantRestart = gfxPlatform::FallbackFromAcceleration(
          FeatureStatus::Unavailable, aMessage,
          "FEATURE_FAILURE_GPU_PROCESS_ERROR"_ns);
    }
    if (aAllowRestart && wantRestart) {
      // The fallback method can make use of the GPU process.
      return false;
    }

    if (aAllowRestart) {
      gfxConfig::SetFailed(Feature::GPU_PROCESS, FeatureStatus::Failed,
                           aMessage);
      gfxVars::SetGPUProcessEnabled(false);
    }

    MOZ_ASSERT(!gfxConfig::IsEnabled(Feature::GPU_PROCESS));

    gfxCriticalNote << aMessage;

    gfxPlatform::DisableGPUProcess();

    MaybeCrashIfGpuProcessOnceStable();
  }

  mozilla::glean::gpu_process::feature_status.Set(
      gfxConfig::GetFeature(Feature::GPU_PROCESS)
          .GetStatusAndFailureIdString());

  mozilla::glean::gpu_process::crash_fallbacks.Get("disabled"_ns).Add(1);

  DestroyProcess();
  ShutdownVsyncIOThread();

  // Now the stability state is based upon the in process compositor session.
  ResetProcessStable();

  // We may have been in the middle of guaranteeing our various services are
  // available when one failed. Some callers may fallback to using the same
  // process equivalent, and we need to make sure those services are setup
  // correctly. We cannot re-enter DisableGPUProcess from this call because we
  // know that it is disabled in the config above.
  if (NS_WARN_IF(NS_FAILED(EnsureGPUReady()))) {
    MOZ_ASSERT(AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMShutdown));
  } else {
    DebugOnly<bool> ready = EnsureProtocolsReady();
    MOZ_ASSERT(ready);
  }

  // If we disable the GPU process during reinitialization after a previous
  // crash, then we need to tell the content processes again, because they
  // need to rebind to the UI process.
  HandleProcessLost();
  return true;
}

bool GPUProcessManager::IsGPUReady() const {
  // If we have disabled the GPU process, then we know we are always ready.
  if (!gfxConfig::IsEnabled(Feature::GPU_PROCESS)) {
    MOZ_ASSERT(!mProcess);
    MOZ_ASSERT(!mGPUChild);
    return true;
  }

  // If we have a GPUChild, then we know the process has finished launching.
  if (mGPUChild) {
    return mGPUChild->IsGPUReady();
  }

  return false;
}

nsresult GPUProcessManager::EnsureGPUReady() {
  MOZ_ASSERT(NS_IsMainThread());

  // Common case is we already have a GPU process.
  if (mProcess && mProcess->IsConnected() && mGPUChild) {
    MOZ_DIAGNOSTIC_ASSERT(mGPUChild->IsGPUReady());
    return NS_OK;
  }

  // Next most common case is that we are compositing in the parent process.
  if (!gfxConfig::IsEnabled(Feature::GPU_PROCESS)) {
    MOZ_DIAGNOSTIC_ASSERT(!mProcess);
    MOZ_DIAGNOSTIC_ASSERT(!mGPUChild);
    return NS_OK;
  }

  // We aren't ready and in shutdown, we should just abort.
  if (AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMShutdown)) {
    return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
  }

  while (true) {
    // We allow the GPU process to launch if we are:
    // 1) in the foreground, as the application is being actively used.
    // 2) if we have no launch failures, because even if we are backgrounded, we
    //    can try once to secure it. This is useful for geckoview-junit tests.
    // 3) if our pref is set to allow background launches; this is false by
    //    default on Android due to its issues with keeping the GPU process
    //    alive in the background, and true on all other platforms.
    //
    // If we are not in a position to try launching and/or waiting for the GPU
    // process, then we should just abort for now. The higher levels will fail
    // to create the content process, but all of this should get recreated when
    // the app comes back into the foreground.
    if (!mAppInForeground && mLaunchProcessAttempts > 0 &&
        !StaticPrefs::layers_gpu_process_launch_in_background()) {
      return NS_ERROR_ABORT;
    }

    // Launch the GPU process if it is enabled but hasn't been (re-)launched
    // yet, and wait for it to complete the handshake. As part of WaitForLaunch,
    // we know that OnProcessLaunchComplete has been called. If it succeeds,
    // we know that mGPUChild has been set and we already waited for it to be
    // ready. If it fails, then we know that the GPU process must have been
    // destroyed and/or disabled.
    nsresult rv = LaunchGPUProcess();
    if (NS_SUCCEEDED(rv) && mProcess->WaitForLaunch() && mGPUChild) {
      MOZ_DIAGNOSTIC_ASSERT(mGPUChild->IsGPUReady());
      break;
    }

    MOZ_RELEASE_ASSERT(rv != NS_ERROR_ILLEGAL_DURING_SHUTDOWN);
    MOZ_RELEASE_ASSERT(!mProcess);
    MOZ_RELEASE_ASSERT(!mGPUChild);
    MOZ_DIAGNOSTIC_ASSERT(mLaunchProcessAttempts > 0);

    if (!gfxConfig::IsEnabled(Feature::GPU_PROCESS)) {
      break;
    }
  }

  return NS_OK;
}

bool GPUProcessManager::EnsureProtocolsReady() {
  return EnsureCompositorManagerChild() && EnsureImageBridgeChild() &&
         EnsureVRManager();
}

bool GPUProcessManager::EnsureCompositorManagerChild() {
  MOZ_DIAGNOSTIC_ASSERT(IsGPUReady());

  if (CompositorManagerChild::IsInitialized(mProcessToken)) {
    return true;
  }

  if (!mGPUChild) {
    CompositorManagerChild::InitSameProcess(AllocateNamespace(), mProcessToken);
    return true;
  }

  ipc::Endpoint<PCompositorManagerParent> parentPipe;
  ipc::Endpoint<PCompositorManagerChild> childPipe;
  nsresult rv = PCompositorManager::CreateEndpoints(
      mGPUChild->OtherEndpointProcInfo(), ipc::EndpointProcInfo::Current(),
      &parentPipe, &childPipe);
  if (NS_FAILED(rv)) {
    DisableGPUProcess("Failed to create PCompositorManager endpoints");
    return true;
  }

  uint32_t cmNamespace = AllocateNamespace();
  mGPUChild->SendInitCompositorManager(std::move(parentPipe), cmNamespace);
  CompositorManagerChild::Init(std::move(childPipe), cmNamespace,
                               mProcessToken);
  return true;
}

bool GPUProcessManager::EnsureImageBridgeChild() {
  MOZ_DIAGNOSTIC_ASSERT(IsGPUReady());

  if (ImageBridgeChild::GetSingleton()) {
    return true;
  }

  if (!mGPUChild) {
    ImageBridgeChild::InitSameProcess(AllocateNamespace());
    return true;
  }

  ipc::Endpoint<PImageBridgeParent> parentPipe;
  ipc::Endpoint<PImageBridgeChild> childPipe;
  nsresult rv = PImageBridge::CreateEndpoints(
      mGPUChild->OtherEndpointProcInfo(), ipc::EndpointProcInfo::Current(),
      &parentPipe, &childPipe);
  if (NS_FAILED(rv)) {
    DisableGPUProcess("Failed to create PImageBridge endpoints");
    return true;
  }

  uint32_t ibNamespace = AllocateNamespace();
  mGPUChild->SendInitImageBridge(std::move(parentPipe), ibNamespace);
  ImageBridgeChild::InitWithGPUProcess(std::move(childPipe), ibNamespace);
  return true;
}

bool GPUProcessManager::EnsureVRManager() {
  MOZ_DIAGNOSTIC_ASSERT(IsGPUReady());

  if (VRManagerChild::IsCreated()) {
    return true;
  }

  if (!mGPUChild) {
    VRManagerChild::InitSameProcess(AllocateNamespace());
    return true;
  }

  ipc::Endpoint<PVRManagerParent> parentPipe;
  ipc::Endpoint<PVRManagerChild> childPipe;
  nsresult rv = PVRManager::CreateEndpoints(mGPUChild->OtherEndpointProcInfo(),
                                            ipc::EndpointProcInfo::Current(),
                                            &parentPipe, &childPipe);
  if (NS_FAILED(rv)) {
    DisableGPUProcess("Failed to create PVRManager endpoints");
    return true;
  }

  uint32_t vrNamespace = AllocateNamespace();
  mGPUChild->SendInitVRManager(std::move(parentPipe), vrNamespace);
  VRManagerChild::InitWithGPUProcess(std::move(childPipe), vrNamespace);
  return true;
}

#if defined(MOZ_WIDGET_ANDROID)
RefPtr<UiCompositorControllerChild>
GPUProcessManager::CreateUiCompositorController(nsIWidget* aWidget,
                                                const LayersId aId) {
  MOZ_DIAGNOSTIC_ASSERT(IsGPUReady());

  if (!mGPUChild) {
    return UiCompositorControllerChild::CreateForSameProcess(aId, aWidget);
  }

  ipc::Endpoint<PUiCompositorControllerParent> parentPipe;
  ipc::Endpoint<PUiCompositorControllerChild> childPipe;
  nsresult rv = PUiCompositorController::CreateEndpoints(
      mGPUChild->OtherEndpointProcInfo(), ipc::EndpointProcInfo::Current(),
      &parentPipe, &childPipe);
  if (NS_FAILED(rv)) {
    DisableGPUProcess("Failed to create PUiCompositorController endpoints");
    return nullptr;
  }

  mGPUChild->SendInitUiCompositorController(aId, std::move(parentPipe));
  RefPtr<UiCompositorControllerChild> result =
      UiCompositorControllerChild::CreateForGPUProcess(
          mProcessToken, std::move(childPipe), aWidget);

  if (result) {
    result->SetCompositorSurfaceManager(
        mProcess->GetCompositorSurfaceManager());
  }
  return result;
}
#endif  // defined(MOZ_WIDGET_ANDROID)

void GPUProcessManager::OnProcessLaunchComplete(GPUProcessHost* aHost) {
  MOZ_ASSERT(mProcess && mProcess == aHost);

  // By definition, the process failing to launch is an unstable attempt. While
  // we did not get to the point where we are using the features, we should just
  // follow the same fallback procedure.
  auto* gpuChild = mProcess->GetActor();
  if (NS_WARN_IF(!mProcess->IsConnected()) || NS_WARN_IF(!gpuChild) ||
      NS_WARN_IF(!gpuChild->EnsureGPUReady())) {
    ++mLaunchProcessAttempts;
    if (mLaunchProcessAttempts >
        uint32_t(StaticPrefs::layers_gpu_process_max_launch_attempts())) {
      char disableMessage[64];
      SprintfLiteral(disableMessage,
                     "Failed to launch GPU process after %d attempts",
                     mLaunchProcessAttempts);
      DisableGPUProcess(disableMessage);
    } else {
      DestroyProcess(/* aUnexpectedShutdown */ true);
    }
    return;
  }

  mLaunchProcessAttempts = 0;
  mGPUChild = gpuChild;
  mProcessToken = mProcess->GetProcessToken();
#if defined(XP_WIN)
  if (mAppInForeground) {
    SetProcessIsForeground();
  }
#endif

  // Set a high priority for the newly-created gpu process.
  int pID = mProcess->GetChildProcessId();
  hal::SetProcessPriority(pID, hal::PROCESS_PRIORITY_FOREGROUND_HIGH);

  ipc::Endpoint<PVsyncBridgeParent> vsyncParent;
  ipc::Endpoint<PVsyncBridgeChild> vsyncChild;
  nsresult rv = PVsyncBridge::CreateEndpoints(
      mGPUChild->OtherEndpointProcInfo(), ipc::EndpointProcInfo::Current(),
      &vsyncParent, &vsyncChild);
  if (NS_FAILED(rv)) {
    DisableGPUProcess("Failed to create PVsyncBridge endpoints");
    return;
  }

  mVsyncBridge = VsyncBridgeChild::Create(mVsyncIOThread, mProcessToken,
                                          std::move(vsyncChild));
  mGPUChild->SendInitVsyncBridge(std::move(vsyncParent));

  MOZ_DIAGNOSTIC_ASSERT(!mBatteryObserver);
  if (!AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMWillShutdown)) {
    mBatteryObserver = new BatteryObserver();
  }

  // Flush any pref updates that happened during launch and weren't
  // included in the blobs set up in LaunchGPUProcess.
  for (const mozilla::dom::Pref& pref : mQueuedPrefs) {
    (void)NS_WARN_IF(!mGPUChild->SendPreferenceUpdate(pref));
  }
  mQueuedPrefs.Clear();

  CrashReporter::RecordAnnotationCString(
      CrashReporter::Annotation::GPUProcessStatus, "Running");

  CrashReporter::RecordAnnotationU32(
      CrashReporter::Annotation::GPUProcessLaunchCount, mTotalProcessAttempts);

  ReinitializeRendering();
}

void GPUProcessManager::OnProcessDeclaredStable() { mProcessStable = true; }

static bool ShouldLimitDeviceResets(uint32_t count, int32_t deltaMilliseconds) {
  // We decide to limit by comparing the amount of resets that have happened
  // and time since the last reset to two prefs.
  int32_t timeLimit = StaticPrefs::gfx_device_reset_threshold_ms_AtStartup();
  int32_t countLimit = StaticPrefs::gfx_device_reset_limit_AtStartup();

  bool hasTimeLimit = timeLimit >= 0;
  bool hasCountLimit = countLimit >= 0;

  bool triggeredTime = deltaMilliseconds < timeLimit;
  bool triggeredCount = count > (uint32_t)countLimit;

  // If we have both prefs set then it needs to trigger both limits,
  // otherwise we only test the pref that is set or none
  if (hasTimeLimit && hasCountLimit) {
    return triggeredTime && triggeredCount;
  } else if (hasTimeLimit) {
    return triggeredTime;
  } else if (hasCountLimit) {
    return triggeredCount;
  }

  return false;
}

void GPUProcessManager::ResetCompositors() {
  // Note: this will recreate devices in addition to recreating compositors.
  // This isn't optimal, but this is only used on linux where acceleration
  // isn't enabled by default, and this way we don't need a new code path.
  SimulateDeviceReset();
}

void GPUProcessManager::SimulateDeviceReset() {
  // Make sure we rebuild environment and configuration for accelerated
  // features.
  gfxPlatform::GetPlatform()->CompositorUpdated();

  if (mProcess) {
    if (mGPUChild) {
      mGPUChild->SendSimulateDeviceReset();
    }
  } else {
    wr::RenderThread::Get()->SimulateDeviceReset();
  }
}

bool GPUProcessManager::FallbackFromAcceleration(wr::WebRenderError aError,
                                                 const nsCString& aMsg) {
  if (aError == wr::WebRenderError::INITIALIZE) {
    return gfxPlatform::FallbackFromAcceleration(
        gfx::FeatureStatus::Unavailable, "WebRender initialization failed",
        aMsg);
  } else if (aError == wr::WebRenderError::MAKE_CURRENT) {
    return gfxPlatform::FallbackFromAcceleration(
        gfx::FeatureStatus::Unavailable,
        "Failed to make render context current",
        "FEATURE_FAILURE_WEBRENDER_MAKE_CURRENT"_ns);
  } else if (aError == wr::WebRenderError::RENDER) {
    return gfxPlatform::FallbackFromAcceleration(
        gfx::FeatureStatus::Unavailable, "Failed to render WebRender",
        "FEATURE_FAILURE_WEBRENDER_RENDER"_ns);
  } else if (aError == wr::WebRenderError::NEW_SURFACE) {
    // If we cannot create a new Surface even in the final fallback
    // configuration then force a crash.
    return gfxPlatform::FallbackFromAcceleration(
        gfx::FeatureStatus::Unavailable, "Failed to create new surface",
        "FEATURE_FAILURE_WEBRENDER_NEW_SURFACE"_ns,
        /* aCrashAfterFinalFallback */ true);
  } else if (aError == wr::WebRenderError::BEGIN_DRAW) {
    return gfxPlatform::FallbackFromAcceleration(
        gfx::FeatureStatus::Unavailable, "BeginDraw() failed",
        "FEATURE_FAILURE_WEBRENDER_BEGIN_DRAW"_ns);
  } else if (aError == wr::WebRenderError::EXCESSIVE_RESETS) {
    return gfxPlatform::FallbackFromAcceleration(
        gfx::FeatureStatus::Unavailable, "Device resets exceeded threshold",
        "FEATURE_FAILURE_WEBRENDER_EXCESSIVE_RESETS"_ns);
  } else {
    MOZ_ASSERT_UNREACHABLE("Invalid value");
    return gfxPlatform::FallbackFromAcceleration(
        gfx::FeatureStatus::Unavailable, "Unhandled failure reason",
        "FEATURE_FAILURE_WEBRENDER_UNHANDLED"_ns);
  }
}

void GPUProcessManager::DisableWebRenderConfig(wr::WebRenderError aError,
                                               const nsCString& aMsg) {
  // Clear out any cached errors from a remote device reset.
  mLastError.reset();
  mLastErrorMsg.reset();

  bool wantRestart;
  {
    // Collect the gfxVar updates into a single message.
    gfxVarsCollectUpdates collect;

    // Disable WebRender
    wantRestart = FallbackFromAcceleration(aError, aMsg);
    gfxVars::SetUseWebRenderDCompVideoHwOverlayWin(false);
    gfxVars::SetUseWebRenderDCompVideoSwOverlayWin(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. Because we
  // updated the gfxVars, we call GPUChild::EnsureGPUReady to force us to wait
  // for the update to be processed before creating new compositor sessions.
  // Otherwise we risk them being out of sync with the content/parent processes.
  if (wantRestart && mProcess && mGPUChild) {
    mUnstableProcessAttempts = 1;
    mGPUChild->EnsureGPUReady(/* aForceSync */ true);
  }
}

void GPUProcessManager::DisableWebRender(wr::WebRenderError aError,
                                         const nsCString& aMsg) {
  DisableWebRenderConfig(aError, aMsg);
  if (mProcess) {
    DestroyRemoteCompositorSessions();
  } else {
    DestroyInProcessCompositorSessions();
  }
  NotifyListenersOnCompositeDeviceReset();
}

void GPUProcessManager::NotifyWebRenderError(wr::WebRenderError aError) {
  gfxCriticalNote << "Handling webrender error " << (unsigned int)aError;
#ifdef XP_WIN
  if (aError == wr::WebRenderError::VIDEO_OVERLAY) {
    gfxVarsCollectUpdates collect;
    gfxVars::SetUseWebRenderDCompVideoHwOverlayWin(false);
    gfxVars::SetUseWebRenderDCompVideoSwOverlayWin(false);
    return;
  }
  if (aError == wr::WebRenderError::VIDEO_HW_OVERLAY) {
    gfxVars::SetUseWebRenderDCompVideoHwOverlayWin(false);
    return;
  }
  if (aError == wr::WebRenderError::VIDEO_SW_OVERLAY) {
    gfxVars::SetUseWebRenderDCompVideoSwOverlayWin(false);
    return;
  }
  if (aError == wr::WebRenderError::DCOMP_TEXTURE_OVERLAY) {
    gfxVars::SetUseWebRenderDCompositionTextureOverlayWin(false);
    return;
  }
#else
  if (aError == wr::WebRenderError::VIDEO_OVERLAY ||
      aError == wr::WebRenderError::VIDEO_HW_OVERLAY ||
      aError == wr::WebRenderError::VIDEO_SW_OVERLAY ||
      aError == wr::WebRenderError::DCOMP_TEXTURE_OVERLAY) {
    MOZ_ASSERT_UNREACHABLE("unexpected to be called");
    return;
  }
#endif

  // If we have a stable GPU process, this may just be due to an OOM or bad
  // driver state. In that case, we should consider restarting the GPU process
  // to hopefully alleviate the situation.
  if (mProcess && (IsProcessStable(TimeStamp::Now()) ||
                   (kIsAndroid && !mAppInForeground))) {
    mProcess->KillProcess(/* aGenerateMinidump */ false);
    mLastError = Some(aError);
    mLastErrorMsg = Some(""_ns);
    return;
  }

  DisableWebRender(aError, ""_ns);
}

/* static */
void GPUProcessManager::RecordDeviceReset(DeviceResetReason aReason) {
  if (aReason != DeviceResetReason::FORCED_RESET) {
    glean::gfx::device_reset_reason.AccumulateSingleSample(uint32_t(aReason));
  }

  CrashReporter::RecordAnnotationU32(
      CrashReporter::Annotation::DeviceResetReason,
      static_cast<uint32_t>(aReason));
}

/* static */
void GPUProcessManager::NotifyDeviceReset(DeviceResetReason aReason,
                                          DeviceResetDetectPlace aPlace) {
  if (!NS_IsMainThread()) {
    NS_DispatchToMainThread(NS_NewRunnableFunction(
        "gfx::GPUProcessManager::NotifyDeviceReset",
        [aReason, aPlace]() -> void {
          gfx::GPUProcessManager::NotifyDeviceReset(aReason, aPlace);
        }));
    return;
  }

#ifdef XP_WIN
  // Reset and reinitialize the compositor devices
  if (auto* deviceManager = DeviceManagerDx::Get()) {
    deviceManager->MaybeResetAndReacquireDevices();
  }
#else
  gfx::GPUProcessManager::RecordDeviceReset(aReason);
#endif

  if (XRE_IsGPUProcess()) {
    if (auto* gpuParent = GPUParent::GetSingleton()) {
      // End up to GPUProcessManager::OnRemoteProcessDeviceReset()
      gpuParent->NotifyDeviceReset(aReason, aPlace);
    } else {
      MOZ_ASSERT_UNREACHABLE("unexpected to be called");
    }
    return;
  }

  MOZ_ASSERT(XRE_IsParentProcess());
  if (auto* gpm = GPUProcessManager::Get()) {
    gpm->OnInProcessDeviceReset(aReason, aPlace);
  } else {
    MOZ_ASSERT_UNREACHABLE("unexpected to be called");
  }
}

bool GPUProcessManager::OnDeviceReset(bool aTrackThreshold) {
  // Ignore resets for thresholding if requested.
  if (!aTrackThreshold) {
    return false;
  }

  // Detect whether the device is resetting too quickly or too much
  // indicating that we should give up and use software
  mDeviceResetCount++;

  auto newTime = TimeStamp::Now();
  auto delta = (int32_t)(newTime - mDeviceResetLastTime).ToMilliseconds();
  mDeviceResetLastTime = newTime;

  // Returns true if we should disable acceleration due to the reset.
  return ShouldLimitDeviceResets(mDeviceResetCount, delta);
}

void GPUProcessManager::OnInProcessDeviceReset(DeviceResetReason aReason,
                                               DeviceResetDetectPlace aPlace) {
  gfxCriticalNote << "Detect DeviceReset " << aReason << " " << aPlace
                  << " in Parent process";

  bool guilty;
  switch (aReason) {
    case DeviceResetReason::HUNG:
    case DeviceResetReason::RESET:
    case DeviceResetReason::INVALID_CALL:
      guilty = true;
      break;
    default:
      guilty = false;
      break;
  }

  if (OnDeviceReset(guilty)) {
    gfxCriticalNoteOnce << "In-process device reset threshold exceeded";
#ifdef MOZ_WIDGET_GTK
    // FIXME(aosmond): Should we disable WebRender on other platforms?
    DisableWebRenderConfig(wr::WebRenderError::EXCESSIVE_RESETS, nsCString());
#endif
  }
#ifdef XP_WIN
  // Ensure device reset handling before re-creating in process sessions.
  // Normally nsWindow::OnPaint() already handled it.
  gfxWindowsPlatform::GetPlatform()->HandleDeviceReset();
#endif
  DestroyInProcessCompositorSessions();
  NotifyListenersOnCompositeDeviceReset();
}

void GPUProcessManager::OnRemoteProcessDeviceReset(
    GPUProcessHost* aHost, const DeviceResetReason& aReason,
    const DeviceResetDetectPlace& aPlace) {
  gfxCriticalNote << "Detect DeviceReset " << aReason << " " << aPlace
                  << " in GPU process";

  if (OnDeviceReset(/* aTrackThreshold */ true)) {
    // If we have a stable GPU process, this may just be due to an OOM or bad
    // driver state. In that case, we should consider restarting the GPU process
    // to hopefully alleviate the situation.
    if (mProcess && (IsProcessStable(TimeStamp::Now()) ||
                     (kIsAndroid && !mAppInForeground))) {
      mProcess->KillProcess(/* aGenerateMinidump */ false);
      mLastError = Some(wr::WebRenderError::EXCESSIVE_RESETS);
      mLastErrorMsg = Some(""_ns);
      return;
    }

    DisableWebRenderConfig(wr::WebRenderError::EXCESSIVE_RESETS, ""_ns);
  }

  DestroyRemoteCompositorSessions();
  NotifyListenersOnCompositeDeviceReset();
}

void GPUProcessManager::NotifyListenersOnCompositeDeviceReset() {
  nsTArray<RefPtr<GPUProcessListener>> listeners;
  listeners.AppendElements(mListeners);
  for (const auto& listener : listeners) {
    listener->OnCompositorDeviceReset();
  }
}

void GPUProcessManager::OnProcessUnexpectedShutdown(GPUProcessHost* aHost) {
  MOZ_ASSERT(mProcess && mProcess == aHost);

  if (StaticPrefs::layers_gpu_process_crash_also_crashes_browser()) {
    MOZ_CRASH("GPU process crashed and pref is set to crash the browser.");
  }

  CompositorManagerChild::OnGPUProcessLost(aHost->GetProcessToken());
  DestroyProcess(/* aUnexpectedShutdown */ true);

  // If the process didn't live long enough, increment our unstable attempts
  // counter so that we don't end up in a restart loop. If the process did live
  // long enough, reset the counter so that we don't disable the process too
  // eagerly.
  if (IsProcessStable(TimeStamp::Now())) {
    mProcessStableOnce = true;
    mUnstableProcessAttempts = 0;
  } else if (kIsAndroid && !mAppInForeground) {
    // On Android if the process is lost whilst in the background it was
    // probably killed by the OS, and it may never have had a chance to have
    // been declared stable prior to being killed. We don't want this happening
    // repeatedly to result in the GPU process being disabled, so treat any
    // process lost whilst in the background as stable.
    mUnstableProcessAttempts = 0;
  } else {
    mUnstableProcessAttempts++;
    mozilla::glean::gpu_process::unstable_launch_attempts.Set(
        mUnstableProcessAttempts);
  }

  if (mUnstableProcessAttempts >
      uint32_t(StaticPrefs::layers_gpu_process_max_restarts())) {
    char disableMessage[64];
    SprintfLiteral(disableMessage, "GPU process disabled after %d attempts",
                   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::
                              layers_gpu_process_max_restarts_with_decoder()) &&
             mDecodeVideoOnGpuProcess) {
    mDecodeVideoOnGpuProcess = false;
    mozilla::glean::gpu_process::crash_fallbacks.Get("decoding_disabled"_ns)
        .Add(1);
    HandleProcessLost();
  } else {
    mozilla::glean::gpu_process::crash_fallbacks.Get("none"_ns).Add(1);
    HandleProcessLost();
  }
}

void GPUProcessManager::HandleProcessLost() {
  MOZ_ASSERT(NS_IsMainThread());

  // The shutdown and restart sequence for the GPU process is as follows:
  //
  //  (1) The GPU process dies. IPDL will enqueue an ActorDestroy message on
  //      each channel owning a bridge to the GPU process, on the thread owning
  //      that channel.
  //
  //  (2) The first channel to process its ActorDestroy message will post a
  //      message to the main thread to call NotifyRemoteActorDestroyed on the
  //      GPUProcessManager, which calls OnProcessUnexpectedShutdown if it has
  //      not handled shutdown for this process yet. OnProcessUnexpectedShutdown
  //      is responsible for tearing down the old process and deciding whether
  //      or not to disable the GPU process. It then calls this function,
  //      HandleProcessLost.
  //
  //  (3) We then notify each widget that its session with the compositor is now
  //      invalid. The widget is responsible for destroying its layer manager
  //      and CompositorBridgeChild. Note that at this stage, not all actors may
  //      have received ActorDestroy yet. CompositorBridgeChild may attempt to
  //      send messages, and if this happens, it will probably report a
  //      MsgDropped error. This is okay.
  //
  //  (4) At this point, the UI process has a clean slate: no layers should
  //      exist for the old compositor. We may make a decision on whether or not
  //      to re-launch the GPU process. Or, on Android if the app is in the
  //      background we may decide to wait until it comes to the foreground
  //      before re-launching.
  //
  //  (5) When we do decide to re-launch, or continue without a GPU process, we
  //      notify each ContentParent of the lost connection. It will request new
  //      endpoints from the GPUProcessManager and forward them to its
  //      ContentChild. The parent-side of these endpoints may come from the
  //      compositor thread of the UI process, or the compositor thread of the
  //      GPU process. However, no actual compositors should exist yet.
  //
  //  (6) Each ContentChild will receive new endpoints. It will destroy its
  //      Compositor/ImageBridgeChild singletons and recreate them, as well
  //      as invalidate all retained layers.
  //
  //  (7) In addition, each ContentChild will ask each of its BrowserChildren
  //      to re-request association with the compositor for the window
  //      owning the tab. The sequence of calls looks like:
  //        (a) [CONTENT] ContentChild::RecvReinitRendering
  //        (b) [CONTENT] BrowserChild::ReinitRendering
  //        (c) [CONTENT] BrowserChild::SendEnsureLayersConnected
  //        (d)      [UI] BrowserParent::RecvEnsureLayersConnected
  //        (e)      [UI] RemoteLayerTreeOwner::EnsureLayersConnected
  //        (f)      [UI] CompositorBridgeChild::SendNotifyChildRecreated
  //
  //      Note that at step (e), RemoteLayerTreeOwner will call
  //      GetWindowRenderer on the nsIWidget owning the tab. This step ensures
  //      that a compositor exists for the window. If we decided to launch a new
  //      GPU Process, at this point we block until the process has launched and
  //      we're able to create a new window compositor. Otherwise, if
  //      compositing is now in-process, this will simply create a new
  //      CompositorBridgeParent in the UI process. If there are multiple tabs
  //      in the same window, additional tabs will simply return the already-
  //      established compositor.
  //
  //      Finally, this step serves one other crucial function: tabs must be
  //      associated with a window compositor or else they can't forward
  //      layer transactions. So this step both ensures that a compositor
  //      exists, and that the tab can forward layers.
  //
  //  (8) Last, if the window had no remote tabs, step (7) will not have
  //      applied, and the window will not have a new compositor just yet. The
  //      next refresh tick and paint will ensure that one exists, again via
  //      nsIWidget::GetWindowRenderer. On Android, we called
  //      nsIWidgetListener::RequestRepaint back in step (3) to ensure this
  //      tick occurs, but on other platforms this is not necessary.

  DestroyRemoteCompositorSessions();

#ifdef MOZ_WIDGET_ANDROID
  java::SurfaceControlManager::GetInstance()->OnGpuProcessLoss();
#endif

  // Re-launch the process if immediately if the GPU process is still enabled.
  // Except on Android if the app is in the background, where we want to wait
  // until the app is in the foreground again.
  if (gfxConfig::IsEnabled(Feature::GPU_PROCESS)) {
#ifdef MOZ_WIDGET_ANDROID
    if (mAppInForeground) {
#else
    {
#endif
      (void)LaunchGPUProcess();
    }
  } else {
    // If the GPU process is disabled we can reinitialize rendering immediately.
    // This will be handled in OnProcessLaunchComplete() if the GPU process is
    // enabled.
    ReinitializeRendering();
  }
}

void GPUProcessManager::ReinitializeRendering() {
  // Notify content. This will ensure that each content process re-establishes
  // a connection to the compositor thread (whether it's in-process or in a
  // newly launched GPU process).
  nsTArray<RefPtr<GPUProcessListener>> listeners;
  listeners.AppendElements(mListeners);
  // Make sure any fallback renderers get destroyed first.
  for (const auto& listener : listeners) {
    listener->OnCompositorDestroyBackgrounded();
  }
  // Then do the recreations.
  for (const auto& listener : listeners) {
    listener->OnCompositorUnexpectedShutdown();
  }

  // Notify any observers that the compositor has been reinitialized,
  // eg the ZoomConstraintsClients for parent process documents.
  nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
  if (observerService) {
    observerService->NotifyObservers(nullptr, "compositor-reinitialized",
                                     nullptr);
  }
}

void GPUProcessManager::DestroyRemoteCompositorSessions() {
  // Build a list of sessions to notify, since notification might delete
  // entries from the list.
  nsTArray<RefPtr<RemoteCompositorSession>> sessions;
  for (auto& session : mRemoteSessions) {
    sessions.AppendElement(session);
  }

  // Notify each widget that we have lost the GPU process. This will ensure
  // that each widget destroys its layer manager and CompositorBridgeChild.
  for (const auto& session : sessions) {
    session->NotifySessionLost();
  }
}

void GPUProcessManager::DestroyInProcessCompositorSessions() {
  // Build a list of sessions to notify, since notification might delete
  // entries from the list.
  nsTArray<RefPtr<InProcessCompositorSession>> sessions;
  for (auto& session : mInProcessSessions) {
    sessions.AppendElement(session);
  }

  // Notify each widget that we have lost the GPU process. This will ensure
  // that each widget destroys its layer manager and CompositorBridgeChild.
  for (const auto& session : sessions) {
    session->NotifySessionLost();
  }

  // Ensure our stablility state is reset so that we don't necessarily crash
  // right away on some WebRender errors.
  CompositorBridgeParent::ResetStable();
  ResetProcessStable();
}

void GPUProcessManager::NotifyRemoteActorDestroyed(
    const uint64_t& aProcessToken) {
  if (!NS_IsMainThread()) {
    RefPtr<Runnable> task = mTaskFactory.NewRunnableMethod(
        &GPUProcessManager::NotifyRemoteActorDestroyed, aProcessToken);
    NS_DispatchToMainThread(task.forget());
    return;
  }

  if (mProcessToken != aProcessToken) {
    // This token is for an older process; we can safely ignore it.
    return;
  }

  // One of the bridged top-level actors for the GPU process has been
  // prematurely terminated, and we're receiving a notification. This
  // can happen if the ActorDestroy for a bridged protocol fires
  // before the ActorDestroy for PGPUChild.
  OnProcessUnexpectedShutdown(mProcess);
}

void GPUProcessManager::ShutdownInternal() {
  if (mObserver) {
    mObserver->Shutdown();
    mObserver = nullptr;
  }

  DestroyProcess();
  mVsyncIOThread = nullptr;
}

void GPUProcessManager::KillProcess(bool aGenerateMinidump) {
  if (!NS_IsMainThread()) {
    RefPtr<Runnable> task = mTaskFactory.NewRunnableMethod(
        &GPUProcessManager::KillProcess, aGenerateMinidump);
    NS_DispatchToMainThread(task.forget());
    return;
  }

  if (!mProcess) {
    return;
  }

  mProcess->KillProcess(aGenerateMinidump);
}

void GPUProcessManager::CrashProcess() {
  if (!mProcess) {
    return;
  }

  mProcess->CrashProcess();
}

void GPUProcessManager::DestroyProcess(bool aUnexpectedShutdown) {
  if (!mProcess) {
    return;
  }

  mProcess->Shutdown(aUnexpectedShutdown);
  mProcessToken = 0;
  mProcess = nullptr;
  mGPUChild = nullptr;
  mQueuedPrefs.Clear();
  if (mVsyncBridge) {
    mVsyncBridge->Close();
    mVsyncBridge = nullptr;
  }
  StopBatteryObserving();

  CrashReporter::RecordAnnotationCString(
      CrashReporter::Annotation::GPUProcessStatus, "Destroyed");
}

void GPUProcessManager::StopBatteryObserving() {
  if (mBatteryObserver) {
    mBatteryObserver->Shutdown();
    mBatteryObserver = nullptr;
  }
}

already_AddRefed<CompositorSession> GPUProcessManager::CreateTopLevelCompositor(
    nsIWidget* aWidget, CSSToLayoutDeviceScale aScale,
    const CompositorOptions& aOptions, bool aUseExternalSurfaceSize,
    const gfx::IntSize& aSurfaceSize, uint64_t aInnerWindowId,
    bool* aRetryOut) {
  MOZ_DIAGNOSTIC_ASSERT(IsGPUReady());
  MOZ_ASSERT(aRetryOut);

  if (!EnsureProtocolsReady()) {
    *aRetryOut = false;
    return nullptr;
  }

  LayersId layerTreeId = AllocateLayerTreeId();
  RefPtr<CompositorSession> session;
  if (mGPUChild) {
    session = CreateRemoteSession(aWidget, layerTreeId, aScale, aOptions,
                                  aUseExternalSurfaceSize, aSurfaceSize,
                                  aInnerWindowId);
    if (NS_WARN_IF(!session)) {
      // This may have failed for intermittent reasons, or perhaps indicates we
      // are fundamentally unable to use acceleration.
      // OnProcessUnexpectedShutdown will first attempt to relaunch the GPU
      // process in the same configuration a number of times, then fallback from
      // acceleration, then finally disable the GPU process if it continues to
      // fail.
      OnProcessUnexpectedShutdown(mProcess);
      *aRetryOut = true;
      return nullptr;
    }
  } else {
    session = InProcessCompositorSession::Create(
        aWidget, layerTreeId, aScale, aOptions, aUseExternalSurfaceSize,
        aSurfaceSize, AllocateNamespace(), aInnerWindowId);
  }

#if defined(MOZ_WIDGET_ANDROID)
  if (session) {
    // Nothing to do if controller gets a nullptr
    auto controller =
        CreateUiCompositorController(aWidget, session->RootLayerTreeId());
    MOZ_ASSERT(controller);
    session->SetUiCompositorControllerChild(std::move(controller));
  }
#endif  // defined(MOZ_WIDGET_ANDROID)

  *aRetryOut = false;
  return session.forget();
}

RefPtr<CompositorSession> GPUProcessManager::CreateRemoteSession(
    nsIWidget* aWidget, const LayersId& aRootLayerTreeId,
    CSSToLayoutDeviceScale aScale, const CompositorOptions& aOptions,
    bool aUseExternalSurfaceSize, const gfx::IntSize& aSurfaceSize,
    uint64_t aInnerWindowId) {
#ifdef MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING
  widget::CompositorWidgetInitData initData;
  aWidget->GetCompositorWidgetInitData(&initData);

  RefPtr<CompositorBridgeChild> child =
      CompositorManagerChild::CreateWidgetCompositorBridge(
          mProcessToken, AllocateNamespace(), aScale, aOptions,
          aUseExternalSurfaceSize, aSurfaceSize, aInnerWindowId);
  if (!child) {
    gfxCriticalNote << "Failed to create CompositorBridgeChild";
    return nullptr;
  }

  RefPtr<CompositorVsyncDispatcher> dispatcher =
      aWidget->GetCompositorVsyncDispatcher();
  RefPtr<widget::CompositorWidgetVsyncObserver> observer =
      new widget::CompositorWidgetVsyncObserver(mVsyncBridge, aRootLayerTreeId);

  widget::CompositorWidgetChild* widget =
      new widget::CompositorWidgetChild(dispatcher, observer, initData);
  if (!child->SendPCompositorWidgetConstructor(widget, std::move(initData))) {
    return nullptr;
  }
  if (!widget->Initialize(aOptions)) {
    return nullptr;
  }
  if (!child->SendInitialize(aRootLayerTreeId)) {
    return nullptr;
  }

  RefPtr<APZCTreeManagerChild> apz = nullptr;
  if (aOptions.UseAPZ()) {
    apz = MakeRefPtr<APZCTreeManagerChild>();
    if (!child->SendPAPZCTreeManagerConstructor(apz, LayersId{0})) {
      return nullptr;
    }

    ipc::Endpoint<PAPZInputBridgeParent> parentPipe;
    ipc::Endpoint<PAPZInputBridgeChild> childPipe;
    nsresult rv = PAPZInputBridge::CreateEndpoints(
        mGPUChild->OtherEndpointProcInfo(), ipc::EndpointProcInfo::Current(),
        &parentPipe, &childPipe);
    if (NS_FAILED(rv)) {
      return nullptr;
    }
    mGPUChild->SendInitAPZInputBridge(aRootLayerTreeId, std::move(parentPipe));

    RefPtr<APZInputBridgeChild> inputBridge =
        APZInputBridgeChild::Create(mProcessToken, std::move(childPipe));
    if (!inputBridge) {
      return nullptr;
    }

    apz->SetInputBridge(std::move(inputBridge));
  }

  return MakeRefPtr<RemoteCompositorSession>(aWidget, child, widget,
                                             std::move(apz), aRootLayerTreeId);
#else
  gfxCriticalNote << "Platform does not support out-of-process compositing";
  return nullptr;
#endif
}

bool GPUProcessManager::CreateContentBridges(
    ipc::EndpointProcInfo aOtherProcess,
    ipc::Endpoint<PCompositorManagerChild>* aOutCompositor,
    ipc::Endpoint<PImageBridgeChild>* aOutImageBridge,
    ipc::Endpoint<PVRManagerChild>* aOutVRBridge,
    ipc::Endpoint<PRemoteMediaManagerChild>* aOutVideoManager,
    dom::ContentParentId aChildId, nsTArray<uint32_t>* aNamespaces) {
  const uint32_t compositorManagerNamespace = AllocateNamespace();
  const uint32_t compositorBridgeNamespace = AllocateNamespace();
  const uint32_t imageBridgeNamespace = AllocateNamespace();
  const uint32_t vrManagerNamespace = AllocateNamespace();
  if (!CreateContentCompositorManager(aOtherProcess, aChildId,
                                      compositorManagerNamespace,
                                      aOutCompositor) ||
      !CreateContentImageBridge(aOtherProcess, aChildId, imageBridgeNamespace,
                                aOutImageBridge) ||
      !CreateContentVRManager(aOtherProcess, aChildId, vrManagerNamespace,
                              aOutVRBridge)) {
    return false;
  }
  // RemoteMediaManager is only supported in the GPU process, so we allow this
  // to be fallible.
  CreateContentRemoteMediaManager(aOtherProcess, aChildId, aOutVideoManager);

  // The order of the outparam namespaces must be kept in sync with
  // ContentChild::RecvInitRendering and ContentChild::RecvReinitRendering.
  aNamespaces->AppendElement(compositorManagerNamespace);
  aNamespaces->AppendElement(compositorBridgeNamespace);
  aNamespaces->AppendElement(imageBridgeNamespace);
  aNamespaces->AppendElement(vrManagerNamespace);
  return true;
}

bool GPUProcessManager::CreateContentCompositorManager(
    ipc::EndpointProcInfo aOtherProcess, dom::ContentParentId aChildId,
    uint32_t aNamespace, ipc::Endpoint<PCompositorManagerChild>* aOutEndpoint) {
  MOZ_DIAGNOSTIC_ASSERT(IsGPUReady());

  ipc::Endpoint<PCompositorManagerParent> parentPipe;
  ipc::Endpoint<PCompositorManagerChild> childPipe;

  ipc::EndpointProcInfo parentInfo = mGPUChild
                                         ? mGPUChild->OtherEndpointProcInfo()
                                         : ipc::EndpointProcInfo::Current();

  nsresult rv = PCompositorManager::CreateEndpoints(parentInfo, aOtherProcess,
                                                    &parentPipe, &childPipe);
  if (NS_FAILED(rv)) {
    gfxCriticalNote << "Could not create content compositor manager: "
                    << hexa(int(rv));
    return false;
  }

  if (mGPUChild) {
    mGPUChild->SendNewContentCompositorManager(std::move(parentPipe), aChildId,
                                               aNamespace);
  } else if (!CompositorManagerParent::Create(std::move(parentPipe), aChildId,
                                              aNamespace,
                                              /* aIsRoot */ false)) {
    return false;
  }

  *aOutEndpoint = std::move(childPipe);
  return true;
}

bool GPUProcessManager::CreateContentImageBridge(
    ipc::EndpointProcInfo aOtherProcess, dom::ContentParentId aChildId,
    uint32_t aNamespace, ipc::Endpoint<PImageBridgeChild>* aOutEndpoint) {
  MOZ_DIAGNOSTIC_ASSERT(IsGPUReady());

  if (!EnsureImageBridgeChild()) {
    return false;
  }

  ipc::EndpointProcInfo parentInfo = mGPUChild
                                         ? mGPUChild->OtherEndpointProcInfo()
                                         : ipc::EndpointProcInfo::Current();

  ipc::Endpoint<PImageBridgeParent> parentPipe;
  ipc::Endpoint<PImageBridgeChild> childPipe;
  nsresult rv = PImageBridge::CreateEndpoints(parentInfo, aOtherProcess,
                                              &parentPipe, &childPipe);
  if (NS_FAILED(rv)) {
    gfxCriticalNote << "Could not create content compositor bridge: "
                    << hexa(int(rv));
    return false;
  }

  if (mGPUChild) {
    mGPUChild->SendNewContentImageBridge(std::move(parentPipe), aChildId,
                                         aNamespace);
  } else {
    if (!ImageBridgeParent::CreateForContent(std::move(parentPipe), aChildId,
                                             aNamespace)) {
      return false;
    }
  }

  *aOutEndpoint = std::move(childPipe);
  return true;
}

base::ProcessId GPUProcessManager::GPUProcessPid() {
  base::ProcessId gpuPid =
      mGPUChild ? mGPUChild->OtherPid() : base::kInvalidProcessId;
  return gpuPid;
}

ipc::EndpointProcInfo GPUProcessManager::GPUEndpointProcInfo() {
  return mGPUChild ? mGPUChild->OtherEndpointProcInfo()
                   : ipc::EndpointProcInfo::Invalid();
}

bool GPUProcessManager::CreateContentVRManager(
    ipc::EndpointProcInfo aOtherProcess, dom::ContentParentId aChildId,
    uint32_t aNamespace, ipc::Endpoint<PVRManagerChild>* aOutEndpoint) {
  MOZ_DIAGNOSTIC_ASSERT(IsGPUReady());

  if (NS_WARN_IF(!EnsureVRManager())) {
    return false;
  }

  ipc::EndpointProcInfo parentInfo = mGPUChild
                                         ? mGPUChild->OtherEndpointProcInfo()
                                         : ipc::EndpointProcInfo::Current();

  ipc::Endpoint<PVRManagerParent> parentPipe;
  ipc::Endpoint<PVRManagerChild> childPipe;
  nsresult rv = PVRManager::CreateEndpoints(parentInfo, aOtherProcess,
                                            &parentPipe, &childPipe);
  if (NS_FAILED(rv)) {
    gfxCriticalNote << "Could not create content compositor bridge: "
                    << hexa(int(rv));
    return false;
  }

  if (mGPUChild) {
    mGPUChild->SendNewContentVRManager(std::move(parentPipe), aChildId,
                                       aNamespace);
  } else {
    if (!VRManagerParent::CreateForContent(std::move(parentPipe), aChildId,
                                           aNamespace)) {
      return false;
    }
  }

  *aOutEndpoint = std::move(childPipe);
  return true;
}

void GPUProcessManager::CreateContentRemoteMediaManager(
    ipc::EndpointProcInfo aOtherProcess, dom::ContentParentId aChildId,
    ipc::Endpoint<PRemoteMediaManagerChild>* aOutEndpoint) {
  MOZ_DIAGNOSTIC_ASSERT(IsGPUReady());

  if (!mGPUChild || !StaticPrefs::media_gpu_process_decoder() ||
      !mDecodeVideoOnGpuProcess) {
    return;
  }

  ipc::Endpoint<PRemoteMediaManagerParent> parentPipe;
  ipc::Endpoint<PRemoteMediaManagerChild> childPipe;

  nsresult rv = PRemoteMediaManager::CreateEndpoints(
      mGPUChild->OtherEndpointProcInfo(), aOtherProcess, &parentPipe,
      &childPipe);
  if (NS_FAILED(rv)) {
    gfxCriticalNote << "Could not create content video decoder: "
                    << hexa(int(rv));
    return;
  }

  mGPUChild->SendNewContentRemoteMediaManager(std::move(parentPipe), aChildId);

  *aOutEndpoint = std::move(childPipe);
}

#ifdef MOZ_WMF_MEDIA_ENGINE
nsresult GPUProcessManager::CreateUtilityMFCDMVideoBridge(
    mozilla::ipc::UtilityMediaServiceChild* aChild,
    mozilla::ipc::EndpointProcInfo aOtherProcess) {
  MOZ_ASSERT(aChild);
  MOZ_ASSERT(aChild->CanSend());

  ipc::Endpoint<PVideoBridgeChild> childPipe;
  nsresult rv = EnsureVideoBridge(VideoBridgeSource::MFMediaEngineCDMProcess,
                                  aOtherProcess, &childPipe);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  gfx::ContentDeviceData contentDeviceData;
  gfxPlatform::GetPlatform()->BuildContentDeviceData(&contentDeviceData);
  aChild->SendInitVideoBridge(std::move(childPipe), contentDeviceData);
  return NS_OK;
}
#endif

nsresult GPUProcessManager::CreateRddVideoBridge(RDDProcessManager* aRDD,
                                                 RDDChild* aChild) {
  MOZ_ASSERT(aRDD);
  MOZ_ASSERT(aChild);
  MOZ_ASSERT(aChild->CanSend());

  ipc::Endpoint<PVideoBridgeChild> childPipe;
  nsresult rv = EnsureVideoBridge(VideoBridgeSource::RddProcess,
                                  aChild->OtherEndpointProcInfo(), &childPipe);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  gfx::ContentDeviceData contentDeviceData;
  gfxPlatform::GetPlatform()->BuildContentDeviceData(&contentDeviceData);
  aChild->SendInitVideoBridge(std::move(childPipe),
                              !aRDD->AttemptedRDDProcess(), contentDeviceData);
  return NS_OK;
}

nsresult GPUProcessManager::EnsureVideoBridge(
    layers::VideoBridgeSource aSource,
    mozilla::ipc::EndpointProcInfo aOtherProcess,
    mozilla::ipc::Endpoint<layers::PVideoBridgeChild>* aOutChildPipe) {
  MOZ_ASSERT(aOutChildPipe);
  MOZ_DIAGNOSTIC_ASSERT(IsGPUReady());

  ipc::EndpointProcInfo gpuInfo = mGPUChild ? mGPUChild->OtherEndpointProcInfo()
                                            : ipc::EndpointProcInfo::Current();

  // The child end is the producer of video frames; the parent end is the
  // consumer.
  ipc::Endpoint<PVideoBridgeParent> parentPipe;
  nsresult rv = PVideoBridge::CreateEndpoints(gpuInfo, aOtherProcess,
                                              &parentPipe, aOutChildPipe);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  if (mGPUChild) {
    mGPUChild->SendInitVideoBridge(std::move(parentPipe), aSource);
  } else {
    VideoBridgeParent::Open(std::move(parentPipe), aSource);
  }
  return NS_OK;
}

void GPUProcessManager::UnmapLayerTreeId(LayersId aLayersId,
                                         base::ProcessId aOwningId) {
  // If the GPU process is down, but not disabled, there is no need to relaunch
  // here because the layers ID is already invalid.
  if (mGPUChild) {
    mGPUChild->SendRemoveLayerTreeIdMapping(
        LayerTreeIdMapping(aLayersId, aOwningId));
  } else if (!gfxConfig::IsEnabled(Feature::GPU_PROCESS)) {
    CompositorBridgeParent::DeallocateLayerTreeId(aLayersId);
  }

  LayerTreeOwnerTracker::Get()->Unmap(aLayersId, aOwningId);
}

bool GPUProcessManager::IsLayerTreeIdMapped(LayersId aLayersId,
                                            base::ProcessId aRequestingId) {
  return LayerTreeOwnerTracker::Get()->IsMapped(aLayersId, aRequestingId);
}

LayersId GPUProcessManager::AllocateLayerTreeId() {
  // Allocate tree id by using id namespace.
  // By it, tree id does not conflict with external image id and
  // async image pipeline id.
  MOZ_ASSERT(NS_IsMainThread());
  // Increment the resource id by two instead of one so that each
  // WebRenderLayerManager and WebRenderBridgeParent gets two distinct
  // pipeline IDs they can use.
  // This is gross but the steps to create a temporary pipeline
  // ID from the content process or the compositor thread are too
  // complex and expensive.
  // TODO: Ideally, we'd allocate only the namespace here and let the
  // WR layer manager produce any number of pipeline IDs.
  mResourceId += 2;
  if (mResourceId >= UINT32_MAX - 1) {
    // Move to next id namespace.
    mIdNamespace = AllocateNamespace();
    mResourceId = 2;
  }

  uint64_t layerTreeId = mIdNamespace;
  layerTreeId = (layerTreeId << 32) | mResourceId;
  return LayersId{layerTreeId};
}

// See the comment in AllocateLayerTreeId above.
// For now this is only used for view-transition snapshots of the old state,
// it's probably best to avoid using this for anything else.
wr::PipelineId GetTemporaryWebRenderPipelineId(wr::PipelineId aMainPipeline) {
  // Sanity check that we are have the expected even number for
  // the main pipeline handle.
  MOZ_ASSERT(aMainPipeline.mHandle % 2 == 0);
  auto id = aMainPipeline;
  id.mHandle += 1;
  return id;
}

uint32_t GPUProcessManager::AllocateNamespace() {
  MOZ_ASSERT(NS_IsMainThread());
  return ++mNextNamespace;
}

bool GPUProcessManager::AllocateAndConnectLayerTreeId(
    PCompositorBridgeChild* aCompositorBridge, base::ProcessId aOtherPid,
    LayersId* aOutLayersId, CompositorOptions* aOutCompositorOptions) {
  MOZ_ASSERT(aOutLayersId);

  LayersId layersId = AllocateLayerTreeId();
  *aOutLayersId = layersId;

  // We always map the layer ID in the parent process so that we can recover
  // from GPU process crashes. In that case, the tree will be shared with the
  // new GPU process at initialization.
  LayerTreeOwnerTracker::Get()->Map(layersId, aOtherPid);

  if (NS_WARN_IF(NS_FAILED(EnsureGPUReady()))) {
    return false;
  }

  // If we have a CompositorBridgeChild, then we need to call
  // CompositorBridgeParent::NotifyChildCreated. If this is in the GPU process,
  // we can combine it with LayerTreeOwnerTracker::Map to minimize IPC.
  // messages.
  if (aCompositorBridge) {
    if (mGPUChild) {
      return aCompositorBridge->SendMapAndNotifyChildCreated(
          layersId, aOtherPid, aOutCompositorOptions);
    }
    return aCompositorBridge->SendNotifyChildCreated(layersId,
                                                     aOutCompositorOptions);
  }

  // If we don't have a CompositorBridgeChild, we just need to call
  // LayerTreeOwnerTracker::Map in the compositing process.
  if (mGPUChild) {
    mGPUChild->SendAddLayerTreeIdMapping(
        LayerTreeIdMapping(layersId, aOtherPid));
  }
  return false;
}

void GPUProcessManager::EnsureVsyncIOThread() {
  if (mVsyncIOThread) {
    return;
  }

  mVsyncIOThread = new VsyncIOThreadHolder();
  MOZ_RELEASE_ASSERT(mVsyncIOThread->Start());
}

void GPUProcessManager::ShutdownVsyncIOThread() { mVsyncIOThread = nullptr; }

void GPUProcessManager::RegisterRemoteProcessSession(
    RemoteCompositorSession* aSession) {
  mRemoteSessions.AppendElement(aSession);
}

void GPUProcessManager::UnregisterRemoteProcessSession(
    RemoteCompositorSession* aSession) {
  mRemoteSessions.RemoveElement(aSession);
}

void GPUProcessManager::RegisterInProcessSession(
    InProcessCompositorSession* aSession) {
  mInProcessSessions.AppendElement(aSession);
}

void GPUProcessManager::UnregisterInProcessSession(
    InProcessCompositorSession* aSession) {
  mInProcessSessions.RemoveElement(aSession);
}

void GPUProcessManager::AddListener(GPUProcessListener* aListener) {
  if (!mListeners.Contains(aListener)) {
    mListeners.AppendElement(aListener);
  }
}

void GPUProcessManager::RemoveListener(GPUProcessListener* aListener) {
  mListeners.RemoveElement(aListener);
}

bool GPUProcessManager::FlushActiveCheckerboardReports() {
  if (mGPUChild) {
    mGPUChild->SendFlushActiveCheckerboardReports();
    return true;
  }

  if (!gfxConfig::IsEnabled(Feature::GPU_PROCESS)) {
    nsCOMPtr<nsIObserverService> obsSvc =
        mozilla::services::GetObserverService();
    MOZ_ASSERT(obsSvc);
    if (obsSvc) {
      obsSvc->NotifyObservers(nullptr, "APZ:FlushActiveCheckerboard", nullptr);
    }
    return true;
  }

  // If we still are using a GPU process, but do not have one ready at the
  // moment, we can drop these notifications since there is nothing to do.
  return false;
}

class GPUMemoryReporter : public MemoryReportingProcess {
 public:
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GPUMemoryReporter, override)

  bool IsAlive() const override {
    if (GPUProcessManager* gpm = GPUProcessManager::Get()) {
      return !!gpm->GetGPUChild();
    }
    return false;
  }

  bool SendRequestMemoryReport(
      const uint32_t& aGeneration, const bool& aAnonymize,
      const bool& aMinimizeMemoryUsage,
      const Maybe<ipc::FileDescriptor>& aDMDFile) override {
    GPUChild* child = GetChild();
    if (!child) {
      return false;
    }

    return child->SendRequestMemoryReport(aGeneration, aAnonymize,
                                          aMinimizeMemoryUsage, aDMDFile);
  }

  int32_t Pid() const override {
    if (GPUChild* child = GetChild()) {
      return (int32_t)child->OtherPid();
    }
    return 0;
  }

 private:
  GPUChild* GetChild() const {
    if (GPUProcessManager* gpm = GPUProcessManager::Get()) {
      return gpm->GetGPUChild();
    }
    return nullptr;
  }

 protected:
  ~GPUMemoryReporter() = default;
};

RefPtr<MemoryReportingProcess> GPUProcessManager::GetProcessMemoryReporter() {
  // If we are in the middle of launching a GPU process, we can wait for it to
  // finish, otherwise if there is no GPU process, we should just return now to
  // avoid launching it again.
  if (!mProcess || AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMShutdown) ||
      !mProcess->WaitForLaunch()) {
    return nullptr;
  }
  return MakeRefPtr<GPUMemoryReporter>();
}

void GPUProcessManager::SetAppInForeground(bool aInForeground) {
  if (mAppInForeground == aInForeground) {
    return;
  }

  mAppInForeground = aInForeground;
#if defined(XP_WIN)
  SetProcessIsForeground();
#endif

  // If we moved into the foreground, then we need to make sure the GPU process
  // completes its launch. Otherwise listeners may be left dangling from
  // previous calls that returned NS_ERROR_ABORT due to being in the background.
  if (aInForeground && gfxConfig::IsEnabled(Feature::GPU_PROCESS)) {
    (void)LaunchGPUProcess();
  }
}

#if defined(XP_WIN)
void GPUProcessManager::SetProcessIsForeground() {
  NTSTATUS WINAPI NtSetInformationProcess(
      IN HANDLE process_handle, IN ULONG info_class,
      IN PVOID process_information, IN ULONG information_length);
  constexpr unsigned int NtProcessInformationForeground = 25;

  static bool alreadyInitialized = false;
  static decltype(NtSetInformationProcess)* setInformationProcess = nullptr;
  if (!alreadyInitialized) {
    alreadyInitialized = true;
    nsModuleHandle module(LoadLibrary(L"ntdll.dll"));
    if (module) {
      setInformationProcess =
          (decltype(NtSetInformationProcess)*)GetProcAddress(
              module, "NtSetInformationProcess");
    }
  }
  if (MOZ_UNLIKELY(!setInformationProcess)) {
    return;
  }

  unsigned pid = GPUProcessPid();
  if (pid <= 0) {
    return;
  }
  // Using the handle from mProcess->GetChildProcessHandle() fails;
  // the PROCESS_SET_INFORMATION permission is probably missing.
  nsAutoHandle processHandle(
      ::OpenProcess(PROCESS_SET_INFORMATION, FALSE, pid));
  if (!processHandle) {
    return;
  }

  BOOLEAN foreground = mAppInForeground;
  setInformationProcess(processHandle, NtProcessInformationForeground,
                        (PVOID)&foreground, sizeof(foreground));
}
#endif

RefPtr<PGPUChild::TestTriggerMetricsPromise>
GPUProcessManager::TestTriggerMetrics() {
  if (!NS_WARN_IF(!mGPUChild)) {
    return mGPUChild->SendTestTriggerMetrics();
  }

  return PGPUChild::TestTriggerMetricsPromise::CreateAndReject(
      ipc::ResponseRejectReason::SendError, __func__);
}

}  // namespace gfx
}  // namespace mozilla

Messung V0.5 in Prozent
C=89 H=95 G=91

¤ Dauer der Verarbeitung: 0.24 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

letze Version des Elbe Quellennavigators


Jenseits des Üblichen ....
    

Besucher

Besucher

Statistik
#Sources=141584
#Domains=752002