/src/mozilla-central/gfx/layers/ipc/ImageBridgeParent.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 "ImageBridgeParent.h" |
8 | | #include <stdint.h> // for uint64_t, uint32_t |
9 | | #include "CompositableHost.h" // for CompositableParent, Create |
10 | | #include "base/message_loop.h" // for MessageLoop |
11 | | #include "base/process.h" // for ProcessId |
12 | | #include "base/task.h" // for CancelableTask, DeleteTask, etc |
13 | | #include "mozilla/ClearOnShutdown.h" |
14 | | #include "mozilla/gfx/Point.h" // for IntSize |
15 | | #include "mozilla/Hal.h" // for hal::SetCurrentThreadPriority() |
16 | | #include "mozilla/HalTypes.h" // for hal::THREAD_PRIORITY_COMPOSITOR |
17 | | #include "mozilla/ipc/MessageChannel.h" // for MessageChannel, etc |
18 | | #include "mozilla/ipc/ProtocolUtils.h" |
19 | | #include "mozilla/ipc/Transport.h" // for Transport |
20 | | #include "mozilla/media/MediaSystemResourceManagerParent.h" // for MediaSystemResourceManagerParent |
21 | | #include "mozilla/layers/CompositableTransactionParent.h" |
22 | | #include "mozilla/layers/LayerManagerComposite.h" |
23 | | #include "mozilla/layers/LayersMessages.h" // for EditReply |
24 | | #include "mozilla/layers/PImageBridgeParent.h" |
25 | | #include "mozilla/layers/TextureHostOGL.h" // for TextureHostOGL |
26 | | #include "mozilla/layers/Compositor.h" |
27 | | #include "mozilla/Monitor.h" |
28 | | #include "mozilla/mozalloc.h" // for operator new, etc |
29 | | #include "mozilla/Unused.h" |
30 | | #include "nsDebug.h" // for NS_ASSERTION, etc |
31 | | #include "nsISupportsImpl.h" // for ImageBridgeParent::Release, etc |
32 | | #include "nsTArray.h" // for nsTArray, nsTArray_Impl |
33 | | #include "nsTArrayForwardDeclare.h" // for InfallibleTArray |
34 | | #include "nsXULAppAPI.h" // for XRE_GetIOMessageLoop |
35 | | #include "mozilla/layers/TextureHost.h" |
36 | | #include "nsThreadUtils.h" |
37 | | |
38 | | namespace mozilla { |
39 | | namespace layers { |
40 | | |
41 | | using namespace mozilla::ipc; |
42 | | using namespace mozilla::gfx; |
43 | | using namespace mozilla::media; |
44 | | |
45 | | ImageBridgeParent::ImageBridgeMap ImageBridgeParent::sImageBridges; |
46 | | |
47 | | StaticAutoPtr<mozilla::Monitor> sImageBridgesLock; |
48 | | |
49 | | static StaticRefPtr<ImageBridgeParent> sImageBridgeParentSingleton; |
50 | | |
51 | | // defined in CompositorBridgeParent.cpp |
52 | | CompositorThreadHolder* GetCompositorThreadHolder(); |
53 | | |
54 | | /* static */ void |
55 | | ImageBridgeParent::Setup() |
56 | 0 | { |
57 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
58 | 0 | if (!sImageBridgesLock) { |
59 | 0 | sImageBridgesLock = new Monitor("ImageBridges"); |
60 | 0 | mozilla::ClearOnShutdown(&sImageBridgesLock); |
61 | 0 | } |
62 | 0 | } |
63 | | |
64 | | ImageBridgeParent::ImageBridgeParent(MessageLoop* aLoop, |
65 | | ProcessId aChildProcessId) |
66 | | : mMessageLoop(aLoop) |
67 | | , mClosed(false) |
68 | | , mCompositorThreadHolder(CompositorThreadHolder::GetSingleton()) |
69 | 0 | { |
70 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
71 | 0 | SetOtherProcessId(aChildProcessId); |
72 | 0 | } |
73 | | |
74 | | ImageBridgeParent::~ImageBridgeParent() |
75 | 0 | { |
76 | 0 | } |
77 | | |
78 | | /* static */ ImageBridgeParent* |
79 | | ImageBridgeParent::CreateSameProcess() |
80 | 0 | { |
81 | 0 | base::ProcessId pid = base::GetCurrentProcId(); |
82 | 0 | RefPtr<ImageBridgeParent> parent = |
83 | 0 | new ImageBridgeParent(CompositorThreadHolder::Loop(), pid); |
84 | 0 | parent->mSelfRef = parent; |
85 | 0 |
|
86 | 0 | { |
87 | 0 | MonitorAutoLock lock(*sImageBridgesLock); |
88 | 0 | MOZ_RELEASE_ASSERT(sImageBridges.count(pid) == 0); |
89 | 0 | sImageBridges[pid] = parent; |
90 | 0 | } |
91 | 0 |
|
92 | 0 | sImageBridgeParentSingleton = parent; |
93 | 0 | return parent; |
94 | 0 | } |
95 | | |
96 | | /* static */ bool |
97 | | ImageBridgeParent::CreateForGPUProcess(Endpoint<PImageBridgeParent>&& aEndpoint) |
98 | 0 | { |
99 | 0 | MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_GPU); |
100 | 0 |
|
101 | 0 | MessageLoop* loop = CompositorThreadHolder::Loop(); |
102 | 0 | RefPtr<ImageBridgeParent> parent = new ImageBridgeParent(loop, aEndpoint.OtherPid()); |
103 | 0 |
|
104 | 0 | loop->PostTask(NewRunnableMethod<Endpoint<PImageBridgeParent>&&>( |
105 | 0 | "layers::ImageBridgeParent::Bind", |
106 | 0 | parent, |
107 | 0 | &ImageBridgeParent::Bind, |
108 | 0 | std::move(aEndpoint))); |
109 | 0 |
|
110 | 0 | sImageBridgeParentSingleton = parent; |
111 | 0 | return true; |
112 | 0 | } |
113 | | |
114 | | /* static */ void |
115 | | ImageBridgeParent::ShutdownInternal() |
116 | 0 | { |
117 | 0 | // We make a copy because we don't want to hold the lock while closing and we |
118 | 0 | // don't want the object to get freed underneath us. |
119 | 0 | nsTArray<RefPtr<ImageBridgeParent>> actors; |
120 | 0 | { |
121 | 0 | MonitorAutoLock lock(*sImageBridgesLock); |
122 | 0 | for (const auto& iter : sImageBridges) { |
123 | 0 | actors.AppendElement(iter.second); |
124 | 0 | } |
125 | 0 | } |
126 | 0 |
|
127 | 0 | for (auto const& actor : actors) { |
128 | 0 | MOZ_RELEASE_ASSERT(!actor->mClosed); |
129 | 0 | actor->Close(); |
130 | 0 | } |
131 | 0 |
|
132 | 0 | sImageBridgeParentSingleton = nullptr; |
133 | 0 | } |
134 | | |
135 | | /* static */ void |
136 | | ImageBridgeParent::Shutdown() |
137 | 0 | { |
138 | 0 | CompositorThreadHolder::Loop()->PostTask( |
139 | 0 | NS_NewRunnableFunction("ImageBridgeParent::Shutdown", []() -> void { |
140 | 0 | ImageBridgeParent::ShutdownInternal(); |
141 | 0 | })); |
142 | 0 | } |
143 | | |
144 | | void |
145 | | ImageBridgeParent::ActorDestroy(ActorDestroyReason aWhy) |
146 | 0 | { |
147 | 0 | // Can't alloc/dealloc shmems from now on. |
148 | 0 | mClosed = true; |
149 | 0 | mCompositables.clear(); |
150 | 0 | { |
151 | 0 | MonitorAutoLock lock(*sImageBridgesLock); |
152 | 0 | sImageBridges.erase(OtherPid()); |
153 | 0 | } |
154 | 0 | MessageLoop::current()->PostTask( |
155 | 0 | NewRunnableMethod("layers::ImageBridgeParent::DeferredDestroy", |
156 | 0 | this, |
157 | 0 | &ImageBridgeParent::DeferredDestroy)); |
158 | 0 |
|
159 | 0 | // It is very important that this method gets called at shutdown (be it a clean |
160 | 0 | // or an abnormal shutdown), because DeferredDestroy is what clears mSelfRef. |
161 | 0 | // If mSelfRef is not null and ActorDestroy is not called, the ImageBridgeParent |
162 | 0 | // is leaked which causes the CompositorThreadHolder to be leaked and |
163 | 0 | // CompsoitorParent's shutdown ends up spinning the event loop forever, waiting |
164 | 0 | // for the compositor thread to terminate. |
165 | 0 | } |
166 | | |
167 | | class MOZ_STACK_CLASS AutoImageBridgeParentAsyncMessageSender |
168 | | { |
169 | | public: |
170 | | explicit AutoImageBridgeParentAsyncMessageSender(ImageBridgeParent* aImageBridge, |
171 | | InfallibleTArray<OpDestroy>* aToDestroy = nullptr) |
172 | | : mImageBridge(aImageBridge) |
173 | | , mToDestroy(aToDestroy) |
174 | 0 | { |
175 | 0 | mImageBridge->SetAboutToSendAsyncMessages(); |
176 | 0 | } |
177 | | |
178 | | ~AutoImageBridgeParentAsyncMessageSender() |
179 | 0 | { |
180 | 0 | mImageBridge->SendPendingAsyncMessages(); |
181 | 0 | if (mToDestroy) { |
182 | 0 | for (const auto& op : *mToDestroy) { |
183 | 0 | mImageBridge->DestroyActor(op); |
184 | 0 | } |
185 | 0 | } |
186 | 0 | } |
187 | | private: |
188 | | ImageBridgeParent* mImageBridge; |
189 | | InfallibleTArray<OpDestroy>* mToDestroy; |
190 | | }; |
191 | | |
192 | | mozilla::ipc::IPCResult |
193 | | ImageBridgeParent::RecvUpdate(EditArray&& aEdits, OpDestroyArray&& aToDestroy, |
194 | | const uint64_t& aFwdTransactionId) |
195 | 0 | { |
196 | 0 | // This ensures that destroy operations are always processed. It is not safe |
197 | 0 | // to early-return from RecvUpdate without doing so. |
198 | 0 | AutoImageBridgeParentAsyncMessageSender autoAsyncMessageSender(this, &aToDestroy); |
199 | 0 | UpdateFwdTransactionId(aFwdTransactionId); |
200 | 0 |
|
201 | 0 | for (const auto& edit : aEdits) { |
202 | 0 | RefPtr<CompositableHost> compositable = |
203 | 0 | FindCompositable(edit.compositable()); |
204 | 0 | if (!compositable || |
205 | 0 | !ReceiveCompositableUpdate(edit.detail(), WrapNotNull(compositable))) { |
206 | 0 | return IPC_FAIL_NO_REASON(this); |
207 | 0 | } |
208 | 0 | uint32_t dropped = compositable->GetDroppedFrames(); |
209 | 0 | if (dropped) { |
210 | 0 | Unused << SendReportFramesDropped(edit.compositable(), dropped); |
211 | 0 | } |
212 | 0 | } |
213 | 0 |
|
214 | 0 | if (!IsSameProcess()) { |
215 | 0 | // Ensure that any pending operations involving back and front |
216 | 0 | // buffers have completed, so that neither process stomps on the |
217 | 0 | // other's buffer contents. |
218 | 0 | LayerManagerComposite::PlatformSyncBeforeReplyUpdate(); |
219 | 0 | } |
220 | 0 |
|
221 | 0 | return IPC_OK(); |
222 | 0 | } |
223 | | |
224 | | /* static */ bool |
225 | | ImageBridgeParent::CreateForContent(Endpoint<PImageBridgeParent>&& aEndpoint) |
226 | 0 | { |
227 | 0 | MessageLoop* loop = CompositorThreadHolder::Loop(); |
228 | 0 |
|
229 | 0 | RefPtr<ImageBridgeParent> bridge = new ImageBridgeParent(loop, aEndpoint.OtherPid()); |
230 | 0 | loop->PostTask(NewRunnableMethod<Endpoint<PImageBridgeParent>&&>( |
231 | 0 | "layers::ImageBridgeParent::Bind", |
232 | 0 | bridge, |
233 | 0 | &ImageBridgeParent::Bind, |
234 | 0 | std::move(aEndpoint))); |
235 | 0 |
|
236 | 0 | return true; |
237 | 0 | } |
238 | | |
239 | | void |
240 | | ImageBridgeParent::Bind(Endpoint<PImageBridgeParent>&& aEndpoint) |
241 | 0 | { |
242 | 0 | if (!aEndpoint.Bind(this)) |
243 | 0 | return; |
244 | 0 | mSelfRef = this; |
245 | 0 |
|
246 | 0 | // If the child process ID was reused by the OS before the ImageBridgeParent |
247 | 0 | // object was destroyed, we need to clean it up first. |
248 | 0 | RefPtr<ImageBridgeParent> oldActor; |
249 | 0 | { |
250 | 0 | MonitorAutoLock lock(*sImageBridgesLock); |
251 | 0 | ImageBridgeMap::const_iterator i = sImageBridges.find(OtherPid()); |
252 | 0 | if (i != sImageBridges.end()) { |
253 | 0 | oldActor = i->second; |
254 | 0 | } |
255 | 0 | } |
256 | 0 |
|
257 | 0 | // We can't hold the lock during Close because it erases itself from the map. |
258 | 0 | if (oldActor) { |
259 | 0 | MOZ_RELEASE_ASSERT(!oldActor->mClosed); |
260 | 0 | oldActor->Close(); |
261 | 0 | } |
262 | 0 |
|
263 | 0 | { |
264 | 0 | MonitorAutoLock lock(*sImageBridgesLock); |
265 | 0 | sImageBridges[OtherPid()] = this; |
266 | 0 | } |
267 | 0 | } |
268 | | |
269 | | mozilla::ipc::IPCResult ImageBridgeParent::RecvWillClose() |
270 | 0 | { |
271 | 0 | // If there is any texture still alive we have to force it to deallocate the |
272 | 0 | // device data (GL textures, etc.) now because shortly after SenStop() returns |
273 | 0 | // on the child side the widget will be destroyed along with it's associated |
274 | 0 | // GL context. |
275 | 0 | InfallibleTArray<PTextureParent*> textures; |
276 | 0 | ManagedPTextureParent(textures); |
277 | 0 | for (unsigned int i = 0; i < textures.Length(); ++i) { |
278 | 0 | RefPtr<TextureHost> tex = TextureHost::AsTextureHost(textures[i]); |
279 | 0 | tex->DeallocateDeviceData(); |
280 | 0 | } |
281 | 0 | return IPC_OK(); |
282 | 0 | } |
283 | | |
284 | | mozilla::ipc::IPCResult |
285 | | ImageBridgeParent::RecvNewCompositable(const CompositableHandle& aHandle, |
286 | | const TextureInfo& aInfo, |
287 | | const LayersBackend& aLayersBackend) |
288 | 0 | { |
289 | 0 | bool useWebRender = aLayersBackend == LayersBackend::LAYERS_WR; |
290 | 0 | RefPtr<CompositableHost> host = AddCompositable(aHandle, aInfo, useWebRender); |
291 | 0 | if (!host) { |
292 | 0 | return IPC_FAIL_NO_REASON(this); |
293 | 0 | } |
294 | 0 |
|
295 | 0 | host->SetAsyncRef(AsyncCompositableRef(OtherPid(), aHandle)); |
296 | 0 | return IPC_OK(); |
297 | 0 | } |
298 | | |
299 | | mozilla::ipc::IPCResult |
300 | | ImageBridgeParent::RecvReleaseCompositable(const CompositableHandle& aHandle) |
301 | 0 | { |
302 | 0 | ReleaseCompositable(aHandle); |
303 | 0 | return IPC_OK(); |
304 | 0 | } |
305 | | |
306 | | PTextureParent* |
307 | | ImageBridgeParent::AllocPTextureParent(const SurfaceDescriptor& aSharedData, |
308 | | const ReadLockDescriptor& aReadLock, |
309 | | const LayersBackend& aLayersBackend, |
310 | | const TextureFlags& aFlags, |
311 | | const uint64_t& aSerial, |
312 | | const wr::MaybeExternalImageId& aExternalImageId) |
313 | 0 | { |
314 | 0 | return TextureHost::CreateIPDLActor(this, aSharedData, aReadLock, aLayersBackend, aFlags, aSerial, aExternalImageId); |
315 | 0 | } |
316 | | |
317 | | bool |
318 | | ImageBridgeParent::DeallocPTextureParent(PTextureParent* actor) |
319 | 0 | { |
320 | 0 | return TextureHost::DestroyIPDLActor(actor); |
321 | 0 | } |
322 | | |
323 | | PMediaSystemResourceManagerParent* |
324 | | ImageBridgeParent::AllocPMediaSystemResourceManagerParent() |
325 | 0 | { |
326 | 0 | return new mozilla::media::MediaSystemResourceManagerParent(); |
327 | 0 | } |
328 | | |
329 | | bool |
330 | | ImageBridgeParent::DeallocPMediaSystemResourceManagerParent(PMediaSystemResourceManagerParent* aActor) |
331 | 0 | { |
332 | 0 | MOZ_ASSERT(aActor); |
333 | 0 | delete static_cast<mozilla::media::MediaSystemResourceManagerParent*>(aActor); |
334 | 0 | return true; |
335 | 0 | } |
336 | | |
337 | | void |
338 | | ImageBridgeParent::SendAsyncMessage(const InfallibleTArray<AsyncParentMessageData>& aMessage) |
339 | 0 | { |
340 | 0 | mozilla::Unused << SendParentAsyncMessages(aMessage); |
341 | 0 | } |
342 | | |
343 | | class ProcessIdComparator |
344 | | { |
345 | | public: |
346 | | bool Equals(const ImageCompositeNotificationInfo& aA, |
347 | | const ImageCompositeNotificationInfo& aB) const |
348 | 0 | { |
349 | 0 | return aA.mImageBridgeProcessId == aB.mImageBridgeProcessId; |
350 | 0 | } |
351 | | bool LessThan(const ImageCompositeNotificationInfo& aA, |
352 | | const ImageCompositeNotificationInfo& aB) const |
353 | 0 | { |
354 | 0 | return aA.mImageBridgeProcessId < aB.mImageBridgeProcessId; |
355 | 0 | } |
356 | | }; |
357 | | |
358 | | /* static */ bool |
359 | | ImageBridgeParent::NotifyImageComposites(nsTArray<ImageCompositeNotificationInfo>& aNotifications) |
360 | 0 | { |
361 | 0 | // Group the notifications by destination process ID and then send the |
362 | 0 | // notifications in one message per group. |
363 | 0 | aNotifications.Sort(ProcessIdComparator()); |
364 | 0 | uint32_t i = 0; |
365 | 0 | bool ok = true; |
366 | 0 | while (i < aNotifications.Length()) { |
367 | 0 | AutoTArray<ImageCompositeNotification,1> notifications; |
368 | 0 | notifications.AppendElement(aNotifications[i].mNotification); |
369 | 0 | uint32_t end = i + 1; |
370 | 0 | MOZ_ASSERT(aNotifications[i].mNotification.compositable()); |
371 | 0 | ProcessId pid = aNotifications[i].mImageBridgeProcessId; |
372 | 0 | while (end < aNotifications.Length() && |
373 | 0 | aNotifications[end].mImageBridgeProcessId == pid) { |
374 | 0 | notifications.AppendElement(aNotifications[end].mNotification); |
375 | 0 | ++end; |
376 | 0 | } |
377 | 0 | RefPtr<ImageBridgeParent> bridge = GetInstance(pid); |
378 | 0 | if (!bridge || bridge->mClosed) { |
379 | 0 | continue; |
380 | 0 | } |
381 | 0 | bridge->SendPendingAsyncMessages(); |
382 | 0 | if (!bridge->SendDidComposite(notifications)) { |
383 | 0 | ok = false; |
384 | 0 | } |
385 | 0 | i = end; |
386 | 0 | } |
387 | 0 | return ok; |
388 | 0 | } |
389 | | |
390 | | void |
391 | | ImageBridgeParent::DeferredDestroy() |
392 | 0 | { |
393 | 0 | mCompositorThreadHolder = nullptr; |
394 | 0 | mSelfRef = nullptr; // "this" ImageBridge may get deleted here. |
395 | 0 | } |
396 | | |
397 | | already_AddRefed<ImageBridgeParent> |
398 | | ImageBridgeParent::GetInstance(ProcessId aId) |
399 | 0 | { |
400 | 0 | MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); |
401 | 0 | MonitorAutoLock lock(*sImageBridgesLock); |
402 | 0 | ImageBridgeMap::const_iterator i = sImageBridges.find(aId); |
403 | 0 | if (i == sImageBridges.end()) { |
404 | 0 | NS_ASSERTION(false, "Cannot find image bridge for process!"); |
405 | 0 | return nullptr; |
406 | 0 | } |
407 | 0 | RefPtr<ImageBridgeParent> bridge = i->second; |
408 | 0 | return bridge.forget(); |
409 | 0 | } |
410 | | |
411 | | bool |
412 | | ImageBridgeParent::AllocShmem(size_t aSize, |
413 | | ipc::SharedMemory::SharedMemoryType aType, |
414 | | ipc::Shmem* aShmem) |
415 | 0 | { |
416 | 0 | if (mClosed) { |
417 | 0 | return false; |
418 | 0 | } |
419 | 0 | return PImageBridgeParent::AllocShmem(aSize, aType, aShmem); |
420 | 0 | } |
421 | | |
422 | | bool |
423 | | ImageBridgeParent::AllocUnsafeShmem(size_t aSize, |
424 | | ipc::SharedMemory::SharedMemoryType aType, |
425 | | ipc::Shmem* aShmem) |
426 | 0 | { |
427 | 0 | if (mClosed) { |
428 | 0 | return false; |
429 | 0 | } |
430 | 0 | return PImageBridgeParent::AllocUnsafeShmem(aSize, aType, aShmem); |
431 | 0 | } |
432 | | |
433 | | void |
434 | | ImageBridgeParent::DeallocShmem(ipc::Shmem& aShmem) |
435 | 0 | { |
436 | 0 | if (mClosed) { |
437 | 0 | return; |
438 | 0 | } |
439 | 0 | PImageBridgeParent::DeallocShmem(aShmem); |
440 | 0 | } |
441 | | |
442 | | bool ImageBridgeParent::IsSameProcess() const |
443 | 0 | { |
444 | 0 | return OtherPid() == base::GetCurrentProcId(); |
445 | 0 | } |
446 | | |
447 | | void |
448 | | ImageBridgeParent::NotifyNotUsed(PTextureParent* aTexture, uint64_t aTransactionId) |
449 | 0 | { |
450 | 0 | RefPtr<TextureHost> texture = TextureHost::AsTextureHost(aTexture); |
451 | 0 | if (!texture) { |
452 | 0 | return; |
453 | 0 | } |
454 | 0 | |
455 | 0 | if (!(texture->GetFlags() & TextureFlags::RECYCLE)) { |
456 | 0 | return; |
457 | 0 | } |
458 | 0 | |
459 | 0 | uint64_t textureId = TextureHost::GetTextureSerial(aTexture); |
460 | 0 | mPendingAsyncMessage.push_back( |
461 | 0 | OpNotifyNotUsed(textureId, aTransactionId)); |
462 | 0 |
|
463 | 0 | if (!IsAboutToSendAsyncMessages()) { |
464 | 0 | SendPendingAsyncMessages(); |
465 | 0 | } |
466 | 0 | } |
467 | | |
468 | | } // namespace layers |
469 | | } // namespace mozilla |