summaryrefslogtreecommitdiffstats
path: root/gfx/layers/PaintThread.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /gfx/layers/PaintThread.cpp
parentInitial commit. (diff)
downloadfirefox-upstream.tar.xz
firefox-upstream.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gfx/layers/PaintThread.cpp')
-rw-r--r--gfx/layers/PaintThread.cpp272
1 files changed, 272 insertions, 0 deletions
diff --git a/gfx/layers/PaintThread.cpp b/gfx/layers/PaintThread.cpp
new file mode 100644
index 0000000000..2c85236c84
--- /dev/null
+++ b/gfx/layers/PaintThread.cpp
@@ -0,0 +1,272 @@
+/* -*- 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 "PaintThread.h"
+
+#include <algorithm>
+
+#include "base/task.h"
+#include "gfxPlatform.h"
+#include "GeckoProfiler.h"
+#include "mozilla/layers/CompositorBridgeChild.h"
+#include "mozilla/layers/ShadowLayers.h"
+#include "mozilla/layers/SyncObject.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/StaticPrefs_layers.h"
+#include "mozilla/SharedThreadPool.h"
+#include "mozilla/SyncRunnable.h"
+#include "nsProxyRelease.h"
+#ifdef XP_MACOSX
+# include "nsCocoaFeatures.h"
+#endif
+#include "nsIThreadManager.h"
+#include "nsServiceManagerUtils.h"
+#include "prsystem.h"
+
+// Uncomment the following line to dispatch sync runnables when
+// painting so that rasterization happens synchronously from
+// the perspective of the main thread
+// #define OMTP_FORCE_SYNC
+
+namespace mozilla {
+namespace layers {
+
+using namespace gfx;
+
+void PaintTask::DropTextureClients() { mClients.Clear(); }
+
+StaticAutoPtr<PaintThread> PaintThread::sSingleton;
+StaticRefPtr<nsIThread> PaintThread::sThread;
+PlatformThreadId PaintThread::sThreadId;
+
+PaintThread::PaintThread() = default;
+
+void PaintThread::Release() {}
+
+void PaintThread::AddRef() {}
+
+/* static */
+int32_t PaintThread::CalculatePaintWorkerCount() {
+ int32_t cpuCores = PR_GetNumberOfProcessors();
+ int32_t workerCount = StaticPrefs::layers_omtp_paint_workers_AtStartup();
+
+ // If not manually specified, default to (cpuCores * 3) / 4, and clamp
+ // between 1 and 4. If a user wants more, they can manually specify it
+ if (workerCount < 1) {
+ workerCount = std::min(std::max((cpuCores * 3) / 4, 1), 4);
+ }
+
+ return workerCount;
+}
+
+/* static */
+void PaintThread::Start() {
+ PaintThread::sSingleton = new PaintThread();
+
+ if (!PaintThread::sSingleton->Init()) {
+ gfxCriticalNote << "Unable to start paint thread";
+ PaintThread::sSingleton = nullptr;
+ }
+}
+
+static uint32_t GetPaintThreadStackSize() {
+#ifndef XP_MACOSX
+ return nsIThreadManager::DEFAULT_STACK_SIZE;
+#else
+ // Workaround bug 1578075 by increasing the stack size of paint threads
+ if (nsCocoaFeatures::OnCatalinaOrLater()) {
+ static const uint32_t kCatalinaPaintThreadStackSize = 512 * 1024;
+ static_assert(
+ kCatalinaPaintThreadStackSize >= nsIThreadManager::DEFAULT_STACK_SIZE,
+ "update default stack size of paint "
+ "workers");
+ return kCatalinaPaintThreadStackSize;
+ }
+ return nsIThreadManager::DEFAULT_STACK_SIZE;
+#endif
+}
+
+bool PaintThread::Init() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ RefPtr<nsIThread> thread;
+ nsresult rv = NS_NewNamedThread("PaintThread", getter_AddRefs(thread),
+ nullptr, GetPaintThreadStackSize());
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+ sThread = thread;
+
+ // Only create paint workers for tiling if we are using tiling or could
+ // expect to dynamically switch to tiling in the future
+ if (gfxPlatform::GetPlatform()->UsesTiling()) {
+ InitPaintWorkers();
+ }
+
+ nsCOMPtr<nsIRunnable> paintInitTask = NewRunnableMethod(
+ "PaintThread::InitOnPaintThread", this, &PaintThread::InitOnPaintThread);
+ SyncRunnable::DispatchToThread(sThread, paintInitTask);
+ return true;
+}
+
+void PaintThread::InitOnPaintThread() {
+ MOZ_ASSERT(!NS_IsMainThread());
+ sThreadId = PlatformThread::CurrentId();
+}
+
+void PaintThread::InitPaintWorkers() {
+ MOZ_ASSERT(NS_IsMainThread());
+ int32_t count = PaintThread::CalculatePaintWorkerCount();
+ if (count != 1) {
+ mPaintWorkers = SharedThreadPool::Get("PaintWorker"_ns, count);
+ mPaintWorkers->SetThreadStackSize(GetPaintThreadStackSize());
+ }
+}
+
+void DestroyPaintThread(UniquePtr<PaintThread>&& pt) {
+ MOZ_ASSERT(PaintThread::IsOnPaintThread());
+ pt->ShutdownOnPaintThread();
+}
+
+/* static */
+void PaintThread::Shutdown() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ UniquePtr<PaintThread> pt(sSingleton.forget());
+ if (!pt) {
+ return;
+ }
+
+ sThread->Dispatch(NewRunnableFunction("DestroyPaintThreadRunnable",
+ DestroyPaintThread, std::move(pt)));
+ sThread->Shutdown();
+ sThread = nullptr;
+}
+
+void PaintThread::ShutdownOnPaintThread() { MOZ_ASSERT(IsOnPaintThread()); }
+
+/* static */
+PaintThread* PaintThread::Get() { return PaintThread::sSingleton.get(); }
+
+/* static */
+bool PaintThread::IsOnPaintThread() {
+ return sThreadId == PlatformThread::CurrentId();
+}
+
+bool PaintThread::IsOnPaintWorkerThread() {
+ return (mPaintWorkers && mPaintWorkers->IsOnCurrentThread()) ||
+ (sThreadId == PlatformThread::CurrentId());
+}
+
+void PaintThread::Dispatch(RefPtr<Runnable>& aRunnable) {
+#ifndef OMTP_FORCE_SYNC
+ sThread->Dispatch(aRunnable.forget());
+#else
+ SyncRunnable::DispatchToThread(sThread, aRunnable);
+#endif
+}
+
+void PaintThread::UpdateRenderMode() {
+ if (!!mPaintWorkers != gfxPlatform::GetPlatform()->UsesTiling()) {
+ if (mPaintWorkers) {
+ mPaintWorkers = nullptr;
+ } else {
+ InitPaintWorkers();
+ }
+ }
+}
+
+void PaintThread::QueuePaintTask(UniquePtr<PaintTask>&& aTask) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aTask);
+
+ if (StaticPrefs::layers_omtp_dump_capture() && aTask->mCapture) {
+ aTask->mCapture->Dump();
+ }
+
+ MOZ_RELEASE_ASSERT(aTask->mCapture->hasOneRef());
+
+ RefPtr<CompositorBridgeChild> cbc(CompositorBridgeChild::Get());
+ cbc->NotifyBeginAsyncPaint(aTask.get());
+
+ RefPtr<PaintThread> self = this;
+ RefPtr<Runnable> task =
+ NS_NewRunnableFunction("PaintThread::AsyncPaintTask",
+ [self, cbc, task = std::move(aTask)]() -> void {
+ self->AsyncPaintTask(cbc, task.get());
+ });
+
+ nsIEventTarget* paintThread =
+ mPaintWorkers ? static_cast<nsIEventTarget*>(mPaintWorkers.get())
+ : static_cast<nsIEventTarget*>(sThread.get());
+
+#ifndef OMTP_FORCE_SYNC
+ paintThread->Dispatch(task.forget());
+#else
+ SyncRunnable::DispatchToThread(paintThread, task);
+#endif
+}
+
+void PaintThread::AsyncPaintTask(CompositorBridgeChild* aBridge,
+ PaintTask* aTask) {
+ AUTO_PROFILER_LABEL("PaintThread::AsyncPaintTask", GRAPHICS);
+
+ MOZ_ASSERT(IsOnPaintWorkerThread());
+ MOZ_ASSERT(aTask);
+
+ gfx::DrawTargetCapture* capture = aTask->mCapture;
+ gfx::DrawTarget* target = aTask->mTarget;
+
+ if (target->IsValid()) {
+ // Do not replay to invalid targets. This can happen on device resets and
+ // the browser will ensure the graphics stack is reinitialized on the main
+ // thread.
+ target->DrawCapturedDT(capture, Matrix());
+ target->Flush();
+ }
+
+ if (StaticPrefs::layers_omtp_release_capture_on_main_thread()) {
+ // This should ensure the capture drawtarget, which may hold on to
+ // UnscaledFont objects, gets destroyed on the main thread (See bug
+ // 1404742). This assumes (unflushed) target DrawTargets do not themselves
+ // hold on to UnscaledFonts.
+ NS_ReleaseOnMainThread("PaintTask::DrawTargetCapture",
+ aTask->mCapture.forget());
+ }
+
+ if (aBridge->NotifyFinishedAsyncWorkerPaint(aTask)) {
+ AsyncEndLayerTransaction(aBridge);
+ }
+}
+
+void PaintThread::QueueEndLayerTransaction(SyncObjectClient* aSyncObject) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ RefPtr<CompositorBridgeChild> cbc(CompositorBridgeChild::Get());
+
+ if (cbc->NotifyBeginAsyncEndLayerTransaction(aSyncObject)) {
+ RefPtr<PaintThread> self = this;
+ RefPtr<Runnable> task = NS_NewRunnableFunction(
+ "PaintThread::AsyncEndLayerTransaction",
+ [self, cbc]() -> void { self->AsyncEndLayerTransaction(cbc); });
+
+#ifndef OMTP_FORCE_SYNC
+ sThread->Dispatch(task.forget());
+#else
+ SyncRunnable::DispatchToThread(sThread, task);
+#endif
+ }
+}
+
+void PaintThread::AsyncEndLayerTransaction(CompositorBridgeChild* aBridge) {
+ MOZ_ASSERT(IsOnPaintWorkerThread());
+
+ aBridge->NotifyFinishedAsyncEndLayerTransaction();
+}
+
+} // namespace layers
+} // namespace mozilla