summaryrefslogtreecommitdiffstats
path: root/gfx/vr/VRServiceHost.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--gfx/vr/VRServiceHost.cpp328
1 files changed, 328 insertions, 0 deletions
diff --git a/gfx/vr/VRServiceHost.cpp b/gfx/vr/VRServiceHost.cpp
new file mode 100644
index 0000000000..933a5467e5
--- /dev/null
+++ b/gfx/vr/VRServiceHost.cpp
@@ -0,0 +1,328 @@
+/* -*- 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<VRServiceHost> 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<Runnable> 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<Runnable> 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<uint64_t>& 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<uint64_t>& 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<Runnable> 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::XPCOMShutdownFinal))) {
+ // 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<Runnable> 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<Runnable> 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