Source code

Revision control

Copy as Markdown

Other Tools

/* -*- 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;
MOZ_RUNINIT 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->SetOtherEndpointProcInfo(ipc::EndpointProcInfo::Current());
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