CompositorThread.cpp (7568B)
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 #include "CompositorThread.h" 7 8 #include "CompositorBridgeParent.h" 9 #include "gfxGradientCache.h" 10 #include "MainThreadUtils.h" 11 #include "VRManagerParent.h" 12 #include "mozilla/BackgroundHangMonitor.h" 13 #include "mozilla/SpinEventLoopUntil.h" 14 #include "mozilla/gfx/gfxVars.h" 15 #include "mozilla/layers/CompositorManagerParent.h" 16 #include "mozilla/layers/ImageBridgeParent.h" 17 #include "mozilla/layers/VideoBridgeParent.h" 18 #include "mozilla/media/MediaSystemResourceService.h" 19 #include "nsThread.h" 20 #include "nsThreadUtils.h" 21 22 namespace mozilla { 23 namespace layers { 24 25 static StaticRefPtr<CompositorThreadHolder> sCompositorThreadHolder; 26 static Atomic<bool> sFinishedCompositorShutDown(false); 27 static mozilla::BackgroundHangMonitor* sBackgroundHangMonitor; 28 static ProfilerThreadId sProfilerThreadId; 29 30 nsIThread* CompositorThread() { 31 return sCompositorThreadHolder 32 ? sCompositorThreadHolder->GetCompositorThread() 33 : nullptr; 34 } 35 36 CompositorThreadHolder* CompositorThreadHolder::GetSingleton() { 37 return sCompositorThreadHolder; 38 } 39 40 CompositorThreadHolder::CompositorThreadHolder() 41 : mCompositorThread(CreateCompositorThread()) { 42 MOZ_ASSERT(NS_IsMainThread()); 43 } 44 45 CompositorThreadHolder::~CompositorThreadHolder() { 46 sFinishedCompositorShutDown = true; 47 } 48 49 /* static */ already_AddRefed<nsIThread> 50 CompositorThreadHolder::CreateCompositorThread() { 51 MOZ_ASSERT(NS_IsMainThread()); 52 53 MOZ_ASSERT(!sCompositorThreadHolder, 54 "The compositor thread has already been started!"); 55 56 // When the CanvasRenderer thread is disabled, WebGL may be handled on this 57 // thread, requiring a bigger stack size. See: CanvasManagerParent::Init 58 // 59 // This is 4M, which is higher than the default 256K. 60 // Increased with bug 1753349 to accommodate the `chromium/5359` branch of 61 // ANGLE, which has large peak stack usage for some pathological shader 62 // compilations. 63 // 64 // Previously increased to 512K to accommodate Mesa in bug 1753340. 65 // 66 // Previously increased to 320K to avoid a stack overflow in the 67 // Intel Vulkan driver initialization in bug 1716120. 68 // 69 // Note: we only override it if it's limited already. 70 uint32_t stackSize = nsIThreadManager::DEFAULT_STACK_SIZE; 71 if (stackSize) { 72 stackSize = 73 std::max(stackSize, gfx::gfxVars::SupportsThreadsafeGL() && 74 !gfx::gfxVars::UseCanvasRenderThread() 75 ? 4096U << 10 76 : 512U << 10); 77 } 78 79 nsCOMPtr<nsIThread> compositorThread; 80 nsresult rv = NS_NewNamedThread( 81 "Compositor", getter_AddRefs(compositorThread), 82 NS_NewRunnableFunction( 83 "CompositorThreadHolder::CompositorThreadHolderSetup", 84 []() { 85 sProfilerThreadId = profiler_current_thread_id(); 86 sBackgroundHangMonitor = new mozilla::BackgroundHangMonitor( 87 "Compositor", 88 /* Timeout values are powers-of-two to enable us get better 89 data. 128ms is chosen for transient hangs because 8Hz should 90 be the minimally acceptable goal for Compositor 91 responsiveness (normal goal is 60Hz). */ 92 128, 93 /* 2048ms is chosen for permanent hangs because it's longer than 94 * most Compositor hangs seen in the wild, but is short enough 95 * to not miss getting native hang stacks. */ 96 2048); 97 nsCOMPtr<nsIThread> thread = NS_GetCurrentThread(); 98 static_cast<nsThread*>(thread.get())->SetUseHangMonitor(true); 99 }), 100 {.stackSize = stackSize}); 101 102 if (NS_FAILED(rv)) { 103 return nullptr; 104 } 105 106 ImageBridgeParent::Setup(); 107 108 return compositorThread.forget(); 109 } 110 111 void CompositorThreadHolder::Start() { 112 MOZ_ASSERT(NS_IsMainThread(), "Should be on the main Thread!"); 113 MOZ_ASSERT(!sCompositorThreadHolder, 114 "The compositor thread has already been started!"); 115 116 // We unset the holder instead of asserting because failing to start the 117 // compositor thread may not be a fatal error. As long as this succeeds in 118 // either the GPU process or the UI process, the user will have a usable 119 // browser. If we get neither, it will crash as soon as we try to post to the 120 // compositor thread for the first time. 121 sProfilerThreadId = ProfilerThreadId(); 122 sCompositorThreadHolder = new CompositorThreadHolder(); 123 if (!sCompositorThreadHolder->GetCompositorThread()) { 124 gfxCriticalNote << "Compositor thread not started (" 125 << XRE_IsParentProcess() << ")"; 126 sCompositorThreadHolder = nullptr; 127 } 128 } 129 130 void CompositorThreadHolder::Shutdown() { 131 MOZ_ASSERT(NS_IsMainThread(), "Should be on the main Thread!"); 132 if (!sCompositorThreadHolder) { 133 // We've already shutdown or never started. 134 return; 135 } 136 137 ImageBridgeParent::Shutdown(); 138 gfx::VRManagerParent::Shutdown(); 139 MediaSystemResourceService::Shutdown(); 140 CompositorManagerParent::Shutdown(); 141 gfx::gfxGradientCache::Shutdown(); 142 143 // Ensure there are no pending tasks that would cause an access to the 144 // thread's HangMonitor. APZ and Canvas can keep a reference to the compositor 145 // thread and may continue to dispatch tasks on it as the system shuts down. 146 CompositorThread()->Dispatch(NS_NewRunnableFunction( 147 "CompositorThreadHolder::Shutdown", 148 [compositorThreadHolder = 149 RefPtr<CompositorThreadHolder>(sCompositorThreadHolder), 150 backgroundHangMonitor = UniquePtr<mozilla::BackgroundHangMonitor>( 151 sBackgroundHangMonitor)]() { 152 VideoBridgeParent::UnregisterExternalImages(); 153 nsCOMPtr<nsIThread> thread = NS_GetCurrentThread(); 154 static_cast<nsThread*>(thread.get())->SetUseHangMonitor(false); 155 })); 156 157 sCompositorThreadHolder = nullptr; 158 sBackgroundHangMonitor = nullptr; 159 160 SpinEventLoopUntil("CompositorThreadHolder::Shutdown"_ns, [&]() { 161 bool finished = sFinishedCompositorShutDown; 162 return finished; 163 }); 164 165 // At this point, the CompositorThreadHolder instance will have been 166 // destroyed, but the compositor thread itself may still be running due to 167 // APZ/Canvas code holding a reference to the underlying 168 // nsIThread/nsISerialEventTarget. Any tasks scheduled to run on the 169 // compositor thread earlier in this function will have been run to 170 // completion. 171 CompositorBridgeParent::FinishShutdown(); 172 } 173 174 /* static */ 175 bool CompositorThreadHolder::IsInCompositorThread() { 176 if (!CompositorThread()) { 177 // We no longer have a pointer to the compositor thread, but we might 178 // still be running in the compositor thread. In we ever had a 179 // compositor thread, we logged the thread id in sProfilerThreadId, 180 // so we compare that value against the current thread id. 181 return (sProfilerThreadId == profiler_current_thread_id()); 182 } 183 bool in = false; 184 MOZ_ALWAYS_SUCCEEDS(CompositorThread()->IsOnCurrentThread(&in)); 185 return in; 186 } 187 188 /* static */ 189 ProfilerThreadId CompositorThreadHolder::GetThreadId() { 190 return sCompositorThreadHolder ? sProfilerThreadId : ProfilerThreadId(); 191 } 192 193 } // namespace layers 194 } // namespace mozilla 195 196 bool NS_IsInCompositorThread() { 197 return mozilla::layers::CompositorThreadHolder::IsInCompositorThread(); 198 }