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/gfx/CanvasManagerParent.h"
10 #include "mozilla/webrender/RenderThread.h"
11 #include "mozilla/ipc/Endpoint.h"
12 #include "mozilla/layers/CompositorBridgeParent.h"
13 #include "mozilla/layers/ContentCompositorBridgeParent.h"
14 #include "mozilla/layers/CompositorThread.h"
15 #include "mozilla/layers/SharedSurfacesParent.h"
16 #include "mozilla/UniquePtr.h"
17 #include "mozilla/Unused.h"
18 #include "gfxPlatform.h"
19 #include "VsyncSource.h"
20 
21 namespace mozilla {
22 namespace layers {
23 
24 StaticRefPtr<CompositorManagerParent> CompositorManagerParent::sInstance;
25 StaticMutex CompositorManagerParent::sMutex;
26 
27 #ifdef COMPOSITOR_MANAGER_PARENT_EXPLICIT_SHUTDOWN
28 StaticAutoPtr<nsTArray<CompositorManagerParent*>>
29     CompositorManagerParent::sActiveActors;
30 #endif
31 
32 /* static */
33 already_AddRefed<CompositorManagerParent>
CreateSameProcess()34 CompositorManagerParent::CreateSameProcess() {
35   MOZ_ASSERT(XRE_IsParentProcess());
36   MOZ_ASSERT(NS_IsMainThread());
37   StaticMutexAutoLock lock(sMutex);
38 
39   // We are creating a manager for the UI process, inside the combined GPU/UI
40   // process. It is created more-or-less the same but we retain a reference to
41   // the parent to access state.
42   if (NS_WARN_IF(sInstance)) {
43     MOZ_ASSERT_UNREACHABLE("Already initialized");
44     return nullptr;
45   }
46 
47   // The child is responsible for setting up the IPC channel in the same
48   // process case because if we open from the child perspective, we can do it
49   // on the main thread and complete before we return the manager handles.
50   RefPtr<CompositorManagerParent> parent = new CompositorManagerParent();
51   parent->SetOtherProcessId(base::GetCurrentProcId());
52   return parent.forget();
53 }
54 
55 /* static */
Create(Endpoint<PCompositorManagerParent> && aEndpoint,bool aIsRoot)56 bool CompositorManagerParent::Create(
57     Endpoint<PCompositorManagerParent>&& aEndpoint, bool aIsRoot) {
58   MOZ_ASSERT(NS_IsMainThread());
59 
60   // We are creating a manager for the another process, inside the GPU process
61   // (or UI process if it subsumbed the GPU process).
62   MOZ_ASSERT(aEndpoint.OtherPid() != base::GetCurrentProcId());
63 
64   if (!CompositorThreadHolder::IsActive()) {
65     return false;
66   }
67 
68   RefPtr<CompositorManagerParent> bridge = new CompositorManagerParent();
69 
70   RefPtr<Runnable> runnable =
71       NewRunnableMethod<Endpoint<PCompositorManagerParent>&&, bool>(
72           "CompositorManagerParent::Bind", bridge,
73           &CompositorManagerParent::Bind, std::move(aEndpoint), aIsRoot);
74   CompositorThread()->Dispatch(runnable.forget());
75   return true;
76 }
77 
78 /* static */
79 already_AddRefed<CompositorBridgeParent>
CreateSameProcessWidgetCompositorBridge(CSSToLayoutDeviceScale aScale,const CompositorOptions & aOptions,bool aUseExternalSurfaceSize,const gfx::IntSize & aSurfaceSize)80 CompositorManagerParent::CreateSameProcessWidgetCompositorBridge(
81     CSSToLayoutDeviceScale aScale, const CompositorOptions& aOptions,
82     bool aUseExternalSurfaceSize, const gfx::IntSize& aSurfaceSize) {
83   MOZ_ASSERT(XRE_IsParentProcess());
84   MOZ_ASSERT(NS_IsMainThread());
85 
86   // When we are in a combined UI / GPU process, InProcessCompositorSession
87   // requires both the parent and child PCompositorBridge actors for its own
88   // construction, which is done on the main thread. Normally
89   // CompositorBridgeParent is created on the compositor thread via the IPDL
90   // plumbing (CompositorManagerParent::AllocPCompositorBridgeParent). Thus to
91   // actually get a reference to the parent, we would need to block on the
92   // compositor thread until it handles our constructor message. Because only
93   // one one IPDL constructor is permitted per parent and child protocol, we
94   // cannot make the normal case async and this case sync. Instead what we do
95   // is leave the constructor async (a boon to the content process setup) and
96   // create the parent ahead of time. It will pull the preinitialized parent
97   // from the queue when it receives the message and give that to IPDL.
98 
99   // Note that the static mutex not only is used to protect sInstance, but also
100   // mPendingCompositorBridges.
101   StaticMutexAutoLock lock(sMutex);
102   if (NS_WARN_IF(!sInstance)) {
103     return nullptr;
104   }
105 
106   TimeDuration vsyncRate = gfxPlatform::GetPlatform()
107                                ->GetHardwareVsync()
108                                ->GetGlobalDisplay()
109                                .GetVsyncRate();
110 
111   RefPtr<CompositorBridgeParent> bridge =
112       new CompositorBridgeParent(sInstance, aScale, vsyncRate, aOptions,
113                                  aUseExternalSurfaceSize, aSurfaceSize);
114 
115   sInstance->mPendingCompositorBridges.AppendElement(bridge);
116   return bridge.forget();
117 }
118 
CompositorManagerParent()119 CompositorManagerParent::CompositorManagerParent()
120     : mCompositorThreadHolder(CompositorThreadHolder::GetSingleton()) {}
121 
122 CompositorManagerParent::~CompositorManagerParent() = default;
123 
Bind(Endpoint<PCompositorManagerParent> && aEndpoint,bool aIsRoot)124 void CompositorManagerParent::Bind(
125     Endpoint<PCompositorManagerParent>&& aEndpoint, bool aIsRoot) {
126   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
127   if (NS_WARN_IF(!aEndpoint.Bind(this))) {
128     return;
129   }
130 
131   BindComplete(aIsRoot);
132 }
133 
BindComplete(bool aIsRoot)134 void CompositorManagerParent::BindComplete(bool aIsRoot) {
135   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread() ||
136              NS_IsMainThread());
137 
138   // Add the IPDL reference to ourself, so we can't get freed until IPDL is
139   // done with us.
140   AddRef();
141 
142   StaticMutexAutoLock lock(sMutex);
143   if (aIsRoot) {
144     sInstance = this;
145   }
146 
147 #ifdef COMPOSITOR_MANAGER_PARENT_EXPLICIT_SHUTDOWN
148   if (!sActiveActors) {
149     sActiveActors = new nsTArray<CompositorManagerParent*>();
150   }
151   sActiveActors->AppendElement(this);
152 #endif
153 }
154 
ActorDestroy(ActorDestroyReason aReason)155 void CompositorManagerParent::ActorDestroy(ActorDestroyReason aReason) {
156   SharedSurfacesParent::DestroyProcess(OtherPid());
157 
158   StaticMutexAutoLock lock(sMutex);
159   if (sInstance == this) {
160     sInstance = nullptr;
161   }
162 }
163 
ActorDealloc()164 void CompositorManagerParent::ActorDealloc() {
165   GetCurrentSerialEventTarget()->Dispatch(
166       NewRunnableMethod("layers::CompositorManagerParent::DeferredDestroy",
167                         this, &CompositorManagerParent::DeferredDestroy));
168 
169 #ifdef COMPOSITOR_MANAGER_PARENT_EXPLICIT_SHUTDOWN
170   StaticMutexAutoLock lock(sMutex);
171   if (sActiveActors) {
172     sActiveActors->RemoveElement(this);
173   }
174 #endif
175   Release();
176 }
177 
DeferredDestroy()178 void CompositorManagerParent::DeferredDestroy() {
179   mCompositorThreadHolder = nullptr;
180 }
181 
182 #ifdef COMPOSITOR_MANAGER_PARENT_EXPLICIT_SHUTDOWN
183 /* static */
ShutdownInternal()184 void CompositorManagerParent::ShutdownInternal() {
185   UniquePtr<nsTArray<CompositorManagerParent*>> actors;
186 
187   // We move here because we may attempt to acquire the same lock during the
188   // destroy to remove the reference in sActiveActors.
189   {
190     StaticMutexAutoLock lock(sMutex);
191     actors = WrapUnique(sActiveActors.forget());
192   }
193 
194   if (actors) {
195     for (auto& actor : *actors) {
196       actor->Close();
197     }
198   }
199 }
200 #endif  // COMPOSITOR_MANAGER_PARENT_EXPLICIT_SHUTDOWN
201 
202 /* static */
Shutdown()203 void CompositorManagerParent::Shutdown() {
204   MOZ_ASSERT(NS_IsMainThread());
205 
206 #ifdef COMPOSITOR_MANAGER_PARENT_EXPLICIT_SHUTDOWN
207   CompositorThread()->Dispatch(NS_NewRunnableFunction(
208       "layers::CompositorManagerParent::Shutdown",
209       []() -> void { CompositorManagerParent::ShutdownInternal(); }));
210 #endif
211 }
212 
213 already_AddRefed<PCompositorBridgeParent>
AllocPCompositorBridgeParent(const CompositorBridgeOptions & aOpt)214 CompositorManagerParent::AllocPCompositorBridgeParent(
215     const CompositorBridgeOptions& aOpt) {
216   switch (aOpt.type()) {
217     case CompositorBridgeOptions::TContentCompositorOptions: {
218       RefPtr<ContentCompositorBridgeParent> bridge =
219           new ContentCompositorBridgeParent(this);
220       return bridge.forget();
221     }
222     case CompositorBridgeOptions::TWidgetCompositorOptions: {
223       // Only the UI process is allowed to create widget compositors in the
224       // compositor process.
225       gfx::GPUParent* gpu = gfx::GPUParent::GetSingleton();
226       if (NS_WARN_IF(!gpu || OtherPid() != gpu->OtherPid())) {
227         MOZ_ASSERT_UNREACHABLE("Child cannot create widget compositor!");
228         break;
229       }
230 
231       const WidgetCompositorOptions& opt = aOpt.get_WidgetCompositorOptions();
232       RefPtr<CompositorBridgeParent> bridge = new CompositorBridgeParent(
233           this, opt.scale(), opt.vsyncRate(), opt.options(),
234           opt.useExternalSurfaceSize(), opt.surfaceSize());
235       return bridge.forget();
236     }
237     case CompositorBridgeOptions::TSameProcessWidgetCompositorOptions: {
238       // If the GPU and UI process are combined, we actually already created the
239       // CompositorBridgeParent, so we need to reuse that to inject it into the
240       // IPDL framework.
241       if (NS_WARN_IF(OtherPid() != base::GetCurrentProcId())) {
242         MOZ_ASSERT_UNREACHABLE("Child cannot create same process compositor!");
243         break;
244       }
245 
246       // Note that the static mutex not only is used to protect sInstance, but
247       // also mPendingCompositorBridges.
248       StaticMutexAutoLock lock(sMutex);
249       if (mPendingCompositorBridges.IsEmpty()) {
250         break;
251       }
252 
253       RefPtr<CompositorBridgeParent> bridge = mPendingCompositorBridges[0];
254       mPendingCompositorBridges.RemoveElementAt(0);
255       return bridge.forget();
256     }
257     default:
258       break;
259   }
260 
261   return nullptr;
262 }
263 
RecvAddSharedSurface(const wr::ExternalImageId & aId,SurfaceDescriptorShared && aDesc)264 mozilla::ipc::IPCResult CompositorManagerParent::RecvAddSharedSurface(
265     const wr::ExternalImageId& aId, SurfaceDescriptorShared&& aDesc) {
266   SharedSurfacesParent::Add(aId, std::move(aDesc), OtherPid());
267   return IPC_OK();
268 }
269 
RecvRemoveSharedSurface(const wr::ExternalImageId & aId)270 mozilla::ipc::IPCResult CompositorManagerParent::RecvRemoveSharedSurface(
271     const wr::ExternalImageId& aId) {
272   SharedSurfacesParent::Remove(aId);
273   return IPC_OK();
274 }
275 
RecvReportSharedSurfacesMemory(ReportSharedSurfacesMemoryResolver && aResolver)276 mozilla::ipc::IPCResult CompositorManagerParent::RecvReportSharedSurfacesMemory(
277     ReportSharedSurfacesMemoryResolver&& aResolver) {
278   SharedSurfacesMemoryReport report;
279   SharedSurfacesParent::AccumulateMemoryReport(OtherPid(), report);
280   aResolver(std::move(report));
281   return IPC_OK();
282 }
283 
RecvNotifyMemoryPressure()284 mozilla::ipc::IPCResult CompositorManagerParent::RecvNotifyMemoryPressure() {
285   nsTArray<PCompositorBridgeParent*> compositorBridges;
286   ManagedPCompositorBridgeParent(compositorBridges);
287   for (auto bridge : compositorBridges) {
288     static_cast<CompositorBridgeParentBase*>(bridge)->NotifyMemoryPressure();
289   }
290   return IPC_OK();
291 }
292 
RecvReportMemory(ReportMemoryResolver && aResolver)293 mozilla::ipc::IPCResult CompositorManagerParent::RecvReportMemory(
294     ReportMemoryResolver&& aResolver) {
295   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
296   MemoryReport aggregate;
297   PodZero(&aggregate);
298 
299   // Accumulate RenderBackend usage.
300   nsTArray<PCompositorBridgeParent*> compositorBridges;
301   ManagedPCompositorBridgeParent(compositorBridges);
302   for (auto bridge : compositorBridges) {
303     static_cast<CompositorBridgeParentBase*>(bridge)->AccumulateMemoryReport(
304         &aggregate);
305   }
306 
307   // Accumulate Renderer usage asynchronously, and resolve.
308   //
309   // Note that the IPDL machinery requires aResolver to be called on this
310   // thread, so we can't just pass it over to the renderer thread. We use
311   // an intermediate MozPromise instead.
312   wr::RenderThread::AccumulateMemoryReport(aggregate)->Then(
313       CompositorThread(), __func__,
314       [resolver = std::move(aResolver)](MemoryReport aReport) {
315         resolver(aReport);
316       },
317       [](bool) {
318         MOZ_ASSERT_UNREACHABLE("MemoryReport promises are never rejected");
319       });
320 
321   return IPC_OK();
322 }
323 
RecvInitCanvasManager(Endpoint<PCanvasManagerParent> && aEndpoint)324 mozilla::ipc::IPCResult CompositorManagerParent::RecvInitCanvasManager(
325     Endpoint<PCanvasManagerParent>&& aEndpoint) {
326   gfx::CanvasManagerParent::Init(std::move(aEndpoint));
327   return IPC_OK();
328 }
329 
330 /* static */
NotifyWebRenderError(wr::WebRenderError aError)331 void CompositorManagerParent::NotifyWebRenderError(wr::WebRenderError aError) {
332   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
333 
334   StaticMutexAutoLock lock(sMutex);
335   if (NS_WARN_IF(!sInstance)) {
336     return;
337   }
338   Unused << sInstance->SendNotifyWebRenderError(aError);
339 }
340 
341 }  // namespace layers
342 }  // namespace mozilla
343