SharedSurfacesParent.cpp (12949B)
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 "SharedSurfacesParent.h" 8 #include "mozilla/DebugOnly.h" 9 #include "mozilla/StaticPrefs_image.h" 10 #include "mozilla/gfx/Logging.h" 11 #include "mozilla/gfx/gfxVars.h" 12 #include "mozilla/gfx/GPUProcessManager.h" 13 #include "mozilla/layers/SharedSurfacesMemoryReport.h" 14 #include "mozilla/layers/SourceSurfaceSharedData.h" 15 #include "mozilla/layers/CompositorManagerParent.h" 16 #include "mozilla/layers/CompositorThread.h" 17 #include "mozilla/webrender/RenderSharedSurfaceTextureHost.h" 18 #include "mozilla/webrender/RenderThread.h" 19 #include "nsThreadUtils.h" // for GetCurrentSerialEventTarget 20 21 namespace mozilla { 22 namespace layers { 23 24 using namespace mozilla::gfx; 25 26 StaticMutex SharedSurfacesParent::sMutex; 27 StaticAutoPtr<SharedSurfacesParent> SharedSurfacesParent::sInstance; 28 29 void SharedSurfacesParent::MappingTracker::NotifyExpiredLocked( 30 SourceSurfaceSharedDataWrapper* aSurface, 31 const StaticMutexAutoLock& aAutoLock) { 32 RemoveObjectLocked(aSurface, aAutoLock); 33 mExpired.AppendElement(aSurface); 34 } 35 36 void SharedSurfacesParent::MappingTracker::TakeExpired( 37 nsTArray<RefPtr<gfx::SourceSurfaceSharedDataWrapper>>& aExpired, 38 const StaticMutexAutoLock& aAutoLock) { 39 aExpired = std::move(mExpired); 40 } 41 42 void SharedSurfacesParent::MappingTracker::NotifyHandlerEnd() { 43 nsTArray<RefPtr<gfx::SourceSurfaceSharedDataWrapper>> expired; 44 { 45 StaticMutexAutoLock lock(sMutex); 46 TakeExpired(expired, lock); 47 } 48 49 SharedSurfacesParent::ExpireMap(expired); 50 } 51 52 SharedSurfacesParent::SharedSurfacesParent() 53 : mTracker( 54 StaticPrefs::image_mem_shared_unmap_min_expiration_ms_AtStartup(), 55 mozilla::GetCurrentSerialEventTarget()) {} 56 57 /* static */ 58 void SharedSurfacesParent::Initialize() { 59 MOZ_ASSERT(NS_IsMainThread()); 60 StaticMutexAutoLock lock(sMutex); 61 if (!sInstance) { 62 sInstance = new SharedSurfacesParent(); 63 } 64 } 65 66 /* static */ 67 void SharedSurfacesParent::ShutdownRenderThread() { 68 // The main thread should blocked on waiting for the render thread to 69 // complete so this should be safe to release off the main thread. 70 MOZ_ASSERT(wr::RenderThread::IsInRenderThread()); 71 StaticMutexAutoLock lock(sMutex); 72 MOZ_ASSERT(sInstance); 73 74 for (const auto& key : sInstance->mSurfaces.Keys()) { 75 // There may be lingering consumers of the surfaces that didn't get shutdown 76 // yet but since we are here, we know the render thread is finished and we 77 // can unregister everything. 78 wr::RenderThread::Get()->UnregisterExternalImageDuringShutdown( 79 wr::ToExternalImageId(key)); 80 } 81 } 82 83 /* static */ 84 void SharedSurfacesParent::Shutdown() { 85 // The compositor thread and render threads are shutdown, so this is the last 86 // thread that could use it. The expiration tracker needs to be freed on the 87 // main thread. 88 MOZ_ASSERT(NS_IsMainThread()); 89 StaticMutexAutoLock lock(sMutex); 90 sInstance = nullptr; 91 } 92 93 /* static */ 94 already_AddRefed<DataSourceSurface> SharedSurfacesParent::Get( 95 const wr::ExternalImageId& aId) { 96 RefPtr<SourceSurfaceSharedDataWrapper> surface; 97 98 { 99 StaticMutexAutoLock lock(sMutex); 100 if (!sInstance) { 101 gfxCriticalNote << "SSP:Get " << wr::AsUint64(aId) << " shtd"; 102 return nullptr; 103 } 104 105 if (sInstance->mSurfaces.Get(wr::AsUint64(aId), getter_AddRefs(surface))) { 106 return surface.forget(); 107 } 108 } 109 110 // We cannot block the compositor thread since that's the thread the necessary 111 // IPDL events would come in on. 112 if (NS_WARN_IF(CompositorThreadHolder::IsInCompositorThread())) { 113 return nullptr; 114 } 115 116 // Block until we see the relevant resource come in or the actor is destroyed. 117 CompositorManagerParent::WaitForSharedSurface(aId); 118 119 StaticMutexAutoLock lock(sMutex); 120 if (!sInstance) { 121 gfxCriticalNote << "SSP:Get " << wr::AsUint64(aId) << " shtd"; 122 return nullptr; 123 } 124 125 sInstance->mSurfaces.Get(wr::AsUint64(aId), getter_AddRefs(surface)); 126 return surface.forget(); 127 } 128 129 /* static */ 130 already_AddRefed<DataSourceSurface> SharedSurfacesParent::Acquire( 131 const wr::ExternalImageId& aId) { 132 StaticMutexAutoLock lock(sMutex); 133 if (!sInstance) { 134 gfxCriticalNote << "SSP:Acq " << wr::AsUint64(aId) << " shtd"; 135 return nullptr; 136 } 137 138 RefPtr<SourceSurfaceSharedDataWrapper> surface; 139 sInstance->mSurfaces.Get(wr::AsUint64(aId), getter_AddRefs(surface)); 140 141 if (surface) { 142 DebugOnly<bool> rv = surface->AddConsumer(); 143 MOZ_ASSERT(!rv); 144 } 145 return surface.forget(); 146 } 147 148 /* static */ 149 bool SharedSurfacesParent::Release(const wr::ExternalImageId& aId, 150 bool aForCreator) { 151 StaticMutexAutoLock lock(sMutex); 152 if (!sInstance) { 153 return false; 154 } 155 156 uint64_t id = wr::AsUint64(aId); 157 RefPtr<SourceSurfaceSharedDataWrapper> surface; 158 sInstance->mSurfaces.Get(wr::AsUint64(aId), getter_AddRefs(surface)); 159 if (!surface) { 160 return false; 161 } 162 163 if (surface->RemoveConsumer(aForCreator)) { 164 RemoveTrackingLocked(surface, lock); 165 wr::RenderThread::Get()->UnregisterExternalImage(wr::ToExternalImageId(id)); 166 sInstance->mSurfaces.Remove(id); 167 } 168 169 return true; 170 } 171 172 /* static */ 173 void SharedSurfacesParent::AddSameProcess(const wr::ExternalImageId& aId, 174 SourceSurfaceSharedData* aSurface) { 175 MOZ_ASSERT(XRE_IsParentProcess()); 176 MOZ_ASSERT(NS_IsMainThread()); 177 StaticMutexAutoLock lock(sMutex); 178 if (!sInstance) { 179 gfxCriticalNote << "SSP:Ads " << wr::AsUint64(aId) << " shtd"; 180 return; 181 } 182 183 // If the child bridge detects it is in the combined UI/GPU process, then it 184 // will insert a wrapper surface holding the shared memory buffer directly. 185 // This is good because we avoid mapping the same shared memory twice, but 186 // still allow the original surface to be freed and remove the wrapper from 187 // the table when it is no longer needed. 188 RefPtr<SourceSurfaceSharedDataWrapper> surface = 189 new SourceSurfaceSharedDataWrapper(); 190 surface->Init(aSurface); 191 192 uint64_t id = wr::AsUint64(aId); 193 if (sInstance->mSurfaces.Contains(id)) { 194 gfxCriticalNote << "SSP:Ads " << wr::AsUint64(aId) << " dupe"; 195 SharedSurfacesParent::RemoveTrackingLocked(surface, lock); 196 MOZ_DIAGNOSTIC_CRASH("External image ID reused!"); 197 return; 198 } 199 200 auto texture = MakeRefPtr<wr::RenderSharedSurfaceTextureHost>(surface); 201 wr::RenderThread::Get()->RegisterExternalImage(aId, texture.forget()); 202 203 sInstance->mSurfaces.InsertOrUpdate(id, std::move(surface)); 204 } 205 206 /* static */ 207 void SharedSurfacesParent::RemoveAll(uint32_t aNamespace) { 208 StaticMutexAutoLock lock(sMutex); 209 if (!sInstance) { 210 return; 211 } 212 213 auto* renderThread = wr::RenderThread::Get(); 214 215 // Note that the destruction of a parent may not be cheap if it still has a 216 // lot of surfaces still bound that require unmapping. 217 for (auto i = sInstance->mSurfaces.Iter(); !i.Done(); i.Next()) { 218 if (static_cast<uint32_t>(i.Key() >> 32) != aNamespace) { 219 continue; 220 } 221 222 SourceSurfaceSharedDataWrapper* surface = i.Data(); 223 if (surface->HasCreatorRef() && 224 surface->RemoveConsumer(/* aForCreator */ true)) { 225 RemoveTrackingLocked(surface, lock); 226 if (renderThread) { 227 renderThread->UnregisterExternalImage(wr::ToExternalImageId(i.Key())); 228 } 229 i.Remove(); 230 } 231 } 232 } 233 234 /* static */ 235 void SharedSurfacesParent::Add(const wr::ExternalImageId& aId, 236 SurfaceDescriptorShared&& aDesc, 237 base::ProcessId aPid) { 238 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); 239 MOZ_ASSERT(aPid != base::GetCurrentProcId()); 240 241 RefPtr<SourceSurfaceSharedDataWrapper> surface = 242 new SourceSurfaceSharedDataWrapper(); 243 244 // We preferentially map in new surfaces when they are initially received 245 // because we are likely to reference them in a display list soon. The unmap 246 // will ensure we add the surface to the expiration tracker. We do it outside 247 // the mutex to ensure we always lock the surface mutex first, and our mutex 248 // second, to avoid deadlock. 249 // 250 // Note that the surface wrapper maps in the given handle as read only. 251 surface->Init(aDesc.size(), aDesc.stride(), aDesc.format(), 252 std::move(aDesc.handle()), aPid); 253 254 StaticMutexAutoLock lock(sMutex); 255 if (!sInstance) { 256 gfxCriticalNote << "SSP:Add " << wr::AsUint64(aId) << " shtd"; 257 return; 258 } 259 260 uint64_t id = wr::AsUint64(aId); 261 if (sInstance->mSurfaces.Contains(id)) { 262 gfxCriticalNote << "SSP:Add " << wr::AsUint64(aId) << " dupe"; 263 SharedSurfacesParent::RemoveTrackingLocked(surface, lock); 264 MOZ_DIAGNOSTIC_CRASH("External image ID reused!"); 265 return; 266 } 267 268 auto texture = MakeRefPtr<wr::RenderSharedSurfaceTextureHost>(surface); 269 wr::RenderThread::Get()->RegisterExternalImage(aId, texture.forget()); 270 271 sInstance->mSurfaces.InsertOrUpdate(id, std::move(surface)); 272 } 273 274 /* static */ 275 void SharedSurfacesParent::Remove(const wr::ExternalImageId& aId) { 276 DebugOnly<bool> rv = Release(aId, /* aForCreator */ true); 277 MOZ_ASSERT(rv); 278 } 279 280 /* static */ 281 void SharedSurfacesParent::AddTrackingLocked( 282 SourceSurfaceSharedDataWrapper* aSurface, 283 const StaticMutexAutoLock& aAutoLock) { 284 MOZ_ASSERT(!aSurface->GetExpirationState()->IsTracked()); 285 MOZ_ASSERT(aSurface->GetConsumers() > 0); 286 sInstance->mTracker.AddObjectLocked(aSurface, aAutoLock); 287 } 288 289 /* static */ 290 void SharedSurfacesParent::AddTracking( 291 SourceSurfaceSharedDataWrapper* aSurface) { 292 StaticMutexAutoLock lock(sMutex); 293 if (!sInstance) { 294 return; 295 } 296 297 AddTrackingLocked(aSurface, lock); 298 } 299 300 /* static */ 301 void SharedSurfacesParent::RemoveTrackingLocked( 302 SourceSurfaceSharedDataWrapper* aSurface, 303 const StaticMutexAutoLock& aAutoLock) { 304 if (!aSurface->GetExpirationState()->IsTracked()) { 305 return; 306 } 307 308 sInstance->mTracker.RemoveObjectLocked(aSurface, aAutoLock); 309 } 310 311 /* static */ 312 void SharedSurfacesParent::RemoveTracking( 313 SourceSurfaceSharedDataWrapper* aSurface) { 314 StaticMutexAutoLock lock(sMutex); 315 if (!sInstance) { 316 return; 317 } 318 319 RemoveTrackingLocked(aSurface, lock); 320 } 321 322 /* static */ 323 bool SharedSurfacesParent::AgeOneGenerationLocked( 324 nsTArray<RefPtr<SourceSurfaceSharedDataWrapper>>& aExpired, 325 const StaticMutexAutoLock& aAutoLock) { 326 if (sInstance->mTracker.IsEmptyLocked(aAutoLock)) { 327 return false; 328 } 329 330 sInstance->mTracker.AgeOneGenerationLocked(aAutoLock); 331 sInstance->mTracker.TakeExpired(aExpired, aAutoLock); 332 return true; 333 } 334 335 /* static */ 336 bool SharedSurfacesParent::AgeOneGeneration( 337 nsTArray<RefPtr<SourceSurfaceSharedDataWrapper>>& aExpired) { 338 StaticMutexAutoLock lock(sMutex); 339 if (!sInstance) { 340 return false; 341 } 342 343 return AgeOneGenerationLocked(aExpired, lock); 344 } 345 346 /* static */ 347 bool SharedSurfacesParent::AgeAndExpireOneGeneration() { 348 nsTArray<RefPtr<SourceSurfaceSharedDataWrapper>> expired; 349 bool aged = AgeOneGeneration(expired); 350 ExpireMap(expired); 351 return aged; 352 } 353 354 /* static */ 355 void SharedSurfacesParent::ExpireMap( 356 nsTArray<RefPtr<SourceSurfaceSharedDataWrapper>>& aExpired) { 357 for (auto& surface : aExpired) { 358 MOZ_ASSERT(surface->GetConsumers() > 0); 359 surface->ExpireMap(); 360 } 361 } 362 363 /* static */ 364 void SharedSurfacesParent::AccumulateMemoryReport( 365 uint32_t aNamespace, SharedSurfacesMemoryReport& aReport) { 366 StaticMutexAutoLock lock(sMutex); 367 if (!sInstance) { 368 return; 369 } 370 371 for (const auto& entry : sInstance->mSurfaces) { 372 if (static_cast<uint32_t>(entry.GetKey() >> 32) != aNamespace) { 373 continue; 374 } 375 376 SourceSurfaceSharedDataWrapper* surface = entry.GetData(); 377 aReport.mSurfaces.insert(std::make_pair( 378 entry.GetKey(), 379 SharedSurfacesMemoryReport::SurfaceEntry{ 380 surface->GetCreatorPid(), surface->GetSize(), surface->Stride(), 381 surface->GetConsumers(), surface->HasCreatorRef()})); 382 } 383 } 384 385 /* static */ 386 bool SharedSurfacesParent::AccumulateMemoryReport( 387 SharedSurfacesMemoryReport& aReport) { 388 if (XRE_IsParentProcess()) { 389 GPUProcessManager* gpm = GPUProcessManager::Get(); 390 if (!gpm || gpm->GPUProcessPid() != base::kInvalidProcessId) { 391 return false; 392 } 393 } else if (!XRE_IsGPUProcess()) { 394 return false; 395 } 396 397 StaticMutexAutoLock lock(sMutex); 398 if (!sInstance) { 399 return true; 400 } 401 402 for (const auto& entry : sInstance->mSurfaces) { 403 SourceSurfaceSharedDataWrapper* surface = entry.GetData(); 404 aReport.mSurfaces.insert(std::make_pair( 405 entry.GetKey(), 406 SharedSurfacesMemoryReport::SurfaceEntry{ 407 surface->GetCreatorPid(), surface->GetSize(), surface->Stride(), 408 surface->GetConsumers(), surface->HasCreatorRef()})); 409 } 410 411 return true; 412 } 413 414 } // namespace layers 415 } // namespace mozilla