tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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