diff options
Diffstat (limited to 'gfx/layers/ipc/SharedSurfacesParent.cpp')
-rw-r--r-- | gfx/layers/ipc/SharedSurfacesParent.cpp | 405 |
1 files changed, 405 insertions, 0 deletions
diff --git a/gfx/layers/ipc/SharedSurfacesParent.cpp b/gfx/layers/ipc/SharedSurfacesParent.cpp new file mode 100644 index 0000000000..932a7558ad --- /dev/null +++ b/gfx/layers/ipc/SharedSurfacesParent.cpp @@ -0,0 +1,405 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SharedSurfacesParent.h" +#include "mozilla/DebugOnly.h" +#include "mozilla/StaticPrefs_image.h" +#include "mozilla/gfx/Logging.h" +#include "mozilla/gfx/gfxVars.h" +#include "mozilla/gfx/GPUProcessManager.h" +#include "mozilla/layers/SharedSurfacesMemoryReport.h" +#include "mozilla/layers/SourceSurfaceSharedData.h" +#include "mozilla/layers/CompositorManagerParent.h" +#include "mozilla/layers/CompositorThread.h" +#include "mozilla/webrender/RenderSharedSurfaceTextureHost.h" +#include "mozilla/webrender/RenderThread.h" +#include "nsThreadUtils.h" // for GetCurrentSerialEventTarget + +namespace mozilla { +namespace layers { + +using namespace mozilla::gfx; + +StaticMutex SharedSurfacesParent::sMutex; +StaticAutoPtr<SharedSurfacesParent> SharedSurfacesParent::sInstance; + +void SharedSurfacesParent::MappingTracker::NotifyExpiredLocked( + SourceSurfaceSharedDataWrapper* aSurface, + const StaticMutexAutoLock& aAutoLock) { + RemoveObjectLocked(aSurface, aAutoLock); + mExpired.AppendElement(aSurface); +} + +void SharedSurfacesParent::MappingTracker::TakeExpired( + nsTArray<RefPtr<gfx::SourceSurfaceSharedDataWrapper>>& aExpired, + const StaticMutexAutoLock& aAutoLock) { + aExpired = std::move(mExpired); +} + +void SharedSurfacesParent::MappingTracker::NotifyHandlerEnd() { + nsTArray<RefPtr<gfx::SourceSurfaceSharedDataWrapper>> expired; + { + StaticMutexAutoLock lock(sMutex); + TakeExpired(expired, lock); + } + + SharedSurfacesParent::ExpireMap(expired); +} + +SharedSurfacesParent::SharedSurfacesParent() + : mTracker( + StaticPrefs::image_mem_shared_unmap_min_expiration_ms_AtStartup(), + mozilla::GetCurrentSerialEventTarget()) {} + +/* static */ +void SharedSurfacesParent::Initialize() { + MOZ_ASSERT(NS_IsMainThread()); + StaticMutexAutoLock lock(sMutex); + if (!sInstance) { + sInstance = new SharedSurfacesParent(); + } +} + +/* static */ +void SharedSurfacesParent::ShutdownRenderThread() { + // The main thread should blocked on waiting for the render thread to + // complete so this should be safe to release off the main thread. + MOZ_ASSERT(wr::RenderThread::IsInRenderThread()); + StaticMutexAutoLock lock(sMutex); + MOZ_ASSERT(sInstance); + + for (const auto& key : sInstance->mSurfaces.Keys()) { + // There may be lingering consumers of the surfaces that didn't get shutdown + // yet but since we are here, we know the render thread is finished and we + // can unregister everything. + wr::RenderThread::Get()->UnregisterExternalImageDuringShutdown( + wr::ToExternalImageId(key)); + } +} + +/* static */ +void SharedSurfacesParent::Shutdown() { + // The compositor thread and render threads are shutdown, so this is the last + // thread that could use it. The expiration tracker needs to be freed on the + // main thread. + MOZ_ASSERT(NS_IsMainThread()); + StaticMutexAutoLock lock(sMutex); + sInstance = nullptr; +} + +/* static */ +already_AddRefed<DataSourceSurface> SharedSurfacesParent::Get( + const wr::ExternalImageId& aId) { + RefPtr<SourceSurfaceSharedDataWrapper> surface; + + { + StaticMutexAutoLock lock(sMutex); + if (!sInstance) { + gfxCriticalNote << "SSP:Get " << wr::AsUint64(aId) << " shtd"; + return nullptr; + } + + if (sInstance->mSurfaces.Get(wr::AsUint64(aId), getter_AddRefs(surface))) { + return surface.forget(); + } + } + + // We cannot block the compositor thread since that's the thread the necessary + // IPDL events would come in on. + if (NS_WARN_IF(CompositorThreadHolder::IsInCompositorThread())) { + return nullptr; + } + + // Block until we see the relevant resource come in or the actor is destroyed. + CompositorManagerParent::WaitForSharedSurface(aId); + + StaticMutexAutoLock lock(sMutex); + if (!sInstance) { + gfxCriticalNote << "SSP:Get " << wr::AsUint64(aId) << " shtd"; + return nullptr; + } + + sInstance->mSurfaces.Get(wr::AsUint64(aId), getter_AddRefs(surface)); + return surface.forget(); +} + +/* static */ +already_AddRefed<DataSourceSurface> SharedSurfacesParent::Acquire( + const wr::ExternalImageId& aId) { + StaticMutexAutoLock lock(sMutex); + if (!sInstance) { + gfxCriticalNote << "SSP:Acq " << wr::AsUint64(aId) << " shtd"; + return nullptr; + } + + RefPtr<SourceSurfaceSharedDataWrapper> surface; + sInstance->mSurfaces.Get(wr::AsUint64(aId), getter_AddRefs(surface)); + + if (surface) { + DebugOnly<bool> rv = surface->AddConsumer(); + MOZ_ASSERT(!rv); + } + return surface.forget(); +} + +/* static */ +bool SharedSurfacesParent::Release(const wr::ExternalImageId& aId, + bool aForCreator) { + StaticMutexAutoLock lock(sMutex); + if (!sInstance) { + return false; + } + + uint64_t id = wr::AsUint64(aId); + RefPtr<SourceSurfaceSharedDataWrapper> surface; + sInstance->mSurfaces.Get(wr::AsUint64(aId), getter_AddRefs(surface)); + if (!surface) { + return false; + } + + if (surface->RemoveConsumer(aForCreator)) { + RemoveTrackingLocked(surface, lock); + wr::RenderThread::Get()->UnregisterExternalImage(wr::ToExternalImageId(id)); + sInstance->mSurfaces.Remove(id); + } + + return true; +} + +/* static */ +void SharedSurfacesParent::AddSameProcess(const wr::ExternalImageId& aId, + SourceSurfaceSharedData* aSurface) { + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(NS_IsMainThread()); + StaticMutexAutoLock lock(sMutex); + if (!sInstance) { + gfxCriticalNote << "SSP:Ads " << wr::AsUint64(aId) << " shtd"; + return; + } + + // If the child bridge detects it is in the combined UI/GPU process, then it + // will insert a wrapper surface holding the shared memory buffer directly. + // This is good because we avoid mapping the same shared memory twice, but + // still allow the original surface to be freed and remove the wrapper from + // the table when it is no longer needed. + RefPtr<SourceSurfaceSharedDataWrapper> surface = + new SourceSurfaceSharedDataWrapper(); + surface->Init(aSurface); + + uint64_t id = wr::AsUint64(aId); + MOZ_ASSERT(!sInstance->mSurfaces.Contains(id)); + + auto texture = MakeRefPtr<wr::RenderSharedSurfaceTextureHost>(surface); + wr::RenderThread::Get()->RegisterExternalImage(aId, texture.forget()); + + surface->AddConsumer(); + sInstance->mSurfaces.InsertOrUpdate(id, std::move(surface)); +} + +/* static */ +void SharedSurfacesParent::RemoveAll(uint32_t aNamespace) { + StaticMutexAutoLock lock(sMutex); + if (!sInstance) { + return; + } + + auto* renderThread = wr::RenderThread::Get(); + + // Note that the destruction of a parent may not be cheap if it still has a + // lot of surfaces still bound that require unmapping. + for (auto i = sInstance->mSurfaces.Iter(); !i.Done(); i.Next()) { + if (static_cast<uint32_t>(i.Key() >> 32) != aNamespace) { + continue; + } + + SourceSurfaceSharedDataWrapper* surface = i.Data(); + if (surface->HasCreatorRef() && + surface->RemoveConsumer(/* aForCreator */ true)) { + RemoveTrackingLocked(surface, lock); + if (renderThread) { + renderThread->UnregisterExternalImage(wr::ToExternalImageId(i.Key())); + } + i.Remove(); + } + } +} + +/* static */ +void SharedSurfacesParent::Add(const wr::ExternalImageId& aId, + SurfaceDescriptorShared&& aDesc, + base::ProcessId aPid) { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + MOZ_ASSERT(aPid != base::GetCurrentProcId()); + + RefPtr<SourceSurfaceSharedDataWrapper> surface = + new SourceSurfaceSharedDataWrapper(); + + // We preferentially map in new surfaces when they are initially received + // because we are likely to reference them in a display list soon. The unmap + // will ensure we add the surface to the expiration tracker. We do it outside + // the mutex to ensure we always lock the surface mutex first, and our mutex + // second, to avoid deadlock. + // + // Note that the surface wrapper maps in the given handle as read only. + surface->Init(aDesc.size(), aDesc.stride(), aDesc.format(), + std::move(aDesc.handle()), aPid); + + StaticMutexAutoLock lock(sMutex); + if (!sInstance) { + gfxCriticalNote << "SSP:Add " << wr::AsUint64(aId) << " shtd"; + return; + } + + uint64_t id = wr::AsUint64(aId); + MOZ_ASSERT(!sInstance->mSurfaces.Contains(id)); + + auto texture = MakeRefPtr<wr::RenderSharedSurfaceTextureHost>(surface); + wr::RenderThread::Get()->RegisterExternalImage(aId, texture.forget()); + + surface->AddConsumer(); + sInstance->mSurfaces.InsertOrUpdate(id, std::move(surface)); +} + +/* static */ +void SharedSurfacesParent::Remove(const wr::ExternalImageId& aId) { + DebugOnly<bool> rv = Release(aId, /* aForCreator */ true); + MOZ_ASSERT(rv); +} + +/* static */ +void SharedSurfacesParent::AddTrackingLocked( + SourceSurfaceSharedDataWrapper* aSurface, + const StaticMutexAutoLock& aAutoLock) { + MOZ_ASSERT(!aSurface->GetExpirationState()->IsTracked()); + sInstance->mTracker.AddObjectLocked(aSurface, aAutoLock); +} + +/* static */ +void SharedSurfacesParent::AddTracking( + SourceSurfaceSharedDataWrapper* aSurface) { + StaticMutexAutoLock lock(sMutex); + if (!sInstance) { + return; + } + + AddTrackingLocked(aSurface, lock); +} + +/* static */ +void SharedSurfacesParent::RemoveTrackingLocked( + SourceSurfaceSharedDataWrapper* aSurface, + const StaticMutexAutoLock& aAutoLock) { + if (!aSurface->GetExpirationState()->IsTracked()) { + return; + } + + sInstance->mTracker.RemoveObjectLocked(aSurface, aAutoLock); +} + +/* static */ +void SharedSurfacesParent::RemoveTracking( + SourceSurfaceSharedDataWrapper* aSurface) { + StaticMutexAutoLock lock(sMutex); + if (!sInstance) { + return; + } + + RemoveTrackingLocked(aSurface, lock); +} + +/* static */ +bool SharedSurfacesParent::AgeOneGenerationLocked( + nsTArray<RefPtr<SourceSurfaceSharedDataWrapper>>& aExpired, + const StaticMutexAutoLock& aAutoLock) { + if (sInstance->mTracker.IsEmptyLocked(aAutoLock)) { + return false; + } + + sInstance->mTracker.AgeOneGenerationLocked(aAutoLock); + sInstance->mTracker.TakeExpired(aExpired, aAutoLock); + return true; +} + +/* static */ +bool SharedSurfacesParent::AgeOneGeneration( + nsTArray<RefPtr<SourceSurfaceSharedDataWrapper>>& aExpired) { + StaticMutexAutoLock lock(sMutex); + if (!sInstance) { + return false; + } + + return AgeOneGenerationLocked(aExpired, lock); +} + +/* static */ +bool SharedSurfacesParent::AgeAndExpireOneGeneration() { + nsTArray<RefPtr<SourceSurfaceSharedDataWrapper>> expired; + bool aged = AgeOneGeneration(expired); + ExpireMap(expired); + return aged; +} + +/* static */ +void SharedSurfacesParent::ExpireMap( + nsTArray<RefPtr<SourceSurfaceSharedDataWrapper>>& aExpired) { + for (auto& surface : aExpired) { + surface->ExpireMap(); + } +} + +/* static */ +void SharedSurfacesParent::AccumulateMemoryReport( + uint32_t aNamespace, SharedSurfacesMemoryReport& aReport) { + StaticMutexAutoLock lock(sMutex); + if (!sInstance) { + return; + } + + for (const auto& entry : sInstance->mSurfaces) { + if (static_cast<uint32_t>(entry.GetKey() >> 32) != aNamespace) { + continue; + } + + SourceSurfaceSharedDataWrapper* surface = entry.GetData(); + aReport.mSurfaces.insert(std::make_pair( + entry.GetKey(), + SharedSurfacesMemoryReport::SurfaceEntry{ + surface->GetCreatorPid(), surface->GetSize(), surface->Stride(), + surface->GetConsumers(), surface->HasCreatorRef()})); + } +} + +/* static */ +bool SharedSurfacesParent::AccumulateMemoryReport( + SharedSurfacesMemoryReport& aReport) { + if (XRE_IsParentProcess()) { + GPUProcessManager* gpm = GPUProcessManager::Get(); + if (!gpm || gpm->GPUProcessPid() != base::kInvalidProcessId) { + return false; + } + } else if (!XRE_IsGPUProcess()) { + return false; + } + + StaticMutexAutoLock lock(sMutex); + if (!sInstance) { + return true; + } + + for (const auto& entry : sInstance->mSurfaces) { + SourceSurfaceSharedDataWrapper* surface = entry.GetData(); + aReport.mSurfaces.insert(std::make_pair( + entry.GetKey(), + SharedSurfacesMemoryReport::SurfaceEntry{ + surface->GetCreatorPid(), surface->GetSize(), surface->Stride(), + surface->GetConsumers(), surface->HasCreatorRef()})); + } + + return true; +} + +} // namespace layers +} // namespace mozilla |