/* -*- 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 "mozilla/layers/CompositorManagerChild.h" #include "mozilla/StaticPrefs_layers.h" #include "mozilla/layers/CompositorBridgeChild.h" #include "mozilla/layers/CompositorManagerParent.h" #include "mozilla/layers/CompositorThread.h" #include "mozilla/gfx/gfxVars.h" #include "mozilla/gfx/GPUProcessManager.h" #include "mozilla/dom/ContentChild.h" // for ContentChild #include "mozilla/dom/BrowserChild.h" // for BrowserChild #include "mozilla/ipc/Endpoint.h" #include "VsyncSource.h" namespace mozilla { namespace layers { using gfx::GPUProcessManager; StaticRefPtr CompositorManagerChild::sInstance; Atomic CompositorManagerChild::sOtherPid(0); /* static */ bool CompositorManagerChild::IsInitialized(uint64_t aProcessToken) { MOZ_ASSERT(NS_IsMainThread()); return sInstance && sInstance->CanSend() && sInstance->mProcessToken == aProcessToken; } /* static */ void CompositorManagerChild::InitSameProcess(uint32_t aNamespace, uint64_t aProcessToken) { MOZ_ASSERT(NS_IsMainThread()); if (NS_WARN_IF(IsInitialized(aProcessToken))) { MOZ_ASSERT_UNREACHABLE("Already initialized same process"); return; } RefPtr parent = CompositorManagerParent::CreateSameProcess(); RefPtr child = new CompositorManagerChild(parent, aProcessToken, aNamespace); if (NS_WARN_IF(!child->CanSend())) { MOZ_DIAGNOSTIC_ASSERT(false, "Failed to open same process protocol"); return; } parent->BindComplete(/* aIsRoot */ true); sInstance = std::move(child); sOtherPid = sInstance->OtherPid(); } /* static */ bool CompositorManagerChild::Init(Endpoint&& aEndpoint, uint32_t aNamespace, uint64_t aProcessToken /* = 0 */) { MOZ_ASSERT(NS_IsMainThread()); if (sInstance) { MOZ_ASSERT(sInstance->mNamespace != aNamespace); } sInstance = new CompositorManagerChild(std::move(aEndpoint), aProcessToken, aNamespace); sOtherPid = sInstance->OtherPid(); return sInstance->CanSend(); } /* static */ void CompositorManagerChild::Shutdown() { MOZ_ASSERT(NS_IsMainThread()); CompositorBridgeChild::ShutDown(); if (!sInstance) { return; } sInstance->Close(); sInstance = nullptr; sOtherPid = 0; } /* static */ void CompositorManagerChild::OnGPUProcessLost(uint64_t aProcessToken) { MOZ_ASSERT(NS_IsMainThread()); // Since GPUChild and CompositorManagerChild will race on ActorDestroy, we // cannot know if the CompositorManagerChild is about to be released but has // yet to be. As such, we want to pre-emptively set mCanSend to false. if (sInstance && sInstance->mProcessToken == aProcessToken) { sInstance->mCanSend = false; sOtherPid = 0; } } /* static */ bool CompositorManagerChild::CreateContentCompositorBridge( uint32_t aNamespace) { MOZ_ASSERT(NS_IsMainThread()); if (NS_WARN_IF(!sInstance || !sInstance->CanSend())) { return false; } CompositorBridgeOptions options = ContentCompositorOptions(); RefPtr bridge = new CompositorBridgeChild(sInstance); if (NS_WARN_IF( !sInstance->SendPCompositorBridgeConstructor(bridge, options))) { return false; } bridge->InitForContent(aNamespace); return true; } /* static */ already_AddRefed CompositorManagerChild::CreateWidgetCompositorBridge( uint64_t aProcessToken, WebRenderLayerManager* aLayerManager, uint32_t aNamespace, CSSToLayoutDeviceScale aScale, const CompositorOptions& aOptions, bool aUseExternalSurfaceSize, const gfx::IntSize& aSurfaceSize, uint64_t aInnerWindowId) { MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(NS_IsMainThread()); if (NS_WARN_IF(!sInstance || !sInstance->CanSend())) { return nullptr; } TimeDuration vsyncRate = gfxPlatform::GetPlatform()->GetGlobalVsyncDispatcher()->GetVsyncRate(); CompositorBridgeOptions options = WidgetCompositorOptions( aScale, vsyncRate, aOptions, aUseExternalSurfaceSize, aSurfaceSize, aInnerWindowId); RefPtr bridge = new CompositorBridgeChild(sInstance); if (NS_WARN_IF( !sInstance->SendPCompositorBridgeConstructor(bridge, options))) { return nullptr; } bridge->InitForWidget(aProcessToken, aLayerManager, aNamespace); return bridge.forget(); } /* static */ already_AddRefed CompositorManagerChild::CreateSameProcessWidgetCompositorBridge( WebRenderLayerManager* aLayerManager, uint32_t aNamespace) { MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(NS_IsMainThread()); if (NS_WARN_IF(!sInstance || !sInstance->CanSend())) { return nullptr; } CompositorBridgeOptions options = SameProcessWidgetCompositorOptions(); RefPtr bridge = new CompositorBridgeChild(sInstance); if (NS_WARN_IF( !sInstance->SendPCompositorBridgeConstructor(bridge, options))) { return nullptr; } bridge->InitForWidget(1, aLayerManager, aNamespace); return bridge.forget(); } CompositorManagerChild::CompositorManagerChild(CompositorManagerParent* aParent, uint64_t aProcessToken, uint32_t aNamespace) : mProcessToken(aProcessToken), mNamespace(aNamespace), mResourceId(0), mCanSend(false), mSameProcess(true) { MOZ_ASSERT(aParent); SetOtherProcessId(base::GetCurrentProcId()); if (NS_WARN_IF(!Open(aParent, CompositorThread(), ipc::ChildSide))) { return; } mCanSend = true; SetReplyTimeout(); } CompositorManagerChild::CompositorManagerChild( Endpoint&& aEndpoint, uint64_t aProcessToken, uint32_t aNamespace) : mProcessToken(aProcessToken), mNamespace(aNamespace), mResourceId(0), mCanSend(false), mSameProcess(false) { if (NS_WARN_IF(!aEndpoint.Bind(this))) { return; } mCanSend = true; SetReplyTimeout(); } void CompositorManagerChild::ActorDestroy(ActorDestroyReason aReason) { mCanSend = false; if (sInstance == this) { sInstance = nullptr; } } void CompositorManagerChild::HandleFatalError(const char* aMsg) { dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aMsg, OtherPid()); } void CompositorManagerChild::ProcessingError(Result aCode, const char* aReason) { if (aCode != MsgDropped) { gfxDevCrash(gfx::LogReason::ProcessingError) << "Processing error in CompositorBridgeChild: " << int(aCode); } } void CompositorManagerChild::SetReplyTimeout() { #ifndef DEBUG // Add a timeout for release builds to kill GPU process when it hangs. if (XRE_IsParentProcess() && GPUProcessManager::Get()->GetGPUChild()) { int32_t timeout = StaticPrefs::layers_gpu_process_ipc_reply_timeout_ms_AtStartup(); SetReplyTimeoutMs(timeout); } #endif } bool CompositorManagerChild::ShouldContinueFromReplyTimeout() { if (XRE_IsParentProcess()) { gfxCriticalNote << "Killing GPU process due to IPC reply timeout"; MOZ_DIAGNOSTIC_ASSERT(GPUProcessManager::Get()->GetGPUChild()); GPUProcessManager::Get()->KillProcess(); } return false; } mozilla::ipc::IPCResult CompositorManagerChild::RecvNotifyWebRenderError( const WebRenderError&& aError) { MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(NS_IsMainThread()); GPUProcessManager::Get()->NotifyWebRenderError(aError); return IPC_OK(); } } // namespace layers } // namespace mozilla