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