diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /gfx/layers/ipc/CompositorManagerParent.cpp | |
parent | Initial commit. (diff) | |
download | firefox-e51783d008170d9ab27d25da98ca3a38b0a41b67.tar.xz firefox-e51783d008170d9ab27d25da98ca3a38b0a41b67.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gfx/layers/ipc/CompositorManagerParent.cpp')
-rw-r--r-- | gfx/layers/ipc/CompositorManagerParent.cpp | 396 |
1 files changed, 396 insertions, 0 deletions
diff --git a/gfx/layers/ipc/CompositorManagerParent.cpp b/gfx/layers/ipc/CompositorManagerParent.cpp new file mode 100644 index 0000000000..29d9699033 --- /dev/null +++ b/gfx/layers/ipc/CompositorManagerParent.cpp @@ -0,0 +1,396 @@ +/* -*- 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 "mozilla/layers/CompositorManagerParent.h" +#include "mozilla/gfx/GPUParent.h" +#include "mozilla/gfx/CanvasManagerParent.h" +#include "mozilla/webrender/RenderThread.h" +#include "mozilla/ipc/Endpoint.h" +#include "mozilla/layers/CompositorBridgeParent.h" +#include "mozilla/layers/ContentCompositorBridgeParent.h" +#include "mozilla/layers/CompositorThread.h" +#include "mozilla/layers/RemoteTextureMap.h" +#include "mozilla/layers/SharedSurfacesParent.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/Unused.h" +#include "gfxPlatform.h" +#include "VsyncSource.h" + +namespace mozilla { +namespace layers { + +StaticMonitor CompositorManagerParent::sMonitor; +StaticRefPtr<CompositorManagerParent> CompositorManagerParent::sInstance; +CompositorManagerParent::ManagerMap CompositorManagerParent::sManagers; + +/* static */ +already_AddRefed<CompositorManagerParent> +CompositorManagerParent::CreateSameProcess(uint32_t aNamespace) { + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(NS_IsMainThread()); + StaticMonitorAutoLock lock(sMonitor); + + // We are creating a manager for the UI process, inside the combined GPU/UI + // process. It is created more-or-less the same but we retain a reference to + // the parent to access state. + if (NS_WARN_IF(sInstance)) { + MOZ_ASSERT_UNREACHABLE("Already initialized"); + return nullptr; + } + + // The child is responsible for setting up the IPC channel in the same + // process case because if we open from the child perspective, we can do it + // on the main thread and complete before we return the manager handles. + RefPtr<CompositorManagerParent> parent = + new CompositorManagerParent(dom::ContentParentId(), aNamespace); + parent->SetOtherProcessId(base::GetCurrentProcId()); + return parent.forget(); +} + +/* static */ +bool CompositorManagerParent::Create( + Endpoint<PCompositorManagerParent>&& aEndpoint, + dom::ContentParentId aChildId, uint32_t aNamespace, bool aIsRoot) { + MOZ_ASSERT(NS_IsMainThread()); + + // We are creating a manager for the another process, inside the GPU process + // (or UI process if it subsumbed the GPU process). + MOZ_ASSERT(aEndpoint.OtherPid() != base::GetCurrentProcId()); + + if (!CompositorThreadHolder::IsActive()) { + return false; + } + + RefPtr<CompositorManagerParent> bridge = + new CompositorManagerParent(aChildId, aNamespace); + + RefPtr<Runnable> runnable = + NewRunnableMethod<Endpoint<PCompositorManagerParent>&&, bool>( + "CompositorManagerParent::Bind", bridge, + &CompositorManagerParent::Bind, std::move(aEndpoint), aIsRoot); + CompositorThread()->Dispatch(runnable.forget()); + return true; +} + +/* static */ +already_AddRefed<CompositorBridgeParent> +CompositorManagerParent::CreateSameProcessWidgetCompositorBridge( + CSSToLayoutDeviceScale aScale, const CompositorOptions& aOptions, + bool aUseExternalSurfaceSize, const gfx::IntSize& aSurfaceSize, + uint64_t aInnerWindowId) { + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(NS_IsMainThread()); + + // When we are in a combined UI / GPU process, InProcessCompositorSession + // requires both the parent and child PCompositorBridge actors for its own + // construction, which is done on the main thread. Normally + // CompositorBridgeParent is created on the compositor thread via the IPDL + // plumbing (CompositorManagerParent::AllocPCompositorBridgeParent). Thus to + // actually get a reference to the parent, we would need to block on the + // compositor thread until it handles our constructor message. Because only + // one one IPDL constructor is permitted per parent and child protocol, we + // cannot make the normal case async and this case sync. Instead what we do + // is leave the constructor async (a boon to the content process setup) and + // create the parent ahead of time. It will pull the preinitialized parent + // from the queue when it receives the message and give that to IPDL. + + // Note that the static mutex not only is used to protect sInstance, but also + // mPendingCompositorBridges. + StaticMonitorAutoLock lock(sMonitor); + if (NS_WARN_IF(!sInstance)) { + return nullptr; + } + + TimeDuration vsyncRate = + gfxPlatform::GetPlatform()->GetGlobalVsyncDispatcher()->GetVsyncRate(); + + RefPtr<CompositorBridgeParent> bridge = new CompositorBridgeParent( + sInstance, aScale, vsyncRate, aOptions, aUseExternalSurfaceSize, + aSurfaceSize, aInnerWindowId); + + sInstance->mPendingCompositorBridges.AppendElement(bridge); + return bridge.forget(); +} + +CompositorManagerParent::CompositorManagerParent( + dom::ContentParentId aContentId, uint32_t aNamespace) + : mCompositorThreadHolder(CompositorThreadHolder::GetSingleton()), + mSharedSurfacesHolder(MakeRefPtr<SharedSurfacesHolder>(aNamespace)), + mContentId(aContentId), + mNamespace(aNamespace) {} + +CompositorManagerParent::~CompositorManagerParent() = default; + +void CompositorManagerParent::Bind( + Endpoint<PCompositorManagerParent>&& aEndpoint, bool aIsRoot) { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + if (NS_WARN_IF(!aEndpoint.Bind(this))) { + return; + } + + BindComplete(aIsRoot); +} + +void CompositorManagerParent::BindComplete(bool aIsRoot) { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread() || + NS_IsMainThread()); + + StaticMonitorAutoLock lock(sMonitor); + if (aIsRoot) { + MOZ_ASSERT(!sInstance); + sInstance = this; + } + + MOZ_RELEASE_ASSERT(sManagers.try_emplace(mNamespace, this).second); +} + +void CompositorManagerParent::ActorDestroy(ActorDestroyReason aReason) { + GetCurrentSerialEventTarget()->Dispatch( + NewRunnableMethod("layers::CompositorManagerParent::DeferredDestroy", + this, &CompositorManagerParent::DeferredDestroy)); + + if (mRemoteTextureTxnScheduler) { + mRemoteTextureTxnScheduler = nullptr; + } + + StaticMonitorAutoLock lock(sMonitor); + if (sInstance == this) { + sInstance = nullptr; + } + + MOZ_RELEASE_ASSERT(sManagers.erase(mNamespace) > 0); + sMonitor.NotifyAll(); +} + +void CompositorManagerParent::DeferredDestroy() { + mCompositorThreadHolder = nullptr; +} + +/* static */ +void CompositorManagerParent::ShutdownInternal() { + nsTArray<RefPtr<CompositorManagerParent>> actors; + + // We move here because we may attempt to acquire the same lock during the + // destroy to remove the reference in sManagers. + { + StaticMonitorAutoLock lock(sMonitor); + actors.SetCapacity(sManagers.size()); + for (auto& i : sManagers) { + actors.AppendElement(i.second); + } + } + + for (auto& actor : actors) { + actor->Close(); + } +} + +/* static */ +void CompositorManagerParent::Shutdown() { + MOZ_ASSERT(NS_IsMainThread()); + + CompositorThread()->Dispatch(NS_NewRunnableFunction( + "layers::CompositorManagerParent::Shutdown", + []() -> void { CompositorManagerParent::ShutdownInternal(); })); +} + +/* static */ void CompositorManagerParent::WaitForSharedSurface( + const wr::ExternalImageId& aId) { + uint32_t extNamespace = static_cast<uint32_t>(wr::AsUint64(aId) >> 32); + uint32_t resourceId = static_cast<uint32_t>(wr::AsUint64(aId)); + + StaticMonitorAutoLock lock(sMonitor); + + while (true) { + const auto i = sManagers.find(extNamespace); + if (NS_WARN_IF(i == sManagers.end())) { + break; + } + + // We know that when the resource ID is allocated, we either fail to have + // shared the surface with the compositor process, and so we don't use the + // external image ID, or we have queued an IPDL message over the + // corresponding CompositorManagerParent object to map that surface into + // memory. They are dispatched in order, so we can safely wait until either + // the actor is closed, or the last seen resource ID reaches the target. + if (i->second->mLastSharedSurfaceResourceId >= resourceId) { + break; + } + + lock.Wait(); + } +} + +already_AddRefed<PCompositorBridgeParent> +CompositorManagerParent::AllocPCompositorBridgeParent( + const CompositorBridgeOptions& aOpt) { + switch (aOpt.type()) { + case CompositorBridgeOptions::TContentCompositorOptions: { + RefPtr<ContentCompositorBridgeParent> bridge = + new ContentCompositorBridgeParent(this); + return bridge.forget(); + } + case CompositorBridgeOptions::TWidgetCompositorOptions: { + // Only the UI process is allowed to create widget compositors in the + // compositor process. + gfx::GPUParent* gpu = gfx::GPUParent::GetSingleton(); + if (NS_WARN_IF(!gpu || OtherPid() != gpu->OtherPid())) { + MOZ_ASSERT_UNREACHABLE("Child cannot create widget compositor!"); + break; + } + + const WidgetCompositorOptions& opt = aOpt.get_WidgetCompositorOptions(); + RefPtr<CompositorBridgeParent> bridge = new CompositorBridgeParent( + this, opt.scale(), opt.vsyncRate(), opt.options(), + opt.useExternalSurfaceSize(), opt.surfaceSize(), opt.innerWindowId()); + return bridge.forget(); + } + case CompositorBridgeOptions::TSameProcessWidgetCompositorOptions: { + // If the GPU and UI process are combined, we actually already created the + // CompositorBridgeParent, so we need to reuse that to inject it into the + // IPDL framework. + if (NS_WARN_IF(OtherPid() != base::GetCurrentProcId())) { + MOZ_ASSERT_UNREACHABLE("Child cannot create same process compositor!"); + break; + } + + // Note that the static mutex not only is used to protect sInstance, but + // also mPendingCompositorBridges. + StaticMonitorAutoLock lock(sMonitor); + if (mPendingCompositorBridges.IsEmpty()) { + break; + } + + RefPtr<CompositorBridgeParent> bridge = mPendingCompositorBridges[0]; + mPendingCompositorBridges.RemoveElementAt(0); + return bridge.forget(); + } + default: + break; + } + + return nullptr; +} + +/* static */ void CompositorManagerParent::AddSharedSurface( + const wr::ExternalImageId& aId, gfx::SourceSurfaceSharedData* aSurface) { + MOZ_ASSERT(XRE_IsParentProcess()); + + StaticMonitorAutoLock lock(sMonitor); + if (NS_WARN_IF(!sInstance)) { + return; + } + + if (NS_WARN_IF(!sInstance->OwnsExternalImageId(aId))) { + MOZ_ASSERT_UNREACHABLE("Wrong namespace?"); + return; + } + + SharedSurfacesParent::AddSameProcess(aId, aSurface); + + uint32_t resourceId = static_cast<uint32_t>(wr::AsUint64(aId)); + MOZ_RELEASE_ASSERT(sInstance->mLastSharedSurfaceResourceId < resourceId); + sInstance->mLastSharedSurfaceResourceId = resourceId; + sMonitor.NotifyAll(); +} + +mozilla::ipc::IPCResult CompositorManagerParent::RecvAddSharedSurface( + const wr::ExternalImageId& aId, SurfaceDescriptorShared&& aDesc) { + if (NS_WARN_IF(!OwnsExternalImageId(aId))) { + MOZ_ASSERT_UNREACHABLE("Wrong namespace?"); + return IPC_OK(); + } + + SharedSurfacesParent::Add(aId, std::move(aDesc), OtherPid()); + + StaticMonitorAutoLock lock(sMonitor); + uint32_t resourceId = static_cast<uint32_t>(wr::AsUint64(aId)); + MOZ_RELEASE_ASSERT(mLastSharedSurfaceResourceId < resourceId); + mLastSharedSurfaceResourceId = resourceId; + sMonitor.NotifyAll(); + return IPC_OK(); +} + +mozilla::ipc::IPCResult CompositorManagerParent::RecvRemoveSharedSurface( + const wr::ExternalImageId& aId) { + if (NS_WARN_IF(!OwnsExternalImageId(aId))) { + MOZ_ASSERT_UNREACHABLE("Wrong namespace?"); + return IPC_OK(); + } + + SharedSurfacesParent::Remove(aId); + return IPC_OK(); +} + +mozilla::ipc::IPCResult CompositorManagerParent::RecvReportSharedSurfacesMemory( + ReportSharedSurfacesMemoryResolver&& aResolver) { + SharedSurfacesMemoryReport report; + SharedSurfacesParent::AccumulateMemoryReport(mNamespace, report); + aResolver(std::move(report)); + return IPC_OK(); +} + +mozilla::ipc::IPCResult CompositorManagerParent::RecvNotifyMemoryPressure() { + nsTArray<PCompositorBridgeParent*> compositorBridges; + ManagedPCompositorBridgeParent(compositorBridges); + for (auto bridge : compositorBridges) { + static_cast<CompositorBridgeParentBase*>(bridge)->NotifyMemoryPressure(); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult CompositorManagerParent::RecvReportMemory( + ReportMemoryResolver&& aResolver) { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + MemoryReport aggregate; + PodZero(&aggregate); + + // Accumulate RenderBackend usage. + nsTArray<PCompositorBridgeParent*> compositorBridges; + ManagedPCompositorBridgeParent(compositorBridges); + for (auto bridge : compositorBridges) { + static_cast<CompositorBridgeParentBase*>(bridge)->AccumulateMemoryReport( + &aggregate); + } + + // Accumulate Renderer usage asynchronously, and resolve. + // + // Note that the IPDL machinery requires aResolver to be called on this + // thread, so we can't just pass it over to the renderer thread. We use + // an intermediate MozPromise instead. + wr::RenderThread::AccumulateMemoryReport(aggregate)->Then( + CompositorThread(), __func__, + [resolver = std::move(aResolver)](MemoryReport aReport) { + resolver(aReport); + }, + [](bool) { + MOZ_ASSERT_UNREACHABLE("MemoryReport promises are never rejected"); + }); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult CompositorManagerParent::RecvInitCanvasManager( + Endpoint<PCanvasManagerParent>&& aEndpoint) { + gfx::CanvasManagerParent::Init(std::move(aEndpoint), mSharedSurfacesHolder, + mContentId); + mRemoteTextureTxnScheduler = RemoteTextureTxnScheduler::Create(this); + return IPC_OK(); +} + +/* static */ +void CompositorManagerParent::NotifyWebRenderError(wr::WebRenderError aError) { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + + StaticMonitorAutoLock lock(sMonitor); + if (NS_WARN_IF(!sInstance)) { + return; + } + Unused << sInstance->SendNotifyWebRenderError(aError); +} + +} // namespace layers +} // namespace mozilla |