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 "ImageBridgeChild.h"
8
9 #include <vector> // for vector
10
11 #include "ImageBridgeParent.h" // for ImageBridgeParent
12 #include "ImageContainer.h" // for ImageContainer
13 #include "Layers.h" // for Layer, etc
14 #include "ShadowLayers.h" // for ShadowLayerForwarder
15 #include "SynchronousTask.h"
16 #include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
17 #include "mozilla/Monitor.h" // for Monitor, MonitorAutoLock
18 #include "mozilla/ReentrantMonitor.h" // for ReentrantMonitor, etc
19 #include "mozilla/StaticMutex.h"
20 #include "mozilla/StaticPtr.h" // for StaticRefPtr
21 #include "mozilla/dom/ContentChild.h"
22 #include "mozilla/gfx/Point.h" // for IntSize
23 #include "mozilla/gfx/gfxVars.h"
24 #include "mozilla/ipc/Endpoint.h"
25 #include "mozilla/ipc/MessageChannel.h" // for MessageChannel, etc
26 #include "mozilla/ipc/Transport.h" // for Transport
27 #include "mozilla/layers/CompositableClient.h" // for CompositableChild, etc
28 #include "mozilla/layers/CompositorThread.h"
29 #include "mozilla/layers/ISurfaceAllocator.h" // for ISurfaceAllocator
30 #include "mozilla/layers/ImageClient.h" // for ImageClient
31 #include "mozilla/layers/LayersMessages.h" // for CompositableOperation
32 #include "mozilla/layers/TextureClient.h" // for TextureClient
33 #include "mozilla/layers/TextureClient.h"
34 #include "mozilla/media/MediaSystemResourceManager.h" // for MediaSystemResourceManager
35 #include "mozilla/media/MediaSystemResourceManagerChild.h" // for MediaSystemResourceManagerChild
36 #include "mozilla/mozalloc.h" // for operator new, etc
37 #include "transport/runnable_utils.h"
38 #include "nsContentUtils.h"
39 #include "nsISupportsImpl.h" // for ImageContainer::AddRef, etc
40 #include "nsTArray.h" // for AutoTArray, nsTArray, etc
41 #include "nsTArrayForwardDeclare.h" // for AutoTArray
42 #include "nsThreadUtils.h" // for NS_IsMainThread
43
44 #if defined(XP_WIN)
45 # include "mozilla/gfx/DeviceManagerDx.h"
46 #endif
47
48 #ifdef MOZ_WIDGET_ANDROID
49 # include "mozilla/layers/AndroidHardwareBuffer.h"
50 #endif
51
52 namespace mozilla {
53 namespace ipc {
54 class Shmem;
55 } // namespace ipc
56
57 namespace layers {
58
59 using namespace mozilla::ipc;
60 using namespace mozilla::gfx;
61 using namespace mozilla::media;
62
63 typedef std::vector<CompositableOperation> OpVector;
64 typedef nsTArray<OpDestroy> OpDestroyVector;
65
66 struct CompositableTransaction {
CompositableTransactionmozilla::layers::CompositableTransaction67 CompositableTransaction() : mFinished(true) {}
~CompositableTransactionmozilla::layers::CompositableTransaction68 ~CompositableTransaction() { End(); }
Finishedmozilla::layers::CompositableTransaction69 bool Finished() const { return mFinished; }
Beginmozilla::layers::CompositableTransaction70 void Begin() {
71 MOZ_ASSERT(mFinished);
72 mFinished = false;
73 }
Endmozilla::layers::CompositableTransaction74 void End() {
75 mFinished = true;
76 mOperations.clear();
77 mDestroyedActors.Clear();
78 }
IsEmptymozilla::layers::CompositableTransaction79 bool IsEmpty() const {
80 return mOperations.empty() && mDestroyedActors.IsEmpty();
81 }
AddNoSwapEditmozilla::layers::CompositableTransaction82 void AddNoSwapEdit(const CompositableOperation& op) {
83 MOZ_ASSERT(!Finished(), "forgot BeginTransaction?");
84 mOperations.push_back(op);
85 }
86
87 OpVector mOperations;
88 OpDestroyVector mDestroyedActors;
89
90 bool mFinished;
91 };
92
93 struct AutoEndTransaction final {
AutoEndTransactionmozilla::layers::AutoEndTransaction94 explicit AutoEndTransaction(CompositableTransaction* aTxn) : mTxn(aTxn) {}
~AutoEndTransactionmozilla::layers::AutoEndTransaction95 ~AutoEndTransaction() { mTxn->End(); }
96 CompositableTransaction* mTxn;
97 };
98
UseTextures(CompositableClient * aCompositable,const nsTArray<TimedTextureClient> & aTextures)99 void ImageBridgeChild::UseTextures(
100 CompositableClient* aCompositable,
101 const nsTArray<TimedTextureClient>& aTextures) {
102 MOZ_ASSERT(aCompositable);
103 MOZ_ASSERT(aCompositable->GetIPCHandle());
104 MOZ_ASSERT(aCompositable->IsConnected());
105
106 AutoTArray<TimedTexture, 4> textures;
107
108 for (auto& t : aTextures) {
109 MOZ_ASSERT(t.mTextureClient);
110 MOZ_ASSERT(t.mTextureClient->GetIPDLActor());
111
112 if (!t.mTextureClient->IsSharedWithCompositor()) {
113 return;
114 }
115
116 bool readLocked = t.mTextureClient->OnForwardedToHost();
117
118 auto fenceFd = t.mTextureClient->GetInternalData()->GetAcquireFence();
119 if (fenceFd.IsValid()) {
120 mTxn->AddNoSwapEdit(CompositableOperation(
121 aCompositable->GetIPCHandle(),
122 OpDeliverAcquireFence(nullptr, t.mTextureClient->GetIPDLActor(),
123 fenceFd)));
124 }
125
126 textures.AppendElement(
127 TimedTexture(nullptr, t.mTextureClient->GetIPDLActor(), t.mTimeStamp,
128 t.mPictureRect, t.mFrameID, t.mProducerID, readLocked));
129
130 // Wait end of usage on host side if TextureFlags::RECYCLE is set
131 HoldUntilCompositableRefReleasedIfNecessary(t.mTextureClient);
132 }
133 mTxn->AddNoSwapEdit(CompositableOperation(aCompositable->GetIPCHandle(),
134 OpUseTexture(textures)));
135 }
136
UseComponentAlphaTextures(CompositableClient * aCompositable,TextureClient * aTextureOnBlack,TextureClient * aTextureOnWhite)137 void ImageBridgeChild::UseComponentAlphaTextures(
138 CompositableClient* aCompositable, TextureClient* aTextureOnBlack,
139 TextureClient* aTextureOnWhite) {
140 MOZ_CRASH("should not be called");
141 }
142
HoldUntilCompositableRefReleasedIfNecessary(TextureClient * aClient)143 void ImageBridgeChild::HoldUntilCompositableRefReleasedIfNecessary(
144 TextureClient* aClient) {
145 if (!aClient) {
146 return;
147 }
148
149 #ifdef MOZ_WIDGET_ANDROID
150 auto bufferId = aClient->GetInternalData()->GetBufferId();
151 if (bufferId.isSome()) {
152 MOZ_ASSERT(aClient->GetFlags() & TextureFlags::WAIT_HOST_USAGE_END);
153 AndroidHardwareBufferManager::Get()->HoldUntilNotifyNotUsed(
154 bufferId.ref(), GetFwdTransactionId(), /* aUsesImageBridge */ true);
155 }
156 #endif
157
158 // Wait ReleaseCompositableRef only when TextureFlags::RECYCLE or
159 // TextureFlags::WAIT_HOST_USAGE_END is set on ImageBridge.
160 bool waitNotifyNotUsed =
161 aClient->GetFlags() & TextureFlags::RECYCLE ||
162 aClient->GetFlags() & TextureFlags::WAIT_HOST_USAGE_END;
163 if (!waitNotifyNotUsed) {
164 return;
165 }
166
167 aClient->SetLastFwdTransactionId(GetFwdTransactionId());
168 mTexturesWaitingNotifyNotUsed.emplace(aClient->GetSerial(), aClient);
169 }
170
NotifyNotUsed(uint64_t aTextureId,uint64_t aFwdTransactionId)171 void ImageBridgeChild::NotifyNotUsed(uint64_t aTextureId,
172 uint64_t aFwdTransactionId) {
173 auto it = mTexturesWaitingNotifyNotUsed.find(aTextureId);
174 if (it != mTexturesWaitingNotifyNotUsed.end()) {
175 if (aFwdTransactionId < it->second->GetLastFwdTransactionId()) {
176 // Released on host side, but client already requested newer use texture.
177 return;
178 }
179 mTexturesWaitingNotifyNotUsed.erase(it);
180 }
181 }
182
CancelWaitForNotifyNotUsed(uint64_t aTextureId)183 void ImageBridgeChild::CancelWaitForNotifyNotUsed(uint64_t aTextureId) {
184 MOZ_ASSERT(InImageBridgeChildThread());
185 mTexturesWaitingNotifyNotUsed.erase(aTextureId);
186 }
187
188 // Singleton
189 static StaticMutex sImageBridgeSingletonLock;
190 static StaticRefPtr<ImageBridgeChild> sImageBridgeChildSingleton;
191 static StaticRefPtr<nsIThread> sImageBridgeChildThread;
192
193 // dispatched function
ShutdownStep1(SynchronousTask * aTask)194 void ImageBridgeChild::ShutdownStep1(SynchronousTask* aTask) {
195 AutoCompleteTask complete(aTask);
196
197 MOZ_ASSERT(InImageBridgeChildThread(),
198 "Should be in ImageBridgeChild thread.");
199
200 MediaSystemResourceManager::Shutdown();
201
202 // Force all managed protocols to shut themselves down cleanly
203 nsTArray<PTextureChild*> textures;
204 ManagedPTextureChild(textures);
205 for (int i = textures.Length() - 1; i >= 0; --i) {
206 RefPtr<TextureClient> client = TextureClient::AsTextureClient(textures[i]);
207 if (client) {
208 client->Destroy();
209 }
210 }
211
212 if (mCanSend) {
213 SendWillClose();
214 }
215 MarkShutDown();
216
217 // From now on, no message can be sent through the image bridge from the
218 // client side except the final Stop message.
219 }
220
221 // dispatched function
ShutdownStep2(SynchronousTask * aTask)222 void ImageBridgeChild::ShutdownStep2(SynchronousTask* aTask) {
223 AutoCompleteTask complete(aTask);
224
225 MOZ_ASSERT(InImageBridgeChildThread(),
226 "Should be in ImageBridgeChild thread.");
227 if (!mDestroyed) {
228 Close();
229 }
230 }
231
ActorDestroy(ActorDestroyReason aWhy)232 void ImageBridgeChild::ActorDestroy(ActorDestroyReason aWhy) {
233 mCanSend = false;
234 mDestroyed = true;
235 {
236 MutexAutoLock lock(mContainerMapLock);
237 mImageContainerListeners.clear();
238 }
239 }
240
ActorDealloc()241 void ImageBridgeChild::ActorDealloc() { this->Release(); }
242
CreateImageClientSync(SynchronousTask * aTask,RefPtr<ImageClient> * result,CompositableType aType,ImageContainer * aImageContainer)243 void ImageBridgeChild::CreateImageClientSync(SynchronousTask* aTask,
244 RefPtr<ImageClient>* result,
245 CompositableType aType,
246 ImageContainer* aImageContainer) {
247 AutoCompleteTask complete(aTask);
248 *result = CreateImageClientNow(aType, aImageContainer);
249 }
250
ImageBridgeChild(uint32_t aNamespace)251 ImageBridgeChild::ImageBridgeChild(uint32_t aNamespace)
252 : mNamespace(aNamespace),
253 mCanSend(false),
254 mDestroyed(false),
255 mFwdTransactionId(0),
256 mContainerMapLock("ImageBridgeChild.mContainerMapLock") {
257 MOZ_ASSERT(mNamespace);
258 MOZ_ASSERT(NS_IsMainThread());
259
260 mTxn = new CompositableTransaction();
261 }
262
~ImageBridgeChild()263 ImageBridgeChild::~ImageBridgeChild() { delete mTxn; }
264
MarkShutDown()265 void ImageBridgeChild::MarkShutDown() {
266 mTexturesWaitingNotifyNotUsed.clear();
267
268 mCanSend = false;
269 }
270
Connect(CompositableClient * aCompositable,ImageContainer * aImageContainer)271 void ImageBridgeChild::Connect(CompositableClient* aCompositable,
272 ImageContainer* aImageContainer) {
273 MOZ_ASSERT(aCompositable);
274 MOZ_ASSERT(InImageBridgeChildThread());
275 MOZ_ASSERT(CanSend());
276
277 // Note: this is static, rather than per-IBC, so IDs are not re-used across
278 // ImageBridgeChild instances. This is relevant for the GPU process, where
279 // we don't want old IDs to potentially leak into a recreated ImageBridge.
280 static uint64_t sNextID = 1;
281 uint64_t id = sNextID++;
282
283 // ImageClient of ImageContainer provides aImageContainer.
284 // But offscreen canvas does not provide it.
285 if (aImageContainer) {
286 MutexAutoLock lock(mContainerMapLock);
287 MOZ_ASSERT(mImageContainerListeners.find(id) ==
288 mImageContainerListeners.end());
289 mImageContainerListeners.emplace(
290 id, aImageContainer->GetImageContainerListener());
291 }
292
293 CompositableHandle handle(id);
294 aCompositable->InitIPDL(handle);
295 SendNewCompositable(handle, aCompositable->GetTextureInfo(),
296 GetCompositorBackendType());
297 }
298
ForgetImageContainer(const CompositableHandle & aHandle)299 void ImageBridgeChild::ForgetImageContainer(const CompositableHandle& aHandle) {
300 MutexAutoLock lock(mContainerMapLock);
301 mImageContainerListeners.erase(aHandle.Value());
302 }
303
304 /* static */
GetSingleton()305 RefPtr<ImageBridgeChild> ImageBridgeChild::GetSingleton() {
306 StaticMutexAutoLock lock(sImageBridgeSingletonLock);
307 return sImageBridgeChildSingleton;
308 }
309
UpdateImageClient(RefPtr<ImageContainer> aContainer)310 void ImageBridgeChild::UpdateImageClient(RefPtr<ImageContainer> aContainer) {
311 if (!aContainer) {
312 return;
313 }
314
315 if (!InImageBridgeChildThread()) {
316 RefPtr<Runnable> runnable =
317 WrapRunnable(RefPtr<ImageBridgeChild>(this),
318 &ImageBridgeChild::UpdateImageClient, aContainer);
319 GetThread()->Dispatch(runnable.forget());
320 return;
321 }
322
323 if (!CanSend()) {
324 return;
325 }
326
327 RefPtr<ImageClient> client = aContainer->GetImageClient();
328 if (NS_WARN_IF(!client)) {
329 return;
330 }
331
332 // If the client has become disconnected before this event was dispatched,
333 // early return now.
334 if (!client->IsConnected()) {
335 return;
336 }
337
338 BeginTransaction();
339 client->UpdateImage(aContainer, Layer::CONTENT_OPAQUE);
340 EndTransaction();
341 }
342
FlushAllImagesSync(SynchronousTask * aTask,ImageClient * aClient,ImageContainer * aContainer)343 void ImageBridgeChild::FlushAllImagesSync(SynchronousTask* aTask,
344 ImageClient* aClient,
345 ImageContainer* aContainer) {
346 AutoCompleteTask complete(aTask);
347
348 if (!CanSend()) {
349 return;
350 }
351
352 MOZ_ASSERT(aClient);
353 BeginTransaction();
354 if (aContainer) {
355 aContainer->ClearImagesFromImageBridge();
356 }
357 aClient->FlushAllImages();
358 EndTransaction();
359 }
360
FlushAllImages(ImageClient * aClient,ImageContainer * aContainer)361 void ImageBridgeChild::FlushAllImages(ImageClient* aClient,
362 ImageContainer* aContainer) {
363 MOZ_ASSERT(aClient);
364 MOZ_ASSERT(!InImageBridgeChildThread());
365
366 if (InImageBridgeChildThread()) {
367 NS_ERROR(
368 "ImageBridgeChild::FlushAllImages() is called on ImageBridge thread.");
369 return;
370 }
371
372 SynchronousTask task("FlushAllImages Lock");
373
374 // RefPtrs on arguments are not needed since this dispatches synchronously.
375 RefPtr<Runnable> runnable = WrapRunnable(
376 RefPtr<ImageBridgeChild>(this), &ImageBridgeChild::FlushAllImagesSync,
377 &task, aClient, aContainer);
378 GetThread()->Dispatch(runnable.forget());
379
380 task.Wait();
381 }
382
BeginTransaction()383 void ImageBridgeChild::BeginTransaction() {
384 MOZ_ASSERT(CanSend());
385 MOZ_ASSERT(mTxn->Finished(), "uncommitted txn?");
386 UpdateFwdTransactionId();
387 mTxn->Begin();
388 }
389
EndTransaction()390 void ImageBridgeChild::EndTransaction() {
391 MOZ_ASSERT(CanSend());
392 MOZ_ASSERT(!mTxn->Finished(), "forgot BeginTransaction?");
393
394 AutoEndTransaction _(mTxn);
395
396 if (mTxn->IsEmpty()) {
397 return;
398 }
399
400 AutoTArray<CompositableOperation, 10> cset;
401 cset.SetCapacity(mTxn->mOperations.size());
402 if (!mTxn->mOperations.empty()) {
403 cset.AppendElements(&mTxn->mOperations.front(), mTxn->mOperations.size());
404 }
405
406 if (!IsSameProcess()) {
407 ShadowLayerForwarder::PlatformSyncBeforeUpdate();
408 }
409
410 if (!SendUpdate(cset, mTxn->mDestroyedActors, GetFwdTransactionId())) {
411 NS_WARNING("could not send async texture transaction");
412 return;
413 }
414 }
415
InitForContent(Endpoint<PImageBridgeChild> && aEndpoint,uint32_t aNamespace)416 bool ImageBridgeChild::InitForContent(Endpoint<PImageBridgeChild>&& aEndpoint,
417 uint32_t aNamespace) {
418 MOZ_ASSERT(NS_IsMainThread());
419
420 gfxPlatform::GetPlatform();
421
422 if (!sImageBridgeChildThread) {
423 nsCOMPtr<nsIThread> thread;
424 nsresult rv = NS_NewNamedThread("ImageBridgeChld", getter_AddRefs(thread));
425 MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv),
426 "Failed to start ImageBridgeChild thread!");
427 sImageBridgeChildThread = thread.forget();
428 }
429
430 RefPtr<ImageBridgeChild> child = new ImageBridgeChild(aNamespace);
431
432 child->GetThread()->Dispatch(NS_NewRunnableFunction(
433 "layers::ImageBridgeChild::Bind",
434 [child, endpoint = std::move(aEndpoint)]() mutable {
435 child->Bind(std::move(endpoint));
436 }));
437
438 // Assign this after so other threads can't post messages before we connect to
439 // IPDL.
440 {
441 StaticMutexAutoLock lock(sImageBridgeSingletonLock);
442 sImageBridgeChildSingleton = child;
443 }
444
445 return true;
446 }
447
ReinitForContent(Endpoint<PImageBridgeChild> && aEndpoint,uint32_t aNamespace)448 bool ImageBridgeChild::ReinitForContent(Endpoint<PImageBridgeChild>&& aEndpoint,
449 uint32_t aNamespace) {
450 MOZ_ASSERT(NS_IsMainThread());
451
452 // Note that at this point, ActorDestroy may not have been called yet,
453 // meaning mCanSend is still true. In this case we will try to send a
454 // synchronous WillClose message to the parent, and will certainly get a
455 // false result and a MsgDropped processing error. This is okay.
456 ShutdownSingleton();
457
458 return InitForContent(std::move(aEndpoint), aNamespace);
459 }
460
Bind(Endpoint<PImageBridgeChild> && aEndpoint)461 void ImageBridgeChild::Bind(Endpoint<PImageBridgeChild>&& aEndpoint) {
462 if (!aEndpoint.Bind(this)) {
463 return;
464 }
465
466 // This reference is dropped in DeallocPImageBridgeChild.
467 this->AddRef();
468
469 mCanSend = true;
470 }
471
BindSameProcess(RefPtr<ImageBridgeParent> aParent)472 void ImageBridgeChild::BindSameProcess(RefPtr<ImageBridgeParent> aParent) {
473 ipc::MessageChannel* parentChannel = aParent->GetIPCChannel();
474 Open(parentChannel, aParent->GetThread(), mozilla::ipc::ChildSide);
475
476 // This reference is dropped in DeallocPImageBridgeChild.
477 this->AddRef();
478
479 mCanSend = true;
480 }
481
482 /* static */
ShutDown()483 void ImageBridgeChild::ShutDown() {
484 MOZ_ASSERT(NS_IsMainThread());
485
486 ShutdownSingleton();
487
488 if (sImageBridgeChildThread) {
489 sImageBridgeChildThread->Shutdown();
490 sImageBridgeChildThread = nullptr;
491 }
492 }
493
494 /* static */
ShutdownSingleton()495 void ImageBridgeChild::ShutdownSingleton() {
496 MOZ_ASSERT(NS_IsMainThread());
497
498 if (RefPtr<ImageBridgeChild> child = GetSingleton()) {
499 child->WillShutdown();
500
501 StaticMutexAutoLock lock(sImageBridgeSingletonLock);
502 sImageBridgeChildSingleton = nullptr;
503 }
504 }
505
WillShutdown()506 void ImageBridgeChild::WillShutdown() {
507 {
508 SynchronousTask task("ImageBridge ShutdownStep1 lock");
509
510 RefPtr<Runnable> runnable =
511 WrapRunnable(RefPtr<ImageBridgeChild>(this),
512 &ImageBridgeChild::ShutdownStep1, &task);
513 GetThread()->Dispatch(runnable.forget());
514
515 task.Wait();
516 }
517
518 {
519 SynchronousTask task("ImageBridge ShutdownStep2 lock");
520
521 RefPtr<Runnable> runnable =
522 WrapRunnable(RefPtr<ImageBridgeChild>(this),
523 &ImageBridgeChild::ShutdownStep2, &task);
524 GetThread()->Dispatch(runnable.forget());
525
526 task.Wait();
527 }
528 }
529
InitSameProcess(uint32_t aNamespace)530 void ImageBridgeChild::InitSameProcess(uint32_t aNamespace) {
531 NS_ASSERTION(NS_IsMainThread(), "Should be on the main Thread!");
532
533 MOZ_ASSERT(!sImageBridgeChildSingleton);
534 MOZ_ASSERT(!sImageBridgeChildThread);
535
536 nsCOMPtr<nsIThread> thread;
537 nsresult rv = NS_NewNamedThread("ImageBridgeChld", getter_AddRefs(thread));
538 MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv),
539 "Failed to start ImageBridgeChild thread!");
540 sImageBridgeChildThread = thread.forget();
541
542 RefPtr<ImageBridgeChild> child = new ImageBridgeChild(aNamespace);
543 RefPtr<ImageBridgeParent> parent = ImageBridgeParent::CreateSameProcess();
544
545 RefPtr<Runnable> runnable =
546 WrapRunnable(child, &ImageBridgeChild::BindSameProcess, parent);
547 child->GetThread()->Dispatch(runnable.forget());
548
549 // Assign this after so other threads can't post messages before we connect to
550 // IPDL.
551 {
552 StaticMutexAutoLock lock(sImageBridgeSingletonLock);
553 sImageBridgeChildSingleton = child;
554 }
555 }
556
557 /* static */
InitWithGPUProcess(Endpoint<PImageBridgeChild> && aEndpoint,uint32_t aNamespace)558 void ImageBridgeChild::InitWithGPUProcess(
559 Endpoint<PImageBridgeChild>&& aEndpoint, uint32_t aNamespace) {
560 MOZ_ASSERT(NS_IsMainThread());
561 MOZ_ASSERT(!sImageBridgeChildSingleton);
562 MOZ_ASSERT(!sImageBridgeChildThread);
563
564 nsCOMPtr<nsIThread> thread;
565 nsresult rv = NS_NewNamedThread("ImageBridgeChld", getter_AddRefs(thread));
566 MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv),
567 "Failed to start ImageBridgeChild thread!");
568 sImageBridgeChildThread = thread.forget();
569
570 RefPtr<ImageBridgeChild> child = new ImageBridgeChild(aNamespace);
571
572 child->GetThread()->Dispatch(NS_NewRunnableFunction(
573 "layers::ImageBridgeChild::Bind",
574 [child, endpoint = std::move(aEndpoint)]() mutable {
575 child->Bind(std::move(endpoint));
576 }));
577
578 // Assign this after so other threads can't post messages before we connect to
579 // IPDL.
580 {
581 StaticMutexAutoLock lock(sImageBridgeSingletonLock);
582 sImageBridgeChildSingleton = child;
583 }
584 }
585
InImageBridgeChildThread()586 bool InImageBridgeChildThread() {
587 return sImageBridgeChildThread &&
588 sImageBridgeChildThread->IsOnCurrentThread();
589 }
590
GetThread() const591 nsISerialEventTarget* ImageBridgeChild::GetThread() const {
592 return sImageBridgeChildThread;
593 }
594
595 /* static */
IdentifyCompositorTextureHost(const TextureFactoryIdentifier & aIdentifier)596 void ImageBridgeChild::IdentifyCompositorTextureHost(
597 const TextureFactoryIdentifier& aIdentifier) {
598 if (RefPtr<ImageBridgeChild> child = GetSingleton()) {
599 child->UpdateTextureFactoryIdentifier(aIdentifier);
600 }
601 }
602
UpdateTextureFactoryIdentifier(const TextureFactoryIdentifier & aIdentifier)603 void ImageBridgeChild::UpdateTextureFactoryIdentifier(
604 const TextureFactoryIdentifier& aIdentifier) {
605 // ImageHost is incompatible between WebRender enabled and WebRender disabled.
606 // Then drop all ImageContainers' ImageClients during disabling WebRender.
607 bool disablingWebRender =
608 GetCompositorBackendType() == LayersBackend::LAYERS_WR &&
609 aIdentifier.mParentBackend != LayersBackend::LAYERS_WR;
610
611 // Do not update TextureFactoryIdentifier if aIdentifier is going to disable
612 // WebRender, but gecko is still using WebRender. Since gecko uses different
613 // incompatible ImageHost and TextureHost between WebRender and non-WebRender.
614 //
615 // Even when WebRender is still in use, if non-accelerated widget is opened,
616 // aIdentifier disables WebRender at ImageBridgeChild.
617 if (disablingWebRender && gfxVars::UseWebRender()) {
618 return;
619 }
620
621 // D3DTexture might become obsolte. To prevent to use obsoleted D3DTexture,
622 // drop all ImageContainers' ImageClients.
623
624 // During re-creating GPU process, there was a period that ImageBridgeChild
625 // was re-created, but ImageBridgeChild::UpdateTextureFactoryIdentifier() was
626 // not called yet. In the period, if ImageBridgeChild::CreateImageClient() is
627 // called, ImageBridgeParent creates incompatible ImageHost than
628 // WebRenderImageHost.
629 bool initializingWebRender =
630 GetCompositorBackendType() != LayersBackend::LAYERS_WR &&
631 aIdentifier.mParentBackend == LayersBackend::LAYERS_WR;
632
633 bool needsDrop = disablingWebRender || initializingWebRender;
634
635 #if defined(XP_WIN)
636 RefPtr<ID3D11Device> device = gfx::DeviceManagerDx::Get()->GetImageDevice();
637 needsDrop |= !!mImageDevice && mImageDevice != device &&
638 GetCompositorBackendType() == LayersBackend::LAYERS_D3D11;
639 mImageDevice = device;
640 #endif
641
642 IdentifyTextureHost(aIdentifier);
643 if (needsDrop) {
644 nsTArray<RefPtr<ImageContainerListener> > listeners;
645 {
646 MutexAutoLock lock(mContainerMapLock);
647 for (const auto& entry : mImageContainerListeners) {
648 listeners.AppendElement(entry.second);
649 }
650 }
651 // Drop ImageContainer's ImageClient whithout holding mContainerMapLock to
652 // avoid deadlock.
653 for (auto container : listeners) {
654 container->DropImageClient();
655 }
656 }
657 }
658
CreateImageClient(CompositableType aType,ImageContainer * aImageContainer)659 RefPtr<ImageClient> ImageBridgeChild::CreateImageClient(
660 CompositableType aType, ImageContainer* aImageContainer) {
661 if (InImageBridgeChildThread()) {
662 return CreateImageClientNow(aType, aImageContainer);
663 }
664
665 SynchronousTask task("CreateImageClient Lock");
666
667 RefPtr<ImageClient> result = nullptr;
668
669 RefPtr<Runnable> runnable = WrapRunnable(
670 RefPtr<ImageBridgeChild>(this), &ImageBridgeChild::CreateImageClientSync,
671 &task, &result, aType, aImageContainer);
672 GetThread()->Dispatch(runnable.forget());
673
674 task.Wait();
675
676 return result;
677 }
678
CreateImageClientNow(CompositableType aType,ImageContainer * aImageContainer)679 RefPtr<ImageClient> ImageBridgeChild::CreateImageClientNow(
680 CompositableType aType, ImageContainer* aImageContainer) {
681 MOZ_ASSERT(InImageBridgeChildThread());
682 if (!CanSend()) {
683 return nullptr;
684 }
685
686 RefPtr<ImageClient> client =
687 ImageClient::CreateImageClient(aType, this, TextureFlags::NO_FLAGS);
688 MOZ_ASSERT(client, "failed to create ImageClient");
689 if (client) {
690 client->Connect(aImageContainer);
691 }
692 return client;
693 }
694
AllocUnsafeShmem(size_t aSize,ipc::SharedMemory::SharedMemoryType aType,ipc::Shmem * aShmem)695 bool ImageBridgeChild::AllocUnsafeShmem(
696 size_t aSize, ipc::SharedMemory::SharedMemoryType aType,
697 ipc::Shmem* aShmem) {
698 if (!InImageBridgeChildThread()) {
699 return DispatchAllocShmemInternal(aSize, aType, aShmem,
700 true); // true: unsafe
701 }
702
703 if (!CanSend()) {
704 return false;
705 }
706 return PImageBridgeChild::AllocUnsafeShmem(aSize, aType, aShmem);
707 }
708
AllocShmem(size_t aSize,ipc::SharedMemory::SharedMemoryType aType,ipc::Shmem * aShmem)709 bool ImageBridgeChild::AllocShmem(size_t aSize,
710 ipc::SharedMemory::SharedMemoryType aType,
711 ipc::Shmem* aShmem) {
712 if (!InImageBridgeChildThread()) {
713 return DispatchAllocShmemInternal(aSize, aType, aShmem,
714 false); // false: unsafe
715 }
716
717 if (!CanSend()) {
718 return false;
719 }
720 return PImageBridgeChild::AllocShmem(aSize, aType, aShmem);
721 }
722
ProxyAllocShmemNow(SynchronousTask * aTask,size_t aSize,SharedMemory::SharedMemoryType aType,ipc::Shmem * aShmem,bool aUnsafe,bool * aSuccess)723 void ImageBridgeChild::ProxyAllocShmemNow(SynchronousTask* aTask, size_t aSize,
724 SharedMemory::SharedMemoryType aType,
725 ipc::Shmem* aShmem, bool aUnsafe,
726 bool* aSuccess) {
727 AutoCompleteTask complete(aTask);
728
729 if (!CanSend()) {
730 return;
731 }
732
733 bool ok = false;
734 if (aUnsafe) {
735 ok = AllocUnsafeShmem(aSize, aType, aShmem);
736 } else {
737 ok = AllocShmem(aSize, aType, aShmem);
738 }
739 *aSuccess = ok;
740 }
741
DispatchAllocShmemInternal(size_t aSize,SharedMemory::SharedMemoryType aType,ipc::Shmem * aShmem,bool aUnsafe)742 bool ImageBridgeChild::DispatchAllocShmemInternal(
743 size_t aSize, SharedMemory::SharedMemoryType aType, ipc::Shmem* aShmem,
744 bool aUnsafe) {
745 SynchronousTask task("AllocatorProxy alloc");
746
747 bool success = false;
748 RefPtr<Runnable> runnable = WrapRunnable(
749 RefPtr<ImageBridgeChild>(this), &ImageBridgeChild::ProxyAllocShmemNow,
750 &task, aSize, aType, aShmem, aUnsafe, &success);
751 GetThread()->Dispatch(runnable.forget());
752
753 task.Wait();
754
755 return success;
756 }
757
ProxyDeallocShmemNow(SynchronousTask * aTask,ipc::Shmem * aShmem,bool * aResult)758 void ImageBridgeChild::ProxyDeallocShmemNow(SynchronousTask* aTask,
759 ipc::Shmem* aShmem, bool* aResult) {
760 AutoCompleteTask complete(aTask);
761
762 if (!CanSend()) {
763 return;
764 }
765 *aResult = DeallocShmem(*aShmem);
766 }
767
DeallocShmem(ipc::Shmem & aShmem)768 bool ImageBridgeChild::DeallocShmem(ipc::Shmem& aShmem) {
769 if (InImageBridgeChildThread()) {
770 if (!CanSend()) {
771 return false;
772 }
773 return PImageBridgeChild::DeallocShmem(aShmem);
774 }
775
776 // If we can't post a task, then we definitely cannot send, so there's
777 // no reason to queue up this send.
778 if (!CanPostTask()) {
779 return false;
780 }
781
782 SynchronousTask task("AllocatorProxy Dealloc");
783 bool result = false;
784
785 RefPtr<Runnable> runnable = WrapRunnable(
786 RefPtr<ImageBridgeChild>(this), &ImageBridgeChild::ProxyDeallocShmemNow,
787 &task, &aShmem, &result);
788 GetThread()->Dispatch(runnable.forget());
789
790 task.Wait();
791 return result;
792 }
793
AllocPTextureChild(const SurfaceDescriptor &,const ReadLockDescriptor &,const LayersBackend &,const TextureFlags &,const uint64_t & aSerial,const wr::MaybeExternalImageId & aExternalImageId)794 PTextureChild* ImageBridgeChild::AllocPTextureChild(
795 const SurfaceDescriptor&, const ReadLockDescriptor&, const LayersBackend&,
796 const TextureFlags&, const uint64_t& aSerial,
797 const wr::MaybeExternalImageId& aExternalImageId) {
798 MOZ_ASSERT(CanSend());
799 return TextureClient::CreateIPDLActor();
800 }
801
DeallocPTextureChild(PTextureChild * actor)802 bool ImageBridgeChild::DeallocPTextureChild(PTextureChild* actor) {
803 return TextureClient::DestroyIPDLActor(actor);
804 }
805
806 PMediaSystemResourceManagerChild*
AllocPMediaSystemResourceManagerChild()807 ImageBridgeChild::AllocPMediaSystemResourceManagerChild() {
808 MOZ_ASSERT(CanSend());
809 return new mozilla::media::MediaSystemResourceManagerChild();
810 }
811
DeallocPMediaSystemResourceManagerChild(PMediaSystemResourceManagerChild * aActor)812 bool ImageBridgeChild::DeallocPMediaSystemResourceManagerChild(
813 PMediaSystemResourceManagerChild* aActor) {
814 MOZ_ASSERT(aActor);
815 delete static_cast<mozilla::media::MediaSystemResourceManagerChild*>(aActor);
816 return true;
817 }
818
RecvParentAsyncMessages(nsTArray<AsyncParentMessageData> && aMessages)819 mozilla::ipc::IPCResult ImageBridgeChild::RecvParentAsyncMessages(
820 nsTArray<AsyncParentMessageData>&& aMessages) {
821 for (AsyncParentMessageArray::index_type i = 0; i < aMessages.Length(); ++i) {
822 const AsyncParentMessageData& message = aMessages[i];
823
824 switch (message.type()) {
825 case AsyncParentMessageData::TOpNotifyNotUsed: {
826 const OpNotifyNotUsed& op = message.get_OpNotifyNotUsed();
827 NotifyNotUsed(op.TextureId(), op.fwdTransactionId());
828 break;
829 }
830 case AsyncParentMessageData::TOpDeliverReleaseFence: {
831 #ifdef MOZ_WIDGET_ANDROID
832 const OpDeliverReleaseFence& op = message.get_OpDeliverReleaseFence();
833 ipc::FileDescriptor fenceFd;
834 if (op.fenceFd().isSome()) {
835 fenceFd = *op.fenceFd();
836 }
837 AndroidHardwareBufferManager::Get()->NotifyNotUsed(
838 std::move(fenceFd), op.bufferId(), op.fwdTransactionId(),
839 op.usesImageBridge());
840 #else
841 MOZ_ASSERT_UNREACHABLE("unexpected to be called");
842 #endif
843 break;
844 }
845 default:
846 NS_ERROR("unknown AsyncParentMessageData type");
847 return IPC_FAIL_NO_REASON(this);
848 }
849 }
850 return IPC_OK();
851 }
852
FindListener(const CompositableHandle & aHandle)853 RefPtr<ImageContainerListener> ImageBridgeChild::FindListener(
854 const CompositableHandle& aHandle) {
855 RefPtr<ImageContainerListener> listener;
856 MutexAutoLock lock(mContainerMapLock);
857 auto it = mImageContainerListeners.find(aHandle.Value());
858 if (it != mImageContainerListeners.end()) {
859 listener = it->second;
860 }
861 return listener;
862 }
863
RecvDidComposite(nsTArray<ImageCompositeNotification> && aNotifications)864 mozilla::ipc::IPCResult ImageBridgeChild::RecvDidComposite(
865 nsTArray<ImageCompositeNotification>&& aNotifications) {
866 for (auto& n : aNotifications) {
867 RefPtr<ImageContainerListener> listener = FindListener(n.compositable());
868 if (listener) {
869 listener->NotifyComposite(n);
870 }
871 }
872 return IPC_OK();
873 }
874
RecvReportFramesDropped(const CompositableHandle & aHandle,const uint32_t & aFrames)875 mozilla::ipc::IPCResult ImageBridgeChild::RecvReportFramesDropped(
876 const CompositableHandle& aHandle, const uint32_t& aFrames) {
877 RefPtr<ImageContainerListener> listener = FindListener(aHandle);
878 if (listener) {
879 listener->NotifyDropped(aFrames);
880 }
881
882 return IPC_OK();
883 }
884
CreateTexture(const SurfaceDescriptor & aSharedData,const ReadLockDescriptor & aReadLock,LayersBackend aLayersBackend,TextureFlags aFlags,uint64_t aSerial,wr::MaybeExternalImageId & aExternalImageId,nsISerialEventTarget * aTarget)885 PTextureChild* ImageBridgeChild::CreateTexture(
886 const SurfaceDescriptor& aSharedData, const ReadLockDescriptor& aReadLock,
887 LayersBackend aLayersBackend, TextureFlags aFlags, uint64_t aSerial,
888 wr::MaybeExternalImageId& aExternalImageId, nsISerialEventTarget* aTarget) {
889 MOZ_ASSERT(CanSend());
890 return SendPTextureConstructor(aSharedData, aReadLock, aLayersBackend, aFlags,
891 aSerial, aExternalImageId);
892 }
893
IBCAddOpDestroy(CompositableTransaction * aTxn,const OpDestroy & op)894 static bool IBCAddOpDestroy(CompositableTransaction* aTxn,
895 const OpDestroy& op) {
896 if (aTxn->Finished()) {
897 return false;
898 }
899
900 aTxn->mDestroyedActors.AppendElement(op);
901 return true;
902 }
903
DestroyInTransaction(PTextureChild * aTexture)904 bool ImageBridgeChild::DestroyInTransaction(PTextureChild* aTexture) {
905 return IBCAddOpDestroy(mTxn, OpDestroy(aTexture));
906 }
907
DestroyInTransaction(const CompositableHandle & aHandle)908 bool ImageBridgeChild::DestroyInTransaction(const CompositableHandle& aHandle) {
909 return IBCAddOpDestroy(mTxn, OpDestroy(aHandle));
910 }
911
RemoveTextureFromCompositable(CompositableClient * aCompositable,TextureClient * aTexture)912 void ImageBridgeChild::RemoveTextureFromCompositable(
913 CompositableClient* aCompositable, TextureClient* aTexture) {
914 MOZ_ASSERT(CanSend());
915 MOZ_ASSERT(aTexture);
916 MOZ_ASSERT(aTexture->IsSharedWithCompositor());
917 MOZ_ASSERT(aCompositable->IsConnected());
918 if (!aTexture || !aTexture->IsSharedWithCompositor() ||
919 !aCompositable->IsConnected()) {
920 return;
921 }
922
923 mTxn->AddNoSwapEdit(CompositableOperation(
924 aCompositable->GetIPCHandle(),
925 OpRemoveTexture(nullptr, aTexture->GetIPDLActor())));
926 }
927
IsSameProcess() const928 bool ImageBridgeChild::IsSameProcess() const {
929 return OtherPid() == base::GetCurrentProcId();
930 }
931
CanPostTask() const932 bool ImageBridgeChild::CanPostTask() const {
933 // During shutdown, the cycle collector may free objects that are holding a
934 // reference to ImageBridgeChild. Since this happens on the main thread,
935 // ImageBridgeChild will attempt to post a task to the ImageBridge thread.
936 // However the thread manager has already been shut down, so the task cannot
937 // post.
938 //
939 // It's okay if this races. We only care about the shutdown case where
940 // everything's happening on the main thread. Even if it races outside of
941 // shutdown, it's still harmless to post the task, since the task must
942 // check CanSend().
943 return !mDestroyed;
944 }
945
ReleaseCompositable(const CompositableHandle & aHandle)946 void ImageBridgeChild::ReleaseCompositable(const CompositableHandle& aHandle) {
947 if (!InImageBridgeChildThread()) {
948 // If we can't post a task, then we definitely cannot send, so there's
949 // no reason to queue up this send.
950 if (!CanPostTask()) {
951 return;
952 }
953
954 RefPtr<Runnable> runnable =
955 WrapRunnable(RefPtr<ImageBridgeChild>(this),
956 &ImageBridgeChild::ReleaseCompositable, aHandle);
957 GetThread()->Dispatch(runnable.forget());
958 return;
959 }
960
961 if (!CanSend()) {
962 return;
963 }
964
965 if (!DestroyInTransaction(aHandle)) {
966 SendReleaseCompositable(aHandle);
967 }
968
969 {
970 MutexAutoLock lock(mContainerMapLock);
971 mImageContainerListeners.erase(aHandle.Value());
972 }
973 }
974
CanSend() const975 bool ImageBridgeChild::CanSend() const {
976 MOZ_ASSERT(InImageBridgeChildThread());
977 return mCanSend;
978 }
979
HandleFatalError(const char * aMsg) const980 void ImageBridgeChild::HandleFatalError(const char* aMsg) const {
981 dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aMsg, OtherPid());
982 }
983
GetNextExternalImageId()984 wr::MaybeExternalImageId ImageBridgeChild::GetNextExternalImageId() {
985 static uint32_t sNextID = 1;
986 ++sNextID;
987 MOZ_RELEASE_ASSERT(sNextID != UINT32_MAX);
988
989 uint64_t imageId = mNamespace;
990 imageId = imageId << 32 | sNextID;
991 return Some(wr::ToExternalImageId(imageId));
992 }
993
994 } // namespace layers
995 } // namespace mozilla
996