/* -*- 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 "VRServiceHost.h" #include "VRGPUChild.h" #include "VRPuppetCommandBuffer.h" #include "mozilla/ClearOnShutdown.h" #include "mozilla/gfx/GPUParent.h" #include "service/VRService.h" #include "VRManager.h" namespace mozilla { namespace gfx { static StaticRefPtr sVRServiceHostSingleton; /* static */ void VRServiceHost::Init(bool aEnableVRProcess) { MOZ_ASSERT(NS_IsMainThread()); if (sVRServiceHostSingleton == nullptr) { sVRServiceHostSingleton = new VRServiceHost(aEnableVRProcess); ClearOnShutdown(&sVRServiceHostSingleton); } } /* static */ VRServiceHost* VRServiceHost::Get() { MOZ_ASSERT(sVRServiceHostSingleton != nullptr); return sVRServiceHostSingleton; } VRServiceHost::VRServiceHost(bool aEnableVRProcess) : mVRService(nullptr), mVRProcessEnabled(aEnableVRProcess), mVRProcessStarted(false), mVRServiceReadyInVRProcess(false), mVRServiceRequested(false) { MOZ_COUNT_CTOR(VRServiceHost); } VRServiceHost::~VRServiceHost() { MOZ_ASSERT(NS_IsMainThread()); MOZ_COUNT_DTOR(VRServiceHost); } void VRServiceHost::StartService() { mVRServiceRequested = true; if (mVRProcessEnabled) { // VRService in the VR process RefreshVRProcess(); } else if (mVRService) { // VRService in the GPU process if enabled, or // the parent process if the GPU process is not enabled. mVRService->Start(); } } void VRServiceHost::StopService() { mVRServiceRequested = false; if (mVRProcessEnabled) { // VRService in the VR process RefreshVRProcess(); } else if (mVRService) { // VRService in the GPU process if enabled, or // the parent process if the GPU process is not enabled. mVRService->Stop(); } } void VRServiceHost::Shutdown() { PuppetReset(); StopService(); mVRService = nullptr; } void VRServiceHost::Refresh() { if (mVRService) { mVRService->Refresh(); } } void VRServiceHost::CreateService(volatile VRExternalShmem* aShmem) { MOZ_ASSERT(!mVRProcessEnabled); mVRService = VRService::Create(aShmem); } bool VRServiceHost::NeedVRProcess() { if (!mVRProcessEnabled) { return false; } return mVRServiceRequested; } void VRServiceHost::RefreshVRProcess() { // Start or stop the VR process if needed if (NeedVRProcess()) { if (!mVRProcessStarted) { CreateVRProcess(); } } else { if (mVRProcessStarted) { ShutdownVRProcess(); } } } void VRServiceHost::CreateVRProcess() { // This is only allowed to run in the main thread of the GPU process if (!XRE_IsGPUProcess()) { return; } // Forward this to the main thread if not already there if (!NS_IsMainThread()) { RefPtr task = NS_NewRunnableFunction( "VRServiceHost::CreateVRProcess", []() -> void { VRServiceHost::Get()->CreateVRProcess(); }); NS_DispatchToMainThread(task.forget()); return; } if (mVRProcessStarted) { return; } mVRProcessStarted = true; // Using PGPU channel to tell the main process // to create the VR process. gfx::GPUParent* gpu = GPUParent::GetSingleton(); MOZ_ASSERT(gpu); Unused << gpu->SendCreateVRProcess(); } void VRServiceHost::NotifyVRProcessStarted() { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(mVRProcessEnabled); if (!mVRProcessStarted) { // We have received this after the VR process // has been stopped; the VR service is no // longer running in the VR process. return; } if (!VRGPUChild::IsCreated()) { return; } VRGPUChild* vrGPUChild = VRGPUChild::Get(); // The VR service has started in the VR process // If there were pending puppet commands, we // can send them now. // This must occur before the VRService // is started so the buffer can be seen // by VRPuppetSession::Initialize(). if (!mPuppetPendingCommands.IsEmpty()) { vrGPUChild->SendPuppetSubmit(mPuppetPendingCommands); mPuppetPendingCommands.Clear(); } vrGPUChild->SendStartVRService(); mVRServiceReadyInVRProcess = true; } void VRServiceHost::ShutdownVRProcess() { // This is only allowed to run in the main thread of the GPU process if (!XRE_IsGPUProcess()) { return; } // Forward this to the main thread if not already there if (!NS_IsMainThread()) { RefPtr task = NS_NewRunnableFunction( "VRServiceHost::ShutdownVRProcess", []() -> void { VRServiceHost::Get()->ShutdownVRProcess(); }); NS_DispatchToMainThread(task.forget()); return; } if (VRGPUChild::IsCreated()) { VRGPUChild* vrGPUChild = VRGPUChild::Get(); vrGPUChild->SendStopVRService(); if (!vrGPUChild->IsClosed()) { vrGPUChild->Close(); } VRGPUChild::Shutdown(); } if (!mVRProcessStarted) { return; } // Using PGPU channel to tell the main process // to shutdown VR process. gfx::GPUParent* gpu = GPUParent::GetSingleton(); MOZ_ASSERT(gpu); Unused << gpu->SendShutdownVRProcess(); mVRProcessStarted = false; mVRServiceReadyInVRProcess = false; } void VRServiceHost::PuppetSubmit(const nsTArray& aBuffer) { if (!mVRProcessEnabled) { // Puppet is running in this process, submit commands directly VRPuppetCommandBuffer::Get().Submit(aBuffer); return; } // We need to send the buffer to the VR process SendPuppetSubmitToVRProcess(aBuffer); } void VRServiceHost::SendPuppetSubmitToVRProcess( const nsTArray& aBuffer) { // This is only allowed to run in the main thread of the GPU process if (!XRE_IsGPUProcess()) { return; } // Forward this to the main thread if not already there if (!NS_IsMainThread()) { RefPtr task = NS_NewRunnableFunction( "VRServiceHost::SendPuppetSubmitToVRProcess", [buffer{aBuffer.Clone()}]() -> void { VRServiceHost::Get()->SendPuppetSubmitToVRProcess(buffer); }); NS_DispatchToMainThread(task.forget()); return; } if (!mVRServiceReadyInVRProcess) { // Queue the commands to be sent to the VR process once it is started mPuppetPendingCommands.AppendElements(aBuffer); return; } if (VRGPUChild::IsCreated()) { VRGPUChild* vrGPUChild = VRGPUChild::Get(); vrGPUChild->SendPuppetSubmit(aBuffer); } } void VRServiceHost::PuppetReset() { // If we're already into ShutdownFinal, the VRPuppetCommandBuffer instance // will have been cleared, so don't try to access it after that point. if (!mVRProcessEnabled && !(NS_IsMainThread() && PastShutdownPhase(ShutdownPhase::ShutdownFinal))) { // Puppet is running in this process, tell it to reset directly. VRPuppetCommandBuffer::Get().Reset(); } mPuppetPendingCommands.Clear(); if (!mVRProcessStarted) { // Process is stopped, so puppet state is already clear return; } // We need to tell the VR process to reset the puppet SendPuppetResetToVRProcess(); } void VRServiceHost::SendPuppetResetToVRProcess() { // This is only allowed to run in the main thread of the GPU process if (!XRE_IsGPUProcess()) { return; } // Forward this to the main thread if not already there if (!NS_IsMainThread()) { RefPtr task = NS_NewRunnableFunction( "VRServiceHost::SendPuppetResetToVRProcess", []() -> void { VRServiceHost::Get()->SendPuppetResetToVRProcess(); }); NS_DispatchToMainThread(task.forget()); return; } if (VRGPUChild::IsCreated()) { VRGPUChild* vrGPUChild = VRGPUChild::Get(); vrGPUChild->SendPuppetReset(); } } void VRServiceHost::CheckForPuppetCompletion() { if (!mVRProcessEnabled) { // Puppet is running in this process, ask it directly if (VRPuppetCommandBuffer::Get().HasEnded()) { VRManager::Get()->NotifyPuppetComplete(); } } if (!mPuppetPendingCommands.IsEmpty()) { // There are puppet commands pending to be sent to the // VR process once its started, thus it has not ended. return; } if (!mVRProcessStarted) { // The VR process will be kept alive as long // as there is a queue in the puppet command // buffer. If the process is stopped, we can // infer that the queue has been cleared and // puppet state is reset. VRManager::Get()->NotifyPuppetComplete(); } // We need to ask the VR process if the puppet has ended SendPuppetCheckForCompletionToVRProcess(); // VRGPUChild::RecvNotifyPuppetComplete will call // VRManager::NotifyPuppetComplete if the puppet has completed // in the VR Process. } void VRServiceHost::SendPuppetCheckForCompletionToVRProcess() { // This is only allowed to run in the main thread of the GPU process if (!XRE_IsGPUProcess()) { return; } // Forward this to the main thread if not already there if (!NS_IsMainThread()) { RefPtr task = NS_NewRunnableFunction( "VRServiceHost::SendPuppetCheckForCompletionToVRProcess", []() -> void { VRServiceHost::Get()->SendPuppetCheckForCompletionToVRProcess(); }); NS_DispatchToMainThread(task.forget()); return; } if (VRGPUChild::IsCreated()) { VRGPUChild* vrGPUChild = VRGPUChild::Get(); vrGPUChild->SendPuppetCheckForCompletion(); } } } // namespace gfx } // namespace mozilla