Bug 1852145 - Part 1. Refactor CanvasRenderThread to represent a virtual thread. r=gfx-reviewers,lsalzman

Prior to this patch, CanvasRenderThread represents the concrete thread
we would spawn if gfxVars::UseCanvasRenderThread() returned true.
CanvasManagerParent was responsible for checking our state and deciding
between using the Compositor, Renderer and CanvasRender threads.

This patch makes the CanvasRenderThread class represent a virtual
thread. It will spawn a CanvasRender thread if necessary. Other classes
may use the abstraction to run on the correct thread without having to
duplicate the selection logic.

Differential Revision: https://phabricator.services.mozilla.com/D187719
This commit is contained in:
Andrew Osmond 2023-09-25 16:18:35 +00:00
Родитель 620833163a
Коммит f1d22fb2f1
7 изменённых файлов: 90 добавлений и 81 удалений

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

@ -13,7 +13,6 @@
#include "mozilla/ipc/Endpoint.h"
#include "mozilla/layers/CompositorThread.h"
#include "mozilla/webgpu/WebGPUParent.h"
#include "mozilla/webrender/RenderThread.h"
#include "nsIThread.h"
#include "nsThreadUtils.h"
@ -27,41 +26,21 @@ CanvasManagerParent::ManagerSet CanvasManagerParent::sManagers;
auto manager = MakeRefPtr<CanvasManagerParent>();
if (!gfxVars::SupportsThreadsafeGL()) {
nsCOMPtr<nsIThread> owningThread;
owningThread = wr::RenderThread::GetRenderThread();
MOZ_ASSERT(owningThread);
nsCOMPtr<nsIThread> owningThread =
gfx::CanvasRenderThread::GetCanvasRenderThread();
MOZ_ASSERT(owningThread);
owningThread->Dispatch(NewRunnableMethod<Endpoint<PCanvasManagerParent>&&>(
"CanvasManagerParent::Bind", manager, &CanvasManagerParent::Bind,
std::move(aEndpoint)));
} else if (gfxVars::UseCanvasRenderThread()) {
nsCOMPtr<nsIThread> owningThread;
owningThread = gfx::CanvasRenderThread::GetCanvasRenderThread();
MOZ_ASSERT(owningThread);
owningThread->Dispatch(NewRunnableMethod<Endpoint<PCanvasManagerParent>&&>(
"CanvasManagerParent::Bind", manager, &CanvasManagerParent::Bind,
std::move(aEndpoint)));
} else {
manager->Bind(std::move(aEndpoint));
}
owningThread->Dispatch(NewRunnableMethod<Endpoint<PCanvasManagerParent>&&>(
"CanvasManagerParent::Bind", manager, &CanvasManagerParent::Bind,
std::move(aEndpoint)));
}
/* static */ void CanvasManagerParent::Shutdown() {
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsISerialEventTarget> owningThread;
if (!gfxVars::SupportsThreadsafeGL()) {
owningThread = wr::RenderThread::GetRenderThread();
} else if (gfxVars::UseCanvasRenderThread()) {
owningThread = gfx::CanvasRenderThread::GetCanvasRenderThread();
} else {
owningThread = layers::CompositorThread();
}
if (!owningThread) {
return;
}
nsCOMPtr<nsIThread> owningThread =
gfx::CanvasRenderThread::GetCanvasRenderThread();
MOZ_ASSERT(owningThread);
NS_DispatchAndSpinEventLoopUntilComplete(
"CanvasManagerParent::Shutdown"_ns, owningThread,

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

@ -7,6 +7,10 @@
#include "CanvasRenderThread.h"
#include "mozilla/BackgroundHangMonitor.h"
#include "mozilla/gfx/CanvasManagerParent.h"
#include "mozilla/gfx/gfxVars.h"
#include "mozilla/layers/CompositorThread.h"
#include "mozilla/webrender/RenderThread.h"
#include "transport/runnable_utils.h"
namespace mozilla::gfx {
@ -17,13 +21,11 @@ static mozilla::BackgroundHangMonitor* sBackgroundHangMonitor;
static bool sCanvasRenderThreadEverStarted = false;
#endif
CanvasRenderThread::CanvasRenderThread(RefPtr<nsIThread> aThread)
: mThread(std::move(aThread)) {}
CanvasRenderThread::CanvasRenderThread(nsCOMPtr<nsIThread>&& aThread,
bool aCreatedThread)
: mThread(std::move(aThread)), mCreatedThread(aCreatedThread) {}
CanvasRenderThread::~CanvasRenderThread() {}
// static
CanvasRenderThread* CanvasRenderThread::Get() { return sCanvasRenderThread; }
CanvasRenderThread::~CanvasRenderThread() = default;
// static
void CanvasRenderThread::Start() {
@ -37,6 +39,21 @@ void CanvasRenderThread::Start() {
sCanvasRenderThreadEverStarted = true;
#endif
nsCOMPtr<nsIThread> thread;
if (!gfxVars::SupportsThreadsafeGL()) {
thread = wr::RenderThread::GetRenderThread();
MOZ_ASSERT(thread);
} else if (!gfxVars::UseCanvasRenderThread()) {
thread = layers::CompositorThread();
MOZ_ASSERT(thread);
}
if (thread) {
sCanvasRenderThread =
new CanvasRenderThread(std::move(thread), /* aCreatedThread */ false);
return;
}
// This is 4M, which is higher than the default 256K.
// Increased with bug 1753349 to accommodate the `chromium/5359` branch of
// ANGLE, which has large peak stack usage for some pathological shader
@ -51,7 +68,6 @@ void CanvasRenderThread::Start() {
const uint32_t stackSize =
nsIThreadManager::DEFAULT_STACK_SIZE ? 4096 << 10 : 0;
RefPtr<nsIThread> thread;
nsresult rv = NS_NewNamedThread(
"CanvasRenderer", getter_AddRefs(thread),
NS_NewRunnableFunction(
@ -75,26 +91,41 @@ void CanvasRenderThread::Start() {
}),
{.stackSize = stackSize});
if (NS_FAILED(rv)) {
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
sCanvasRenderThread = new CanvasRenderThread(thread);
sCanvasRenderThread =
new CanvasRenderThread(std::move(thread), /* aCreatedThread */ true);
}
// static
void CanvasRenderThread::ShutDown() {
void CanvasRenderThread::Shutdown() {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(sCanvasRenderThread);
// It is possible we never initialized this thread in the parent process,
// because we used the GPU process instead.
if (!sCanvasRenderThread) {
MOZ_ASSERT(XRE_IsParentProcess());
return;
}
CanvasManagerParent::Shutdown();
// Null out sCanvasRenderThread before we enter synchronous Shutdown,
// from here on we are to be considered shut down for our consumers.
nsCOMPtr<nsIThread> oldThread = sCanvasRenderThread->GetCanvasRenderThread();
nsCOMPtr<nsIThread> oldThread;
if (sCanvasRenderThread->mCreatedThread) {
oldThread = sCanvasRenderThread->GetCanvasRenderThread();
MOZ_ASSERT(oldThread);
}
sCanvasRenderThread = nullptr;
// We do a synchronous shutdown here while spinning the MT event loop.
MOZ_ASSERT(oldThread);
oldThread->Shutdown();
// We do a synchronous shutdown here while spinning the MT event loop, but
// only if we created a dedicated CanvasRender thread.
if (oldThread) {
oldThread->Shutdown();
}
}
// static

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

@ -13,22 +13,28 @@
#include "nsISupportsImpl.h"
#include "nsThread.h"
class nsIRunnable;
namespace mozilla::gfx {
/**
* This class represents the virtual thread for canvas rendering. Depending on
* platform requirements and user configuration, canvas rendering may happen on
* the Compositor thread, Render thread or the CanvasRender thread.
*/
class CanvasRenderThread final {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_DELETE_ON_MAIN_THREAD(
CanvasRenderThread)
public:
/// Can be called from any thread, may return nullptr late in shutdown.
static CanvasRenderThread* Get();
/// Can only be called from the main thread, expected to be called at most
/// once during a process' lifetime.
/// once during a process' lifetime. Must be called after the Compositor and
/// Render threads are initialized.
static void Start();
/// Can only be called from the main thread.
static void ShutDown();
/// Can only be called from the main thread. Must be called before the
/// Compositor and Render threads are shutdown.
static void Shutdown();
/// Can be called from any thread.
static bool IsInCanvasRenderThread();
@ -37,12 +43,16 @@ class CanvasRenderThread final {
static already_AddRefed<nsIThread> GetCanvasRenderThread();
private:
explicit CanvasRenderThread(RefPtr<nsIThread> aThread);
CanvasRenderThread(nsCOMPtr<nsIThread>&& aThread, bool aCreatedThread);
~CanvasRenderThread();
void PostRunnable(already_AddRefed<nsIRunnable> aRunnable);
RefPtr<nsIThread> const mThread;
nsCOMPtr<nsIThread> const mThread;
// True if mThread points to CanvasRender thread, false if mThread points to
// Compositor/Render thread.
bool mCreatedThread;
};
} // namespace mozilla::gfx

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

@ -14,7 +14,6 @@
#include "GPUProcessManager.h"
#include "gfxGradientCache.h"
#include "GfxInfoBase.h"
#include "CanvasManagerParent.h"
#include "VRGPUChild.h"
#include "VRManager.h"
#include "VRManagerParent.h"
@ -378,10 +377,8 @@ mozilla::ipc::IPCResult GPUParent::RecvInit(
#endif
// Make sure to do this *after* we update gfxVars above.
if (gfxVars::UseCanvasRenderThread()) {
gfx::CanvasRenderThread::Start();
}
wr::RenderThread::Start(aWrNamespace);
gfx::CanvasRenderThread::Start();
image::ImageMemoryReporter::InitForWebRender();
VRManager::ManagerInit();
@ -700,9 +697,10 @@ void GPUParent::ActorDestroy(ActorDestroyReason aWhy) {
self->mVsyncBridge = nullptr;
}
VideoBridgeParent::Shutdown();
// This could be running on either the Compositor or the Renderer
// thread.
CanvasManagerParent::Shutdown();
// This could be running on either the Compositor thread, the Renderer
// thread, or the dedicated CanvasRender thread, so we need to shutdown
// before the former two.
CanvasRenderThread::Shutdown();
CompositorThreadHolder::Shutdown();
RemoteTextureMap::Shutdown();
// There is a case that RenderThread exists when gfxVars::UseWebRender()
@ -711,9 +709,6 @@ void GPUParent::ActorDestroy(ActorDestroyReason aWhy) {
if (wr::RenderThread::Get()) {
wr::RenderThread::ShutDown();
}
if (gfx::CanvasRenderThread::Get()) {
gfx::CanvasRenderThread::ShutDown();
}
#ifdef XP_WIN
if (widget::WinCompositorWindowThread::Get()) {
widget::WinCompositorWindowThread::ShutDown();

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

@ -28,7 +28,7 @@ static Atomic<bool> sFinishedCompositorShutDown(false);
static mozilla::BackgroundHangMonitor* sBackgroundHangMonitor;
static ProfilerThreadId sProfilerThreadId;
nsISerialEventTarget* CompositorThread() {
nsIThread* CompositorThread() {
return sCompositorThreadHolder
? sCompositorThreadHolder->GetCompositorThread()
: nullptr;

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

@ -13,7 +13,6 @@ namespace mozilla::baseprofiler {
class BaseProfilerThreadId;
}
using ProfilerThreadId = mozilla::baseprofiler::BaseProfilerThreadId;
class nsISerialEventTarget;
class nsIThread;
namespace mozilla {
@ -26,9 +25,7 @@ class CompositorThreadHolder final {
public:
CompositorThreadHolder();
nsISerialEventTarget* GetCompositorThread() const {
return mCompositorThread;
}
nsIThread* GetCompositorThread() const { return mCompositorThread; }
static CompositorThreadHolder* GetSingleton();
@ -61,7 +58,7 @@ class CompositorThreadHolder final {
friend class CompositorBridgeParent;
};
nsISerialEventTarget* CompositorThread();
nsIThread* CompositorThread();
} // namespace layers
} // namespace mozilla

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

@ -22,7 +22,6 @@
#include "mozilla/gfx/GPUProcessManager.h"
#include "mozilla/gfx/GraphicsMessages.h"
#include "mozilla/gfx/CanvasManagerChild.h"
#include "mozilla/gfx/CanvasManagerParent.h"
#include "mozilla/gfx/CanvasRenderThread.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/DebugOnly.h"
@ -1309,14 +1308,15 @@ void gfxPlatform::InitLayersIPC() {
#endif
if (!gfxConfig::IsEnabled(Feature::GPU_PROCESS)) {
RemoteTextureMap::Init();
if (gfxVars::UseCanvasRenderThread()) {
gfx::CanvasRenderThread::Start();
}
wr::RenderThread::Start(GPUProcessManager::Get()->AllocateNamespace());
image::ImageMemoryReporter::InitForWebRender();
}
layers::CompositorThreadHolder::Start();
if (!gfxConfig::IsEnabled(Feature::GPU_PROCESS)) {
gfx::CanvasRenderThread::Start();
}
}
}
@ -1343,8 +1343,10 @@ void gfxPlatform::ShutdownLayersIPC() {
gfx::CanvasManagerChild::Shutdown();
layers::CompositorManagerChild::Shutdown();
layers::ImageBridgeChild::ShutDown();
// This could be running on either the Compositor or the Renderer thread.
gfx::CanvasManagerParent::Shutdown();
// This could be running on either the Compositor thread, the Renderer
// thread, or the dedicated CanvasRender thread, so we need to shutdown
// before the former two.
gfx::CanvasRenderThread::Shutdown();
// This has to happen after shutting down the child protocols.
layers::CompositorThreadHolder::Shutdown();
RemoteTextureMap::Shutdown();
@ -1363,9 +1365,6 @@ void gfxPlatform::ShutdownLayersIPC() {
nsDependentCString(
StaticPrefs::GetPrefName_gfx_webrender_blob_tile_size()));
}
if (gfx::CanvasRenderThread::Get()) {
gfx::CanvasRenderThread::ShutDown();
}
#if defined(XP_WIN)
widget::WinWindowOcclusionTracker::ShutDown();
#endif
@ -3851,12 +3850,10 @@ void gfxPlatform::DisableGPUProcess() {
}
RemoteTextureMap::Init();
if (gfxVars::UseCanvasRenderThread()) {
gfx::CanvasRenderThread::Start();
}
// We need to initialize the parent process to prepare for WebRender if we
// did not end up disabling it, despite losing the GPU process.
wr::RenderThread::Start(GPUProcessManager::Get()->AllocateNamespace());
gfx::CanvasRenderThread::Start();
image::ImageMemoryReporter::InitForWebRender();
}