Bug 1852145 - Part 2. Spawn worker threads in CanvasRenderThread. r=gfx-reviewers,lsalzman

In preparation for moving PCanvas to be instantiated from
PCanvasManager, and running its IPDL objects on the virtual thread
chosen by CanvasRenderThread, this patch adds the necessary plumbing for
utilizing worker threads for PCanvas rendering.

A future patch in the series will make use of it.

Differential Revision: https://phabricator.services.mozilla.com/D187720
This commit is contained in:
Andrew Osmond 2023-09-25 16:18:36 +00:00
Родитель f1d22fb2f1
Коммит 0a322737dd
3 изменённых файлов: 124 добавлений и 17 удалений

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

@ -7,10 +7,15 @@
#include "CanvasRenderThread.h"
#include "mozilla/BackgroundHangMonitor.h"
#include "mozilla/SharedThreadPool.h"
#include "mozilla/StaticPrefs_gfx.h"
#include "mozilla/TaskQueue.h"
#include "mozilla/gfx/CanvasManagerParent.h"
#include "mozilla/gfx/gfxVars.h"
#include "mozilla/layers/CompositorThread.h"
#include "mozilla/webrender/RenderThread.h"
#include "nsThread.h"
#include "prsystem.h"
#include "transport/runnable_utils.h"
namespace mozilla::gfx {
@ -22,8 +27,11 @@ static bool sCanvasRenderThreadEverStarted = false;
#endif
CanvasRenderThread::CanvasRenderThread(nsCOMPtr<nsIThread>&& aThread,
nsCOMPtr<nsIThreadPool>&& aWorkers,
bool aCreatedThread)
: mThread(std::move(aThread)), mCreatedThread(aCreatedThread) {}
: mThread(std::move(aThread)),
mWorkers(std::move(aWorkers)),
mCreatedThread(aCreatedThread) {}
CanvasRenderThread::~CanvasRenderThread() = default;
@ -39,6 +47,31 @@ void CanvasRenderThread::Start() {
sCanvasRenderThreadEverStarted = true;
#endif
int32_t threadPref =
StaticPrefs::gfx_canvas_remote_worker_threads_AtStartup();
uint32_t threadLimit;
if (threadPref < 0) {
// Given that the canvas workers are receiving instructions from
// content processes, it probably doesn't make sense to have more than
// half the number of processors doing canvas drawing. We set the
// lower limit to 2, so that even on single processor systems, if
// there is more than one window with canvas drawing, the OS can
// manage the load between them.
threadLimit = std::max(2, PR_GetNumberOfProcessors() / 2);
} else {
threadLimit = uint32_t(threadPref);
}
// We don't spawn any workers if the user set the limit to 0. Instead we will
// use the CanvasRenderThread virtual thread.
nsCOMPtr<nsIThreadPool> workers;
if (threadLimit > 0) {
workers = SharedThreadPool::Get("CanvasWorkers"_ns, threadLimit);
if (NS_WARN_IF(!workers)) {
return;
}
}
nsCOMPtr<nsIThread> thread;
if (!gfxVars::SupportsThreadsafeGL()) {
thread = wr::RenderThread::GetRenderThread();
@ -49,8 +82,8 @@ void CanvasRenderThread::Start() {
}
if (thread) {
sCanvasRenderThread =
new CanvasRenderThread(std::move(thread), /* aCreatedThread */ false);
sCanvasRenderThread = new CanvasRenderThread(
std::move(thread), std::move(workers), /* aCreatedThread */ false);
return;
}
@ -95,8 +128,8 @@ void CanvasRenderThread::Start() {
return;
}
sCanvasRenderThread =
new CanvasRenderThread(std::move(thread), /* aCreatedThread */ true);
sCanvasRenderThread = new CanvasRenderThread(
std::move(thread), std::move(workers), /* aCreatedThread */ true);
}
// static
@ -114,6 +147,7 @@ void CanvasRenderThread::Shutdown() {
// Null out sCanvasRenderThread before we enter synchronous Shutdown,
// from here on we are to be considered shut down for our consumers.
nsCOMPtr<nsIThreadPool> oldWorkers = sCanvasRenderThread->mWorkers;
nsCOMPtr<nsIThread> oldThread;
if (sCanvasRenderThread->mCreatedThread) {
oldThread = sCanvasRenderThread->GetCanvasRenderThread();
@ -121,6 +155,10 @@ void CanvasRenderThread::Shutdown() {
}
sCanvasRenderThread = nullptr;
if (oldWorkers) {
oldWorkers->Shutdown();
}
// We do a synchronous shutdown here while spinning the MT event loop, but
// only if we created a dedicated CanvasRender thread.
if (oldThread) {
@ -134,6 +172,25 @@ bool CanvasRenderThread::IsInCanvasRenderThread() {
sCanvasRenderThread->mThread == NS_GetCurrentThread();
}
/* static */ bool CanvasRenderThread::IsInCanvasWorkerThread() {
// It is possible there are no worker threads, and the worker is the same as
// the CanvasRenderThread itself.
return sCanvasRenderThread &&
((sCanvasRenderThread->mWorkers &&
sCanvasRenderThread->mWorkers->IsOnCurrentThread()) ||
(!sCanvasRenderThread->mWorkers &&
sCanvasRenderThread->mThread == NS_GetCurrentThread()));
}
/* static */ bool CanvasRenderThread::IsInCanvasRenderOrWorkerThread() {
// It is possible there are no worker threads, and the worker is the same as
// the CanvasRenderThread itself.
return sCanvasRenderThread &&
(sCanvasRenderThread->mThread == NS_GetCurrentThread() ||
(sCanvasRenderThread->mWorkers &&
sCanvasRenderThread->mWorkers->IsOnCurrentThread()));
}
// static
already_AddRefed<nsIThread> CanvasRenderThread::GetCanvasRenderThread() {
nsCOMPtr<nsIThread> thread;
@ -143,9 +200,31 @@ already_AddRefed<nsIThread> CanvasRenderThread::GetCanvasRenderThread() {
return thread.forget();
}
void CanvasRenderThread::PostRunnable(already_AddRefed<nsIRunnable> aRunnable) {
nsCOMPtr<nsIRunnable> runnable = aRunnable;
mThread->Dispatch(runnable.forget());
/* static */ already_AddRefed<TaskQueue> CanvasRenderThread::CreateTaskQueue(
bool aPreferWorkers) {
if (!sCanvasRenderThread) {
return nullptr;
}
if (!aPreferWorkers || !sCanvasRenderThread->mWorkers) {
return TaskQueue::Create(do_AddRef(sCanvasRenderThread->mThread),
"CanvasWorker")
.forget();
}
return TaskQueue::Create(do_AddRef(sCanvasRenderThread->mWorkers),
"CanvasWorker")
.forget();
}
/* static */ void CanvasRenderThread::Dispatch(
already_AddRefed<nsIRunnable> aRunnable) {
if (!sCanvasRenderThread) {
MOZ_DIAGNOSTIC_ASSERT(false,
"Dispatching after CanvasRenderThread shutdown!");
return;
}
sCanvasRenderThread->mThread->Dispatch(std::move(aRunnable));
}
} // namespace mozilla::gfx

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

@ -7,15 +7,18 @@
#ifndef _include_gfx_ipc_CanvasRenderThread_h__
#define _include_gfx_ipc_CanvasRenderThread_h__
#include "mozilla/DataMutex.h"
#include "mozilla/layers/SynchronousTask.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/AlreadyAddRefed.h"
#include "nsCOMPtr.h"
#include "nsISupportsImpl.h"
#include "nsThread.h"
class nsIRunnable;
class nsIThread;
class nsIThreadPool;
namespace mozilla::gfx {
namespace mozilla {
class TaskQueue;
namespace gfx {
/**
* This class represents the virtual thread for canvas rendering. Depending on
@ -39,22 +42,34 @@ class CanvasRenderThread final {
/// Can be called from any thread.
static bool IsInCanvasRenderThread();
/// Can be called from any thread.
static bool IsInCanvasWorkerThread();
/// Can be called from any thread.
static bool IsInCanvasRenderOrWorkerThread();
/// Can be called from any thread, may return nullptr late in shutdown.
static already_AddRefed<nsIThread> GetCanvasRenderThread();
static already_AddRefed<TaskQueue> CreateTaskQueue(bool aPreferWorkers);
static void Dispatch(already_AddRefed<nsIRunnable> aRunnable);
private:
CanvasRenderThread(nsCOMPtr<nsIThread>&& aThread, bool aCreatedThread);
CanvasRenderThread(nsCOMPtr<nsIThread>&& aThread,
nsCOMPtr<nsIThreadPool>&& aWorkers, bool aCreatedThread);
~CanvasRenderThread();
void PostRunnable(already_AddRefed<nsIRunnable> aRunnable);
nsCOMPtr<nsIThread> const mThread;
nsCOMPtr<nsIThreadPool> const mWorkers;
// True if mThread points to CanvasRender thread, false if mThread points to
// Compositor/Render thread.
bool mCreatedThread;
};
} // namespace mozilla::gfx
} // namespace gfx
} // namespace mozilla
#endif // _include_gfx_ipc_CanvasRenderThread_h__

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

@ -5666,6 +5666,19 @@
value: false
mirror: once
# How many worker threads spawned for remote canvas
# -1 - Calculate based on processor cores
# 0 - No worker threads spawned, will do work on CanvasRenderThread
# >0 - Create worker thread pool with given size
- name: gfx.canvas.remote.worker-threads
type: int32_t
#if defined(XP_WIN)
value: -1
#else
value: 0
#endif
mirror: once
- name: gfx.canvas.willreadfrequently.enabled
type: bool
#if defined(XP_WIN)