Bug 1734649 - Part 2. Create PCanvasManager to manage WebGL instances. r=jrmuizel

This patch adds the necessary IPDL plumbing to allow us to create WebGL
instances off the main thread in the content process, and to execute
them on the Renderer thread in the compositor process.

Differential Revision: https://phabricator.services.mozilla.com/D127839
This commit is contained in:
Andrew Osmond 2021-11-04 16:29:44 +00:00
Родитель bbbb7c2f0e
Коммит cdc239b74f
16 изменённых файлов: 371 добавлений и 3 удалений

Просмотреть файл

@ -6,6 +6,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
include protocol PCompositorBridge;
include protocol PCanvasManager;
using mozilla::layers::CompositableHandle from "mozilla/layers/LayersTypes.h";
using mozilla::layers::SurfaceDescriptor from "mozilla/layers/LayersTypes.h";
@ -35,7 +36,7 @@ namespace dom {
*/
[RefCounted] sync protocol PWebGL
{
manager PCompositorBridge;
manager PCompositorBridge or PCanvasManager;
parent:
sync Initialize(InitContextDesc desc)

Просмотреть файл

@ -0,0 +1,129 @@
/* -*- 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 "CanvasManagerChild.h"
#include "mozilla/dom/WorkerPrivate.h"
#include "mozilla/dom/WorkerRef.h"
#include "mozilla/ipc/Endpoint.h"
#include "mozilla/layers/CompositorManagerChild.h"
using namespace mozilla::dom;
using namespace mozilla::layers;
namespace mozilla::gfx {
// The IPDL actor holds a strong reference to CanvasManagerChild which we use
// to keep it alive. The owning thread will tell us to close when it is
// shutdown, either via CanvasManagerChild::Shutdown for the main thread, or
// via a shutdown callback from IPCWorkerRef for worker threads.
MOZ_THREAD_LOCAL(CanvasManagerChild*) CanvasManagerChild::sLocalManager;
CanvasManagerChild::CanvasManagerChild() = default;
CanvasManagerChild::~CanvasManagerChild() = default;
void CanvasManagerChild::ActorDestroy(ActorDestroyReason aReason) {
if (sLocalManager.get() == this) {
sLocalManager.set(nullptr);
}
}
void CanvasManagerChild::Destroy() {
// The caller has a strong reference. ActorDestroy will clear sLocalManager.
Close();
mWorkerRef = nullptr;
}
/* static */ void CanvasManagerChild::Shutdown() {
MOZ_ASSERT(NS_IsMainThread());
// The worker threads should destroy their own CanvasManagerChild instances
// during their shutdown sequence. We just need to take care of the main
// thread. We need to init here because we may have never created a
// CanvasManagerChild for the main thread in the first place.
if (sLocalManager.init()) {
RefPtr<CanvasManagerChild> manager = sLocalManager.get();
if (manager) {
manager->Destroy();
}
}
}
/* static */ bool CanvasManagerChild::CreateParent(
ipc::Endpoint<PCanvasManagerParent>&& aEndpoint) {
MOZ_ASSERT(NS_IsMainThread());
CompositorManagerChild* manager = CompositorManagerChild::GetInstance();
if (!manager || !manager->CanSend()) {
return false;
}
return manager->SendInitCanvasManager(std::move(aEndpoint));
}
/* static */ CanvasManagerChild* CanvasManagerChild::Get() {
if (NS_WARN_IF(!sLocalManager.init())) {
return nullptr;
}
CanvasManagerChild* managerWeak = sLocalManager.get();
if (managerWeak) {
return managerWeak;
}
// We are only used on the main thread, or on worker threads.
WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT_IF(!worker, NS_IsMainThread());
ipc::Endpoint<PCanvasManagerParent> parentEndpoint;
ipc::Endpoint<PCanvasManagerChild> childEndpoint;
auto compositorPid = CompositorManagerChild::GetOtherPid();
if (NS_WARN_IF(!compositorPid)) {
return nullptr;
}
nsresult rv = PCanvasManager::CreateEndpoints(
compositorPid, base::GetCurrentProcId(), &parentEndpoint, &childEndpoint);
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}
auto manager = MakeRefPtr<CanvasManagerChild>();
if (worker) {
// The IPCWorkerRef will let us know when the worker is shutting down. This
// will let us clear our threadlocal reference and close the actor. We rely
// upon an explicit shutdown for the main thread.
manager->mWorkerRef = IPCWorkerRef::Create(
worker, "CanvasManager", [manager]() { manager->Destroy(); });
if (NS_WARN_IF(!manager->mWorkerRef)) {
return nullptr;
}
}
if (NS_WARN_IF(!childEndpoint.Bind(manager))) {
return nullptr;
}
// We can't talk to the compositor process directly from worker threads, but
// the main thread can via CompositorManagerChild.
if (worker) {
worker->DispatchToMainThread(NS_NewRunnableFunction(
"CanvasManagerChild::CreateParent",
[parentEndpoint = std::move(parentEndpoint)]() {
CreateParent(
std::move(const_cast<ipc::Endpoint<PCanvasManagerParent>&&>(
parentEndpoint)));
}));
} else if (NS_WARN_IF(!CreateParent(std::move(parentEndpoint)))) {
return nullptr;
}
sLocalManager.set(manager);
return manager;
}
} // namespace mozilla::gfx

Просмотреть файл

@ -0,0 +1,44 @@
/* -*- 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/. */
#ifndef _include_gfx_ipc_CanvasManagerChild_h__
#define _include_gfx_ipc_CanvasManagerChild_h__
#include "mozilla/gfx/PCanvasManagerChild.h"
#include "mozilla/ThreadLocal.h"
namespace mozilla {
namespace dom {
class IPCWorkerRef;
class WorkerPrivate;
} // namespace dom
namespace gfx {
class CanvasManagerChild final : public PCanvasManagerChild {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CanvasManagerChild, override);
CanvasManagerChild();
void ActorDestroy(ActorDestroyReason aReason) override;
static CanvasManagerChild* Get();
static void Shutdown();
static bool CreateParent(
mozilla::ipc::Endpoint<PCanvasManagerParent>&& aEndpoint);
private:
~CanvasManagerChild();
void Destroy();
RefPtr<mozilla::dom::IPCWorkerRef> mWorkerRef;
static MOZ_THREAD_LOCAL(CanvasManagerChild*) sLocalManager;
};
} // namespace gfx
} // namespace mozilla
#endif // _include_gfx_ipc_CanvasManagerChild_h__

Просмотреть файл

@ -0,0 +1,78 @@
/* -*- 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 "CanvasManagerParent.h"
#include "mozilla/dom/WebGLParent.h"
#include "mozilla/ipc/Endpoint.h"
#include "mozilla/layers/CompositorThread.h"
#include "mozilla/webrender/RenderThread.h"
#include "nsIThread.h"
namespace mozilla::gfx {
CanvasManagerParent::ManagerSet CanvasManagerParent::sManagers;
/* static */ void CanvasManagerParent::Init(
Endpoint<PCanvasManagerParent>&& aEndpoint) {
MOZ_ASSERT(layers::CompositorThreadHolder::IsInCompositorThread());
nsCOMPtr<nsIThread> owningThread = wr::RenderThread::GetRenderThread();
MOZ_ASSERT(owningThread);
auto manager = MakeRefPtr<CanvasManagerParent>();
owningThread->Dispatch(NewRunnableMethod<Endpoint<PCanvasManagerParent>&&>(
"CanvasManagerParent::Bind", manager, &CanvasManagerParent::Bind,
std::move(aEndpoint)));
}
/* static */ void CanvasManagerParent::Shutdown() {
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIThread> owningThread = wr::RenderThread::GetRenderThread();
if (!owningThread) {
return;
}
owningThread->Dispatch(
NS_NewRunnableFunction(
"CanvasManagerParent::Shutdown",
[]() -> void { CanvasManagerParent::ShutdownInternal(); }),
NS_DISPATCH_SYNC);
}
/* static */ void CanvasManagerParent::ShutdownInternal() {
nsTArray<RefPtr<CanvasManagerParent>> actors(sManagers.Count());
// Do a copy since Close will remove the entry from the set.
for (const auto& actor : sManagers) {
actors.AppendElement(actor);
}
for (auto const& actor : actors) {
actor->Close();
}
}
CanvasManagerParent::CanvasManagerParent() = default;
CanvasManagerParent::~CanvasManagerParent() = default;
void CanvasManagerParent::Bind(Endpoint<PCanvasManagerParent>&& aEndpoint) {
if (!aEndpoint.Bind(this)) {
NS_WARNING("Failed to bind CanvasManagerParent!");
return;
}
sManagers.Insert(this);
}
void CanvasManagerParent::ActorDestroy(ActorDestroyReason aWhy) {
sManagers.Remove(this);
}
already_AddRefed<dom::PWebGLParent> CanvasManagerParent::AllocPWebGLParent() {
return MakeAndAddRef<dom::WebGLParent>();
}
} // namespace mozilla::gfx

Просмотреть файл

@ -0,0 +1,39 @@
/* -*- 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/. */
#ifndef _include_gfx_ipc_CanvasManagerParent_h__
#define _include_gfx_ipc_CanvasManagerParent_h__
#include "mozilla/gfx/PCanvasManagerParent.h"
#include "nsHashtablesFwd.h"
namespace mozilla::gfx {
class CanvasManagerParent final : public PCanvasManagerParent {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CanvasManagerParent, override);
static void Init(Endpoint<PCanvasManagerParent>&& aEndpoint);
static void Shutdown();
CanvasManagerParent();
void Bind(Endpoint<PCanvasManagerParent>&& aEndpoint);
void ActorDestroy(ActorDestroyReason aWhy) override;
already_AddRefed<PWebGLParent> AllocPWebGLParent();
private:
static void ShutdownInternal();
~CanvasManagerParent();
using ManagerSet = nsTHashSet<CanvasManagerParent*>;
static ManagerSet sManagers;
};
} // namespace mozilla::gfx
#endif // _include_gfx_ipc_CanvasManagerParent_h__

Просмотреть файл

@ -14,6 +14,7 @@
#include "GPUProcessManager.h"
#include "gfxGradientCache.h"
#include "GfxInfoBase.h"
#include "CanvasManagerParent.h"
#include "VRGPUChild.h"
#include "VRManager.h"
#include "VRManagerParent.h"
@ -629,6 +630,7 @@ void GPUParent::ActorDestroy(ActorDestroyReason aWhy) {
}
RemoteDecoderManagerParent::ShutdownVideoBridge();
CompositorThreadHolder::Shutdown();
CanvasManagerParent::Shutdown();
// There is a case that RenderThread exists when gfxVars::UseWebRender()
// is false. This could happen when WebRender was fallbacked to
// compositor.

Просмотреть файл

@ -0,0 +1,29 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: sw=2 ts=8 et :
*/
/* 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 protocol PWebGL;
namespace mozilla {
namespace gfx {
/**
* The PCanvasManager protocol is the top-level protocol between the main and
* worker threads in the content process, and the renderer thread in the
* compositor process. This protocol should be used to create accelerated
* canvas instances.
*/
[RefCounted] sync protocol PCanvasManager
{
manages PWebGL;
parent:
// Actor that represents one WebGL context.
async PWebGL();
};
} // gfx
} // mozilla

Просмотреть файл

@ -10,6 +10,8 @@ with Files("**"):
EXPORTS.mozilla += ["D3DMessageUtils.h", "GfxMessageUtils.h"]
EXPORTS.mozilla.gfx += [
"CanvasManagerChild.h",
"CanvasManagerParent.h",
"CrossProcessPaint.h",
"GPUChild.h",
"GPUParent.h",
@ -34,6 +36,8 @@ EXPORTS.mozilla.widget += [
]
UNIFIED_SOURCES += [
"CanvasManagerChild.cpp",
"CanvasManagerParent.cpp",
"CompositorSession.cpp",
"CompositorWidgetVsyncObserver.cpp",
"CrossProcessPaint.cpp",
@ -55,6 +59,7 @@ SOURCES += [
IPDL_SOURCES = [
"GraphicsMessages.ipdlh",
"PCanvasManager.ipdl",
"PVsyncBridge.ipdl",
]
@ -65,6 +70,7 @@ PREPROCESSED_IPDL_SOURCES += [
LOCAL_INCLUDES += [
"/dom/ipc",
"/gfx/cairo/cairo/src",
"/ipc/glue",
"/toolkit/crashreporter",
"/xpcom/threads",
]

Просмотреть файл

@ -23,6 +23,7 @@ namespace layers {
using gfx::GPUProcessManager;
StaticRefPtr<CompositorManagerChild> CompositorManagerChild::sInstance;
Atomic<base::ProcessId> CompositorManagerChild::sOtherPid(0);
/* static */
bool CompositorManagerChild::IsInitialized(uint64_t aProcessToken) {
@ -51,6 +52,7 @@ void CompositorManagerChild::InitSameProcess(uint32_t aNamespace,
parent->BindComplete(/* aIsRoot */ true);
sInstance = std::move(child);
sOtherPid = sInstance->OtherPid();
}
/* static */
@ -64,6 +66,7 @@ bool CompositorManagerChild::Init(Endpoint<PCompositorManagerChild>&& aEndpoint,
sInstance = new CompositorManagerChild(std::move(aEndpoint), aProcessToken,
aNamespace);
sOtherPid = sInstance->OtherPid();
return sInstance->CanSend();
}
@ -78,6 +81,7 @@ void CompositorManagerChild::Shutdown() {
sInstance->Close();
sInstance = nullptr;
sOtherPid = 0;
}
/* static */
@ -89,6 +93,7 @@ void CompositorManagerChild::OnGPUProcessLost(uint64_t aProcessToken) {
// yet to be. As such, we want to pre-emptively set mCanSend to false.
if (sInstance && sInstance->mProcessToken == aProcessToken) {
sInstance->mCanSend = false;
sOtherPid = 0;
}
}

Просмотреть файл

@ -7,8 +7,9 @@
#ifndef MOZILLA_GFX_COMPOSITORMANAGERCHILD_H
#define MOZILLA_GFX_COMPOSITORMANAGERCHILD_H
#include <stddef.h> // for size_t
#include <stdint.h> // for uint32_t, uint64_t
#include <stddef.h> // for size_t
#include <stdint.h> // for uint32_t, uint64_t
#include "mozilla/Atomics.h"
#include "mozilla/Attributes.h" // for override
#include "mozilla/RefPtr.h" // for already_AddRefed
#include "mozilla/StaticPtr.h" // for StaticRefPtr
@ -49,6 +50,9 @@ class CompositorManagerChild : public PCompositorManagerChild {
return sInstance;
}
// Threadsafe way to get the compositor process ID.
static base::ProcessId GetOtherPid() { return sOtherPid; }
bool CanSend() const {
MOZ_ASSERT(NS_IsMainThread());
return mCanSend;
@ -85,6 +89,7 @@ class CompositorManagerChild : public PCompositorManagerChild {
private:
static StaticRefPtr<CompositorManagerChild> sInstance;
static Atomic<base::ProcessId> sOtherPid;
CompositorManagerChild(CompositorManagerParent* aParent,
uint64_t aProcessToken, uint32_t aNamespace);

Просмотреть файл

@ -6,6 +6,7 @@
#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"
@ -320,6 +321,12 @@ mozilla::ipc::IPCResult CompositorManagerParent::RecvReportMemory(
return IPC_OK();
}
mozilla::ipc::IPCResult CompositorManagerParent::RecvInitCanvasManager(
Endpoint<PCanvasManagerParent>&& aEndpoint) {
gfx::CanvasManagerParent::Init(std::move(aEndpoint));
return IPC_OK();
}
/* static */
void CompositorManagerParent::NotifyWebRenderError(wr::WebRenderError aError) {
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());

Просмотреть файл

@ -51,6 +51,9 @@ class CompositorManagerParent final : public PCompositorManagerParent {
mozilla::ipc::IPCResult RecvReportMemory(ReportMemoryResolver&&);
mozilla::ipc::IPCResult RecvInitCanvasManager(
Endpoint<PCanvasManagerParent>&&);
void BindComplete(bool aIsRoot);
void ActorDestroy(ActorDestroyReason aReason) override;

Просмотреть файл

@ -5,6 +5,7 @@
* 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 protocol PCanvasManager;
include protocol PCompositorBridge;
include LayersSurfaces;
include "mozilla/GfxMessageUtils.h";
@ -84,6 +85,8 @@ parent:
async ReportMemory() returns (MemoryReport aReport);
async InitCanvasManager(Endpoint<PCanvasManagerParent> endpoint);
child:
async NotifyWebRenderError(WebRenderError error);
};

Просмотреть файл

@ -19,6 +19,8 @@
#include "mozilla/gfx/gfxVars.h"
#include "mozilla/gfx/GPUProcessManager.h"
#include "mozilla/gfx/GraphicsMessages.h"
#include "mozilla/gfx/CanvasManagerChild.h"
#include "mozilla/gfx/CanvasManagerParent.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/StaticPrefs_accessibility.h"
#include "mozilla/StaticPrefs_apz.h"
@ -1305,6 +1307,7 @@ void gfxPlatform::ShutdownLayersIPC() {
if (XRE_IsContentProcess()) {
gfx::VRManagerChild::ShutDown();
gfx::CanvasManagerChild::Shutdown();
// cf bug 1215265.
if (StaticPrefs::layers_child_process_shutdown()) {
layers::CompositorManagerChild::Shutdown();
@ -1313,11 +1316,13 @@ void gfxPlatform::ShutdownLayersIPC() {
} else if (XRE_IsParentProcess()) {
gfx::VRManagerChild::ShutDown();
gfx::CanvasManagerChild::Shutdown();
layers::CompositorManagerChild::Shutdown();
layers::ImageBridgeChild::ShutDown();
// This has to happen after shutting down the child protocols.
layers::CompositorThreadHolder::Shutdown();
image::ImageMemoryReporter::ShutdownForWebRender();
gfx::CanvasManagerParent::Shutdown();
// There is a case that RenderThread exists when UseWebRender() is
// false. This could happen when WebRender was fallbacked to compositor.
if (wr::RenderThread::Get()) {

Просмотреть файл

@ -173,6 +173,15 @@ bool RenderThread::IsInRenderThread() {
return sRenderThread && sRenderThread->mThread == NS_GetCurrentThread();
}
// static
already_AddRefed<nsIThread> RenderThread::GetRenderThread() {
nsCOMPtr<nsIThread> thread;
if (sRenderThread) {
thread = sRenderThread->mThread;
}
return thread.forget();
}
void RenderThread::DoAccumulateMemoryReport(
MemoryReport aReport,
const RefPtr<MemoryReportPromise::Private>& aPromise) {

Просмотреть файл

@ -146,6 +146,9 @@ class RenderThread final {
/// Can be called from any thread.
static bool IsInRenderThread();
/// Can be called from any thread.
static already_AddRefed<nsIThread> GetRenderThread();
// Can be called from any thread. Dispatches an event to the Renderer thread
// to iterate over all Renderers, accumulates memory statistics, and resolves
// the return promise.