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