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