CompositorManagerParent.cpp (14020B)
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 "mozilla/layers/CompositorManagerParent.h" 8 #include "mozilla/gfx/GPUParent.h" 9 #include "mozilla/gfx/CanvasManagerParent.h" 10 #include "mozilla/webrender/RenderThread.h" 11 #include "mozilla/ipc/Endpoint.h" 12 #include "mozilla/layers/CompositorBridgeParent.h" 13 #include "mozilla/layers/ContentCompositorBridgeParent.h" 14 #include "mozilla/layers/CompositorThread.h" 15 #include "mozilla/layers/RemoteTextureMap.h" 16 #include "mozilla/layers/SharedSurfacesParent.h" 17 #include "gfxPlatform.h" 18 #include "VsyncSource.h" 19 20 namespace mozilla { 21 namespace layers { 22 23 StaticMonitor CompositorManagerParent::sMonitor; 24 StaticRefPtr<CompositorManagerParent> CompositorManagerParent::sInstance; 25 MOZ_RUNINIT CompositorManagerParent::ManagerMap 26 CompositorManagerParent::sManagers; 27 28 /* static */ 29 already_AddRefed<CompositorManagerParent> 30 CompositorManagerParent::CreateSameProcess(uint32_t aNamespace) { 31 MOZ_ASSERT(XRE_IsParentProcess()); 32 MOZ_ASSERT(NS_IsMainThread()); 33 StaticMonitorAutoLock lock(sMonitor); 34 35 // We are creating a manager for the UI process, inside the combined GPU/UI 36 // process. It is created more-or-less the same but we retain a reference to 37 // the parent to access state. 38 if (NS_WARN_IF(sInstance)) { 39 MOZ_ASSERT_UNREACHABLE("Already initialized"); 40 return nullptr; 41 } 42 43 // The child is responsible for setting up the IPC channel in the same 44 // process case because if we open from the child perspective, we can do it 45 // on the main thread and complete before we return the manager handles. 46 RefPtr<CompositorManagerParent> parent = 47 new CompositorManagerParent(dom::ContentParentId(), aNamespace); 48 parent->SetOtherEndpointProcInfo(ipc::EndpointProcInfo::Current()); 49 return parent.forget(); 50 } 51 52 /* static */ 53 bool CompositorManagerParent::Create( 54 Endpoint<PCompositorManagerParent>&& aEndpoint, 55 dom::ContentParentId aChildId, uint32_t aNamespace, bool aIsRoot) { 56 MOZ_ASSERT(NS_IsMainThread()); 57 58 // We are creating a manager for the another process, inside the GPU process 59 // (or UI process if it subsumbed the GPU process). 60 MOZ_ASSERT(aEndpoint.OtherPid() != base::GetCurrentProcId()); 61 62 if (!CompositorThreadHolder::IsActive()) { 63 return false; 64 } 65 66 RefPtr<CompositorManagerParent> bridge = 67 new CompositorManagerParent(aChildId, aNamespace); 68 69 RefPtr<Runnable> runnable = 70 NewRunnableMethod<Endpoint<PCompositorManagerParent>&&, bool>( 71 "CompositorManagerParent::Bind", bridge, 72 &CompositorManagerParent::Bind, std::move(aEndpoint), aIsRoot); 73 CompositorThread()->Dispatch(runnable.forget()); 74 return true; 75 } 76 77 /* static */ 78 already_AddRefed<CompositorBridgeParent> 79 CompositorManagerParent::CreateSameProcessWidgetCompositorBridge( 80 CSSToLayoutDeviceScale aScale, const CompositorOptions& aOptions, 81 bool aUseExternalSurfaceSize, const gfx::IntSize& aSurfaceSize, 82 uint64_t aInnerWindowId) { 83 MOZ_ASSERT(XRE_IsParentProcess()); 84 MOZ_ASSERT(NS_IsMainThread()); 85 86 // When we are in a combined UI / GPU process, InProcessCompositorSession 87 // requires both the parent and child PCompositorBridge actors for its own 88 // construction, which is done on the main thread. Normally 89 // CompositorBridgeParent is created on the compositor thread via the IPDL 90 // plumbing (CompositorManagerParent::AllocPCompositorBridgeParent). Thus to 91 // actually get a reference to the parent, we would need to block on the 92 // compositor thread until it handles our constructor message. Because only 93 // one one IPDL constructor is permitted per parent and child protocol, we 94 // cannot make the normal case async and this case sync. Instead what we do 95 // is leave the constructor async (a boon to the content process setup) and 96 // create the parent ahead of time. It will pull the preinitialized parent 97 // from the queue when it receives the message and give that to IPDL. 98 99 // Note that the static mutex not only is used to protect sInstance, but also 100 // mPendingCompositorBridges. 101 StaticMonitorAutoLock lock(sMonitor); 102 if (NS_WARN_IF(!sInstance)) { 103 return nullptr; 104 } 105 106 TimeDuration vsyncRate = 107 gfxPlatform::GetPlatform()->GetGlobalVsyncDispatcher()->GetVsyncRate(); 108 109 RefPtr<CompositorBridgeParent> bridge = new CompositorBridgeParent( 110 sInstance, aScale, vsyncRate, aOptions, aUseExternalSurfaceSize, 111 aSurfaceSize, aInnerWindowId); 112 113 sInstance->mPendingCompositorBridges.AppendElement(bridge); 114 return bridge.forget(); 115 } 116 117 CompositorManagerParent::CompositorManagerParent( 118 dom::ContentParentId aContentId, uint32_t aNamespace) 119 : mCompositorThreadHolder(CompositorThreadHolder::GetSingleton()), 120 mSharedSurfacesHolder(MakeRefPtr<SharedSurfacesHolder>(aNamespace)), 121 mContentId(aContentId), 122 mNamespace(aNamespace) {} 123 124 CompositorManagerParent::~CompositorManagerParent() = default; 125 126 void CompositorManagerParent::Bind( 127 Endpoint<PCompositorManagerParent>&& aEndpoint, bool aIsRoot) { 128 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); 129 if (NS_WARN_IF(!aEndpoint.Bind(this))) { 130 return; 131 } 132 133 BindComplete(aIsRoot); 134 } 135 136 void CompositorManagerParent::BindComplete(bool aIsRoot) { 137 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread() || 138 NS_IsMainThread()); 139 140 StaticMonitorAutoLock lock(sMonitor); 141 if (aIsRoot) { 142 MOZ_ASSERT(!sInstance); 143 sInstance = this; 144 } 145 146 MOZ_RELEASE_ASSERT(sManagers.try_emplace(mNamespace, this).second); 147 } 148 149 void CompositorManagerParent::ActorDestroy(ActorDestroyReason aReason) { 150 GetCurrentSerialEventTarget()->Dispatch( 151 NewRunnableMethod("layers::CompositorManagerParent::DeferredDestroy", 152 this, &CompositorManagerParent::DeferredDestroy)); 153 154 if (mRemoteTextureTxnScheduler) { 155 mRemoteTextureTxnScheduler = nullptr; 156 } 157 158 StaticMonitorAutoLock lock(sMonitor); 159 if (sInstance == this) { 160 sInstance = nullptr; 161 } 162 163 MOZ_RELEASE_ASSERT(sManagers.erase(mNamespace) > 0); 164 sMonitor.NotifyAll(); 165 } 166 167 void CompositorManagerParent::DeferredDestroy() { 168 mCompositorThreadHolder = nullptr; 169 } 170 171 /* static */ 172 void CompositorManagerParent::ShutdownInternal() { 173 nsTArray<RefPtr<CompositorManagerParent>> actors; 174 175 // We move here because we may attempt to acquire the same lock during the 176 // destroy to remove the reference in sManagers. 177 { 178 StaticMonitorAutoLock lock(sMonitor); 179 actors.SetCapacity(sManagers.size()); 180 for (auto& i : sManagers) { 181 actors.AppendElement(i.second); 182 } 183 } 184 185 for (auto& actor : actors) { 186 actor->Close(); 187 } 188 } 189 190 /* static */ 191 void CompositorManagerParent::Shutdown() { 192 MOZ_ASSERT(NS_IsMainThread()); 193 194 CompositorThread()->Dispatch(NS_NewRunnableFunction( 195 "layers::CompositorManagerParent::Shutdown", 196 []() -> void { CompositorManagerParent::ShutdownInternal(); })); 197 } 198 199 /* static */ void CompositorManagerParent::WaitForSharedSurface( 200 const wr::ExternalImageId& aId) { 201 uint32_t extNamespace = static_cast<uint32_t>(wr::AsUint64(aId) >> 32); 202 uint32_t resourceId = static_cast<uint32_t>(wr::AsUint64(aId)); 203 204 StaticMonitorAutoLock lock(sMonitor); 205 206 while (true) { 207 const auto i = sManagers.find(extNamespace); 208 if (NS_WARN_IF(i == sManagers.end())) { 209 break; 210 } 211 212 // We know that when the resource ID is allocated, we either fail to have 213 // shared the surface with the compositor process, and so we don't use the 214 // external image ID, or we have queued an IPDL message over the 215 // corresponding CompositorManagerParent object to map that surface into 216 // memory. They are dispatched in order, so we can safely wait until either 217 // the actor is closed, or the last seen resource ID reaches the target. 218 if (i->second->mLastSharedSurfaceResourceId >= resourceId) { 219 break; 220 } 221 222 lock.Wait(); 223 } 224 } 225 226 already_AddRefed<PCompositorBridgeParent> 227 CompositorManagerParent::AllocPCompositorBridgeParent( 228 const CompositorBridgeOptions& aOpt) { 229 switch (aOpt.type()) { 230 case CompositorBridgeOptions::TContentCompositorOptions: { 231 RefPtr<ContentCompositorBridgeParent> bridge = 232 new ContentCompositorBridgeParent(this); 233 return bridge.forget(); 234 } 235 case CompositorBridgeOptions::TWidgetCompositorOptions: { 236 // Only the UI process is allowed to create widget compositors in the 237 // compositor process. 238 gfx::GPUParent* gpu = gfx::GPUParent::GetSingleton(); 239 if (NS_WARN_IF(!gpu || OtherPid() != gpu->OtherPid())) { 240 MOZ_ASSERT_UNREACHABLE("Child cannot create widget compositor!"); 241 break; 242 } 243 244 const WidgetCompositorOptions& opt = aOpt.get_WidgetCompositorOptions(); 245 RefPtr<CompositorBridgeParent> bridge = new CompositorBridgeParent( 246 this, opt.scale(), opt.vsyncRate(), opt.options(), 247 opt.useExternalSurfaceSize(), opt.surfaceSize(), opt.innerWindowId()); 248 return bridge.forget(); 249 } 250 case CompositorBridgeOptions::TSameProcessWidgetCompositorOptions: { 251 // If the GPU and UI process are combined, we actually already created the 252 // CompositorBridgeParent, so we need to reuse that to inject it into the 253 // IPDL framework. 254 if (NS_WARN_IF(OtherPid() != base::GetCurrentProcId())) { 255 MOZ_ASSERT_UNREACHABLE("Child cannot create same process compositor!"); 256 break; 257 } 258 259 // Note that the static mutex not only is used to protect sInstance, but 260 // also mPendingCompositorBridges. 261 StaticMonitorAutoLock lock(sMonitor); 262 if (mPendingCompositorBridges.IsEmpty()) { 263 break; 264 } 265 266 RefPtr<CompositorBridgeParent> bridge = mPendingCompositorBridges[0]; 267 mPendingCompositorBridges.RemoveElementAt(0); 268 return bridge.forget(); 269 } 270 default: 271 break; 272 } 273 274 return nullptr; 275 } 276 277 /* static */ void CompositorManagerParent::AddSharedSurface( 278 const wr::ExternalImageId& aId, gfx::SourceSurfaceSharedData* aSurface) { 279 MOZ_ASSERT(XRE_IsParentProcess()); 280 281 StaticMonitorAutoLock lock(sMonitor); 282 if (NS_WARN_IF(!sInstance)) { 283 return; 284 } 285 286 if (NS_WARN_IF(!sInstance->OwnsExternalImageId(aId))) { 287 MOZ_ASSERT_UNREACHABLE("Wrong namespace?"); 288 return; 289 } 290 291 SharedSurfacesParent::AddSameProcess(aId, aSurface); 292 293 uint32_t resourceId = static_cast<uint32_t>(wr::AsUint64(aId)); 294 MOZ_RELEASE_ASSERT(sInstance->mLastSharedSurfaceResourceId < resourceId); 295 sInstance->mLastSharedSurfaceResourceId = resourceId; 296 sMonitor.NotifyAll(); 297 } 298 299 mozilla::ipc::IPCResult CompositorManagerParent::RecvAddSharedSurface( 300 const wr::ExternalImageId& aId, SurfaceDescriptorShared&& aDesc) { 301 if (NS_WARN_IF(!OwnsExternalImageId(aId))) { 302 MOZ_ASSERT_UNREACHABLE("Wrong namespace?"); 303 return IPC_OK(); 304 } 305 306 SharedSurfacesParent::Add(aId, std::move(aDesc), OtherPid()); 307 308 StaticMonitorAutoLock lock(sMonitor); 309 uint32_t resourceId = static_cast<uint32_t>(wr::AsUint64(aId)); 310 MOZ_RELEASE_ASSERT(mLastSharedSurfaceResourceId < resourceId); 311 mLastSharedSurfaceResourceId = resourceId; 312 sMonitor.NotifyAll(); 313 return IPC_OK(); 314 } 315 316 mozilla::ipc::IPCResult CompositorManagerParent::RecvRemoveSharedSurface( 317 const wr::ExternalImageId& aId) { 318 if (NS_WARN_IF(!OwnsExternalImageId(aId))) { 319 MOZ_ASSERT_UNREACHABLE("Wrong namespace?"); 320 return IPC_OK(); 321 } 322 323 SharedSurfacesParent::Remove(aId); 324 return IPC_OK(); 325 } 326 327 mozilla::ipc::IPCResult CompositorManagerParent::RecvReportSharedSurfacesMemory( 328 ReportSharedSurfacesMemoryResolver&& aResolver) { 329 SharedSurfacesMemoryReport report; 330 SharedSurfacesParent::AccumulateMemoryReport(mNamespace, report); 331 aResolver(std::move(report)); 332 return IPC_OK(); 333 } 334 335 mozilla::ipc::IPCResult CompositorManagerParent::RecvNotifyMemoryPressure() { 336 nsTArray<PCompositorBridgeParent*> compositorBridges; 337 ManagedPCompositorBridgeParent(compositorBridges); 338 for (auto bridge : compositorBridges) { 339 static_cast<CompositorBridgeParentBase*>(bridge)->NotifyMemoryPressure(); 340 } 341 return IPC_OK(); 342 } 343 344 mozilla::ipc::IPCResult CompositorManagerParent::RecvReportMemory( 345 ReportMemoryResolver&& aResolver) { 346 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); 347 MemoryReport aggregate; 348 PodZero(&aggregate); 349 350 // Accumulate RenderBackend usage. 351 nsTArray<PCompositorBridgeParent*> compositorBridges; 352 ManagedPCompositorBridgeParent(compositorBridges); 353 for (auto bridge : compositorBridges) { 354 static_cast<CompositorBridgeParentBase*>(bridge)->AccumulateMemoryReport( 355 &aggregate); 356 } 357 358 // Accumulate Renderer usage asynchronously, and resolve. 359 // 360 // Note that the IPDL machinery requires aResolver to be called on this 361 // thread, so we can't just pass it over to the renderer thread. We use 362 // an intermediate MozPromise instead. 363 wr::RenderThread::AccumulateMemoryReport(aggregate)->Then( 364 CompositorThread(), __func__, 365 [resolver = std::move(aResolver)](MemoryReport aReport) { 366 resolver(aReport); 367 }, 368 [](bool) { 369 MOZ_ASSERT_UNREACHABLE("MemoryReport promises are never rejected"); 370 }); 371 372 return IPC_OK(); 373 } 374 375 mozilla::ipc::IPCResult CompositorManagerParent::RecvInitCanvasManager( 376 Endpoint<PCanvasManagerParent>&& aEndpoint) { 377 gfx::CanvasManagerParent::Init(std::move(aEndpoint), mSharedSurfacesHolder, 378 mContentId); 379 mRemoteTextureTxnScheduler = RemoteTextureTxnScheduler::Create(this); 380 return IPC_OK(); 381 } 382 383 /* static */ 384 void CompositorManagerParent::NotifyWebRenderError(wr::WebRenderError aError) { 385 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); 386 387 StaticMonitorAutoLock lock(sMonitor); 388 if (NS_WARN_IF(!sInstance)) { 389 return; 390 } 391 (void)sInstance->SendNotifyWebRenderError(aError); 392 } 393 394 } // namespace layers 395 } // namespace mozilla