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 #include "RemoteDecoderManagerChild.h"
7 
8 #include "base/task.h"
9 
10 #include "RemoteDecoderChild.h"
11 #include "mozilla/dom/ContentChild.h"
12 #include "mozilla/gfx/2D.h"
13 #include "mozilla/ipc/ProtocolUtils.h"
14 #include "mozilla/layers/SynchronousTask.h"
15 #include "mozilla/gfx/DataSurfaceHelpers.h"
16 #include "mozilla/layers/ISurfaceAllocator.h"
17 
18 namespace mozilla {
19 
20 using namespace layers;
21 using namespace gfx;
22 
23 // Only modified on the main-thread
24 StaticRefPtr<nsIThread> sRemoteDecoderManagerChildThread;
25 StaticRefPtr<AbstractThread> sRemoteDecoderManagerChildAbstractThread;
26 
27 // Only accessed from sRemoteDecoderManagerChildThread
28 static StaticRefPtr<RemoteDecoderManagerChild>
29     sRemoteDecoderManagerChildForRDDProcess;
30 
31 static StaticRefPtr<RemoteDecoderManagerChild>
32     sRemoteDecoderManagerChildForGPUProcess;
33 static UniquePtr<nsTArray<RefPtr<Runnable>>> sRecreateTasks;
34 
35 /* static */
InitializeThread()36 void RemoteDecoderManagerChild::InitializeThread() {
37   MOZ_ASSERT(NS_IsMainThread());
38 
39   if (!sRemoteDecoderManagerChildThread) {
40     RefPtr<nsIThread> childThread;
41     nsresult rv = NS_NewNamedThread(
42         "RemVidChild", getter_AddRefs(childThread),
43         NS_NewRunnableFunction(
44             "RemoteDecoderManagerChild::InitializeThread::AbstractThread",
45             []() {
46               // We create an AbstractThread for this thread so that we can
47               // use direct task
48               // dispatching with MozPromise, which is similar (but not
49               // identical to) the microtask semantics of JS promises.
50               sRemoteDecoderManagerChildAbstractThread =
51                   AbstractThread::CreateXPCOMThreadWrapper(
52                       NS_GetCurrentThread(), false /* require tail dispatch */);
53             }));
54     NS_ENSURE_SUCCESS_VOID(rv);
55     sRemoteDecoderManagerChildThread = childThread;
56 
57     sRecreateTasks = MakeUnique<nsTArray<RefPtr<Runnable>>>();
58   }
59 }
60 
61 /* static */
InitForRDDProcess(Endpoint<PRemoteDecoderManagerChild> && aVideoManager)62 void RemoteDecoderManagerChild::InitForRDDProcess(
63     Endpoint<PRemoteDecoderManagerChild>&& aVideoManager) {
64   InitializeThread();
65   sRemoteDecoderManagerChildThread->Dispatch(
66       NewRunnableFunction("InitForContentRunnable", &OpenForRDDProcess,
67                           std::move(aVideoManager)),
68       NS_DISPATCH_NORMAL);
69 }
70 
71 /* static */
InitForGPUProcess(Endpoint<PRemoteDecoderManagerChild> && aVideoManager)72 void RemoteDecoderManagerChild::InitForGPUProcess(
73     Endpoint<PRemoteDecoderManagerChild>&& aVideoManager) {
74   InitializeThread();
75   sRemoteDecoderManagerChildThread->Dispatch(
76       NewRunnableFunction("InitForContentRunnable", &OpenForGPUProcess,
77                           std::move(aVideoManager)),
78       NS_DISPATCH_NORMAL);
79 }
80 
81 /* static */
Shutdown()82 void RemoteDecoderManagerChild::Shutdown() {
83   MOZ_ASSERT(NS_IsMainThread());
84 
85   if (sRemoteDecoderManagerChildThread) {
86     sRemoteDecoderManagerChildThread->Dispatch(
87         NS_NewRunnableFunction(
88             "dom::RemoteDecoderManagerChild::Shutdown",
89             []() {
90               if (sRemoteDecoderManagerChildForRDDProcess &&
91                   sRemoteDecoderManagerChildForRDDProcess->CanSend()) {
92                 sRemoteDecoderManagerChildForRDDProcess->Close();
93                 sRemoteDecoderManagerChildForRDDProcess = nullptr;
94               }
95               if (sRemoteDecoderManagerChildForGPUProcess &&
96                   sRemoteDecoderManagerChildForGPUProcess->CanSend()) {
97                 sRemoteDecoderManagerChildForGPUProcess->Close();
98                 sRemoteDecoderManagerChildForGPUProcess = nullptr;
99               }
100             }),
101         NS_DISPATCH_NORMAL);
102 
103     sRemoteDecoderManagerChildThread->Shutdown();
104     sRemoteDecoderManagerChildAbstractThread = nullptr;
105     sRemoteDecoderManagerChildThread = nullptr;
106 
107     sRecreateTasks = nullptr;
108   }
109 }
110 
RunWhenGPUProcessRecreated(already_AddRefed<Runnable> aTask)111 void RemoteDecoderManagerChild::RunWhenGPUProcessRecreated(
112     already_AddRefed<Runnable> aTask) {
113   MOZ_ASSERT(NS_GetCurrentThread() == GetManagerThread());
114 
115   // If we've already been recreated, then run the task immediately.
116   if (GetGPUProcessSingleton() && GetGPUProcessSingleton() != this &&
117       GetGPUProcessSingleton()->CanSend()) {
118     RefPtr<Runnable> task = aTask;
119     task->Run();
120   } else {
121     sRecreateTasks->AppendElement(aTask);
122   }
123 }
124 
125 /* static */
GetRDDProcessSingleton()126 RemoteDecoderManagerChild* RemoteDecoderManagerChild::GetRDDProcessSingleton() {
127   MOZ_ASSERT(NS_GetCurrentThread() == GetManagerThread());
128   return sRemoteDecoderManagerChildForRDDProcess;
129 }
130 
131 /* static */
GetGPUProcessSingleton()132 RemoteDecoderManagerChild* RemoteDecoderManagerChild::GetGPUProcessSingleton() {
133   MOZ_ASSERT(NS_GetCurrentThread() == GetManagerThread());
134   return sRemoteDecoderManagerChildForGPUProcess;
135 }
136 
137 /* static */
GetManagerThread()138 nsIThread* RemoteDecoderManagerChild::GetManagerThread() {
139   return sRemoteDecoderManagerChildThread;
140 }
141 
AllocPRemoteDecoderChild(const RemoteDecoderInfoIPDL &,const CreateDecoderParams::OptionSet & aOptions,const Maybe<layers::TextureFactoryIdentifier> & aIdentifier,bool * aSuccess,nsCString *)142 PRemoteDecoderChild* RemoteDecoderManagerChild::AllocPRemoteDecoderChild(
143     const RemoteDecoderInfoIPDL& /* not used */,
144     const CreateDecoderParams::OptionSet& aOptions,
145     const Maybe<layers::TextureFactoryIdentifier>& aIdentifier, bool* aSuccess,
146     nsCString* /* not used */) {
147   // RemoteDecoderModule is responsible for creating RemoteDecoderChild
148   // classes.
149   MOZ_ASSERT(false,
150              "RemoteDecoderManagerChild cannot create "
151              "RemoteDecoderChild classes");
152   return nullptr;
153 }
154 
DeallocPRemoteDecoderChild(PRemoteDecoderChild * actor)155 bool RemoteDecoderManagerChild::DeallocPRemoteDecoderChild(
156     PRemoteDecoderChild* actor) {
157   RemoteDecoderChild* child = static_cast<RemoteDecoderChild*>(actor);
158   child->IPDLActorDestroyed();
159   return true;
160 }
161 
RemoteDecoderManagerChild(layers::VideoBridgeSource aSource)162 RemoteDecoderManagerChild::RemoteDecoderManagerChild(
163     layers::VideoBridgeSource aSource)
164     : mSource(aSource) {}
165 
OpenForRDDProcess(Endpoint<PRemoteDecoderManagerChild> && aEndpoint)166 void RemoteDecoderManagerChild::OpenForRDDProcess(
167     Endpoint<PRemoteDecoderManagerChild>&& aEndpoint) {
168   MOZ_ASSERT(NS_GetCurrentThread() == GetManagerThread());
169   // Only create RemoteDecoderManagerChild, bind new endpoint and init
170   // ipdl if:
171   // 1) haven't init'd sRemoteDecoderManagerChild
172   // or
173   // 2) if ActorDestroy was called (mCanSend is false) meaning the other
174   // end of the ipc channel was torn down
175   if (sRemoteDecoderManagerChildForRDDProcess &&
176       sRemoteDecoderManagerChildForRDDProcess->mCanSend) {
177     return;
178   }
179   sRemoteDecoderManagerChildForRDDProcess = nullptr;
180   if (aEndpoint.IsValid()) {
181     RefPtr<RemoteDecoderManagerChild> manager =
182         new RemoteDecoderManagerChild(VideoBridgeSource::RddProcess);
183     if (aEndpoint.Bind(manager)) {
184       sRemoteDecoderManagerChildForRDDProcess = manager;
185       manager->InitIPDL();
186     }
187   }
188 }
189 
OpenForGPUProcess(Endpoint<PRemoteDecoderManagerChild> && aEndpoint)190 void RemoteDecoderManagerChild::OpenForGPUProcess(
191     Endpoint<PRemoteDecoderManagerChild>&& aEndpoint) {
192   // Make sure we always dispatch everything in sRecreateTasks, even if we
193   // fail since this is as close to being recreated as we will ever be.
194   sRemoteDecoderManagerChildForGPUProcess = nullptr;
195   if (aEndpoint.IsValid()) {
196     RefPtr<RemoteDecoderManagerChild> manager =
197         new RemoteDecoderManagerChild(VideoBridgeSource::GpuProcess);
198     if (aEndpoint.Bind(manager)) {
199       sRemoteDecoderManagerChildForGPUProcess = manager;
200       manager->InitIPDL();
201     }
202   }
203   for (Runnable* task : *sRecreateTasks) {
204     task->Run();
205   }
206   sRecreateTasks->Clear();
207 }
208 
InitIPDL()209 void RemoteDecoderManagerChild::InitIPDL() {
210   mCanSend = true;
211   mIPDLSelfRef = this;
212 }
213 
ActorDestroy(ActorDestroyReason aWhy)214 void RemoteDecoderManagerChild::ActorDestroy(ActorDestroyReason aWhy) {
215   mCanSend = false;
216 }
217 
ActorDealloc()218 void RemoteDecoderManagerChild::ActorDealloc() { mIPDLSelfRef = nullptr; }
219 
CanSend()220 bool RemoteDecoderManagerChild::CanSend() {
221   MOZ_ASSERT(NS_GetCurrentThread() == GetManagerThread());
222   return mCanSend;
223 }
224 
DeallocShmem(mozilla::ipc::Shmem & aShmem)225 bool RemoteDecoderManagerChild::DeallocShmem(mozilla::ipc::Shmem& aShmem) {
226   if (NS_GetCurrentThread() != sRemoteDecoderManagerChildThread) {
227     RefPtr<RemoteDecoderManagerChild> self = this;
228     mozilla::ipc::Shmem shmem = aShmem;
229     sRemoteDecoderManagerChildThread->Dispatch(
230         NS_NewRunnableFunction("RemoteDecoderManagerChild::DeallocShmem",
231                                [self, shmem]() {
232                                  if (self->CanSend()) {
233                                    mozilla::ipc::Shmem shmemCopy = shmem;
234                                    self->DeallocShmem(shmemCopy);
235                                  }
236                                }),
237         NS_DISPATCH_NORMAL);
238     return true;
239   }
240   return PRemoteDecoderManagerChild::DeallocShmem(aShmem);
241 }
242 
243 struct SurfaceDescriptorUserData {
SurfaceDescriptorUserDatamozilla::SurfaceDescriptorUserData244   SurfaceDescriptorUserData(RemoteDecoderManagerChild* aAllocator,
245                             SurfaceDescriptor& aSD)
246       : mAllocator(aAllocator), mSD(aSD) {}
~SurfaceDescriptorUserDatamozilla::SurfaceDescriptorUserData247   ~SurfaceDescriptorUserData() { DestroySurfaceDescriptor(mAllocator, &mSD); }
248 
249   RefPtr<RemoteDecoderManagerChild> mAllocator;
250   SurfaceDescriptor mSD;
251 };
252 
DeleteSurfaceDescriptorUserData(void * aClosure)253 void DeleteSurfaceDescriptorUserData(void* aClosure) {
254   SurfaceDescriptorUserData* sd =
255       reinterpret_cast<SurfaceDescriptorUserData*>(aClosure);
256   delete sd;
257 }
258 
Readback(const SurfaceDescriptorGPUVideo & aSD)259 already_AddRefed<SourceSurface> RemoteDecoderManagerChild::Readback(
260     const SurfaceDescriptorGPUVideo& aSD) {
261   // We can't use NS_DISPATCH_SYNC here since that can spin the event
262   // loop while it waits. This function can be called from JS and we
263   // don't want that to happen.
264   SynchronousTask task("Readback sync");
265 
266   RefPtr<RemoteDecoderManagerChild> ref = this;
267   SurfaceDescriptor sd;
268   if (NS_FAILED(sRemoteDecoderManagerChildThread->Dispatch(
269           NS_NewRunnableFunction("RemoteDecoderManagerChild::Readback",
270                                  [&]() {
271                                    AutoCompleteTask complete(&task);
272                                    if (ref->CanSend()) {
273                                      ref->SendReadback(aSD, &sd);
274                                    }
275                                  }),
276           NS_DISPATCH_NORMAL))) {
277     return nullptr;
278   }
279 
280   task.Wait();
281 
282   if (!IsSurfaceDescriptorValid(sd)) {
283     return nullptr;
284   }
285 
286   RefPtr<DataSourceSurface> source = GetSurfaceForDescriptor(sd);
287   if (!source) {
288     DestroySurfaceDescriptor(this, &sd);
289     NS_WARNING("Failed to map SurfaceDescriptor in Readback");
290     return nullptr;
291   }
292 
293   static UserDataKey sSurfaceDescriptor;
294   source->AddUserData(&sSurfaceDescriptor,
295                       new SurfaceDescriptorUserData(this, sd),
296                       DeleteSurfaceDescriptorUserData);
297 
298   return source.forget();
299 }
300 
DeallocateSurfaceDescriptor(const SurfaceDescriptorGPUVideo & aSD)301 void RemoteDecoderManagerChild::DeallocateSurfaceDescriptor(
302     const SurfaceDescriptorGPUVideo& aSD) {
303   RefPtr<RemoteDecoderManagerChild> ref = this;
304   SurfaceDescriptorGPUVideo sd = std::move(aSD);
305   sRemoteDecoderManagerChildThread->Dispatch(
306       NS_NewRunnableFunction(
307           "RemoteDecoderManagerChild::DeallocateSurfaceDescriptor",
308           [ref, sd]() {
309             if (ref->CanSend()) {
310               ref->SendDeallocateSurfaceDescriptorGPUVideo(sd);
311             }
312           }),
313       NS_DISPATCH_NORMAL);
314 }
315 
HandleFatalError(const char * aMsg) const316 void RemoteDecoderManagerChild::HandleFatalError(const char* aMsg) const {
317   dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aMsg, OtherPid());
318 }
319 
320 }  // namespace mozilla
321