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/gfxVars.h"
14 #include "mozilla/gfx/GPUProcessManager.h"
15 #include "mozilla/dom/ContentChild.h"  // for ContentChild
16 #include "mozilla/dom/BrowserChild.h"  // for BrowserChild
17 #include "mozilla/ipc/Endpoint.h"
18 #include "VsyncSource.h"
19 
20 namespace mozilla {
21 namespace layers {
22 
23 using gfx::GPUProcessManager;
24 
25 StaticRefPtr<CompositorManagerChild> CompositorManagerChild::sInstance;
26 
27 /* static */
IsInitialized(uint64_t aProcessToken)28 bool CompositorManagerChild::IsInitialized(uint64_t aProcessToken) {
29   MOZ_ASSERT(NS_IsMainThread());
30   return sInstance && sInstance->CanSend() &&
31          sInstance->mProcessToken == aProcessToken;
32 }
33 
34 /* static */
InitSameProcess(uint32_t aNamespace,uint64_t aProcessToken)35 void CompositorManagerChild::InitSameProcess(uint32_t aNamespace,
36                                              uint64_t aProcessToken) {
37   MOZ_ASSERT(NS_IsMainThread());
38   if (NS_WARN_IF(IsInitialized(aProcessToken))) {
39     MOZ_ASSERT_UNREACHABLE("Already initialized same process");
40     return;
41   }
42 
43   RefPtr<CompositorManagerParent> parent =
44       CompositorManagerParent::CreateSameProcess();
45   RefPtr<CompositorManagerChild> child =
46       new CompositorManagerChild(parent, aProcessToken, aNamespace);
47   if (NS_WARN_IF(!child->CanSend())) {
48     MOZ_DIAGNOSTIC_ASSERT(false, "Failed to open same process protocol");
49     return;
50   }
51 
52   parent->BindComplete(/* aIsRoot */ true);
53   sInstance = std::move(child);
54 }
55 
56 /* static */
Init(Endpoint<PCompositorManagerChild> && aEndpoint,uint32_t aNamespace,uint64_t aProcessToken)57 bool CompositorManagerChild::Init(Endpoint<PCompositorManagerChild>&& aEndpoint,
58                                   uint32_t aNamespace,
59                                   uint64_t aProcessToken /* = 0 */) {
60   MOZ_ASSERT(NS_IsMainThread());
61   if (sInstance) {
62     MOZ_ASSERT(sInstance->mNamespace != aNamespace);
63   }
64 
65   sInstance = new CompositorManagerChild(std::move(aEndpoint), aProcessToken,
66                                          aNamespace);
67   return sInstance->CanSend();
68 }
69 
70 /* static */
Shutdown()71 void CompositorManagerChild::Shutdown() {
72   MOZ_ASSERT(NS_IsMainThread());
73   CompositorBridgeChild::ShutDown();
74 
75   if (!sInstance) {
76     return;
77   }
78 
79   sInstance->Close();
80   sInstance = nullptr;
81 }
82 
83 /* static */
OnGPUProcessLost(uint64_t aProcessToken)84 void CompositorManagerChild::OnGPUProcessLost(uint64_t aProcessToken) {
85   MOZ_ASSERT(NS_IsMainThread());
86 
87   // Since GPUChild and CompositorManagerChild will race on ActorDestroy, we
88   // cannot know if the CompositorManagerChild is about to be released but has
89   // yet to be. As such, we want to pre-emptively set mCanSend to false.
90   if (sInstance && sInstance->mProcessToken == aProcessToken) {
91     sInstance->mCanSend = false;
92   }
93 }
94 
95 /* static */
CreateContentCompositorBridge(uint32_t aNamespace)96 bool CompositorManagerChild::CreateContentCompositorBridge(
97     uint32_t aNamespace) {
98   MOZ_ASSERT(NS_IsMainThread());
99   if (NS_WARN_IF(!sInstance || !sInstance->CanSend())) {
100     return false;
101   }
102 
103   CompositorBridgeOptions options = ContentCompositorOptions();
104 
105   RefPtr<CompositorBridgeChild> bridge = new CompositorBridgeChild(sInstance);
106   if (NS_WARN_IF(
107           !sInstance->SendPCompositorBridgeConstructor(bridge, options))) {
108     return false;
109   }
110 
111   bridge->InitForContent(aNamespace);
112   return true;
113 }
114 
115 /* static */
116 already_AddRefed<CompositorBridgeChild>
CreateWidgetCompositorBridge(uint64_t aProcessToken,LayerManager * aLayerManager,uint32_t aNamespace,CSSToLayoutDeviceScale aScale,const CompositorOptions & aOptions,bool aUseExternalSurfaceSize,const gfx::IntSize & aSurfaceSize)117 CompositorManagerChild::CreateWidgetCompositorBridge(
118     uint64_t aProcessToken, LayerManager* aLayerManager, uint32_t aNamespace,
119     CSSToLayoutDeviceScale aScale, const CompositorOptions& aOptions,
120     bool aUseExternalSurfaceSize, const gfx::IntSize& aSurfaceSize) {
121   MOZ_ASSERT(XRE_IsParentProcess());
122   MOZ_ASSERT(NS_IsMainThread());
123   if (NS_WARN_IF(!sInstance || !sInstance->CanSend())) {
124     return nullptr;
125   }
126 
127   TimeDuration vsyncRate = gfxPlatform::GetPlatform()
128                                ->GetHardwareVsync()
129                                ->GetGlobalDisplay()
130                                .GetVsyncRate();
131 
132   CompositorBridgeOptions options = WidgetCompositorOptions(
133       aScale, vsyncRate, aOptions, aUseExternalSurfaceSize, aSurfaceSize);
134 
135   RefPtr<CompositorBridgeChild> bridge = new CompositorBridgeChild(sInstance);
136   if (NS_WARN_IF(
137           !sInstance->SendPCompositorBridgeConstructor(bridge, options))) {
138     return nullptr;
139   }
140 
141   bridge->InitForWidget(aProcessToken, aLayerManager, aNamespace);
142   return bridge.forget();
143 }
144 
145 /* static */
146 already_AddRefed<CompositorBridgeChild>
CreateSameProcessWidgetCompositorBridge(LayerManager * aLayerManager,uint32_t aNamespace)147 CompositorManagerChild::CreateSameProcessWidgetCompositorBridge(
148     LayerManager* aLayerManager, uint32_t aNamespace) {
149   MOZ_ASSERT(XRE_IsParentProcess());
150   MOZ_ASSERT(NS_IsMainThread());
151   if (NS_WARN_IF(!sInstance || !sInstance->CanSend())) {
152     return nullptr;
153   }
154 
155   CompositorBridgeOptions options = SameProcessWidgetCompositorOptions();
156 
157   RefPtr<CompositorBridgeChild> bridge = new CompositorBridgeChild(sInstance);
158   if (NS_WARN_IF(
159           !sInstance->SendPCompositorBridgeConstructor(bridge, options))) {
160     return nullptr;
161   }
162 
163   bridge->InitForWidget(1, aLayerManager, aNamespace);
164   return bridge.forget();
165 }
166 
CompositorManagerChild(CompositorManagerParent * aParent,uint64_t aProcessToken,uint32_t aNamespace)167 CompositorManagerChild::CompositorManagerChild(CompositorManagerParent* aParent,
168                                                uint64_t aProcessToken,
169                                                uint32_t aNamespace)
170     : mProcessToken(aProcessToken),
171       mNamespace(aNamespace),
172       mResourceId(0),
173       mCanSend(false) {
174   MOZ_ASSERT(aParent);
175 
176   SetOtherProcessId(base::GetCurrentProcId());
177   ipc::MessageChannel* channel = aParent->GetIPCChannel();
178   if (NS_WARN_IF(!Open(channel, CompositorThread(), ipc::ChildSide))) {
179     return;
180   }
181 
182   mCanSend = true;
183   AddRef();
184   SetReplyTimeout();
185 }
186 
CompositorManagerChild(Endpoint<PCompositorManagerChild> && aEndpoint,uint64_t aProcessToken,uint32_t aNamespace)187 CompositorManagerChild::CompositorManagerChild(
188     Endpoint<PCompositorManagerChild>&& aEndpoint, uint64_t aProcessToken,
189     uint32_t aNamespace)
190     : mProcessToken(aProcessToken),
191       mNamespace(aNamespace),
192       mResourceId(0),
193       mCanSend(false) {
194   if (NS_WARN_IF(!aEndpoint.Bind(this))) {
195     return;
196   }
197 
198   mCanSend = true;
199   AddRef();
200   SetReplyTimeout();
201 }
202 
ActorDealloc()203 void CompositorManagerChild::ActorDealloc() {
204   MOZ_ASSERT(!mCanSend);
205   Release();
206 }
207 
ActorDestroy(ActorDestroyReason aReason)208 void CompositorManagerChild::ActorDestroy(ActorDestroyReason aReason) {
209   mCanSend = false;
210   if (sInstance == this) {
211     sInstance = nullptr;
212   }
213 }
214 
HandleFatalError(const char * aMsg) const215 void CompositorManagerChild::HandleFatalError(const char* aMsg) const {
216   dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aMsg, OtherPid());
217 }
218 
ProcessingError(Result aCode,const char * aReason)219 void CompositorManagerChild::ProcessingError(Result aCode,
220                                              const char* aReason) {
221   if (aCode != MsgDropped) {
222     gfxDevCrash(gfx::LogReason::ProcessingError)
223         << "Processing error in CompositorBridgeChild: " << int(aCode);
224   }
225 }
226 
SetReplyTimeout()227 void CompositorManagerChild::SetReplyTimeout() {
228 #ifndef DEBUG
229   // Add a timeout for release builds to kill GPU process when it hangs.
230   if (XRE_IsParentProcess() && GPUProcessManager::Get()->GetGPUChild()) {
231     int32_t timeout =
232         StaticPrefs::layers_gpu_process_ipc_reply_timeout_ms_AtStartup();
233     SetReplyTimeoutMs(timeout);
234   }
235 #endif
236 }
237 
ShouldContinueFromReplyTimeout()238 bool CompositorManagerChild::ShouldContinueFromReplyTimeout() {
239   if (XRE_IsParentProcess()) {
240     gfxCriticalNote << "Killing GPU process due to IPC reply timeout";
241     MOZ_DIAGNOSTIC_ASSERT(GPUProcessManager::Get()->GetGPUChild());
242     GPUProcessManager::Get()->KillProcess();
243   }
244   return false;
245 }
246 
RecvNotifyWebRenderError(const WebRenderError && aError)247 mozilla::ipc::IPCResult CompositorManagerChild::RecvNotifyWebRenderError(
248     const WebRenderError&& aError) {
249   MOZ_ASSERT(XRE_IsParentProcess());
250   MOZ_ASSERT(NS_IsMainThread());
251   GPUProcessManager::Get()->NotifyWebRenderError(aError);
252   return IPC_OK();
253 }
254 
255 }  // namespace layers
256 }  // namespace mozilla
257