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