summaryrefslogtreecommitdiffstats
path: root/gfx/vr/ipc
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/vr/ipc')
-rw-r--r--gfx/vr/ipc/PVR.ipdl49
-rw-r--r--gfx/vr/ipc/PVRGPU.ipdl28
-rw-r--r--gfx/vr/ipc/PVRLayer.ipdl35
-rw-r--r--gfx/vr/ipc/PVRManager.ipdl87
-rw-r--r--gfx/vr/ipc/VRChild.cpp212
-rw-r--r--gfx/vr/ipc/VRChild.h63
-rw-r--r--gfx/vr/ipc/VRGPUChild.cpp86
-rw-r--r--gfx/vr/ipc/VRGPUChild.h43
-rw-r--r--gfx/vr/ipc/VRGPUParent.cpp117
-rw-r--r--gfx/vr/ipc/VRGPUParent.h52
-rw-r--r--gfx/vr/ipc/VRLayerChild.cpp151
-rw-r--r--gfx/vr/ipc/VRLayerChild.h76
-rw-r--r--gfx/vr/ipc/VRLayerParent.cpp55
-rw-r--r--gfx/vr/ipc/VRLayerParent.h45
-rw-r--r--gfx/vr/ipc/VRManagerChild.cpp631
-rw-r--r--gfx/vr/ipc/VRManagerChild.h199
-rw-r--r--gfx/vr/ipc/VRManagerParent.cpp308
-rw-r--r--gfx/vr/ipc/VRManagerParent.h122
-rw-r--r--gfx/vr/ipc/VRMessageUtils.h73
-rw-r--r--gfx/vr/ipc/VRParent.cpp191
-rw-r--r--gfx/vr/ipc/VRParent.h64
-rw-r--r--gfx/vr/ipc/VRProcessChild.cpp49
-rw-r--r--gfx/vr/ipc/VRProcessChild.h39
-rw-r--r--gfx/vr/ipc/VRProcessManager.cpp287
-rw-r--r--gfx/vr/ipc/VRProcessManager.h95
-rw-r--r--gfx/vr/ipc/VRProcessParent.cpp248
-rw-r--r--gfx/vr/ipc/VRProcessParent.h87
27 files changed, 3492 insertions, 0 deletions
diff --git a/gfx/vr/ipc/PVR.ipdl b/gfx/vr/ipc/PVR.ipdl
new file mode 100644
index 0000000000..34f01fb51c
--- /dev/null
+++ b/gfx/vr/ipc/PVR.ipdl
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+using mozilla::TimeStamp from "mozilla/TimeStamp.h";
+using mozilla::gfx::VRControllerType from "moz_external_vr.h";
+using mozilla::dom::NativeThreadId from "mozilla/dom/NativeThreadId.h";
+include "VRMessageUtils.h";
+include "VRParent.h";
+include "VRChild.h";
+
+include GraphicsMessages;
+include MemoryReportTypes;
+include PrefsTypes;
+include protocol PVRGPU;
+
+namespace mozilla {
+namespace gfx {
+
+[ManualDealloc, NeedsOtherPid, ChildImpl="VRChild", ParentImpl="VRParent"]
+async protocol PVR
+{
+parent:
+ async NewGPUVRManager(Endpoint<PVRGPUParent> endpoint);
+ async Init(GfxVarUpdate[] vars, DevicePrefs devicePrefs);
+
+ async UpdateVar(GfxVarUpdate var);
+ async PreferenceUpdate(Pref pref);
+ async OpenVRControllerActionPathToVR(nsCString aPath);
+ async OpenVRControllerManifestPathToVR(VRControllerType aType, nsCString aPath);
+ async RequestMemoryReport(uint32_t generation,
+ bool anonymize,
+ bool minimizeMemoryUsage,
+ FileDescriptor? DMDFile)
+ returns (uint32_t aGeneration);
+
+child:
+ // Sent when the GPU process has initialized devices. This occurs once, after
+ // Init().
+ async InitComplete();
+ async OpenVRControllerActionPathToParent(nsCString aPath);
+ async OpenVRControllerManifestPathToParent(VRControllerType aType, nsCString aPath);
+ async InitCrashReporter(NativeThreadId threadId);
+ async AddMemoryReport(MemoryReport aReport);
+};
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/vr/ipc/PVRGPU.ipdl b/gfx/vr/ipc/PVRGPU.ipdl
new file mode 100644
index 0000000000..51ebee8c89
--- /dev/null
+++ b/gfx/vr/ipc/PVRGPU.ipdl
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "VRGPUParent.h";
+include "VRGPUChild.h";
+
+namespace mozilla {
+namespace gfx {
+
+// The parent process is the VR process.
+// The child process is the GPU process.
+[ManualDealloc, NeedsOtherPid, ChildImpl="VRGPUChild", ParentImpl="VRGPUParent"]
+async protocol PVRGPU
+{
+parent:
+ async StartVRService();
+ async StopVRService();
+ async PuppetSubmit(uint64_t[] aBuffer);
+ async PuppetReset();
+ async PuppetCheckForCompletion();
+child:
+ async NotifyPuppetComplete();
+};
+
+} // gfx
+} // mozilla \ No newline at end of file
diff --git a/gfx/vr/ipc/PVRLayer.ipdl b/gfx/vr/ipc/PVRLayer.ipdl
new file mode 100644
index 0000000000..c6fa16fd2e
--- /dev/null
+++ b/gfx/vr/ipc/PVRLayer.ipdl
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* 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 LayersSurfaces;
+include protocol PVRManager;
+
+include "mozilla/GfxMessageUtils.h";
+include "VRLayerChild.h";
+
+using mozilla::gfx::Rect from "mozilla/gfx/Rect.h";
+
+namespace mozilla {
+namespace gfx {
+
+[ManualDealloc, ChildImpl="VRLayerChild", ParentImpl=virtual]
+async protocol PVRLayer
+{
+ manager PVRManager;
+
+parent:
+ async SubmitFrame(SurfaceDescriptor aTexture, uint64_t aFrameId,
+ Rect aLeftEyeRect, Rect aRightEyeRect);
+
+ async Destroy();
+
+child:
+ async __delete__();
+};
+
+} // gfx
+} // mozilla
diff --git a/gfx/vr/ipc/PVRManager.ipdl b/gfx/vr/ipc/PVRManager.ipdl
new file mode 100644
index 0000000000..73c2a1bb4e
--- /dev/null
+++ b/gfx/vr/ipc/PVRManager.ipdl
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* 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 LayersSurfaces;
+include protocol PVRLayer;
+include LayersMessages;
+include GamepadEventTypes;
+
+include "VRMessageUtils.h";
+include "VRManagerParent.h";
+include "VRManagerChild.h";
+
+using struct mozilla::gfx::VRFieldOfView from "gfxVR.h";
+using struct mozilla::gfx::VRDisplayInfo from "gfxVR.h";
+using struct mozilla::gfx::VRSensorUpdate from "gfxVR.h";
+using struct mozilla::gfx::VRHMDSensorState from "gfxVR.h";
+using struct mozilla::gfx::VRControllerInfo from "gfxVR.h";
+using struct mozilla::gfx::VRSubmitFrameResultInfo from "gfxVR.h";
+using mozilla::gfx::VRDisplayCapabilityFlags from "moz_external_vr.h";
+using mozilla::layers::LayersBackend from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::TextureFlags from "mozilla/layers/CompositorTypes.h";
+using mozilla::dom::GamepadHandle from "mozilla/dom/GamepadHandle.h";
+
+
+namespace mozilla {
+namespace gfx {
+
+/**
+ * The PVRManager protocol is used to enable communication of VR display
+ * enumeration and sensor state between the compositor thread and
+ * content threads/processes.
+ */
+[ManualDealloc, NeedsOtherPid, ChildImpl="VRManagerChild", ParentImpl="VRManagerParent"]
+sync protocol PVRManager
+{
+ manages PVRLayer;
+
+parent:
+ async PVRLayer(uint32_t aDisplayID, uint32_t aGroup);
+
+ // Detect runtime capabilities. This will return the presense of VR and/or AR
+ // runtime software, without enumerating or activating any hardware devices.
+ async DetectRuntimes();
+
+ // (Re)Enumerate VR Displays. An updated list of VR displays will be returned
+ // asynchronously to children via UpdateDisplayInfo.
+ async RefreshDisplays();
+
+ async SetGroupMask(uint32_t aDisplayID, uint32_t aGroupMask);
+ async SetHaveEventListener(bool aHaveEventListener);
+
+ async ControllerListenerAdded();
+ async ControllerListenerRemoved();
+ async VibrateHaptic(GamepadHandle aGamepadHandle, uint32_t aHapticIndex,
+ double aIntensity, double aDuration, uint32_t aPromiseID);
+ async StopVibrateHaptic(GamepadHandle aGamepadHandle);
+ async StartVRNavigation(uint32_t aDeviceID);
+ async StopVRNavigation(uint32_t aDeviceID, TimeDuration aDuration);
+ async StartActivity();
+ async StopActivity();
+
+ async RunPuppet(uint64_t[] buffer);
+ async ResetPuppet();
+
+child:
+ // Notify children of updated VR display enumeration and details. This will
+ // be sent to all children when the parent receives RefreshDisplays, even
+ // if no changes have been detected. This ensures that Promises exposed
+ // through DOM calls are always resolved.
+ async UpdateDisplayInfo(VRDisplayInfo aDisplayInfo);
+
+ async UpdateRuntimeCapabilities(VRDisplayCapabilityFlags aCapabilities);
+
+ async ReplyGamepadVibrateHaptic(uint32_t aPromiseID);
+ async NotifyPuppetCommandBufferCompleted(bool aSuccess);
+ async NotifyPuppetResetComplete();
+
+ async __delete__();
+
+};
+
+} // gfx
+} // mozilla
diff --git a/gfx/vr/ipc/VRChild.cpp b/gfx/vr/ipc/VRChild.cpp
new file mode 100644
index 0000000000..858568455b
--- /dev/null
+++ b/gfx/vr/ipc/VRChild.cpp
@@ -0,0 +1,212 @@
+/* -*- 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 "VRChild.h"
+#include "VRProcessManager.h"
+#include "VRProcessParent.h"
+#include "gfxConfig.h"
+
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/VsyncDispatcher.h"
+#include "mozilla/dom/MemoryReportRequest.h"
+
+namespace mozilla {
+namespace gfx {
+
+class OpenVRControllerManifestManager {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(OpenVRControllerManifestManager)
+ public:
+ explicit OpenVRControllerManifestManager() = default;
+
+ void SetOpenVRControllerActionPath(const nsCString& aPath) {
+ mAction = aPath;
+ }
+
+ void SetOpenVRControllerManifestPath(VRControllerType aType,
+ const nsCString& aPath) {
+ mManifest.InsertOrUpdate(static_cast<uint32_t>(aType), aPath);
+ }
+
+ bool GetActionPath(nsCString* aPath) {
+ if (!mAction.IsEmpty()) {
+ *aPath = mAction;
+ return true;
+ }
+ return false;
+ }
+
+ bool GetManifestPath(VRControllerType aType, nsCString* aPath) {
+ return mManifest.Get(static_cast<uint32_t>(aType), aPath);
+ }
+
+ private:
+ ~OpenVRControllerManifestManager() {
+ if (!mAction.IsEmpty() && remove(mAction.BeginReading()) != 0) {
+ MOZ_ASSERT(false, "Delete controller action file failed.");
+ }
+ mAction = "";
+
+ for (const auto& path : mManifest.Values()) {
+ if (!path.IsEmpty() && remove(path.BeginReading()) != 0) {
+ MOZ_ASSERT(false, "Delete controller manifest file failed.");
+ }
+ }
+ mManifest.Clear();
+ }
+
+ nsCString mAction;
+ nsTHashMap<nsUint32HashKey, nsCString> mManifest;
+ OpenVRControllerManifestManager(const OpenVRControllerManifestManager&) =
+ delete;
+
+ const OpenVRControllerManifestManager& operator=(
+ const OpenVRControllerManifestManager&) = delete;
+};
+
+StaticRefPtr<OpenVRControllerManifestManager> sOpenVRControllerManifestManager;
+
+VRChild::VRChild(VRProcessParent* aHost) : mHost(aHost), mVRReady(false) {
+ MOZ_ASSERT(XRE_IsParentProcess());
+}
+
+mozilla::ipc::IPCResult VRChild::RecvAddMemoryReport(
+ const MemoryReport& aReport) {
+ if (mMemoryReportRequest) {
+ mMemoryReportRequest->RecvReport(aReport);
+ }
+ return IPC_OK();
+}
+
+void VRChild::ActorDestroy(ActorDestroyReason aWhy) {
+ if (aWhy == AbnormalShutdown) {
+ GenerateCrashReport(OtherPid());
+
+ Telemetry::Accumulate(
+ Telemetry::SUBPROCESS_ABNORMAL_ABORT,
+ nsDependentCString(XRE_GeckoProcessTypeToString(GeckoProcessType_VR)),
+ 1);
+ }
+ gfxVars::RemoveReceiver(this);
+ mHost->OnChannelClosed();
+}
+
+void VRChild::Init() {
+ nsTArray<GfxVarUpdate> updates = gfxVars::FetchNonDefaultVars();
+
+ DevicePrefs devicePrefs;
+ devicePrefs.hwCompositing() = gfxConfig::GetValue(Feature::HW_COMPOSITING);
+ devicePrefs.d3d11Compositing() =
+ gfxConfig::GetValue(Feature::D3D11_COMPOSITING);
+ devicePrefs.oglCompositing() =
+ gfxConfig::GetValue(Feature::OPENGL_COMPOSITING);
+ devicePrefs.useD2D1() = gfxConfig::GetValue(Feature::DIRECT2D);
+
+ SendInit(updates, devicePrefs);
+
+ if (!sOpenVRControllerManifestManager) {
+ sOpenVRControllerManifestManager = new OpenVRControllerManifestManager();
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "ClearOnShutdown OpenVRControllerManifestManager",
+ []() { ClearOnShutdown(&sOpenVRControllerManifestManager); }));
+ }
+
+ nsCString output;
+ if (sOpenVRControllerManifestManager->GetActionPath(&output)) {
+ SendOpenVRControllerActionPathToVR(output);
+ }
+ if (sOpenVRControllerManifestManager->GetManifestPath(
+ VRControllerType::HTCVive, &output)) {
+ SendOpenVRControllerManifestPathToVR(VRControllerType::HTCVive, output);
+ }
+ if (sOpenVRControllerManifestManager->GetManifestPath(VRControllerType::MSMR,
+ &output)) {
+ SendOpenVRControllerManifestPathToVR(VRControllerType::MSMR, output);
+ }
+ if (sOpenVRControllerManifestManager->GetManifestPath(
+ VRControllerType::ValveIndex, &output)) {
+ SendOpenVRControllerManifestPathToVR(VRControllerType::ValveIndex, output);
+ }
+ gfxVars::AddReceiver(this);
+}
+
+bool VRChild::EnsureVRReady() {
+ if (!mVRReady) {
+ return false;
+ }
+
+ return true;
+}
+
+mozilla::ipc::IPCResult VRChild::RecvOpenVRControllerActionPathToParent(
+ const nsCString& aPath) {
+ sOpenVRControllerManifestManager->SetOpenVRControllerActionPath(aPath);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult VRChild::RecvOpenVRControllerManifestPathToParent(
+ const VRControllerType& aType, const nsCString& aPath) {
+ sOpenVRControllerManifestManager->SetOpenVRControllerManifestPath(aType,
+ aPath);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult VRChild::RecvInitComplete() {
+ // We synchronously requested VR parameters before this arrived.
+ mVRReady = true;
+ return IPC_OK();
+}
+
+bool VRChild::SendRequestMemoryReport(const uint32_t& aGeneration,
+ const bool& aAnonymize,
+ const bool& aMinimizeMemoryUsage,
+ const Maybe<FileDescriptor>& aDMDFile) {
+ mMemoryReportRequest = MakeUnique<MemoryReportRequestHost>(aGeneration);
+
+ PVRChild::SendRequestMemoryReport(
+ aGeneration, aAnonymize, aMinimizeMemoryUsage, aDMDFile,
+ [&](const uint32_t& aGeneration2) {
+ if (VRProcessManager* vpm = VRProcessManager::Get()) {
+ if (VRChild* child = vpm->GetVRChild()) {
+ if (child->mMemoryReportRequest) {
+ child->mMemoryReportRequest->Finish(aGeneration2);
+ child->mMemoryReportRequest = nullptr;
+ }
+ }
+ }
+ },
+ [&](mozilla::ipc::ResponseRejectReason) {
+ if (VRProcessManager* vpm = VRProcessManager::Get()) {
+ if (VRChild* child = vpm->GetVRChild()) {
+ child->mMemoryReportRequest = nullptr;
+ }
+ }
+ });
+
+ return true;
+}
+
+void VRChild::OnVarChanged(const GfxVarUpdate& aVar) { SendUpdateVar(aVar); }
+
+class DeferredDeleteVRChild : public Runnable {
+ public:
+ explicit DeferredDeleteVRChild(UniquePtr<VRChild>&& aChild)
+ : Runnable("gfx::DeferredDeleteVRChild"), mChild(std::move(aChild)) {}
+
+ NS_IMETHODIMP Run() override { return NS_OK; }
+
+ private:
+ UniquePtr<VRChild> mChild;
+};
+
+/* static */
+void VRChild::Destroy(UniquePtr<VRChild>&& aChild) {
+ NS_DispatchToMainThread(new DeferredDeleteVRChild(std::move(aChild)));
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/vr/ipc/VRChild.h b/gfx/vr/ipc/VRChild.h
new file mode 100644
index 0000000000..04d9501fe8
--- /dev/null
+++ b/gfx/vr/ipc/VRChild.h
@@ -0,0 +1,63 @@
+/* -*- 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/. */
+
+#ifndef GFX_VR_CHILD_H
+#define GFX_VR_CHILD_H
+
+#include "mozilla/gfx/PVRChild.h"
+#include "mozilla/gfx/gfxVarReceiver.h"
+#include "mozilla/ipc/CrashReporterHelper.h"
+#include "mozilla/VsyncDispatcher.h"
+#include "moz_external_vr.h"
+
+namespace mozilla {
+namespace dom {
+class MemoryReportRequestHost;
+} // namespace dom
+namespace gfx {
+
+class VRProcessParent;
+class VRChild;
+
+class VRChild final : public PVRChild,
+ public ipc::CrashReporterHelper<GeckoProcessType_VR>,
+ public gfxVarReceiver {
+ typedef mozilla::dom::MemoryReportRequestHost MemoryReportRequestHost;
+ friend class PVRChild;
+
+ public:
+ explicit VRChild(VRProcessParent* aHost);
+ ~VRChild() = default;
+
+ static void Destroy(UniquePtr<VRChild>&& aChild);
+ void Init();
+ bool EnsureVRReady();
+ virtual void OnVarChanged(const GfxVarUpdate& aVar) override;
+ bool SendRequestMemoryReport(const uint32_t& aGeneration,
+ const bool& aAnonymize,
+ const bool& aMinimizeMemoryUsage,
+ const Maybe<ipc::FileDescriptor>& aDMDFile);
+
+ protected:
+ virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+ mozilla::ipc::IPCResult RecvOpenVRControllerActionPathToParent(
+ const nsCString& aPath);
+ mozilla::ipc::IPCResult RecvOpenVRControllerManifestPathToParent(
+ const VRControllerType& aType, const nsCString& aPath);
+ mozilla::ipc::IPCResult RecvInitComplete();
+
+ mozilla::ipc::IPCResult RecvAddMemoryReport(const MemoryReport& aReport);
+
+ private:
+ VRProcessParent* mHost;
+ UniquePtr<MemoryReportRequestHost> mMemoryReportRequest;
+ bool mVRReady;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // GFX_VR_CHILD_H
diff --git a/gfx/vr/ipc/VRGPUChild.cpp b/gfx/vr/ipc/VRGPUChild.cpp
new file mode 100644
index 0000000000..793778c032
--- /dev/null
+++ b/gfx/vr/ipc/VRGPUChild.cpp
@@ -0,0 +1,86 @@
+/* -*- 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 "VRGPUChild.h"
+#include "VRServiceHost.h"
+
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/StaticPrefs_dom.h"
+#include "VRManager.h"
+
+namespace mozilla {
+namespace gfx {
+
+static StaticRefPtr<VRGPUChild> sVRGPUChildSingleton;
+
+/* static */
+bool VRGPUChild::InitForGPUProcess(Endpoint<PVRGPUChild>&& aEndpoint) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(!sVRGPUChildSingleton);
+
+ if (!StaticPrefs::dom_vr_enabled() && !StaticPrefs::dom_vr_webxr_enabled()) {
+ return false;
+ }
+
+ RefPtr<VRGPUChild> child(new VRGPUChild());
+ if (!aEndpoint.Bind(child)) {
+ return false;
+ }
+ sVRGPUChildSingleton = child;
+
+#if !defined(MOZ_WIDGET_ANDROID)
+ RefPtr<Runnable> task = NS_NewRunnableFunction(
+ "VRServiceHost::NotifyVRProcessStarted", []() -> void {
+ VRServiceHost* host = VRServiceHost::Get();
+ host->NotifyVRProcessStarted();
+ });
+
+ NS_DispatchToMainThread(task.forget());
+#endif
+
+ return true;
+}
+
+/* static */
+bool VRGPUChild::IsCreated() { return !!sVRGPUChildSingleton; }
+
+/* static */
+VRGPUChild* VRGPUChild::Get() {
+ MOZ_ASSERT(IsCreated(), "VRGPUChild haven't initialized yet.");
+ return sVRGPUChildSingleton;
+}
+
+/*static*/
+void VRGPUChild::Shutdown() {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (sVRGPUChildSingleton && !sVRGPUChildSingleton->IsClosed()) {
+ sVRGPUChildSingleton->Close();
+ }
+ sVRGPUChildSingleton = nullptr;
+}
+
+void VRGPUChild::ActorDestroy(ActorDestroyReason aWhy) {
+ VRManager* vm = VRManager::Get();
+ mozilla::layers::CompositorThread()->Dispatch(
+ NewRunnableMethod("VRGPUChild::ActorDestroy", vm, &VRManager::Shutdown));
+
+ mClosed = true;
+}
+
+mozilla::ipc::IPCResult VRGPUChild::RecvNotifyPuppetComplete() {
+#if !defined(MOZ_WIDGET_ANDROID)
+ VRManager* vm = VRManager::Get();
+ mozilla::layers::CompositorThread()->Dispatch(NewRunnableMethod(
+ "VRManager::NotifyPuppetComplete", vm, &VRManager::NotifyPuppetComplete));
+#endif
+ return IPC_OK();
+}
+
+bool VRGPUChild::IsClosed() { return mClosed; }
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/vr/ipc/VRGPUChild.h b/gfx/vr/ipc/VRGPUChild.h
new file mode 100644
index 0000000000..3c13ecfc76
--- /dev/null
+++ b/gfx/vr/ipc/VRGPUChild.h
@@ -0,0 +1,43 @@
+/* -*- 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/. */
+
+#ifndef GFX_VR_GPU_CHILD_H
+#define GFX_VR_GPU_CHILD_H
+
+#include "mozilla/gfx/PVRGPUChild.h"
+
+namespace mozilla {
+namespace gfx {
+
+class VRGPUChild final : public PVRGPUChild {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VRGPUChild);
+
+ static VRGPUChild* Get();
+ static bool InitForGPUProcess(Endpoint<PVRGPUChild>&& aEndpoint);
+ static bool IsCreated();
+ static void Shutdown();
+
+ mozilla::ipc::IPCResult RecvNotifyPuppetComplete();
+ mozilla::ipc::IPCResult RecvNotifyServiceStarted();
+
+ virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+ bool IsClosed();
+
+ protected:
+ explicit VRGPUChild() : mClosed(false) {}
+ ~VRGPUChild() = default;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(VRGPUChild);
+
+ bool mClosed;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // GFX_VR_GPU_CHILD_H
diff --git a/gfx/vr/ipc/VRGPUParent.cpp b/gfx/vr/ipc/VRGPUParent.cpp
new file mode 100644
index 0000000000..72d943086d
--- /dev/null
+++ b/gfx/vr/ipc/VRGPUParent.cpp
@@ -0,0 +1,117 @@
+/* -*- 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 "VRGPUParent.h"
+#include "VRPuppetCommandBuffer.h"
+
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/ipc/ProcessChild.h"
+#include "mozilla/StaticPrefs_dom.h"
+
+namespace mozilla {
+namespace gfx {
+
+using namespace ipc;
+
+VRGPUParent::VRGPUParent(ProcessId aChildProcessId) : mClosed(false) {
+ MOZ_COUNT_CTOR(VRGPUParent);
+ MOZ_ASSERT(NS_IsMainThread());
+
+ SetOtherProcessId(aChildProcessId);
+}
+
+VRGPUParent::~VRGPUParent() { MOZ_COUNT_DTOR(VRGPUParent); }
+
+void VRGPUParent::ActorDestroy(ActorDestroyReason aWhy) {
+#if !defined(MOZ_WIDGET_ANDROID)
+ if (mVRService) {
+ mVRService->Stop();
+ mVRService = nullptr;
+ }
+#endif
+
+ mClosed = true;
+ GetCurrentSerialEventTarget()->Dispatch(
+ NewRunnableMethod("gfx::VRGPUParent::DeferredDestroy", this,
+ &VRGPUParent::DeferredDestroy));
+}
+
+void VRGPUParent::DeferredDestroy() { mSelfRef = nullptr; }
+
+/* static */
+RefPtr<VRGPUParent> VRGPUParent::CreateForGPU(
+ Endpoint<PVRGPUParent>&& aEndpoint) {
+ if (!StaticPrefs::dom_vr_enabled() && !StaticPrefs::dom_vr_webxr_enabled()) {
+ return nullptr;
+ }
+
+ RefPtr<VRGPUParent> vcp = new VRGPUParent(aEndpoint.OtherPid());
+ GetCurrentSerialEventTarget()->Dispatch(
+ NewRunnableMethod<Endpoint<PVRGPUParent>&&>("gfx::VRGPUParent::Bind", vcp,
+ &VRGPUParent::Bind,
+ std::move(aEndpoint)));
+
+ return vcp;
+}
+
+void VRGPUParent::Bind(Endpoint<PVRGPUParent>&& aEndpoint) {
+ if (!aEndpoint.Bind(this)) {
+ return;
+ }
+
+ mSelfRef = this;
+}
+
+mozilla::ipc::IPCResult VRGPUParent::RecvStartVRService() {
+#if !defined(MOZ_WIDGET_ANDROID)
+ mVRService = VRService::Create();
+ MOZ_ASSERT(mVRService);
+
+ mVRService->Start();
+#endif
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult VRGPUParent::RecvStopVRService() {
+#if !defined(MOZ_WIDGET_ANDROID)
+ if (mVRService) {
+ mVRService->Stop();
+ mVRService = nullptr;
+ }
+#endif
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult VRGPUParent::RecvPuppetReset() {
+#if !defined(MOZ_WIDGET_ANDROID)
+ VRPuppetCommandBuffer::Get().Reset();
+#endif
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult VRGPUParent::RecvPuppetSubmit(
+ const nsTArray<uint64_t>& aBuffer) {
+#if !defined(MOZ_WIDGET_ANDROID)
+ VRPuppetCommandBuffer::Get().Submit(aBuffer);
+#endif
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult VRGPUParent::RecvPuppetCheckForCompletion() {
+#if !defined(MOZ_WIDGET_ANDROID)
+ if (VRPuppetCommandBuffer::Get().HasEnded()) {
+ Unused << SendNotifyPuppetComplete();
+ }
+#endif
+ return IPC_OK();
+}
+
+bool VRGPUParent::IsClosed() { return mClosed; }
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/vr/ipc/VRGPUParent.h b/gfx/vr/ipc/VRGPUParent.h
new file mode 100644
index 0000000000..0a5b5ca9b0
--- /dev/null
+++ b/gfx/vr/ipc/VRGPUParent.h
@@ -0,0 +1,52 @@
+/* -*- 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/. */
+
+#ifndef GFX_VR_GPU_PARENT_H
+#define GFX_VR_GPU_PARENT_H
+
+#include "mozilla/gfx/PVRGPUParent.h"
+#include "VRService.h"
+
+namespace mozilla {
+namespace gfx {
+
+class VRGPUParent final : public PVRGPUParent {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VRGPUParent)
+
+ friend class PVRGPUParent;
+
+ public:
+ static RefPtr<VRGPUParent> CreateForGPU(Endpoint<PVRGPUParent>&& aEndpoint);
+ virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+ bool IsClosed();
+
+ protected:
+ void Bind(Endpoint<PVRGPUParent>&& aEndpoint);
+ mozilla::ipc::IPCResult RecvStartVRService();
+ mozilla::ipc::IPCResult RecvStopVRService();
+ mozilla::ipc::IPCResult RecvPuppetReset();
+ mozilla::ipc::IPCResult RecvPuppetSubmit(const nsTArray<uint64_t>& aBuffer);
+ mozilla::ipc::IPCResult RecvPuppetCheckForCompletion();
+
+ private:
+ explicit VRGPUParent(ProcessId aChildProcessId);
+ ~VRGPUParent();
+
+ void DeferredDestroy();
+
+ RefPtr<VRGPUParent> mSelfRef;
+#if !defined(MOZ_WIDGET_ANDROID)
+ RefPtr<VRService> mVRService;
+#endif
+ bool mClosed;
+
+ DISALLOW_COPY_AND_ASSIGN(VRGPUParent);
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // GFX_VR_CONTENT_PARENT_H
diff --git a/gfx/vr/ipc/VRLayerChild.cpp b/gfx/vr/ipc/VRLayerChild.cpp
new file mode 100644
index 0000000000..57f69f7d2b
--- /dev/null
+++ b/gfx/vr/ipc/VRLayerChild.cpp
@@ -0,0 +1,151 @@
+/* -*- 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 "VRLayerChild.h"
+
+#include "mozilla/dom/HTMLCanvasElement.h"
+#include "mozilla/layers/ImageBridgeChild.h"
+#include "mozilla/layers/LayersMessages.h" // for TimedTexture
+#include "mozilla/layers/SyncObject.h" // for SyncObjectClient
+#include "mozilla/StaticPrefs_dom.h"
+#include "mozilla/StaticPrefs_webgl.h"
+
+#include "ClientWebGLContext.h"
+#include "gfxPlatform.h"
+#include "GLContext.h"
+#include "GLScreenBuffer.h"
+#include "SharedSurface.h" // for SharedSurface
+#include "SharedSurfaceGL.h" // for SharedSurface
+
+namespace mozilla::gfx {
+
+VRLayerChild::VRLayerChild() { MOZ_COUNT_CTOR(VRLayerChild); }
+
+VRLayerChild::~VRLayerChild() {
+ ClearSurfaces();
+
+ MOZ_COUNT_DTOR(VRLayerChild);
+}
+
+void VRLayerChild::Initialize(dom::HTMLCanvasElement* aCanvasElement,
+ const gfx::Rect& aLeftEyeRect,
+ const gfx::Rect& aRightEyeRect) {
+ MOZ_ASSERT(aCanvasElement);
+ mLeftEyeRect = aLeftEyeRect;
+ mRightEyeRect = aRightEyeRect;
+ mCanvasElement = aCanvasElement;
+}
+
+void VRLayerChild::SetXRFramebuffer(WebGLFramebufferJS* fb) {
+ mFramebuffer = fb;
+}
+
+static constexpr bool kIsAndroid =
+#if defined(MOZ_WIDGET_ANDROID)
+ true;
+#else
+ false;
+#endif
+
+void VRLayerChild::SubmitFrame(const VRDisplayInfo& aDisplayInfo) {
+ uint64_t frameId = aDisplayInfo.GetFrameId();
+
+ // aFrameId will not increment unless the previuosly submitted
+ // frame was received by the VR thread and submitted to the VR
+ // compositor. We early-exit here in the event that SubmitFrame
+ // was called twice for the same aFrameId.
+ if (!mCanvasElement || frameId == mLastSubmittedFrameId) {
+ return;
+ }
+
+ const auto& webgl = mCanvasElement->GetWebGLContext();
+ if (!webgl) return;
+
+ // Keep the SharedSurfaceTextureClient alive long enough for
+ // 1 extra frame, accomodating overlapped asynchronous rendering.
+ mLastFrameTextureDesc = mThisFrameTextureDesc;
+
+ bool getNewFrame = true;
+ if (kIsAndroid) {
+ /**
+ * Do not blit WebGL to a SurfaceTexture until the last submitted frame is
+ * already processed and the new frame poses are ready. SurfaceTextures need
+ * to be released in the VR render thread in order to allow to be used again
+ * in the WebGLContext GLScreenBuffer producer. Not doing so causes some
+ * freezes, crashes or other undefined behaviour.
+ */
+ getNewFrame = (!mThisFrameTextureDesc ||
+ aDisplayInfo.mDisplayState.lastSubmittedFrameId ==
+ mLastSubmittedFrameId);
+ }
+ if (getNewFrame) {
+ const RefPtr<layers::ImageBridgeChild> imageBridge =
+ layers::ImageBridgeChild::GetSingleton();
+
+ auto texType = layers::TextureType::Unknown;
+ if (imageBridge) {
+ texType = layers::PreferredCanvasTextureType(imageBridge);
+ }
+ if (kIsAndroid && StaticPrefs::webgl_enable_surface_texture()) {
+ texType = layers::TextureType::AndroidNativeWindow;
+ }
+
+ webgl->Present(mFramebuffer, texType, true);
+ mThisFrameTextureDesc = webgl->GetFrontBuffer(mFramebuffer, true);
+ }
+
+ mLastSubmittedFrameId = frameId;
+
+ if (!mThisFrameTextureDesc) {
+ gfxCriticalError() << "ToSurfaceDescriptor failed in "
+ "VRLayerChild::SubmitFrame";
+ return;
+ }
+
+ SendSubmitFrame(*mThisFrameTextureDesc, frameId, mLeftEyeRect, mRightEyeRect);
+}
+
+bool VRLayerChild::IsIPCOpen() { return mIPCOpen; }
+
+void VRLayerChild::ClearSurfaces() {
+ mThisFrameTextureDesc = Nothing();
+ mLastFrameTextureDesc = Nothing();
+ const auto& webgl = mCanvasElement->GetWebGLContext();
+ if (!mFramebuffer && webgl) {
+ webgl->ClearVRSwapChain();
+ }
+}
+
+void VRLayerChild::ActorDestroy(ActorDestroyReason aWhy) { mIPCOpen = false; }
+
+// static
+PVRLayerChild* VRLayerChild::CreateIPDLActor() {
+ if (!StaticPrefs::dom_vr_enabled() && !StaticPrefs::dom_vr_webxr_enabled()) {
+ return nullptr;
+ }
+
+ VRLayerChild* c = new VRLayerChild();
+ c->AddIPDLReference();
+ return c;
+}
+
+// static
+bool VRLayerChild::DestroyIPDLActor(PVRLayerChild* actor) {
+ static_cast<VRLayerChild*>(actor)->ReleaseIPDLReference();
+ return true;
+}
+
+void VRLayerChild::AddIPDLReference() {
+ MOZ_ASSERT(mIPCOpen == false);
+ mIPCOpen = true;
+ AddRef();
+}
+void VRLayerChild::ReleaseIPDLReference() {
+ MOZ_ASSERT(mIPCOpen == false);
+ Release();
+}
+
+} // namespace mozilla::gfx
diff --git a/gfx/vr/ipc/VRLayerChild.h b/gfx/vr/ipc/VRLayerChild.h
new file mode 100644
index 0000000000..de91a46b87
--- /dev/null
+++ b/gfx/vr/ipc/VRLayerChild.h
@@ -0,0 +1,76 @@
+/* -*- 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/. */
+
+#ifndef GFX_VR_LAYERCHILD_H
+#define GFX_VR_LAYERCHILD_H
+
+#include "VRManagerChild.h"
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/gfx/PVRLayerChild.h"
+#include "gfxVR.h"
+
+class nsICanvasRenderingContextInternal;
+
+namespace mozilla {
+class WebGLContext;
+class WebGLFramebufferJS;
+namespace dom {
+class HTMLCanvasElement;
+}
+namespace layers {
+class SharedSurfaceTextureClient;
+}
+namespace gl {
+class SurfaceFactory;
+}
+namespace gfx {
+
+class VRLayerChild : public PVRLayerChild {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VRLayerChild)
+
+ public:
+ static PVRLayerChild* CreateIPDLActor();
+ static bool DestroyIPDLActor(PVRLayerChild* actor);
+
+ void Initialize(dom::HTMLCanvasElement* aCanvasElement,
+ const gfx::Rect& aLeftEyeRect,
+ const gfx::Rect& aRightEyeRect);
+ void SetXRFramebuffer(WebGLFramebufferJS*);
+ void SubmitFrame(const VRDisplayInfo& aDisplayInfo);
+ bool IsIPCOpen();
+
+ private:
+ VRLayerChild();
+ virtual ~VRLayerChild();
+ void ClearSurfaces();
+ virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ RefPtr<dom::HTMLCanvasElement> mCanvasElement;
+ bool mIPCOpen = false;
+
+ // AddIPDLReference and ReleaseIPDLReference are only to be called by
+ // CreateIPDLActor and DestroyIPDLActor, respectively. We intentionally make
+ // them private to prevent misuse. The purpose of these methods is to be aware
+ // of when the IPC system around this actor goes down: mIPCOpen is then set to
+ // false.
+ void AddIPDLReference();
+ void ReleaseIPDLReference();
+
+ gfx::Rect mLeftEyeRect;
+ gfx::Rect mRightEyeRect;
+ RefPtr<WebGLFramebufferJS> mFramebuffer;
+
+ Maybe<layers::SurfaceDescriptor> mThisFrameTextureDesc;
+ Maybe<layers::SurfaceDescriptor> mLastFrameTextureDesc;
+
+ uint64_t mLastSubmittedFrameId = 0;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif
diff --git a/gfx/vr/ipc/VRLayerParent.cpp b/gfx/vr/ipc/VRLayerParent.cpp
new file mode 100644
index 0000000000..b75a151027
--- /dev/null
+++ b/gfx/vr/ipc/VRLayerParent.cpp
@@ -0,0 +1,55 @@
+/* -*- 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 "VRLayerParent.h"
+#include "VRManager.h"
+#include "mozilla/Unused.h"
+#include "mozilla/layers/CompositorThread.h"
+
+namespace mozilla {
+using namespace layers;
+namespace gfx {
+
+VRLayerParent::VRLayerParent(uint32_t aVRDisplayID, const uint32_t aGroup)
+ : mIPCOpen(true), mDestroyed(false), mGroup(aGroup) {}
+
+VRLayerParent::~VRLayerParent() {
+ Destroy();
+ MOZ_COUNT_DTOR(VRLayerParent);
+}
+
+mozilla::ipc::IPCResult VRLayerParent::RecvDestroy() {
+ Destroy();
+ return IPC_OK();
+}
+
+void VRLayerParent::ActorDestroy(ActorDestroyReason aWhy) { mIPCOpen = false; }
+
+void VRLayerParent::Destroy() {
+ if (!mDestroyed) {
+ VRManager* vm = VRManager::Get();
+ vm->RemoveLayer(this);
+ mDestroyed = true;
+ }
+
+ if (mIPCOpen) {
+ Unused << PVRLayerParent::Send__delete__(this);
+ }
+}
+
+mozilla::ipc::IPCResult VRLayerParent::RecvSubmitFrame(
+ const layers::SurfaceDescriptor& aTexture, const uint64_t& aFrameId,
+ const gfx::Rect& aLeftEyeRect, const gfx::Rect& aRightEyeRect) {
+ if (!mDestroyed) {
+ VRManager* vm = VRManager::Get();
+ vm->SubmitFrame(this, aTexture, aFrameId, aLeftEyeRect, aRightEyeRect);
+ }
+
+ return IPC_OK();
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/vr/ipc/VRLayerParent.h b/gfx/vr/ipc/VRLayerParent.h
new file mode 100644
index 0000000000..559ee1c7de
--- /dev/null
+++ b/gfx/vr/ipc/VRLayerParent.h
@@ -0,0 +1,45 @@
+/* -*- 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/. */
+
+#ifndef GFX_VR_LAYERPARENT_H
+#define GFX_VR_LAYERPARENT_H
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/gfx/PVRLayerParent.h"
+#include "gfxVR.h"
+
+namespace mozilla {
+namespace gfx {
+
+class VRLayerParent : public PVRLayerParent {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VRLayerParent)
+
+ public:
+ VRLayerParent(uint32_t aVRDisplayID, const uint32_t aGroup);
+ virtual mozilla::ipc::IPCResult RecvSubmitFrame(
+ const layers::SurfaceDescriptor& aTexture, const uint64_t& aFrameId,
+ const gfx::Rect& aLeftEyeRect, const gfx::Rect& aRightEyeRect) override;
+ virtual mozilla::ipc::IPCResult RecvDestroy() override;
+ uint32_t GetGroup() const { return mGroup; }
+
+ protected:
+ virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ virtual ~VRLayerParent();
+ void Destroy();
+
+ bool mIPCOpen;
+
+ bool mDestroyed;
+ gfx::Rect mLeftEyeRect;
+ gfx::Rect mRightEyeRect;
+ uint32_t mGroup;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif
diff --git a/gfx/vr/ipc/VRManagerChild.cpp b/gfx/vr/ipc/VRManagerChild.cpp
new file mode 100644
index 0000000000..84e293568d
--- /dev/null
+++ b/gfx/vr/ipc/VRManagerChild.cpp
@@ -0,0 +1,631 @@
+/* -*- 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 "VRManagerChild.h"
+
+#include "VRLayerChild.h"
+#include "VRManagerParent.h"
+#include "VRThread.h"
+#include "VRDisplayClient.h"
+#include "nsGlobalWindow.h"
+#include "mozilla/ProfilerMarkers.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/layers/CompositorThread.h" // for CompositorThread
+#include "mozilla/dom/Navigator.h"
+#include "mozilla/dom/VREventObserver.h"
+#include "mozilla/dom/WebXRBinding.h"
+#include "mozilla/dom/WindowBinding.h" // for FrameRequestCallback
+#include "mozilla/dom/XRSystem.h"
+#include "mozilla/dom/XRFrame.h"
+#include "mozilla/dom/ContentChild.h"
+#include "nsContentUtils.h"
+#include "mozilla/dom/GamepadManager.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/layers/SyncObject.h"
+#include "mozilla/layers/TextureForwarder.h"
+
+using namespace mozilla::dom;
+
+namespace {
+const nsTArray<RefPtr<mozilla::gfx::VRManagerEventObserver>>::index_type
+ kNoIndex = nsTArray<RefPtr<mozilla::gfx::VRManagerEventObserver>>::NoIndex;
+} // namespace
+
+namespace mozilla {
+namespace gfx {
+
+static StaticRefPtr<VRManagerChild> sVRManagerChildSingleton;
+static StaticRefPtr<VRManagerParent> sVRManagerParentSingleton;
+
+static TimeStamp sMostRecentFrameEnd;
+static TimeDuration sAverageFrameInterval;
+
+void ReleaseVRManagerParentSingleton() { sVRManagerParentSingleton = nullptr; }
+
+VRManagerChild::VRManagerChild()
+ : mRuntimeCapabilities(VRDisplayCapabilityFlags::Cap_None),
+ mFrameRequestCallbackCounter(0),
+ mWaitingForEnumeration(false),
+ mBackend(layers::LayersBackend::LAYERS_NONE) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ mStartTimeStamp = TimeStamp::Now();
+ AddRef();
+}
+
+VRManagerChild::~VRManagerChild() { MOZ_ASSERT(NS_IsMainThread()); }
+
+/*static*/
+void VRManagerChild::IdentifyTextureHost(
+ const TextureFactoryIdentifier& aIdentifier) {
+ if (sVRManagerChildSingleton) {
+ sVRManagerChildSingleton->mBackend = aIdentifier.mParentBackend;
+ }
+}
+
+layers::LayersBackend VRManagerChild::GetBackendType() const {
+ return mBackend;
+}
+
+/*static*/
+VRManagerChild* VRManagerChild::Get() {
+ MOZ_ASSERT(sVRManagerChildSingleton);
+ return sVRManagerChildSingleton;
+}
+
+/* static */
+bool VRManagerChild::IsCreated() { return !!sVRManagerChildSingleton; }
+
+/* static */
+bool VRManagerChild::IsPresenting() {
+ if (!VRManagerChild::IsCreated()) {
+ return false;
+ }
+
+ nsTArray<RefPtr<VRDisplayClient>> displays;
+ sVRManagerChildSingleton->GetVRDisplays(displays);
+
+ bool result = false;
+ for (auto& display : displays) {
+ result |= display->IsPresenting();
+ }
+ return result;
+}
+
+TimeStamp VRManagerChild::GetIdleDeadlineHint(TimeStamp aDefault) {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!VRManagerChild::IsCreated() || sMostRecentFrameEnd.IsNull()) {
+ return aDefault;
+ }
+
+ TimeStamp idleEnd = sMostRecentFrameEnd + sAverageFrameInterval;
+ return idleEnd < aDefault ? idleEnd : aDefault;
+}
+
+/* static */
+bool VRManagerChild::InitForContent(Endpoint<PVRManagerChild>&& aEndpoint) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ RefPtr<VRManagerChild> child(new VRManagerChild());
+ if (!aEndpoint.Bind(child)) {
+ return false;
+ }
+ sVRManagerChildSingleton = child;
+ return true;
+}
+
+/*static*/
+void VRManagerChild::InitSameProcess() {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(!sVRManagerChildSingleton);
+
+ sVRManagerChildSingleton = new VRManagerChild();
+ sVRManagerParentSingleton = VRManagerParent::CreateSameProcess();
+ sVRManagerChildSingleton->Open(sVRManagerParentSingleton, CompositorThread(),
+ mozilla::ipc::ChildSide);
+}
+
+/* static */
+void VRManagerChild::InitWithGPUProcess(Endpoint<PVRManagerChild>&& aEndpoint) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(!sVRManagerChildSingleton);
+
+ sVRManagerChildSingleton = new VRManagerChild();
+ if (!aEndpoint.Bind(sVRManagerChildSingleton)) {
+ MOZ_CRASH("Couldn't Open() Compositor channel.");
+ }
+}
+
+/*static*/
+void VRManagerChild::ShutDown() {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!sVRManagerChildSingleton) {
+ return;
+ }
+ sVRManagerChildSingleton->Close();
+ sVRManagerChildSingleton = nullptr;
+}
+
+void VRManagerChild::ActorDealloc() { Release(); }
+
+void VRManagerChild::ActorDestroy(ActorDestroyReason aReason) {
+ if (sVRManagerChildSingleton == this) {
+ sVRManagerChildSingleton = nullptr;
+ }
+}
+
+PVRLayerChild* VRManagerChild::AllocPVRLayerChild(const uint32_t& aDisplayID,
+ const uint32_t& aGroup) {
+ return VRLayerChild::CreateIPDLActor();
+}
+
+bool VRManagerChild::DeallocPVRLayerChild(PVRLayerChild* actor) {
+ return VRLayerChild::DestroyIPDLActor(actor);
+}
+
+void VRManagerChild::UpdateDisplayInfo(const VRDisplayInfo& aDisplayInfo) {
+ nsTArray<uint32_t> disconnectedDisplays;
+ nsTArray<uint32_t> connectedDisplays;
+
+ const nsTArray<RefPtr<VRDisplayClient>> prevDisplays(mDisplays.Clone());
+
+ // Check if any displays have been disconnected
+ for (auto& display : prevDisplays) {
+ bool found = false;
+ if (aDisplayInfo.GetDisplayID() != 0) {
+ if (display->GetDisplayInfo().GetDisplayID() ==
+ aDisplayInfo.GetDisplayID()) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ // In order to make the current VRDisplay can continue to apply for the
+ // newest VRDisplayInfo, we need to exit presentionation before
+ // disconnecting.
+ if (display->IsPresentationGenerationCurrent()) {
+ NotifyPresentationGenerationChangedInternal(
+ display->GetDisplayInfo().GetDisplayID());
+
+ RefPtr<VRManagerChild> vm = VRManagerChild::Get();
+ vm->FireDOMVRDisplayPresentChangeEvent(
+ display->GetDisplayInfo().GetDisplayID());
+ }
+ display->NotifyDisconnected();
+ disconnectedDisplays.AppendElement(
+ display->GetDisplayInfo().GetDisplayID());
+ }
+ }
+
+ // mDisplays could be a hashed container for more scalability, but not worth
+ // it now as we expect < 10 entries.
+ nsTArray<RefPtr<VRDisplayClient>> displays;
+ if (aDisplayInfo.GetDisplayID() != 0) {
+ bool isNewDisplay = true;
+ for (auto& display : prevDisplays) {
+ const VRDisplayInfo& prevInfo = display->GetDisplayInfo();
+ if (prevInfo.GetDisplayID() == aDisplayInfo.GetDisplayID()) {
+ if (aDisplayInfo.GetIsConnected() && !prevInfo.GetIsConnected()) {
+ connectedDisplays.AppendElement(aDisplayInfo.GetDisplayID());
+ }
+ if (!aDisplayInfo.GetIsConnected() && prevInfo.GetIsConnected()) {
+ disconnectedDisplays.AppendElement(aDisplayInfo.GetDisplayID());
+ }
+ // MOZ_KnownLive because 'prevDisplays' is guaranteed to keep it alive.
+ //
+ // This can go away once
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=1620312 is fixed.
+ MOZ_KnownLive(display)->UpdateDisplayInfo(aDisplayInfo);
+ displays.AppendElement(display);
+ isNewDisplay = false;
+ break;
+ }
+ }
+ if (isNewDisplay) {
+ displays.AppendElement(new VRDisplayClient(aDisplayInfo));
+ connectedDisplays.AppendElement(aDisplayInfo.GetDisplayID());
+ }
+ }
+
+ mDisplays = std::move(displays);
+
+ // We wish to fire the events only after mDisplays is updated
+ for (uint32_t displayID : disconnectedDisplays) {
+ FireDOMVRDisplayDisconnectEvent(displayID);
+ }
+
+ for (uint32_t displayID : connectedDisplays) {
+ FireDOMVRDisplayConnectEvent(displayID);
+ }
+}
+
+bool VRManagerChild::RuntimeSupportsVR() const {
+ return bool(mRuntimeCapabilities & VRDisplayCapabilityFlags::Cap_ImmersiveVR);
+}
+bool VRManagerChild::RuntimeSupportsAR() const {
+ return bool(mRuntimeCapabilities & VRDisplayCapabilityFlags::Cap_ImmersiveAR);
+}
+bool VRManagerChild::RuntimeSupportsInline() const {
+ return bool(mRuntimeCapabilities & VRDisplayCapabilityFlags::Cap_Inline);
+}
+
+mozilla::ipc::IPCResult VRManagerChild::RecvUpdateRuntimeCapabilities(
+ const VRDisplayCapabilityFlags& aCapabilities) {
+ mRuntimeCapabilities = aCapabilities;
+ nsContentUtils::AddScriptRunner(NewRunnableMethod<>(
+ "gfx::VRManagerChild::NotifyRuntimeCapabilitiesUpdatedInternal", this,
+ &VRManagerChild::NotifyRuntimeCapabilitiesUpdatedInternal));
+ return IPC_OK();
+}
+
+void VRManagerChild::NotifyRuntimeCapabilitiesUpdatedInternal() {
+ const nsTArray<RefPtr<VRManagerEventObserver>> listeners = mListeners.Clone();
+ for (auto& listener : listeners) {
+ listener->NotifyDetectRuntimesCompleted();
+ }
+}
+
+mozilla::ipc::IPCResult VRManagerChild::RecvUpdateDisplayInfo(
+ const VRDisplayInfo& aDisplayInfo) {
+ UpdateDisplayInfo(aDisplayInfo);
+ for (auto& windowId : mNavigatorCallbacks) {
+ /** We must call NotifyVRDisplaysUpdated for every
+ * window's Navigator in mNavigatorCallbacks to ensure that
+ * the promise returned by Navigator.GetVRDevices
+ * can resolve. This must happen even if no changes
+ * to VRDisplays have been detected here.
+ */
+ nsGlobalWindowInner* window =
+ nsGlobalWindowInner::GetInnerWindowWithId(windowId);
+ if (!window) {
+ continue;
+ }
+ dom::Navigator* nav = window->Navigator();
+ if (!nav) {
+ continue;
+ }
+ nav->NotifyVRDisplaysUpdated();
+ }
+ mNavigatorCallbacks.Clear();
+ if (mWaitingForEnumeration) {
+ nsContentUtils::AddScriptRunner(NewRunnableMethod<>(
+ "gfx::VRManagerChild::NotifyEnumerationCompletedInternal", this,
+ &VRManagerChild::NotifyEnumerationCompletedInternal));
+ mWaitingForEnumeration = false;
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult VRManagerChild::RecvNotifyPuppetCommandBufferCompleted(
+ bool aSuccess) {
+ RefPtr<dom::Promise> promise = mRunPuppetPromise;
+ mRunPuppetPromise = nullptr;
+ if (aSuccess) {
+ promise->MaybeResolve(JS::UndefinedHandleValue);
+ } else {
+ promise->MaybeRejectWithUndefined();
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult VRManagerChild::RecvNotifyPuppetResetComplete() {
+ nsTArray<RefPtr<dom::Promise>> promises;
+ promises.AppendElements(mResetPuppetPromises);
+ mResetPuppetPromises.Clear();
+ for (const auto& promise : promises) {
+ promise->MaybeResolve(JS::UndefinedHandleValue);
+ }
+ return IPC_OK();
+}
+
+void VRManagerChild::RunPuppet(const nsTArray<uint64_t>& aBuffer,
+ dom::Promise* aPromise, ErrorResult& aRv) {
+ if (mRunPuppetPromise) {
+ // We only allow one puppet script to run simultaneously.
+ // The prior promise must be resolved before running a new
+ // script.
+ aRv.Throw(NS_ERROR_INVALID_ARG);
+ return;
+ }
+ if (!SendRunPuppet(aBuffer)) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return;
+ }
+ mRunPuppetPromise = aPromise;
+}
+
+void VRManagerChild::ResetPuppet(dom::Promise* aPromise, ErrorResult& aRv) {
+ if (!SendResetPuppet()) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return;
+ }
+ mResetPuppetPromises.AppendElement(aPromise);
+}
+
+void VRManagerChild::GetVRDisplays(
+ nsTArray<RefPtr<VRDisplayClient>>& aDisplays) {
+ aDisplays = mDisplays.Clone();
+}
+
+bool VRManagerChild::RefreshVRDisplaysWithCallback(uint64_t aWindowId) {
+ bool success = SendRefreshDisplays();
+ if (success) {
+ mNavigatorCallbacks.AppendElement(aWindowId);
+ }
+ return success;
+}
+
+bool VRManagerChild::EnumerateVRDisplays() {
+ bool success = SendRefreshDisplays();
+ if (success) {
+ mWaitingForEnumeration = true;
+ }
+ return success;
+}
+
+void VRManagerChild::DetectRuntimes() { Unused << SendDetectRuntimes(); }
+
+PVRLayerChild* VRManagerChild::CreateVRLayer(uint32_t aDisplayID,
+ uint32_t aGroup) {
+ PVRLayerChild* vrLayerChild = AllocPVRLayerChild(aDisplayID, aGroup);
+ return SendPVRLayerConstructor(vrLayerChild, aDisplayID, aGroup);
+}
+
+void VRManagerChild::XRFrameRequest::Call(
+ const DOMHighResTimeStamp& aTimeStamp) {
+ if (mCallback) {
+ RefPtr<mozilla::dom::FrameRequestCallback> callback = mCallback;
+ callback->Call(aTimeStamp);
+ } else {
+ RefPtr<mozilla::dom::XRFrameRequestCallback> callback = mXRCallback;
+ RefPtr<mozilla::dom::XRFrame> frame = mXRFrame;
+ callback->Call(aTimeStamp, *frame);
+ }
+}
+
+nsresult VRManagerChild::ScheduleFrameRequestCallback(
+ mozilla::dom::FrameRequestCallback& aCallback, int32_t* aHandle) {
+ if (mFrameRequestCallbackCounter == INT32_MAX) {
+ // Can't increment without overflowing; bail out
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ int32_t newHandle = ++mFrameRequestCallbackCounter;
+
+ mFrameRequestCallbacks.AppendElement(XRFrameRequest(aCallback, newHandle));
+
+ *aHandle = newHandle;
+ return NS_OK;
+}
+
+void VRManagerChild::CancelFrameRequestCallback(int32_t aHandle) {
+ // mFrameRequestCallbacks is stored sorted by handle
+ mFrameRequestCallbacks.RemoveElementSorted(aHandle);
+}
+
+void VRManagerChild::RunFrameRequestCallbacks() {
+ AUTO_PROFILER_TRACING_MARKER("VR", "RunFrameRequestCallbacks", GRAPHICS);
+
+ TimeStamp nowTime = TimeStamp::Now();
+ mozilla::TimeDuration duration = nowTime - mStartTimeStamp;
+ DOMHighResTimeStamp timeStamp = duration.ToMilliseconds();
+
+ if (!sMostRecentFrameEnd.IsNull()) {
+ TimeDuration frameInterval = nowTime - sMostRecentFrameEnd;
+ if (sAverageFrameInterval.IsZero()) {
+ sAverageFrameInterval = frameInterval;
+ } else {
+ // Calculate the average interval between frame end and next frame start.
+ // Apply some smoothing to make it more stable.
+ const double smooth = 0.9;
+ sAverageFrameInterval = sAverageFrameInterval.MultDouble(smooth) +
+ frameInterval.MultDouble(1.0 - smooth);
+ }
+ }
+
+ nsTArray<XRFrameRequest> callbacks;
+ callbacks.AppendElements(mFrameRequestCallbacks);
+ mFrameRequestCallbacks.Clear();
+ for (auto& callback : callbacks) {
+ // The FrameRequest copied into the on-stack array holds a strong ref to its
+ // mCallback and there's nothing that can drop that ref until we return.
+ MOZ_KnownLive(callback.mCallback)->Call(timeStamp);
+ }
+
+ if (IsPresenting()) {
+ sMostRecentFrameEnd = TimeStamp::Now();
+ }
+}
+
+void VRManagerChild::NotifyPresentationGenerationChanged(uint32_t aDisplayID) {
+ nsContentUtils::AddScriptRunner(NewRunnableMethod<uint32_t>(
+ "gfx::VRManagerChild::NotifyPresentationGenerationChangedInternal", this,
+ &VRManagerChild::NotifyPresentationGenerationChangedInternal,
+ aDisplayID));
+}
+
+void VRManagerChild::FireDOMVRDisplayMountedEvent(uint32_t aDisplayID) {
+ nsContentUtils::AddScriptRunner(NewRunnableMethod<uint32_t>(
+ "gfx::VRManagerChild::FireDOMVRDisplayMountedEventInternal", this,
+ &VRManagerChild::FireDOMVRDisplayMountedEventInternal, aDisplayID));
+}
+
+void VRManagerChild::FireDOMVRDisplayUnmountedEvent(uint32_t aDisplayID) {
+ nsContentUtils::AddScriptRunner(NewRunnableMethod<uint32_t>(
+ "gfx::VRManagerChild::FireDOMVRDisplayUnmountedEventInternal", this,
+ &VRManagerChild::FireDOMVRDisplayUnmountedEventInternal, aDisplayID));
+}
+
+void VRManagerChild::FireDOMVRDisplayConnectEvent(uint32_t aDisplayID) {
+ nsContentUtils::AddScriptRunner(NewRunnableMethod<uint32_t>(
+ "gfx::VRManagerChild::FireDOMVRDisplayConnectEventInternal", this,
+ &VRManagerChild::FireDOMVRDisplayConnectEventInternal, aDisplayID));
+}
+
+void VRManagerChild::FireDOMVRDisplayDisconnectEvent(uint32_t aDisplayID) {
+ nsContentUtils::AddScriptRunner(NewRunnableMethod<uint32_t>(
+ "gfx::VRManagerChild::FireDOMVRDisplayDisconnectEventInternal", this,
+ &VRManagerChild::FireDOMVRDisplayDisconnectEventInternal, aDisplayID));
+}
+
+void VRManagerChild::FireDOMVRDisplayPresentChangeEvent(uint32_t aDisplayID) {
+ nsContentUtils::AddScriptRunner(NewRunnableMethod<uint32_t>(
+ "gfx::VRManagerChild::FireDOMVRDisplayPresentChangeEventInternal", this,
+ &VRManagerChild::FireDOMVRDisplayPresentChangeEventInternal, aDisplayID));
+
+ if (!IsPresenting()) {
+ sMostRecentFrameEnd = TimeStamp();
+ sAverageFrameInterval = 0;
+ }
+}
+
+void VRManagerChild::FireDOMVRDisplayMountedEventInternal(uint32_t aDisplayID) {
+ // Iterate over a copy of mListeners, as dispatched events may modify it.
+ for (auto& listener : mListeners.Clone()) {
+ listener->NotifyVRDisplayMounted(aDisplayID);
+ }
+}
+
+void VRManagerChild::FireDOMVRDisplayUnmountedEventInternal(
+ uint32_t aDisplayID) {
+ // Iterate over a copy of mListeners, as dispatched events may modify it.
+ for (auto& listener : mListeners.Clone()) {
+ listener->NotifyVRDisplayUnmounted(aDisplayID);
+ }
+}
+
+void VRManagerChild::FireDOMVRDisplayConnectEventInternal(uint32_t aDisplayID) {
+ // Iterate over a copy of mListeners, as dispatched events may modify it.
+ for (auto& listener : mListeners.Clone()) {
+ listener->NotifyVRDisplayConnect(aDisplayID);
+ }
+}
+
+void VRManagerChild::FireDOMVRDisplayDisconnectEventInternal(
+ uint32_t aDisplayID) {
+ // Iterate over a copy of mListeners, as dispatched events may modify it.
+ for (auto& listener : mListeners.Clone()) {
+ listener->NotifyVRDisplayDisconnect(aDisplayID);
+ }
+}
+
+void VRManagerChild::FireDOMVRDisplayPresentChangeEventInternal(
+ uint32_t aDisplayID) {
+ // Iterate over a copy of mListeners, as dispatched events may modify it.
+ for (auto& listener : mListeners.Clone()) {
+ // MOZ_KnownLive because 'listeners' is guaranteed to keep it alive.
+ //
+ // This can go away once
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=1620312 is fixed.
+ MOZ_KnownLive(listener)->NotifyVRDisplayPresentChange(aDisplayID);
+ }
+}
+
+void VRManagerChild::FireDOMVRDisplayConnectEventsForLoadInternal(
+ uint32_t aDisplayID, VRManagerEventObserver* aObserver) {
+ aObserver->NotifyVRDisplayConnect(aDisplayID);
+}
+
+void VRManagerChild::NotifyPresentationGenerationChangedInternal(
+ uint32_t aDisplayID) {
+ for (auto& listener : mListeners.Clone()) {
+ listener->NotifyPresentationGenerationChanged(aDisplayID);
+ }
+}
+
+void VRManagerChild::NotifyEnumerationCompletedInternal() {
+ for (auto& listener : mListeners.Clone()) {
+ listener->NotifyEnumerationCompleted();
+ }
+}
+
+void VRManagerChild::FireDOMVRDisplayConnectEventsForLoad(
+ VRManagerEventObserver* aObserver) {
+ // We need to fire the VRDisplayConnect event when a page is loaded
+ // for each VR Display that has already been enumerated
+ for (const auto& display : mDisplays.Clone()) {
+ const VRDisplayInfo& info = display->GetDisplayInfo();
+ if (info.GetIsConnected()) {
+ nsContentUtils::AddScriptRunner(NewRunnableMethod<
+ uint32_t, RefPtr<VRManagerEventObserver>>(
+ "gfx::VRManagerChild::FireDOMVRDisplayConnectEventsForLoadInternal",
+ this, &VRManagerChild::FireDOMVRDisplayConnectEventsForLoadInternal,
+ info.GetDisplayID(), aObserver));
+ }
+ }
+}
+
+void VRManagerChild::AddListener(VRManagerEventObserver* aObserver) {
+ MOZ_ASSERT(aObserver);
+
+ if (mListeners.IndexOf(aObserver) != kNoIndex) {
+ return; // already exists
+ }
+
+ mListeners.AppendElement(aObserver);
+ if (mListeners.Length() == 1) {
+ Unused << SendSetHaveEventListener(true);
+ }
+}
+
+void VRManagerChild::RemoveListener(VRManagerEventObserver* aObserver) {
+ MOZ_ASSERT(aObserver);
+
+ mListeners.RemoveElement(aObserver);
+ if (mListeners.IsEmpty()) {
+ Unused << SendSetHaveEventListener(false);
+ }
+}
+
+void VRManagerChild::StartActivity() { Unused << SendStartActivity(); }
+
+void VRManagerChild::StopActivity() {
+ for (auto& listener : mListeners) {
+ if (!listener->GetStopActivityStatus()) {
+ // We are still showing VR in the active window.
+ return;
+ }
+ }
+
+ Unused << SendStopActivity();
+}
+
+void VRManagerChild::HandleFatalError(const char* aMsg) const {
+ dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aMsg, OtherPid());
+}
+
+void VRManagerChild::AddPromise(const uint32_t& aID, dom::Promise* aPromise) {
+ MOZ_ASSERT(!mGamepadPromiseList.Contains(aID));
+ mGamepadPromiseList.InsertOrUpdate(aID, RefPtr{aPromise});
+}
+
+gfx::VRAPIMode VRManagerChild::GetVRAPIMode(uint32_t aDisplayID) const {
+ for (auto& display : mDisplays) {
+ if (display->GetDisplayInfo().GetDisplayID() == aDisplayID) {
+ return display->GetXRAPIMode();
+ }
+ }
+ return VRAPIMode::WebXR;
+}
+
+mozilla::ipc::IPCResult VRManagerChild::RecvReplyGamepadVibrateHaptic(
+ const uint32_t& aPromiseID) {
+ // VRManagerChild could be at other processes, but GamepadManager
+ // only exists at the content process or the same process
+ // in non-e10s mode.
+ MOZ_ASSERT(XRE_IsContentProcess() || IsSameProcess());
+
+ RefPtr<dom::Promise> p;
+ if (!mGamepadPromiseList.Get(aPromiseID, getter_AddRefs(p))) {
+ MOZ_CRASH("We should always have a promise.");
+ }
+
+ p->MaybeResolve(true);
+ mGamepadPromiseList.Remove(aPromiseID);
+ return IPC_OK();
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/vr/ipc/VRManagerChild.h b/gfx/vr/ipc/VRManagerChild.h
new file mode 100644
index 0000000000..e980c69b88
--- /dev/null
+++ b/gfx/vr/ipc/VRManagerChild.h
@@ -0,0 +1,199 @@
+/* -*- 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/. */
+
+#ifndef MOZILLA_GFX_VR_VRMANAGERCHILD_H
+#define MOZILLA_GFX_VR_VRMANAGERCHILD_H
+
+#include "nsISupportsImpl.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/dom/AnimationFrameProviderBinding.h"
+#include "mozilla/dom/WebXRBinding.h"
+#include "mozilla/dom/XRFrame.h"
+#include "mozilla/gfx/PVRManagerChild.h"
+#include "mozilla/ipc/SharedMemory.h" // for SharedMemory, etc
+#include "mozilla/layers/ISurfaceAllocator.h" // for ISurfaceAllocator
+#include "mozilla/layers/LayersTypes.h" // for LayersBackend
+
+namespace mozilla {
+namespace dom {
+class Promise;
+class GamepadManager;
+class Navigator;
+class VRDisplay;
+class FrameRequestCallback;
+} // namespace dom
+namespace gfx {
+class VRLayerChild;
+class VRDisplayClient;
+
+class VRManagerEventObserver {
+ public:
+ NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
+ virtual void NotifyVRDisplayMounted(uint32_t aDisplayID) = 0;
+ virtual void NotifyVRDisplayUnmounted(uint32_t aDisplayID) = 0;
+ virtual void NotifyVRDisplayConnect(uint32_t aDisplayID) = 0;
+ virtual void NotifyVRDisplayDisconnect(uint32_t aDisplayID) = 0;
+ virtual void NotifyVRDisplayPresentChange(uint32_t aDisplayID) = 0;
+ virtual void NotifyPresentationGenerationChanged(uint32_t aDisplayID) = 0;
+ virtual bool GetStopActivityStatus() const = 0;
+ virtual void NotifyEnumerationCompleted() = 0;
+ virtual void NotifyDetectRuntimesCompleted() = 0;
+
+ protected:
+ virtual ~VRManagerEventObserver() = default;
+};
+
+class VRManagerChild : public PVRManagerChild {
+ friend class PVRManagerChild;
+
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VRManagerChild);
+
+ static VRManagerChild* Get();
+
+ // Indicate that an observer wants to receive VR events.
+ void AddListener(VRManagerEventObserver* aObserver);
+ // Indicate that an observer should no longer receive VR events.
+ void RemoveListener(VRManagerEventObserver* aObserver);
+ void StartActivity();
+ void StopActivity();
+ bool RuntimeSupportsVR() const;
+ bool RuntimeSupportsAR() const;
+ bool RuntimeSupportsInline() const;
+
+ void GetVRDisplays(nsTArray<RefPtr<VRDisplayClient>>& aDisplays);
+ bool RefreshVRDisplaysWithCallback(uint64_t aWindowId);
+ bool EnumerateVRDisplays();
+ void DetectRuntimes();
+ void AddPromise(const uint32_t& aID, dom::Promise* aPromise);
+ gfx::VRAPIMode GetVRAPIMode(uint32_t aDisplayID) const;
+
+ static void InitSameProcess();
+ static void InitWithGPUProcess(Endpoint<PVRManagerChild>&& aEndpoint);
+ static bool InitForContent(Endpoint<PVRManagerChild>&& aEndpoint);
+ static void ShutDown();
+
+ static bool IsCreated();
+ static bool IsPresenting();
+ static TimeStamp GetIdleDeadlineHint(TimeStamp aDefault);
+
+ PVRLayerChild* CreateVRLayer(uint32_t aDisplayID, uint32_t aGroup);
+
+ static void IdentifyTextureHost(
+ const layers::TextureFactoryIdentifier& aIdentifier);
+ layers::LayersBackend GetBackendType() const;
+
+ nsresult ScheduleFrameRequestCallback(dom::FrameRequestCallback& aCallback,
+ int32_t* aHandle);
+ void CancelFrameRequestCallback(int32_t aHandle);
+ MOZ_CAN_RUN_SCRIPT
+ void RunFrameRequestCallbacks();
+ void NotifyPresentationGenerationChanged(uint32_t aDisplayID);
+
+ MOZ_CAN_RUN_SCRIPT
+ void UpdateDisplayInfo(const VRDisplayInfo& aDisplayInfo);
+ void FireDOMVRDisplayMountedEvent(uint32_t aDisplayID);
+ void FireDOMVRDisplayUnmountedEvent(uint32_t aDisplayID);
+ void FireDOMVRDisplayConnectEvent(uint32_t aDisplayID);
+ void FireDOMVRDisplayDisconnectEvent(uint32_t aDisplayID);
+ void FireDOMVRDisplayPresentChangeEvent(uint32_t aDisplayID);
+ void FireDOMVRDisplayConnectEventsForLoad(VRManagerEventObserver* aObserver);
+
+ void HandleFatalError(const char* aMsg) const override;
+ void ActorDestroy(ActorDestroyReason aReason) override;
+
+ void RunPuppet(const nsTArray<uint64_t>& aBuffer, dom::Promise* aPromise,
+ ErrorResult& aRv);
+ void ResetPuppet(dom::Promise* aPromise, ErrorResult& aRv);
+
+ protected:
+ explicit VRManagerChild();
+ ~VRManagerChild();
+
+ PVRLayerChild* AllocPVRLayerChild(const uint32_t& aDisplayID,
+ const uint32_t& aGroup);
+ bool DeallocPVRLayerChild(PVRLayerChild* actor);
+
+ void ActorDealloc() override;
+
+ // MOZ_CAN_RUN_SCRIPT_BOUNDARY until we can mark ipdl-generated things as
+ // MOZ_CAN_RUN_SCRIPT.
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY
+ mozilla::ipc::IPCResult RecvUpdateDisplayInfo(
+ const VRDisplayInfo& aDisplayInfo);
+ mozilla::ipc::IPCResult RecvUpdateRuntimeCapabilities(
+ const VRDisplayCapabilityFlags& aCapabilities);
+ mozilla::ipc::IPCResult RecvReplyGamepadVibrateHaptic(
+ const uint32_t& aPromiseID);
+
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY
+ mozilla::ipc::IPCResult RecvNotifyPuppetCommandBufferCompleted(bool aSuccess);
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY
+ mozilla::ipc::IPCResult RecvNotifyPuppetResetComplete();
+
+ bool IsSameProcess() const { return OtherPid() == base::GetCurrentProcId(); }
+
+ private:
+ void FireDOMVRDisplayMountedEventInternal(uint32_t aDisplayID);
+ void FireDOMVRDisplayUnmountedEventInternal(uint32_t aDisplayID);
+ void FireDOMVRDisplayConnectEventInternal(uint32_t aDisplayID);
+ void FireDOMVRDisplayDisconnectEventInternal(uint32_t aDisplayID);
+ void FireDOMVRDisplayPresentChangeEventInternal(uint32_t aDisplayID);
+ void FireDOMVRDisplayConnectEventsForLoadInternal(
+ uint32_t aDisplayID, VRManagerEventObserver* aObserver);
+ void NotifyPresentationGenerationChangedInternal(uint32_t aDisplayID);
+ void NotifyEnumerationCompletedInternal();
+ void NotifyRuntimeCapabilitiesUpdatedInternal();
+
+ nsTArray<RefPtr<VRDisplayClient>> mDisplays;
+ VRDisplayCapabilityFlags mRuntimeCapabilities;
+ bool mDisplaysInitialized;
+ nsTArray<uint64_t> mNavigatorCallbacks;
+
+ struct XRFrameRequest {
+ XRFrameRequest(mozilla::dom::FrameRequestCallback& aCallback,
+ int32_t aHandle)
+ : mCallback(&aCallback), mHandle(aHandle) {}
+
+ XRFrameRequest(mozilla::dom::XRFrameRequestCallback& aCallback,
+ mozilla::dom::XRFrame& aFrame, int32_t aHandle)
+ : mXRCallback(&aCallback), mXRFrame(&aFrame), mHandle(aHandle) {}
+ MOZ_CAN_RUN_SCRIPT
+ void Call(const DOMHighResTimeStamp& aTimeStamp);
+
+ // Comparator operators to allow RemoveElementSorted with an
+ // integer argument on arrays of XRFrameRequest
+ bool operator==(int32_t aHandle) const { return mHandle == aHandle; }
+ bool operator<(int32_t aHandle) const { return mHandle < aHandle; }
+
+ RefPtr<mozilla::dom::FrameRequestCallback> mCallback;
+ RefPtr<mozilla::dom::XRFrameRequestCallback> mXRCallback;
+ RefPtr<mozilla::dom::XRFrame> mXRFrame;
+ int32_t mHandle;
+ };
+
+ nsTArray<XRFrameRequest> mFrameRequestCallbacks;
+ /**
+ * The current frame request callback handle
+ */
+ int32_t mFrameRequestCallbackCounter;
+ mozilla::TimeStamp mStartTimeStamp;
+
+ nsTArray<RefPtr<VRManagerEventObserver>> mListeners;
+ bool mWaitingForEnumeration;
+
+ layers::LayersBackend mBackend;
+ nsRefPtrHashtable<nsUint32HashKey, dom::Promise> mGamepadPromiseList;
+ RefPtr<dom::Promise> mRunPuppetPromise;
+ nsTArray<RefPtr<dom::Promise>> mResetPuppetPromises;
+
+ DISALLOW_COPY_AND_ASSIGN(VRManagerChild);
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_VR_VRMANAGERCHILD_H
diff --git a/gfx/vr/ipc/VRManagerParent.cpp b/gfx/vr/ipc/VRManagerParent.cpp
new file mode 100644
index 0000000000..01a2f4e6a5
--- /dev/null
+++ b/gfx/vr/ipc/VRManagerParent.cpp
@@ -0,0 +1,308 @@
+/* -*- 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 "VRManagerParent.h"
+
+#include "ipc/VRLayerParent.h"
+#include "mozilla/gfx/PVRManagerParent.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/ipc/ProtocolTypes.h"
+#include "mozilla/StaticPrefs_dom.h"
+#include "mozilla/ipc/ProtocolUtils.h" // for IToplevelProtocol
+#include "mozilla/TimeStamp.h" // for TimeStamp
+#include "mozilla/Unused.h"
+#include "VRManager.h"
+#include "VRThread.h"
+
+using mozilla::dom::GamepadHandle;
+
+namespace mozilla {
+using namespace layers;
+namespace gfx {
+
+// See VRManagerChild.cpp
+void ReleaseVRManagerParentSingleton();
+
+VRManagerParent::VRManagerParent(ProcessId aChildProcessId,
+ bool aIsContentChild)
+ : mHaveEventListener(false),
+ mHaveControllerListener(false),
+ mIsContentChild(aIsContentChild),
+ mVRActiveStatus(false) {
+ MOZ_COUNT_CTOR(VRManagerParent);
+ MOZ_ASSERT(NS_IsMainThread());
+
+ SetOtherProcessId(aChildProcessId);
+}
+
+VRManagerParent::~VRManagerParent() {
+ MOZ_ASSERT(!mVRManagerHolder);
+
+ MOZ_COUNT_DTOR(VRManagerParent);
+}
+
+PVRLayerParent* VRManagerParent::AllocPVRLayerParent(const uint32_t& aDisplayID,
+ const uint32_t& aGroup) {
+ if (!StaticPrefs::dom_vr_enabled() && !StaticPrefs::dom_vr_webxr_enabled()) {
+ return nullptr;
+ }
+
+ RefPtr<VRLayerParent> layer;
+ layer = new VRLayerParent(aDisplayID, aGroup);
+ VRManager* vm = VRManager::Get();
+ vm->AddLayer(layer);
+ return layer.forget().take();
+}
+
+bool VRManagerParent::DeallocPVRLayerParent(PVRLayerParent* actor) {
+ delete actor;
+ return true;
+}
+
+bool VRManagerParent::IsSameProcess() const {
+ return OtherPid() == base::GetCurrentProcId();
+}
+
+void VRManagerParent::RegisterWithManager() {
+ VRManager* vm = VRManager::Get();
+ vm->AddVRManagerParent(this);
+ mVRManagerHolder = vm;
+}
+
+void VRManagerParent::UnregisterFromManager() {
+ VRManager* vm = VRManager::Get();
+ vm->RemoveVRManagerParent(this);
+ mVRManagerHolder = nullptr;
+}
+
+/* static */
+bool VRManagerParent::CreateForContent(Endpoint<PVRManagerParent>&& aEndpoint) {
+ if (!CompositorThread()) {
+ return false;
+ }
+
+ RefPtr<VRManagerParent> vmp = new VRManagerParent(aEndpoint.OtherPid(), true);
+ CompositorThread()->Dispatch(NewRunnableMethod<Endpoint<PVRManagerParent>&&>(
+ "gfx::VRManagerParent::Bind", vmp, &VRManagerParent::Bind,
+ std::move(aEndpoint)));
+
+ return true;
+}
+
+void VRManagerParent::Bind(Endpoint<PVRManagerParent>&& aEndpoint) {
+ if (!aEndpoint.Bind(this)) {
+ return;
+ }
+ mSelfRef = this;
+
+ RegisterWithManager();
+}
+
+/*static*/
+void VRManagerParent::RegisterVRManagerInCompositorThread(
+ VRManagerParent* aVRManager) {
+ aVRManager->RegisterWithManager();
+}
+
+/*static*/
+VRManagerParent* VRManagerParent::CreateSameProcess() {
+ RefPtr<VRManagerParent> vmp =
+ new VRManagerParent(base::GetCurrentProcId(), false);
+ vmp->mCompositorThreadHolder = CompositorThreadHolder::GetSingleton();
+ vmp->mSelfRef = vmp;
+ CompositorThread()->Dispatch(
+ NewRunnableFunction("RegisterVRManagerIncompositorThreadRunnable",
+ RegisterVRManagerInCompositorThread, vmp.get()));
+ return vmp.get();
+}
+
+bool VRManagerParent::CreateForGPUProcess(
+ Endpoint<PVRManagerParent>&& aEndpoint) {
+ RefPtr<VRManagerParent> vmp =
+ new VRManagerParent(aEndpoint.OtherPid(), false);
+ vmp->mCompositorThreadHolder = CompositorThreadHolder::GetSingleton();
+ vmp->mSelfRef = vmp;
+ CompositorThread()->Dispatch(NewRunnableMethod<Endpoint<PVRManagerParent>&&>(
+ "gfx::VRManagerParent::Bind", vmp, &VRManagerParent::Bind,
+ std::move(aEndpoint)));
+ return true;
+}
+
+/*static*/
+void VRManagerParent::Shutdown() {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(
+ CompositorThread(),
+ "Shutdown() must gets called before the compositor thread is shutdown");
+ ReleaseVRManagerParentSingleton();
+ CompositorThread()->Dispatch(NS_NewRunnableFunction(
+ "VRManagerParent::Shutdown",
+ [vm = RefPtr<VRManager>(VRManager::MaybeGet())]() -> void {
+ if (!vm) {
+ return;
+ }
+ vm->ShutdownVRManagerParents();
+ }));
+}
+
+void VRManagerParent::ActorDestroy(ActorDestroyReason why) {}
+
+void VRManagerParent::ActorAlloc() {
+ // FIXME: This actor should probably use proper refcounting instead of manual
+ // reference management, and probably shouldn't manage
+ // `mCompositorThreadHolder` in the alloc/dealloc methods.
+ PVRManagerParent::ActorAlloc();
+ mCompositorThreadHolder = CompositorThreadHolder::GetSingleton();
+}
+
+void VRManagerParent::ActorDealloc() {
+ UnregisterFromManager();
+ mCompositorThreadHolder = nullptr;
+ mSelfRef = nullptr;
+}
+
+mozilla::ipc::IPCResult VRManagerParent::RecvDetectRuntimes() {
+ // Detect runtime capabilities. This will return the presense of VR and/or AR
+ // runtime software, without enumerating or activating any hardware devices.
+ // UpdateDisplayInfo will be sent to VRManagerChild with the results of the
+ // detection.
+ VRManager* vm = VRManager::Get();
+ vm->DetectRuntimes();
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult VRManagerParent::RecvRefreshDisplays() {
+ // This is called to activate the VR runtimes, detecting the
+ // presence and capabilities of XR hardware.
+ // UpdateDisplayInfo will be sent to VRManagerChild with the results of the
+ // enumerated hardware.
+ VRManager* vm = VRManager::Get();
+ vm->EnumerateDevices();
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult VRManagerParent::RecvSetGroupMask(
+ const uint32_t& aDisplayID, const uint32_t& aGroupMask) {
+ VRManager* vm = VRManager::Get();
+ vm->SetGroupMask(aGroupMask);
+ return IPC_OK();
+}
+
+bool VRManagerParent::HaveEventListener() { return mHaveEventListener; }
+
+bool VRManagerParent::HaveControllerListener() {
+ return mHaveControllerListener;
+}
+
+bool VRManagerParent::GetVRActiveStatus() { return mVRActiveStatus; }
+
+mozilla::ipc::IPCResult VRManagerParent::RecvSetHaveEventListener(
+ const bool& aHaveEventListener) {
+ mHaveEventListener = aHaveEventListener;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult VRManagerParent::RecvControllerListenerAdded() {
+ // Force update the available controllers for GamepadManager,
+ VRManager* vm = VRManager::Get();
+ vm->StopAllHaptics();
+ mHaveControllerListener = true;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult VRManagerParent::RecvControllerListenerRemoved() {
+ mHaveControllerListener = false;
+ VRManager* vm = VRManager::Get();
+ vm->StopAllHaptics();
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult VRManagerParent::RecvRunPuppet(
+ const nsTArray<uint64_t>& aBuffer) {
+#if defined(MOZ_WIDGET_ANDROID)
+ // Not yet implemented for Android / GeckoView
+ // See Bug 1555192
+ Unused << SendNotifyPuppetCommandBufferCompleted(false);
+#else
+ VRManager* vm = VRManager::Get();
+ if (!vm->RunPuppet(aBuffer, this)) {
+ // We have immediately failed, need to resolve the
+ // promise right away
+ Unused << SendNotifyPuppetCommandBufferCompleted(false);
+ }
+#endif // defined(MOZ_WIDGET_ANDROID)
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult VRManagerParent::RecvResetPuppet() {
+#if defined(MOZ_WIDGET_ANDROID)
+ // Not yet implemented for Android / GeckoView
+ // See Bug 1555192
+#else
+ VRManager* vm = VRManager::Get();
+ vm->ResetPuppet(this);
+#endif // defined(MOZ_WIDGET_ANDROID)
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult VRManagerParent::RecvVibrateHaptic(
+ const mozilla::dom::GamepadHandle& aGamepadHandle,
+ const uint32_t& aHapticIndex, const double& aIntensity,
+ const double& aDuration, const uint32_t& aPromiseID) {
+ VRManager* vm = VRManager::Get();
+ VRManagerPromise promise(this, aPromiseID);
+
+ vm->VibrateHaptic(aGamepadHandle, aHapticIndex, aIntensity, aDuration,
+ promise);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult VRManagerParent::RecvStopVibrateHaptic(
+ const mozilla::dom::GamepadHandle& aGamepadHandle) {
+ VRManager* vm = VRManager::Get();
+ vm->StopVibrateHaptic(aGamepadHandle);
+ return IPC_OK();
+}
+
+bool VRManagerParent::SendReplyGamepadVibrateHaptic(
+ const uint32_t& aPromiseID) {
+ // GamepadManager only exists at the content process
+ // or the same process in non-e10s mode.
+ if (mHaveControllerListener && (mIsContentChild || IsSameProcess())) {
+ return PVRManagerParent::SendReplyGamepadVibrateHaptic(aPromiseID);
+ }
+
+ return true;
+}
+
+mozilla::ipc::IPCResult VRManagerParent::RecvStartVRNavigation(
+ const uint32_t& aDeviceID) {
+ VRManager* vm = VRManager::Get();
+ vm->StartVRNavigation(aDeviceID);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult VRManagerParent::RecvStopVRNavigation(
+ const uint32_t& aDeviceID, const TimeDuration& aTimeout) {
+ VRManager* vm = VRManager::Get();
+ vm->StopVRNavigation(aDeviceID, aTimeout);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult VRManagerParent::RecvStartActivity() {
+ mVRActiveStatus = true;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult VRManagerParent::RecvStopActivity() {
+ mVRActiveStatus = false;
+ return IPC_OK();
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/vr/ipc/VRManagerParent.h b/gfx/vr/ipc/VRManagerParent.h
new file mode 100644
index 0000000000..bc23d541a6
--- /dev/null
+++ b/gfx/vr/ipc/VRManagerParent.h
@@ -0,0 +1,122 @@
+/* -*- 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/. */
+
+#ifndef MOZILLA_GFX_VR_VRMANAGERPARENT_H
+#define MOZILLA_GFX_VR_VRMANAGERPARENT_H
+
+#include "mozilla/layers/CompositorThread.h" // for CompositorThreadHolder
+#include "mozilla/layers/CompositableTransactionParent.h" // need?
+#include "mozilla/gfx/PVRManagerParent.h" // for PVRManagerParent
+#include "mozilla/gfx/PVRLayerParent.h" // for PVRLayerParent
+#include "mozilla/ipc/ProtocolUtils.h" // for IToplevelProtocol
+#include "mozilla/TimeStamp.h" // for TimeStamp
+#include "gfxVR.h" // for VRFieldOfView
+
+namespace mozilla {
+using namespace layers;
+namespace gfx {
+
+class VRManager;
+
+class VRManagerParent final : public PVRManagerParent {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VRManagerParent);
+
+ friend class PVRManagerParent;
+
+ public:
+ explicit VRManagerParent(ProcessId aChildProcessId, bool aIsContentChild);
+
+ static VRManagerParent* CreateSameProcess();
+ static bool CreateForGPUProcess(Endpoint<PVRManagerParent>&& aEndpoint);
+ static bool CreateForContent(Endpoint<PVRManagerParent>&& aEndpoint);
+ static void Shutdown();
+
+ bool IsSameProcess() const;
+ bool HaveEventListener();
+ bool HaveControllerListener();
+ bool GetVRActiveStatus();
+ bool SendReplyGamepadVibrateHaptic(const uint32_t& aPromiseID);
+
+ protected:
+ ~VRManagerParent();
+
+ PVRLayerParent* AllocPVRLayerParent(const uint32_t& aDisplayID,
+ const uint32_t& aGroup);
+ bool DeallocPVRLayerParent(PVRLayerParent* actor);
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+
+ mozilla::ipc::IPCResult RecvDetectRuntimes();
+ mozilla::ipc::IPCResult RecvRefreshDisplays();
+ mozilla::ipc::IPCResult RecvSetGroupMask(const uint32_t& aDisplayID,
+ const uint32_t& aGroupMask);
+ mozilla::ipc::IPCResult RecvSetHaveEventListener(
+ const bool& aHaveEventListener);
+ mozilla::ipc::IPCResult RecvControllerListenerAdded();
+ mozilla::ipc::IPCResult RecvControllerListenerRemoved();
+ mozilla::ipc::IPCResult RecvVibrateHaptic(
+ const mozilla::dom::GamepadHandle& aGamepadHandle,
+ const uint32_t& aHapticIndex, const double& aIntensity,
+ const double& aDuration, const uint32_t& aPromiseID);
+ mozilla::ipc::IPCResult RecvStopVibrateHaptic(
+ const mozilla::dom::GamepadHandle& aGamepadHandle);
+ mozilla::ipc::IPCResult RecvStartVRNavigation(const uint32_t& aDeviceID);
+ mozilla::ipc::IPCResult RecvStopVRNavigation(const uint32_t& aDeviceID,
+ const TimeDuration& aTimeout);
+ mozilla::ipc::IPCResult RecvStartActivity();
+ mozilla::ipc::IPCResult RecvStopActivity();
+
+ mozilla::ipc::IPCResult RecvRunPuppet(const nsTArray<uint64_t>& aBuffer);
+ mozilla::ipc::IPCResult RecvResetPuppet();
+
+ private:
+ void ActorAlloc() override;
+ void ActorDealloc() override;
+ void RegisterWithManager();
+ void UnregisterFromManager();
+
+ void Bind(Endpoint<PVRManagerParent>&& aEndpoint);
+
+ static void RegisterVRManagerInCompositorThread(VRManagerParent* aVRManager);
+
+ // This keeps us alive until ActorDestroy(), at which point we do a
+ // deferred destruction of ourselves.
+ RefPtr<VRManagerParent> mSelfRef;
+ // Keep the compositor thread alive, until we have destroyed ourselves.
+ RefPtr<CompositorThreadHolder> mCompositorThreadHolder;
+
+ // Keep the VRManager alive, until we have destroyed ourselves.
+ RefPtr<VRManager> mVRManagerHolder;
+ bool mHaveEventListener;
+ bool mHaveControllerListener;
+ bool mIsContentChild;
+
+ // When VR tabs are switched the background, we won't need to
+ // initialize its session in VRService thread.
+ bool mVRActiveStatus;
+};
+
+class VRManagerPromise final {
+ friend class VRManager;
+
+ public:
+ explicit VRManagerPromise(RefPtr<VRManagerParent> aParent,
+ uint32_t aPromiseID)
+ : mParent(aParent), mPromiseID(aPromiseID) {}
+ ~VRManagerPromise() { mParent = nullptr; }
+ bool operator==(const VRManagerPromise& aOther) const {
+ return mParent == aOther.mParent && mPromiseID == aOther.mPromiseID;
+ }
+
+ private:
+ RefPtr<VRManagerParent> mParent;
+ uint32_t mPromiseID;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_VR_VRMANAGERPARENT_H
diff --git a/gfx/vr/ipc/VRMessageUtils.h b/gfx/vr/ipc/VRMessageUtils.h
new file mode 100644
index 0000000000..0d19c9e4ec
--- /dev/null
+++ b/gfx/vr/ipc/VRMessageUtils.h
@@ -0,0 +1,73 @@
+/* -*- 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/. */
+
+#ifndef mozilla_gfx_vr_VRMessageUtils_h
+#define mozilla_gfx_vr_VRMessageUtils_h
+
+#include "ipc/EnumSerializer.h"
+#include "ipc/IPCMessageUtils.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/GfxMessageUtils.h"
+#include "mozilla/dom/GamepadMessageUtils.h"
+
+#include "gfxVR.h"
+
+namespace IPC {
+
+template <>
+struct ParamTraits<mozilla::gfx::VRControllerType>
+ : public ContiguousEnumSerializer<mozilla::gfx::VRControllerType,
+ mozilla::gfx::VRControllerType::_empty,
+ mozilla::gfx::VRControllerType::_end> {};
+
+// VRHMDSensorState is POD, we can use PlainOldDataSerializer
+static_assert(std::is_pod<mozilla::gfx::VRHMDSensorState>::value,
+ "mozilla::gfx::VRHMDSensorState must be a POD type.");
+template <>
+struct ParamTraits<mozilla::gfx::VRHMDSensorState>
+ : public PlainOldDataSerializer<mozilla::gfx::VRHMDSensorState> {};
+
+// VRDisplayInfo is POD, we can use PlainOldDataSerializer
+static_assert(std::is_pod<mozilla::gfx::VRDisplayInfo>::value,
+ "mozilla::gfx::VRDisplayInfo must be a POD type.");
+template <>
+struct ParamTraits<mozilla::gfx::VRDisplayInfo>
+ : public PlainOldDataSerializer<mozilla::gfx::VRDisplayInfo> {};
+
+template <>
+struct ParamTraits<mozilla::gfx::VRSubmitFrameResultInfo> {
+ typedef mozilla::gfx::VRSubmitFrameResultInfo paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ WriteParam(aWriter, aParam.mBase64Image);
+ WriteParam(aWriter, aParam.mFormat);
+ WriteParam(aWriter, aParam.mWidth);
+ WriteParam(aWriter, aParam.mHeight);
+ WriteParam(aWriter, aParam.mFrameNum);
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ if (!ReadParam(aReader, &(aResult->mBase64Image)) ||
+ !ReadParam(aReader, &(aResult->mFormat)) ||
+ !ReadParam(aReader, &(aResult->mWidth)) ||
+ !ReadParam(aReader, &(aResult->mHeight)) ||
+ !ReadParam(aReader, &(aResult->mFrameNum))) {
+ return false;
+ }
+
+ return true;
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::gfx::VRDisplayCapabilityFlags>
+ : public BitFlagsEnumSerializer<
+ mozilla::gfx::VRDisplayCapabilityFlags,
+ mozilla::gfx::VRDisplayCapabilityFlags::Cap_All> {};
+
+} // namespace IPC
+
+#endif // mozilla_gfx_vr_VRMessageUtils_h
diff --git a/gfx/vr/ipc/VRParent.cpp b/gfx/vr/ipc/VRParent.cpp
new file mode 100644
index 0000000000..234352ba08
--- /dev/null
+++ b/gfx/vr/ipc/VRParent.cpp
@@ -0,0 +1,191 @@
+/* -*- 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 "VRParent.h"
+#include "VRGPUParent.h"
+#include "gfxConfig.h"
+#include "nsDebugImpl.h"
+#include "nsThreadManager.h"
+#include "nsPrintfCString.h"
+
+#include "mozilla/dom/MemoryReportRequest.h"
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/ipc/CrashReporterClient.h"
+#include "mozilla/ipc/ProcessChild.h"
+#include "mozilla/ipc/ProcessUtils.h"
+#include "mozilla/Preferences.h"
+
+#if defined(XP_WIN)
+# include <process.h>
+# include "mozilla/gfx/DeviceManagerDx.h"
+#else
+# include <unistd.h>
+#endif
+
+namespace mozilla {
+namespace gfx {
+
+using mozilla::ipc::IPCResult;
+
+VRParent::VRParent() : mVRGPUParent(nullptr) {}
+
+IPCResult VRParent::RecvNewGPUVRManager(Endpoint<PVRGPUParent>&& aEndpoint) {
+ RefPtr<VRGPUParent> vrGPUParent =
+ VRGPUParent::CreateForGPU(std::move(aEndpoint));
+ if (!vrGPUParent) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ mVRGPUParent = std::move(vrGPUParent);
+ return IPC_OK();
+}
+
+IPCResult VRParent::RecvInit(nsTArray<GfxVarUpdate>&& vars,
+ const DevicePrefs& devicePrefs) {
+ Unused << SendInitComplete();
+
+ for (const auto& var : vars) {
+ gfxVars::ApplyUpdate(var);
+ }
+
+ // Inherit device preferences.
+ gfxConfig::Inherit(Feature::HW_COMPOSITING, devicePrefs.hwCompositing());
+ gfxConfig::Inherit(Feature::D3D11_COMPOSITING,
+ devicePrefs.d3d11Compositing());
+ gfxConfig::Inherit(Feature::OPENGL_COMPOSITING, devicePrefs.oglCompositing());
+ gfxConfig::Inherit(Feature::DIRECT2D, devicePrefs.useD2D1());
+
+#if defined(XP_WIN)
+ if (gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
+ DeviceManagerDx::Get()->CreateCompositorDevices();
+ }
+#endif
+ return IPC_OK();
+}
+
+IPCResult VRParent::RecvUpdateVar(const GfxVarUpdate& aUpdate) {
+ gfxVars::ApplyUpdate(aUpdate);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult VRParent::RecvPreferenceUpdate(const Pref& aPref) {
+ Preferences::SetPreference(aPref);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult VRParent::RecvOpenVRControllerActionPathToVR(
+ const nsCString& aPath) {
+ mOpenVRControllerAction = aPath;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult VRParent::RecvOpenVRControllerManifestPathToVR(
+ const VRControllerType& aType, const nsCString& aPath) {
+ mOpenVRControllerManifest.InsertOrUpdate(static_cast<uint32_t>(aType), aPath);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult VRParent::RecvRequestMemoryReport(
+ const uint32_t& aGeneration, const bool& aAnonymize,
+ const bool& aMinimizeMemoryUsage, const Maybe<FileDescriptor>& aDMDFile,
+ const RequestMemoryReportResolver& aResolver) {
+ MOZ_ASSERT(XRE_IsVRProcess());
+ nsPrintfCString processName("VR (pid %u)", (unsigned)getpid());
+
+ mozilla::dom::MemoryReportRequestClient::Start(
+ aGeneration, aAnonymize, aMinimizeMemoryUsage, aDMDFile, processName,
+ [&](const MemoryReport& aReport) {
+ Unused << SendAddMemoryReport(aReport);
+ },
+ aResolver);
+ return IPC_OK();
+}
+
+void VRParent::ActorDestroy(ActorDestroyReason aWhy) {
+ if (AbnormalShutdown == aWhy) {
+ NS_WARNING("Shutting down VR process early due to a crash!");
+ ipc::ProcessChild::QuickExit();
+ }
+ if (mVRGPUParent && !mVRGPUParent->IsClosed()) {
+ mVRGPUParent->Close();
+ }
+ mVRGPUParent = nullptr;
+
+#ifndef NS_FREE_PERMANENT_DATA
+ // No point in going through XPCOM shutdown because we don't keep persistent
+ // state.
+ ipc::ProcessChild::QuickExit();
+#endif
+
+#if defined(XP_WIN)
+ DeviceManagerDx::Shutdown();
+#endif
+ gfxVars::Shutdown();
+ gfxConfig::Shutdown();
+ ipc::CrashReporterClient::DestroySingleton();
+ // Only calling XRE_ShutdownChildProcess() at the child process
+ // instead of the main process. Otherwise, it will close all child processes
+ // that are spawned from the main process.
+ XRE_ShutdownChildProcess();
+}
+
+bool VRParent::Init(mozilla::ipc::UntypedEndpoint&& aEndpoint,
+ const char* aParentBuildID) {
+ // Initialize the thread manager before starting IPC. Otherwise, messages
+ // may be posted to the main thread and we won't be able to process them.
+ if (NS_WARN_IF(NS_FAILED(nsThreadManager::get().Init()))) {
+ return false;
+ }
+
+ // Now it's safe to start IPC.
+ if (NS_WARN_IF(!aEndpoint.Bind(this))) {
+ return false;
+ }
+
+ nsDebugImpl::SetMultiprocessMode("VR");
+
+ // This must be checked before any IPDL message, which may hit sentinel
+ // errors due to parent and content processes having different
+ // versions.
+ MessageChannel* channel = GetIPCChannel();
+ if (channel && !channel->SendBuildIDsMatchMessage(aParentBuildID)) {
+ // We need to quit this process if the buildID doesn't match the parent's.
+ // This can occur when an update occurred in the background.
+ ipc::ProcessChild::QuickExit();
+ }
+
+ // Init crash reporter support.
+ ipc::CrashReporterClient::InitSingleton(this);
+
+ gfxConfig::Init();
+ gfxVars::Initialize();
+#if defined(XP_WIN)
+ DeviceManagerDx::Init();
+#endif
+ if (NS_FAILED(NS_InitMinimalXPCOM())) {
+ return false;
+ }
+
+ mozilla::ipc::SetThisProcessName("VR Process");
+ return true;
+}
+
+bool VRParent::GetOpenVRControllerActionPath(nsCString* aPath) {
+ if (!mOpenVRControllerAction.IsEmpty()) {
+ *aPath = mOpenVRControllerAction;
+ return true;
+ }
+
+ return false;
+}
+
+bool VRParent::GetOpenVRControllerManifestPath(VRControllerType aType,
+ nsCString* aPath) {
+ return mOpenVRControllerManifest.Get(static_cast<uint32_t>(aType), aPath);
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/vr/ipc/VRParent.h b/gfx/vr/ipc/VRParent.h
new file mode 100644
index 0000000000..222ef7ace2
--- /dev/null
+++ b/gfx/vr/ipc/VRParent.h
@@ -0,0 +1,64 @@
+/* -*- 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/. */
+
+#ifndef GFX_VR_PARENT_H
+#define GFX_VR_PARENT_H
+
+#include "mozilla/gfx/PVRParent.h"
+#include "VRGPUParent.h"
+
+namespace mozilla {
+namespace gfx {
+
+class VRService;
+class VRSystemManagerExternal;
+
+class VRParent final : public PVRParent {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VRParent);
+
+ friend class PVRParent;
+
+ public:
+ explicit VRParent();
+
+ bool Init(mozilla::ipc::UntypedEndpoint&& aEndpoint,
+ const char* aParentBuildID);
+ virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+ bool GetOpenVRControllerActionPath(nsCString* aPath);
+ bool GetOpenVRControllerManifestPath(VRControllerType aType,
+ nsCString* aPath);
+
+ protected:
+ ~VRParent() = default;
+
+ mozilla::ipc::IPCResult RecvNewGPUVRManager(
+ Endpoint<PVRGPUParent>&& aEndpoint);
+ mozilla::ipc::IPCResult RecvInit(nsTArray<GfxVarUpdate>&& vars,
+ const DevicePrefs& devicePrefs);
+ mozilla::ipc::IPCResult RecvNotifyVsync(const TimeStamp& vsyncTimestamp);
+ mozilla::ipc::IPCResult RecvUpdateVar(const GfxVarUpdate& pref);
+ mozilla::ipc::IPCResult RecvPreferenceUpdate(const Pref& pref);
+ mozilla::ipc::IPCResult RecvOpenVRControllerActionPathToVR(
+ const nsCString& aPath);
+ mozilla::ipc::IPCResult RecvOpenVRControllerManifestPathToVR(
+ const VRControllerType& aType, const nsCString& aPath);
+ mozilla::ipc::IPCResult RecvRequestMemoryReport(
+ const uint32_t& generation, const bool& anonymize,
+ const bool& minimizeMemoryUsage,
+ const Maybe<ipc::FileDescriptor>& DMDFile,
+ const RequestMemoryReportResolver& aResolver);
+
+ private:
+ nsCString mOpenVRControllerAction;
+ nsTHashMap<nsUint32HashKey, nsCString> mOpenVRControllerManifest;
+ RefPtr<VRGPUParent> mVRGPUParent;
+ DISALLOW_COPY_AND_ASSIGN(VRParent);
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // GFX_VR_PARENT_H
diff --git a/gfx/vr/ipc/VRProcessChild.cpp b/gfx/vr/ipc/VRProcessChild.cpp
new file mode 100644
index 0000000000..be7dc89bd3
--- /dev/null
+++ b/gfx/vr/ipc/VRProcessChild.cpp
@@ -0,0 +1,49 @@
+/* -*- 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 "VRProcessChild.h"
+
+#include "mozilla/BackgroundHangMonitor.h"
+#include "mozilla/GeckoArgs.h"
+#include "mozilla/ipc/IOThreadChild.h"
+#include "mozilla/ipc/ProcessUtils.h"
+#include "mozilla/StaticPrefs_dom.h"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+using mozilla::ipc::IOThreadChild;
+
+StaticRefPtr<VRParent> sVRParent;
+
+VRProcessChild::~VRProcessChild() { sVRParent = nullptr; }
+
+/*static*/
+VRParent* VRProcessChild::GetVRParent() {
+ MOZ_ASSERT(sVRParent);
+ return sVRParent;
+}
+
+bool VRProcessChild::Init(int aArgc, char* aArgv[]) {
+ Maybe<const char*> parentBuildID =
+ geckoargs::sParentBuildID.Get(aArgc, aArgv);
+ if (parentBuildID.isNothing()) {
+ return false;
+ }
+
+ if (!ProcessChild::InitPrefs(aArgc, aArgv)) {
+ return false;
+ }
+
+ sVRParent = new VRParent();
+ sVRParent->Init(TakeInitialEndpoint(), *parentBuildID);
+
+ return true;
+}
+
+void VRProcessChild::CleanUp() {
+ sVRParent = nullptr;
+ NS_ShutdownXPCOM(nullptr);
+}
diff --git a/gfx/vr/ipc/VRProcessChild.h b/gfx/vr/ipc/VRProcessChild.h
new file mode 100644
index 0000000000..e3f275c9af
--- /dev/null
+++ b/gfx/vr/ipc/VRProcessChild.h
@@ -0,0 +1,39 @@
+/* -*- 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/. */
+
+#ifndef GFX_VR_PROCESS_CHILD_H
+#define GFX_VR_PROCESS_CHILD_H
+
+#include "mozilla/ipc/ProcessChild.h"
+#include "VRParent.h"
+
+namespace mozilla {
+namespace gfx {
+
+/**
+ * Contains the VRChild object that facilitates IPC communication to/from
+ * the instance of the VR library that is run in this process.
+ */
+class VRProcessChild final : public mozilla::ipc::ProcessChild {
+ protected:
+ typedef mozilla::ipc::ProcessChild ProcessChild;
+
+ public:
+ using ProcessChild::ProcessChild;
+ ~VRProcessChild();
+
+ // IPC channel for VR process talk to the parent process.
+ static VRParent* GetVRParent();
+
+ // ProcessChild functions.
+ virtual bool Init(int aArgc, char* aArgv[]) override;
+ virtual void CleanUp() override;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* GFX_VR_PROCESS_CHILD_H */
diff --git a/gfx/vr/ipc/VRProcessManager.cpp b/gfx/vr/ipc/VRProcessManager.cpp
new file mode 100644
index 0000000000..f3bc13bac0
--- /dev/null
+++ b/gfx/vr/ipc/VRProcessManager.cpp
@@ -0,0 +1,287 @@
+/* -*- 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 "VRProcessManager.h"
+
+#include "VRProcessParent.h"
+#include "VRChild.h"
+#include "VRGPUChild.h"
+#include "VRGPUParent.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/MemoryReportingProcess.h"
+#include "mozilla/Preferences.h"
+
+namespace mozilla {
+namespace gfx {
+
+static StaticAutoPtr<VRProcessManager> sSingleton;
+
+/* static */
+VRProcessManager* VRProcessManager::Get() { return sSingleton; }
+
+/* static */
+void VRProcessManager::Initialize() {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ if (sSingleton == nullptr) {
+ sSingleton = new VRProcessManager();
+ }
+}
+
+/* static */
+void VRProcessManager::Shutdown() { sSingleton = nullptr; }
+
+VRProcessManager::VRProcessManager() : mProcess(nullptr), mVRChild(nullptr) {
+ MOZ_COUNT_CTOR(VRProcessManager);
+
+ mObserver = new Observer(this);
+ nsContentUtils::RegisterShutdownObserver(mObserver);
+ Preferences::AddStrongObserver(mObserver, "");
+}
+
+VRProcessManager::~VRProcessManager() {
+ MOZ_COUNT_DTOR(VRProcessManager);
+
+ if (mObserver) {
+ nsContentUtils::UnregisterShutdownObserver(mObserver);
+ Preferences::RemoveObserver(mObserver, "");
+ mObserver = nullptr;
+ }
+
+ DestroyProcess();
+ // The VR process should have already been shut down.
+ MOZ_ASSERT(!mProcess);
+}
+
+void VRProcessManager::LaunchVRProcess() {
+ if (mProcess) {
+ return;
+ }
+
+ // The subprocess is launched asynchronously, so we wait for a callback to
+ // acquire the IPDL actor.
+ mProcess = new VRProcessParent(this);
+ if (!mProcess->Launch()) {
+ DisableVRProcess("Failed to launch VR process");
+ }
+}
+
+void VRProcessManager::DisableVRProcess(const char* aMessage) {
+ if (!StaticPrefs::dom_vr_process_enabled_AtStartup()) {
+ return;
+ }
+
+ DestroyProcess();
+}
+
+void VRProcessManager::DestroyProcess() {
+ if (!mProcess) {
+ return;
+ }
+
+ mProcess->Shutdown();
+ mProcess = nullptr;
+ mVRChild = nullptr;
+
+ CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::VRProcessStatus,
+ "Destroyed"_ns);
+}
+
+bool VRProcessManager::EnsureVRReady() {
+ if (mProcess && !mProcess->IsConnected()) {
+ if (!mProcess->WaitForLaunch()) {
+ // If this fails, we should have fired OnProcessLaunchComplete and
+ // removed the process.
+ MOZ_ASSERT(!mProcess && !mVRChild);
+ return false;
+ }
+ }
+
+ if (mVRChild) {
+ if (mVRChild->EnsureVRReady()) {
+ return true;
+ }
+
+ // If the initialization above fails, we likely have a GPU process teardown
+ // waiting in our message queue (or will soon). We need to ensure we don't
+ // restart it later because if we fail here, our callers assume they should
+ // fall back to a combined UI/GPU process. This also ensures our internal
+ // state is consistent (e.g. process token is reset).
+ DisableVRProcess("Failed to initialize VR process");
+ }
+
+ return false;
+}
+
+void VRProcessManager::OnProcessLaunchComplete(VRProcessParent* aParent) {
+ MOZ_ASSERT(mProcess && mProcess == aParent);
+
+ mVRChild = mProcess->GetActor();
+
+ if (!mProcess->IsConnected()) {
+ DestroyProcess();
+ return;
+ }
+
+ // Flush any pref updates that happened during launch and weren't
+ // included in the blobs set up in LaunchGPUProcess.
+ for (const mozilla::dom::Pref& pref : mQueuedPrefs) {
+ Unused << NS_WARN_IF(!mVRChild->SendPreferenceUpdate(pref));
+ }
+ mQueuedPrefs.Clear();
+
+ CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::VRProcessStatus,
+ "Running"_ns);
+}
+
+void VRProcessManager::OnProcessUnexpectedShutdown(VRProcessParent* aParent) {
+ MOZ_ASSERT(mProcess && mProcess == aParent);
+
+ DestroyProcess();
+}
+
+bool VRProcessManager::CreateGPUBridges(
+ base::ProcessId aOtherProcess,
+ mozilla::ipc::Endpoint<PVRGPUChild>* aOutVRBridge) {
+ if (!CreateGPUVRManager(aOtherProcess, aOutVRBridge)) {
+ return false;
+ }
+ return true;
+}
+
+bool VRProcessManager::CreateGPUVRManager(
+ base::ProcessId aOtherProcess,
+ mozilla::ipc::Endpoint<PVRGPUChild>* aOutEndpoint) {
+ if (mProcess && !mProcess->IsConnected()) {
+ NS_WARNING("VR process haven't connected with the parent process yet");
+ return false;
+ }
+
+ base::ProcessId vrparentPid = mProcess
+ ? mProcess->OtherPid() // VR process id.
+ : base::GetCurrentProcId();
+
+ ipc::Endpoint<PVRGPUParent> vrparentPipe;
+ ipc::Endpoint<PVRGPUChild> vrchildPipe;
+ nsresult rv = PVRGPU::CreateEndpoints(vrparentPid, // vr process id
+ aOtherProcess, // gpu process id
+ &vrparentPipe, &vrchildPipe);
+
+ if (NS_FAILED(rv)) {
+ gfxCriticalNote << "Could not create gpu-vr bridge: " << hexa(int(rv));
+ return false;
+ }
+
+ // Bind vr-gpu pipe to VRParent and make a PVRGPU connection.
+ VRChild* vrChild = mProcess->GetActor();
+ vrChild->SendNewGPUVRManager(std::move(vrparentPipe));
+
+ *aOutEndpoint = std::move(vrchildPipe);
+ return true;
+}
+
+NS_IMPL_ISUPPORTS(VRProcessManager::Observer, nsIObserver);
+
+VRProcessManager::Observer::Observer(VRProcessManager* aManager)
+ : mManager(aManager) {}
+
+NS_IMETHODIMP
+VRProcessManager::Observer::Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData) {
+ if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
+ mManager->OnXPCOMShutdown();
+ } else if (!strcmp(aTopic, "nsPref:changed")) {
+ mManager->OnPreferenceChange(aData);
+ }
+ return NS_OK;
+}
+
+void VRProcessManager::CleanShutdown() { DestroyProcess(); }
+
+void VRProcessManager::OnXPCOMShutdown() {
+ if (mObserver) {
+ nsContentUtils::UnregisterShutdownObserver(mObserver);
+ Preferences::RemoveObserver(mObserver, "");
+ mObserver = nullptr;
+ }
+
+ CleanShutdown();
+}
+
+void VRProcessManager::OnPreferenceChange(const char16_t* aData) {
+ // We know prefs are ASCII here.
+ NS_LossyConvertUTF16toASCII strData(aData);
+
+ mozilla::dom::Pref pref(strData, /* isLocked */ false,
+ /* isSanitized */ false, Nothing(), Nothing());
+
+ Preferences::GetPreference(&pref, GeckoProcessType_VR,
+ /* remoteType */ ""_ns);
+ if (!!mVRChild) {
+ MOZ_ASSERT(mQueuedPrefs.IsEmpty());
+ mVRChild->SendPreferenceUpdate(pref);
+ } else {
+ mQueuedPrefs.AppendElement(pref);
+ }
+}
+
+VRChild* VRProcessManager::GetVRChild() { return mProcess->GetActor(); }
+
+class VRMemoryReporter : public MemoryReportingProcess {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VRMemoryReporter, override)
+
+ bool IsAlive() const override {
+ if (VRProcessManager* vpm = VRProcessManager::Get()) {
+ return !!vpm->GetVRChild();
+ }
+ return false;
+ }
+
+ bool SendRequestMemoryReport(
+ const uint32_t& aGeneration, const bool& aAnonymize,
+ const bool& aMinimizeMemoryUsage,
+ const Maybe<ipc::FileDescriptor>& aDMDFile) override {
+ VRChild* child = GetChild();
+ if (!child) {
+ return false;
+ }
+
+ return child->SendRequestMemoryReport(aGeneration, aAnonymize,
+ aMinimizeMemoryUsage, aDMDFile);
+ }
+
+ int32_t Pid() const override {
+ if (VRChild* child = GetChild()) {
+ return (int32_t)child->OtherPid();
+ }
+ return 0;
+ }
+
+ private:
+ VRChild* GetChild() const {
+ if (VRProcessManager* vpm = VRProcessManager::Get()) {
+ if (VRChild* child = vpm->GetVRChild()) {
+ return child;
+ }
+ }
+ return nullptr;
+ }
+
+ protected:
+ ~VRMemoryReporter() = default;
+};
+
+RefPtr<MemoryReportingProcess> VRProcessManager::GetProcessMemoryReporter() {
+ if (!EnsureVRReady()) {
+ return nullptr;
+ }
+ return new VRMemoryReporter();
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/vr/ipc/VRProcessManager.h b/gfx/vr/ipc/VRProcessManager.h
new file mode 100644
index 0000000000..030045d300
--- /dev/null
+++ b/gfx/vr/ipc/VRProcessManager.h
@@ -0,0 +1,95 @@
+/* -*- 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/. */
+#ifndef GFX_VR_PROCESS_MANAGER_H
+#define GFX_VR_PROCESS_MANAGER_H
+
+#include "VRProcessParent.h"
+
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "nsIObserver.h"
+
+namespace mozilla {
+class MemoryReportingProcess;
+namespace ipc {
+template <typename T>
+class Endpoint;
+}
+namespace gfx {
+
+class VRManagerChild;
+class PVRGPUChild;
+class VRChild;
+
+// The VRProcessManager is a singleton responsible for creating VR-bound
+// objects that may live in another process.
+class VRProcessManager final : public VRProcessParent::Listener {
+ public:
+ static VRProcessManager* Get();
+ static void Initialize();
+ static void Shutdown();
+
+ ~VRProcessManager();
+
+ // If not using a VR process, launch a new VR process asynchronously.
+ void LaunchVRProcess();
+
+ // Ensure that VR-bound methods can be used. If no VR process is being
+ // used, or one is launched and ready, this function returns immediately.
+ // Otherwise it blocks until the VR process has finished launching.
+ bool EnsureVRReady();
+
+ bool CreateGPUBridges(base::ProcessId aOtherProcess,
+ mozilla::ipc::Endpoint<PVRGPUChild>* aOutVRBridge);
+
+ VRChild* GetVRChild();
+ // If a VR process is present, create a MemoryReportingProcess object.
+ // Otherwise, return null.
+ RefPtr<MemoryReportingProcess> GetProcessMemoryReporter();
+
+ virtual void OnProcessLaunchComplete(VRProcessParent* aParent) override;
+ virtual void OnProcessUnexpectedShutdown(VRProcessParent* aParent) override;
+
+ private:
+ VRProcessManager();
+
+ DISALLOW_COPY_AND_ASSIGN(VRProcessManager);
+
+ bool CreateGPUVRManager(base::ProcessId aOtherProcess,
+ mozilla::ipc::Endpoint<PVRGPUChild>* aOutEndpoint);
+ void OnXPCOMShutdown();
+ void OnPreferenceChange(const char16_t* aData);
+ void CleanShutdown();
+ void DestroyProcess();
+
+ // Permanently disable the VR process and record a message why.
+ void DisableVRProcess(const char* aMessage);
+
+ class Observer final : public nsIObserver {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+ explicit Observer(VRProcessManager* aManager);
+
+ protected:
+ ~Observer() = default;
+
+ VRProcessManager* mManager;
+ };
+ friend class Observer;
+
+ RefPtr<Observer> mObserver;
+ VRProcessParent* mProcess;
+ VRChild* mVRChild;
+ // Collects any pref changes that occur during process launch (after
+ // the initial map is passed in command-line arguments) to be sent
+ // when the process can receive IPC messages.
+ nsTArray<mozilla::dom::Pref> mQueuedPrefs;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // GFX_VR_PROCESS_MANAGER_H
diff --git a/gfx/vr/ipc/VRProcessParent.cpp b/gfx/vr/ipc/VRProcessParent.cpp
new file mode 100644
index 0000000000..cdc50c7dd0
--- /dev/null
+++ b/gfx/vr/ipc/VRProcessParent.cpp
@@ -0,0 +1,248 @@
+/* -*- 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 "VRProcessParent.h"
+#include "VRGPUChild.h"
+#include "VRProcessManager.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/MemoryReportRequest.h"
+#include "mozilla/gfx/GPUProcessManager.h"
+#include "mozilla/gfx/GPUChild.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/ipc/ProcessChild.h"
+#include "mozilla/ipc/ProcessUtils.h"
+#include "mozilla/ipc/ProtocolTypes.h"
+#include "mozilla/ipc/ProtocolUtils.h" // for IToplevelProtocol
+#include "mozilla/Preferences.h"
+#include "mozilla/StaticPrefs_dom.h"
+#include "mozilla/TimeStamp.h" // for TimeStamp
+#include "mozilla/Unused.h"
+#include "VRChild.h"
+#include "VRThread.h"
+
+#include "nsAppRunner.h" // for IToplevelProtocol
+
+using std::string;
+using std::vector;
+
+using namespace mozilla::ipc;
+
+namespace mozilla {
+namespace gfx {
+
+VRProcessParent::VRProcessParent(Listener* aListener)
+ : GeckoChildProcessHost(GeckoProcessType_VR),
+ mTaskFactory(this),
+ mListener(aListener),
+ mLaunchPhase(LaunchPhase::Unlaunched),
+ mChannelClosed(false),
+ mShutdownRequested(false) {
+ MOZ_COUNT_CTOR(VRProcessParent);
+}
+
+VRProcessParent::~VRProcessParent() { MOZ_COUNT_DTOR(VRProcessParent); }
+
+bool VRProcessParent::Launch() {
+ MOZ_ASSERT(mLaunchPhase == LaunchPhase::Unlaunched);
+ MOZ_ASSERT(!mVRChild);
+ mLaunchThread = NS_GetCurrentThread();
+
+ mLaunchPhase = LaunchPhase::Waiting;
+
+ std::vector<std::string> extraArgs;
+ ProcessChild::AddPlatformBuildID(extraArgs);
+
+ mPrefSerializer = MakeUnique<ipc::SharedPreferenceSerializer>();
+ if (!mPrefSerializer->SerializeToSharedMemory(GeckoProcessType_VR,
+ /* remoteType */ ""_ns)) {
+ return false;
+ }
+ mPrefSerializer->AddSharedPrefCmdLineArgs(*this, extraArgs);
+
+ if (!GeckoChildProcessHost::AsyncLaunch(extraArgs)) {
+ mLaunchPhase = LaunchPhase::Complete;
+ mPrefSerializer = nullptr;
+ return false;
+ }
+ return true;
+}
+
+bool VRProcessParent::WaitForLaunch() {
+ if (mLaunchPhase == LaunchPhase::Complete) {
+ return !!mVRChild;
+ }
+
+ int32_t timeoutMs =
+ StaticPrefs::dom_vr_process_startup_timeout_ms_AtStartup();
+
+ // If one of the following environment variables are set we can effectively
+ // ignore the timeout - as we can guarantee the compositor process will be
+ // terminated
+ if (PR_GetEnv("MOZ_DEBUG_CHILD_PROCESS") ||
+ PR_GetEnv("MOZ_DEBUG_CHILD_PAUSE")) {
+ timeoutMs = 0;
+ }
+
+ // Our caller expects the connection to be finished after we return, so we
+ // immediately set up the IPDL actor and fire callbacks. The IO thread will
+ // still dispatch a notification to the main thread - we'll just ignore it.
+ bool result = GeckoChildProcessHost::WaitUntilConnected(timeoutMs);
+ result &= InitAfterConnect(result);
+ return result;
+}
+
+void VRProcessParent::Shutdown() {
+ MOZ_ASSERT(!mShutdownRequested);
+ mListener = nullptr;
+
+ if (mVRChild) {
+ // The channel might already be closed if we got here unexpectedly.
+ if (!mChannelClosed) {
+ mVRChild->Close();
+ }
+ // OnChannelClosed uses this to check if the shutdown was expected or
+ // unexpected.
+ mShutdownRequested = true;
+
+#ifndef NS_FREE_PERMANENT_DATA
+ // No need to communicate shutdown, the VR process doesn't need to
+ // communicate anything back.
+ KillHard("NormalShutdown");
+#endif
+
+ // If we're shutting down unexpectedly, we're in the middle of handling an
+ // ActorDestroy for PVRChild, which is still on the stack. We'll return
+ // back to OnChannelClosed.
+ //
+ // Otherwise, we'll wait for OnChannelClose to be called whenever PVRChild
+ // acknowledges shutdown.
+ return;
+ }
+
+ DestroyProcess();
+}
+
+void VRProcessParent::DestroyProcess() {
+ if (mLaunchThread) {
+ // Cancel all tasks. We don't want anything triggering after our caller
+ // expects this to go away.
+ {
+ MonitorAutoLock lock(mMonitor);
+ mTaskFactory.RevokeAll();
+ }
+
+ mLaunchThread->Dispatch(NS_NewRunnableFunction("DestroyProcessRunnable",
+ [this] { Destroy(); }));
+ }
+}
+
+bool VRProcessParent::InitAfterConnect(bool aSucceeded) {
+ MOZ_ASSERT(mLaunchPhase == LaunchPhase::Waiting);
+ MOZ_ASSERT(!mVRChild);
+
+ mLaunchPhase = LaunchPhase::Complete;
+ mPrefSerializer = nullptr;
+
+ if (aSucceeded) {
+ GPUChild* gpuChild = GPUProcessManager::Get()->GetGPUChild();
+ if (!gpuChild) {
+ NS_WARNING(
+ "GPU process haven't connected with the parent process yet"
+ "when creating VR process.");
+ return false;
+ }
+
+ if (!StaticPrefs::dom_vr_enabled() &&
+ !StaticPrefs::dom_vr_webxr_enabled()) {
+ NS_WARNING("VR is not enabled when trying to create a VRChild");
+ return false;
+ }
+
+ mVRChild = MakeUnique<VRChild>(this);
+
+ DebugOnly<bool> rv = TakeInitialEndpoint().Bind(mVRChild.get());
+ MOZ_ASSERT(rv);
+
+ mVRChild->Init();
+
+ if (mListener) {
+ mListener->OnProcessLaunchComplete(this);
+ }
+
+ // Make vr-gpu process connection
+ Endpoint<PVRGPUChild> vrGPUBridge;
+ VRProcessManager* vpm = VRProcessManager::Get();
+ DebugOnly<bool> opened =
+ vpm->CreateGPUBridges(gpuChild->OtherPid(), &vrGPUBridge);
+ MOZ_ASSERT(opened);
+
+ Unused << gpuChild->SendInitVR(std::move(vrGPUBridge));
+ }
+
+ return true;
+}
+
+void VRProcessParent::KillHard(const char* aReason) {
+ ProcessHandle handle = GetChildProcessHandle();
+ if (!base::KillProcess(handle, base::PROCESS_END_KILLED_BY_USER)) {
+ NS_WARNING("failed to kill subprocess!");
+ }
+
+ SetAlreadyDead();
+}
+
+void VRProcessParent::OnChannelError() {
+ MOZ_ASSERT(false, "VR process channel error.");
+}
+
+void VRProcessParent::OnChannelConnected(base::ProcessId peer_pid) {
+ MOZ_ASSERT(!NS_IsMainThread());
+
+ GeckoChildProcessHost::OnChannelConnected(peer_pid);
+
+ // Post a task to the main thread. Take the lock because mTaskFactory is not
+ // thread-safe.
+ RefPtr<Runnable> runnable;
+ {
+ MonitorAutoLock lock(mMonitor);
+ runnable = mTaskFactory.NewRunnableMethod(
+ &VRProcessParent::OnChannelConnectedTask);
+ }
+ NS_DispatchToMainThread(runnable);
+}
+
+void VRProcessParent::OnChannelConnectedTask() {
+ if (mLaunchPhase == LaunchPhase::Waiting) {
+ InitAfterConnect(true);
+ }
+}
+
+void VRProcessParent::OnChannelErrorTask() {
+ if (mLaunchPhase == LaunchPhase::Waiting) {
+ InitAfterConnect(false);
+ }
+}
+
+void VRProcessParent::OnChannelClosed() {
+ mChannelClosed = true;
+ if (!mShutdownRequested && mListener) {
+ // This is an unclean shutdown. Notify we're going away.
+ mListener->OnProcessUnexpectedShutdown(this);
+ } else {
+ DestroyProcess();
+ }
+
+ // Release the actor.
+ VRChild::Destroy(std::move(mVRChild));
+ MOZ_ASSERT(!mVRChild);
+}
+
+base::ProcessId VRProcessParent::OtherPid() { return mVRChild->OtherPid(); }
+
+bool VRProcessParent::IsConnected() const { return !!mVRChild; }
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/vr/ipc/VRProcessParent.h b/gfx/vr/ipc/VRProcessParent.h
new file mode 100644
index 0000000000..75c65e594e
--- /dev/null
+++ b/gfx/vr/ipc/VRProcessParent.h
@@ -0,0 +1,87 @@
+/* -*- 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/. */
+
+#ifndef GFX_VR_PROCESS_PARENT_H
+#define GFX_VR_PROCESS_PARENT_H
+
+#include "mozilla/UniquePtr.h"
+
+#include "mozilla/ipc/GeckoChildProcessHost.h"
+#include "mozilla/ipc/TaskFactory.h"
+
+namespace mozilla {
+namespace ipc {
+class SharedPreferenceSerializer;
+}
+namespace gfx {
+
+class VRChild;
+
+class VRProcessParent final : public mozilla::ipc::GeckoChildProcessHost {
+ public:
+ class Listener {
+ public:
+ virtual void OnProcessLaunchComplete(VRProcessParent* aParent) {}
+
+ // Follow GPU and RDD process manager, adding this to avoid
+ // unexpectedly shutdown or had its connection severed.
+ // This is not called if an error occurs after calling Shutdown().
+ virtual void OnProcessUnexpectedShutdown(VRProcessParent* aParent) {}
+ };
+
+ explicit VRProcessParent(Listener* aListener);
+
+ // Launch the subprocess asynchronously. On failure, false is returned.
+ // Otherwise, true is returned, and the OnProcessLaunchComplete listener
+ // callback will be invoked either when a connection has been established, or
+ // if a connection could not be established due to an asynchronous error.
+ bool Launch();
+ // If the process is being launched, block until it has launched and
+ // connected. If a launch task is pending, it will fire immediately.
+ //
+ // Returns true if the process is successfully connected; false otherwise.
+ bool WaitForLaunch();
+ void Shutdown();
+ void DestroyProcess();
+ bool CanShutdown() override { return true; }
+
+ void OnChannelError() override;
+ void OnChannelConnected(base::ProcessId peer_pid) override;
+ void OnChannelConnectedTask();
+ void OnChannelErrorTask();
+ void OnChannelClosed();
+ bool IsConnected() const;
+
+ base::ProcessId OtherPid();
+ VRChild* GetActor() const { return mVRChild.get(); }
+ // Return a unique id for this process, guaranteed not to be shared with any
+ // past or future instance of VRProcessParent.
+ uint64_t GetProcessToken() const;
+
+ private:
+ ~VRProcessParent();
+
+ DISALLOW_COPY_AND_ASSIGN(VRProcessParent);
+
+ bool InitAfterConnect(bool aSucceeded);
+ void KillHard(const char* aReason);
+
+ UniquePtr<VRChild> mVRChild;
+ mozilla::ipc::TaskFactory<VRProcessParent> mTaskFactory;
+ nsCOMPtr<nsIThread> mLaunchThread;
+ Listener* mListener;
+
+ enum class LaunchPhase { Unlaunched, Waiting, Complete };
+ LaunchPhase mLaunchPhase;
+ bool mChannelClosed;
+ bool mShutdownRequested;
+ UniquePtr<mozilla::ipc::SharedPreferenceSerializer> mPrefSerializer;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // ifndef GFX_VR_PROCESS_PARENT_H