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