CompositorManagerChild.cpp (9460B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "mozilla/layers/CompositorManagerChild.h" 8 9 #include "mozilla/StaticPrefs_layers.h" 10 #include "mozilla/layers/CompositorBridgeChild.h" 11 #include "mozilla/layers/CompositorManagerParent.h" 12 #include "mozilla/layers/CompositorThread.h" 13 #include "mozilla/gfx/CanvasShutdownManager.h" 14 #include "mozilla/gfx/gfxVars.h" 15 #include "mozilla/gfx/GPUProcessManager.h" 16 #include "mozilla/dom/ContentChild.h" // for ContentChild 17 #include "mozilla/dom/BrowserChild.h" // for BrowserChild 18 #include "mozilla/ipc/Endpoint.h" 19 #include "VsyncSource.h" 20 21 namespace mozilla { 22 namespace layers { 23 24 using gfx::GPUProcessManager; 25 26 StaticRefPtr<CompositorManagerChild> CompositorManagerChild::sInstance; 27 28 static StaticMutex sCompositorProcInfoMutex; 29 static ipc::EndpointProcInfo sCompositorProcInfo 30 MOZ_GUARDED_BY(sCompositorProcInfoMutex); 31 32 static void SetCompositorProcInfo(ipc::EndpointProcInfo aInfo) { 33 StaticMutexAutoLock lock(sCompositorProcInfoMutex); 34 sCompositorProcInfo = aInfo; 35 } 36 37 /* static */ 38 ipc::EndpointProcInfo CompositorManagerChild::GetCompositorProcInfo() { 39 StaticMutexAutoLock lock(sCompositorProcInfoMutex); 40 return sCompositorProcInfo; 41 } 42 43 /* static */ 44 bool CompositorManagerChild::IsInitialized(uint64_t aProcessToken) { 45 MOZ_ASSERT(NS_IsMainThread()); 46 return sInstance && sInstance->CanSend() && 47 sInstance->mProcessToken == aProcessToken; 48 } 49 50 /* static */ 51 void CompositorManagerChild::InitSameProcess(uint32_t aNamespace, 52 uint64_t aProcessToken) { 53 MOZ_ASSERT(NS_IsMainThread()); 54 if (NS_WARN_IF(IsInitialized(aProcessToken))) { 55 MOZ_ASSERT_UNREACHABLE("Already initialized same process"); 56 return; 57 } 58 59 RefPtr<CompositorManagerParent> parent = 60 CompositorManagerParent::CreateSameProcess(aNamespace); 61 RefPtr<CompositorManagerChild> child = new CompositorManagerChild( 62 aProcessToken, aNamespace, /* aSameProcess */ true); 63 child->SetOtherEndpointProcInfo(ipc::EndpointProcInfo::Current()); 64 if (NS_WARN_IF(!child->Open(parent, CompositorThread(), ipc::ChildSide))) { 65 MOZ_DIAGNOSTIC_CRASH("Failed to open same process protocol"); 66 return; 67 } 68 child->mCanSend = true; 69 child->SetReplyTimeout(); 70 71 parent->BindComplete(/* aIsRoot */ true); 72 sInstance = std::move(child); 73 SetCompositorProcInfo(sInstance->OtherEndpointProcInfo()); 74 } 75 76 /* static */ 77 bool CompositorManagerChild::Init(Endpoint<PCompositorManagerChild>&& aEndpoint, 78 uint32_t aNamespace, 79 uint64_t aProcessToken /* = 0 */) { 80 MOZ_ASSERT(NS_IsMainThread()); 81 if (sInstance) { 82 MOZ_ASSERT(sInstance->mNamespace != aNamespace); 83 } 84 85 RefPtr<CompositorManagerChild> child = 86 new CompositorManagerChild(aProcessToken, aNamespace, 87 /* aSameProcess */ false); 88 if (NS_WARN_IF(!aEndpoint.Bind(child))) { 89 return false; 90 } 91 child->mCanSend = true; 92 child->SetReplyTimeout(); 93 94 sInstance = std::move(child); 95 SetCompositorProcInfo(sInstance->OtherEndpointProcInfo()); 96 97 // If there are any canvases waiting on the recreation of the GPUProcess or 98 // CompositorManagerChild, then we need to notify them so that they can 99 // restore their contexts. 100 gfx::CanvasShutdownManager::OnCompositorManagerRestored(); 101 return true; 102 } 103 104 /* static */ 105 void CompositorManagerChild::Shutdown() { 106 MOZ_ASSERT(NS_IsMainThread()); 107 CompositorBridgeChild::ShutDown(); 108 109 if (!sInstance) { 110 return; 111 } 112 113 sInstance->Close(); 114 sInstance = nullptr; 115 SetCompositorProcInfo(ipc::EndpointProcInfo::Invalid()); 116 } 117 118 /* static */ 119 void CompositorManagerChild::OnGPUProcessLost(uint64_t aProcessToken) { 120 MOZ_ASSERT(NS_IsMainThread()); 121 122 // Since GPUChild and CompositorManagerChild will race on ActorDestroy, we 123 // cannot know if the CompositorManagerChild is about to be released but has 124 // yet to be. As such, we want to pre-emptively set mCanSend to false. 125 if (sInstance && sInstance->mProcessToken == aProcessToken) { 126 sInstance->mCanSend = false; 127 SetCompositorProcInfo(ipc::EndpointProcInfo::Invalid()); 128 } 129 } 130 131 /* static */ 132 bool CompositorManagerChild::CreateContentCompositorBridge( 133 uint32_t aNamespace) { 134 MOZ_ASSERT(NS_IsMainThread()); 135 if (NS_WARN_IF(!sInstance || !sInstance->CanSend())) { 136 return false; 137 } 138 139 CompositorBridgeOptions options = ContentCompositorOptions(); 140 141 RefPtr<CompositorBridgeChild> bridge = new CompositorBridgeChild(sInstance); 142 if (NS_WARN_IF( 143 !sInstance->SendPCompositorBridgeConstructor(bridge, options))) { 144 return false; 145 } 146 147 bridge->InitForContent(aNamespace); 148 return true; 149 } 150 151 /* static */ 152 already_AddRefed<CompositorBridgeChild> 153 CompositorManagerChild::CreateWidgetCompositorBridge( 154 uint64_t aProcessToken, WebRenderLayerManager* aLayerManager, 155 uint32_t aNamespace, CSSToLayoutDeviceScale aScale, 156 const CompositorOptions& aOptions, bool aUseExternalSurfaceSize, 157 const gfx::IntSize& aSurfaceSize, uint64_t aInnerWindowId) { 158 MOZ_ASSERT(XRE_IsParentProcess()); 159 MOZ_ASSERT(NS_IsMainThread()); 160 if (NS_WARN_IF(!sInstance || !sInstance->CanSend())) { 161 return nullptr; 162 } 163 164 TimeDuration vsyncRate = 165 gfxPlatform::GetPlatform()->GetGlobalVsyncDispatcher()->GetVsyncRate(); 166 167 CompositorBridgeOptions options = WidgetCompositorOptions( 168 aScale, vsyncRate, aOptions, aUseExternalSurfaceSize, aSurfaceSize, 169 aInnerWindowId); 170 171 RefPtr<CompositorBridgeChild> bridge = new CompositorBridgeChild(sInstance); 172 if (NS_WARN_IF( 173 !sInstance->SendPCompositorBridgeConstructor(bridge, options))) { 174 return nullptr; 175 } 176 177 bridge->InitForWidget(aProcessToken, aLayerManager, aNamespace); 178 return bridge.forget(); 179 } 180 181 /* static */ 182 already_AddRefed<CompositorBridgeChild> 183 CompositorManagerChild::CreateSameProcessWidgetCompositorBridge( 184 WebRenderLayerManager* aLayerManager, uint32_t aNamespace) { 185 MOZ_ASSERT(XRE_IsParentProcess()); 186 MOZ_ASSERT(NS_IsMainThread()); 187 if (NS_WARN_IF(!sInstance || !sInstance->CanSend())) { 188 return nullptr; 189 } 190 191 CompositorBridgeOptions options = SameProcessWidgetCompositorOptions(); 192 193 RefPtr<CompositorBridgeChild> bridge = new CompositorBridgeChild(sInstance); 194 if (NS_WARN_IF( 195 !sInstance->SendPCompositorBridgeConstructor(bridge, options))) { 196 return nullptr; 197 } 198 199 bridge->InitForWidget(1, aLayerManager, aNamespace); 200 return bridge.forget(); 201 } 202 203 CompositorManagerChild::CompositorManagerChild(uint64_t aProcessToken, 204 uint32_t aNamespace, 205 bool aSameProcess) 206 : mProcessToken(aProcessToken), 207 mNamespace(aNamespace), 208 mResourceId(0), 209 mCanSend(false), 210 mSameProcess(aSameProcess), 211 mFwdTransactionCounter(this) {} 212 213 void CompositorManagerChild::ActorDestroy(ActorDestroyReason aReason) { 214 mCanSend = false; 215 if (sInstance == this) { 216 sInstance = nullptr; 217 } 218 } 219 220 void CompositorManagerChild::HandleFatalError(const char* aMsg) { 221 dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aMsg, OtherChildID()); 222 } 223 224 void CompositorManagerChild::ProcessingError(Result aCode, 225 const char* aReason) { 226 if (aCode != MsgDropped) { 227 gfxDevCrash(gfx::LogReason::ProcessingError) 228 << "Processing error in CompositorBridgeChild: " << int(aCode); 229 } 230 } 231 232 void CompositorManagerChild::SetReplyTimeout() { 233 #ifndef DEBUG 234 // Add a timeout for release builds to kill GPU process when it hangs. 235 if (XRE_IsParentProcess() && GPUProcessManager::Get()->GetGPUChild()) { 236 int32_t timeout = 237 StaticPrefs::layers_gpu_process_ipc_reply_timeout_ms_AtStartup(); 238 SetReplyTimeoutMs(timeout); 239 } 240 #endif 241 } 242 243 bool CompositorManagerChild::ShouldContinueFromReplyTimeout() { 244 MOZ_ASSERT_IF(mSyncIPCStartTimeStamp.isSome(), XRE_IsParentProcess()); 245 246 if (XRE_IsParentProcess()) { 247 #ifndef DEBUG 248 // Extend sync IPC reply timeout 249 if (mSyncIPCStartTimeStamp.isSome()) { 250 const int32_t maxDurationMs = StaticPrefs:: 251 layers_gpu_process_extend_ipc_reply_timeout_ms_AtStartup(); 252 const auto now = TimeStamp::Now(); 253 const auto durationMs = static_cast<int32_t>( 254 (now - mSyncIPCStartTimeStamp.ref()).ToMilliseconds()); 255 256 if (durationMs < maxDurationMs) { 257 return true; 258 } 259 } 260 #endif 261 gfxCriticalNote << "Killing GPU process due to IPC reply timeout"; 262 MOZ_DIAGNOSTIC_ASSERT(GPUProcessManager::Get()->GetGPUChild()); 263 GPUProcessManager::Get()->KillProcess(/* aGenerateMinidump */ true); 264 } 265 return false; 266 } 267 268 mozilla::ipc::IPCResult CompositorManagerChild::RecvNotifyWebRenderError( 269 const WebRenderError&& aError) { 270 MOZ_ASSERT(XRE_IsParentProcess()); 271 MOZ_ASSERT(NS_IsMainThread()); 272 GPUProcessManager::Get()->NotifyWebRenderError(aError); 273 return IPC_OK(); 274 } 275 276 void CompositorManagerChild::SetSyncIPCStartTimeStamp() { 277 MOZ_ASSERT(mSyncIPCStartTimeStamp.isNothing()); 278 279 mSyncIPCStartTimeStamp = Some(TimeStamp::Now()); 280 } 281 282 void CompositorManagerChild::ClearSyncIPCStartTimeStamp() { 283 mSyncIPCStartTimeStamp = Nothing(); 284 } 285 286 } // namespace layers 287 } // namespace mozilla