CanvasRenderThread.cpp (6593B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "CanvasRenderThread.h" 8 9 #include "mozilla/BackgroundHangMonitor.h" 10 #include "mozilla/SharedThreadPool.h" 11 #include "mozilla/StaticPrefs_gfx.h" 12 #include "mozilla/gfx/CanvasManagerParent.h" 13 #include "mozilla/gfx/gfxVars.h" 14 #include "mozilla/layers/CanvasTranslator.h" 15 #include "mozilla/layers/CompositorThread.h" 16 #include "mozilla/webrender/RenderThread.h" 17 #include "nsThread.h" 18 #include "prsystem.h" 19 #include "transport/runnable_utils.h" 20 21 bool NS_IsInCanvasThreadOrWorker() { 22 return mozilla::gfx::CanvasRenderThread::IsInCanvasRenderOrWorkerThread(); 23 } 24 25 namespace mozilla::gfx { 26 27 static StaticRefPtr<CanvasRenderThread> sCanvasRenderThread; 28 static mozilla::BackgroundHangMonitor* sBackgroundHangMonitor; 29 #ifdef DEBUG 30 static bool sCanvasRenderThreadEverStarted = false; 31 #endif 32 33 CanvasRenderThread::CanvasRenderThread(nsCOMPtr<nsIThread>&& aThread, 34 bool aCreatedThread) 35 : mMutex("CanvasRenderThread::mMutex"), 36 mThread(std::move(aThread)), 37 mCreatedThread(aCreatedThread) {} 38 39 CanvasRenderThread::~CanvasRenderThread() = default; 40 41 // static 42 void CanvasRenderThread::Start() { 43 MOZ_ASSERT(NS_IsMainThread()); 44 MOZ_ASSERT(!sCanvasRenderThread); 45 46 #ifdef DEBUG 47 // Check to ensure nobody will try to ever start us more than once during 48 // the process' lifetime (in particular after Stop). 49 MOZ_ASSERT(!sCanvasRenderThreadEverStarted); 50 sCanvasRenderThreadEverStarted = true; 51 #endif 52 53 nsCOMPtr<nsIThread> thread; 54 if (!gfxVars::SupportsThreadsafeGL()) { 55 thread = wr::RenderThread::GetRenderThread(); 56 MOZ_ASSERT(thread); 57 } else if (!gfxVars::UseCanvasRenderThread()) { 58 thread = layers::CompositorThread(); 59 MOZ_ASSERT(thread); 60 } 61 62 if (thread) { 63 sCanvasRenderThread = 64 new CanvasRenderThread(std::move(thread), /* aCreatedThread */ false); 65 return; 66 } 67 68 // This is 4M, which is higher than the default 256K. 69 // Increased with bug 1753349 to accommodate the `chromium/5359` branch of 70 // ANGLE, which has large peak stack usage for some pathological shader 71 // compilations. 72 // 73 // Previously increased to 512K to accommodate Mesa in bug 1753340. 74 // 75 // Previously increased to 320K to avoid a stack overflow in the 76 // Intel Vulkan driver initialization in bug 1716120. 77 // 78 // Note: we only override it if it's limited already. 79 const uint32_t stackSize = 80 nsIThreadManager::DEFAULT_STACK_SIZE ? 4096 << 10 : 0; 81 82 nsresult rv = NS_NewNamedThread( 83 "CanvasRenderer", getter_AddRefs(thread), 84 NS_NewRunnableFunction( 85 "CanvasRender::BackgroundHangSetup", 86 []() { 87 sBackgroundHangMonitor = new mozilla::BackgroundHangMonitor( 88 "CanvasRendererBHM", 89 /* Timeout values are powers-of-two to enable us get better 90 data. 128ms is chosen for transient hangs because 8Hz should 91 be the minimally acceptable goal for Compositor 92 responsiveness (normal goal is 60Hz). */ 93 128, 94 /* 2048ms is chosen for permanent hangs because it's longer than 95 * most Compositor hangs seen in the wild, but is short enough 96 * to not miss getting native hang stacks. */ 97 2048); 98 nsCOMPtr<nsIThread> thread = NS_GetCurrentThread(); 99 nsThread* nsthread = static_cast<nsThread*>(thread.get()); 100 nsthread->SetUseHangMonitor(true); 101 nsthread->SetPriority(nsISupportsPriority::PRIORITY_HIGH); 102 }), 103 {.stackSize = stackSize}); 104 105 if (NS_WARN_IF(NS_FAILED(rv))) { 106 return; 107 } 108 109 sCanvasRenderThread = 110 new CanvasRenderThread(std::move(thread), /* aCreatedThread */ true); 111 } 112 113 // static 114 void CanvasRenderThread::Shutdown() { 115 MOZ_ASSERT(NS_IsMainThread()); 116 117 // It is possible we never initialized this thread in the parent process, 118 // because we used the GPU process instead. 119 if (!sCanvasRenderThread) { 120 MOZ_ASSERT(XRE_IsParentProcess()); 121 return; 122 } 123 124 // This closes all of the IPDL actors with possibly active task queues. 125 CanvasManagerParent::Shutdown(); 126 127 // Queue any remaining global cleanup for CanvasTranslator 128 layers::CanvasTranslator::Shutdown(); 129 130 bool createdThread = sCanvasRenderThread->mCreatedThread; 131 nsCOMPtr<nsIThread> oldThread = sCanvasRenderThread->GetCanvasRenderThread(); 132 133 // Ensure that we flush the CanvasRenderThread event queue before clearing our 134 // singleton. 135 NS_DispatchAndSpinEventLoopUntilComplete( 136 "CanvasRenderThread::Shutdown"_ns, oldThread, 137 NS_NewRunnableFunction("CanvasRenderThread::Shutdown", []() -> void {})); 138 139 // Null out sCanvasRenderThread before we enter synchronous Shutdown, 140 // from here on we are to be considered shut down for our consumers. 141 sCanvasRenderThread = nullptr; 142 143 // We do a synchronous shutdown here while spinning the MT event loop, but 144 // only if we created a dedicated CanvasRender thread. 145 if (createdThread) { 146 oldThread->Shutdown(); 147 } 148 } 149 150 // static 151 bool CanvasRenderThread::IsInCanvasRenderThread() { 152 return sCanvasRenderThread && 153 sCanvasRenderThread->mThread == NS_GetCurrentThread(); 154 } 155 156 /* static */ bool CanvasRenderThread::IsInCanvasWorkerThread() { 157 // It is possible there are no worker threads, and the worker is the same as 158 // the CanvasRenderThread itself. 159 return sCanvasRenderThread && 160 sCanvasRenderThread->mThread == NS_GetCurrentThread(); 161 } 162 163 /* static */ bool CanvasRenderThread::IsInCanvasRenderOrWorkerThread() { 164 // It is possible there are no worker threads, and the worker is the same as 165 // the CanvasRenderThread itself. 166 return sCanvasRenderThread && 167 sCanvasRenderThread->mThread == NS_GetCurrentThread(); 168 } 169 170 // static 171 already_AddRefed<nsIThread> CanvasRenderThread::GetCanvasRenderThread() { 172 nsCOMPtr<nsIThread> thread; 173 if (sCanvasRenderThread) { 174 thread = sCanvasRenderThread->mThread; 175 } 176 return thread.forget(); 177 } 178 179 /* static */ void CanvasRenderThread::Dispatch( 180 already_AddRefed<nsIRunnable> aRunnable) { 181 if (!sCanvasRenderThread) { 182 MOZ_DIAGNOSTIC_CRASH("Dispatching after CanvasRenderThread shutdown!"); 183 return; 184 } 185 sCanvasRenderThread->mThread->Dispatch(std::move(aRunnable)); 186 } 187 188 } // namespace mozilla::gfx