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/CompositorManagerParent.h"
8 #include "mozilla/gfx/GPUParent.h"
9 #include "mozilla/layers/CompositorBridgeParent.h"
10 #include "mozilla/layers/CrossProcessCompositorBridgeParent.h"
11 #include "mozilla/layers/CompositorThread.h"
12 #include "mozilla/layers/SharedSurfacesParent.h"
13 #include "nsAutoPtr.h"
14 #include "VsyncSource.h"
15 
16 namespace mozilla {
17 namespace layers {
18 
19 StaticRefPtr<CompositorManagerParent> CompositorManagerParent::sInstance;
20 StaticMutex CompositorManagerParent::sMutex;
21 
22 #ifdef COMPOSITOR_MANAGER_PARENT_EXPLICIT_SHUTDOWN
23 StaticAutoPtr<nsTArray<CompositorManagerParent*>>
24     CompositorManagerParent::sActiveActors;
25 #endif
26 
27 /* static */ already_AddRefed<CompositorManagerParent>
CreateSameProcess()28 CompositorManagerParent::CreateSameProcess() {
29   MOZ_ASSERT(XRE_IsParentProcess());
30   MOZ_ASSERT(NS_IsMainThread());
31   StaticMutexAutoLock lock(sMutex);
32 
33   // We are creating a manager for the UI process, inside the combined GPU/UI
34   // process. It is created more-or-less the same but we retain a reference to
35   // the parent to access state.
36   if (NS_WARN_IF(sInstance)) {
37     MOZ_ASSERT_UNREACHABLE("Already initialized");
38     return nullptr;
39   }
40 
41   // The child is responsible for setting up the IPC channel in the same
42   // process case because if we open from the child perspective, we can do it
43   // on the main thread and complete before we return the manager handles.
44   RefPtr<CompositorManagerParent> parent = new CompositorManagerParent();
45   parent->SetOtherProcessId(base::GetCurrentProcId());
46   return parent.forget();
47 }
48 
49 /* static */
Create(Endpoint<PCompositorManagerParent> && aEndpoint)50 bool CompositorManagerParent::Create(
51     Endpoint<PCompositorManagerParent>&& aEndpoint) {
52   MOZ_ASSERT(NS_IsMainThread());
53 
54   // We are creating a manager for the another process, inside the GPU process
55   // (or UI process if it subsumbed the GPU process).
56   MOZ_ASSERT(aEndpoint.OtherPid() != base::GetCurrentProcId());
57 
58   if (!CompositorThreadHolder::IsActive()) {
59     return false;
60   }
61 
62   RefPtr<CompositorManagerParent> bridge = new CompositorManagerParent();
63 
64   RefPtr<Runnable> runnable =
65       NewRunnableMethod<Endpoint<PCompositorManagerParent>&&>(
66           "CompositorManagerParent::Bind", bridge,
67           &CompositorManagerParent::Bind, Move(aEndpoint));
68   CompositorThreadHolder::Loop()->PostTask(runnable.forget());
69   return true;
70 }
71 
72 /* static */ already_AddRefed<CompositorBridgeParent>
CreateSameProcessWidgetCompositorBridge(CSSToLayoutDeviceScale aScale,const CompositorOptions & aOptions,bool aUseExternalSurfaceSize,const gfx::IntSize & aSurfaceSize)73 CompositorManagerParent::CreateSameProcessWidgetCompositorBridge(
74     CSSToLayoutDeviceScale aScale, const CompositorOptions& aOptions,
75     bool aUseExternalSurfaceSize, const gfx::IntSize& aSurfaceSize) {
76   MOZ_ASSERT(XRE_IsParentProcess());
77   MOZ_ASSERT(NS_IsMainThread());
78 
79   // When we are in a combined UI / GPU process, InProcessCompositorSession
80   // requires both the parent and child PCompositorBridge actors for its own
81   // construction, which is done on the main thread. Normally
82   // CompositorBridgeParent is created on the compositor thread via the IPDL
83   // plumbing (CompositorManagerParent::AllocPCompositorBridgeParent). Thus to
84   // actually get a reference to the parent, we would need to block on the
85   // compositor thread until it handles our constructor message. Because only
86   // one one IPDL constructor is permitted per parent and child protocol, we
87   // cannot make the normal case async and this case sync. Instead what we do
88   // is leave the constructor async (a boon to the content process setup) and
89   // create the parent ahead of time. It will pull the preinitialized parent
90   // from the queue when it receives the message and give that to IPDL.
91 
92   // Note that the static mutex not only is used to protect sInstance, but also
93   // mPendingCompositorBridges.
94   StaticMutexAutoLock lock(sMutex);
95   if (NS_WARN_IF(!sInstance)) {
96     return nullptr;
97   }
98 
99   TimeDuration vsyncRate = gfxPlatform::GetPlatform()
100                                ->GetHardwareVsync()
101                                ->GetGlobalDisplay()
102                                .GetVsyncRate();
103 
104   RefPtr<CompositorBridgeParent> bridge =
105       new CompositorBridgeParent(sInstance, aScale, vsyncRate, aOptions,
106                                  aUseExternalSurfaceSize, aSurfaceSize);
107 
108   sInstance->mPendingCompositorBridges.AppendElement(bridge);
109   return bridge.forget();
110 }
111 
CompositorManagerParent()112 CompositorManagerParent::CompositorManagerParent()
113     : mCompositorThreadHolder(CompositorThreadHolder::GetSingleton()) {}
114 
~CompositorManagerParent()115 CompositorManagerParent::~CompositorManagerParent() {}
116 
Bind(Endpoint<PCompositorManagerParent> && aEndpoint)117 void CompositorManagerParent::Bind(
118     Endpoint<PCompositorManagerParent>&& aEndpoint) {
119   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
120   if (NS_WARN_IF(!aEndpoint.Bind(this))) {
121     return;
122   }
123 
124   BindComplete();
125 }
126 
BindComplete()127 void CompositorManagerParent::BindComplete() {
128   // Add the IPDL reference to ourself, so we can't get freed until IPDL is
129   // done with us.
130   AddRef();
131 
132   StaticMutexAutoLock lock(sMutex);
133   if (OtherPid() == base::GetCurrentProcId()) {
134     sInstance = this;
135   }
136 
137 #ifdef COMPOSITOR_MANAGER_PARENT_EXPLICIT_SHUTDOWN
138   if (!sActiveActors) {
139     sActiveActors = new nsTArray<CompositorManagerParent*>();
140   }
141   sActiveActors->AppendElement(this);
142 #endif
143 }
144 
ActorDestroy(ActorDestroyReason aReason)145 void CompositorManagerParent::ActorDestroy(ActorDestroyReason aReason) {
146   SharedSurfacesParent::DestroyProcess(OtherPid());
147 
148   StaticMutexAutoLock lock(sMutex);
149   if (sInstance == this) {
150     sInstance = nullptr;
151   }
152 }
153 
DeallocPCompositorManagerParent()154 void CompositorManagerParent::DeallocPCompositorManagerParent() {
155   MessageLoop::current()->PostTask(
156       NewRunnableMethod("layers::CompositorManagerParent::DeferredDestroy",
157                         this, &CompositorManagerParent::DeferredDestroy));
158 
159 #ifdef COMPOSITOR_MANAGER_PARENT_EXPLICIT_SHUTDOWN
160   StaticMutexAutoLock lock(sMutex);
161   if (sActiveActors) {
162     sActiveActors->RemoveElement(this);
163   }
164 #endif
165   Release();
166 }
167 
DeferredDestroy()168 void CompositorManagerParent::DeferredDestroy() {
169   mCompositorThreadHolder = nullptr;
170 }
171 
172 #ifdef COMPOSITOR_MANAGER_PARENT_EXPLICIT_SHUTDOWN
ShutdownInternal()173 /* static */ void CompositorManagerParent::ShutdownInternal() {
174   nsAutoPtr<nsTArray<CompositorManagerParent*>> actors;
175 
176   // We move here because we may attempt to acquire the same lock during the
177   // destroy to remove the reference in sActiveActors.
178   {
179     StaticMutexAutoLock lock(sMutex);
180     actors = sActiveActors.forget();
181   }
182 
183   if (actors) {
184     for (auto& actor : *actors) {
185       actor->Close();
186     }
187   }
188 }
189 #endif  // COMPOSITOR_MANAGER_PARENT_EXPLICIT_SHUTDOWN
190 
Shutdown()191 /* static */ void CompositorManagerParent::Shutdown() {
192   MOZ_ASSERT(NS_IsMainThread());
193 
194 #ifdef COMPOSITOR_MANAGER_PARENT_EXPLICIT_SHUTDOWN
195   CompositorThreadHolder::Loop()->PostTask(NS_NewRunnableFunction(
196       "layers::CompositorManagerParent::Shutdown",
197       []() -> void { CompositorManagerParent::ShutdownInternal(); }));
198 #endif
199 }
200 
AllocPCompositorBridgeParent(const CompositorBridgeOptions & aOpt)201 PCompositorBridgeParent* CompositorManagerParent::AllocPCompositorBridgeParent(
202     const CompositorBridgeOptions& aOpt) {
203   switch (aOpt.type()) {
204     case CompositorBridgeOptions::TContentCompositorOptions: {
205       CrossProcessCompositorBridgeParent* bridge =
206           new CrossProcessCompositorBridgeParent(this);
207       bridge->AddRef();
208       return bridge;
209     }
210     case CompositorBridgeOptions::TWidgetCompositorOptions: {
211       // Only the UI process is allowed to create widget compositors in the
212       // compositor process.
213       gfx::GPUParent* gpu = gfx::GPUParent::GetSingleton();
214       if (NS_WARN_IF(!gpu || OtherPid() != gpu->OtherPid())) {
215         MOZ_ASSERT_UNREACHABLE("Child cannot create widget compositor!");
216         break;
217       }
218 
219       const WidgetCompositorOptions& opt = aOpt.get_WidgetCompositorOptions();
220       CompositorBridgeParent* bridge = new CompositorBridgeParent(
221           this, opt.scale(), opt.vsyncRate(), opt.options(),
222           opt.useExternalSurfaceSize(), opt.surfaceSize());
223       bridge->AddRef();
224       return bridge;
225     }
226     case CompositorBridgeOptions::TSameProcessWidgetCompositorOptions: {
227       // If the GPU and UI process are combined, we actually already created the
228       // CompositorBridgeParent, so we need to reuse that to inject it into the
229       // IPDL framework.
230       if (NS_WARN_IF(OtherPid() != base::GetCurrentProcId())) {
231         MOZ_ASSERT_UNREACHABLE("Child cannot create same process compositor!");
232         break;
233       }
234 
235       // Note that the static mutex not only is used to protect sInstance, but
236       // also mPendingCompositorBridges.
237       StaticMutexAutoLock lock(sMutex);
238       MOZ_ASSERT(!mPendingCompositorBridges.IsEmpty());
239 
240       CompositorBridgeParent* bridge = mPendingCompositorBridges[0];
241       bridge->AddRef();
242       mPendingCompositorBridges.RemoveElementAt(0);
243       return bridge;
244     }
245     default:
246       break;
247   }
248 
249   return nullptr;
250 }
251 
DeallocPCompositorBridgeParent(PCompositorBridgeParent * aActor)252 bool CompositorManagerParent::DeallocPCompositorBridgeParent(
253     PCompositorBridgeParent* aActor) {
254   static_cast<CompositorBridgeParentBase*>(aActor)->Release();
255   return true;
256 }
257 
RecvAddSharedSurface(const wr::ExternalImageId & aId,const SurfaceDescriptorShared & aDesc)258 mozilla::ipc::IPCResult CompositorManagerParent::RecvAddSharedSurface(
259     const wr::ExternalImageId& aId, const SurfaceDescriptorShared& aDesc) {
260   SharedSurfacesParent::Add(aId, aDesc, OtherPid());
261   return IPC_OK();
262 }
263 
RecvRemoveSharedSurface(const wr::ExternalImageId & aId)264 mozilla::ipc::IPCResult CompositorManagerParent::RecvRemoveSharedSurface(
265     const wr::ExternalImageId& aId) {
266   SharedSurfacesParent::Remove(aId);
267   return IPC_OK();
268 }
269 
270 }  // namespace layers
271 }  // namespace mozilla
272