summaryrefslogtreecommitdiffstats
path: root/gfx/layers/ipc/SharedSurfacesParent.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/layers/ipc/SharedSurfacesParent.cpp')
-rw-r--r--gfx/layers/ipc/SharedSurfacesParent.cpp405
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