VRServiceHost.cpp (9519B)
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 "VRServiceHost.h" 8 #include "VRGPUChild.h" 9 #include "VRPuppetCommandBuffer.h" 10 #include "mozilla/ClearOnShutdown.h" 11 #include "mozilla/gfx/GPUParent.h" 12 #include "service/VRService.h" 13 #include "VRManager.h" 14 15 namespace mozilla { 16 namespace gfx { 17 18 static StaticRefPtr<VRServiceHost> sVRServiceHostSingleton; 19 20 /* static */ 21 void VRServiceHost::Init(bool aEnableVRProcess) { 22 MOZ_ASSERT(NS_IsMainThread()); 23 24 if (sVRServiceHostSingleton == nullptr) { 25 sVRServiceHostSingleton = new VRServiceHost(aEnableVRProcess); 26 ClearOnShutdown(&sVRServiceHostSingleton); 27 } 28 } 29 30 /* static */ 31 VRServiceHost* VRServiceHost::Get() { 32 MOZ_ASSERT(sVRServiceHostSingleton != nullptr); 33 return sVRServiceHostSingleton; 34 } 35 36 VRServiceHost::VRServiceHost(bool aEnableVRProcess) 37 : mVRService(nullptr), 38 mVRProcessEnabled(aEnableVRProcess), 39 mVRProcessStarted(false), 40 mVRServiceReadyInVRProcess(false), 41 mVRServiceRequested(false) 42 43 { 44 MOZ_COUNT_CTOR(VRServiceHost); 45 } 46 47 VRServiceHost::~VRServiceHost() { 48 MOZ_ASSERT(NS_IsMainThread()); 49 MOZ_COUNT_DTOR(VRServiceHost); 50 } 51 52 void VRServiceHost::StartService() { 53 mVRServiceRequested = true; 54 if (mVRProcessEnabled) { 55 // VRService in the VR process 56 RefreshVRProcess(); 57 } else if (mVRService) { 58 // VRService in the GPU process if enabled, or 59 // the parent process if the GPU process is not enabled. 60 mVRService->Start(); 61 } 62 } 63 64 void VRServiceHost::StopService() { 65 mVRServiceRequested = false; 66 if (mVRProcessEnabled) { 67 // VRService in the VR process 68 RefreshVRProcess(); 69 } else if (mVRService) { 70 // VRService in the GPU process if enabled, or 71 // the parent process if the GPU process is not enabled. 72 mVRService->Stop(); 73 } 74 } 75 76 void VRServiceHost::Shutdown() { 77 PuppetReset(); 78 StopService(); 79 mVRService = nullptr; 80 } 81 82 void VRServiceHost::Refresh() { 83 if (mVRService) { 84 mVRService->Refresh(); 85 } 86 } 87 88 void VRServiceHost::CreateService(volatile VRExternalShmem* aShmem) { 89 MOZ_ASSERT(!mVRProcessEnabled); 90 mVRService = VRService::Create(aShmem); 91 } 92 93 bool VRServiceHost::NeedVRProcess() { 94 if (!mVRProcessEnabled) { 95 return false; 96 } 97 return mVRServiceRequested; 98 } 99 100 void VRServiceHost::RefreshVRProcess() { 101 // Start or stop the VR process if needed 102 if (NeedVRProcess()) { 103 if (!mVRProcessStarted) { 104 CreateVRProcess(); 105 } 106 } else { 107 if (mVRProcessStarted) { 108 ShutdownVRProcess(); 109 } 110 } 111 } 112 113 void VRServiceHost::CreateVRProcess() { 114 // This is only allowed to run in the main thread of the GPU process 115 if (!XRE_IsGPUProcess()) { 116 return; 117 } 118 // Forward this to the main thread if not already there 119 if (!NS_IsMainThread()) { 120 RefPtr<Runnable> task = NS_NewRunnableFunction( 121 "VRServiceHost::CreateVRProcess", 122 []() -> void { VRServiceHost::Get()->CreateVRProcess(); }); 123 NS_DispatchToMainThread(task.forget()); 124 return; 125 } 126 if (mVRProcessStarted) { 127 return; 128 } 129 130 mVRProcessStarted = true; 131 // Using PGPU channel to tell the main process 132 // to create the VR process. 133 gfx::GPUParent* gpu = GPUParent::GetSingleton(); 134 MOZ_ASSERT(gpu); 135 (void)gpu->SendCreateVRProcess(); 136 } 137 138 void VRServiceHost::NotifyVRProcessStarted() { 139 MOZ_ASSERT(NS_IsMainThread()); 140 MOZ_ASSERT(mVRProcessEnabled); 141 if (!mVRProcessStarted) { 142 // We have received this after the VR process 143 // has been stopped; the VR service is no 144 // longer running in the VR process. 145 return; 146 } 147 148 if (!VRGPUChild::IsCreated()) { 149 return; 150 } 151 VRGPUChild* vrGPUChild = VRGPUChild::Get(); 152 153 // The VR service has started in the VR process 154 // If there were pending puppet commands, we 155 // can send them now. 156 // This must occur before the VRService 157 // is started so the buffer can be seen 158 // by VRPuppetSession::Initialize(). 159 if (!mPuppetPendingCommands.IsEmpty()) { 160 vrGPUChild->SendPuppetSubmit(mPuppetPendingCommands); 161 mPuppetPendingCommands.Clear(); 162 } 163 164 vrGPUChild->SendStartVRService(); 165 mVRServiceReadyInVRProcess = true; 166 } 167 168 void VRServiceHost::ShutdownVRProcess() { 169 // This is only allowed to run in the main thread of the GPU process 170 if (!XRE_IsGPUProcess()) { 171 return; 172 } 173 // Forward this to the main thread if not already there 174 if (!NS_IsMainThread()) { 175 RefPtr<Runnable> task = NS_NewRunnableFunction( 176 "VRServiceHost::ShutdownVRProcess", 177 []() -> void { VRServiceHost::Get()->ShutdownVRProcess(); }); 178 NS_DispatchToMainThread(task.forget()); 179 return; 180 } 181 if (VRGPUChild::IsCreated()) { 182 VRGPUChild* vrGPUChild = VRGPUChild::Get(); 183 vrGPUChild->SendStopVRService(); 184 if (!vrGPUChild->IsClosed()) { 185 vrGPUChild->Close(); 186 } 187 VRGPUChild::Shutdown(); 188 } 189 if (!mVRProcessStarted) { 190 return; 191 } 192 // Using PGPU channel to tell the main process 193 // to shutdown VR process. 194 gfx::GPUParent* gpu = GPUParent::GetSingleton(); 195 MOZ_ASSERT(gpu); 196 (void)gpu->SendShutdownVRProcess(); 197 mVRProcessStarted = false; 198 mVRServiceReadyInVRProcess = false; 199 } 200 201 void VRServiceHost::PuppetSubmit(const nsTArray<uint64_t>& aBuffer) { 202 if (!mVRProcessEnabled) { 203 // Puppet is running in this process, submit commands directly 204 VRPuppetCommandBuffer::Get().Submit(aBuffer); 205 return; 206 } 207 208 // We need to send the buffer to the VR process 209 SendPuppetSubmitToVRProcess(aBuffer); 210 } 211 212 void VRServiceHost::SendPuppetSubmitToVRProcess( 213 const nsTArray<uint64_t>& aBuffer) { 214 // This is only allowed to run in the main thread of the GPU process 215 if (!XRE_IsGPUProcess()) { 216 return; 217 } 218 // Forward this to the main thread if not already there 219 if (!NS_IsMainThread()) { 220 RefPtr<Runnable> task = NS_NewRunnableFunction( 221 "VRServiceHost::SendPuppetSubmitToVRProcess", 222 [buffer{aBuffer.Clone()}]() -> void { 223 VRServiceHost::Get()->SendPuppetSubmitToVRProcess(buffer); 224 }); 225 NS_DispatchToMainThread(task.forget()); 226 return; 227 } 228 if (!mVRServiceReadyInVRProcess) { 229 // Queue the commands to be sent to the VR process once it is started 230 mPuppetPendingCommands.AppendElements(aBuffer); 231 return; 232 } 233 if (VRGPUChild::IsCreated()) { 234 VRGPUChild* vrGPUChild = VRGPUChild::Get(); 235 vrGPUChild->SendPuppetSubmit(aBuffer); 236 } 237 } 238 239 void VRServiceHost::PuppetReset() { 240 // If we're already into ShutdownFinal, the VRPuppetCommandBuffer instance 241 // will have been cleared, so don't try to access it after that point. 242 if (!mVRProcessEnabled && 243 !(NS_IsMainThread() && 244 PastShutdownPhase(ShutdownPhase::XPCOMShutdownFinal))) { 245 // Puppet is running in this process, tell it to reset directly. 246 VRPuppetCommandBuffer::Get().Reset(); 247 } 248 249 mPuppetPendingCommands.Clear(); 250 if (!mVRProcessStarted) { 251 // Process is stopped, so puppet state is already clear 252 return; 253 } 254 255 // We need to tell the VR process to reset the puppet 256 SendPuppetResetToVRProcess(); 257 } 258 259 void VRServiceHost::SendPuppetResetToVRProcess() { 260 // This is only allowed to run in the main thread of the GPU process 261 if (!XRE_IsGPUProcess()) { 262 return; 263 } 264 // Forward this to the main thread if not already there 265 if (!NS_IsMainThread()) { 266 RefPtr<Runnable> task = NS_NewRunnableFunction( 267 "VRServiceHost::SendPuppetResetToVRProcess", 268 []() -> void { VRServiceHost::Get()->SendPuppetResetToVRProcess(); }); 269 NS_DispatchToMainThread(task.forget()); 270 return; 271 } 272 if (VRGPUChild::IsCreated()) { 273 VRGPUChild* vrGPUChild = VRGPUChild::Get(); 274 vrGPUChild->SendPuppetReset(); 275 } 276 } 277 278 void VRServiceHost::CheckForPuppetCompletion() { 279 if (!mVRProcessEnabled) { 280 // Puppet is running in this process, ask it directly 281 if (VRPuppetCommandBuffer::Get().HasEnded()) { 282 VRManager::Get()->NotifyPuppetComplete(); 283 } 284 } 285 if (!mPuppetPendingCommands.IsEmpty()) { 286 // There are puppet commands pending to be sent to the 287 // VR process once its started, thus it has not ended. 288 return; 289 } 290 if (!mVRProcessStarted) { 291 // The VR process will be kept alive as long 292 // as there is a queue in the puppet command 293 // buffer. If the process is stopped, we can 294 // infer that the queue has been cleared and 295 // puppet state is reset. 296 VRManager::Get()->NotifyPuppetComplete(); 297 } 298 299 // We need to ask the VR process if the puppet has ended 300 SendPuppetCheckForCompletionToVRProcess(); 301 302 // VRGPUChild::RecvNotifyPuppetComplete will call 303 // VRManager::NotifyPuppetComplete if the puppet has completed 304 // in the VR Process. 305 } 306 307 void VRServiceHost::SendPuppetCheckForCompletionToVRProcess() { 308 // This is only allowed to run in the main thread of the GPU process 309 if (!XRE_IsGPUProcess()) { 310 return; 311 } 312 // Forward this to the main thread if not already there 313 if (!NS_IsMainThread()) { 314 RefPtr<Runnable> task = NS_NewRunnableFunction( 315 "VRServiceHost::SendPuppetCheckForCompletionToVRProcess", []() -> void { 316 VRServiceHost::Get()->SendPuppetCheckForCompletionToVRProcess(); 317 }); 318 NS_DispatchToMainThread(task.forget()); 319 return; 320 } 321 if (VRGPUChild::IsCreated()) { 322 VRGPUChild* vrGPUChild = VRGPUChild::Get(); 323 vrGPUChild->SendPuppetCheckForCompletion(); 324 } 325 } 326 327 } // namespace gfx 328 } // namespace mozilla