/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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/MemoryReportingProcess.h" #include "mozilla/Preferences.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/RemoteDecoderManagerChild.h" #include "mozilla/RemoteDecoderManagerParent.h" #include "mozilla/Telemetry.h" #include "mozilla/dom/ContentParent.h" #include "mozilla/gfx/gfxVars.h" #include "mozilla/gfx/GPUChild.h" #include "mozilla/ipc/Endpoint.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/widget/PlatformWidgetTypes.h" #include "nsAppRunner.h" #ifdef MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING # include "mozilla/widget/CompositorWidgetChild.h" #endif #include "nsBaseWidget.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" #if defined(MOZ_WIDGET_ANDROID) # include "mozilla/widget/AndroidUiThread.h" # include "mozilla/layers/UiCompositorControllerChild.h" #endif // defined(MOZ_WIDGET_ANDROID) namespace mozilla { namespace gfx { using namespace mozilla::layers; enum class FallbackType : uint32_t { NONE = 0, DECODINGDISABLED, DISABLED, }; static StaticAutoPtr sSingleton; GPUProcessManager* GPUProcessManager::Get() { return sSingleton; } void GPUProcessManager::Initialize() { MOZ_ASSERT(XRE_IsParentProcess()); sSingleton = new GPUProcessManager(); } void GPUProcessManager::Shutdown() { sSingleton = nullptr; } GPUProcessManager::GPUProcessManager() : mTaskFactory(this), mNextNamespace(0), mIdNamespace(0), mResourceId(0), mNumProcessAttempts(0), mDeviceResetCount(0), 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_ASSERT(!mObserver); } NS_IMPL_ISUPPORTS(GPUProcessManager::Observer, nsIObserver); GPUProcessManager::Observer::Observer(GPUProcessManager* aManager) : mManager(aManager) {} NS_IMETHODIMP GPUProcessManager::Observer::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) { if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) { mManager->OnXPCOMShutdown(); } else if (!strcmp(aTopic, "nsPref:changed")) { mManager->OnPreferenceChange(aData); } return NS_OK; } void GPUProcessManager::OnXPCOMShutdown() { if (mObserver) { nsContentUtils::UnregisterShutdownObserver(mObserver); Preferences::RemoveObserver(mObserver, ""); mObserver = nullptr; } CleanShutdown(); } void GPUProcessManager::OnPreferenceChange(const char16_t* aData) { // A pref changed. If it is useful to do so, inform child processes. if (!dom::ContentParent::ShouldSyncPreference(aData)) { return; } // We know prefs are ASCII here. NS_LossyConvertUTF16toASCII strData(aData); mozilla::dom::Pref pref(strData, /* isLocked */ false, Nothing(), Nothing()); Preferences::GetPreference(&pref); if (!!mGPUChild) { MOZ_ASSERT(mQueuedPrefs.IsEmpty()); mGPUChild->SendPreferenceUpdate(pref); } else if (IsGPUProcessLaunching()) { mQueuedPrefs.AppendElement(pref); } } void GPUProcessManager::LaunchGPUProcess() { if (mProcess) { return; } // Start listening for pref changes so we can // forward them to the process once it is running. if (!mObserver) { mObserver = new Observer(this); nsContentUtils::RegisterShutdownObserver(mObserver); Preferences::AddStrongObserver(mObserver, ""); } // Start the Vsync I/O thread so can use it as soon as the process launches. EnsureVsyncIOThread(); mNumProcessAttempts++; std::vector extraArgs; nsCString parentBuildID(mozilla::PlatformBuildID()); extraArgs.push_back("-parentBuildID"); extraArgs.push_back(parentBuildID.get()); // The subprocess is launched asynchronously, so we wait for a callback to // acquire the IPDL actor. mProcess = new GPUProcessHost(this); if (!mProcess->Launch(extraArgs)) { DisableGPUProcess("Failed to launch GPU process"); } } bool GPUProcessManager::IsGPUProcessLaunching() { MOZ_ASSERT(NS_IsMainThread()); return !!mProcess && !mGPUChild; } void GPUProcessManager::DisableGPUProcess(const char* aMessage) { if (!gfxConfig::IsEnabled(Feature::GPU_PROCESS)) { return; } gfxConfig::SetFailed(Feature::GPU_PROCESS, FeatureStatus::Failed, aMessage); gfxCriticalNote << aMessage; gfxPlatform::NotifyGPUProcessDisabled(); Telemetry::Accumulate(Telemetry::GPU_PROCESS_CRASH_FALLBACKS, uint32_t(FallbackType::DISABLED)); DestroyProcess(); ShutdownVsyncIOThread(); // 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. EnsureProtocolsReady(); // 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(); // 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 } bool GPUProcessManager::EnsureGPUReady() { if (mProcess && !mProcess->IsConnected()) { if (!mProcess->WaitForLaunch()) { // If this fails, we should have fired OnProcessLaunchComplete and // removed the process. MOZ_ASSERT(!mProcess && !mGPUChild); return false; } } if (mGPUChild) { if (mGPUChild->EnsureGPUReady()) { return true; } // 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"); } return false; } void GPUProcessManager::EnsureProtocolsReady() { EnsureCompositorManagerChild(); EnsureImageBridgeChild(); EnsureVRManager(); } void GPUProcessManager::EnsureCompositorManagerChild() { bool gpuReady = EnsureGPUReady(); if (CompositorManagerChild::IsInitialized(mProcessToken)) { return; } if (!gpuReady) { CompositorManagerChild::InitSameProcess(AllocateNamespace(), mProcessToken); return; } ipc::Endpoint parentPipe; ipc::Endpoint childPipe; nsresult rv = PCompositorManager::CreateEndpoints( mGPUChild->OtherPid(), base::GetCurrentProcId(), &parentPipe, &childPipe); if (NS_FAILED(rv)) { DisableGPUProcess("Failed to create PCompositorManager endpoints"); return; } mGPUChild->SendInitCompositorManager(std::move(parentPipe)); CompositorManagerChild::Init(std::move(childPipe), AllocateNamespace(), mProcessToken); } void GPUProcessManager::EnsureImageBridgeChild() { if (ImageBridgeChild::GetSingleton()) { return; } if (!EnsureGPUReady()) { ImageBridgeChild::InitSameProcess(AllocateNamespace()); return; } ipc::Endpoint parentPipe; ipc::Endpoint childPipe; nsresult rv = PImageBridge::CreateEndpoints( mGPUChild->OtherPid(), base::GetCurrentProcId(), &parentPipe, &childPipe); if (NS_FAILED(rv)) { DisableGPUProcess("Failed to create PImageBridge endpoints"); return; } mGPUChild->SendInitImageBridge(std::move(parentPipe)); ImageBridgeChild::InitWithGPUProcess(std::move(childPipe), AllocateNamespace()); } void GPUProcessManager::EnsureVRManager() { if (VRManagerChild::IsCreated()) { return; } if (!EnsureGPUReady()) { VRManagerChild::InitSameProcess(); return; } ipc::Endpoint parentPipe; ipc::Endpoint childPipe; nsresult rv = PVRManager::CreateEndpoints( mGPUChild->OtherPid(), base::GetCurrentProcId(), &parentPipe, &childPipe); if (NS_FAILED(rv)) { DisableGPUProcess("Failed to create PVRManager endpoints"); return; } mGPUChild->SendInitVRManager(std::move(parentPipe)); VRManagerChild::InitWithGPUProcess(std::move(childPipe)); } #if defined(MOZ_WIDGET_ANDROID) already_AddRefed GPUProcessManager::CreateUiCompositorController(nsBaseWidget* aWidget, const LayersId aId) { RefPtr result; if (!EnsureGPUReady()) { result = UiCompositorControllerChild::CreateForSameProcess(aId); } else { ipc::Endpoint parentPipe; ipc::Endpoint childPipe; nsresult rv = PUiCompositorController::CreateEndpoints( mGPUChild->OtherPid(), base::GetCurrentProcId(), &parentPipe, &childPipe); if (NS_FAILED(rv)) { DisableGPUProcess("Failed to create PUiCompositorController endpoints"); return nullptr; } mGPUChild->SendInitUiCompositorController(aId, std::move(parentPipe)); result = UiCompositorControllerChild::CreateForGPUProcess( mProcessToken, std::move(childPipe)); } if (result) { result->SetBaseWidget(aWidget); } return result.forget(); } #endif // defined(MOZ_WIDGET_ANDROID) void GPUProcessManager::OnProcessLaunchComplete(GPUProcessHost* aHost) { MOZ_ASSERT(mProcess && mProcess == aHost); if (!mProcess->IsConnected()) { DisableGPUProcess("Failed to connect GPU process"); return; } mGPUChild = mProcess->GetActor(); mProcessToken = mProcess->GetProcessToken(); ipc::Endpoint vsyncParent; ipc::Endpoint vsyncChild; nsresult rv = PVsyncBridge::CreateEndpoints(mGPUChild->OtherPid(), base::GetCurrentProcId(), &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)); // 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) { Unused << NS_WARN_IF(!mGPUChild->SendPreferenceUpdate(pref)); } mQueuedPrefs.Clear(); CrashReporter::AnnotateCrashReport( CrashReporter::Annotation::GPUProcessStatus, "Running"_ns); CrashReporter::AnnotateCrashReport( CrashReporter::Annotation::GPUProcessLaunchCount, static_cast(mNumProcessAttempts)); } 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) { GPUDeviceData data; if (mGPUChild->SendSimulateDeviceReset(&data)) { gfxPlatform::GetPlatform()->ImportGPUDeviceData(data); } OnRemoteProcessDeviceReset(mProcess); } else { OnInProcessDeviceReset(/* aTrackThreshold */ false); } } bool GPUProcessManager::DisableWebRenderConfig(wr::WebRenderError aError, const nsCString& aMsg) { if (!gfx::gfxVars::UseWebRender()) { return false; } // Disable WebRender if (aError == wr::WebRenderError::INITIALIZE) { gfxPlatform::DisableWebRender(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); } else if (aError == wr::WebRenderError::RENDER) { gfxPlatform::DisableWebRender(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); } else if (aError == wr::WebRenderError::EXCESSIVE_RESETS) { gfxPlatform::DisableWebRender( 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); } gfx::gfxVars::SetUseWebRenderDCompVideoOverlayWin(false); #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; } void GPUProcessManager::DisableWebRender(wr::WebRenderError aError, const nsCString& aMsg) { if (DisableWebRenderConfig(aError, aMsg)) { if (mProcess) { OnRemoteProcessDeviceReset(mProcess); } else { OnInProcessDeviceReset(/* aTrackThreshold */ false); } } } void GPUProcessManager::NotifyWebRenderError(wr::WebRenderError aError) { if (aError == wr::WebRenderError::VIDEO_OVERLAY) { #ifdef XP_WIN gfxVars::SetUseWebRenderDCompVideoOverlayWin(false); #else MOZ_ASSERT_UNREACHABLE("unexpected to be called"); #endif return; } DisableWebRender(aError, nsCString()); } /* static */ void GPUProcessManager::RecordDeviceReset( DeviceResetReason aReason) { if (aReason != DeviceResetReason::FORCED_RESET) { Telemetry::Accumulate(Telemetry::DEVICE_RESET_REASON, uint32_t(aReason)); } CrashReporter::AnnotateCrashReport( CrashReporter::Annotation::DeviceResetReason, int(aReason)); } bool GPUProcessManager::OnDeviceReset(bool aTrackThreshold) { #ifdef XP_WIN // Disable double buffering when device reset happens. if (!gfxVars::UseWebRender() && gfxVars::UseDoubleBufferingWithCompositor()) { gfxVars::SetUseDoubleBufferingWithCompositor(false); } #endif // 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(bool aTrackThreshold) { if (OnDeviceReset(aTrackThreshold)) { 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 } RebuildInProcessSessions(); NotifyListenersOnCompositeDeviceReset(); } void GPUProcessManager::OnRemoteProcessDeviceReset(GPUProcessHost* aHost) { if (OnDeviceReset(/* aTrackThreshold */ true)) { DestroyProcess(); DisableGPUProcess("GPU processed experienced too many device resets"); HandleProcessLost(); return; } RebuildRemoteSessions(); 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(); } } 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(); if (mNumProcessAttempts > uint32_t(StaticPrefs::layers_gpu_process_max_restarts())) { char disableMessage[64]; SprintfLiteral(disableMessage, "GPU process disabled after %d attempts", mNumProcessAttempts); DisableGPUProcess(disableMessage); } else if (mNumProcessAttempts > uint32_t(StaticPrefs:: layers_gpu_process_max_restarts_with_decoder()) && mDecodeVideoOnGpuProcess) { mDecodeVideoOnGpuProcess = false; Telemetry::Accumulate(Telemetry::GPU_PROCESS_CRASH_FALLBACKS, uint32_t(FallbackType::DECODINGDISABLED)); HandleProcessLost(); } else { Telemetry::Accumulate(Telemetry::GPU_PROCESS_CRASH_FALLBACKS, uint32_t(FallbackType::NONE)); HandleProcessLost(); } } void GPUProcessManager::HandleProcessLost() { if (gfxConfig::IsEnabled(Feature::GPU_PROCESS)) { LaunchGPUProcess(); } // 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. // // (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. Currently, we do not relaunch it, // and any new compositors will be created in-process and will default // to software. // // (5) Next 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] RenderFrame::EnsureLayersConnected // (f) [UI] CompositorBridgeChild::SendNotifyChildRecreated // // Note that at step (e), RenderFrame will call GetLayerManager // 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::GetLayerManager. RebuildRemoteSessions(); // 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). for (const auto& listener : mListeners) { listener->OnCompositorUnexpectedShutdown(); } } void GPUProcessManager::RebuildRemoteSessions() { // Build a list of sessions to notify, since notification might delete // entries from the list. nsTArray> 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::RebuildInProcessSessions() { // Build a list of sessions to notify, since notification might delete // entries from the list. nsTArray> 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(); } } void GPUProcessManager::NotifyDisablingWebRender() { #if defined(MOZ_WIDGET_ANDROID) for (const auto& session : mRemoteSessions) { session->NotifyDisablingWebRender(); } for (const auto& session : mInProcessSessions) { session->NotifyDisablingWebRender(); } #endif } void GPUProcessManager::NotifyRemoteActorDestroyed( const uint64_t& aProcessToken) { if (!NS_IsMainThread()) { RefPtr 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::CleanShutdown() { DestroyProcess(); mVsyncIOThread = nullptr; } void GPUProcessManager::KillProcess() { if (!mProcess) { return; } mProcess->KillProcess(); } void GPUProcessManager::DestroyProcess() { if (!mProcess) { return; } mProcess->Shutdown(); mProcessToken = 0; mProcess = nullptr; mGPUChild = nullptr; mQueuedPrefs.Clear(); if (mVsyncBridge) { mVsyncBridge->Close(); mVsyncBridge = nullptr; } CrashReporter::AnnotateCrashReport( CrashReporter::Annotation::GPUProcessStatus, "Destroyed"_ns); } already_AddRefed GPUProcessManager::CreateTopLevelCompositor( nsBaseWidget* aWidget, LayerManager* aLayerManager, CSSToLayoutDeviceScale aScale, const CompositorOptions& aOptions, bool aUseExternalSurfaceSize, const gfx::IntSize& aSurfaceSize, bool* aRetryOut) { MOZ_ASSERT(aRetryOut); LayersId layerTreeId = AllocateLayerTreeId(); EnsureProtocolsReady(); RefPtr session; if (EnsureGPUReady()) { session = CreateRemoteSession(aWidget, aLayerManager, layerTreeId, aScale, aOptions, aUseExternalSurfaceSize, aSurfaceSize); if (!session) { // We couldn't create a remote compositor, so abort the process. DisableGPUProcess("Failed to create remote compositor"); *aRetryOut = true; return nullptr; } } else { session = InProcessCompositorSession::Create( aWidget, aLayerManager, layerTreeId, aScale, aOptions, aUseExternalSurfaceSize, aSurfaceSize, AllocateNamespace()); } #if defined(MOZ_WIDGET_ANDROID) if (session) { // Nothing to do if controller gets a nullptr RefPtr controller = CreateUiCompositorController(aWidget, session->RootLayerTreeId()); session->SetUiCompositorControllerChild(controller); } #endif // defined(MOZ_WIDGET_ANDROID) *aRetryOut = false; return session.forget(); } RefPtr GPUProcessManager::CreateRemoteSession( nsBaseWidget* aWidget, LayerManager* aLayerManager, const LayersId& aRootLayerTreeId, CSSToLayoutDeviceScale aScale, const CompositorOptions& aOptions, bool aUseExternalSurfaceSize, const gfx::IntSize& aSurfaceSize) { #ifdef MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING widget::CompositorWidgetInitData initData; aWidget->GetCompositorWidgetInitData(&initData); RefPtr child = CompositorManagerChild::CreateWidgetCompositorBridge( mProcessToken, aLayerManager, AllocateNamespace(), aScale, aOptions, aUseExternalSurfaceSize, aSurfaceSize); if (!child) { gfxCriticalNote << "Failed to create CompositorBridgeChild"; return nullptr; } RefPtr dispatcher = aWidget->GetCompositorVsyncDispatcher(); RefPtr observer = new widget::CompositorWidgetVsyncObserver(mVsyncBridge, aRootLayerTreeId); widget::CompositorWidgetChild* widget = new widget::CompositorWidgetChild(dispatcher, observer, initData); if (!child->SendPCompositorWidgetConstructor(widget, initData)) { return nullptr; } if (!widget->Initialize()) { return nullptr; } if (!child->SendInitialize(aRootLayerTreeId)) { return nullptr; } RefPtr apz = nullptr; if (aOptions.UseAPZ()) { PAPZCTreeManagerChild* papz = child->SendPAPZCTreeManagerConstructor(LayersId{0}); if (!papz) { return nullptr; } apz = static_cast(papz); RefPtr pinput = new APZInputBridgeChild(); if (!mGPUChild->SendPAPZInputBridgeConstructor(pinput, aRootLayerTreeId)) { return nullptr; } apz->SetInputBridge(pinput); } return new RemoteCompositorSession(aWidget, child, widget, apz, aRootLayerTreeId); #else gfxCriticalNote << "Platform does not support out-of-process compositing"; return nullptr; #endif } bool GPUProcessManager::CreateContentBridges( base::ProcessId aOtherProcess, ipc::Endpoint* aOutCompositor, ipc::Endpoint* aOutImageBridge, ipc::Endpoint* aOutVRBridge, ipc::Endpoint* aOutVideoManager, nsTArray* aNamespaces) { if (!CreateContentCompositorManager(aOtherProcess, aOutCompositor) || !CreateContentImageBridge(aOtherProcess, aOutImageBridge) || !CreateContentVRManager(aOtherProcess, aOutVRBridge)) { return false; } // VideoDeocderManager is only supported in the GPU process, so we allow this // to be fallible. CreateContentRemoteDecoderManager(aOtherProcess, aOutVideoManager); // Allocates 3 namespaces(for CompositorManagerChild, CompositorBridgeChild // and ImageBridgeChild) aNamespaces->AppendElement(AllocateNamespace()); aNamespaces->AppendElement(AllocateNamespace()); aNamespaces->AppendElement(AllocateNamespace()); return true; } bool GPUProcessManager::CreateContentCompositorManager( base::ProcessId aOtherProcess, ipc::Endpoint* aOutEndpoint) { ipc::Endpoint parentPipe; ipc::Endpoint childPipe; base::ProcessId parentPid = EnsureGPUReady() ? mGPUChild->OtherPid() : base::GetCurrentProcId(); nsresult rv = PCompositorManager::CreateEndpoints(parentPid, 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)); } else if (!CompositorManagerParent::Create(std::move(parentPipe), /* aIsRoot */ false)) { return false; } *aOutEndpoint = std::move(childPipe); return true; } bool GPUProcessManager::CreateContentImageBridge( base::ProcessId aOtherProcess, ipc::Endpoint* aOutEndpoint) { EnsureImageBridgeChild(); base::ProcessId parentPid = EnsureGPUReady() ? mGPUChild->OtherPid() : base::GetCurrentProcId(); ipc::Endpoint parentPipe; ipc::Endpoint childPipe; nsresult rv = PImageBridge::CreateEndpoints(parentPid, 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)); } else { if (!ImageBridgeParent::CreateForContent(std::move(parentPipe))) { return false; } } *aOutEndpoint = std::move(childPipe); return true; } base::ProcessId GPUProcessManager::GPUProcessPid() { base::ProcessId gpuPid = mGPUChild ? mGPUChild->OtherPid() : -1; return gpuPid; } bool GPUProcessManager::CreateContentVRManager( base::ProcessId aOtherProcess, ipc::Endpoint* aOutEndpoint) { EnsureVRManager(); base::ProcessId parentPid = EnsureGPUReady() ? mGPUChild->OtherPid() : base::GetCurrentProcId(); ipc::Endpoint parentPipe; ipc::Endpoint childPipe; nsresult rv = PVRManager::CreateEndpoints(parentPid, 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)); } else { if (!VRManagerParent::CreateForContent(std::move(parentPipe))) { return false; } } *aOutEndpoint = std::move(childPipe); return true; } void GPUProcessManager::CreateContentRemoteDecoderManager( base::ProcessId aOtherProcess, ipc::Endpoint* aOutEndpoint) { if (!EnsureGPUReady() || !StaticPrefs::media_gpu_process_decoder() || !mDecodeVideoOnGpuProcess) { return; } ipc::Endpoint parentPipe; ipc::Endpoint childPipe; nsresult rv = PRemoteDecoderManager::CreateEndpoints( mGPUChild->OtherPid(), aOtherProcess, &parentPipe, &childPipe); if (NS_FAILED(rv)) { gfxCriticalNote << "Could not create content video decoder: " << hexa(int(rv)); return; } mGPUChild->SendNewContentRemoteDecoderManager(std::move(parentPipe)); *aOutEndpoint = std::move(childPipe); } void GPUProcessManager::InitVideoBridge( ipc::Endpoint&& aVideoBridge) { if (EnsureGPUReady()) { mGPUChild->SendInitVideoBridge(std::move(aVideoBridge)); } } void GPUProcessManager::MapLayerTreeId(LayersId aLayersId, base::ProcessId aOwningId) { LayerTreeOwnerTracker::Get()->Map(aLayersId, aOwningId); if (EnsureGPUReady()) { mGPUChild->SendAddLayerTreeIdMapping( LayerTreeIdMapping(aLayersId, aOwningId)); } } void GPUProcessManager::UnmapLayerTreeId(LayersId aLayersId, base::ProcessId aOwningId) { LayerTreeOwnerTracker::Get()->Unmap(aLayersId, aOwningId); if (EnsureGPUReady()) { mGPUChild->SendRemoveLayerTreeIdMapping( LayerTreeIdMapping(aLayersId, aOwningId)); return; } CompositorBridgeParent::DeallocateLayerTreeId(aLayersId); } 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()); ++mResourceId; if (mResourceId == UINT32_MAX) { // Move to next id namespace. mIdNamespace = AllocateNamespace(); mResourceId = 1; } uint64_t layerTreeId = mIdNamespace; layerTreeId = (layerTreeId << 32) | mResourceId; return LayersId{layerTreeId}; } uint32_t GPUProcessManager::AllocateNamespace() { MOZ_ASSERT(NS_IsMainThread()); return ++mNextNamespace; } bool GPUProcessManager::AllocateAndConnectLayerTreeId( PCompositorBridgeChild* aCompositorBridge, base::ProcessId aOtherPid, LayersId* aOutLayersId, CompositorOptions* aOutCompositorOptions) { LayersId layersId = AllocateLayerTreeId(); *aOutLayersId = layersId; if (!mGPUChild || !aCompositorBridge) { // If we're not remoting to another process, or there is no compositor, // then we'll send at most one message. In this case we can just keep // the old behavior of making sure the mapping occurs, and maybe sending // a creation notification. MapLayerTreeId(layersId, aOtherPid); if (!aCompositorBridge) { return false; } return aCompositorBridge->SendNotifyChildCreated(layersId, aOutCompositorOptions); } // Use the combined message path. LayerTreeOwnerTracker::Get()->Map(layersId, aOtherPid); return aCompositorBridge->SendMapAndNotifyChildCreated(layersId, aOtherPid, aOutCompositorOptions); } 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) { mListeners.AppendElement(aListener); } void GPUProcessManager::RemoveListener(GPUProcessListener* aListener) { mListeners.RemoveElement(aListener); } bool GPUProcessManager::NotifyGpuObservers(const char* aTopic) { if (!EnsureGPUReady()) { return false; } nsCString topic(aTopic); mGPUChild->SendNotifyGpuObservers(topic); return true; } 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& 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()) { if (GPUChild* child = gpm->GetGPUChild()) { return child; } } return nullptr; } protected: ~GPUMemoryReporter() = default; }; RefPtr GPUProcessManager::GetProcessMemoryReporter() { if (!EnsureGPUReady()) { return nullptr; } return new GPUMemoryReporter(); } } // namespace gfx } // namespace mozilla