diff --git a/gfx/ipc/CanvasRenderThread.cpp b/gfx/ipc/CanvasRenderThread.cpp index 4e6285d7a48a..dd9b03e85253 100644 --- a/gfx/ipc/CanvasRenderThread.cpp +++ b/gfx/ipc/CanvasRenderThread.cpp @@ -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&& aThread, + nsCOMPtr&& 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 workers; + if (threadLimit > 0) { + workers = SharedThreadPool::Get("CanvasWorkers"_ns, threadLimit); + if (NS_WARN_IF(!workers)) { + return; + } + } + nsCOMPtr 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 oldWorkers = sCanvasRenderThread->mWorkers; nsCOMPtr 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 CanvasRenderThread::GetCanvasRenderThread() { nsCOMPtr thread; @@ -143,9 +200,31 @@ already_AddRefed CanvasRenderThread::GetCanvasRenderThread() { return thread.forget(); } -void CanvasRenderThread::PostRunnable(already_AddRefed aRunnable) { - nsCOMPtr runnable = aRunnable; - mThread->Dispatch(runnable.forget()); +/* static */ already_AddRefed 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 aRunnable) { + if (!sCanvasRenderThread) { + MOZ_DIAGNOSTIC_ASSERT(false, + "Dispatching after CanvasRenderThread shutdown!"); + return; + } + sCanvasRenderThread->mThread->Dispatch(std::move(aRunnable)); } } // namespace mozilla::gfx diff --git a/gfx/ipc/CanvasRenderThread.h b/gfx/ipc/CanvasRenderThread.h index 303f489e5334..1983630ca9c1 100644 --- a/gfx/ipc/CanvasRenderThread.h +++ b/gfx/ipc/CanvasRenderThread.h @@ -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 GetCanvasRenderThread(); + static already_AddRefed CreateTaskQueue(bool aPreferWorkers); + + static void Dispatch(already_AddRefed aRunnable); + private: - CanvasRenderThread(nsCOMPtr&& aThread, bool aCreatedThread); + CanvasRenderThread(nsCOMPtr&& aThread, + nsCOMPtr&& aWorkers, bool aCreatedThread); ~CanvasRenderThread(); - void PostRunnable(already_AddRefed aRunnable); - nsCOMPtr const mThread; + nsCOMPtr 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__ diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml index d5e45ef64f07..9e90d0be4bb4 100644 --- a/modules/libpref/init/StaticPrefList.yaml +++ b/modules/libpref/init/StaticPrefList.yaml @@ -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)