summaryrefslogtreecommitdiffstats
path: root/gfx/vr
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /gfx/vr
parentInitial commit. (diff)
downloadfirefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz
firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--gfx/vr/FxROutputHandler.cpp96
-rw-r--r--gfx/vr/FxROutputHandler.h30
-rw-r--r--gfx/vr/FxRWindowManager.cpp50
-rw-r--r--gfx/vr/FxRWindowManager.h31
-rw-r--r--gfx/vr/VRDisplayClient.cpp665
-rw-r--r--gfx/vr/VRDisplayClient.h103
-rw-r--r--gfx/vr/VRDisplayPresentation.cpp155
-rw-r--r--gfx/vr/VRDisplayPresentation.h47
-rw-r--r--gfx/vr/VRManager.cpp1542
-rw-r--r--gfx/vr/VRManager.h194
-rw-r--r--gfx/vr/VRPuppetCommandBuffer.cpp515
-rw-r--r--gfx/vr/VRPuppetCommandBuffer.h236
-rw-r--r--gfx/vr/VRServiceHost.cpp327
-rw-r--r--gfx/vr/VRServiceHost.h93
-rw-r--r--gfx/vr/VRShMem.cpp736
-rw-r--r--gfx/vr/VRShMem.h105
-rw-r--r--gfx/vr/VRThread.cpp117
-rw-r--r--gfx/vr/VRThread.h46
-rw-r--r--gfx/vr/components.conf15
-rw-r--r--gfx/vr/external_api/moz_external_vr.h670
-rw-r--r--gfx/vr/gfxVR.cpp91
-rw-r--r--gfx/vr/gfxVR.h162
-rw-r--r--gfx/vr/gfxVRMutex.h63
-rw-r--r--gfx/vr/ipc/PVR.ipdl46
-rw-r--r--gfx/vr/ipc/PVRGPU.ipdl24
-rw-r--r--gfx/vr/ipc/PVRLayer.ipdl31
-rw-r--r--gfx/vr/ipc/PVRManager.ipdl84
-rw-r--r--gfx/vr/ipc/VRChild.cpp214
-rw-r--r--gfx/vr/ipc/VRChild.h63
-rw-r--r--gfx/vr/ipc/VRGPUChild.cpp81
-rw-r--r--gfx/vr/ipc/VRGPUChild.h43
-rw-r--r--gfx/vr/ipc/VRGPUParent.cpp112
-rw-r--r--gfx/vr/ipc/VRGPUParent.h52
-rw-r--r--gfx/vr/ipc/VRLayerChild.cpp146
-rw-r--r--gfx/vr/ipc/VRLayerChild.h76
-rw-r--r--gfx/vr/ipc/VRLayerParent.cpp54
-rw-r--r--gfx/vr/ipc/VRLayerParent.h46
-rw-r--r--gfx/vr/ipc/VRManagerChild.cpp637
-rw-r--r--gfx/vr/ipc/VRManagerChild.h200
-rw-r--r--gfx/vr/ipc/VRManagerParent.cpp299
-rw-r--r--gfx/vr/ipc/VRManagerParent.h122
-rw-r--r--gfx/vr/ipc/VRMessageUtils.h74
-rw-r--r--gfx/vr/ipc/VRParent.cpp191
-rw-r--r--gfx/vr/ipc/VRParent.h64
-rw-r--r--gfx/vr/ipc/VRProcessChild.cpp84
-rw-r--r--gfx/vr/ipc/VRProcessChild.h42
-rw-r--r--gfx/vr/ipc/VRProcessManager.cpp289
-rw-r--r--gfx/vr/ipc/VRProcessManager.h95
-rw-r--r--gfx/vr/ipc/VRProcessParent.cpp243
-rw-r--r--gfx/vr/ipc/VRProcessParent.h87
-rw-r--r--gfx/vr/moz.build112
-rw-r--r--gfx/vr/nsFxrCommandLineHandler.cpp139
-rw-r--r--gfx/vr/nsFxrCommandLineHandler.h32
-rw-r--r--gfx/vr/service/OSVRSession.cpp511
-rw-r--r--gfx/vr/service/OSVRSession.h76
-rw-r--r--gfx/vr/service/OculusSession.cpp1526
-rw-r--r--gfx/vr/service/OculusSession.h114
-rw-r--r--gfx/vr/service/OpenVRControllerMapper.cpp89
-rw-r--r--gfx/vr/service/OpenVRControllerMapper.h96
-rw-r--r--gfx/vr/service/OpenVRCosmosMapper.cpp51
-rw-r--r--gfx/vr/service/OpenVRCosmosMapper.h26
-rw-r--r--gfx/vr/service/OpenVRDefaultMapper.cpp70
-rw-r--r--gfx/vr/service/OpenVRDefaultMapper.h26
-rw-r--r--gfx/vr/service/OpenVRKnucklesMapper.cpp62
-rw-r--r--gfx/vr/service/OpenVRKnucklesMapper.h26
-rw-r--r--gfx/vr/service/OpenVRSession.cpp1480
-rw-r--r--gfx/vr/service/OpenVRSession.h112
-rw-r--r--gfx/vr/service/OpenVRViveMapper.cpp43
-rw-r--r--gfx/vr/service/OpenVRViveMapper.h26
-rw-r--r--gfx/vr/service/OpenVRWMRMapper.cpp51
-rw-r--r--gfx/vr/service/OpenVRWMRMapper.h26
-rw-r--r--gfx/vr/service/PuppetSession.cpp124
-rw-r--r--gfx/vr/service/PuppetSession.h58
-rw-r--r--gfx/vr/service/VRService.cpp418
-rw-r--r--gfx/vr/service/VRService.h92
-rw-r--r--gfx/vr/service/VRSession.cpp196
-rw-r--r--gfx/vr/service/VRSession.h94
-rw-r--r--gfx/vr/service/binding/OpenVRCosmosBinding.h204
-rw-r--r--gfx/vr/service/binding/OpenVRKnucklesBinding.h300
-rw-r--r--gfx/vr/service/binding/OpenVRViveBinding.h174
-rw-r--r--gfx/vr/service/binding/OpenVRWMRBinding.h192
-rw-r--r--gfx/vr/service/moz.build48
-rw-r--r--gfx/vr/service/oculus/ovr_capi_dynamic.h984
-rw-r--r--gfx/vr/service/openvr/LICENSE27
-rw-r--r--gfx/vr/service/openvr/README.md13
-rw-r--r--gfx/vr/service/openvr/README.mozilla80
-rw-r--r--gfx/vr/service/openvr/headers/openvr.h5509
-rw-r--r--gfx/vr/service/openvr/moz.build62
-rw-r--r--gfx/vr/service/openvr/src/README39
-rw-r--r--gfx/vr/service/openvr/src/dirtools_public.cpp101
-rw-r--r--gfx/vr/service/openvr/src/dirtools_public.h17
-rw-r--r--gfx/vr/service/openvr/src/envvartools_public.cpp88
-rw-r--r--gfx/vr/service/openvr/src/envvartools_public.h8
-rw-r--r--gfx/vr/service/openvr/src/hmderrors_public.cpp326
-rw-r--r--gfx/vr/service/openvr/src/hmderrors_public.h6
-rw-r--r--gfx/vr/service/openvr/src/ivrclientcore.h35
-rw-r--r--gfx/vr/service/openvr/src/openvr_api_public.cpp352
-rw-r--r--gfx/vr/service/openvr/src/pathtools_public.cpp901
-rw-r--r--gfx/vr/service/openvr/src/pathtools_public.h150
-rw-r--r--gfx/vr/service/openvr/src/sharedlibtools_public.cpp43
-rw-r--r--gfx/vr/service/openvr/src/sharedlibtools_public.h10
-rw-r--r--gfx/vr/service/openvr/src/strtools_public.cpp571
-rw-r--r--gfx/vr/service/openvr/src/strtools_public.h156
-rw-r--r--gfx/vr/service/openvr/src/vrpathregistry_public.cpp442
-rw-r--r--gfx/vr/service/openvr/src/vrpathregistry_public.h45
-rw-r--r--gfx/vr/service/osvr/ClientKit/ClientKitC.h37
-rw-r--r--gfx/vr/service/osvr/ClientKit/ContextC.h96
-rw-r--r--gfx/vr/service/osvr/ClientKit/DisplayC.h506
-rw-r--r--gfx/vr/service/osvr/ClientKit/Export.h140
-rw-r--r--gfx/vr/service/osvr/ClientKit/InterfaceC.h74
-rw-r--r--gfx/vr/service/osvr/ClientKit/InterfaceCallbackC.h77
-rw-r--r--gfx/vr/service/osvr/ClientKit/InterfaceStateC.h79
-rw-r--r--gfx/vr/service/osvr/ClientKit/SystemCallbackC.h47
-rw-r--r--gfx/vr/service/osvr/ClientKit/TransformsC.h75
-rw-r--r--gfx/vr/service/osvr/Util/APIBaseC.h50
-rw-r--r--gfx/vr/service/osvr/Util/AnnotationMacrosC.h232
-rw-r--r--gfx/vr/service/osvr/Util/BoolC.h59
-rw-r--r--gfx/vr/service/osvr/Util/ChannelCountC.h57
-rw-r--r--gfx/vr/service/osvr/Util/ClientCallbackTypesC.h140
-rw-r--r--gfx/vr/service/osvr/Util/ClientOpaqueTypesC.h69
-rw-r--r--gfx/vr/service/osvr/Util/ClientReportTypesC.h348
-rw-r--r--gfx/vr/service/osvr/Util/Export.h139
-rw-r--r--gfx/vr/service/osvr/Util/ImagingReportTypesC.h91
-rw-r--r--gfx/vr/service/osvr/Util/MatrixConventionsC.h190
-rw-r--r--gfx/vr/service/osvr/Util/PlatformConfig.h87
-rw-r--r--gfx/vr/service/osvr/Util/Pose3C.h70
-rw-r--r--gfx/vr/service/osvr/Util/QuaternionC.h92
-rw-r--r--gfx/vr/service/osvr/Util/QuatlibInteropC.h84
-rw-r--r--gfx/vr/service/osvr/Util/RadialDistortionParametersC.h62
-rw-r--r--gfx/vr/service/osvr/Util/RenderingTypesC.h134
-rw-r--r--gfx/vr/service/osvr/Util/ReturnCodesC.h57
-rw-r--r--gfx/vr/service/osvr/Util/StdInt.h42
-rw-r--r--gfx/vr/service/osvr/Util/TimeValueC.h267
-rw-r--r--gfx/vr/service/osvr/Util/Vec2C.h86
-rw-r--r--gfx/vr/service/osvr/Util/Vec3C.h88
-rw-r--r--gfx/vr/vrhost/moz.build40
-rw-r--r--gfx/vr/vrhost/testhost/moz.build12
-rw-r--r--gfx/vr/vrhost/testhost/testhost.cpp43
-rw-r--r--gfx/vr/vrhost/vrhost.def15
-rw-r--r--gfx/vr/vrhost/vrhostapi.cpp374
-rw-r--r--gfx/vr/vrhost/vrhostex.h33
-rw-r--r--gfx/vr/vrhost/vrhostnightly.def23
-rw-r--r--gfx/vr/vrhost/vrhosttest.cpp370
143 files changed, 30632 insertions, 0 deletions
diff --git a/gfx/vr/FxROutputHandler.cpp b/gfx/vr/FxROutputHandler.cpp
new file mode 100644
index 0000000000..3bf2fb62ce
--- /dev/null
+++ b/gfx/vr/FxROutputHandler.cpp
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 2; 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 "FxROutputHandler.h"
+#include "mozilla/Assertions.h"
+#include "moz_external_vr.h"
+#include "VRShMem.h"
+
+// TryInitialize is responsible for associating this output handler with the
+// calling window's swapchain for subsequent updates. This also creates a
+// texture that can be shared across processes and updates VRShMem with the
+// shared texture handle.
+// See nsFxrCommandLineHandler::Handle for more information about the
+// bootstrap process.
+bool FxROutputHandler::TryInitialize(IDXGISwapChain* aSwapChain,
+ ID3D11Device* aDevice) {
+ if (mSwapChain == nullptr) {
+ RefPtr<ID3D11Texture2D> texOrig = nullptr;
+ HRESULT hr =
+ aSwapChain->GetBuffer(0, IID_ID3D11Texture2D, getter_AddRefs(texOrig));
+ if (hr != S_OK) {
+ return false;
+ }
+
+ // Create shareable texture, which will be copied to
+ D3D11_TEXTURE2D_DESC descOrig = {0};
+ texOrig->GetDesc(&descOrig);
+ descOrig.MiscFlags |= D3D11_RESOURCE_MISC_SHARED;
+ hr = aDevice->CreateTexture2D(&descOrig, nullptr,
+ mTexCopy.StartAssignment());
+ if (hr != S_OK) {
+ return false;
+ }
+
+ // Now, share the texture to a handle that can be marshaled to another
+ // process
+ HANDLE hCopy = nullptr;
+ RefPtr<IDXGIResource> texResource;
+ hr = mTexCopy->QueryInterface(IID_IDXGIResource,
+ getter_AddRefs(texResource));
+ if (hr != S_OK) {
+ return false;
+ }
+
+ hr = texResource->GetSharedHandle(&hCopy);
+ if (hr != S_OK) {
+ return false;
+ }
+
+ // The texture is successfully created and shared, so cache a
+ // pointer to the swapchain to indicate this success.
+ mSwapChain = aSwapChain;
+
+ // Finally, marshal the shared texture handle via VRShMem
+ mozilla::gfx::VRShMem shmem(nullptr, true /*aRequiresMutex*/);
+ if (shmem.JoinShMem()) {
+ mozilla::gfx::VRWindowState windowState = {0};
+ shmem.PullWindowState(windowState);
+
+ // The CLH should have populated hwndFx first
+ MOZ_ASSERT(windowState.hwndFx != 0);
+ MOZ_ASSERT(windowState.textureFx == nullptr);
+
+ windowState.textureFx = (HANDLE)hCopy;
+
+ shmem.PushWindowState(windowState);
+ shmem.LeaveShMem();
+
+ // Notify the waiting host process that the data is now available
+ HANDLE hSignal = ::OpenEventA(EVENT_ALL_ACCESS, // dwDesiredAccess
+ FALSE, // bInheritHandle
+ windowState.signalName // lpName
+ );
+ ::SetEvent(hSignal);
+ ::CloseHandle(hSignal);
+ }
+ } else {
+ MOZ_ASSERT(aSwapChain == mSwapChain);
+ }
+
+ return mSwapChain != nullptr && aSwapChain == mSwapChain;
+}
+
+// Update the contents of the shared texture.
+void FxROutputHandler::UpdateOutput(ID3D11DeviceContext* aCtx) {
+ MOZ_ASSERT(mSwapChain != nullptr);
+
+ ID3D11Texture2D* texOrig = nullptr;
+ HRESULT hr = mSwapChain->GetBuffer(0, IID_PPV_ARGS(&texOrig));
+ if (hr == S_OK) {
+ aCtx->CopyResource(mTexCopy, texOrig);
+ texOrig->Release();
+ }
+}
diff --git a/gfx/vr/FxROutputHandler.h b/gfx/vr/FxROutputHandler.h
new file mode 100644
index 0000000000..33e246aae5
--- /dev/null
+++ b/gfx/vr/FxROutputHandler.h
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 2; 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/. */
+
+#pragma once
+
+struct ID3D11Texture2D;
+struct IDXGISwapChain;
+struct ID3D11DeviceContext;
+struct ID3D11Device;
+
+#include <windows.h>
+#include <d3d11_1.h>
+
+#include "mozilla/RefPtr.h"
+
+// FxROutputHandler is responsible for managing resources to share a Desktop
+// browser window with a Firefox Reality VR window.
+// Note: this object is created on the Compositor thread, but its usage should
+// only be on the RenderThread.
+class FxROutputHandler final {
+ public:
+ bool TryInitialize(IDXGISwapChain* aSwapChain, ID3D11Device* aDevice);
+ void UpdateOutput(ID3D11DeviceContext* aCtx);
+
+ private:
+ RefPtr<IDXGISwapChain> mSwapChain = nullptr;
+ RefPtr<ID3D11Texture2D> mTexCopy = nullptr;
+};
diff --git a/gfx/vr/FxRWindowManager.cpp b/gfx/vr/FxRWindowManager.cpp
new file mode 100644
index 0000000000..becaf21447
--- /dev/null
+++ b/gfx/vr/FxRWindowManager.cpp
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 2; 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 "FxRWindowManager.h"
+#include "mozilla/Assertions.h"
+#include "nsPIDOMWindow.h"
+#include "mozilla/ClearOnShutdown.h"
+
+#include "nsWindow.h"
+
+static mozilla::StaticAutoPtr<FxRWindowManager> sFxrWinMgrInstance;
+
+FxRWindowManager* FxRWindowManager::GetInstance() {
+ if (sFxrWinMgrInstance == nullptr) {
+ sFxrWinMgrInstance = new FxRWindowManager();
+ ClearOnShutdown(&sFxrWinMgrInstance);
+ }
+
+ return sFxrWinMgrInstance;
+}
+
+FxRWindowManager::FxRWindowManager() : mWindow(nullptr) {}
+
+// Track this new Firefox Reality window instance
+void FxRWindowManager::AddWindow(nsPIDOMWindowOuter* aWindow) {
+ if (mWindow != nullptr) {
+ MOZ_CRASH("Only one window is supported");
+ }
+
+ mWindow = aWindow;
+}
+
+// Returns true if the window at the provided ID was created for Firefox Reality
+bool FxRWindowManager::IsFxRWindow(uint64_t aOuterWindowID) {
+ return (mWindow != nullptr) && (mWindow->WindowID() == aOuterWindowID);
+}
+
+// Returns true if the window was created for Firefox Reality
+bool FxRWindowManager::IsFxRWindow(const nsWindow* aWindow) const {
+ return (mWindow != nullptr) &&
+ (aWindow ==
+ mozilla::widget::WidgetUtils::DOMWindowToWidget(mWindow).take());
+}
+
+uint64_t FxRWindowManager::GetWindowID() const {
+ MOZ_ASSERT(mWindow);
+ return mWindow->WindowID();
+}
diff --git a/gfx/vr/FxRWindowManager.h b/gfx/vr/FxRWindowManager.h
new file mode 100644
index 0000000000..214ee1ecbf
--- /dev/null
+++ b/gfx/vr/FxRWindowManager.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 2; 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/. */
+
+#pragma once
+#include <cstdint>
+
+class nsPIDOMWindowOuter;
+class nsWindow;
+
+// FxRWindowManager is a singleton that is responsible for tracking all of
+// the top-level windows created for Firefox Reality on Desktop. Only a
+// single window is initially supported.
+class FxRWindowManager final {
+ public:
+ static FxRWindowManager* GetInstance();
+
+ void AddWindow(nsPIDOMWindowOuter* aWindow);
+ bool IsFxRWindow(uint64_t aOuterWindowID);
+ bool IsFxRWindow(const nsWindow* aWindow) const;
+ uint64_t GetWindowID() const;
+
+ private:
+ FxRWindowManager();
+
+ // Only a single window is supported for tracking. Support for multiple
+ // windows will require a data structure to collect windows as they are
+ // created.
+ nsPIDOMWindowOuter* mWindow;
+};
diff --git a/gfx/vr/VRDisplayClient.cpp b/gfx/vr/VRDisplayClient.cpp
new file mode 100644
index 0000000000..002bbefaef
--- /dev/null
+++ b/gfx/vr/VRDisplayClient.cpp
@@ -0,0 +1,665 @@
+/* -*- 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 <math.h>
+
+#include "prlink.h"
+#include "prenv.h"
+
+#include "nsIGlobalObject.h"
+#include "nsRefPtrHashtable.h"
+#include "nsString.h"
+#include "mozilla/dom/GamepadHandle.h"
+#include "mozilla/dom/GamepadManager.h"
+#include "mozilla/dom/Gamepad.h"
+#include "mozilla/dom/XRSession.h"
+#include "mozilla/dom/XRInputSourceArray.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Unused.h"
+#include "mozilla/StaticPrefs_dom.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/dom/WebXRBinding.h"
+#include "nsServiceManagerUtils.h"
+
+#ifdef XP_WIN
+# include "../layers/d3d11/CompositorD3D11.h"
+#endif
+
+#include "VRDisplayClient.h"
+#include "VRDisplayPresentation.h"
+#include "VRManagerChild.h"
+#include "VRLayerChild.h"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+
+using mozilla::dom::GamepadHandle;
+using mozilla::dom::GamepadHandleKind;
+
+VRDisplayClient::VRDisplayClient(const VRDisplayInfo& aDisplayInfo)
+ : mDisplayInfo(aDisplayInfo),
+ bLastEventWasMounted(false),
+ bLastEventWasPresenting(false),
+ mPresentationCount(0),
+ mLastEventFrameId(0),
+ mLastPresentingGeneration(0),
+ mLastEventControllerState{},
+ // For now WebVR is default to prevent a VRDisplay restore bug in WebVR
+ // compatibility mode. See Bug 1630512
+ mAPIMode(VRAPIMode::WebVR) {
+ MOZ_COUNT_CTOR(VRDisplayClient);
+}
+
+VRDisplayClient::~VRDisplayClient() { MOZ_COUNT_DTOR(VRDisplayClient); }
+
+void VRDisplayClient::UpdateDisplayInfo(const VRDisplayInfo& aDisplayInfo) {
+ mDisplayInfo = aDisplayInfo;
+ FireEvents();
+}
+
+already_AddRefed<VRDisplayPresentation> VRDisplayClient::BeginPresentation(
+ const nsTArray<mozilla::dom::VRLayer>& aLayers, uint32_t aGroup) {
+ PresentationCreated();
+ RefPtr<VRDisplayPresentation> presentation =
+ new VRDisplayPresentation(this, aLayers, aGroup);
+ return presentation.forget();
+}
+
+void VRDisplayClient::PresentationCreated() { ++mPresentationCount; }
+
+void VRDisplayClient::PresentationDestroyed() { --mPresentationCount; }
+
+void VRDisplayClient::SessionStarted(dom::XRSession* aSession) {
+ PresentationCreated();
+ MakePresentationGenerationCurrent();
+ mSessions.AppendElement(aSession);
+}
+void VRDisplayClient::SessionEnded(dom::XRSession* aSession) {
+ mSessions.RemoveElement(aSession);
+ PresentationDestroyed();
+}
+
+void VRDisplayClient::StartFrame() {
+ RefPtr<VRManagerChild> vm = VRManagerChild::Get();
+ vm->RunFrameRequestCallbacks();
+
+ nsTArray<RefPtr<dom::XRSession>> sessions;
+ sessions.AppendElements(mSessions);
+ for (auto session : sessions) {
+ session->StartFrame();
+ }
+}
+
+void VRDisplayClient::SetGroupMask(uint32_t aGroupMask) {
+ VRManagerChild* vm = VRManagerChild::Get();
+ vm->SendSetGroupMask(mDisplayInfo.mDisplayID, aGroupMask);
+}
+
+bool VRDisplayClient::IsPresentationGenerationCurrent() const {
+ if (mLastPresentingGeneration !=
+ mDisplayInfo.mDisplayState.presentingGeneration) {
+ return false;
+ }
+
+ return true;
+}
+
+void VRDisplayClient::MakePresentationGenerationCurrent() {
+ mLastPresentingGeneration = mDisplayInfo.mDisplayState.presentingGeneration;
+}
+
+gfx::VRAPIMode VRDisplayClient::GetXRAPIMode() const { return mAPIMode; }
+
+void VRDisplayClient::SetXRAPIMode(gfx::VRAPIMode aMode) {
+ mAPIMode = aMode;
+ Telemetry::Accumulate(Telemetry::WEBXR_API_MODE,
+ static_cast<uint32_t>(mAPIMode));
+}
+
+void VRDisplayClient::FireEvents() {
+ RefPtr<VRManagerChild> vm = VRManagerChild::Get();
+ // Only fire these events for non-chrome VR sessions
+ bool isPresenting = (mDisplayInfo.mPresentingGroups & kVRGroupContent) != 0;
+
+ // Check if we need to trigger onVRDisplayPresentChange event
+ if (bLastEventWasPresenting != isPresenting) {
+ bLastEventWasPresenting = isPresenting;
+ vm->FireDOMVRDisplayPresentChangeEvent(mDisplayInfo.mDisplayID);
+ }
+
+ // Check if we need to trigger onvrdisplayactivate event
+ if (!bLastEventWasMounted && mDisplayInfo.mDisplayState.isMounted) {
+ bLastEventWasMounted = true;
+ if (StaticPrefs::dom_vr_autoactivate_enabled()) {
+ vm->FireDOMVRDisplayMountedEvent(mDisplayInfo.mDisplayID);
+ }
+ }
+
+ // Check if we need to trigger onvrdisplaydeactivate event
+ if (bLastEventWasMounted && !mDisplayInfo.mDisplayState.isMounted) {
+ bLastEventWasMounted = false;
+ if (StaticPrefs::dom_vr_autoactivate_enabled()) {
+ vm->FireDOMVRDisplayUnmountedEvent(mDisplayInfo.mDisplayID);
+ }
+ }
+
+ if (mLastPresentingGeneration !=
+ mDisplayInfo.mDisplayState.presentingGeneration) {
+ mLastPresentingGeneration = mDisplayInfo.mDisplayState.presentingGeneration;
+ vm->NotifyPresentationGenerationChanged(mDisplayInfo.mDisplayID);
+ }
+
+ // In WebXR spec, Gamepad instances returned by an XRInputSource's gamepad
+ // attribute MUST NOT be included in the array returned by
+ // navigator.getGamepads().
+ if (mAPIMode == VRAPIMode::WebVR) {
+ FireGamepadEvents();
+ }
+ // Update controller states into XRInputSourceArray.
+ for (auto& session : mSessions) {
+ dom::XRInputSourceArray* inputs = session->InputSources();
+ if (inputs) {
+ inputs->Update(session);
+ }
+ }
+
+ // Check if we need to trigger VRDisplay.requestAnimationFrame
+ if (mLastEventFrameId != mDisplayInfo.mFrameId) {
+ mLastEventFrameId = mDisplayInfo.mFrameId;
+ StartFrame();
+ }
+}
+
+void VRDisplayClient::GamepadMappingForWebVR(
+ VRControllerState& aControllerState) {
+ float triggerValue[kVRControllerMaxButtons];
+ memcpy(triggerValue, aControllerState.triggerValue,
+ sizeof(aControllerState.triggerValue));
+ const uint64_t buttonPressed = aControllerState.buttonPressed;
+ const uint64_t buttonTouched = aControllerState.buttonTouched;
+
+ auto SetTriggerValue = [&](uint64_t newSlot, uint64_t oldSlot) {
+ aControllerState.triggerValue[newSlot] = triggerValue[oldSlot];
+ };
+ auto ShiftButtonBitForNewSlot = [&](uint64_t newSlot, uint64_t oldSlot,
+ bool aIsTouch = false) {
+ if (aIsTouch) {
+ return ((buttonTouched & (1ULL << oldSlot)) != 0) * (1ULL << newSlot);
+ }
+ SetTriggerValue(newSlot, oldSlot);
+ return ((buttonPressed & (1ULL << oldSlot)) != 0) * (1ULL << newSlot);
+ };
+
+ switch (aControllerState.type) {
+ case VRControllerType::HTCVive:
+ aControllerState.buttonPressed =
+ ShiftButtonBitForNewSlot(1, 0) | ShiftButtonBitForNewSlot(2, 1) |
+ ShiftButtonBitForNewSlot(0, 2) | ShiftButtonBitForNewSlot(3, 4);
+ aControllerState.buttonTouched = ShiftButtonBitForNewSlot(0, 1, true) |
+ ShiftButtonBitForNewSlot(2, 1, true) |
+ ShiftButtonBitForNewSlot(0, 2, true) |
+ ShiftButtonBitForNewSlot(3, 4, true);
+ aControllerState.numButtons = 4;
+ aControllerState.numAxes = 2;
+ break;
+ case VRControllerType::MSMR:
+ aControllerState.buttonPressed =
+ ShiftButtonBitForNewSlot(1, 0) | ShiftButtonBitForNewSlot(2, 1) |
+ ShiftButtonBitForNewSlot(0, 2) | ShiftButtonBitForNewSlot(3, 3) |
+ ShiftButtonBitForNewSlot(4, 4);
+ aControllerState.buttonTouched = ShiftButtonBitForNewSlot(1, 0, true) |
+ ShiftButtonBitForNewSlot(2, 1, true) |
+ ShiftButtonBitForNewSlot(0, 2, true) |
+ ShiftButtonBitForNewSlot(3, 3, true) |
+ ShiftButtonBitForNewSlot(4, 4, true);
+ aControllerState.numButtons = 5;
+ aControllerState.numAxes = 4;
+ break;
+ case VRControllerType::HTCViveCosmos:
+ aControllerState.buttonPressed =
+ ShiftButtonBitForNewSlot(0, 0) | ShiftButtonBitForNewSlot(1, 1) |
+ ShiftButtonBitForNewSlot(4, 3) | ShiftButtonBitForNewSlot(2, 4) |
+ ShiftButtonBitForNewSlot(3, 5) | ShiftButtonBitForNewSlot(5, 6);
+ aControllerState.buttonTouched = ShiftButtonBitForNewSlot(0, 0, true) |
+ ShiftButtonBitForNewSlot(1, 1, true) |
+ ShiftButtonBitForNewSlot(4, 3, true) |
+ ShiftButtonBitForNewSlot(2, 4, true) |
+ ShiftButtonBitForNewSlot(3, 5, true) |
+ ShiftButtonBitForNewSlot(5, 6, true);
+ aControllerState.axisValue[0] = aControllerState.axisValue[2];
+ aControllerState.axisValue[1] = aControllerState.axisValue[3];
+ aControllerState.numButtons = 6;
+ aControllerState.numAxes = 2;
+ break;
+ case VRControllerType::HTCViveFocus:
+ aControllerState.buttonPressed =
+ ShiftButtonBitForNewSlot(0, 2) | ShiftButtonBitForNewSlot(1, 0);
+ aControllerState.buttonTouched = ShiftButtonBitForNewSlot(0, 2, true) |
+ ShiftButtonBitForNewSlot(1, 0, true);
+ aControllerState.numButtons = 2;
+ aControllerState.numAxes = 2;
+ break;
+ case VRControllerType::HTCViveFocusPlus: {
+ aControllerState.buttonPressed = ShiftButtonBitForNewSlot(0, 2) |
+ ShiftButtonBitForNewSlot(1, 0) |
+ ShiftButtonBitForNewSlot(2, 1);
+ aControllerState.buttonTouched = ShiftButtonBitForNewSlot(0, 2, true) |
+ ShiftButtonBitForNewSlot(1, 0, true) |
+ ShiftButtonBitForNewSlot(2, 1, true);
+ aControllerState.numButtons = 3;
+ aControllerState.numAxes = 2;
+
+ static Matrix4x4 focusPlusTransform;
+ Matrix4x4 originalMtx;
+ if (focusPlusTransform.IsIdentity()) {
+ focusPlusTransform.RotateX(-0.70f);
+ focusPlusTransform.PostTranslate(0.0f, 0.0f, 0.01f);
+ focusPlusTransform.Inverse();
+ }
+ gfx::Quaternion quat(aControllerState.pose.orientation[0],
+ aControllerState.pose.orientation[1],
+ aControllerState.pose.orientation[2],
+ aControllerState.pose.orientation[3]);
+ originalMtx.SetRotationFromQuaternion(quat);
+ originalMtx._41 = aControllerState.pose.position[0];
+ originalMtx._42 = aControllerState.pose.position[1];
+ originalMtx._43 = aControllerState.pose.position[2];
+ originalMtx = focusPlusTransform * originalMtx;
+
+ gfx::Point3D pos, scale;
+ originalMtx.Decompose(pos, quat, scale);
+
+ aControllerState.pose.position[0] = pos.x;
+ aControllerState.pose.position[1] = pos.y;
+ aControllerState.pose.position[2] = pos.z;
+
+ aControllerState.pose.orientation[0] = quat.x;
+ aControllerState.pose.orientation[1] = quat.y;
+ aControllerState.pose.orientation[2] = quat.z;
+ aControllerState.pose.orientation[3] = quat.w;
+ break;
+ }
+ case VRControllerType::OculusGo: {
+ aControllerState.buttonPressed =
+ ShiftButtonBitForNewSlot(0, 2) | ShiftButtonBitForNewSlot(1, 0);
+ aControllerState.buttonTouched = ShiftButtonBitForNewSlot(0, 2, true) |
+ ShiftButtonBitForNewSlot(1, 0, true);
+ aControllerState.numButtons = 2;
+ aControllerState.numAxes = 2;
+
+ static Matrix4x4 goTransform;
+ Matrix4x4 originalMtx;
+
+ if (goTransform.IsIdentity()) {
+ goTransform.RotateX(-0.60f);
+ goTransform.Inverse();
+ }
+ gfx::Quaternion quat(aControllerState.pose.orientation[0],
+ aControllerState.pose.orientation[1],
+ aControllerState.pose.orientation[2],
+ aControllerState.pose.orientation[3]);
+ originalMtx.SetRotationFromQuaternion(quat);
+ originalMtx._41 = aControllerState.pose.position[0];
+ originalMtx._42 = aControllerState.pose.position[1];
+ originalMtx._43 = aControllerState.pose.position[2];
+ originalMtx = goTransform * originalMtx;
+
+ gfx::Point3D pos, scale;
+ originalMtx.Decompose(pos, quat, scale);
+
+ aControllerState.pose.position[0] = pos.x;
+ aControllerState.pose.position[1] = pos.y;
+ aControllerState.pose.position[2] = pos.z;
+
+ aControllerState.pose.orientation[0] = quat.x;
+ aControllerState.pose.orientation[1] = quat.y;
+ aControllerState.pose.orientation[2] = quat.z;
+ aControllerState.pose.orientation[3] = quat.w;
+ break;
+ }
+ case VRControllerType::OculusTouch:
+ aControllerState.buttonPressed =
+ ShiftButtonBitForNewSlot(0, 3) | ShiftButtonBitForNewSlot(1, 0) |
+ ShiftButtonBitForNewSlot(2, 1) | ShiftButtonBitForNewSlot(3, 4) |
+ ShiftButtonBitForNewSlot(4, 5) | ShiftButtonBitForNewSlot(5, 6);
+ aControllerState.buttonTouched = ShiftButtonBitForNewSlot(0, 3, true) |
+ ShiftButtonBitForNewSlot(1, 0, true) |
+ ShiftButtonBitForNewSlot(2, 1, true) |
+ ShiftButtonBitForNewSlot(3, 4, true) |
+ ShiftButtonBitForNewSlot(4, 5, true) |
+ ShiftButtonBitForNewSlot(5, 6, true);
+ aControllerState.axisValue[0] = aControllerState.axisValue[2];
+ aControllerState.axisValue[1] = aControllerState.axisValue[3];
+ aControllerState.numButtons = 6;
+ aControllerState.numAxes = 2;
+ break;
+ case VRControllerType::OculusTouch2: {
+ aControllerState.buttonPressed =
+ ShiftButtonBitForNewSlot(0, 3) | ShiftButtonBitForNewSlot(1, 0) |
+ ShiftButtonBitForNewSlot(2, 1) | ShiftButtonBitForNewSlot(3, 4) |
+ ShiftButtonBitForNewSlot(4, 5) | ShiftButtonBitForNewSlot(5, 6);
+ aControllerState.buttonTouched = ShiftButtonBitForNewSlot(0, 3, true) |
+ ShiftButtonBitForNewSlot(1, 0, true) |
+ ShiftButtonBitForNewSlot(2, 1, true) |
+ ShiftButtonBitForNewSlot(3, 4, true) |
+ ShiftButtonBitForNewSlot(4, 5, true) |
+ ShiftButtonBitForNewSlot(5, 6, true);
+ aControllerState.axisValue[0] = aControllerState.axisValue[2];
+ aControllerState.axisValue[1] = aControllerState.axisValue[3];
+ aControllerState.numButtons = 6;
+ aControllerState.numAxes = 2;
+
+ static Matrix4x4 touch2Transform;
+ Matrix4x4 originalMtx;
+
+ if (touch2Transform.IsIdentity()) {
+ touch2Transform.RotateX(-0.77f);
+ touch2Transform.PostTranslate(0.0f, 0.0f, -0.025f);
+ touch2Transform.Inverse();
+ }
+ gfx::Quaternion quat(aControllerState.pose.orientation[0],
+ aControllerState.pose.orientation[1],
+ aControllerState.pose.orientation[2],
+ aControllerState.pose.orientation[3]);
+ originalMtx.SetRotationFromQuaternion(quat);
+ originalMtx._41 = aControllerState.pose.position[0];
+ originalMtx._42 = aControllerState.pose.position[1];
+ originalMtx._43 = aControllerState.pose.position[2];
+ originalMtx = touch2Transform * originalMtx;
+
+ gfx::Point3D pos, scale;
+ originalMtx.Decompose(pos, quat, scale);
+
+ aControllerState.pose.position[0] = pos.x;
+ aControllerState.pose.position[1] = pos.y;
+ aControllerState.pose.position[2] = pos.z;
+
+ aControllerState.pose.orientation[0] = quat.x;
+ aControllerState.pose.orientation[1] = quat.y;
+ aControllerState.pose.orientation[2] = quat.z;
+ aControllerState.pose.orientation[3] = quat.w;
+ break;
+ }
+ case VRControllerType::ValveIndex:
+ aControllerState.buttonPressed =
+ ShiftButtonBitForNewSlot(1, 0) | ShiftButtonBitForNewSlot(2, 1) |
+ ShiftButtonBitForNewSlot(0, 2) | ShiftButtonBitForNewSlot(5, 3) |
+ ShiftButtonBitForNewSlot(3, 4) | ShiftButtonBitForNewSlot(4, 5) |
+ ShiftButtonBitForNewSlot(6, 6) | ShiftButtonBitForNewSlot(7, 7) |
+ ShiftButtonBitForNewSlot(8, 8) | ShiftButtonBitForNewSlot(9, 9);
+ aControllerState.buttonTouched = ShiftButtonBitForNewSlot(1, 0, true) |
+ ShiftButtonBitForNewSlot(2, 1, true) |
+ ShiftButtonBitForNewSlot(0, 2, true) |
+ ShiftButtonBitForNewSlot(5, 3, true) |
+ ShiftButtonBitForNewSlot(3, 4, true) |
+ ShiftButtonBitForNewSlot(4, 5, true) |
+ ShiftButtonBitForNewSlot(6, 6, true) |
+ ShiftButtonBitForNewSlot(7, 7, true) |
+ ShiftButtonBitForNewSlot(8, 8, true) |
+ ShiftButtonBitForNewSlot(9, 9, true);
+ aControllerState.numButtons = 10;
+ aControllerState.numAxes = 4;
+ break;
+ case VRControllerType::PicoGaze:
+ aControllerState.buttonPressed = ShiftButtonBitForNewSlot(0, 0);
+ aControllerState.buttonTouched = ShiftButtonBitForNewSlot(0, 0, true);
+ aControllerState.numButtons = 1;
+ aControllerState.numAxes = 0;
+ break;
+ case VRControllerType::PicoG2:
+ aControllerState.buttonPressed =
+ ShiftButtonBitForNewSlot(0, 2) | ShiftButtonBitForNewSlot(1, 0);
+ aControllerState.buttonTouched = ShiftButtonBitForNewSlot(0, 2, true) |
+ ShiftButtonBitForNewSlot(1, 0, true);
+ aControllerState.numButtons = 2;
+ aControllerState.numAxes = 2;
+ break;
+ case VRControllerType::PicoNeo2:
+ aControllerState.buttonPressed =
+ ShiftButtonBitForNewSlot(0, 3) | ShiftButtonBitForNewSlot(1, 0) |
+ ShiftButtonBitForNewSlot(2, 1) | ShiftButtonBitForNewSlot(3, 4) |
+ ShiftButtonBitForNewSlot(4, 5) | ShiftButtonBitForNewSlot(5, 6);
+ aControllerState.buttonTouched = ShiftButtonBitForNewSlot(0, 3, true) |
+ ShiftButtonBitForNewSlot(1, 0, true) |
+ ShiftButtonBitForNewSlot(2, 1, true) |
+ ShiftButtonBitForNewSlot(3, 4, true) |
+ ShiftButtonBitForNewSlot(4, 5, true) |
+ ShiftButtonBitForNewSlot(5, 6, true);
+ aControllerState.axisValue[0] = aControllerState.axisValue[2];
+ aControllerState.axisValue[1] = aControllerState.axisValue[3];
+ aControllerState.numButtons = 6;
+ aControllerState.numAxes = 2;
+ break;
+ default:
+ // Undefined controller types, we will keep its the same order.
+ break;
+ }
+}
+
+void VRDisplayClient::FireGamepadEvents() {
+ RefPtr<dom::GamepadManager> gamepadManager(dom::GamepadManager::GetService());
+ if (!gamepadManager) {
+ return;
+ }
+ for (int stateIndex = 0; stateIndex < kVRControllerMaxCount; stateIndex++) {
+ VRControllerState state = {}, lastState = {};
+ memcpy(&state, &mDisplayInfo.mControllerState[stateIndex],
+ sizeof(VRControllerState));
+ memcpy(&lastState, &mLastEventControllerState[stateIndex],
+ sizeof(VRControllerState));
+ GamepadMappingForWebVR(state);
+ GamepadMappingForWebVR(lastState);
+
+ uint32_t gamepadHandleValue =
+ mDisplayInfo.mDisplayID * kVRControllerMaxCount + stateIndex;
+
+ GamepadHandle gamepadHandle{gamepadHandleValue, GamepadHandleKind::VR};
+
+ bool bIsNew = false;
+
+ // Send events to notify that controllers are removed
+ if (state.controllerName[0] == '\0') {
+ // Controller is not present
+ if (lastState.controllerName[0] != '\0') {
+ // Controller has been removed
+ dom::GamepadRemoved info;
+ dom::GamepadChangeEventBody body(info);
+ dom::GamepadChangeEvent event(gamepadHandle, body);
+ gamepadManager->Update(event);
+ }
+ // Do not process any further events for removed controllers
+ continue;
+ }
+
+ // Send events to notify that new controllers are added
+ RefPtr<dom::Gamepad> existing = gamepadManager->GetGamepad(gamepadHandle);
+ // ControllerState in OpenVR action-based API gets delay to query btn and
+ // axis count. So, we need to check if they are more than zero.
+ if ((lastState.controllerName[0] == '\0' || !existing) &&
+ (state.numButtons > 0 || state.numAxes > 0)) {
+ dom::GamepadAdded info(NS_ConvertUTF8toUTF16(state.controllerName),
+ dom::GamepadMappingType::_empty, state.hand,
+ mDisplayInfo.mDisplayID, state.numButtons,
+ state.numAxes, state.numHaptics, 0, 0);
+ dom::GamepadChangeEventBody body(info);
+ dom::GamepadChangeEvent event(gamepadHandle, body);
+ gamepadManager->Update(event);
+ bIsNew = true;
+ }
+
+ // Send events for handedness changes
+ if (state.hand != lastState.hand) {
+ dom::GamepadHandInformation info(state.hand);
+ dom::GamepadChangeEventBody body(info);
+ dom::GamepadChangeEvent event(gamepadHandle, body);
+ gamepadManager->Update(event);
+ }
+
+ // Send events for axis value changes
+ for (uint32_t axisIndex = 0; axisIndex < state.numAxes; axisIndex++) {
+ if (state.axisValue[axisIndex] != lastState.axisValue[axisIndex]) {
+ dom::GamepadAxisInformation info(axisIndex, state.axisValue[axisIndex]);
+ dom::GamepadChangeEventBody body(info);
+ dom::GamepadChangeEvent event(gamepadHandle, body);
+ gamepadManager->Update(event);
+ }
+ }
+
+ // Send events for trigger, touch, and button value changes
+ if (!bIsNew) {
+ // When a new controller is added, we do not emit button events for
+ // the initial state of the inputs.
+ for (uint32_t buttonIndex = 0; buttonIndex < state.numButtons;
+ buttonIndex++) {
+ bool bPressed = (state.buttonPressed & (1ULL << buttonIndex)) != 0;
+ bool bTouched = (state.buttonTouched & (1ULL << buttonIndex)) != 0;
+ bool bLastPressed =
+ (lastState.buttonPressed & (1ULL << buttonIndex)) != 0;
+ bool bLastTouched =
+ (lastState.buttonTouched & (1ULL << buttonIndex)) != 0;
+
+ if (state.triggerValue[buttonIndex] !=
+ lastState.triggerValue[buttonIndex] ||
+ bPressed != bLastPressed || bTouched != bLastTouched) {
+ dom::GamepadButtonInformation info(
+ buttonIndex, state.triggerValue[buttonIndex], bPressed, bTouched);
+ dom::GamepadChangeEventBody body(info);
+ dom::GamepadChangeEvent event(gamepadHandle, body);
+ gamepadManager->Update(event);
+ }
+ }
+ }
+
+ // Send events for pose changes
+ // Note that VRPose is asserted to be a POD type so memcmp is safe
+ if (state.flags != lastState.flags ||
+ state.isPositionValid != lastState.isPositionValid ||
+ state.isOrientationValid != lastState.isOrientationValid ||
+ memcmp(&state.pose, &lastState.pose, sizeof(VRPose)) != 0) {
+ // Convert pose to GamepadPoseState
+ dom::GamepadPoseState poseState;
+ poseState.Clear();
+ poseState.flags = state.flags;
+
+ // Orientation values
+ poseState.isOrientationValid = state.isOrientationValid;
+ poseState.orientation[0] = state.pose.orientation[0];
+ poseState.orientation[1] = state.pose.orientation[1];
+ poseState.orientation[2] = state.pose.orientation[2];
+ poseState.orientation[3] = state.pose.orientation[3];
+ poseState.angularVelocity[0] = state.pose.angularVelocity[0];
+ poseState.angularVelocity[1] = state.pose.angularVelocity[1];
+ poseState.angularVelocity[2] = state.pose.angularVelocity[2];
+ poseState.angularAcceleration[0] = state.pose.angularAcceleration[0];
+ poseState.angularAcceleration[1] = state.pose.angularAcceleration[1];
+ poseState.angularAcceleration[2] = state.pose.angularAcceleration[2];
+
+ // Position values
+ poseState.isPositionValid = state.isPositionValid;
+ poseState.position[0] = state.pose.position[0];
+ poseState.position[1] = state.pose.position[1];
+ poseState.position[2] = state.pose.position[2];
+ poseState.linearVelocity[0] = state.pose.linearVelocity[0];
+ poseState.linearVelocity[1] = state.pose.linearVelocity[1];
+ poseState.linearVelocity[2] = state.pose.linearVelocity[2];
+ poseState.linearAcceleration[0] = state.pose.linearAcceleration[0];
+ poseState.linearAcceleration[1] = state.pose.linearAcceleration[1];
+ poseState.linearAcceleration[2] = state.pose.linearAcceleration[2];
+
+ // Send the event
+ dom::GamepadPoseInformation info(poseState);
+ dom::GamepadChangeEventBody body(info);
+ dom::GamepadChangeEvent event(gamepadHandle, body);
+ gamepadManager->Update(event);
+ }
+ }
+
+ // Note that VRControllerState is asserted to be a POD type and memcpy is
+ // safe.
+ memcpy(mLastEventControllerState, mDisplayInfo.mControllerState,
+ sizeof(VRControllerState) * kVRControllerMaxCount);
+}
+
+const VRHMDSensorState& VRDisplayClient::GetSensorState() const {
+ return mDisplayInfo.GetSensorState();
+}
+
+bool VRDisplayClient::GetIsConnected() const {
+ return mDisplayInfo.GetIsConnected();
+}
+
+bool VRDisplayClient::IsPresenting() {
+ return mDisplayInfo.mPresentingGroups != 0;
+}
+
+void VRDisplayClient::NotifyDisconnected() {
+ mDisplayInfo.mDisplayState.isConnected = false;
+}
+
+void VRDisplayClient::UpdateSubmitFrameResult(
+ const VRSubmitFrameResultInfo& aResult) {
+ mSubmitFrameResult = aResult;
+}
+
+void VRDisplayClient::GetSubmitFrameResult(VRSubmitFrameResultInfo& aResult) {
+ aResult = mSubmitFrameResult;
+}
+
+void VRDisplayClient::StartVRNavigation() {
+ /**
+ * A VR-to-VR site navigation has started, notify VRManager
+ * so we don't drop out of VR during the transition
+ */
+ VRManagerChild* vm = VRManagerChild::Get();
+ vm->SendStartVRNavigation(mDisplayInfo.mDisplayID);
+}
+
+void VRDisplayClient::StopVRNavigation(const TimeDuration& aTimeout) {
+ /**
+ * A VR-to-VR site navigation has ended and the new site
+ * has received a vrdisplayactivate event.
+ * Don't actually consider the navigation transition over
+ * until aTimeout has elapsed.
+ * This may be called multiple times, in which case the timeout
+ * should be reset to aTimeout.
+ * When aTimeout is TimeDuration(0), we should consider the
+ * transition immediately ended.
+ */
+ VRManagerChild* vm = VRManagerChild::Get();
+ vm->SendStopVRNavigation(mDisplayInfo.mDisplayID, aTimeout);
+}
+
+bool VRDisplayClient::IsReferenceSpaceTypeSupported(
+ dom::XRReferenceSpaceType aType) const {
+ /**
+ * https://immersive-web.github.io/webxr/#reference-space-is-supported
+ *
+ * We do not yet support local or local-floor for inline sessions.
+ * This could be expanded if we later support WebXR for inline-ar
+ * sessions on Firefox Fenix.
+ *
+ * We do not yet support unbounded reference spaces.
+ */
+ switch (aType) {
+ case dom::XRReferenceSpaceType::Viewer:
+ // Viewer is always supported, for both inline and immersive sessions
+ return true;
+ case dom::XRReferenceSpaceType::Local:
+ case dom::XRReferenceSpaceType::Local_floor:
+ // Local and Local_Floor are always supported for immersive sessions
+ return bool(mDisplayInfo.GetCapabilities() &
+ (VRDisplayCapabilityFlags::Cap_ImmersiveVR |
+ VRDisplayCapabilityFlags::Cap_ImmersiveAR));
+ case dom::XRReferenceSpaceType::Bounded_floor:
+ return bool(mDisplayInfo.GetCapabilities() &
+ VRDisplayCapabilityFlags::Cap_StageParameters);
+ default:
+ NS_WARNING(
+ "Unknown XRReferenceSpaceType passed to "
+ "VRDisplayClient::IsReferenceSpaceTypeSupported");
+ return false;
+ }
+}
diff --git a/gfx/vr/VRDisplayClient.h b/gfx/vr/VRDisplayClient.h
new file mode 100644
index 0000000000..19f8fdc413
--- /dev/null
+++ b/gfx/vr/VRDisplayClient.h
@@ -0,0 +1,103 @@
+/* -*- 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_DISPLAY_CLIENT_H
+#define GFX_VR_DISPLAY_CLIENT_H
+
+#include "nsCOMPtr.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/dom/VRDisplayBinding.h"
+
+#include "gfxVR.h"
+
+namespace mozilla {
+namespace dom {
+enum class XRReferenceSpaceType : uint8_t;
+class XRSession;
+} // namespace dom
+namespace gfx {
+class VRDisplayPresentation;
+class VRManagerChild;
+
+class VRDisplayClient {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VRDisplayClient)
+
+ explicit VRDisplayClient(const VRDisplayInfo& aDisplayInfo);
+
+ MOZ_CAN_RUN_SCRIPT void UpdateDisplayInfo(const VRDisplayInfo& aDisplayInfo);
+ void UpdateSubmitFrameResult(const VRSubmitFrameResultInfo& aResult);
+
+ const VRDisplayInfo& GetDisplayInfo() const { return mDisplayInfo; }
+ virtual const VRHMDSensorState& GetSensorState() const;
+ void GetSubmitFrameResult(VRSubmitFrameResultInfo& aResult);
+
+ already_AddRefed<VRDisplayPresentation> BeginPresentation(
+ const nsTArray<dom::VRLayer>& aLayers, uint32_t aGroup);
+ void PresentationCreated();
+ void PresentationDestroyed();
+
+ void SessionStarted(dom::XRSession* aSession);
+ void SessionEnded(dom::XRSession* aSession);
+
+ bool GetIsConnected() const;
+
+ void NotifyDisconnected();
+ void SetGroupMask(uint32_t aGroupMask);
+
+ bool IsPresentationGenerationCurrent() const;
+ void MakePresentationGenerationCurrent();
+
+ void StartVRNavigation();
+ void StopVRNavigation(const TimeDuration& aTimeout);
+
+ bool IsPresenting();
+ bool IsReferenceSpaceTypeSupported(dom::XRReferenceSpaceType aType) const;
+ gfx::VRAPIMode GetXRAPIMode() const;
+ void SetXRAPIMode(gfx::VRAPIMode aMode);
+
+ protected:
+ virtual ~VRDisplayClient();
+
+ MOZ_CAN_RUN_SCRIPT void FireEvents();
+ void FireGamepadEvents();
+ MOZ_CAN_RUN_SCRIPT void StartFrame();
+
+ VRDisplayInfo mDisplayInfo;
+
+ bool bLastEventWasMounted;
+ bool bLastEventWasPresenting;
+
+ int mPresentationCount;
+ uint64_t mLastEventFrameId;
+ uint32_t mLastPresentingGeneration;
+
+ // Difference between mDisplayInfo.mControllerState and
+ // mLastEventControllerState determines what gamepad events to fire when
+ // updated.
+ VRControllerState mLastEventControllerState[kVRControllerMaxCount];
+
+ /**
+ * mSessions is cleared in VRDisplayClient::SessionEnded.
+ * SessionEnded is guaranteed to be called by every XRSession
+ * when it is shutdown explicitly with the WebXR XRSession.end
+ * call, when all JS references on the XRSession are released, or
+ * when the window is closed.
+ */
+ nsTArray<RefPtr<dom::XRSession>> mSessions;
+
+ private:
+ void GamepadMappingForWebVR(VRControllerState& aControllerState);
+
+ VRSubmitFrameResultInfo mSubmitFrameResult;
+ gfx::VRAPIMode mAPIMode;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* GFX_VR_DISPLAY_CLIENT_H */
diff --git a/gfx/vr/VRDisplayPresentation.cpp b/gfx/vr/VRDisplayPresentation.cpp
new file mode 100644
index 0000000000..127225105b
--- /dev/null
+++ b/gfx/vr/VRDisplayPresentation.cpp
@@ -0,0 +1,155 @@
+/* -*- 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 "VRDisplayPresentation.h"
+#include "mozilla/dom/DocGroup.h"
+#include "mozilla/dom/Document.h"
+#include "mozilla/dom/XRWebGLLayer.h"
+#include "mozilla/Unused.h"
+#include "VRDisplayClient.h"
+#include "VRLayerChild.h"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+
+VRDisplayPresentation::VRDisplayPresentation(
+ VRDisplayClient* aDisplayClient,
+ const nsTArray<mozilla::dom::VRLayer>& aLayers, uint32_t aGroup)
+ : mDisplayClient(aDisplayClient),
+ mDOMLayers(aLayers.Clone()),
+ mGroup(aGroup) {
+ CreateLayers();
+}
+
+void VRDisplayPresentation::UpdateLayers(
+ const nsTArray<mozilla::dom::VRLayer>& aLayers) {
+ mDOMLayers = aLayers.Clone();
+ CreateLayers();
+}
+
+void VRDisplayPresentation::UpdateXRWebGLLayer(dom::XRWebGLLayer* aLayer) {
+ VRManagerChild* manager = VRManagerChild::Get();
+ if (!manager) {
+ // This should not happen, but let's log it and avoid a crash in case
+ // of regression.
+ NS_WARNING("VRManagerChild::Get returned null!");
+ return;
+ }
+
+ dom::HTMLCanvasElement* canvasElement = aLayer->GetCanvas();
+ nsCOMPtr<nsISerialEventTarget> target =
+ canvasElement->OwnerDoc()->EventTargetFor(TaskCategory::Other);
+
+ if (mLayers.Length() == 0) {
+ // WebXR uses a single layer for now.
+ RefPtr<VRLayerChild> vrLayer =
+ static_cast<VRLayerChild*>(manager->CreateVRLayer(
+ mDisplayClient->GetDisplayInfo().GetDisplayID(), target, mGroup));
+ mLayers.AppendElement(vrLayer);
+ }
+ RefPtr<VRLayerChild> vrLayer = mLayers[0];
+
+ Rect leftBounds(0.0, 0.0, 0.5, 1.0);
+ Rect rightBounds(0.5, 0.0, 0.5, 1.0);
+
+ vrLayer->Initialize(canvasElement, leftBounds, rightBounds);
+ vrLayer->SetXRFramebuffer(aLayer->GetFramebuffer());
+}
+
+uint32_t VRDisplayPresentation::GetGroup() const { return mGroup; }
+
+void VRDisplayPresentation::CreateLayers() {
+ VRManagerChild* manager = VRManagerChild::Get();
+ if (!manager) {
+ // This should not happen, but let's log it and avoid a crash in case
+ // of regression.
+ NS_WARNING("VRManagerChild::Get returned null!");
+ return;
+ }
+
+ unsigned int iLayer = 0;
+ for (dom::VRLayer& layer : mDOMLayers) {
+ dom::HTMLCanvasElement* canvasElement = layer.mSource;
+ if (!canvasElement) {
+ /// XXX In the future we will support WebVR in WebWorkers here
+ continue;
+ }
+
+ Rect leftBounds(0.0, 0.0, 0.5, 1.0);
+ if (layer.mLeftBounds.Length() == 4) {
+ leftBounds.SetRect(layer.mLeftBounds[0], layer.mLeftBounds[1],
+ layer.mLeftBounds[2], layer.mLeftBounds[3]);
+ } else if (layer.mLeftBounds.Length() != 0) {
+ /**
+ * We ignore layers with an incorrect number of values.
+ * In the future, VRDisplay.requestPresent may throw in
+ * this case. See https://github.com/w3c/webvr/issues/71
+ */
+ continue;
+ }
+
+ Rect rightBounds(0.5, 0.0, 0.5, 1.0);
+ if (layer.mRightBounds.Length() == 4) {
+ rightBounds.SetRect(layer.mRightBounds[0], layer.mRightBounds[1],
+ layer.mRightBounds[2], layer.mRightBounds[3]);
+ } else if (layer.mRightBounds.Length() != 0) {
+ /**
+ * We ignore layers with an incorrect number of values.
+ * In the future, VRDisplay.requestPresent may throw in
+ * this case. See https://github.com/w3c/webvr/issues/71
+ */
+ continue;
+ }
+
+ nsCOMPtr<nsISerialEventTarget> target =
+ canvasElement->OwnerDoc()->EventTargetFor(TaskCategory::Other);
+
+ if (mLayers.Length() <= iLayer) {
+ // Not enough layers, let's add one
+ RefPtr<VRLayerChild> vrLayer =
+ static_cast<VRLayerChild*>(manager->CreateVRLayer(
+ mDisplayClient->GetDisplayInfo().GetDisplayID(), target, mGroup));
+ if (!vrLayer) {
+ NS_WARNING("CreateVRLayer returned null!");
+ continue;
+ }
+ vrLayer->Initialize(canvasElement, leftBounds, rightBounds);
+ mLayers.AppendElement(vrLayer);
+ } else {
+ // We already have a layer, let's update it
+ mLayers[iLayer]->Initialize(canvasElement, leftBounds, rightBounds);
+ }
+ iLayer++;
+ }
+
+ // Truncate any excess layers that weren't included in the updated list
+ mLayers.SetLength(iLayer);
+}
+
+void VRDisplayPresentation::DestroyLayers() {
+ for (VRLayerChild* layer : mLayers) {
+ if (layer->IsIPCOpen()) {
+ Unused << layer->SendDestroy();
+ }
+ }
+ mLayers.Clear();
+}
+
+void VRDisplayPresentation::GetDOMLayers(nsTArray<dom::VRLayer>& result) {
+ result = mDOMLayers.Clone();
+}
+
+VRDisplayPresentation::~VRDisplayPresentation() {
+ DestroyLayers();
+ mDisplayClient->PresentationDestroyed();
+}
+
+void VRDisplayPresentation::SubmitFrame() {
+ for (VRLayerChild* layer : mLayers) {
+ layer->SubmitFrame(mDisplayClient->GetDisplayInfo());
+ break; // Currently only one layer supported, submit only the first
+ }
+}
diff --git a/gfx/vr/VRDisplayPresentation.h b/gfx/vr/VRDisplayPresentation.h
new file mode 100644
index 0000000000..368048743f
--- /dev/null
+++ b/gfx/vr/VRDisplayPresentation.h
@@ -0,0 +1,47 @@
+/* -*- 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_DISPLAY_PRESENTATION_H
+#define GFX_VR_DISPLAY_PRESENTATION_H
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/dom/VRDisplayBinding.h"
+
+namespace mozilla {
+namespace dom {
+class XRWebGLLayer;
+}
+namespace gfx {
+class VRDisplayClient;
+class VRLayerChild;
+
+class VRDisplayPresentation final {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VRDisplayPresentation)
+
+ public:
+ VRDisplayPresentation(VRDisplayClient* aDisplayClient,
+ const nsTArray<dom::VRLayer>& aLayers, uint32_t aGroup);
+ void UpdateLayers(const nsTArray<mozilla::dom::VRLayer>& aLayers);
+ void UpdateXRWebGLLayer(dom::XRWebGLLayer* aLayer);
+ void SubmitFrame();
+ void GetDOMLayers(nsTArray<dom::VRLayer>& result);
+ uint32_t GetGroup() const;
+
+ private:
+ ~VRDisplayPresentation();
+ void CreateLayers();
+ void DestroyLayers();
+
+ RefPtr<VRDisplayClient> mDisplayClient;
+ nsTArray<dom::VRLayer> mDOMLayers;
+ nsTArray<RefPtr<VRLayerChild>> mLayers;
+ uint32_t mGroup;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* GFX_VR_DISPLAY_PRESENTAITON_H */
diff --git a/gfx/vr/VRManager.cpp b/gfx/vr/VRManager.cpp
new file mode 100644
index 0000000000..72bc02b674
--- /dev/null
+++ b/gfx/vr/VRManager.cpp
@@ -0,0 +1,1542 @@
+/* -*- 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 "VRManager.h"
+
+#include "GeckoProfiler.h"
+#include "VRManagerParent.h"
+#include "VRShMem.h"
+#include "VRThread.h"
+#include "gfxVR.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/dom/VRDisplay.h"
+#include "mozilla/dom/GamepadEventTypes.h"
+#include "mozilla/layers/TextureHost.h"
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Services.h"
+#include "mozilla/StaticPrefs_dom.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/Unused.h"
+#include "nsIObserverService.h"
+
+#include "gfxVR.h"
+#include <cstring>
+
+#include "ipc/VRLayerParent.h"
+#if !defined(MOZ_WIDGET_ANDROID)
+# include "VRServiceHost.h"
+#endif
+
+#ifdef XP_WIN
+# include "CompositorD3D11.h"
+# include "TextureD3D11.h"
+# include <d3d11.h>
+# include "gfxWindowsPlatform.h"
+# include "mozilla/gfx/DeviceManagerDx.h"
+#elif defined(XP_MACOSX)
+# include "mozilla/gfx/MacIOSurface.h"
+# include <errno.h>
+#elif defined(MOZ_WIDGET_ANDROID)
+# include <string.h>
+# include <pthread.h>
+# include "GeckoVRManager.h"
+# include "mozilla/java/GeckoSurfaceTextureWrappers.h"
+# include "mozilla/layers/CompositorThread.h"
+#endif // defined(MOZ_WIDGET_ANDROID)
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+using namespace mozilla::layers;
+using namespace mozilla::gl;
+
+using mozilla::dom::GamepadHandle;
+
+namespace mozilla::gfx {
+
+/**
+ * When VR content is active, we run the tasks at 1ms
+ * intervals, enabling multiple events to be processed
+ * per frame, such as haptic feedback pulses.
+ */
+const uint32_t kVRActiveTaskInterval = 1; // milliseconds
+
+/**
+ * When VR content is inactive, we run the tasks at 100ms
+ * intervals, enabling VR display enumeration and
+ * presentation startup to be relatively responsive
+ * while not consuming unnecessary resources.
+ */
+const uint32_t kVRIdleTaskInterval = 100; // milliseconds
+
+/**
+ * Max frame duration before the watchdog submits a new one.
+ * Probably we can get rid of this when we enforce that SubmitFrame can only be
+ * called in a VRDisplay loop.
+ */
+const double kVRMaxFrameSubmitDuration = 4000.0f; // milliseconds
+
+static StaticRefPtr<VRManager> sVRManagerSingleton;
+
+static bool ValidVRManagerProcess() {
+ return XRE_IsParentProcess() || XRE_IsGPUProcess();
+}
+
+/* static */
+VRManager* VRManager::Get() {
+ MOZ_ASSERT(sVRManagerSingleton != nullptr);
+ MOZ_ASSERT(ValidVRManagerProcess());
+
+ return sVRManagerSingleton;
+}
+
+/* static */
+VRManager* VRManager::MaybeGet() {
+ MOZ_ASSERT(ValidVRManagerProcess());
+
+ return sVRManagerSingleton;
+}
+
+Atomic<uint32_t> VRManager::sDisplayBase(0);
+
+/* static */
+uint32_t VRManager::AllocateDisplayID() { return ++sDisplayBase; }
+
+/*static*/
+void VRManager::ManagerInit() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!ValidVRManagerProcess()) {
+ return;
+ }
+
+ // Enable gamepad extensions while VR is enabled.
+ // Preference only can be set at the Parent process.
+ if (StaticPrefs::dom_vr_enabled() && XRE_IsParentProcess()) {
+ Preferences::SetBool("dom.gamepad.extensions.enabled", true);
+ }
+
+ if (sVRManagerSingleton == nullptr) {
+ sVRManagerSingleton = new VRManager();
+ ClearOnShutdown(&sVRManagerSingleton);
+ }
+}
+
+VRManager::VRManager()
+ : mState(VRManagerState::Disabled),
+ mAccumulator100ms(0.0f),
+ mRuntimeDetectionRequested(false),
+ mRuntimeDetectionCompleted(false),
+ mEnumerationRequested(false),
+ mEnumerationCompleted(false),
+ mVRDisplaysRequested(false),
+ mVRDisplaysRequestedNonFocus(false),
+ mVRControllersRequested(false),
+ mFrameStarted(false),
+ mTaskInterval(0),
+ mCurrentSubmitTaskMonitor("CurrentSubmitTaskMonitor"),
+ mCurrentSubmitTask(nullptr),
+ mLastSubmittedFrameId(0),
+ mLastStartedFrame(0),
+ mRuntimeSupportFlags(VRDisplayCapabilityFlags::Cap_None),
+ mAppPaused(false),
+ mShmem(nullptr),
+ mHapticPulseRemaining{},
+ mDisplayInfo{},
+ mLastUpdateDisplayInfo{},
+ mBrowserState{},
+ mLastSensorState{} {
+ MOZ_ASSERT(sVRManagerSingleton == nullptr);
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(ValidVRManagerProcess());
+
+#if !defined(MOZ_WIDGET_ANDROID)
+ // XRE_IsGPUProcess() is helping us to check some platforms like
+ // Win 7 try which are not using GPU process but VR process is enabled.
+ mVRProcessEnabled =
+ StaticPrefs::dom_vr_process_enabled_AtStartup() && XRE_IsGPUProcess();
+ VRServiceHost::Init(mVRProcessEnabled);
+ mServiceHost = VRServiceHost::Get();
+ // We must shutdown before VRServiceHost, which is cleared
+ // on ShutdownPhase::ShutdownFinal, potentially before VRManager.
+ // We hold a reference to VRServiceHost to ensure it stays
+ // alive until we have shut down.
+#else
+ // For Android, there is no VRProcess available and no VR service is
+ // created, so default to false.
+ mVRProcessEnabled = false;
+#endif // !defined(MOZ_WIDGET_ANDROID)
+
+ nsCOMPtr<nsIObserverService> service = services::GetObserverService();
+ if (service) {
+ service->AddObserver(this, "application-background", false);
+ service->AddObserver(this, "application-foreground", false);
+ }
+}
+
+void VRManager::OpenShmem() {
+ if (mShmem == nullptr) {
+ mShmem = new VRShMem(nullptr, true /*aRequiresMutex*/);
+
+#if !defined(MOZ_WIDGET_ANDROID)
+ mShmem->CreateShMem(mVRProcessEnabled /*aCreateOnSharedMemory*/);
+ // The VR Service accesses all hardware from a separate process
+ // and replaces the other VRManager when enabled.
+ // If the VR process is not enabled, create an in-process VRService.
+ if (!mVRProcessEnabled) {
+ // If the VR process is disabled, attempt to create a
+ // VR service within the current process
+ mServiceHost->CreateService(mShmem->GetExternalShmem());
+ return;
+ }
+#else
+ mShmem->CreateShMemForAndroid();
+#endif
+ } else {
+ mShmem->ClearShMem();
+ }
+
+ // Reset local information for new connection
+ mDisplayInfo.Clear();
+ mLastUpdateDisplayInfo.Clear();
+ mFrameStarted = false;
+ mBrowserState.Clear();
+ mLastSensorState.Clear();
+ mEnumerationCompleted = false;
+ mDisplayInfo.mGroupMask = kVRGroupContent;
+}
+
+void VRManager::CloseShmem() {
+ if (mShmem != nullptr) {
+ mShmem->CloseShMem();
+ delete mShmem;
+ mShmem = nullptr;
+ }
+}
+
+VRManager::~VRManager() {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mState == VRManagerState::Disabled);
+
+ nsCOMPtr<nsIObserverService> service = services::GetObserverService();
+ if (service) {
+ service->RemoveObserver(this, "application-background");
+ service->RemoveObserver(this, "application-foreground");
+ }
+
+#if !defined(MOZ_WIDGET_ANDROID)
+ mServiceHost->Shutdown();
+#endif
+ CloseShmem();
+}
+
+void VRManager::AddLayer(VRLayerParent* aLayer) {
+ mLayers.AppendElement(aLayer);
+ mDisplayInfo.mPresentingGroups |= aLayer->GetGroup();
+ if (mLayers.Length() == 1) {
+ StartPresentation();
+ }
+
+ // Ensure that the content process receives the change immediately
+ if (mState != VRManagerState::Enumeration &&
+ mState != VRManagerState::RuntimeDetection) {
+ DispatchVRDisplayInfoUpdate();
+ }
+}
+
+void VRManager::RemoveLayer(VRLayerParent* aLayer) {
+ mLayers.RemoveElement(aLayer);
+ if (mLayers.Length() == 0) {
+ StopPresentation();
+ }
+ mDisplayInfo.mPresentingGroups = 0;
+ for (auto layer : mLayers) {
+ mDisplayInfo.mPresentingGroups |= layer->GetGroup();
+ }
+
+ // Ensure that the content process receives the change immediately
+ if (mState != VRManagerState::Enumeration &&
+ mState != VRManagerState::RuntimeDetection) {
+ DispatchVRDisplayInfoUpdate();
+ }
+}
+
+void VRManager::AddVRManagerParent(VRManagerParent* aVRManagerParent) {
+ mVRManagerParents.PutEntry(aVRManagerParent);
+}
+
+void VRManager::RemoveVRManagerParent(VRManagerParent* aVRManagerParent) {
+ mVRManagerParents.RemoveEntry(aVRManagerParent);
+ if (mVRManagerParents.IsEmpty()) {
+ Destroy();
+ }
+}
+
+void VRManager::UpdateRequestedDevices() {
+ bool bHaveEventListener = false;
+ bool bHaveEventListenerNonFocus = false;
+ bool bHaveControllerListener = false;
+
+ for (auto iter = mVRManagerParents.Iter(); !iter.Done(); iter.Next()) {
+ VRManagerParent* vmp = iter.Get()->GetKey();
+ bHaveEventListener |= vmp->HaveEventListener() && vmp->GetVRActiveStatus();
+ bHaveEventListenerNonFocus |=
+ vmp->HaveEventListener() && !vmp->GetVRActiveStatus();
+ bHaveControllerListener |= vmp->HaveControllerListener();
+ }
+
+ mVRDisplaysRequested = bHaveEventListener;
+ mVRDisplaysRequestedNonFocus = bHaveEventListenerNonFocus;
+ // We only currently allow controllers to be used when
+ // also activating a VR display
+ mVRControllersRequested = mVRDisplaysRequested && bHaveControllerListener;
+}
+
+/**
+ * VRManager::NotifyVsync must be called on every 2d vsync (usually at 60hz).
+ * This must be called even when no WebVR site is active.
+ * If we don't have a 2d display attached to the system, we can call this
+ * at the VR display's native refresh rate.
+ **/
+void VRManager::NotifyVsync(const TimeStamp& aVsyncTimestamp) {
+ if (mState != VRManagerState::Active) {
+ return;
+ }
+ /**
+ * If the display isn't presenting, refresh the sensors and trigger
+ * VRDisplay.requestAnimationFrame at the normal 2d display refresh rate.
+ */
+ if (mDisplayInfo.mPresentingGroups == 0) {
+ StartFrame();
+ }
+}
+
+void VRManager::StartTasks() {
+ if (!mTaskTimer) {
+ mTaskInterval = GetOptimalTaskInterval();
+ mTaskTimer = NS_NewTimer();
+ mTaskTimer->SetTarget(CompositorThread());
+ mTaskTimer->InitWithNamedFuncCallback(
+ TaskTimerCallback, this, mTaskInterval,
+ nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP,
+ "VRManager::TaskTimerCallback");
+ }
+}
+
+void VRManager::StopTasks() {
+ if (mTaskTimer) {
+ mTaskTimer->Cancel();
+ mTaskTimer = nullptr;
+ }
+}
+
+/*static*/
+void VRManager::TaskTimerCallback(nsITimer* aTimer, void* aClosure) {
+ /**
+ * It is safe to use the pointer passed in aClosure to reference the
+ * VRManager object as the timer is canceled in VRManager::Destroy.
+ * VRManager::Destroy set mState to VRManagerState::Disabled, which
+ * is asserted in the VRManager destructor, guaranteeing that this
+ * functions runs if and only if the VRManager object is valid.
+ */
+ VRManager* self = static_cast<VRManager*>(aClosure);
+ self->RunTasks();
+
+ if (self->mAppPaused) {
+ // When the apps goes the background (e.g. Android) we should stop the
+ // tasks.
+ self->StopTasks();
+ self->mState = VRManagerState::Idle;
+ }
+}
+
+void VRManager::RunTasks() {
+ // Will be called once every 1ms when a VR presentation
+ // is active or once per vsync when a VR presentation is
+ // not active.
+
+ if (mState == VRManagerState::Disabled) {
+ // We may have been destroyed but still have messages
+ // in the queue from mTaskTimer. Bail out to avoid
+ // running them.
+ return;
+ }
+
+ TimeStamp now = TimeStamp::Now();
+ double lastTickMs = mAccumulator100ms;
+ double deltaTime = 0.0f;
+ if (!mLastTickTime.IsNull()) {
+ deltaTime = (now - mLastTickTime).ToMilliseconds();
+ }
+ mAccumulator100ms += deltaTime;
+ mLastTickTime = now;
+
+ if (deltaTime > 0.0f && floor(mAccumulator100ms) != floor(lastTickMs)) {
+ // Even if more than 1 ms has passed, we will only
+ // execute Run1msTasks() once.
+ Run1msTasks(deltaTime);
+ }
+
+ if (floor(mAccumulator100ms * 0.1f) != floor(lastTickMs * 0.1f)) {
+ // Even if more than 10 ms has passed, we will only
+ // execute Run10msTasks() once.
+ Run10msTasks();
+ }
+
+ if (mAccumulator100ms >= 100.0f) {
+ // Even if more than 100 ms has passed, we will only
+ // execute Run100msTasks() once.
+ Run100msTasks();
+ mAccumulator100ms = fmod(mAccumulator100ms, 100.0f);
+ }
+
+ uint32_t optimalTaskInterval = GetOptimalTaskInterval();
+ if (mTaskTimer && optimalTaskInterval != mTaskInterval) {
+ mTaskTimer->SetDelay(optimalTaskInterval);
+ mTaskInterval = optimalTaskInterval;
+ }
+}
+
+uint32_t VRManager::GetOptimalTaskInterval() {
+ /**
+ * When either VR content is detected or VR hardware
+ * has already been activated, we schedule tasks more
+ * frequently.
+ */
+ bool wantGranularTasks = mVRDisplaysRequested || mVRControllersRequested ||
+ mDisplayInfo.mDisplayID != 0;
+ if (wantGranularTasks) {
+ return kVRActiveTaskInterval;
+ }
+
+ return kVRIdleTaskInterval;
+}
+
+/**
+ * Run1msTasks() is guaranteed not to be
+ * called more than once within 1ms.
+ * When VR is not active, this will be
+ * called once per VSync if it wasn't
+ * called within the last 1ms.
+ */
+void VRManager::Run1msTasks(double aDeltaTime) { UpdateHaptics(aDeltaTime); }
+
+/**
+ * Run10msTasks() is guaranteed not to be
+ * called more than once within 10ms.
+ * When VR is not active, this will be
+ * called once per VSync if it wasn't
+ * called within the last 10ms.
+ */
+void VRManager::Run10msTasks() {
+ UpdateRequestedDevices();
+ CheckWatchDog();
+ ExpireNavigationTransition();
+ PullState();
+ PushState();
+}
+
+/**
+ * Run100msTasks() is guaranteed not to be
+ * called more than once within 100ms.
+ * When VR is not active, this will be
+ * called once per VSync if it wasn't
+ * called within the last 100ms.
+ */
+void VRManager::Run100msTasks() {
+ // We must continually refresh the VR display enumeration to check
+ // for events that we must fire such as Window.onvrdisplayconnect
+ // Note that enumeration itself may activate display hardware, such
+ // as Oculus, so we only do this when we know we are displaying content
+ // that is looking for VR displays.
+#if !defined(MOZ_WIDGET_ANDROID)
+ mServiceHost->Refresh();
+ CheckForPuppetCompletion();
+#endif
+ ProcessManagerState();
+}
+
+void VRManager::CheckForInactiveTimeout() {
+ // Shut down the VR devices when not in use
+ if (mVRDisplaysRequested || mVRDisplaysRequestedNonFocus ||
+ mVRControllersRequested || mEnumerationRequested ||
+ mRuntimeDetectionRequested || mState == VRManagerState::Enumeration ||
+ mState == VRManagerState::RuntimeDetection) {
+ // We are using a VR device, keep it alive
+ mLastActiveTime = TimeStamp::Now();
+ } else if (mLastActiveTime.IsNull()) {
+ Shutdown();
+ } else {
+ TimeDuration duration = TimeStamp::Now() - mLastActiveTime;
+ if (duration.ToMilliseconds() > StaticPrefs::dom_vr_inactive_timeout()) {
+ Shutdown();
+ // We must not throttle the next enumeration request
+ // after an idle timeout, as it may result in the
+ // user needing to refresh the browser to detect
+ // VR hardware when leaving and returning to a VR
+ // site.
+ mLastDisplayEnumerationTime = TimeStamp();
+ }
+ }
+}
+
+void VRManager::CheckForShutdown() {
+ // Check for remote end shutdown
+ if (mDisplayInfo.mDisplayState.shutdown) {
+ Shutdown();
+ }
+}
+
+#if !defined(MOZ_WIDGET_ANDROID)
+void VRManager::CheckForPuppetCompletion() {
+ // Notify content process about completion of puppet test resets
+ if (mState != VRManagerState::Active) {
+ for (auto iter = mManagerParentsWaitingForPuppetReset.Iter(); !iter.Done();
+ iter.Next()) {
+ Unused << iter.Get()->GetKey()->SendNotifyPuppetResetComplete();
+ }
+ mManagerParentsWaitingForPuppetReset.Clear();
+ }
+ // Notify content process about completion of puppet test scripts
+ if (mManagerParentRunningPuppet) {
+ mServiceHost->CheckForPuppetCompletion();
+ }
+}
+
+void VRManager::NotifyPuppetComplete() {
+ // Notify content process about completion of puppet test scripts
+ if (mManagerParentRunningPuppet) {
+ Unused << mManagerParentRunningPuppet
+ ->SendNotifyPuppetCommandBufferCompleted(true);
+ mManagerParentRunningPuppet = nullptr;
+ }
+}
+
+#endif // !defined(MOZ_WIDGET_ANDROID)
+
+void VRManager::StartFrame() {
+ if (mState != VRManagerState::Active) {
+ return;
+ }
+ AUTO_PROFILER_TRACING_MARKER("VR", "GetSensorState", OTHER);
+
+ /**
+ * Do not start more VR frames until the last submitted frame is already
+ * processed, or the last has stalled for more than
+ * kVRMaxFrameSubmitDuration milliseconds.
+ */
+ TimeStamp now = TimeStamp::Now();
+ const TimeStamp lastFrameStart =
+ mLastFrameStart[mDisplayInfo.mFrameId % kVRMaxLatencyFrames];
+ const bool isPresenting = mLastUpdateDisplayInfo.GetPresentingGroups() != 0;
+ double duration =
+ lastFrameStart.IsNull() ? 0.0 : (now - lastFrameStart).ToMilliseconds();
+ if (isPresenting && mLastStartedFrame > 0 &&
+ mDisplayInfo.mDisplayState.lastSubmittedFrameId < mLastStartedFrame &&
+ duration < kVRMaxFrameSubmitDuration) {
+ return;
+ }
+
+ mDisplayInfo.mFrameId++;
+ size_t bufferIndex = mDisplayInfo.mFrameId % kVRMaxLatencyFrames;
+ mDisplayInfo.mLastSensorState[bufferIndex] = mLastSensorState;
+ mLastFrameStart[bufferIndex] = now;
+ mFrameStarted = true;
+ mLastStartedFrame = mDisplayInfo.mFrameId;
+
+ DispatchVRDisplayInfoUpdate();
+}
+
+void VRManager::DetectRuntimes() {
+ if (mState == VRManagerState::RuntimeDetection) {
+ // Runtime detection has already been started.
+ // This additional request will also receive the
+ // result from the first request.
+ return;
+ }
+
+ // Detect XR runtimes to determine if they are
+ // capable of supporting VR or AR sessions, while
+ // avoiding activating any XR devices or persistent
+ // background software.
+ if (mRuntimeDetectionCompleted) {
+ // We have already detected runtimes, so we can
+ // immediately respond with the same results.
+ // This will require the user to restart the browser
+ // after installing or removing an XR device
+ // runtime.
+ DispatchRuntimeCapabilitiesUpdate();
+ return;
+ }
+ mRuntimeDetectionRequested = true;
+ ProcessManagerState();
+}
+
+void VRManager::EnumerateDevices() {
+ if (mState == VRManagerState::Enumeration ||
+ (mRuntimeDetectionCompleted &&
+ (mVRDisplaysRequested || mEnumerationRequested))) {
+ // Enumeration has already been started.
+ // This additional request will also receive the
+ // result from the first request.
+ return;
+ }
+ // Activate XR runtimes and enumerate XR devices.
+ mEnumerationRequested = true;
+ ProcessManagerState();
+}
+
+void VRManager::ProcessManagerState() {
+ switch (mState) {
+ case VRManagerState::Disabled:
+ ProcessManagerState_Disabled();
+ break;
+ case VRManagerState::Idle:
+ ProcessManagerState_Idle();
+ break;
+ case VRManagerState::RuntimeDetection:
+ ProcessManagerState_DetectRuntimes();
+ break;
+ case VRManagerState::Enumeration:
+ ProcessManagerState_Enumeration();
+ break;
+ case VRManagerState::Active:
+ ProcessManagerState_Active();
+ break;
+ case VRManagerState::Stopping:
+ ProcessManagerState_Stopping();
+ break;
+ }
+ CheckForInactiveTimeout();
+ CheckForShutdown();
+}
+
+void VRManager::ProcessManagerState_Disabled() {
+ MOZ_ASSERT(mState == VRManagerState::Disabled);
+
+ if (!StaticPrefs::dom_vr_enabled()) {
+ return;
+ }
+
+ if (mRuntimeDetectionRequested || mEnumerationRequested ||
+ mVRDisplaysRequested) {
+ StartTasks();
+ mState = VRManagerState::Idle;
+ }
+}
+
+void VRManager::ProcessManagerState_Stopping() {
+ MOZ_ASSERT(mState == VRManagerState::Stopping);
+ PullState();
+ /**
+ * In the case of Desktop, the VRService shuts itself down.
+ * Before it's finished stopping, it sets a flag in the ShMem
+ * to let VRManager know that it's done. VRManager watches for
+ * this flag and transitions out of the VRManagerState::Stopping
+ * state to VRManagerState::Idle.
+ */
+#if defined(MOZ_WIDGET_ANDROID)
+ // On Android, the VR service never actually shuts
+ // down or requests VRManager to stop.
+ Shutdown();
+#endif // defined(MOZ_WIDGET_ANDROID)
+}
+
+void VRManager::ProcessManagerState_Idle_StartEnumeration() {
+ MOZ_ASSERT(mState == VRManagerState::Idle);
+
+ if (!mEarliestRestartTime.IsNull() &&
+ mEarliestRestartTime > TimeStamp::Now()) {
+ // When the VR Service shuts down it informs us of how long we
+ // must wait until we can re-start it.
+ // We must wait until mEarliestRestartTime before attempting
+ // to enumerate again.
+ return;
+ }
+
+ /**
+ * Throttle the rate of enumeration to the interval set in
+ * VRDisplayEnumerateInterval
+ */
+ if (!mLastDisplayEnumerationTime.IsNull()) {
+ TimeDuration duration = TimeStamp::Now() - mLastDisplayEnumerationTime;
+ if (duration.ToMilliseconds() <
+ StaticPrefs::dom_vr_display_enumerate_interval()) {
+ return;
+ }
+ }
+
+ /**
+ * If we get this far, don't try again until
+ * the VRDisplayEnumerateInterval elapses
+ */
+ mLastDisplayEnumerationTime = TimeStamp::Now();
+
+ OpenShmem();
+
+ mEnumerationRequested = false;
+ // We must block until enumeration has completed in order
+ // to signal that the WebVR promise should be resolved at the
+ // right time.
+#if defined(MOZ_WIDGET_ANDROID)
+ // In Android, we need to make sure calling
+ // GeckoVRManager::SetExternalContext() from an external VR service
+ // before doing enumeration.
+ if (!mShmem->GetExternalShmem()) {
+ mShmem->CreateShMemForAndroid();
+ }
+ if (mShmem->GetExternalShmem()) {
+ mState = VRManagerState::Enumeration;
+ } else {
+ // Not connected to shmem, so no devices to enumerate.
+ mDisplayInfo.Clear();
+ DispatchVRDisplayInfoUpdate();
+ }
+#else
+
+ PushState();
+
+ /**
+ * We must start the VR Service thread
+ * and VR Process before enumeration.
+ * We don't want to start this until we will
+ * actualy enumerate, to avoid continuously
+ * re-launching the thread/process when
+ * no hardware is found or a VR software update
+ * is in progress
+ */
+ mServiceHost->StartService();
+ mState = VRManagerState::Enumeration;
+#endif // MOZ_WIDGET_ANDROID
+}
+
+void VRManager::ProcessManagerState_Idle_StartRuntimeDetection() {
+ MOZ_ASSERT(mState == VRManagerState::Idle);
+
+ OpenShmem();
+ mBrowserState.detectRuntimesOnly = true;
+ mRuntimeDetectionRequested = false;
+
+ // We must block until enumeration has completed in order
+ // to signal that the WebVR promise should be resolved at the
+ // right time.
+#if defined(MOZ_WIDGET_ANDROID)
+ // In Android, we need to make sure calling
+ // GeckoVRManager::SetExternalContext() from an external VR service
+ // before doing enumeration.
+ if (!mShmem->GetExternalShmem()) {
+ mShmem->CreateShMemForAndroid();
+ }
+ if (mShmem->GetExternalShmem()) {
+ mState = VRManagerState::RuntimeDetection;
+ } else {
+ // Not connected to shmem, so no runtimes to detect.
+ mRuntimeSupportFlags = VRDisplayCapabilityFlags::Cap_None;
+ mRuntimeDetectionCompleted = true;
+ DispatchRuntimeCapabilitiesUpdate();
+ }
+#else
+
+ PushState();
+
+ /**
+ * We must start the VR Service thread
+ * and VR Process before enumeration.
+ * We don't want to start this until we will
+ * actualy enumerate, to avoid continuously
+ * re-launching the thread/process when
+ * no hardware is found or a VR software update
+ * is in progress
+ */
+ mServiceHost->StartService();
+ mState = VRManagerState::RuntimeDetection;
+#endif // MOZ_WIDGET_ANDROID
+}
+
+void VRManager::ProcessManagerState_Idle() {
+ MOZ_ASSERT(mState == VRManagerState::Idle);
+
+ if (!mRuntimeDetectionCompleted) {
+ // Check if we should start detecting runtimes
+ // We must alwasy detect runtimes before doing anything
+ // else with the VR process.
+ // This will happen only once per browser startup.
+ if (mRuntimeDetectionRequested || mEnumerationRequested) {
+ ProcessManagerState_Idle_StartRuntimeDetection();
+ }
+ return;
+ }
+
+ // Check if we should start activating enumerating XR hardware
+ if (mRuntimeDetectionCompleted &&
+ (mVRDisplaysRequested || mEnumerationRequested)) {
+ ProcessManagerState_Idle_StartEnumeration();
+ }
+}
+
+void VRManager::ProcessManagerState_DetectRuntimes() {
+ MOZ_ASSERT(mState == VRManagerState::RuntimeDetection);
+ MOZ_ASSERT(mShmem != nullptr);
+
+ PullState();
+ if (mEnumerationCompleted) {
+ /**
+ * When mBrowserState.detectRuntimesOnly is set, the
+ * VRService and VR process will shut themselves down
+ * automatically after detecting runtimes.
+ * mEnumerationCompleted is also used in this case,
+ * but to mean "enumeration of runtimes" not
+ * "enumeration of VR devices".
+ *
+ * We set mState to `VRManagerState::Stopping`
+ * to make sure that we don't try to do anything
+ * else with the active VRService until it has stopped.
+ * We must start another one when an XR session will be
+ * requested.
+ *
+ * This logic is optimized for the WebXR design, but still
+ * works for WebVR so it can continue to function until
+ * deprecated and removed.
+ */
+ mState = VRManagerState::Stopping;
+ mRuntimeSupportFlags = mDisplayInfo.mDisplayState.capabilityFlags &
+ (VRDisplayCapabilityFlags::Cap_ImmersiveVR |
+ VRDisplayCapabilityFlags::Cap_ImmersiveAR |
+ VRDisplayCapabilityFlags::Cap_Inline);
+ mRuntimeDetectionCompleted = true;
+ DispatchRuntimeCapabilitiesUpdate();
+ }
+}
+
+void VRManager::ProcessManagerState_Enumeration() {
+ MOZ_ASSERT(mState == VRManagerState::Enumeration);
+ MOZ_ASSERT(mShmem != nullptr);
+
+ PullState();
+ if (mEnumerationCompleted) {
+ if (mDisplayInfo.mDisplayState.isConnected) {
+ mDisplayInfo.mDisplayID = VRManager::AllocateDisplayID();
+ mState = VRManagerState::Active;
+ } else {
+ mDisplayInfo.Clear();
+ mState = VRManagerState::Stopping;
+ }
+ DispatchVRDisplayInfoUpdate();
+ }
+}
+
+void VRManager::ProcessManagerState_Active() {
+ MOZ_ASSERT(mState == VRManagerState::Active);
+
+ if (mDisplayInfo != mLastUpdateDisplayInfo) {
+ // While the display is active, send continuous updates
+ DispatchVRDisplayInfoUpdate();
+ }
+}
+
+void VRManager::DispatchVRDisplayInfoUpdate() {
+ for (auto iter = mVRManagerParents.Iter(); !iter.Done(); iter.Next()) {
+ Unused << iter.Get()->GetKey()->SendUpdateDisplayInfo(mDisplayInfo);
+ }
+ mLastUpdateDisplayInfo = mDisplayInfo;
+}
+
+void VRManager::DispatchRuntimeCapabilitiesUpdate() {
+ VRDisplayCapabilityFlags flags = mRuntimeSupportFlags;
+ if (StaticPrefs::dom_vr_always_support_vr()) {
+ flags |= VRDisplayCapabilityFlags::Cap_ImmersiveVR;
+ }
+
+ if (StaticPrefs::dom_vr_always_support_ar()) {
+ flags |= VRDisplayCapabilityFlags::Cap_ImmersiveAR;
+ }
+
+ for (auto iter = mVRManagerParents.Iter(); !iter.Done(); iter.Next()) {
+ Unused << iter.Get()->GetKey()->SendUpdateRuntimeCapabilities(flags);
+ }
+}
+
+void VRManager::StopAllHaptics() {
+ for (size_t i = 0; i < mozilla::ArrayLength(mBrowserState.hapticState); i++) {
+ ClearHapticSlot(i);
+ }
+ PushState();
+}
+
+void VRManager::VibrateHaptic(GamepadHandle aGamepadHandle,
+ uint32_t aHapticIndex, double aIntensity,
+ double aDuration,
+ const VRManagerPromise& aPromise)
+
+{
+ if (mState != VRManagerState::Active) {
+ return;
+ }
+ // VRDisplayClient::FireGamepadEvents() assigns a controller ID with
+ // ranges based on displayID. We must translate this to the indexes
+ // understood by VRDisplayExternal.
+ uint32_t controllerBaseIndex =
+ kVRControllerMaxCount * mDisplayInfo.mDisplayID;
+ uint32_t controllerIndex = aGamepadHandle.GetValue() - controllerBaseIndex;
+
+ TimeStamp now = TimeStamp::Now();
+ size_t bestSlotIndex = 0;
+ // Default to an empty slot, or the slot holding the oldest haptic pulse
+ for (size_t i = 0; i < mozilla::ArrayLength(mBrowserState.hapticState); i++) {
+ const VRHapticState& state = mBrowserState.hapticState[i];
+ if (state.inputFrameID == 0) {
+ // Unused slot, use it
+ bestSlotIndex = i;
+ break;
+ }
+ if (mHapticPulseRemaining[i] < mHapticPulseRemaining[bestSlotIndex]) {
+ // If no empty slots are available, fall back to overriding
+ // the pulse which is ending soonest.
+ bestSlotIndex = i;
+ }
+ }
+ // Override the last pulse on the same actuator if present.
+ for (size_t i = 0; i < mozilla::ArrayLength(mBrowserState.hapticState); i++) {
+ const VRHapticState& state = mBrowserState.hapticState[i];
+ if (state.inputFrameID == 0) {
+ // This is an empty slot -- no match
+ continue;
+ }
+ if (state.controllerIndex == controllerIndex &&
+ state.hapticIndex == aHapticIndex) {
+ // Found pulse on same actuator -- let's override it.
+ bestSlotIndex = i;
+ }
+ }
+ ClearHapticSlot(bestSlotIndex);
+
+ // Populate the selected slot with new haptic state
+ size_t bufferIndex = mDisplayInfo.mFrameId % kVRMaxLatencyFrames;
+ VRHapticState& bestSlot = mBrowserState.hapticState[bestSlotIndex];
+ bestSlot.inputFrameID =
+ mDisplayInfo.mLastSensorState[bufferIndex].inputFrameID;
+ bestSlot.controllerIndex = controllerIndex;
+ bestSlot.hapticIndex = aHapticIndex;
+ bestSlot.pulseStart = (float)(now - mLastFrameStart[bufferIndex]).ToSeconds();
+ bestSlot.pulseDuration =
+ (float)aDuration * 0.001f; // Convert from ms to seconds
+ bestSlot.pulseIntensity = (float)aIntensity;
+
+ mHapticPulseRemaining[bestSlotIndex] = aDuration;
+ MOZ_ASSERT(bestSlotIndex <= mHapticPromises.Length());
+ if (bestSlotIndex == mHapticPromises.Length()) {
+ mHapticPromises.AppendElement(
+ UniquePtr<VRManagerPromise>(new VRManagerPromise(aPromise)));
+ } else {
+ mHapticPromises[bestSlotIndex] =
+ UniquePtr<VRManagerPromise>(new VRManagerPromise(aPromise));
+ }
+ PushState();
+}
+
+void VRManager::StopVibrateHaptic(GamepadHandle aGamepadHandle) {
+ if (mState != VRManagerState::Active) {
+ return;
+ }
+ // VRDisplayClient::FireGamepadEvents() assigns a controller ID with
+ // ranges based on displayID. We must translate this to the indexes
+ // understood by VRDisplayExternal.
+ uint32_t controllerBaseIndex =
+ kVRControllerMaxCount * mDisplayInfo.mDisplayID;
+ uint32_t controllerIndex = aGamepadHandle.GetValue() - controllerBaseIndex;
+
+ for (size_t i = 0; i < mozilla::ArrayLength(mBrowserState.hapticState); i++) {
+ VRHapticState& state = mBrowserState.hapticState[i];
+ if (state.controllerIndex == controllerIndex) {
+ memset(&state, 0, sizeof(VRHapticState));
+ }
+ }
+ PushState();
+}
+
+void VRManager::NotifyVibrateHapticCompleted(const VRManagerPromise& aPromise) {
+ aPromise.mParent->SendReplyGamepadVibrateHaptic(aPromise.mPromiseID);
+}
+
+void VRManager::StartVRNavigation(const uint32_t& aDisplayID) {
+ if (mState != VRManagerState::Active) {
+ return;
+ }
+ /**
+ * We only support a single VRSession with a single VR display at a
+ * time; however, due to the asynchronous nature of the API, it's possible
+ * that the previously used VR display was a different one than the one now
+ * allocated. We catch these cases to avoid automatically activating the new
+ * VR displays. This situation is expected to be very rare and possibly never
+ * seen. Perhaps further simplification could be made in the content process
+ * code which passes around displayID's that may no longer be needed.
+ **/
+ if (mDisplayInfo.GetDisplayID() != aDisplayID) {
+ return;
+ }
+ mBrowserState.navigationTransitionActive = true;
+ mVRNavigationTransitionEnd = TimeStamp();
+ PushState();
+}
+
+void VRManager::StopVRNavigation(const uint32_t& aDisplayID,
+ const TimeDuration& aTimeout) {
+ if (mState != VRManagerState::Active) {
+ return;
+ }
+ if (mDisplayInfo.GetDisplayID() != aDisplayID) {
+ return;
+ }
+ if (aTimeout.ToMilliseconds() <= 0) {
+ mBrowserState.navigationTransitionActive = false;
+ mVRNavigationTransitionEnd = TimeStamp();
+ PushState();
+ }
+ mVRNavigationTransitionEnd = TimeStamp::Now() + aTimeout;
+}
+
+#if !defined(MOZ_WIDGET_ANDROID)
+
+bool VRManager::RunPuppet(const nsTArray<uint64_t>& aBuffer,
+ VRManagerParent* aManagerParent) {
+ if (!StaticPrefs::dom_vr_puppet_enabled()) {
+ // Sanity check to ensure that a compromised content process
+ // can't use this to escalate permissions.
+ return false;
+ }
+ if (mManagerParentRunningPuppet != nullptr) {
+ // Only one parent may run a puppet at a time
+ return false;
+ }
+ mManagerParentRunningPuppet = aManagerParent;
+ mServiceHost->PuppetSubmit(aBuffer);
+ return true;
+}
+
+void VRManager::ResetPuppet(VRManagerParent* aManagerParent) {
+ mManagerParentsWaitingForPuppetReset.PutEntry(aManagerParent);
+ if (mManagerParentRunningPuppet != nullptr) {
+ Unused << mManagerParentRunningPuppet
+ ->SendNotifyPuppetCommandBufferCompleted(false);
+ mManagerParentRunningPuppet = nullptr;
+ }
+ mServiceHost->PuppetReset();
+ // In the event that we are shut down, the task timer won't be running
+ // to trigger CheckForPuppetCompletion.
+ // In this case, CheckForPuppetCompletion() would immediately resolve
+ // the promises for mManagerParentsWaitingForPuppetReset.
+ // We can simply call it once here to handle that case.
+ CheckForPuppetCompletion();
+}
+
+#endif // !defined(MOZ_WIDGET_ANDROID)
+
+void VRManager::PullState(
+ const std::function<bool()>& aWaitCondition /* = nullptr */) {
+ if (mShmem != nullptr) {
+ mShmem->PullSystemState(mDisplayInfo.mDisplayState, mLastSensorState,
+ mDisplayInfo.mControllerState,
+ mEnumerationCompleted, aWaitCondition);
+ }
+}
+
+void VRManager::PushState(bool aNotifyCond) {
+ if (mShmem != nullptr) {
+ mShmem->PushBrowserState(mBrowserState, aNotifyCond);
+ }
+}
+
+void VRManager::Destroy() {
+ if (mState == VRManagerState::Disabled) {
+ return;
+ }
+ Shutdown();
+ StopTasks();
+ mState = VRManagerState::Disabled;
+}
+
+void VRManager::Shutdown() {
+ if (mState == VRManagerState::Disabled || mState == VRManagerState::Idle) {
+ return;
+ }
+
+ if (mDisplayInfo.mDisplayState.shutdown) {
+ // Shutdown was requested by VR Service, so we must throttle
+ // as requested by the VR Service
+ TimeStamp now = TimeStamp::Now();
+ mEarliestRestartTime =
+ now + TimeDuration::FromMilliseconds(
+ (double)mDisplayInfo.mDisplayState.minRestartInterval);
+ }
+
+ StopAllHaptics();
+ StopPresentation();
+ CancelCurrentSubmitTask();
+ ShutdownSubmitThread();
+
+ mDisplayInfo.Clear();
+ mEnumerationCompleted = false;
+
+ if (mState == VRManagerState::RuntimeDetection) {
+ /**
+ * We have failed to detect runtimes before shutting down.
+ * Ensure that promises are resolved
+ *
+ * This call to DispatchRuntimeCapabilitiesUpdate will only
+ * happen when we have failed to detect runtimes. In that case,
+ * mRuntimeSupportFlags will be 0 and send the correct message
+ * to the content process.
+ *
+ * When we are successful, we store the result in mRuntimeSupportFlags
+ * and never try again unless the browser is restarted. mRuntimeSupportFlags
+ * is never reset back to 0 in that case but we will never re-enter the
+ * VRManagerState::RuntimeDetection state and hit this code path again.
+ */
+ DispatchRuntimeCapabilitiesUpdate();
+ }
+
+ if (mState == VRManagerState::Enumeration) {
+ // We have failed to enumerate VR devices before shutting down.
+ // Ensure that promises are resolved
+ DispatchVRDisplayInfoUpdate();
+ }
+
+#if !defined(MOZ_WIDGET_ANDROID)
+ mServiceHost->StopService();
+#endif
+ mState = VRManagerState::Idle;
+
+ // We will close Shmem in the DTOR to avoid
+ // mSubmitThread is still running but its shmem
+ // has been released.
+}
+
+void VRManager::ShutdownVRManagerParents() {
+ // Close removes the CanvasParent from the set so take a copy first.
+ VRManagerParentSet vrManagerParents;
+ for (auto iter = mVRManagerParents.Iter(); !iter.Done(); iter.Next()) {
+ vrManagerParents.PutEntry(iter.Get()->GetKey());
+ }
+
+ for (auto iter = vrManagerParents.Iter(); !iter.Done(); iter.Next()) {
+ iter.Get()->GetKey()->Close();
+ iter.Remove();
+ }
+
+ MOZ_DIAGNOSTIC_ASSERT(mVRManagerParents.IsEmpty(),
+ "Closing should have cleared all entries.");
+}
+
+void VRManager::CheckWatchDog() {
+ /**
+ * We will trigger a new frame immediately after a successful frame
+ * texture submission. If content fails to call VRDisplay.submitFrame
+ * after dom.vr.display.rafMaxDuration milliseconds has elapsed since the
+ * last VRDisplay.requestAnimationFrame, we act as a "watchdog" and
+ * kick-off a new VRDisplay.requestAnimationFrame to avoid a render loop
+ * stall and to give content a chance to recover.
+ *
+ * If the lower level VR platform API's are rejecting submitted frames,
+ * such as when the Oculus "Health and Safety Warning" is displayed,
+ * we will not kick off the next frame immediately after
+ * VRDisplay.submitFrame as it would result in an unthrottled render loop
+ * that would free run at potentially extreme frame rates. To ensure that
+ * content has a chance to resume its presentation when the frames are
+ * accepted once again, we rely on this "watchdog" to act as a VR refresh
+ * driver cycling at a rate defined by dom.vr.display.rafMaxDuration.
+ *
+ * This number must be larger than the slowest expected frame time during
+ * normal VR presentation, but small enough not to break content that
+ * makes assumptions of reasonably minimal VSync rate.
+ *
+ * The slowest expected refresh rate for a VR display currently is an
+ * Oculus CV1 when ASW (Asynchronous Space Warp) is enabled, at 45hz.
+ * A dom.vr.display.rafMaxDuration value of 50 milliseconds results in a
+ * 20hz rate, which avoids inadvertent triggering of the watchdog during
+ * Oculus ASW even if every second frame is dropped.
+ */
+ if (mState != VRManagerState::Active) {
+ return;
+ }
+ bool bShouldStartFrame = false;
+
+ // If content fails to call VRDisplay.submitFrame, we must eventually
+ // time-out and trigger a new frame.
+ TimeStamp lastFrameStart =
+ mLastFrameStart[mDisplayInfo.mFrameId % kVRMaxLatencyFrames];
+ if (lastFrameStart.IsNull()) {
+ bShouldStartFrame = true;
+ } else {
+ TimeDuration duration = TimeStamp::Now() - lastFrameStart;
+ if (duration.ToMilliseconds() >
+ StaticPrefs::dom_vr_display_rafMaxDuration()) {
+ bShouldStartFrame = true;
+ }
+ }
+
+ if (bShouldStartFrame) {
+ StartFrame();
+ }
+}
+
+void VRManager::ExpireNavigationTransition() {
+ if (mState != VRManagerState::Active) {
+ return;
+ }
+ if (!mVRNavigationTransitionEnd.IsNull() &&
+ TimeStamp::Now() > mVRNavigationTransitionEnd) {
+ mBrowserState.navigationTransitionActive = false;
+ }
+}
+
+void VRManager::UpdateHaptics(double aDeltaTime) {
+ if (mState != VRManagerState::Active) {
+ return;
+ }
+ bool bNeedPush = false;
+ // Check for any haptic pulses that have ended and clear them
+ for (size_t i = 0; i < mozilla::ArrayLength(mBrowserState.hapticState); i++) {
+ const VRHapticState& state = mBrowserState.hapticState[i];
+ if (state.inputFrameID == 0) {
+ // Nothing in this slot
+ continue;
+ }
+ mHapticPulseRemaining[i] -= aDeltaTime;
+ if (mHapticPulseRemaining[i] <= 0.0f) {
+ // The pulse has finished
+ ClearHapticSlot(i);
+ bNeedPush = true;
+ }
+ }
+ if (bNeedPush) {
+ PushState();
+ }
+}
+
+void VRManager::ClearHapticSlot(size_t aSlot) {
+ MOZ_ASSERT(aSlot < mozilla::ArrayLength(mBrowserState.hapticState));
+ memset(&mBrowserState.hapticState[aSlot], 0, sizeof(VRHapticState));
+ mHapticPulseRemaining[aSlot] = 0.0f;
+ if (aSlot < mHapticPromises.Length() && mHapticPromises[aSlot]) {
+ NotifyVibrateHapticCompleted(*(mHapticPromises[aSlot]));
+ mHapticPromises[aSlot] = nullptr;
+ }
+}
+
+void VRManager::ShutdownSubmitThread() {
+ if (mSubmitThread) {
+ mSubmitThread->Shutdown();
+ mSubmitThread = nullptr;
+ }
+}
+
+void VRManager::StartPresentation() {
+ if (mState != VRManagerState::Active) {
+ return;
+ }
+ if (mBrowserState.presentationActive) {
+ return;
+ }
+ mTelemetry.Clear();
+ mTelemetry.mPresentationStart = TimeStamp::Now();
+
+ // Indicate that we are ready to start immersive mode
+ mBrowserState.presentationActive = true;
+ mBrowserState.layerState[0].type = VRLayerType::LayerType_Stereo_Immersive;
+ PushState();
+
+ mDisplayInfo.mDisplayState.lastSubmittedFrameId = 0;
+ if (mDisplayInfo.mDisplayState.reportsDroppedFrames) {
+ mTelemetry.mLastDroppedFrameCount =
+ mDisplayInfo.mDisplayState.droppedFrameCount;
+ }
+
+ mLastSubmittedFrameId = 0;
+ mLastStartedFrame = 0;
+}
+
+void VRManager::StopPresentation() {
+ if (mState != VRManagerState::Active) {
+ return;
+ }
+ if (!mBrowserState.presentationActive) {
+ return;
+ }
+
+ // Indicate that we have stopped immersive mode
+ mBrowserState.presentationActive = false;
+ memset(mBrowserState.layerState, 0,
+ sizeof(VRLayerState) * mozilla::ArrayLength(mBrowserState.layerState));
+
+ PushState(true);
+
+ Telemetry::HistogramID timeSpentID = Telemetry::HistogramCount;
+ Telemetry::HistogramID droppedFramesID = Telemetry::HistogramCount;
+ int viewIn = 0;
+
+ if (mDisplayInfo.mDisplayState.eightCC ==
+ GFX_VR_EIGHTCC('O', 'c', 'u', 'l', 'u', 's', ' ', 'D')) {
+ // Oculus Desktop API
+ timeSpentID = Telemetry::WEBVR_TIME_SPENT_VIEWING_IN_OCULUS;
+ droppedFramesID = Telemetry::WEBVR_DROPPED_FRAMES_IN_OCULUS;
+ viewIn = 1;
+ } else if (mDisplayInfo.mDisplayState.eightCC ==
+ GFX_VR_EIGHTCC('O', 'p', 'e', 'n', 'V', 'R', ' ', ' ')) {
+ // OpenVR API
+ timeSpentID = Telemetry::WEBVR_TIME_SPENT_VIEWING_IN_OPENVR;
+ droppedFramesID = Telemetry::WEBVR_DROPPED_FRAMES_IN_OPENVR;
+ viewIn = 2;
+ }
+
+ if (viewIn) {
+ const TimeDuration duration =
+ TimeStamp::Now() - mTelemetry.mPresentationStart;
+ Telemetry::Accumulate(Telemetry::WEBVR_USERS_VIEW_IN, viewIn);
+ Telemetry::Accumulate(timeSpentID, duration.ToMilliseconds());
+ const uint32_t droppedFramesPerSec =
+ (uint32_t)((double)(mDisplayInfo.mDisplayState.droppedFrameCount -
+ mTelemetry.mLastDroppedFrameCount) /
+ duration.ToSeconds());
+ Telemetry::Accumulate(droppedFramesID, droppedFramesPerSec);
+ }
+}
+
+bool VRManager::IsPresenting() {
+ if (mShmem) {
+ return mDisplayInfo.mPresentingGroups != 0;
+ }
+ return false;
+}
+
+void VRManager::SetGroupMask(uint32_t aGroupMask) {
+ if (mState != VRManagerState::Active) {
+ return;
+ }
+ mDisplayInfo.mGroupMask = aGroupMask;
+}
+
+void VRManager::SubmitFrame(VRLayerParent* aLayer,
+ const layers::SurfaceDescriptor& aTexture,
+ uint64_t aFrameId, const gfx::Rect& aLeftEyeRect,
+ const gfx::Rect& aRightEyeRect) {
+ if (mState != VRManagerState::Active) {
+ return;
+ }
+ MonitorAutoLock lock(mCurrentSubmitTaskMonitor);
+ if ((mDisplayInfo.mGroupMask & aLayer->GetGroup()) == 0) {
+ // Suppress layers hidden by the group mask
+ return;
+ }
+
+ // Ensure that we only accept the first SubmitFrame call per RAF cycle.
+ if (!mFrameStarted || aFrameId != mDisplayInfo.mFrameId) {
+ return;
+ }
+
+ /**
+ * Do not queue more submit frames until the last submitted frame is
+ * already processed and the new WebGL texture is ready.
+ */
+ if (mLastSubmittedFrameId > 0 &&
+ mLastSubmittedFrameId !=
+ mDisplayInfo.mDisplayState.lastSubmittedFrameId) {
+ mLastStartedFrame = 0;
+ return;
+ }
+
+ mLastSubmittedFrameId = aFrameId;
+
+ mFrameStarted = false;
+
+ RefPtr<CancelableRunnable> task = NewCancelableRunnableMethod<
+ StoreCopyPassByConstLRef<layers::SurfaceDescriptor>, uint64_t,
+ StoreCopyPassByConstLRef<gfx::Rect>, StoreCopyPassByConstLRef<gfx::Rect>>(
+ "gfx::VRManager::SubmitFrameInternal", this,
+ &VRManager::SubmitFrameInternal, aTexture, aFrameId, aLeftEyeRect,
+ aRightEyeRect);
+
+ if (!mCurrentSubmitTask) {
+ mCurrentSubmitTask = task;
+#if !defined(MOZ_WIDGET_ANDROID)
+ if (!mSubmitThread) {
+ mSubmitThread = new VRThread("VR_SubmitFrame"_ns);
+ }
+ mSubmitThread->Start();
+ mSubmitThread->PostTask(task.forget());
+#else
+ CompositorThread()->Dispatch(task.forget());
+#endif // defined(MOZ_WIDGET_ANDROID)
+ }
+}
+
+bool VRManager::SubmitFrame(const layers::SurfaceDescriptor& aTexture,
+ uint64_t aFrameId, const gfx::Rect& aLeftEyeRect,
+ const gfx::Rect& aRightEyeRect) {
+ if (mState != VRManagerState::Active) {
+ return false;
+ }
+#if defined(XP_WIN) || defined(XP_MACOSX) || defined(MOZ_WIDGET_ANDROID)
+ MOZ_ASSERT(mBrowserState.layerState[0].type ==
+ VRLayerType::LayerType_Stereo_Immersive);
+ VRLayer_Stereo_Immersive& layer =
+ mBrowserState.layerState[0].layer_stereo_immersive;
+
+ switch (aTexture.type()) {
+# if defined(XP_WIN)
+ case SurfaceDescriptor::TSurfaceDescriptorD3D10: {
+ const SurfaceDescriptorD3D10& surf =
+ aTexture.get_SurfaceDescriptorD3D10();
+ layer.textureType =
+ VRLayerTextureType::LayerTextureType_D3D10SurfaceDescriptor;
+ layer.textureHandle = (void*)surf.handle();
+ layer.textureSize.width = surf.size().width;
+ layer.textureSize.height = surf.size().height;
+ } break;
+# elif defined(XP_MACOSX)
+ case SurfaceDescriptor::TSurfaceDescriptorMacIOSurface: {
+ // MacIOSurface ptr can't be fetched or used at different threads.
+ // Both of fetching and using this MacIOSurface are at the VRService
+ // thread.
+ const auto& desc = aTexture.get_SurfaceDescriptorMacIOSurface();
+ layer.textureType = VRLayerTextureType::LayerTextureType_MacIOSurface;
+ layer.textureHandle = desc.surfaceId();
+ RefPtr<MacIOSurface> surf =
+ MacIOSurface::LookupSurface(desc.surfaceId(), desc.scaleFactor(),
+ !desc.isOpaque(), desc.yUVColorSpace());
+ if (surf) {
+ layer.textureSize.width = surf->GetDevicePixelWidth();
+ layer.textureSize.height = surf->GetDevicePixelHeight();
+ }
+ } break;
+# elif defined(MOZ_WIDGET_ANDROID)
+ case SurfaceDescriptor::TSurfaceTextureDescriptor: {
+ const SurfaceTextureDescriptor& desc =
+ aTexture.get_SurfaceTextureDescriptor();
+ java::GeckoSurfaceTexture::LocalRef surfaceTexture =
+ java::GeckoSurfaceTexture::Lookup(desc.handle());
+ if (!surfaceTexture) {
+ NS_WARNING("VRManager::SubmitFrame failed to get a SurfaceTexture");
+ return false;
+ }
+ layer.textureType =
+ VRLayerTextureType::LayerTextureType_GeckoSurfaceTexture;
+ layer.textureHandle = desc.handle();
+ layer.textureSize.width = desc.size().width;
+ layer.textureSize.height = desc.size().height;
+ } break;
+# endif
+ default: {
+ MOZ_ASSERT(false);
+ return false;
+ }
+ }
+
+ layer.frameId = aFrameId;
+ layer.inputFrameId =
+ mDisplayInfo.mLastSensorState[mDisplayInfo.mFrameId % kVRMaxLatencyFrames]
+ .inputFrameID;
+
+ layer.leftEyeRect.x = aLeftEyeRect.x;
+ layer.leftEyeRect.y = aLeftEyeRect.y;
+ layer.leftEyeRect.width = aLeftEyeRect.width;
+ layer.leftEyeRect.height = aLeftEyeRect.height;
+ layer.rightEyeRect.x = aRightEyeRect.x;
+ layer.rightEyeRect.y = aRightEyeRect.y;
+ layer.rightEyeRect.width = aRightEyeRect.width;
+ layer.rightEyeRect.height = aRightEyeRect.height;
+
+ PushState(true);
+
+ PullState([&]() {
+ return (mDisplayInfo.mDisplayState.lastSubmittedFrameId >= aFrameId) ||
+ mDisplayInfo.mDisplayState.suppressFrames ||
+ !mDisplayInfo.mDisplayState.isConnected;
+ });
+
+ if (mDisplayInfo.mDisplayState.suppressFrames ||
+ !mDisplayInfo.mDisplayState.isConnected) {
+ // External implementation wants to supress frames, service has shut
+ // down or hardware has been disconnected.
+ return false;
+ }
+
+ return mDisplayInfo.mDisplayState.lastSubmittedFrameSuccessful;
+#else
+ MOZ_ASSERT(false); // Not implmented for this platform
+ return false;
+#endif
+}
+
+void VRManager::SubmitFrameInternal(const layers::SurfaceDescriptor& aTexture,
+ uint64_t aFrameId,
+ const gfx::Rect& aLeftEyeRect,
+ const gfx::Rect& aRightEyeRect) {
+#if !defined(MOZ_WIDGET_ANDROID)
+ MOZ_ASSERT(mSubmitThread->GetThread() == NS_GetCurrentThread());
+#endif // !defined(MOZ_WIDGET_ANDROID)
+ AUTO_PROFILER_TRACING_MARKER("VR", "SubmitFrameAtVRDisplayExternal", OTHER);
+
+ { // scope lock
+ MonitorAutoLock lock(mCurrentSubmitTaskMonitor);
+
+ if (!SubmitFrame(aTexture, aFrameId, aLeftEyeRect, aRightEyeRect)) {
+ mCurrentSubmitTask = nullptr;
+ return;
+ }
+ mCurrentSubmitTask = nullptr;
+ }
+
+#if defined(XP_WIN) || defined(XP_MACOSX)
+
+ /**
+ * Trigger the next VSync immediately after we are successfully
+ * submitting frames. As SubmitFrame is responsible for throttling
+ * the render loop, if we don't successfully call it, we shouldn't trigger
+ * StartFrame immediately, as it will run unbounded.
+ * If StartFrame is not called here due to SubmitFrame failing, the
+ * fallback "watchdog" code in VRManager::NotifyVSync() will cause
+ * frames to continue at a lower refresh rate until frame submission
+ * succeeds again.
+ */
+ CompositorThread()->Dispatch(NewRunnableMethod("gfx::VRManager::StartFrame",
+ this, &VRManager::StartFrame));
+#elif defined(MOZ_WIDGET_ANDROID)
+ // We are already in the CompositorThreadHolder event loop on Android.
+ StartFrame();
+#endif
+}
+
+void VRManager::CancelCurrentSubmitTask() {
+ MonitorAutoLock lock(mCurrentSubmitTaskMonitor);
+ if (mCurrentSubmitTask) {
+ mCurrentSubmitTask->Cancel();
+ mCurrentSubmitTask = nullptr;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// VRManager::nsIObserver
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+VRManager::Observe(nsISupports* subject, const char* topic,
+ const char16_t* data) {
+ if (!strcmp(topic, "application-background")) {
+ // StopTasks() is called later in the timer thread based on this flag to
+ // avoid threading issues.
+ mAppPaused = true;
+ } else if (!strcmp(topic, "application-foreground") && mAppPaused) {
+ mAppPaused = false;
+ // When the apps goes the foreground (e.g. Android) we should restart the
+ // tasks.
+ StartTasks();
+ }
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(VRManager, nsIObserver)
+
+} // namespace mozilla::gfx
diff --git a/gfx/vr/VRManager.h b/gfx/vr/VRManager.h
new file mode 100644
index 0000000000..8abbad76da
--- /dev/null
+++ b/gfx/vr/VRManager.h
@@ -0,0 +1,194 @@
+/* -*- 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_MANAGER_H
+#define GFX_VR_MANAGER_H
+
+#include "nsHashKeys.h"
+#include "nsIObserver.h"
+#include "nsTArray.h"
+#include "nsThreadUtils.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/dom/GamepadHandle.h"
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
+#include "mozilla/Monitor.h"
+#include "mozilla/TimeStamp.h"
+#include "gfxVR.h"
+
+class nsITimer;
+namespace mozilla {
+namespace gfx {
+class VRLayerParent;
+class VRManagerParent;
+class VRServiceHost;
+class VRThread;
+class VRShMem;
+
+enum class VRManagerState : uint32_t {
+ Disabled, // All VRManager activity is stopped
+ Idle, // No VR hardware has been activated, but background tasks are running
+ RuntimeDetection, // Waiting for detection of runtimes without starting up VR
+ // hardware
+ Enumeration, // Waiting for enumeration and startup of VR hardware
+ Active, // VR hardware is active
+ Stopping, // Waiting for the VRService to stop
+};
+
+class VRManager : nsIObserver {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+ static void ManagerInit();
+ static VRManager* Get();
+ static VRManager* MaybeGet();
+
+ void AddVRManagerParent(VRManagerParent* aVRManagerParent);
+ void RemoveVRManagerParent(VRManagerParent* aVRManagerParent);
+
+ void NotifyVsync(const TimeStamp& aVsyncTimestamp);
+
+ void DetectRuntimes();
+ void EnumerateDevices();
+ void StopAllHaptics();
+
+ void VibrateHaptic(mozilla::dom::GamepadHandle aGamepadHandle,
+ uint32_t aHapticIndex, double aIntensity, double aDuration,
+ const VRManagerPromise& aPromise);
+ void StopVibrateHaptic(mozilla::dom::GamepadHandle aGamepadHandle);
+ void NotifyVibrateHapticCompleted(const VRManagerPromise& aPromise);
+ void StartVRNavigation(const uint32_t& aDisplayID);
+ void StopVRNavigation(const uint32_t& aDisplayID,
+ const TimeDuration& aTimeout);
+ void Shutdown();
+ void ShutdownVRManagerParents();
+#if !defined(MOZ_WIDGET_ANDROID)
+ bool RunPuppet(const nsTArray<uint64_t>& aBuffer,
+ VRManagerParent* aManagerParent);
+ void ResetPuppet(VRManagerParent* aManagerParent);
+ void NotifyPuppetComplete();
+#endif
+ void AddLayer(VRLayerParent* aLayer);
+ void RemoveLayer(VRLayerParent* aLayer);
+ void SetGroupMask(uint32_t aGroupMask);
+ void SubmitFrame(VRLayerParent* aLayer,
+ const layers::SurfaceDescriptor& aTexture, uint64_t aFrameId,
+ const gfx::Rect& aLeftEyeRect,
+ const gfx::Rect& aRightEyeRect);
+ bool IsPresenting();
+
+ private:
+ VRManager();
+ virtual ~VRManager();
+ void Destroy();
+ void StartTasks();
+ void StopTasks();
+ static void TaskTimerCallback(nsITimer* aTimer, void* aClosure);
+ void RunTasks();
+ void Run1msTasks(double aDeltaTime);
+ void Run10msTasks();
+ void Run100msTasks();
+ uint32_t GetOptimalTaskInterval();
+ void ProcessManagerState();
+ void ProcessManagerState_Disabled();
+ void ProcessManagerState_Idle();
+ void ProcessManagerState_Idle_StartRuntimeDetection();
+ void ProcessManagerState_Idle_StartEnumeration();
+ void ProcessManagerState_DetectRuntimes();
+ void ProcessManagerState_Enumeration();
+ void ProcessManagerState_Active();
+ void ProcessManagerState_Stopping();
+ void PullState(const std::function<bool()>& aWaitCondition = nullptr);
+ void PushState(const bool aNotifyCond = false);
+ static uint32_t AllocateDisplayID();
+ void DispatchVRDisplayInfoUpdate();
+ void DispatchRuntimeCapabilitiesUpdate();
+ void UpdateRequestedDevices();
+ void CheckForInactiveTimeout();
+#if !defined(MOZ_WIDGET_ANDROID)
+ void CheckForPuppetCompletion();
+#endif
+ void CheckForShutdown();
+ void CheckWatchDog();
+ void ExpireNavigationTransition();
+ void OpenShmem();
+ void CloseShmem();
+ void UpdateHaptics(double aDeltaTime);
+ void ClearHapticSlot(size_t aSlot);
+ void StartFrame();
+ void ShutdownSubmitThread();
+ void StartPresentation();
+ void StopPresentation();
+ void CancelCurrentSubmitTask();
+
+ void SubmitFrameInternal(const layers::SurfaceDescriptor& aTexture,
+ uint64_t aFrameId, const gfx::Rect& aLeftEyeRect,
+ const gfx::Rect& aRightEyeRect);
+ bool SubmitFrame(const layers::SurfaceDescriptor& aTexture, uint64_t aFrameId,
+ const gfx::Rect& aLeftEyeRect,
+ const gfx::Rect& aRightEyeRect);
+
+ Atomic<VRManagerState> mState;
+ typedef nsTHashtable<nsRefPtrHashKey<VRManagerParent>> VRManagerParentSet;
+ VRManagerParentSet mVRManagerParents;
+#if !defined(MOZ_WIDGET_ANDROID)
+ VRManagerParentSet mManagerParentsWaitingForPuppetReset;
+ RefPtr<VRManagerParent> mManagerParentRunningPuppet;
+#endif
+ // Weak reference to mLayers entries are cleared in
+ // VRLayerParent destructor
+ nsTArray<VRLayerParent*> mLayers;
+
+ TimeStamp mLastDisplayEnumerationTime;
+ TimeStamp mLastActiveTime;
+ TimeStamp mLastTickTime;
+ TimeStamp mEarliestRestartTime;
+ TimeStamp mVRNavigationTransitionEnd;
+ TimeStamp mLastFrameStart[kVRMaxLatencyFrames];
+ double mAccumulator100ms;
+ bool mRuntimeDetectionRequested;
+ bool mRuntimeDetectionCompleted;
+ bool mEnumerationRequested;
+ bool mEnumerationCompleted;
+ bool mVRDisplaysRequested;
+ bool mVRDisplaysRequestedNonFocus;
+ bool mVRControllersRequested;
+ bool mFrameStarted;
+ uint32_t mTaskInterval;
+ RefPtr<nsITimer> mTaskTimer;
+ mozilla::Monitor mCurrentSubmitTaskMonitor;
+ RefPtr<CancelableRunnable> mCurrentSubmitTask;
+ uint64_t mLastSubmittedFrameId;
+ uint64_t mLastStartedFrame;
+ VRDisplayCapabilityFlags mRuntimeSupportFlags;
+ bool mAppPaused;
+
+ // Note: mShmem doesn't support RefPtr; thus, do not share this private
+ // pointer so that its lifetime can still be controlled by VRManager
+ VRShMem* mShmem;
+ bool mVRProcessEnabled;
+
+#if !defined(MOZ_WIDGET_ANDROID)
+ RefPtr<VRServiceHost> mServiceHost;
+#endif
+
+ static Atomic<uint32_t> sDisplayBase;
+ RefPtr<VRThread> mSubmitThread;
+ VRTelemetry mTelemetry;
+ nsTArray<UniquePtr<VRManagerPromise>> mHapticPromises;
+ // Duration of haptic pulse time remaining (milliseconds)
+ double mHapticPulseRemaining[kVRHapticsMaxCount];
+
+ VRDisplayInfo mDisplayInfo;
+ VRDisplayInfo mLastUpdateDisplayInfo;
+ VRBrowserState mBrowserState;
+ VRHMDSensorState mLastSensorState;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // GFX_VR_MANAGER_H
diff --git a/gfx/vr/VRPuppetCommandBuffer.cpp b/gfx/vr/VRPuppetCommandBuffer.cpp
new file mode 100644
index 0000000000..310a233b88
--- /dev/null
+++ b/gfx/vr/VRPuppetCommandBuffer.cpp
@@ -0,0 +1,515 @@
+/* -*- 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 "VRPuppetCommandBuffer.h"
+#include "prthread.h"
+#include "mozilla/ClearOnShutdown.h"
+
+namespace mozilla::gfx {
+
+static StaticRefPtr<VRPuppetCommandBuffer> sVRPuppetCommandBufferSingleton;
+
+/* static */
+VRPuppetCommandBuffer& VRPuppetCommandBuffer::Get() {
+ if (sVRPuppetCommandBufferSingleton == nullptr) {
+ sVRPuppetCommandBufferSingleton = new VRPuppetCommandBuffer();
+ ClearOnShutdown(&sVRPuppetCommandBufferSingleton);
+ }
+ return *sVRPuppetCommandBufferSingleton;
+}
+
+/* static */
+bool VRPuppetCommandBuffer::IsCreated() {
+ return sVRPuppetCommandBufferSingleton != nullptr;
+}
+
+VRPuppetCommandBuffer::VRPuppetCommandBuffer()
+ : mMutex("VRPuppetCommandBuffer::mMutex") {
+ MOZ_COUNT_CTOR(VRPuppetCommandBuffer);
+ MOZ_ASSERT(sVRPuppetCommandBufferSingleton == nullptr);
+ Reset();
+}
+
+VRPuppetCommandBuffer::~VRPuppetCommandBuffer() {
+ MOZ_COUNT_DTOR(VRPuppetCommandBuffer);
+}
+
+void VRPuppetCommandBuffer::Submit(const nsTArray<uint64_t>& aBuffer) {
+ MutexAutoLock lock(mMutex);
+ mBuffer.AppendElements(aBuffer);
+ mEnded = false;
+ mEndedWithTimeout = false;
+}
+
+bool VRPuppetCommandBuffer::HasEnded() {
+ MutexAutoLock lock(mMutex);
+ return mEnded;
+}
+
+void VRPuppetCommandBuffer::Reset() {
+ MutexAutoLock lock(mMutex);
+ memset(&mPendingState, 0, sizeof(VRSystemState));
+ memset(&mCommittedState, 0, sizeof(VRSystemState));
+ for (int iControllerIdx = 0; iControllerIdx < kVRControllerMaxCount;
+ iControllerIdx++) {
+ for (int iHaptic = 0; iHaptic < kNumPuppetHaptics; iHaptic++) {
+ mHapticPulseRemaining[iControllerIdx][iHaptic] = 0.0f;
+ mHapticPulseIntensity[iControllerIdx][iHaptic] = 0.0f;
+ }
+ }
+ mDataOffset = 0;
+ mPresentationRequested = false;
+ mFrameSubmitted = false;
+ mFrameAccepted = false;
+ mTimeoutDuration = 10.0f;
+ mWaitRemaining = 0.0f;
+ mBlockedTime = 0.0f;
+ mTimerElapsed = 0.0f;
+ mEnded = true;
+ mEndedWithTimeout = false;
+ mLastRunTimestamp = TimeStamp();
+ mTimerSamples.Clear();
+ mBuffer.Clear();
+}
+
+bool VRPuppetCommandBuffer::RunCommand(uint64_t aCommand, double aDeltaTime) {
+ /**
+ * Run a single command. If the command is blocking on a state change and
+ * can't be executed, return false.
+ *
+ * VRPuppetCommandBuffer::RunCommand is only called by
+ *VRPuppetCommandBuffer::Run(), which is already holding the mutex.
+ *
+ * Note that VRPuppetCommandBuffer::RunCommand may potentially be called >1000
+ *times per frame. It might not hurt to add an assert here, but we should
+ *avoid adding code which may potentially malloc (eg string handling) even for
+ *debug builds here. This function will need to be reasonably fast, even in
+ *debug builds which will be using it during tests.
+ **/
+ switch ((VRPuppet_Command)(aCommand & 0xff00000000000000)) {
+ case VRPuppet_Command::VRPuppet_End:
+ CompleteTest(false);
+ break;
+ case VRPuppet_Command::VRPuppet_ClearAll:
+ memset(&mPendingState, 0, sizeof(VRSystemState));
+ memset(&mCommittedState, 0, sizeof(VRSystemState));
+ mPresentationRequested = false;
+ mFrameSubmitted = false;
+ mFrameAccepted = false;
+ break;
+ case VRPuppet_Command::VRPuppet_ClearController: {
+ uint8_t controllerIdx = aCommand & 0x00000000000000ff;
+ if (controllerIdx < kVRControllerMaxCount) {
+ mPendingState.controllerState[controllerIdx].Clear();
+ }
+ } break;
+ case VRPuppet_Command::VRPuppet_Timeout:
+ mTimeoutDuration = (double)(aCommand & 0x00000000ffffffff) / 1000.0f;
+ break;
+ case VRPuppet_Command::VRPuppet_Wait:
+ if (mWaitRemaining == 0.0f) {
+ mWaitRemaining = (double)(aCommand & 0x00000000ffffffff) / 1000.0f;
+ // Wait timer started, block
+ return false;
+ }
+ mWaitRemaining -= aDeltaTime;
+ if (mWaitRemaining > 0.0f) {
+ // Wait timer still running, block
+ return false;
+ }
+ // Wait timer has elapsed, unblock
+ mWaitRemaining = 0.0f;
+ break;
+ case VRPuppet_Command::VRPuppet_WaitSubmit:
+ if (!mFrameSubmitted) {
+ return false;
+ }
+ break;
+ case VRPuppet_Command::VRPuppet_CaptureFrame:
+ // TODO - Capture the frame and record the output (Bug 1555180)
+ break;
+ case VRPuppet_Command::VRPuppet_AcknowledgeFrame:
+ mFrameSubmitted = false;
+ mFrameAccepted = true;
+ break;
+ case VRPuppet_Command::VRPuppet_RejectFrame:
+ mFrameSubmitted = false;
+ mFrameAccepted = false;
+ break;
+ case VRPuppet_Command::VRPuppet_WaitPresentationStart:
+ if (!mPresentationRequested) {
+ return false;
+ }
+ break;
+ case VRPuppet_Command::VRPuppet_WaitPresentationEnd:
+ if (mPresentationRequested) {
+ return false;
+ }
+ break;
+ case VRPuppet_Command::VRPuppet_WaitHapticIntensity: {
+ // 0x0800cchhvvvvvvvv - VRPuppet_WaitHapticIntensity(c, h, v)
+ uint8_t iControllerIdx = (aCommand & 0x0000ff0000000000) >> 40;
+ if (iControllerIdx >= kVRControllerMaxCount) {
+ // Puppet test is broken, ensure it fails
+ return false;
+ }
+ uint8_t iHapticIdx = (aCommand & 0x000000ff00000000) >> 32;
+ if (iHapticIdx >= kNumPuppetHaptics) {
+ // Puppet test is broken, ensure it fails
+ return false;
+ }
+ uint32_t iHapticIntensity =
+ aCommand & 0x00000000ffffffff; // interpreted as 16.16 fixed point
+
+ SimulateHaptics(aDeltaTime);
+ uint64_t iCurrentIntensity =
+ round(mHapticPulseIntensity[iControllerIdx][iHapticIdx] *
+ (1 << 16)); // convert to 16.16 fixed point
+ if (iCurrentIntensity > 0xffffffff) {
+ iCurrentIntensity = 0xffffffff;
+ }
+ if (iCurrentIntensity != iHapticIntensity) {
+ return false;
+ }
+ } break;
+
+ case VRPuppet_Command::VRPuppet_StartTimer:
+ mTimerElapsed = 0.0f;
+ break;
+ case VRPuppet_Command::VRPuppet_StopTimer:
+ mTimerSamples.AppendElements(mTimerElapsed);
+ // TODO - Return the timer samples to Javascript once the command buffer
+ // is complete (Bug 1555182)
+ break;
+
+ case VRPuppet_Command::VRPuppet_UpdateDisplay:
+ mDataOffset = (uint8_t*)&mPendingState.displayState -
+ (uint8_t*)&mPendingState + (aCommand & 0x00000000ffffffff);
+ break;
+ case VRPuppet_Command::VRPuppet_UpdateSensor:
+ mDataOffset = (uint8_t*)&mPendingState.sensorState -
+ (uint8_t*)&mPendingState + (aCommand & 0x00000000ffffffff);
+ break;
+ case VRPuppet_Command::VRPuppet_UpdateControllers:
+ mDataOffset = (uint8_t*)&mPendingState.controllerState -
+ (uint8_t*)&mPendingState + (aCommand & 0x00000000ffffffff);
+ break;
+ case VRPuppet_Command::VRPuppet_Commit:
+ memcpy(&mCommittedState, &mPendingState, sizeof(VRSystemState));
+ break;
+
+ case VRPuppet_Command::VRPuppet_Data7:
+ WriteData((aCommand & 0x00ff000000000000) >> 48);
+ [[fallthrough]];
+ // Purposefully, no break
+ case VRPuppet_Command::VRPuppet_Data6:
+ WriteData((aCommand & 0x0000ff0000000000) >> 40);
+ [[fallthrough]];
+ // Purposefully, no break
+ case VRPuppet_Command::VRPuppet_Data5:
+ WriteData((aCommand & 0x000000ff00000000) >> 32);
+ [[fallthrough]];
+ // Purposefully, no break
+ case VRPuppet_Command::VRPuppet_Data4:
+ WriteData((aCommand & 0x00000000ff000000) >> 24);
+ [[fallthrough]];
+ // Purposefully, no break
+ case VRPuppet_Command::VRPuppet_Data3:
+ WriteData((aCommand & 0x0000000000ff0000) >> 16);
+ [[fallthrough]];
+ // Purposefully, no break
+ case VRPuppet_Command::VRPuppet_Data2:
+ WriteData((aCommand & 0x000000000000ff00) >> 8);
+ [[fallthrough]];
+ // Purposefully, no break
+ case VRPuppet_Command::VRPuppet_Data1:
+ WriteData(aCommand & 0x00000000000000ff);
+ break;
+ }
+ return true;
+}
+
+void VRPuppetCommandBuffer::WriteData(uint8_t aData) {
+ if (mDataOffset && mDataOffset < sizeof(VRSystemState)) {
+ ((uint8_t*)&mPendingState)[mDataOffset++] = aData;
+ }
+}
+
+void VRPuppetCommandBuffer::Run() {
+ MutexAutoLock lock(mMutex);
+ TimeStamp now = TimeStamp::Now();
+ double deltaTime = 0.0f;
+ if (!mLastRunTimestamp.IsNull()) {
+ deltaTime = (now - mLastRunTimestamp).ToSeconds();
+ }
+ mLastRunTimestamp = now;
+ mTimerElapsed += deltaTime;
+ size_t transactionLength = 0;
+ while (transactionLength < mBuffer.Length() && !mEnded) {
+ if (RunCommand(mBuffer[transactionLength], deltaTime)) {
+ mBlockedTime = 0.0f;
+ transactionLength++;
+ } else {
+ mBlockedTime += deltaTime;
+ if (mBlockedTime > mTimeoutDuration) {
+ CompleteTest(true);
+ }
+ // If a command is blocked, we don't increment transactionLength,
+ // allowing the command to be retried on the next cycle
+ break;
+ }
+ }
+ mBuffer.RemoveElementsAt(0, transactionLength);
+}
+
+void VRPuppetCommandBuffer::Run(VRSystemState& aState) {
+ Run();
+ // We don't want to stomp over some members
+ bool bEnumerationCompleted = aState.enumerationCompleted;
+ bool bShutdown = aState.displayState.shutdown;
+ uint32_t minRestartInterval = aState.displayState.minRestartInterval;
+
+ // Overwrite it all
+ memcpy(&aState, &mCommittedState, sizeof(VRSystemState));
+
+ // Restore the members
+ aState.enumerationCompleted = bEnumerationCompleted;
+ aState.displayState.shutdown = bShutdown;
+ aState.displayState.minRestartInterval = minRestartInterval;
+}
+
+void VRPuppetCommandBuffer::StartPresentation() {
+ mPresentationRequested = true;
+ Run();
+}
+
+void VRPuppetCommandBuffer::StopPresentation() {
+ mPresentationRequested = false;
+ Run();
+}
+
+bool VRPuppetCommandBuffer::SubmitFrame() {
+ // Emulate blocking behavior of various XR API's as
+ // described by puppet script
+ mFrameSubmitted = true;
+ mFrameAccepted = false;
+ while (true) {
+ Run();
+ if (!mFrameSubmitted || mEnded) {
+ break;
+ }
+ PR_Sleep(PR_INTERVAL_NO_WAIT); // Yield
+ }
+
+ return mFrameAccepted;
+}
+
+void VRPuppetCommandBuffer::VibrateHaptic(uint32_t aControllerIdx,
+ uint32_t aHapticIndex,
+ float aIntensity, float aDuration) {
+ if (aHapticIndex >= kNumPuppetHaptics ||
+ aControllerIdx >= kVRControllerMaxCount) {
+ return;
+ }
+
+ // We must Run() before and after updating haptic state to avoid script
+ // deadlocks
+ // The deadlocks would be caused by scripts that include two
+ // VRPuppet_WaitHapticIntensity commands. If
+ // VRPuppetCommandBuffer::VibrateHaptic() is called twice without advancing
+ // through the command buffer with VRPuppetCommandBuffer::Run() in between,
+ // the first VRPuppet_WaitHapticInensity may not see the transient value that
+ // it is waiting for, thus blocking forever and deadlocking the script.
+ Run();
+ mHapticPulseRemaining[aControllerIdx][aHapticIndex] = aDuration;
+ mHapticPulseIntensity[aControllerIdx][aHapticIndex] = aIntensity;
+ Run();
+}
+
+void VRPuppetCommandBuffer::StopVibrateHaptic(uint32_t aControllerIdx) {
+ if (aControllerIdx >= kVRControllerMaxCount) {
+ return;
+ }
+ // We must Run() before and after updating haptic state to avoid script
+ // deadlocks
+ Run();
+ for (int iHaptic = 0; iHaptic < kNumPuppetHaptics; iHaptic++) {
+ mHapticPulseRemaining[aControllerIdx][iHaptic] = 0.0f;
+ mHapticPulseIntensity[aControllerIdx][iHaptic] = 0.0f;
+ }
+ Run();
+}
+
+void VRPuppetCommandBuffer::StopAllHaptics() {
+ // We must Run() before and after updating haptic state to avoid script
+ // deadlocks
+ Run();
+ for (int iControllerIdx = 0; iControllerIdx < kVRControllerMaxCount;
+ iControllerIdx++) {
+ for (int iHaptic = 0; iHaptic < kNumPuppetHaptics; iHaptic++) {
+ mHapticPulseRemaining[iControllerIdx][iHaptic] = 0.0f;
+ mHapticPulseIntensity[iControllerIdx][iHaptic] = 0.0f;
+ }
+ }
+ Run();
+}
+
+void VRPuppetCommandBuffer::SimulateHaptics(double aDeltaTime) {
+ for (int iControllerIdx = 0; iControllerIdx < kVRControllerMaxCount;
+ iControllerIdx++) {
+ for (int iHaptic = 0; iHaptic < kNumPuppetHaptics; iHaptic++) {
+ if (mHapticPulseIntensity[iControllerIdx][iHaptic] > 0.0f) {
+ mHapticPulseRemaining[iControllerIdx][iHaptic] -= aDeltaTime;
+ if (mHapticPulseRemaining[iControllerIdx][iHaptic] <= 0.0f) {
+ mHapticPulseRemaining[iControllerIdx][iHaptic] = 0.0f;
+ mHapticPulseIntensity[iControllerIdx][iHaptic] = 0.0f;
+ }
+ }
+ }
+ }
+}
+
+void VRPuppetCommandBuffer::CompleteTest(bool aTimedOut) {
+ mEndedWithTimeout = aTimedOut;
+ mEnded = true;
+}
+
+/**
+ * Generates a sequence of VRPuppet_Data# commands, as described
+ * in VRPuppetCommandBuffer.h, to encode the changes to be made to
+ * a "destination" structure to match the "source" structure.
+ * As the commands are encoded, the destination structure is updated
+ * to match the source.
+ *
+ * @param aBuffer
+ * The buffer in which the commands will be appended.
+ * @param aSrcStart
+ * Byte pointer to the start of the structure that
+ * will be copied from.
+ * @param aDstStart
+ * Byte pointer to the start of the structure that
+ * will be copied to.
+ * @param aLength
+ * Length of the structure that will be copied.
+ * @param aUpdateCommand
+ * VRPuppet_... command indicating which structure is being
+ * copied:
+ * VRPuppet_Command::VRPuppet_UpdateDisplay:
+ * A single VRDisplayState struct
+ * VRPuppet_Command::VRPuppet_UpdateSensor:
+ * A single VRHMDSensorState struct
+ * VRPuppet_Command::VRPuppet_UpdateControllers:
+ * An array of VRControllerState structs with a
+ * count of kVRControllerMaxCount
+ */
+void VRPuppetCommandBuffer::EncodeStruct(nsTArray<uint64_t>& aBuffer,
+ uint8_t* aSrcStart, uint8_t* aDstStart,
+ size_t aLength,
+ VRPuppet_Command aUpdateCommand) {
+ // Naive implementation, but will not be executed in realtime, so will not
+ // affect test timer results. Could be improved to avoid unaligned reads and
+ // to use SSE.
+
+ // Pointer to source byte being compared+copied
+ uint8_t* src = aSrcStart;
+
+ // Pointer to destination byte being compared+copied
+ uint8_t* dst = aDstStart;
+
+ // Number of bytes packed into bufData
+ uint8_t bufLen = 0;
+
+ // 64-bits to be interpreted as up to 7 separate bytes
+ // This will form the lower 56 bits of the command
+ uint64_t bufData = 0;
+
+ // purgebuffer takes the bytes stored in bufData and generates a VRPuppet
+ // command representing those bytes as "VRPuppet Data".
+ // VRPUppet_Data1 encodes 1 byte
+ // VRPuppet_Data2 encodes 2 bytes
+ // and so on, until..
+ // VRPuppet_Data7 encodes 7 bytes
+ // This command is appended to aBuffer, then bufLen and bufData are reset
+ auto purgeBuffer = [&]() {
+ // Only emit a command if there are data bytes in bufData
+ if (bufLen > 0) {
+ MOZ_ASSERT(bufLen <= 7);
+ uint64_t command = (uint64_t)VRPuppet_Command::VRPuppet_Data1;
+ command += ((uint64_t)VRPuppet_Command::VRPuppet_Data2 -
+ (uint64_t)VRPuppet_Command::VRPuppet_Data1) *
+ (bufLen - 1);
+ command |= bufData;
+ aBuffer.AppendElement(command);
+ bufLen = 0;
+ bufData = 0;
+ }
+ };
+
+ // Loop through the bytes of the structs.
+ // While copying the struct at aSrcStart to aDstStart,
+ // the differences are encoded as VRPuppet commands and
+ // appended to aBuffer.
+ for (size_t i = 0; i < aLength; i++) {
+ if (*src != *dst) {
+ // This byte is different
+
+ // Copy the byte to the destination
+ *dst = *src;
+
+ if (bufLen == 0) {
+ // This is the start of a new span of changed bytes
+
+ // Output a command to specify the offset of the
+ // span.
+ aBuffer.AppendElement((uint64_t)aUpdateCommand + i);
+
+ // Store this first byte in bufData.
+ // We will batch up to 7 bytes in one VRPuppet_DataXX
+ // command, so we won't emit it yet.
+ bufLen = 1;
+ bufData = *src;
+ } else if (bufLen <= 6) {
+ // This is the continuation of a span of changed bytes.
+ // There is room to add more bytes to bufData.
+
+ // Store the next byte in bufData.
+ // We will batch up to 7 bytes in one VRPuppet_DataXX
+ // command, so we won't emit it yet.
+ bufData = (bufData << 8) | *src;
+ bufLen++;
+ } else {
+ MOZ_ASSERT(bufLen == 7);
+ // This is the continuation of a span of changed bytes.
+ // There are already 7 bytes in bufData, so we must emit
+ // the VRPuppet_Data7 command for the prior bytes before
+ // starting a new command.
+ aBuffer.AppendElement((uint64_t)VRPuppet_Command::VRPuppet_Data7 +
+ bufData);
+
+ // Store this byte to be included in the next VRPuppet_DataXX
+ // command.
+ bufLen = 1;
+ bufData = *src;
+ }
+ } else {
+ // This byte is the same.
+ // If there are bytes in bufData, the span has now ended and we must
+ // emit a VRPuppet_DataXX command for the accumulated bytes.
+ // purgeBuffer will not emit any commands if there are no bytes
+ // accumulated.
+ purgeBuffer();
+ }
+ // Advance to the next source and destination byte.
+ ++src;
+ ++dst;
+ }
+ // In the event that the very last byte of the structs differ, we must
+ // ensure that the accumulated bytes are emitted as a VRPuppet_DataXX
+ // command.
+ purgeBuffer();
+}
+
+} // namespace mozilla::gfx
diff --git a/gfx/vr/VRPuppetCommandBuffer.h b/gfx/vr/VRPuppetCommandBuffer.h
new file mode 100644
index 0000000000..6acbcbe80d
--- /dev/null
+++ b/gfx/vr/VRPuppetCommandBuffer.h
@@ -0,0 +1,236 @@
+/* -*- 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_SERVICE_VRPUPPETCOMMANDBUFFER_H
+#define GFX_VR_SERVICE_VRPUPPETCOMMANDBUFFER_H
+
+#include <inttypes.h>
+#include "mozilla/Mutex.h"
+#include "nsTArray.h"
+#include "moz_external_vr.h"
+#include "mozilla/TimeStamp.h"
+
+namespace mozilla {
+namespace gfx {
+
+/**
+ * A Puppet Device command buffer consists of a stream of 64-bit
+ * commands.
+ * The first 8 bits identifies the command and informs format of
+ * the remaining 56 bits.
+ *
+ * These commands will be streamed into a buffer until
+ * VRPuppet_End (0x0000000000000000) has been received.
+ *
+ * When VRPuppet_End is received, this command buffer will
+ * be executed asynchronously, collecting the timer results
+ * requested.
+ *
+ * In addition to the effects seen through the WebXR/VR API, tests
+ * can get further feedback from the Puppet device.
+ * Command buffers can be constructed such that when expected states
+ * are not reached, the timeout timer will expire.
+ * Data recorded with timer commands is returned when the command
+ * buffer is completed, to validate against accepted ranges and to
+ * quantify performance regressions.
+ * Images submitted to the Puppet display are rendered with the
+ * 2d browser output in order to enable reftests to validate
+ * output against reference images.
+ *
+ * The state of the virtual puppet device is expressed to the VR service
+ * in the same manner as physical devices -- using the VRDisplayState,
+ * VRHMDSensorState and VRControllerState structures.
+ *
+ * By enabling partial updates of these structures, the command buffer
+ * size is reduced to the values that change each frame. This enables
+ * realtime capture of a session, with physical hardware, for
+ * replay in automated tests and benchmarks.
+ *
+ * All updates to the state structures are atomically updated in the
+ * VR session thread, triggered by VRPuppet_Commit (0x1500000000000000).
+ *
+ * Command buffers are expected to be serialized to a human readable,
+ * ascii format if stored on disk. The binary representation is not
+ * guaranteed to be consistent between versions or target platforms.
+ * They should be re-constructed with the VRServiceTest DOM api at
+ * runtime.
+ *
+ * The command set:
+ *
+ * 0x0000000000000000 - VRPuppet_End()
+ * - End of stream, resolve promise returned by VRServiceTest::Play()
+ *
+ * 0x0100000000000000 - VRPuppet_ClearAll()
+ * - Clear all structs
+ *
+ * 0x02000000000000nn - VRPuppet_ClearController(n)
+ * - Clear a single controller struct
+ *
+ * 0x03000000nnnnnnnn - VRPuppet_Timeout(n)
+ * - Reset the timeout timer to n milliseconds
+ * - Initially the timeout timer starts at 10 seconds
+ *
+ * 0x04000000nnnnnnnn - VRPuppet_Wait(n)
+ * - Wait n milliseconds before advancing to next command
+ *
+ * 0x0500000000000000 - VRPuppet_WaitSubmit()
+ * - Wait until a frame has been submitted before advancing to the next command
+ *
+ * 0x0600000000000000 - VRPuppet_WaitPresentationStart()
+ * - Wait until a presentation becomes active
+ *
+ * 0x0700000000000000 - VRPuppet_WaitPresentationEnd()
+ * - Wait until a presentation ends
+ *
+ * 0x0800cchhvvvvvvvv - VRPuppet_WaitHapticIntensity(c, h, v)
+ * - Wait until controller at index c's haptic actuator at index h reaches value
+ * v.
+ * - v is a 16.16 fixed point value, with 1.0f being the highest intensity and
+ * 0.0f indicating that the haptic actuator is not running
+ *
+ * 0x0900000000000000 - VRPuppet_CaptureFrame()
+ * - Captures the submitted frame. Must later call
+ * VRPuppet_AcknowledgeFrame or VRPuppet_RejectFrame
+ * to unblock
+ *
+ * 0x0900000000000000 - VRPuppet_AcknowledgeFrame()
+ * - Acknowledge the submitted frame, unblocking the Submit call.
+ *
+ * 0x0a00000000000000 - VRPuppet_RejectFrame()
+ * - Reject the submitted frame, unblocking the Submit call.
+ *
+ * 0x0b00000000000000 - VRPuppet_StartTimer()
+ * - Starts the timer
+ *
+ * 0x0c00000000000000 - VRPuppet_StopTimer()
+ * - Stops the timer, the elapsed duration is recorded for access after the end
+ * of stream
+ *
+ * 0x0d000000aaaaaaaa - VRPuppet_UpdateDisplay(a)
+ * - Start writing data to the VRDisplayState struct, at offset a
+ *
+ * 0x0e000000aaaaaaaa - VRPuppet_UpdateSensor(a)
+ * - Start writing data to the VRHMDSensorState struct, at offset a
+ *
+ * 0x0f000000aaaaaaaa - VRPuppet_UpdateControllers(a)
+ * - Start writing data to the VRControllerState array, at offset a
+ *
+ * 0x100000000000000 - VRPuppet_Commit
+ * - Atomically commit the VRPuppet_Data updates to VRDisplayState,
+ * VRHMDSensorState and VRControllerState.
+ *
+ * 0xf0000000000000dd - VRPuppet_Data(d)
+ * - 1 byte of data
+ *
+ * 0xf10000000000dddd - VRPuppet_Data(d)
+ * - 2 bytes of data
+ *
+ * 0xf200000000dddddd - VRPuppet_Data(d)
+ * - 3 bytes of data
+ *
+ * 0xf3000000dddddddd - VRPuppet_Data(d)
+ * - 4 bytes of data
+ *
+ * 0xf40000dddddddddd - VRPuppet_Data(d)
+ * - 5 bytes of data
+ *
+ * 0xf500dddddddddddd - VRPuppet_Data(d)
+ * - 6 bytes of data
+ *
+ * 0xf6dddddddddddddd - VRPuppet_Data(d)
+ * - 7 bytes of data
+ *
+ */
+enum class VRPuppet_Command : uint64_t {
+ VRPuppet_End = 0x0000000000000000,
+ VRPuppet_ClearAll = 0x0100000000000000,
+ VRPuppet_ClearController = 0x0200000000000000,
+ VRPuppet_Timeout = 0x0300000000000000,
+ VRPuppet_Wait = 0x0400000000000000,
+ VRPuppet_WaitSubmit = 0x0500000000000000,
+ VRPuppet_WaitPresentationStart = 0x0600000000000000,
+ VRPuppet_WaitPresentationEnd = 0x0700000000000000,
+ VRPuppet_WaitHapticIntensity = 0x0800000000000000,
+ VRPuppet_CaptureFrame = 0x0900000000000000,
+ VRPuppet_AcknowledgeFrame = 0x0a00000000000000,
+ VRPuppet_RejectFrame = 0x0b00000000000000,
+ VRPuppet_StartTimer = 0x0c00000000000000,
+ VRPuppet_StopTimer = 0x0d00000000000000,
+ VRPuppet_UpdateDisplay = 0x0e00000000000000,
+ VRPuppet_UpdateSensor = 0x0f00000000000000,
+ VRPuppet_UpdateControllers = 0x1000000000000000,
+ VRPuppet_Commit = 0x1100000000000000,
+ VRPuppet_Data1 = 0xf000000000000000,
+ VRPuppet_Data2 = 0xf100000000000000,
+ VRPuppet_Data3 = 0xf200000000000000,
+ VRPuppet_Data4 = 0xf300000000000000,
+ VRPuppet_Data5 = 0xf400000000000000,
+ VRPuppet_Data6 = 0xf500000000000000,
+ VRPuppet_Data7 = 0xf600000000000000,
+};
+
+static const int kNumPuppetHaptics = 8;
+
+class VRPuppetCommandBuffer {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::gfx::VRPuppetCommandBuffer)
+ static VRPuppetCommandBuffer& Get();
+ static bool IsCreated();
+
+ // Interface to VRTestSystem
+ void Submit(const nsTArray<uint64_t>& aBuffer);
+ void Reset();
+ bool HasEnded();
+
+ // Interface to PuppetSession
+ void Run(VRSystemState& aState);
+ void StartPresentation();
+ void StopPresentation();
+ bool SubmitFrame();
+ void VibrateHaptic(uint32_t aControllerIdx, uint32_t aHapticIndex,
+ float aIntensity, float aDuration);
+ void StopVibrateHaptic(uint32_t aControllerIdx);
+ void StopAllHaptics();
+
+ static void EncodeStruct(nsTArray<uint64_t>& aBuffer, uint8_t* aSrcStart,
+ uint8_t* aDstStart, size_t aLength,
+ gfx::VRPuppet_Command aUpdateCommand);
+
+ private:
+ VRPuppetCommandBuffer();
+ ~VRPuppetCommandBuffer();
+ void Run();
+ bool RunCommand(uint64_t aCommand, double aDeltaTime);
+ void WriteData(uint8_t aData);
+ void SimulateHaptics(double aDeltaTime);
+ void CompleteTest(bool aTimedOut);
+ nsTArray<uint64_t> mBuffer;
+ mozilla::Mutex mMutex;
+ VRSystemState mPendingState;
+ VRSystemState mCommittedState;
+ double mHapticPulseRemaining[kVRControllerMaxCount][kNumPuppetHaptics];
+ float mHapticPulseIntensity[kVRControllerMaxCount][kNumPuppetHaptics];
+
+ size_t mDataOffset;
+ bool mPresentationRequested;
+ bool mFrameSubmitted;
+ bool mFrameAccepted;
+ double mTimeoutDuration; // Seconds
+ double mWaitRemaining; // Seconds
+ double mBlockedTime; // Seconds
+ double mTimerElapsed; // Seconds
+ TimeStamp mLastRunTimestamp;
+
+ // Test Results:
+ bool mEnded;
+ bool mEndedWithTimeout;
+ nsTArray<double> mTimerSamples;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // GFX_VR_SERVICE_VRPUPPETCOMMANDBUFFER_H
diff --git a/gfx/vr/VRServiceHost.cpp b/gfx/vr/VRServiceHost.cpp
new file mode 100644
index 0000000000..7250bfe74d
--- /dev/null
+++ b/gfx/vr/VRServiceHost.cpp
@@ -0,0 +1,327 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "VRServiceHost.h"
+#include "VRGPUChild.h"
+#include "VRPuppetCommandBuffer.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/gfx/GPUParent.h"
+#include "service/VRService.h"
+#include "VRManager.h"
+
+namespace mozilla {
+namespace gfx {
+
+static StaticRefPtr<VRServiceHost> sVRServiceHostSingleton;
+
+/* static */
+void VRServiceHost::Init(bool aEnableVRProcess) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (sVRServiceHostSingleton == nullptr) {
+ sVRServiceHostSingleton = new VRServiceHost(aEnableVRProcess);
+ ClearOnShutdown(&sVRServiceHostSingleton);
+ }
+}
+
+/* static */
+VRServiceHost* VRServiceHost::Get() {
+ MOZ_ASSERT(sVRServiceHostSingleton != nullptr);
+ return sVRServiceHostSingleton;
+}
+
+VRServiceHost::VRServiceHost(bool aEnableVRProcess)
+ : mVRService(nullptr),
+ mVRProcessEnabled(aEnableVRProcess),
+ mVRProcessStarted(false),
+ mVRServiceReadyInVRProcess(false),
+ mVRServiceRequested(false)
+
+{
+ MOZ_COUNT_CTOR(VRServiceHost);
+}
+
+VRServiceHost::~VRServiceHost() {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_COUNT_DTOR(VRServiceHost);
+}
+
+void VRServiceHost::StartService() {
+ mVRServiceRequested = true;
+ if (mVRProcessEnabled) {
+ // VRService in the VR process
+ RefreshVRProcess();
+ } else if (mVRService) {
+ // VRService in the GPU process if enabled, or
+ // the parent process if the GPU process is not enabled.
+ mVRService->Start();
+ }
+}
+
+void VRServiceHost::StopService() {
+ mVRServiceRequested = false;
+ if (mVRProcessEnabled) {
+ // VRService in the VR process
+ RefreshVRProcess();
+ } else if (mVRService) {
+ // VRService in the GPU process if enabled, or
+ // the parent process if the GPU process is not enabled.
+ mVRService->Stop();
+ }
+}
+
+void VRServiceHost::Shutdown() {
+ PuppetReset();
+ StopService();
+ mVRService = nullptr;
+}
+
+void VRServiceHost::Refresh() {
+ if (mVRService) {
+ mVRService->Refresh();
+ }
+}
+
+void VRServiceHost::CreateService(volatile VRExternalShmem* aShmem) {
+ MOZ_ASSERT(!mVRProcessEnabled);
+ mVRService = VRService::Create(aShmem);
+}
+
+bool VRServiceHost::NeedVRProcess() {
+ if (!mVRProcessEnabled) {
+ return false;
+ }
+ return mVRServiceRequested;
+}
+
+void VRServiceHost::RefreshVRProcess() {
+ // Start or stop the VR process if needed
+ if (NeedVRProcess()) {
+ if (!mVRProcessStarted) {
+ CreateVRProcess();
+ }
+ } else {
+ if (mVRProcessStarted) {
+ ShutdownVRProcess();
+ }
+ }
+}
+
+void VRServiceHost::CreateVRProcess() {
+ // This is only allowed to run in the main thread of the GPU process
+ if (!XRE_IsGPUProcess()) {
+ return;
+ }
+ // Forward this to the main thread if not already there
+ if (!NS_IsMainThread()) {
+ RefPtr<Runnable> task = NS_NewRunnableFunction(
+ "VRServiceHost::CreateVRProcess",
+ []() -> void { VRServiceHost::Get()->CreateVRProcess(); });
+ NS_DispatchToMainThread(task.forget());
+ return;
+ }
+ if (mVRProcessStarted) {
+ return;
+ }
+
+ mVRProcessStarted = true;
+ // Using PGPU channel to tell the main process
+ // to create the VR process.
+ gfx::GPUParent* gpu = GPUParent::GetSingleton();
+ MOZ_ASSERT(gpu);
+ Unused << gpu->SendCreateVRProcess();
+}
+
+void VRServiceHost::NotifyVRProcessStarted() {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mVRProcessEnabled);
+ if (!mVRProcessStarted) {
+ // We have received this after the VR process
+ // has been stopped; the VR service is no
+ // longer running in the VR process.
+ return;
+ }
+
+ if (!VRGPUChild::IsCreated()) {
+ return;
+ }
+ VRGPUChild* vrGPUChild = VRGPUChild::Get();
+
+ // The VR service has started in the VR process
+ // If there were pending puppet commands, we
+ // can send them now.
+ // This must occur before the VRService
+ // is started so the buffer can be seen
+ // by VRPuppetSession::Initialize().
+ if (!mPuppetPendingCommands.IsEmpty()) {
+ vrGPUChild->SendPuppetSubmit(mPuppetPendingCommands);
+ mPuppetPendingCommands.Clear();
+ }
+
+ vrGPUChild->SendStartVRService();
+ mVRServiceReadyInVRProcess = true;
+}
+
+void VRServiceHost::ShutdownVRProcess() {
+ // This is only allowed to run in the main thread of the GPU process
+ if (!XRE_IsGPUProcess()) {
+ return;
+ }
+ // Forward this to the main thread if not already there
+ if (!NS_IsMainThread()) {
+ RefPtr<Runnable> task = NS_NewRunnableFunction(
+ "VRServiceHost::ShutdownVRProcess",
+ []() -> void { VRServiceHost::Get()->ShutdownVRProcess(); });
+ NS_DispatchToMainThread(task.forget());
+ return;
+ }
+ if (VRGPUChild::IsCreated()) {
+ VRGPUChild* vrGPUChild = VRGPUChild::Get();
+ vrGPUChild->SendStopVRService();
+ if (!vrGPUChild->IsClosed()) {
+ vrGPUChild->Close();
+ }
+ VRGPUChild::Shutdown();
+ }
+ if (!mVRProcessStarted) {
+ return;
+ }
+ // Using PGPU channel to tell the main process
+ // to shutdown VR process.
+ gfx::GPUParent* gpu = GPUParent::GetSingleton();
+ MOZ_ASSERT(gpu);
+ Unused << gpu->SendShutdownVRProcess();
+ mVRProcessStarted = false;
+ mVRServiceReadyInVRProcess = false;
+}
+
+void VRServiceHost::PuppetSubmit(const nsTArray<uint64_t>& aBuffer) {
+ if (!mVRProcessEnabled) {
+ // Puppet is running in this process, submit commands directly
+ VRPuppetCommandBuffer::Get().Submit(aBuffer);
+ return;
+ }
+
+ // We need to send the buffer to the VR process
+ SendPuppetSubmitToVRProcess(aBuffer);
+}
+
+void VRServiceHost::SendPuppetSubmitToVRProcess(
+ const nsTArray<uint64_t>& aBuffer) {
+ // This is only allowed to run in the main thread of the GPU process
+ if (!XRE_IsGPUProcess()) {
+ return;
+ }
+ // Forward this to the main thread if not already there
+ if (!NS_IsMainThread()) {
+ RefPtr<Runnable> task = NS_NewRunnableFunction(
+ "VRServiceHost::SendPuppetSubmitToVRProcess",
+ [buffer{aBuffer.Clone()}]() -> void {
+ VRServiceHost::Get()->SendPuppetSubmitToVRProcess(buffer);
+ });
+ NS_DispatchToMainThread(task.forget());
+ return;
+ }
+ if (!mVRServiceReadyInVRProcess) {
+ // Queue the commands to be sent to the VR process once it is started
+ mPuppetPendingCommands.AppendElements(aBuffer);
+ return;
+ }
+ if (VRGPUChild::IsCreated()) {
+ VRGPUChild* vrGPUChild = VRGPUChild::Get();
+ vrGPUChild->SendPuppetSubmit(aBuffer);
+ }
+}
+
+void VRServiceHost::PuppetReset() {
+ // If we're already into ShutdownFinal, the VRPuppetCommandBuffer instance
+ // will have been cleared, so don't try to access it after that point.
+ if (!mVRProcessEnabled &&
+ !(NS_IsMainThread() && PastShutdownPhase(ShutdownPhase::ShutdownFinal))) {
+ // Puppet is running in this process, tell it to reset directly.
+ VRPuppetCommandBuffer::Get().Reset();
+ }
+
+ mPuppetPendingCommands.Clear();
+ if (!mVRProcessStarted) {
+ // Process is stopped, so puppet state is already clear
+ return;
+ }
+
+ // We need to tell the VR process to reset the puppet
+ SendPuppetResetToVRProcess();
+}
+
+void VRServiceHost::SendPuppetResetToVRProcess() {
+ // This is only allowed to run in the main thread of the GPU process
+ if (!XRE_IsGPUProcess()) {
+ return;
+ }
+ // Forward this to the main thread if not already there
+ if (!NS_IsMainThread()) {
+ RefPtr<Runnable> task = NS_NewRunnableFunction(
+ "VRServiceHost::SendPuppetResetToVRProcess",
+ []() -> void { VRServiceHost::Get()->SendPuppetResetToVRProcess(); });
+ NS_DispatchToMainThread(task.forget());
+ return;
+ }
+ if (VRGPUChild::IsCreated()) {
+ VRGPUChild* vrGPUChild = VRGPUChild::Get();
+ vrGPUChild->SendPuppetReset();
+ }
+}
+
+void VRServiceHost::CheckForPuppetCompletion() {
+ if (!mVRProcessEnabled) {
+ // Puppet is running in this process, ask it directly
+ if (VRPuppetCommandBuffer::Get().HasEnded()) {
+ VRManager::Get()->NotifyPuppetComplete();
+ }
+ }
+ if (!mPuppetPendingCommands.IsEmpty()) {
+ // There are puppet commands pending to be sent to the
+ // VR process once its started, thus it has not ended.
+ return;
+ }
+ if (!mVRProcessStarted) {
+ // The VR process will be kept alive as long
+ // as there is a queue in the puppet command
+ // buffer. If the process is stopped, we can
+ // infer that the queue has been cleared and
+ // puppet state is reset.
+ VRManager::Get()->NotifyPuppetComplete();
+ }
+
+ // We need to ask the VR process if the puppet has ended
+ SendPuppetCheckForCompletionToVRProcess();
+
+ // VRGPUChild::RecvNotifyPuppetComplete will call
+ // VRManager::NotifyPuppetComplete if the puppet has completed
+ // in the VR Process.
+}
+
+void VRServiceHost::SendPuppetCheckForCompletionToVRProcess() {
+ // This is only allowed to run in the main thread of the GPU process
+ if (!XRE_IsGPUProcess()) {
+ return;
+ }
+ // Forward this to the main thread if not already there
+ if (!NS_IsMainThread()) {
+ RefPtr<Runnable> task = NS_NewRunnableFunction(
+ "VRServiceHost::SendPuppetCheckForCompletionToVRProcess", []() -> void {
+ VRServiceHost::Get()->SendPuppetCheckForCompletionToVRProcess();
+ });
+ NS_DispatchToMainThread(task.forget());
+ return;
+ }
+ if (VRGPUChild::IsCreated()) {
+ VRGPUChild* vrGPUChild = VRGPUChild::Get();
+ vrGPUChild->SendPuppetCheckForCompletion();
+ }
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/vr/VRServiceHost.h b/gfx/vr/VRServiceHost.h
new file mode 100644
index 0000000000..0c1a6193cc
--- /dev/null
+++ b/gfx/vr/VRServiceHost.h
@@ -0,0 +1,93 @@
+/* -*- 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_SERVICE_HOST_H
+#define GFX_VR_SERVICE_HOST_H
+
+#include "mozilla/RefPtr.h"
+#include "nsISupportsImpl.h"
+#include "nsTArray.h"
+
+#include <cstdint>
+
+namespace mozilla {
+namespace gfx {
+
+struct VRExternalShmem;
+class VRService;
+
+/**
+ * VRServiceHost is allocated as a singleton in the GPU process.
+ * It is responsible for allocating VRService either within the GPU process
+ * or in the VR process.
+ * When the VR process is enabled, it maintains the state of the VR process,
+ * starting and stopping it as needed.
+ * VRServiceHost provides an interface that enables communication of the
+ * VRService in the same way regardless of it running within the GPU process
+ * or the VR process.
+ */
+
+class VRServiceHost {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::gfx::VRServiceHost)
+ public:
+ static void Init(bool aEnableVRProcess);
+ static VRServiceHost* Get();
+
+ void Refresh();
+ void StartService();
+ void StopService();
+ void Shutdown();
+ void CreateService(volatile VRExternalShmem* aShmem);
+ void NotifyVRProcessStarted();
+ void CheckForPuppetCompletion();
+
+ void PuppetSubmit(const nsTArray<uint64_t>& aBuffer);
+ void PuppetReset();
+
+ protected:
+ private:
+ explicit VRServiceHost(bool aEnableVRProcess);
+ ~VRServiceHost();
+
+ void RefreshVRProcess();
+ bool NeedVRProcess();
+ void CreateVRProcess();
+ void ShutdownVRProcess();
+ void SendPuppetResetToVRProcess();
+ void SendPuppetCheckForCompletionToVRProcess();
+ void SendPuppetSubmitToVRProcess(const nsTArray<uint64_t>& aBuffer);
+
+ // Commands pending to be sent to the puppet device
+ // once the VR service is started.
+ nsTArray<uint64_t> mPuppetPendingCommands;
+
+ RefPtr<VRService> mVRService;
+ // mVRProcessEnabled indicates that a separate, VR Process, should be used.
+ // This may be false if the VR process is disabled with the
+ // dom.vr.process.enabled preference or when the GPU process is disabled.
+ // mVRProcessEnabled will not change once the browser is started and does not
+ // reflect the started / stopped state of the VR Process.
+ bool mVRProcessEnabled;
+ // mVRProcessStarted is true when the VR Process is running.
+ bool mVRProcessStarted;
+ // mVRServiceReadyInVRProcess is true when the VR Process is running and the
+ // VRService in the VR Process is ready to accept commands.
+ bool mVRServiceReadyInVRProcess;
+ // mVRServiceRequested is true when the VRService is needed. This can be due
+ // to Web API activity (WebXR, WebVR), browser activity (eg, VR Video
+ // Playback), or a request to simulate a VR device with the VRServiceTest /
+ // puppet API. mVRServiceRequested indicates the intended state of the VR
+ // Service and is not an indication that the VR Service is ready to accept
+ // requests or that the VR Process is enabled or running. Toggling the
+ // mVRServiceRequested flag will result in the VR Service and/or the VR
+ // Process either starting or stopping as needed.
+ bool mVRServiceRequested;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // GFX_VR_SERVICE_HOST_H
diff --git a/gfx/vr/VRShMem.cpp b/gfx/vr/VRShMem.cpp
new file mode 100644
index 0000000000..778a27e54c
--- /dev/null
+++ b/gfx/vr/VRShMem.cpp
@@ -0,0 +1,736 @@
+/* -*- 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 "VRShMem.h"
+
+#ifdef MOZILLA_INTERNAL_API
+# include "nsString.h"
+# include "nsXULAppAPI.h"
+#endif
+
+#include "gfxVRMutex.h"
+
+#if defined(XP_MACOSX)
+# include <sys/mman.h>
+# include <sys/stat.h> /* For mode constants */
+# include <fcntl.h> /* For O_* constants */
+#elif defined(MOZ_WIDGET_ANDROID)
+# include "GeckoVRManager.h"
+#endif
+
+#if !defined(XP_WIN)
+# include <unistd.h> // for ::sleep
+#endif
+
+using namespace mozilla::gfx;
+
+#ifdef XP_WIN
+static const char* kShmemName = "moz.gecko.vr_ext." SHMEM_VERSION;
+static LPCTSTR kMutexName = TEXT("mozilla::vr::ShmemMutex" SHMEM_VERSION);
+#elif defined(XP_MACOSX)
+static const char* kShmemName = "/moz.gecko.vr_ext." SHMEM_VERSION;
+#endif // XP_WIN
+
+#if !defined(MOZ_WIDGET_ANDROID)
+namespace {
+void YieldThread() {
+# if defined(XP_WIN)
+ ::Sleep(0);
+# else
+ ::sleep(0);
+# endif
+}
+} // anonymous namespace
+#endif // !defined(MOZ_WIDGET_ANDROID)
+
+VRShMem::VRShMem(volatile VRExternalShmem* aShmem, bool aRequiresMutex)
+ : mExternalShmem(aShmem),
+ mIsSharedExternalShmem(aShmem != nullptr)
+#if defined(XP_WIN)
+ ,
+ mRequiresMutex(aRequiresMutex)
+#endif
+#if defined(XP_MACOSX)
+ ,
+ mShmemFD(0)
+#elif defined(XP_WIN)
+ ,
+ mShmemFile(nullptr),
+ mMutex(nullptr)
+#endif
+{
+ // Regarding input parameters,
+ // - aShmem is null for VRManager or for VRService in multi-proc
+ // - aShmem is !null for VRService in-proc (i.e., no VR proc)
+}
+
+// Note: This function should only be called for in-proc scenarios, where the
+// shared memory is only shared within the same proc (rather than across
+// processes). Also, this local heap memory's lifetime is tied to the class.
+// Callers to this must ensure that its reference doesn't outlive the owning
+// VRShMem instance.
+volatile VRExternalShmem* VRShMem::GetExternalShmem() const {
+#if defined(XP_MACOSX)
+ MOZ_ASSERT(mShmemFD == 0);
+#elif defined(XP_WIN)
+ MOZ_ASSERT(mShmemFile == nullptr);
+#endif
+ return mExternalShmem;
+}
+
+bool VRShMem::IsDisplayStateShutdown() const {
+ // adapted from VRService::Refresh
+ // Does this need the mutex for getting .shutdown?
+ return mExternalShmem != nullptr &&
+ mExternalShmem->state.displayState.shutdown;
+}
+
+// This method returns true when there is a Shmem struct allocated and
+// when there is a shmem handle from the OS. This implies that the struct
+// is mapped to shared memory rather than being allocated on the heap by
+// this process.
+bool VRShMem::IsCreatedOnSharedMemory() const {
+#if defined(XP_MACOSX)
+ return HasExternalShmem() && (mShmemFD != 0);
+#elif defined(XP_WIN)
+ return HasExternalShmem() && (mShmemFile != nullptr);
+#else
+ // VRShMem does not support system shared memory on remaining platformss
+ return false;
+#endif
+}
+
+// CreateShMem allocates system shared memory for mExternalShmem and
+// synchronization primitives to protect it.
+// Callers/Processes to CreateShMem should followup with CloseShMem
+void VRShMem::CreateShMem(bool aCreateOnSharedMemory) {
+ if (HasExternalShmem()) {
+ MOZ_ASSERT(mIsSharedExternalShmem && !IsCreatedOnSharedMemory());
+ return;
+ }
+#if defined(XP_WIN)
+ if (mMutex == nullptr) {
+ mMutex = CreateMutex(nullptr, // default security descriptor
+ false, // mutex not owned
+ kMutexName); // object name
+ if (mMutex == nullptr) {
+# ifdef MOZILLA_INTERNAL_API
+ nsAutoCString msg;
+ msg.AppendPrintf("VRManager CreateMutex error \"%lu\".", GetLastError());
+ NS_WARNING(msg.get());
+# endif
+ MOZ_ASSERT(false);
+ return;
+ }
+ // At xpcshell extension tests, it creates multiple VRManager
+ // instances in plug-contrainer.exe. It causes GetLastError() return
+ // `ERROR_ALREADY_EXISTS`. However, even though `ERROR_ALREADY_EXISTS`, it
+ // still returns the same mutex handle.
+ //
+ // https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-createmutexa
+ MOZ_ASSERT(GetLastError() == 0 || GetLastError() == ERROR_ALREADY_EXISTS);
+ }
+#endif // XP_WIN
+#if !defined(MOZ_WIDGET_ANDROID)
+ // The VR Service accesses all hardware from a separate process
+ // and replaces the other VRManager when enabled.
+ // If the VR process is not enabled, create an in-process VRService.
+ if (!aCreateOnSharedMemory) {
+ MOZ_ASSERT(mExternalShmem == nullptr);
+ // If the VR process is disabled, attempt to create a
+ // VR service within the current process on the heap
+ mExternalShmem = new VRExternalShmem();
+ ClearShMem();
+ return;
+ }
+#endif
+
+ MOZ_ASSERT(aCreateOnSharedMemory);
+
+#if defined(XP_MACOSX)
+ if (mShmemFD == 0) {
+ mShmemFD =
+ shm_open(kShmemName, O_RDWR, S_IRUSR | S_IWUSR | S_IROTH | S_IWOTH);
+ }
+ if (mShmemFD <= 0) {
+ mShmemFD = 0;
+ return;
+ }
+
+ struct stat sb;
+ fstat(mShmemFD, &sb);
+ off_t length = sb.st_size;
+ if (length < (off_t)sizeof(VRExternalShmem)) {
+ // TODO - Implement logging (Bug 1558912)
+ CloseShMem();
+ return;
+ }
+
+ mExternalShmem = (VRExternalShmem*)mmap(NULL, length, PROT_READ | PROT_WRITE,
+ MAP_SHARED, mShmemFD, 0);
+ if (mExternalShmem == MAP_FAILED) {
+ // TODO - Implement logging (Bug 1558912)
+ mExternalShmem = NULL;
+ CloseShMem();
+ return;
+ }
+
+#elif defined(XP_WIN)
+ if (mShmemFile == nullptr) {
+ mShmemFile =
+ CreateFileMappingA(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0,
+ sizeof(VRExternalShmem), kShmemName);
+ MOZ_ASSERT(GetLastError() == 0 || GetLastError() == ERROR_ALREADY_EXISTS);
+ MOZ_ASSERT(mShmemFile);
+ if (mShmemFile == nullptr) {
+ // TODO - Implement logging (Bug 1558912)
+ CloseShMem();
+ return;
+ }
+ }
+
+ LARGE_INTEGER length;
+ length.QuadPart = sizeof(VRExternalShmem);
+ mExternalShmem = (VRExternalShmem*)MapViewOfFile(
+ mShmemFile, // handle to map object
+ FILE_MAP_ALL_ACCESS, // read/write permission
+ 0, 0, length.QuadPart);
+
+ if (mExternalShmem == nullptr) {
+ // TODO - Implement logging (Bug 1558912)
+ CloseShMem();
+ return;
+ }
+#elif defined(MOZ_WIDGET_ANDROID)
+ MOZ_ASSERT(false,
+ "CreateShMem should not be called for Android. Use "
+ "CreateShMemForAndroid instead");
+#endif
+}
+
+// This function sets mExternalShmem in the Android/GeckoView
+// scenarios where the host creates it in-memory and VRShMem
+// accesses it via GeckVRManager.
+void VRShMem::CreateShMemForAndroid() {
+#if defined(MOZ_WIDGET_ANDROID) && defined(MOZILLA_INTERNAL_API)
+ mExternalShmem =
+ (VRExternalShmem*)mozilla::GeckoVRManager::GetExternalContext();
+ if (!mExternalShmem) {
+ return;
+ } else {
+ mIsSharedExternalShmem = true;
+ }
+
+ int32_t version = -1;
+ int32_t size = 0;
+ if (pthread_mutex_lock((pthread_mutex_t*)&(mExternalShmem->systemMutex)) ==
+ 0) {
+ version = mExternalShmem->version;
+ size = mExternalShmem->size;
+ pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->systemMutex));
+ } else {
+ return;
+ }
+ if (version != kVRExternalVersion) {
+ mExternalShmem = nullptr;
+ return;
+ }
+ if (size != sizeof(VRExternalShmem)) {
+ mExternalShmem = nullptr;
+ return;
+ }
+#endif
+}
+
+void VRShMem::ClearShMem() {
+ if (mExternalShmem != nullptr) {
+#ifdef MOZILLA_INTERNAL_API
+ // VRExternalShmem is asserted to be POD
+ mExternalShmem->Clear();
+#else
+ memset((void*)mExternalShmem, 0, sizeof(VRExternalShmem));
+#endif
+ }
+}
+
+// The cleanup corresponding to CreateShMem
+void VRShMem::CloseShMem() {
+#if !defined(MOZ_WIDGET_ANDROID)
+ if (!IsCreatedOnSharedMemory()) {
+ MOZ_ASSERT(!mIsSharedExternalShmem);
+ if (mExternalShmem) {
+ delete mExternalShmem;
+ mExternalShmem = nullptr;
+ }
+ return;
+ }
+#endif
+#if defined(XP_MACOSX)
+ if (mExternalShmem) {
+ munmap((void*)mExternalShmem, sizeof(VRExternalShmem));
+ mExternalShmem = NULL;
+ }
+ if (mShmemFD) {
+ close(mShmemFD);
+ mShmemFD = 0;
+ }
+#elif defined(XP_WIN)
+ if (mExternalShmem) {
+ UnmapViewOfFile((void*)mExternalShmem);
+ mExternalShmem = nullptr;
+ }
+ if (mShmemFile) {
+ CloseHandle(mShmemFile);
+ mShmemFile = nullptr;
+ }
+#elif defined(MOZ_WIDGET_ANDROID)
+ mExternalShmem = NULL;
+#endif
+
+#if defined(XP_WIN)
+ if (mMutex) {
+ MOZ_ASSERT(mRequiresMutex);
+ CloseHandle(mMutex);
+ mMutex = nullptr;
+ }
+#endif
+}
+
+// Called to use an existing shmem instance created by another process
+// Callers to JoinShMem should call LeaveShMem for cleanup
+bool VRShMem::JoinShMem() {
+#if defined(XP_WIN)
+ if (!mMutex && mRequiresMutex) {
+ // Check that there are no errors before making system calls
+ MOZ_ASSERT(GetLastError() == 0);
+
+ mMutex = OpenMutex(MUTEX_ALL_ACCESS, // request full access
+ false, // handle not inheritable
+ kMutexName); // object name
+
+ if (mMutex == nullptr) {
+# ifdef MOZILLA_INTERNAL_API
+ nsAutoCString msg;
+ msg.AppendPrintf("VRService OpenMutex error \"%lu\".", GetLastError());
+ NS_WARNING(msg.get());
+# endif
+ return false;
+ }
+ MOZ_ASSERT(GetLastError() == 0);
+ }
+#endif
+
+ if (HasExternalShmem()) {
+ // An ExternalShmem is already set. No need to override and rejoin
+ return true;
+ }
+
+#if defined(XP_WIN)
+ // Opening a file-mapping object by name
+ base::ProcessHandle targetHandle =
+ OpenFileMappingA(FILE_MAP_ALL_ACCESS, // read/write access
+ FALSE, // do not inherit the name
+ kShmemName); // name of mapping object
+
+ MOZ_ASSERT(GetLastError() == 0);
+
+ LARGE_INTEGER length;
+ length.QuadPart = sizeof(VRExternalShmem);
+ mExternalShmem = (VRExternalShmem*)MapViewOfFile(
+ reinterpret_cast<base::ProcessHandle>(
+ targetHandle), // handle to map object
+ FILE_MAP_ALL_ACCESS, // read/write permission
+ 0, 0, length.QuadPart);
+ MOZ_ASSERT(GetLastError() == 0);
+
+ // TODO - Implement logging (Bug 1558912)
+ mShmemFile = targetHandle;
+ if (!mExternalShmem) {
+ MOZ_ASSERT(mExternalShmem);
+ return false;
+ }
+#else
+ // TODO: Implement shmem for other platforms.
+ //
+ // TODO: ** Does this mean that ShMem only works in Windows for now? If so,
+ // let's delete the code from other platforms (Bug 1563234)
+ MOZ_ASSERT(false, "JoinShMem not implemented");
+#endif
+ return true;
+}
+
+// The cleanup corresponding to JoinShMem
+void VRShMem::LeaveShMem() {
+#if defined(XP_WIN)
+ // Check that there are no errors before making system calls
+ MOZ_ASSERT(GetLastError() == 0);
+
+ if (mShmemFile) {
+ ::CloseHandle(mShmemFile);
+ mShmemFile = nullptr;
+ }
+#endif
+
+ if (mExternalShmem != nullptr) {
+#if defined(XP_WIN)
+ if (IsCreatedOnSharedMemory()) {
+ UnmapViewOfFile((void*)mExternalShmem);
+ MOZ_ASSERT(GetLastError() == 0);
+ }
+ // Otherwise, if not created on shared memory, simply null the shared
+ // reference to the heap object. The call to CloseShMem will appropriately
+ // free the allocation.
+#endif
+ mExternalShmem = nullptr;
+ }
+#if defined(XP_WIN)
+ if (mMutex) {
+ MOZ_ASSERT(mRequiresMutex);
+ CloseHandle(mMutex);
+ mMutex = nullptr;
+ }
+#endif
+}
+
+void VRShMem::PushBrowserState(VRBrowserState& aBrowserState,
+ bool aNotifyCond) {
+ if (!mExternalShmem) {
+ return;
+ }
+#if defined(MOZ_WIDGET_ANDROID)
+ if (pthread_mutex_lock((pthread_mutex_t*)&(mExternalShmem->geckoMutex)) ==
+ 0) {
+ memcpy((void*)&(mExternalShmem->geckoState), (void*)&aBrowserState,
+ sizeof(VRBrowserState));
+ if (aNotifyCond) {
+ pthread_cond_signal((pthread_cond_t*)&(mExternalShmem->geckoCond));
+ }
+ pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->geckoMutex));
+ }
+#else
+ bool status = true;
+# if defined(XP_WIN)
+ WaitForMutex lock(mMutex);
+ status = lock.GetStatus();
+# endif // defined(XP_WIN)
+ if (status) {
+ mExternalShmem->geckoGenerationA++;
+ memcpy((void*)&(mExternalShmem->geckoState), (void*)&aBrowserState,
+ sizeof(VRBrowserState));
+ mExternalShmem->geckoGenerationB++;
+ }
+#endif // defined(MOZ_WIDGET_ANDROID)
+}
+
+void VRShMem::PullBrowserState(mozilla::gfx::VRBrowserState& aState) {
+ if (!mExternalShmem) {
+ return;
+ }
+ // Copying the browser state from the shmem is non-blocking
+ // on x86/x64 architectures. Arm requires a mutex that is
+ // locked for the duration of the memcpy to and from shmem on
+ // both sides.
+ // On x86/x64 It is fallable -- If a dirty copy is detected by
+ // a mismatch of geckoGenerationA and geckoGenerationB,
+ // the copy is discarded and will not replace the last known
+ // browser state.
+
+#if defined(MOZ_WIDGET_ANDROID)
+ // TODO: This code is out-of-date and fails to compile, as
+ // VRService isn't compiled for Android (Bug 1563234)
+ /*
+ if (pthread_mutex_lock((pthread_mutex_t*)&(mExternalShmem->geckoMutex)) ==
+ 0) {
+ memcpy(&aState, &tmp.geckoState, sizeof(VRBrowserState));
+ pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->geckoMutex));
+ }
+ */
+ MOZ_ASSERT(false, "PullBrowserState not implemented");
+#else
+ bool status = true;
+# if defined(XP_WIN)
+ if (mRequiresMutex) {
+ // TODO: Is this scoped lock okay? Seems like it should allow some
+ // race condition (Bug 1563234)
+ WaitForMutex lock(mMutex);
+ status = lock.GetStatus();
+ }
+# endif // defined(XP_WIN)
+ if (status) {
+ VRExternalShmem tmp;
+ if (mExternalShmem->geckoGenerationA != mBrowserGeneration) {
+ // TODO - (void *) cast removes volatile semantics.
+ // The memcpy is not likely to be optimized out, but is theoretically
+ // possible. Suggest refactoring to either explicitly enforce memory
+ // order or to use locks.
+ memcpy(&tmp, (void*)mExternalShmem, sizeof(VRExternalShmem));
+ if (tmp.geckoGenerationA == tmp.geckoGenerationB &&
+ tmp.geckoGenerationA != 0) {
+ memcpy(&aState, &tmp.geckoState, sizeof(VRBrowserState));
+ mBrowserGeneration = tmp.geckoGenerationA;
+ }
+ }
+ }
+#endif // defined(MOZ_WIDGET_ANDROID)
+}
+
+void VRShMem::PushSystemState(const mozilla::gfx::VRSystemState& aState) {
+ if (!mExternalShmem) {
+ return;
+ }
+ // Copying the VR service state to the shmem is atomic, infallable,
+ // and non-blocking on x86/x64 architectures. Arm requires a mutex
+ // that is locked for the duration of the memcpy to and from shmem on
+ // both sides.
+
+#if defined(MOZ_WIDGET_ANDROID)
+ // TODO: This code is out-of-date and fails to compile, as
+ // VRService isn't compiled for Android (Bug 1563234)
+ MOZ_ASSERT(false, "JoinShMem not implemented");
+ /*
+ if (pthread_mutex_lock((pthread_mutex_t*)&(mExternalShmem->systemMutex)) ==
+ 0) {
+ // We are casting away the volatile keyword, which is not accepted by
+ // memcpy. It is possible (although very unlikely) that the compiler
+ // may optimize out the memcpy here as memcpy isn't explicitly safe for
+ // volatile memory in the C++ standard.
+ memcpy((void*)&mExternalShmem->state, &aState, sizeof(VRSystemState));
+ pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->systemMutex));
+ }
+ */
+#else
+ bool lockState = true;
+# if defined(XP_WIN)
+ if (mRequiresMutex) {
+ // TODO: Is this scoped lock okay? Seems like it should allow some
+ // race condition (Bug 1563234)
+ WaitForMutex lock(mMutex);
+ lockState = lock.GetStatus();
+ }
+# endif // defined(XP_WIN)
+ if (lockState) {
+ mExternalShmem->generationA++;
+ memcpy((void*)&mExternalShmem->state, &aState, sizeof(VRSystemState));
+ mExternalShmem->generationB++;
+ }
+#endif // defined(MOZ_WIDGET_ANDROID)
+}
+
+#if defined(MOZ_WIDGET_ANDROID)
+void VRShMem::PullSystemState(
+ VRDisplayState& aDisplayState, VRHMDSensorState& aSensorState,
+ VRControllerState (&aControllerState)[kVRControllerMaxCount],
+ bool& aEnumerationCompleted,
+ const std::function<bool()>& aWaitCondition /* = nullptr */) {
+ if (!mExternalShmem) {
+ return;
+ }
+ bool done = false;
+ while (!done) {
+ if (pthread_mutex_lock((pthread_mutex_t*)&(mExternalShmem->systemMutex)) ==
+ 0) {
+ while (true) {
+ memcpy(&aDisplayState, (void*)&(mExternalShmem->state.displayState),
+ sizeof(VRDisplayState));
+ memcpy(&aSensorState, (void*)&(mExternalShmem->state.sensorState),
+ sizeof(VRHMDSensorState));
+ memcpy(aControllerState,
+ (void*)&(mExternalShmem->state.controllerState),
+ sizeof(VRControllerState) * kVRControllerMaxCount);
+ aEnumerationCompleted = mExternalShmem->state.enumerationCompleted;
+ if (!aWaitCondition || aWaitCondition()) {
+ done = true;
+ break;
+ }
+ // Block current thead using the condition variable until data
+ // changes
+ pthread_cond_wait((pthread_cond_t*)&mExternalShmem->systemCond,
+ (pthread_mutex_t*)&mExternalShmem->systemMutex);
+ } // while (true)
+ pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->systemMutex));
+ } else if (!aWaitCondition) {
+ // pthread_mutex_lock failed and we are not waiting for a condition to
+ // exit from PullState call.
+ return;
+ }
+ } // while (!done) {
+}
+#else
+void VRShMem::PullSystemState(
+ VRDisplayState& aDisplayState, VRHMDSensorState& aSensorState,
+ VRControllerState (&aControllerState)[kVRControllerMaxCount],
+ bool& aEnumerationCompleted,
+ const std::function<bool()>& aWaitCondition /* = nullptr */) {
+ MOZ_ASSERT(mExternalShmem);
+ if (!mExternalShmem) {
+ return;
+ }
+ while (true) {
+ { // Scope for WaitForMutex
+# if defined(XP_WIN)
+ bool status = true;
+ WaitForMutex lock(mMutex);
+ status = lock.GetStatus();
+ if (status) {
+# endif // defined(XP_WIN)
+ VRExternalShmem tmp;
+ memcpy(&tmp, (void*)mExternalShmem, sizeof(VRExternalShmem));
+ bool isCleanCopy =
+ tmp.generationA == tmp.generationB && tmp.generationA != 0;
+ if (isCleanCopy) {
+ memcpy(&aDisplayState, &tmp.state.displayState,
+ sizeof(VRDisplayState));
+ memcpy(&aSensorState, &tmp.state.sensorState,
+ sizeof(VRHMDSensorState));
+ memcpy(aControllerState,
+ (void*)&(mExternalShmem->state.controllerState),
+ sizeof(VRControllerState) * kVRControllerMaxCount);
+ aEnumerationCompleted = mExternalShmem->state.enumerationCompleted;
+ // Check for wait condition
+ if (!aWaitCondition || aWaitCondition()) {
+ return;
+ }
+ } else if (!aWaitCondition) {
+ // We did not get a clean copy, and we are not waiting for a condition
+ // to exit from PullState call.
+ return;
+ }
+ // Yield the thread while polling
+ YieldThread();
+# if defined(XP_WIN)
+ } else if (!aWaitCondition) {
+ // WaitForMutex failed and we are not waiting for a condition to
+ // exit from PullState call.
+ return;
+ }
+# endif // defined(XP_WIN)
+ } // End: Scope for WaitForMutex
+ // Yield the thread while polling
+ YieldThread();
+ } // while (!true)
+}
+#endif // defined(MOZ_WIDGET_ANDROID)
+
+void VRShMem::PushWindowState(VRWindowState& aState) {
+#if defined(XP_WIN)
+ if (!mExternalShmem) {
+ return;
+ }
+
+ bool status = true;
+ WaitForMutex lock(mMutex);
+ status = lock.GetStatus();
+ if (status) {
+ memcpy((void*)&(mExternalShmem->windowState), (void*)&aState,
+ sizeof(VRWindowState));
+ }
+#endif // defined(XP_WIN)
+}
+
+void VRShMem::PullWindowState(VRWindowState& aState) {
+#if defined(XP_WIN)
+ if (!mExternalShmem) {
+ return;
+ }
+
+ bool status = true;
+ WaitForMutex lock(mMutex);
+ status = lock.GetStatus();
+ if (status) {
+ memcpy((void*)&aState, (void*)&(mExternalShmem->windowState),
+ sizeof(VRWindowState));
+ }
+#endif // defined(XP_WIN)
+}
+
+void VRShMem::PushTelemetryState(VRTelemetryState& aState) {
+#if defined(XP_WIN)
+ if (!mExternalShmem) {
+ return;
+ }
+
+ bool status = true;
+ WaitForMutex lock(mMutex);
+ status = lock.GetStatus();
+ if (status) {
+ memcpy((void*)&(mExternalShmem->telemetryState), (void*)&aState,
+ sizeof(VRTelemetryState));
+ }
+#endif // defined(XP_WIN)
+}
+void VRShMem::PullTelemetryState(VRTelemetryState& aState) {
+#if defined(XP_WIN)
+ if (!mExternalShmem) {
+ return;
+ }
+
+ bool status = true;
+ WaitForMutex lock(mMutex);
+ status = lock.GetStatus();
+ if (status) {
+ memcpy((void*)&aState, (void*)&(mExternalShmem->telemetryState),
+ sizeof(VRTelemetryState));
+ }
+#endif // defined(XP_WIN)
+}
+
+void VRShMem::SendEvent(uint64_t aWindowID,
+ mozilla::gfx::VRFxEventType aEventType,
+ mozilla::gfx::VRFxEventState aEventState) {
+ MOZ_ASSERT(!HasExternalShmem());
+ if (JoinShMem()) {
+ mozilla::gfx::VRWindowState windowState = {0};
+ PullWindowState(windowState);
+ windowState.windowID = aWindowID;
+ windowState.eventType = aEventType;
+ windowState.eventState = aEventState;
+ PushWindowState(windowState);
+ LeaveShMem();
+
+#if defined(XP_WIN)
+ // Notify the waiting host process that the data is now available
+ HANDLE hSignal = ::OpenEventA(EVENT_ALL_ACCESS, // dwDesiredAccess
+ FALSE, // bInheritHandle
+ windowState.signalName // lpName
+ );
+ ::SetEvent(hSignal);
+ ::CloseHandle(hSignal);
+#endif // defined(XP_WIN)
+ }
+}
+
+void VRShMem::SendIMEState(uint64_t aWindowID,
+ mozilla::gfx::VRFxEventState aEventState) {
+ SendEvent(aWindowID, mozilla::gfx::VRFxEventType::IME, aEventState);
+}
+
+void VRShMem::SendFullscreenState(uint64_t aWindowID, bool aFullscreen) {
+ SendEvent(aWindowID, mozilla::gfx::VRFxEventType::FULLSCREEN,
+ aFullscreen ? mozilla::gfx::VRFxEventState::FULLSCREEN_ENTER
+ : mozilla::gfx::VRFxEventState::FULLSCREEN_EXIT);
+}
+
+// Note: this should be called from the VRShMem instance that created
+// the external shmem rather than joined it.
+void VRShMem::SendShutdowmState(uint64_t aWindowID) {
+ MOZ_ASSERT(HasExternalShmem());
+
+ mozilla::gfx::VRWindowState windowState = {0};
+ PullWindowState(windowState);
+ windowState.windowID = aWindowID;
+ windowState.eventType = mozilla::gfx::VRFxEventType::SHUTDOWN;
+ PushWindowState(windowState);
+
+#if defined(XP_WIN)
+ // Notify the waiting host process that the data is now available
+ HANDLE hSignal = ::OpenEventA(EVENT_ALL_ACCESS, // dwDesiredAccess
+ FALSE, // bInheritHandle
+ windowState.signalName // lpName
+ );
+ ::SetEvent(hSignal);
+ ::CloseHandle(hSignal);
+#endif // defined(XP_WIN)
+}
diff --git a/gfx/vr/VRShMem.h b/gfx/vr/VRShMem.h
new file mode 100644
index 0000000000..a10b8189a8
--- /dev/null
+++ b/gfx/vr/VRShMem.h
@@ -0,0 +1,105 @@
+/* -*- 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_VRSHMEM_H
+#define GFX_VR_VRSHMEM_H
+
+// This file declares VRShMem, which is used for cross-platform, cross-process
+// communication between VR components.
+// A shared memory handle is opened for the struct of type VRExternalShmem, and
+// different processes write or read members of it to share data. Note that no
+// process should be reading or writing to the same members, except for the
+// versioning members used for synchronization.
+
+#include "moz_external_vr.h"
+#include "base/process.h" // for base::ProcessHandle
+#include <functional>
+
+namespace mozilla {
+namespace gfx {
+class VRShMem final {
+ public:
+ VRShMem(volatile VRExternalShmem* aShmem, bool aRequiresMutex);
+ ~VRShMem() = default;
+
+ void CreateShMem(bool aCreateOnSharedMemory);
+ void CreateShMemForAndroid();
+ void ClearShMem();
+ void CloseShMem();
+
+ bool JoinShMem();
+ void LeaveShMem();
+
+ void PushBrowserState(VRBrowserState& aBrowserState, bool aNotifyCond);
+ void PullBrowserState(mozilla::gfx::VRBrowserState& aState);
+
+ void PushSystemState(const mozilla::gfx::VRSystemState& aState);
+ void PullSystemState(
+ VRDisplayState& aDisplayState, VRHMDSensorState& aSensorState,
+ VRControllerState (&aControllerState)[kVRControllerMaxCount],
+ bool& aEnumerationCompleted,
+ const std::function<bool()>& aWaitCondition = nullptr);
+
+ void PushWindowState(VRWindowState& aState);
+ void PullWindowState(VRWindowState& aState);
+
+ void PushTelemetryState(VRTelemetryState& aState);
+ void PullTelemetryState(VRTelemetryState& aState);
+
+ void SendIMEState(uint64_t aWindowID, mozilla::gfx::VRFxEventState aImeState);
+ void SendFullscreenState(uint64_t aWindowID, bool aFullscreen);
+ void SendShutdowmState(uint64_t aWindowID);
+
+ bool HasExternalShmem() const { return mExternalShmem != nullptr; }
+ bool IsSharedExternalShmem() const { return mIsSharedExternalShmem; }
+ volatile VRExternalShmem* GetExternalShmem() const;
+ bool IsDisplayStateShutdown() const;
+
+ private:
+ bool IsCreatedOnSharedMemory() const;
+ void SendEvent(uint64_t aWindowID, mozilla::gfx::VRFxEventType aEventType,
+ mozilla::gfx::VRFxEventState aEventState);
+
+ // mExternalShmem can have one of three sources:
+ // - Allocated outside of this class on the heap and passed in via
+ // constructor, or acquired via GeckoVRManager (for GeckoView scenario).
+ // This is usually for scenarios where there is no VR process for cross-
+ // proc communication, and VRService is receiving the object.
+ // --> mIsSharedExternalShmem == true, IsCreatedOnSharedMemory() == false
+ // --> [Windows 7, Mac, Android, Linux]
+ // - Allocated inside this class on the heap. This is usually for scenarios
+ // where there is no VR process, and VRManager is creating the object.
+ // --> mIsSharedExternalShmem == false, IsCreatedOnSharedMemory() == false
+ // --> [Windows 7, Mac, Linux]
+ // - Allocated inside this class on shared memory. This is usually for
+ // scenarios where there is a VR process and cross-process communication
+ // is necessary
+ // --> mIsSharedExternalShmem == false, IsCreatedOnSharedMemory() == true
+ // --> [Windows 10 with VR process enabled]
+ volatile VRExternalShmem* mExternalShmem = nullptr;
+ // This member is true when mExternalShmem was allocated externally, via
+ // being passed into the constructor or accessed via GeckoVRManager
+ bool mIsSharedExternalShmem;
+
+#if defined(XP_WIN)
+ bool mRequiresMutex;
+#endif
+
+#if defined(XP_MACOSX)
+ int mShmemFD;
+#elif defined(XP_WIN)
+ base::ProcessHandle mShmemFile;
+ base::ProcessHandle mMutex;
+#endif
+
+#if !defined(MOZ_WIDGET_ANDROID)
+ int64_t mBrowserGeneration = 0;
+#endif
+};
+} // namespace gfx
+} // namespace mozilla
+
+#endif
diff --git a/gfx/vr/VRThread.cpp b/gfx/vr/VRThread.cpp
new file mode 100644
index 0000000000..818a16dbfd
--- /dev/null
+++ b/gfx/vr/VRThread.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 "VRThread.h"
+#include "nsDebug.h"
+#include "nsThreadManager.h"
+#include "nsThread.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+
+namespace gfx {
+
+static const uint32_t kDefaultThreadLifeTime = 60; // in 60 seconds.
+static const uint32_t kDelayPostTaskTime = 20000; // in 20000 ms.
+
+VRThread::VRThread(const nsCString& aName)
+ : mThread(nullptr), mLifeTime(kDefaultThreadLifeTime), mStarted(false) {
+ mName = aName;
+}
+
+VRThread::~VRThread() { Shutdown(); }
+
+void VRThread::Start() {
+ if (!mThread) {
+ nsresult rv = NS_NewNamedThread(mName, getter_AddRefs(mThread));
+ MOZ_ASSERT(mThread);
+
+ if (NS_FAILED(rv)) {
+ MOZ_ASSERT(false, "Failed to create a vr thread.");
+ }
+ RefPtr<Runnable> runnable =
+ NewRunnableMethod<TimeStamp>("gfx::VRThread::CheckLife", this,
+ &VRThread::CheckLife, TimeStamp::Now());
+ // Post it to the main thread for tracking the lifetime.
+ nsCOMPtr<nsIThread> mainThread;
+ rv = NS_GetMainThread(getter_AddRefs(mainThread));
+ if (NS_FAILED(rv)) {
+ NS_WARNING("VRThread::Start() could not get Main thread");
+ return;
+ }
+ mainThread->DelayedDispatch(runnable.forget(), kDelayPostTaskTime);
+ }
+ mStarted = true;
+ mLastActiveTime = TimeStamp::Now();
+}
+
+void VRThread::Shutdown() {
+ if (mThread) {
+ if (nsThreadManager::get().IsNSThread()) {
+ mThread->Shutdown();
+ } else {
+ NS_WARNING(
+ "VRThread::Shutdown() may only be called from an XPCOM thread");
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "VRThread::Shutdown", [thread = mThread]() { thread->Shutdown(); }));
+ }
+ mThread = nullptr;
+ }
+ mStarted = false;
+}
+
+const nsCOMPtr<nsIThread> VRThread::GetThread() const { return mThread; }
+
+void VRThread::PostTask(already_AddRefed<Runnable> aTask) {
+ PostDelayedTask(std::move(aTask), 0);
+}
+
+void VRThread::PostDelayedTask(already_AddRefed<Runnable> aTask,
+ uint32_t aTime) {
+ MOZ_ASSERT(mStarted, "Must call Start() before posting tasks.");
+ MOZ_ASSERT(mThread);
+ mLastActiveTime = TimeStamp::Now();
+
+ if (!aTime) {
+ mThread->Dispatch(std::move(aTask), NS_DISPATCH_NORMAL);
+ } else {
+ mThread->DelayedDispatch(std::move(aTask), aTime);
+ }
+}
+
+void VRThread::CheckLife(TimeStamp aCheckTimestamp) {
+ // VR system is going to shutdown.
+ if (!mStarted) {
+ Shutdown();
+ return;
+ }
+
+ const TimeDuration timeout = TimeDuration::FromSeconds(mLifeTime);
+ if ((aCheckTimestamp - mLastActiveTime) > timeout) {
+ Shutdown();
+ } else {
+ RefPtr<Runnable> runnable =
+ NewRunnableMethod<TimeStamp>("gfx::VRThread::CheckLife", this,
+ &VRThread::CheckLife, TimeStamp::Now());
+ // Post it to the main thread for tracking the lifetime.
+ nsCOMPtr<nsIThread> mainThread;
+ nsresult rv = NS_GetMainThread(getter_AddRefs(mainThread));
+ if (NS_FAILED(rv)) {
+ NS_WARNING("VRThread::CheckLife() could not get Main thread");
+ return;
+ }
+ mainThread->DelayedDispatch(runnable.forget(), kDelayPostTaskTime);
+ }
+}
+
+void VRThread::SetLifeTime(uint32_t aLifeTime) { mLifeTime = aLifeTime; }
+
+uint32_t VRThread::GetLifeTime() { return mLifeTime; }
+
+bool VRThread::IsActive() { return !!mThread; }
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/vr/VRThread.h b/gfx/vr/VRThread.h
new file mode 100644
index 0000000000..85d9ea4b84
--- /dev/null
+++ b/gfx/vr/VRThread.h
@@ -0,0 +1,46 @@
+/* -*- 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_THREAD_H
+#define GFX_VR_THREAD_H
+
+#include "nsISupportsImpl.h"
+#include "base/thread.h" // for Thread
+
+namespace mozilla {
+namespace gfx {
+
+class VRThread final {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VRThread)
+
+ public:
+ explicit VRThread(const nsCString& aName);
+
+ void Start();
+ void Shutdown();
+ void SetLifeTime(uint32_t aLifeTime);
+ uint32_t GetLifeTime();
+ void CheckLife(TimeStamp aCheckTimestamp);
+ void PostTask(already_AddRefed<Runnable> aTask);
+ void PostDelayedTask(already_AddRefed<Runnable> aTask, uint32_t aTime);
+ const nsCOMPtr<nsIThread> GetThread() const;
+ bool IsActive();
+
+ protected:
+ ~VRThread();
+
+ private:
+ nsCOMPtr<nsIThread> mThread;
+ TimeStamp mLastActiveTime;
+ nsCString mName;
+ uint32_t mLifeTime;
+ Atomic<bool> mStarted;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // GFX_VR_THREAD_H
diff --git a/gfx/vr/components.conf b/gfx/vr/components.conf
new file mode 100644
index 0000000000..169357c40c
--- /dev/null
+++ b/gfx/vr/components.conf
@@ -0,0 +1,15 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+Classes = [
+ {
+ 'cid': '{5baca10a-4d53-4335-b24d-c69696640a9a}',
+ 'contract_ids': ['@mozilla.org/fxr/clh;1'],
+ 'type': 'nsFxrCommandLineHandler',
+ 'headers': ['/gfx/vr/nsFxrCommandLineHandler.h'],
+ 'categories': {'command-line-handler': 'm-vrbrowser'},
+ },
+]
diff --git a/gfx/vr/external_api/moz_external_vr.h b/gfx/vr/external_api/moz_external_vr.h
new file mode 100644
index 0000000000..1727e1e540
--- /dev/null
+++ b/gfx/vr/external_api/moz_external_vr.h
@@ -0,0 +1,670 @@
+/* -*- 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_EXTERNAL_API_H
+#define GFX_VR_EXTERNAL_API_H
+
+#define GFX_VR_EIGHTCC(c1, c2, c3, c4, c5, c6, c7, c8) \
+ ((uint64_t)(c1) << 56 | (uint64_t)(c2) << 48 | (uint64_t)(c3) << 40 | \
+ (uint64_t)(c4) << 32 | (uint64_t)(c5) << 24 | (uint64_t)(c6) << 16 | \
+ (uint64_t)(c7) << 8 | (uint64_t)(c8))
+
+#ifdef MOZILLA_INTERNAL_API
+# define __STDC_WANT_LIB_EXT1__ 1
+// __STDC_WANT_LIB_EXT1__ required for memcpy_s
+# include <stdlib.h>
+# include <string.h>
+# include "mozilla/TypedEnumBits.h"
+# include "mozilla/gfx/2D.h"
+# include <stddef.h>
+# include <stdint.h>
+# include <type_traits>
+#endif // MOZILLA_INTERNAL_API
+
+#if defined(__ANDROID__)
+# include <pthread.h>
+#endif // defined(__ANDROID__)
+
+#include <cstdint>
+#include <type_traits>
+
+namespace mozilla {
+#ifdef MOZILLA_INTERNAL_API
+namespace dom {
+enum class GamepadHand : uint8_t;
+enum class GamepadCapabilityFlags : uint16_t;
+} // namespace dom
+#endif // MOZILLA_INTERNAL_API
+namespace gfx {
+
+// If there is any change of "SHMEM_VERSION" or "kVRExternalVersion",
+// we need to change both of them at the same time.
+
+// TODO: we might need to use different names for the mutexes
+// and mapped files if we have both release and nightlies
+// running at the same time? Or...what if we have multiple
+// release builds running on same machine? (Bug 1563232)
+#define SHMEM_VERSION "0.0.11"
+static const int32_t kVRExternalVersion = 18;
+
+// We assign VR presentations to groups with a bitmask.
+// Currently, we will only display either content or chrome.
+// Later, we will have more groups to support VR home spaces and
+// multitasking environments.
+// These values are not exposed to regular content and only affect
+// chrome-only API's. They may be changed at any time.
+static const uint32_t kVRGroupNone = 0;
+static const uint32_t kVRGroupContent = 1 << 0;
+static const uint32_t kVRGroupChrome = 1 << 1;
+static const uint32_t kVRGroupAll = 0xffffffff;
+
+static const int kVRDisplayNameMaxLen = 256;
+static const int kVRControllerNameMaxLen = 256;
+static const int kVRControllerMaxCount = 16;
+static const int kVRControllerMaxButtons = 64;
+static const int kVRControllerMaxAxis = 16;
+static const int kVRLayerMaxCount = 8;
+static const int kVRHapticsMaxCount = 32;
+
+#if defined(__ANDROID__)
+typedef uint64_t VRLayerTextureHandle;
+#elif defined(XP_MACOSX)
+typedef uint32_t VRLayerTextureHandle;
+#else
+typedef void* VRLayerTextureHandle;
+#endif
+
+struct Point3D_POD {
+ float x;
+ float y;
+ float z;
+};
+
+struct IntSize_POD {
+ int32_t width;
+ int32_t height;
+};
+
+struct FloatSize_POD {
+ float width;
+ float height;
+};
+
+#ifndef MOZILLA_INTERNAL_API
+
+enum class ControllerHand : uint8_t { _empty, Left, Right, EndGuard_ };
+
+enum class ControllerCapabilityFlags : uint16_t {
+ Cap_None = 0,
+ /**
+ * Cap_Position is set if the Gamepad is capable of tracking its position.
+ */
+ Cap_Position = 1 << 1,
+ /**
+ * Cap_Orientation is set if the Gamepad is capable of tracking its
+ * orientation.
+ */
+ Cap_Orientation = 1 << 2,
+ /**
+ * Cap_AngularAcceleration is set if the Gamepad is capable of tracking its
+ * angular acceleration.
+ */
+ Cap_AngularAcceleration = 1 << 3,
+ /**
+ * Cap_LinearAcceleration is set if the Gamepad is capable of tracking its
+ * linear acceleration.
+ */
+ Cap_LinearAcceleration = 1 << 4,
+ /**
+ * Cap_TargetRaySpacePosition is set if the Gamepad has a grip space position.
+ */
+ Cap_GripSpacePosition = 1 << 5,
+ /**
+ * Cap_PositionEmulated is set if the XRInputSoruce is capable of setting a
+ * emulated position (e.g. neck model) even if still doesn't support 6DOF
+ * tracking.
+ */
+ Cap_PositionEmulated = 1 << 6,
+ /**
+ * Cap_All used for validity checking during IPC serialization
+ */
+ Cap_All = (1 << 7) - 1
+};
+
+#endif // ifndef MOZILLA_INTERNAL_API
+
+enum class VRControllerType : uint8_t {
+ _empty,
+ HTCVive,
+ HTCViveCosmos,
+ HTCViveFocus,
+ HTCViveFocusPlus,
+ MSMR,
+ ValveIndex,
+ OculusGo,
+ OculusTouch,
+ OculusTouch2,
+ PicoGaze,
+ PicoG2,
+ PicoNeo2,
+ _end
+};
+
+enum class TargetRayMode : uint8_t { Gaze, TrackedPointer, Screen };
+
+enum class GamepadMappingType : uint8_t { _empty, Standard, XRStandard };
+
+enum class VRDisplayBlendMode : uint8_t { Opaque, Additive, AlphaBlend };
+
+enum class VRDisplayCapabilityFlags : uint16_t {
+ Cap_None = 0,
+ /**
+ * Cap_Position is set if the VRDisplay is capable of tracking its position.
+ */
+ Cap_Position = 1 << 1,
+ /**
+ * Cap_Orientation is set if the VRDisplay is capable of tracking its
+ * orientation.
+ */
+ Cap_Orientation = 1 << 2,
+ /**
+ * Cap_Present is set if the VRDisplay is capable of presenting content to an
+ * HMD or similar device. Can be used to indicate "magic window" devices that
+ * are capable of 6DoF tracking but for which requestPresent is not
+ * meaningful. If false then calls to requestPresent should always fail, and
+ * getEyeParameters should return null.
+ */
+ Cap_Present = 1 << 3,
+ /**
+ * Cap_External is set if the VRDisplay is separate from the device's
+ * primary display. If presenting VR content will obscure
+ * other content on the device, this should be un-set. When
+ * un-set, the application should not attempt to mirror VR content
+ * or update non-VR UI because that content will not be visible.
+ */
+ Cap_External = 1 << 4,
+ /**
+ * Cap_AngularAcceleration is set if the VRDisplay is capable of tracking its
+ * angular acceleration.
+ */
+ Cap_AngularAcceleration = 1 << 5,
+ /**
+ * Cap_LinearAcceleration is set if the VRDisplay is capable of tracking its
+ * linear acceleration.
+ */
+ Cap_LinearAcceleration = 1 << 6,
+ /**
+ * Cap_StageParameters is set if the VRDisplay is capable of room scale VR
+ * and can report the StageParameters to describe the space.
+ */
+ Cap_StageParameters = 1 << 7,
+ /**
+ * Cap_MountDetection is set if the VRDisplay is capable of sensing when the
+ * user is wearing the device.
+ */
+ Cap_MountDetection = 1 << 8,
+ /**
+ * Cap_PositionEmulated is set if the VRDisplay is capable of setting a
+ * emulated position (e.g. neck model) even if still doesn't support 6DOF
+ * tracking.
+ */
+ Cap_PositionEmulated = 1 << 9,
+ /**
+ * Cap_Inline is set if the device can be used for WebXR inline sessions
+ * where the content is displayed within an element on the page.
+ */
+ Cap_Inline = 1 << 10,
+ /**
+ * Cap_ImmersiveVR is set if the device can give exclusive access to the
+ * XR device display and that content is not intended to be integrated
+ * with the user's environment
+ */
+ Cap_ImmersiveVR = 1 << 11,
+ /**
+ * Cap_ImmersiveAR is set if the device can give exclusive access to the
+ * XR device display and that content is intended to be integrated with
+ * the user's environment.
+ */
+ Cap_ImmersiveAR = 1 << 12,
+ /**
+ * Cap_UseDepthValues is set if the device will use the depth values of the
+ * submitted frames if provided. How the depth values are used is determined
+ * by the VR runtime. Often the depth is used for occlusion of system UI
+ * or to enable more effective asynchronous reprojection of frames.
+ */
+ Cap_UseDepthValues = 1 << 13,
+ /**
+ * Cap_All used for validity checking during IPC serialization
+ */
+ Cap_All = (1 << 14) - 1
+};
+
+#ifdef MOZILLA_INTERNAL_API
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(VRDisplayCapabilityFlags)
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(VRDisplayBlendMode)
+#endif // MOZILLA_INTERNAL_API
+
+struct VRPose {
+ float orientation[4];
+ float position[3];
+ float angularVelocity[3];
+ float angularAcceleration[3];
+ float linearVelocity[3];
+ float linearAcceleration[3];
+};
+
+struct VRHMDSensorState {
+ uint64_t inputFrameID;
+ double timestamp;
+ VRDisplayCapabilityFlags flags;
+
+ // These members will only change with inputFrameID:
+ VRPose pose;
+ float leftViewMatrix[16];
+ float rightViewMatrix[16];
+
+#ifdef MOZILLA_INTERNAL_API
+
+ void Clear() { memset(this, 0, sizeof(VRHMDSensorState)); }
+
+ bool operator==(const VRHMDSensorState& other) const {
+ return inputFrameID == other.inputFrameID && timestamp == other.timestamp;
+ }
+
+ bool operator!=(const VRHMDSensorState& other) const {
+ return !(*this == other);
+ }
+
+ void CalcViewMatrices(const gfx::Matrix4x4* aHeadToEyeTransforms);
+
+#endif // MOZILLA_INTERNAL_API
+};
+
+struct VRFieldOfView {
+ double upDegrees;
+ double rightDegrees;
+ double downDegrees;
+ double leftDegrees;
+
+#ifdef MOZILLA_INTERNAL_API
+
+ VRFieldOfView() = default;
+ VRFieldOfView(double up, double right, double down, double left)
+ : upDegrees(up),
+ rightDegrees(right),
+ downDegrees(down),
+ leftDegrees(left) {}
+
+ void SetFromTanRadians(double up, double right, double down, double left) {
+ upDegrees = atan(up) * 180.0 / M_PI;
+ rightDegrees = atan(right) * 180.0 / M_PI;
+ downDegrees = atan(down) * 180.0 / M_PI;
+ leftDegrees = atan(left) * 180.0 / M_PI;
+ }
+
+ bool operator==(const VRFieldOfView& other) const {
+ return other.upDegrees == upDegrees && other.downDegrees == downDegrees &&
+ other.rightDegrees == rightDegrees &&
+ other.leftDegrees == leftDegrees;
+ }
+
+ bool operator!=(const VRFieldOfView& other) const {
+ return !(*this == other);
+ }
+
+ bool IsZero() const {
+ return upDegrees == 0.0 || rightDegrees == 0.0 || downDegrees == 0.0 ||
+ leftDegrees == 0.0;
+ }
+
+ Matrix4x4 ConstructProjectionMatrix(float zNear, float zFar,
+ bool rightHanded) const;
+
+#endif // MOZILLA_INTERNAL_API
+};
+
+struct VRDisplayState {
+ enum Eye { Eye_Left, Eye_Right, NumEyes };
+
+ // When true, indicates that the VR service has shut down
+ bool shutdown;
+ // Minimum number of milliseconds to wait before attempting
+ // to start the VR service again
+ uint32_t minRestartInterval;
+ char displayName[kVRDisplayNameMaxLen];
+ // eight byte character code identifier
+ // LSB first, so "ABCDEFGH" -> ('H'<<56) + ('G'<<48) + ('F'<<40) +
+ // ('E'<<32) + ('D'<<24) + ('C'<<16) +
+ // ('B'<<8) + 'A').
+ uint64_t eightCC;
+ VRDisplayCapabilityFlags capabilityFlags;
+ VRDisplayBlendMode blendMode;
+ VRFieldOfView eyeFOV[VRDisplayState::NumEyes];
+ Point3D_POD eyeTranslation[VRDisplayState::NumEyes];
+ IntSize_POD eyeResolution;
+ float nativeFramebufferScaleFactor;
+ bool suppressFrames;
+ bool isConnected;
+ bool isMounted;
+ FloatSize_POD stageSize;
+ // We can't use a Matrix4x4 here unless we ensure it's a POD type
+ float sittingToStandingTransform[16];
+ uint64_t lastSubmittedFrameId;
+ bool lastSubmittedFrameSuccessful;
+ uint32_t presentingGeneration;
+ // Telemetry
+ bool reportsDroppedFrames;
+ uint64_t droppedFrameCount;
+
+#ifdef MOZILLA_INTERNAL_API
+ void Clear() { memset(this, 0, sizeof(VRDisplayState)); }
+#endif
+};
+
+struct VRControllerState {
+ char controllerName[kVRControllerNameMaxLen];
+#ifdef MOZILLA_INTERNAL_API
+ dom::GamepadHand hand;
+#else
+ ControllerHand hand;
+#endif
+ // For WebXR->WebVR mapping conversion, once we remove WebVR,
+ // we can remove this item.
+ VRControllerType type;
+ // https://immersive-web.github.io/webxr/#enumdef-xrtargetraymode
+ TargetRayMode targetRayMode;
+
+ // https://immersive-web.github.io/webxr-gamepads-module/#enumdef-gamepadmappingtype
+ GamepadMappingType mappingType;
+
+ // Start frame ID of the most recent primary select
+ // action, or 0 if the select action has never occurred.
+ uint64_t selectActionStartFrameId;
+ // End frame Id of the most recent primary select
+ // action, or 0 if action never occurred.
+ // If selectActionStopFrameId is less than
+ // selectActionStartFrameId, then the select
+ // action has not ended yet.
+ uint64_t selectActionStopFrameId;
+
+ // start frame Id of the most recent primary squeeze
+ // action, or 0 if the squeeze action has never occurred.
+ uint64_t squeezeActionStartFrameId;
+ // End frame Id of the most recent primary squeeze
+ // action, or 0 if action never occurred.
+ // If squeezeActionStopFrameId is less than
+ // squeezeActionStartFrameId, then the squeeze
+ // action has not ended yet.
+ uint64_t squeezeActionStopFrameId;
+
+ uint32_t numButtons;
+ uint32_t numAxes;
+ uint32_t numHaptics;
+ // The current button pressed bit of button mask.
+ uint64_t buttonPressed;
+ // The current button touched bit of button mask.
+ uint64_t buttonTouched;
+ float triggerValue[kVRControllerMaxButtons];
+ float axisValue[kVRControllerMaxAxis];
+
+#ifdef MOZILLA_INTERNAL_API
+ dom::GamepadCapabilityFlags flags;
+#else
+ ControllerCapabilityFlags flags;
+#endif
+
+ // When Cap_Position is set in flags, pose corresponds
+ // to the controllers' pose in grip space:
+ // https://immersive-web.github.io/webxr/#dom-xrinputsource-gripspace
+ VRPose pose;
+
+ // When Cap_TargetRaySpacePosition is set in flags, targetRayPose corresponds
+ // to the controllers' pose in target ray space:
+ // https://immersive-web.github.io/webxr/#dom-xrinputsource-targetrayspace
+ VRPose targetRayPose;
+
+ bool isPositionValid;
+ bool isOrientationValid;
+
+#ifdef MOZILLA_INTERNAL_API
+ void Clear() { memset(this, 0, sizeof(VRControllerState)); }
+#endif
+};
+
+struct VRLayerEyeRect {
+ float x;
+ float y;
+ float width;
+ float height;
+};
+
+enum class VRLayerType : uint16_t {
+ LayerType_None = 0,
+ LayerType_2D_Content = 1,
+ LayerType_Stereo_Immersive = 2
+};
+
+enum class VRLayerTextureType : uint16_t {
+ LayerTextureType_None = 0,
+ LayerTextureType_D3D10SurfaceDescriptor = 1,
+ LayerTextureType_MacIOSurface = 2,
+ LayerTextureType_GeckoSurfaceTexture = 3
+};
+
+struct VRLayer_2D_Content {
+ VRLayerTextureHandle textureHandle;
+ VRLayerTextureType textureType;
+ uint64_t frameId;
+};
+
+struct VRLayer_Stereo_Immersive {
+ VRLayerTextureHandle textureHandle;
+ VRLayerTextureType textureType;
+ uint64_t frameId;
+ uint64_t inputFrameId;
+ VRLayerEyeRect leftEyeRect;
+ VRLayerEyeRect rightEyeRect;
+ IntSize_POD textureSize;
+};
+
+struct VRLayerState {
+ VRLayerType type;
+ union {
+ VRLayer_2D_Content layer_2d_content;
+ VRLayer_Stereo_Immersive layer_stereo_immersive;
+ };
+};
+
+struct VRHapticState {
+ // Reference frame for timing.
+ // When 0, this does not represent an active haptic pulse.
+ uint64_t inputFrameID;
+ // Index within VRSystemState.controllerState identifying the controller
+ // to emit the haptic pulse
+ uint32_t controllerIndex;
+ // 0-based index indicating which haptic actuator within the controller
+ uint32_t hapticIndex;
+ // Start time of the haptic feedback pulse, relative to the start of
+ // inputFrameID, in seconds
+ float pulseStart;
+ // Duration of the haptic feedback pulse, in seconds
+ float pulseDuration;
+ // Intensity of the haptic feedback pulse, from 0.0f to 1.0f
+ float pulseIntensity;
+};
+
+struct VRBrowserState {
+#if defined(__ANDROID__)
+ bool shutdown;
+#endif // defined(__ANDROID__)
+ /**
+ * In order to support WebXR's navigator.xr.IsSessionSupported call without
+ * displaying any permission dialogue, it is necessary to have a safe way to
+ * detect the capability of running a VR or AR session without activating XR
+ * runtimes or powering on hardware.
+ *
+ * API's such as OpenVR make no guarantee that hardware and software won't be
+ * left activated after enumerating devices, so each backend in gfx/vr/service
+ * must allow for more granular detection of capabilities.
+ *
+ * When detectRuntimesOnly is true, the initialization exits early after
+ * reporting the presence of XR runtime software.
+ *
+ * The result of the runtime detection is reported with the Cap_ImmersiveVR
+ * and Cap_ImmersiveAR bits in VRDisplayState.flags.
+ */
+ bool detectRuntimesOnly;
+ bool presentationActive;
+ bool navigationTransitionActive;
+ VRLayerState layerState[kVRLayerMaxCount];
+ VRHapticState hapticState[kVRHapticsMaxCount];
+
+#ifdef MOZILLA_INTERNAL_API
+ void Clear() { memset(this, 0, sizeof(VRBrowserState)); }
+#endif
+};
+
+struct VRSystemState {
+ bool enumerationCompleted;
+ VRDisplayState displayState;
+ VRHMDSensorState sensorState;
+ VRControllerState controllerState[kVRControllerMaxCount];
+};
+
+enum class VRFxEventType : uint8_t {
+ NONE = 0,
+ IME,
+ SHUTDOWN,
+ FULLSCREEN,
+ TOTAL
+};
+
+enum class VRFxEventState : uint8_t {
+ NONE = 0,
+ BLUR,
+ FOCUS,
+ FULLSCREEN_ENTER,
+ FULLSCREEN_EXIT,
+ TOTAL
+};
+
+// Data shared via shmem for running Firefox in a VR windowed environment
+struct VRWindowState {
+ // State from Firefox
+ uint64_t hwndFx;
+ uint32_t widthFx;
+ uint32_t heightFx;
+ VRLayerTextureHandle textureFx;
+ uint32_t windowID;
+ VRFxEventType eventType;
+ VRFxEventState eventState;
+
+ // State from VRHost
+ uint32_t dxgiAdapterHost;
+ uint32_t widthHost;
+ uint32_t heightHost;
+
+ // Name of synchronization primitive to signal change to this struct
+ char signalName[32];
+};
+
+enum class VRTelemetryId : uint8_t {
+ NONE = 0,
+ INSTALLED_FROM = 1,
+ ENTRY_METHOD = 2,
+ FIRST_RUN = 3,
+ TOTAL = 4,
+};
+
+enum class VRTelemetryInstallFrom : uint8_t {
+ User = 0,
+ FxR = 1,
+ HTC = 2,
+ Valve = 3,
+ TOTAL = 4,
+};
+
+enum class VRTelemetryEntryMethod : uint8_t {
+ SystemBtn = 0,
+ Library = 1,
+ Gaze = 2,
+ TOTAL = 3,
+};
+
+struct VRTelemetryState {
+ uint32_t uid;
+
+ bool installedFrom : 1;
+ bool entryMethod : 1;
+ bool firstRun : 1;
+
+ uint8_t installedFromValue : 3;
+ uint8_t entryMethodValue : 3;
+ bool firstRunValue : 1;
+};
+
+struct VRExternalShmem {
+ int32_t version;
+ int32_t size;
+#if defined(__ANDROID__)
+ pthread_mutex_t systemMutex;
+ pthread_mutex_t geckoMutex;
+ pthread_mutex_t servoMutex;
+ pthread_cond_t systemCond;
+ pthread_cond_t geckoCond;
+ pthread_cond_t servoCond;
+#else
+ int64_t generationA;
+#endif // defined(__ANDROID__)
+ VRSystemState state;
+#if !defined(__ANDROID__)
+ int64_t generationB;
+ int64_t geckoGenerationA;
+ int64_t servoGenerationA;
+#endif // !defined(__ANDROID__)
+ VRBrowserState geckoState;
+ VRBrowserState servoState;
+#if !defined(__ANDROID__)
+ int64_t geckoGenerationB;
+ int64_t servoGenerationB;
+#endif // !defined(__ANDROID__)
+#if defined(XP_WIN)
+ VRWindowState windowState;
+ VRTelemetryState telemetryState;
+#endif
+#ifdef MOZILLA_INTERNAL_API
+ void Clear() volatile {
+/**
+ * When possible we do a memset_s, which is explicitly safe for
+ * the volatile, POD struct. A memset may be optimized out by
+ * the compiler and will fail to compile due to volatile keyword
+ * propagation.
+ *
+ * A loop-based fallback is provided in case the toolchain does
+ * not include STDC_LIB_EXT1 for memset_s.
+ */
+# ifdef __STDC_LIB_EXT1__
+ memset_s((void*)this, sizeof(VRExternalShmem), 0, sizeof(VRExternalShmem));
+# else
+ size_t remaining = sizeof(VRExternalShmem);
+ volatile unsigned char* d = (volatile unsigned char*)this;
+ while (remaining--) {
+ *d++ = 0;
+ }
+# endif
+ }
+#endif
+};
+
+// As we are memcpy'ing VRExternalShmem and its members around, it must be a POD
+// type
+static_assert(std::is_pod<VRExternalShmem>::value,
+ "VRExternalShmem must be a POD type.");
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* GFX_VR_EXTERNAL_API_H */
diff --git a/gfx/vr/gfxVR.cpp b/gfx/vr/gfxVR.cpp
new file mode 100644
index 0000000000..ed1c6d32d6
--- /dev/null
+++ b/gfx/vr/gfxVR.cpp
@@ -0,0 +1,91 @@
+/* -*- 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 <math.h>
+
+#include "gfxVR.h"
+
+#ifndef M_PI
+# define M_PI 3.14159265358979323846
+#endif
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+
+Matrix4x4 VRFieldOfView::ConstructProjectionMatrix(float zNear, float zFar,
+ bool rightHanded) const {
+ float upTan = tan(upDegrees * M_PI / 180.0);
+ float downTan = tan(downDegrees * M_PI / 180.0);
+ float leftTan = tan(leftDegrees * M_PI / 180.0);
+ float rightTan = tan(rightDegrees * M_PI / 180.0);
+
+ float handednessScale = rightHanded ? -1.0 : 1.0;
+
+ float pxscale = 2.0f / (leftTan + rightTan);
+ float pxoffset = (leftTan - rightTan) * pxscale * 0.5;
+ float pyscale = 2.0f / (upTan + downTan);
+ float pyoffset = (upTan - downTan) * pyscale * 0.5;
+
+ Matrix4x4 mobj;
+ float* m = &mobj._11;
+
+ m[0 * 4 + 0] = pxscale;
+ m[2 * 4 + 0] = pxoffset * handednessScale;
+
+ m[1 * 4 + 1] = pyscale;
+ m[2 * 4 + 1] = -pyoffset * handednessScale;
+
+ m[2 * 4 + 2] = zFar / (zNear - zFar) * -handednessScale;
+ m[3 * 4 + 2] = (zFar * zNear) / (zNear - zFar);
+
+ m[2 * 4 + 3] = handednessScale;
+ m[3 * 4 + 3] = 0.0f;
+
+ return mobj;
+}
+
+void VRHMDSensorState::CalcViewMatrices(
+ const gfx::Matrix4x4* aHeadToEyeTransforms) {
+ gfx::Matrix4x4 matHead;
+ if (flags & VRDisplayCapabilityFlags::Cap_Orientation) {
+ matHead.SetRotationFromQuaternion(
+ gfx::Quaternion(-pose.orientation[0], -pose.orientation[1],
+ -pose.orientation[2], pose.orientation[3]));
+ }
+ matHead.PreTranslate(-pose.position[0], -pose.position[1], -pose.position[2]);
+
+ gfx::Matrix4x4 matView =
+ matHead * aHeadToEyeTransforms[VRDisplayState::Eye_Left];
+ matView.Normalize();
+ memcpy(leftViewMatrix, matView.components, sizeof(matView.components));
+ matView = matHead * aHeadToEyeTransforms[VRDisplayState::Eye_Right];
+ matView.Normalize();
+ memcpy(rightViewMatrix, matView.components, sizeof(matView.components));
+}
+
+const IntSize VRDisplayInfo::SuggestedEyeResolution() const {
+ return IntSize(mDisplayState.eyeResolution.width,
+ mDisplayState.eyeResolution.height);
+}
+
+const Point3D VRDisplayInfo::GetEyeTranslation(uint32_t whichEye) const {
+ return Point3D(mDisplayState.eyeTranslation[whichEye].x,
+ mDisplayState.eyeTranslation[whichEye].y,
+ mDisplayState.eyeTranslation[whichEye].z);
+}
+
+const Size VRDisplayInfo::GetStageSize() const {
+ return Size(mDisplayState.stageSize.width, mDisplayState.stageSize.height);
+}
+
+const Matrix4x4 VRDisplayInfo::GetSittingToStandingTransform() const {
+ Matrix4x4 m;
+ // If we could replace Matrix4x4 with a pod type, we could
+ // use it directly from the VRDisplayInfo struct.
+ memcpy(m.components, mDisplayState.sittingToStandingTransform,
+ sizeof(float) * 16);
+ return m;
+}
diff --git a/gfx/vr/gfxVR.h b/gfx/vr/gfxVR.h
new file mode 100644
index 0000000000..d4d772b6a2
--- /dev/null
+++ b/gfx/vr/gfxVR.h
@@ -0,0 +1,162 @@
+/* -*- 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_H
+#define GFX_VR_H
+
+#include "moz_external_vr.h"
+#include "nsTArray.h"
+#include "nsString.h"
+#include "nsCOMPtr.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/EnumeratedArray.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/TypedEnumBits.h"
+#include <type_traits>
+
+namespace mozilla {
+namespace layers {
+class PTextureParent;
+}
+namespace dom {
+enum class GamepadMappingType : uint8_t;
+enum class GamepadHand : uint8_t;
+} // namespace dom
+namespace gfx {
+enum class VRAPIMode : uint8_t { WebXR, WebVR, NumVRAPIModes };
+
+class VRLayerParent;
+class VRDisplayHost;
+class VRManagerPromise;
+
+// The maximum number of frames of latency that we would expect before we
+// should give up applying pose prediction.
+// If latency is greater than one second, then the experience is not likely
+// to be corrected by pose prediction. Setting this value too
+// high may result in unnecessary memory allocation.
+// As the current fastest refresh rate is 90hz, 100 is selected as a
+// conservative value.
+static const int kVRMaxLatencyFrames = 100;
+
+struct VRDisplayInfo {
+ uint32_t mDisplayID;
+ uint32_t mPresentingGroups;
+ uint32_t mGroupMask;
+ uint64_t mFrameId;
+ VRDisplayState mDisplayState;
+ VRControllerState mControllerState[kVRControllerMaxCount];
+
+ VRHMDSensorState mLastSensorState[kVRMaxLatencyFrames];
+ void Clear() { memset(this, 0, sizeof(VRDisplayInfo)); }
+ const VRHMDSensorState& GetSensorState() const {
+ return mLastSensorState[mFrameId % kVRMaxLatencyFrames];
+ }
+
+ uint32_t GetDisplayID() const { return mDisplayID; }
+ const char* GetDisplayName() const { return mDisplayState.displayName; }
+ VRDisplayCapabilityFlags GetCapabilities() const {
+ return mDisplayState.capabilityFlags;
+ }
+
+ const IntSize SuggestedEyeResolution() const;
+ const Point3D GetEyeTranslation(uint32_t whichEye) const;
+ const VRFieldOfView& GetEyeFOV(uint32_t whichEye) const {
+ return mDisplayState.eyeFOV[whichEye];
+ }
+ bool GetIsConnected() const { return mDisplayState.isConnected; }
+ bool GetIsMounted() const { return mDisplayState.isMounted; }
+ uint32_t GetPresentingGroups() const { return mPresentingGroups; }
+ uint32_t GetGroupMask() const { return mGroupMask; }
+ const Size GetStageSize() const;
+ const Matrix4x4 GetSittingToStandingTransform() const;
+ uint64_t GetFrameId() const { return mFrameId; }
+
+ bool operator==(const VRDisplayInfo& other) const {
+ for (size_t i = 0; i < kVRMaxLatencyFrames; i++) {
+ if (mLastSensorState[i] != other.mLastSensorState[i]) {
+ return false;
+ }
+ }
+ // Note that mDisplayState and mControllerState are asserted to be POD
+ // types, so memcmp is safe
+ return mDisplayID == other.mDisplayID &&
+ memcmp(&mDisplayState, &other.mDisplayState,
+ sizeof(VRDisplayState)) == 0 &&
+ memcmp(mControllerState, other.mControllerState,
+ sizeof(VRControllerState) * kVRControllerMaxCount) == 0 &&
+ mPresentingGroups == other.mPresentingGroups &&
+ mGroupMask == other.mGroupMask && mFrameId == other.mFrameId;
+ }
+
+ bool operator!=(const VRDisplayInfo& other) const {
+ return !(*this == other);
+ }
+};
+
+static_assert(std::is_pod<VRDisplayInfo>::value,
+ "VRDisplayInfo must be a POD type.");
+
+struct VRSubmitFrameResultInfo {
+ VRSubmitFrameResultInfo()
+ : mFormat(SurfaceFormat::UNKNOWN), mFrameNum(0), mWidth(0), mHeight(0) {}
+
+ nsCString mBase64Image;
+ SurfaceFormat mFormat;
+ uint64_t mFrameNum;
+ uint32_t mWidth;
+ uint32_t mHeight;
+};
+
+struct VRControllerInfo {
+ uint32_t GetControllerID() const { return mControllerID; }
+ const char* GetControllerName() const {
+ return mControllerState.controllerName;
+ }
+ dom::GamepadMappingType GetMappingType() const { return mMappingType; }
+ uint32_t GetDisplayID() const { return mDisplayID; }
+ dom::GamepadHand GetHand() const { return mControllerState.hand; }
+ uint32_t GetNumButtons() const { return mControllerState.numButtons; }
+ uint32_t GetNumAxes() const { return mControllerState.numAxes; }
+ uint32_t GetNumHaptics() const { return mControllerState.numHaptics; }
+
+ uint32_t mControllerID;
+ dom::GamepadMappingType mMappingType;
+ uint32_t mDisplayID;
+ VRControllerState mControllerState;
+ bool operator==(const VRControllerInfo& other) const {
+ // Note that mControllerState is asserted to be a POD type, so memcmp is
+ // safe
+ return mControllerID == other.mControllerID &&
+ memcmp(&mControllerState, &other.mControllerState,
+ sizeof(VRControllerState)) == 0 &&
+ mMappingType == other.mMappingType && mDisplayID == other.mDisplayID;
+ }
+
+ bool operator!=(const VRControllerInfo& other) const {
+ return !(*this == other);
+ }
+};
+
+struct VRTelemetry {
+ VRTelemetry() : mLastDroppedFrameCount(-1) {}
+
+ void Clear() {
+ mPresentationStart = TimeStamp();
+ mLastDroppedFrameCount = -1;
+ }
+
+ bool IsLastDroppedFrameValid() { return (mLastDroppedFrameCount != -1); }
+
+ TimeStamp mPresentationStart;
+ int32_t mLastDroppedFrameCount;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* GFX_VR_H */
diff --git a/gfx/vr/gfxVRMutex.h b/gfx/vr/gfxVRMutex.h
new file mode 100644
index 0000000000..a54b8dc8b0
--- /dev/null
+++ b/gfx/vr/gfxVRMutex.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_MUTEX_H
+#define GFX_VR_MUTEX_H
+
+namespace mozilla {
+namespace gfx {
+
+#if defined(XP_WIN)
+class WaitForMutex {
+ public:
+ explicit WaitForMutex(HANDLE handle) : mHandle(handle), mStatus(false) {
+ MOZ_ASSERT(mHandle);
+
+ DWORD dwWaitResult;
+ dwWaitResult = WaitForSingleObject(mHandle, // handle to mutex
+ INFINITE); // no time-out interval
+
+ switch (dwWaitResult) {
+ // The thread got ownership of the mutex
+ case WAIT_OBJECT_0:
+ mStatus = true;
+ break;
+
+ // The thread got ownership of an abandoned mutex
+ // The shmem is in an indeterminate state
+ case WAIT_ABANDONED:
+ mStatus = false;
+ break;
+ default:
+ mStatus = false;
+ break;
+ }
+ }
+
+ ~WaitForMutex() {
+ if (mHandle && !ReleaseMutex(mHandle)) {
+# ifdef MOZILLA_INTERNAL_API
+ nsAutoCString msg;
+ msg.AppendPrintf("WaitForMutex %d ReleaseMutex error \"%lu\".", mHandle,
+ GetLastError());
+ NS_WARNING(msg.get());
+# endif
+ MOZ_ASSERT(false, "Failed to release mutex.");
+ }
+ }
+
+ bool GetStatus() { return mStatus; }
+
+ private:
+ HANDLE mHandle;
+ bool mStatus;
+};
+#endif
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* GFX_VR_MUTEX_H */
diff --git a/gfx/vr/ipc/PVR.ipdl b/gfx/vr/ipc/PVR.ipdl
new file mode 100644
index 0000000000..6633ae957b
--- /dev/null
+++ b/gfx/vr/ipc/PVR.ipdl
@@ -0,0 +1,46 @@
+/* -*- 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 GraphicsMessages;
+include MemoryReportTypes;
+include PrefsTypes;
+include protocol PVRGPU;
+
+namespace mozilla {
+namespace gfx {
+
+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..1e0f2d9fab
--- /dev/null
+++ b/gfx/vr/ipc/PVRGPU.ipdl
@@ -0,0 +1,24 @@
+/* -*- 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/. */
+
+namespace mozilla {
+namespace gfx {
+
+// The parent process is the VR process.
+// The child process is the GPU process.
+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..5c73ccff2c
--- /dev/null
+++ b/gfx/vr/ipc/PVRLayer.ipdl
@@ -0,0 +1,31 @@
+/* -*- 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;
+
+using mozilla::gfx::Rect from "mozilla/gfx/Rect.h";
+
+namespace mozilla {
+namespace gfx {
+
+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..1b97c38170
--- /dev/null
+++ b/gfx/vr/ipc/PVRManager.ipdl
@@ -0,0 +1,84 @@
+/* -*- 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";
+
+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.
+ */
+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..d9e21ddc47
--- /dev/null
+++ b/gfx/vr/ipc/VRChild.cpp
@@ -0,0 +1,214 @@
+/* -*- 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.Put(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 (auto iter = mManifest.Iter(); !iter.Done(); iter.Next()) {
+ nsCString path(iter.Data());
+ if (!path.IsEmpty() && remove(path.BeginReading()) != 0) {
+ MOZ_ASSERT(false, "Delete controller manifest file failed.");
+ }
+ }
+ mManifest.Clear();
+ }
+
+ nsCString mAction;
+ nsDataHashtable<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.advancedLayers() = gfxConfig::GetValue(Feature::ADVANCED_LAYERS);
+ 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..f8b90a5b44
--- /dev/null
+++ b/gfx/vr/ipc/VRGPUChild.cpp
@@ -0,0 +1,81 @@
+/* -*- 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 "VRManager.h"
+
+namespace mozilla {
+namespace gfx {
+
+static StaticRefPtr<VRGPUChild> sVRGPUChildSingleton;
+
+/* static */
+bool VRGPUChild::InitForGPUProcess(Endpoint<PVRGPUChild>&& aEndpoint) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(!sVRGPUChildSingleton);
+
+ 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..ce1eef77fa
--- /dev/null
+++ b/gfx/vr/ipc/VRGPUParent.cpp
@@ -0,0 +1,112 @@
+/* -*- 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"
+
+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) {
+ 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..fd9dd19988
--- /dev/null
+++ b/gfx/vr/ipc/VRLayerChild.cpp
@@ -0,0 +1,146 @@
+/* -*- 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_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() {
+ 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..e7a8b4bf41
--- /dev/null
+++ b/gfx/vr/ipc/VRLayerParent.cpp
@@ -0,0 +1,54 @@
+/* -*- 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), mVRDisplayID(aVRDisplayID), mGroup(aGroup) {}
+
+VRLayerParent::~VRLayerParent() { MOZ_COUNT_DTOR(VRLayerParent); }
+
+mozilla::ipc::IPCResult VRLayerParent::RecvDestroy() {
+ Destroy();
+ return IPC_OK();
+}
+
+void VRLayerParent::ActorDestroy(ActorDestroyReason aWhy) { mIPCOpen = false; }
+
+void VRLayerParent::Destroy() {
+ if (mVRDisplayID) {
+ VRManager* vm = VRManager::Get();
+ vm->RemoveLayer(this);
+ // 0 will never be a valid VRDisplayID; we can use it to indicate that
+ // we are destroyed and no longer associated with a display.
+ mVRDisplayID = 0;
+ }
+
+ 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 (mVRDisplayID) {
+ 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..9fc2f32c0d
--- /dev/null
+++ b/gfx/vr/ipc/VRLayerParent.h
@@ -0,0 +1,46 @@
+/* -*- 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 GetDisplayID() const { return mVRDisplayID; }
+ uint32_t GetGroup() const { return mGroup; }
+
+ protected:
+ virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ virtual ~VRLayerParent();
+ void Destroy();
+
+ bool mIPCOpen;
+
+ uint32_t mVRDisplayID;
+ 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..beb8b5c988
--- /dev/null
+++ b/gfx/vr/ipc/VRManagerChild.cpp
@@ -0,0 +1,637 @@
+/* -*- 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->GetIPCChannel(),
+ 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,
+ nsISerialEventTarget* aTarget,
+ uint32_t aGroup) {
+ PVRLayerChild* vrLayerChild = AllocPVRLayerChild(aDisplayID, aGroup);
+ // Do the DOM labeling.
+ if (aTarget) {
+ SetEventTargetForActor(vrLayerChild, aTarget);
+ MOZ_ASSERT(vrLayerChild->GetActorEventTarget());
+ }
+ 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.Get(aID, nullptr));
+ mGamepadPromiseList.Put(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..b650d80cec
--- /dev/null
+++ b/gfx/vr/ipc/VRManagerChild.h
@@ -0,0 +1,200 @@
+/* -*- 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/WindowBinding.h" // For FrameRequestCallback
+#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,
+ nsISerialEventTarget* aTarget, 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..3f0140596f
--- /dev/null
+++ b/gfx/vr/ipc/VRManagerParent.cpp
@@ -0,0 +1,299 @@
+/* -*- 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/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) {
+ 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::ActorDealloc() {
+ UnregisterFromManager();
+ mCompositorThreadHolder = nullptr;
+ mSelfRef = nullptr;
+}
+
+void VRManagerParent::OnChannelConnected(int32_t aPid) {
+ mCompositorThreadHolder = CompositorThreadHolder::GetSingleton();
+}
+
+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..fee9569b78
--- /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;
+ void OnChannelConnected(int32_t pid) 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 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..df18456b51
--- /dev/null
+++ b/gfx/vr/ipc/VRMessageUtils.h
@@ -0,0 +1,74 @@
+/* -*- 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(Message* aMsg, const paramType& aParam) {
+ WriteParam(aMsg, aParam.mBase64Image);
+ WriteParam(aMsg, aParam.mFormat);
+ WriteParam(aMsg, aParam.mWidth);
+ WriteParam(aMsg, aParam.mHeight);
+ WriteParam(aMsg, aParam.mFrameNum);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult) {
+ if (!ReadParam(aMsg, aIter, &(aResult->mBase64Image)) ||
+ !ReadParam(aMsg, aIter, &(aResult->mFormat)) ||
+ !ReadParam(aMsg, aIter, &(aResult->mWidth)) ||
+ !ReadParam(aMsg, aIter, &(aResult->mHeight)) ||
+ !ReadParam(aMsg, aIter, &(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..21c1cde5de
--- /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 "ProcessUtils.h"
+
+#include "mozilla/dom/MemoryReportRequest.h"
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/ipc/CrashReporterClient.h"
+#include "mozilla/ipc/ProcessChild.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::ADVANCED_LAYERS, devicePrefs.advancedLayers());
+ 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.Put(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.
+ 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(base::ProcessId aParentPid, const char* aParentBuildID,
+ MessageLoop* aIOLoop, UniquePtr<IPC::Channel> aChannel) {
+ // 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(!Open(std::move(aChannel), aParentPid, aIOLoop))) {
+ 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..ab44ce0072
--- /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(base::ProcessId aParentPid, const char* aParentBuildID,
+ MessageLoop* aIOLoop, UniquePtr<IPC::Channel> aChannel);
+ 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;
+ nsDataHashtable<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..da1a7c3a87
--- /dev/null
+++ b/gfx/vr/ipc/VRProcessChild.cpp
@@ -0,0 +1,84 @@
+/* -*- 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/ipc/IOThreadChild.h"
+#include "ProcessUtils.h"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+using mozilla::ipc::IOThreadChild;
+
+StaticRefPtr<VRParent> sVRParent;
+
+VRProcessChild::VRProcessChild(ProcessId aParentPid)
+ : ProcessChild(aParentPid) {}
+
+VRProcessChild::~VRProcessChild() { sVRParent = nullptr; }
+
+/*static*/
+VRParent* VRProcessChild::GetVRParent() {
+ MOZ_ASSERT(sVRParent);
+ return sVRParent;
+}
+
+bool VRProcessChild::Init(int aArgc, char* aArgv[]) {
+ char* parentBuildID = nullptr;
+ char* prefsHandle = nullptr;
+ char* prefMapHandle = nullptr;
+ char* prefsLen = nullptr;
+ char* prefMapSize = nullptr;
+ for (int i = 1; i < aArgc; i++) {
+ if (!aArgv[i]) {
+ continue;
+ }
+ if (strcmp(aArgv[i], "-parentBuildID") == 0) {
+ parentBuildID = aArgv[i + 1];
+
+#ifdef XP_WIN
+ } else if (strcmp(aArgv[i], "-prefsHandle") == 0) {
+ if (++i == aArgc) {
+ return false;
+ }
+ prefsHandle = aArgv[i];
+ } else if (strcmp(aArgv[i], "-prefMapHandle") == 0) {
+ if (++i == aArgc) {
+ return false;
+ }
+ prefMapHandle = aArgv[i];
+#endif
+ } else if (strcmp(aArgv[i], "-prefsLen") == 0) {
+ if (++i == aArgc) {
+ return false;
+ }
+ prefsLen = aArgv[i];
+ } else if (strcmp(aArgv[i], "-prefMapSize") == 0) {
+ if (++i == aArgc) {
+ return false;
+ }
+ prefMapSize = aArgv[i];
+ }
+ }
+
+ ipc::SharedPreferenceDeserializer deserializer;
+ if (!deserializer.DeserializeFromSharedMemory(prefsHandle, prefMapHandle,
+ prefsLen, prefMapSize)) {
+ return false;
+ }
+
+ sVRParent = new VRParent();
+ sVRParent->Init(ParentPid(), parentBuildID, IOThreadChild::message_loop(),
+ IOThreadChild::TakeChannel());
+
+ 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..69d13a37e5
--- /dev/null
+++ b/gfx/vr/ipc/VRProcessChild.h
@@ -0,0 +1,42 @@
+/* -*- 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:
+ explicit VRProcessChild(ProcessId aParentPid);
+ ~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;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(VRProcessChild);
+};
+
+} // 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..082c8643fd
--- /dev/null
+++ b/gfx/vr/ipc/VRProcessManager.cpp
@@ -0,0 +1,289 @@
+/* -*- 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) {
+ // A pref changed. If it is useful to do so, inform child processes.
+ if (!dom::ContentParent::ShouldSyncPreference(aData)) {
+ return;
+ }
+
+ // We know prefs are ASCII here.
+ NS_LossyConvertUTF16toASCII strData(aData);
+
+ mozilla::dom::Pref pref(strData, /* isLocked */ false, Nothing(), Nothing());
+ Preferences::GetPreference(&pref);
+ 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..46c7bd37ab
--- /dev/null
+++ b/gfx/vr/ipc/VRProcessParent.cpp
@@ -0,0 +1,243 @@
+/* -*- 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/MemoryReportRequest.h"
+#include "mozilla/gfx/GPUProcessManager.h"
+#include "mozilla/gfx/GPUChild.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/ipc/ProtocolTypes.h"
+#include "mozilla/ipc/ProtocolUtils.h" // for IToplevelProtocol
+#include "mozilla/StaticPrefs_dom.h"
+#include "mozilla/TimeStamp.h" // for TimeStamp
+#include "mozilla/Unused.h"
+#include "ProcessUtils.h"
+#include "VRChild.h"
+#include "VRThread.h"
+
+#include "nsAppRunner.h" // for IToplevelProtocol
+#include "mozilla/ipc/ProtocolUtils.h"
+
+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() {
+ // Cancel all tasks. We don't want anything triggering after our caller
+ // expects this to go away.
+ {
+ MonitorAutoLock lock(mMonitor);
+ mTaskFactory.RevokeAll();
+ }
+ 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;
+ nsCString parentBuildID(mozilla::PlatformBuildID());
+ extraArgs.push_back("-parentBuildID");
+ extraArgs.push_back(parentBuildID.get());
+
+ mPrefSerializer = MakeUnique<ipc::SharedPreferenceSerializer>();
+ if (!mPrefSerializer->SerializeToSharedMemory()) {
+ 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) {
+ 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;
+ }
+
+ mVRChild = MakeUnique<VRChild>(this);
+
+ DebugOnly<bool> rv =
+ mVRChild->Open(TakeChannel(), base::GetProcId(GetChildProcessHandle()));
+ 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, false)) {
+ NS_WARNING("failed to kill subprocess!");
+ }
+
+ SetAlreadyDead();
+}
+
+void VRProcessParent::OnChannelError() {
+ MOZ_ASSERT(false, "VR process channel error.");
+}
+
+void VRProcessParent::OnChannelConnected(int32_t 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..efe1708af0
--- /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(int32_t 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
diff --git a/gfx/vr/moz.build b/gfx/vr/moz.build
new file mode 100644
index 0000000000..6f24beb9ba
--- /dev/null
+++ b/gfx/vr/moz.build
@@ -0,0 +1,112 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+EXPORTS += [
+ "external_api/moz_external_vr.h",
+ "FxROutputHandler.h",
+ "FxRWindowManager.h",
+ "gfxVR.h",
+ "ipc/VRChild.h",
+ "ipc/VRGPUChild.h",
+ "ipc/VRGPUParent.h",
+ "ipc/VRLayerChild.h",
+ "ipc/VRManagerChild.h",
+ "ipc/VRManagerParent.h",
+ "ipc/VRMessageUtils.h",
+ "ipc/VRParent.h",
+ "ipc/VRProcessChild.h",
+ "ipc/VRProcessManager.h",
+ "ipc/VRProcessParent.h",
+ "service/VRService.h",
+ "VRDisplayClient.h",
+ "VRDisplayPresentation.h",
+ "VRManager.h",
+ "VRPuppetCommandBuffer.h",
+ "VRShMem.h",
+ "VRThread.h",
+]
+
+LOCAL_INCLUDES += [
+ "/dom/base",
+ "/dom/canvas",
+ "/gfx/layers/d3d11",
+ "/gfx/thebes",
+]
+
+UNIFIED_SOURCES += [
+ "gfxVR.cpp",
+ "ipc/VRChild.cpp",
+ "ipc/VRGPUChild.cpp",
+ "ipc/VRGPUParent.cpp",
+ "ipc/VRManagerChild.cpp",
+ "ipc/VRManagerParent.cpp",
+ "ipc/VRParent.cpp",
+ "ipc/VRProcessChild.cpp",
+ "ipc/VRProcessManager.cpp",
+ "ipc/VRProcessParent.cpp",
+ "VRDisplayClient.cpp",
+ "VRDisplayPresentation.cpp",
+ "VRThread.cpp",
+]
+
+SOURCES += [
+ "ipc/VRLayerChild.cpp",
+ "ipc/VRLayerParent.cpp",
+ "VRManager.cpp",
+ "VRPuppetCommandBuffer.cpp",
+ "VRShMem.cpp",
+]
+
+if CONFIG["OS_TARGET"] == "Android":
+ LOCAL_INCLUDES += ["/widget/android"]
+else:
+ DIRS += [
+ "service",
+ ]
+ UNIFIED_SOURCES += [
+ "VRServiceHost.cpp",
+ ]
+
+# Only target x64 for vrhost since WebVR is only supported on 64bit.
+# Also, only use MSVC compiler for Windows-specific linking
+if (
+ CONFIG["OS_ARCH"] == "WINNT"
+ and CONFIG["HAVE_64BIT_BUILD"]
+ and CONFIG["CC_TYPE"] not in ("clang", "gcc")
+):
+ DIRS += ["vrhost"]
+
+IPDL_SOURCES = [
+ "ipc/PVR.ipdl",
+ "ipc/PVRGPU.ipdl",
+ "ipc/PVRLayer.ipdl",
+ "ipc/PVRManager.ipdl",
+]
+
+# For now, only enable FxR CLH for Windows Nightly builds (BUG 1565349)
+if CONFIG["OS_ARCH"] == "WINNT" and CONFIG["NIGHTLY_BUILD"]:
+ XPCOM_MANIFESTS += [
+ "components.conf",
+ ]
+ SOURCES += [
+ "nsFxrCommandLineHandler.cpp",
+ ]
+
+if CONFIG["OS_ARCH"] == "WINNT":
+ LOCAL_INCLUDES += ["/layout/generic", "/widget", "/widget/windows"]
+ SOURCES += ["FxROutputHandler.cpp", "FxRWindowManager.cpp"]
+
+CXXFLAGS += CONFIG["MOZ_CAIRO_CFLAGS"]
+CXXFLAGS += CONFIG["TK_CFLAGS"]
+CFLAGS += CONFIG["MOZ_CAIRO_CFLAGS"]
+CFLAGS += CONFIG["TK_CFLAGS"]
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+FINAL_LIBRARY = "xul"
+
+with Files("**"):
+ BUG_COMPONENT = ("Core", "WebVR")
diff --git a/gfx/vr/nsFxrCommandLineHandler.cpp b/gfx/vr/nsFxrCommandLineHandler.cpp
new file mode 100644
index 0000000000..4c7b930e5c
--- /dev/null
+++ b/gfx/vr/nsFxrCommandLineHandler.cpp
@@ -0,0 +1,139 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+// vim:cindent:tabstop=4:expandtab:shiftwidth=4:
+/* 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 XP_WIN
+# error "nsFxrCommandLineHandler currently only supported on Windows"
+#endif
+
+#include "nsFxrCommandLineHandler.h"
+#include "FxRWindowManager.h"
+
+#include "nsICommandLine.h"
+#include "nsIWindowWatcher.h"
+#include "mozIDOMWindow.h"
+#include "nsPIDOMWindow.h"
+#include "mozilla/WidgetUtils.h"
+#include "nsIWidget.h"
+#include "nsServiceManagerUtils.h"
+#include "nsString.h"
+#include "nsArray.h"
+#include "nsCOMPtr.h"
+#include "mozilla/StaticPrefs_extensions.h"
+
+#include "windows.h"
+#include "WinUtils.h"
+
+#include "VRShMem.h"
+
+NS_IMPL_ISUPPORTS(nsFxrCommandLineHandler, nsICommandLineHandler)
+
+// nsFxrCommandLineHandler acts in the middle of bootstrapping Firefox
+// Reality with desktop Firefox. Details of the processes involved are
+// described below:
+//
+// Host
+// (vrhost!CreateVRWindow) Fx Main Fx GPU
+// | + +
+// VRShMem creates shared + +
+// memory in OS + +
+// | + +
+// Launch firefox.exe + +
+// with --fxr + +
+// | | +
+// Wait for Signal... nsFxrCLH handles param +
+// | joins VRShMem +
+// | creates new window |
+// | sets .hwndFx in VRShMem |
+// | | |
+// | | After compositor and
+// | | swapchain created,
+// | | share texture handle to
+// | | VRShMem and set signal
+// CreateVRWindow returns | |
+// to host with relevant | |
+// return data from VRShMem | |
+// | Fx continues to run |
+// | | Fx continues to render
+// | | |
+// ... ... ...
+
+NS_IMETHODIMP
+nsFxrCommandLineHandler::Handle(nsICommandLine* aCmdLine) {
+ bool handleFlagRetVal = false;
+ nsresult result = aCmdLine->HandleFlag(u"fxr"_ns, false, &handleFlagRetVal);
+ if (result == NS_OK && handleFlagRetVal) {
+ if (XRE_IsParentProcess() && !XRE_IsE10sParentProcess()) {
+ MOZ_CRASH("--fxr not supported without e10s");
+ }
+
+ MOZ_ASSERT(mozilla::StaticPrefs::extensions_webextensions_remote(),
+ "Remote extensions are the only supported configuration on "
+ "desktop platforms");
+
+ aCmdLine->SetPreventDefault(true);
+
+ nsCOMPtr<nsIWindowWatcher> wwatch =
+ do_GetService(NS_WINDOWWATCHER_CONTRACTID);
+ NS_ENSURE_TRUE(wwatch, NS_ERROR_FAILURE);
+
+ nsCOMPtr<mozIDOMWindowProxy> newWindow;
+ result = wwatch->OpenWindow(
+ nullptr, // aParent
+ "chrome://fxr/content/fxrui.html"_ns, // aUrl
+ "_blank"_ns, // aName
+ "chrome,dialog=no,all,private,alwaysontop"_ns, // aFeatures
+ nullptr, // aArguments
+ getter_AddRefs(newWindow));
+
+ MOZ_ASSERT(result == NS_OK);
+
+ nsPIDOMWindowOuter* newWindowOuter = nsPIDOMWindowOuter::From(newWindow);
+ FxRWindowManager::GetInstance()->AddWindow(newWindowOuter);
+
+ // Set ForceFullScreenInWidget so that full-screen (in an FxR window)
+ // fills only the window and thus the same texture that will already be
+ // shared with the host. Also, this is set here per-window because
+ // changing the related pref would impact all browser window instances.
+ newWindowOuter->ForceFullScreenInWidget();
+
+ // Send the window's HWND to vrhost through VRShMem
+ mozilla::gfx::VRShMem shmem(nullptr, true /*aRequiresMutex*/);
+ if (shmem.JoinShMem()) {
+ mozilla::gfx::VRWindowState windowState = {0};
+ shmem.PullWindowState(windowState);
+
+ nsCOMPtr<nsIWidget> newWidget =
+ mozilla::widget::WidgetUtils::DOMWindowToWidget(newWindowOuter);
+ HWND hwndWidget = (HWND)newWidget->GetNativeData(NS_NATIVE_WINDOW);
+
+ // The CLH should populate these members first
+ MOZ_ASSERT(windowState.hwndFx == 0);
+ MOZ_ASSERT(windowState.textureFx == nullptr);
+ windowState.hwndFx = (uint64_t)hwndWidget;
+
+ shmem.PushWindowState(windowState);
+ shmem.LeaveShMem();
+
+ // The GPU process will notify the host that window creation is complete
+ // after output data is set in VRShMem
+ newWidget->RequestFxrOutput();
+ } else {
+#ifndef NIGHTLY_BUILD
+ MOZ_CRASH("failed to start with --fxr");
+#endif
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFxrCommandLineHandler::GetHelpInfo(nsACString& aResult) {
+ aResult.AssignLiteral(
+ " --fxr Creates a new window for Firefox Reality on Desktop when "
+ "available\n");
+ return NS_OK;
+}
diff --git a/gfx/vr/nsFxrCommandLineHandler.h b/gfx/vr/nsFxrCommandLineHandler.h
new file mode 100644
index 0000000000..e9a2a2b489
--- /dev/null
+++ b/gfx/vr/nsFxrCommandLineHandler.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+// vim:cindent:tabstop=4:expandtab:shiftwidth=4:
+/* 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_nsFxrCommandLineHandler_h_
+#define GFX_VR_nsFxrCommandLineHandler_h_
+
+#include "nsICommandLineHandler.h"
+
+// 5baca10a-4d53-4335-b24d-c69696640a9a
+#define NS_FXRCOMMANDLINEHANDLER_CID \
+ { \
+ 0x5baca10a, 0x4d53, 0x4335, { \
+ 0xb2, 0x4d, 0xc6, 0x96, 0x96, 0x64, 0x0a, 0x9a \
+ } \
+ }
+
+// nsFxrCommandLineHandler is responsible for handling parameters used to
+// bootstrap Firefox Reality running on desktop-class machines.
+class nsFxrCommandLineHandler : public nsICommandLineHandler {
+ public:
+ nsFxrCommandLineHandler() = default;
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSICOMMANDLINEHANDLER
+
+ protected:
+ virtual ~nsFxrCommandLineHandler() = default;
+};
+
+#endif /* !defined(GFX_VR_nsFxrCommandLineHandler_h_) */
diff --git a/gfx/vr/service/OSVRSession.cpp b/gfx/vr/service/OSVRSession.cpp
new file mode 100644
index 0000000000..486cb03286
--- /dev/null
+++ b/gfx/vr/service/OSVRSession.cpp
@@ -0,0 +1,511 @@
+/* -*- 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 "OSVRSession.h"
+#include "prenv.h"
+#include "nsString.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/StaticPrefs_dom.h"
+#include "mozilla/SharedLibrary.h"
+#include "mozilla/gfx/Quaternion.h"
+
+#if defined(XP_WIN)
+# include <d3d11.h>
+# include "mozilla/gfx/DeviceManagerDx.h"
+#endif // defined(XP_WIN)
+
+#ifndef M_PI
+# define M_PI 3.14159265358979323846
+#endif
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+
+namespace {
+// need to typedef functions that will be used in the code below
+extern "C" {
+typedef OSVR_ClientContext (*pfn_osvrClientInit)(
+ const char applicationIdentifier[], uint32_t flags);
+typedef OSVR_ReturnCode (*pfn_osvrClientShutdown)(OSVR_ClientContext ctx);
+typedef OSVR_ReturnCode (*pfn_osvrClientUpdate)(OSVR_ClientContext ctx);
+typedef OSVR_ReturnCode (*pfn_osvrClientCheckStatus)(OSVR_ClientContext ctx);
+typedef OSVR_ReturnCode (*pfn_osvrClientGetInterface)(
+ OSVR_ClientContext ctx, const char path[], OSVR_ClientInterface* iface);
+typedef OSVR_ReturnCode (*pfn_osvrClientFreeInterface)(
+ OSVR_ClientContext ctx, OSVR_ClientInterface iface);
+typedef OSVR_ReturnCode (*pfn_osvrGetOrientationState)(
+ OSVR_ClientInterface iface, OSVR_TimeValue* timestamp,
+ OSVR_OrientationState* state);
+typedef OSVR_ReturnCode (*pfn_osvrGetPositionState)(OSVR_ClientInterface iface,
+ OSVR_TimeValue* timestamp,
+ OSVR_PositionState* state);
+typedef OSVR_ReturnCode (*pfn_osvrClientGetDisplay)(OSVR_ClientContext ctx,
+ OSVR_DisplayConfig* disp);
+typedef OSVR_ReturnCode (*pfn_osvrClientFreeDisplay)(OSVR_DisplayConfig disp);
+typedef OSVR_ReturnCode (*pfn_osvrClientGetNumEyesForViewer)(
+ OSVR_DisplayConfig disp, OSVR_ViewerCount viewer, OSVR_EyeCount* eyes);
+typedef OSVR_ReturnCode (*pfn_osvrClientGetViewerEyePose)(
+ OSVR_DisplayConfig disp, OSVR_ViewerCount viewer, OSVR_EyeCount eye,
+ OSVR_Pose3* pose);
+typedef OSVR_ReturnCode (*pfn_osvrClientGetDisplayDimensions)(
+ OSVR_DisplayConfig disp, OSVR_DisplayInputCount displayInputIndex,
+ OSVR_DisplayDimension* width, OSVR_DisplayDimension* height);
+typedef OSVR_ReturnCode (
+ *pfn_osvrClientGetViewerEyeSurfaceProjectionClippingPlanes)(
+ OSVR_DisplayConfig disp, OSVR_ViewerCount viewer, OSVR_EyeCount eye,
+ OSVR_SurfaceCount surface, double* left, double* right, double* bottom,
+ double* top);
+typedef OSVR_ReturnCode (*pfn_osvrClientGetRelativeViewportForViewerEyeSurface)(
+ OSVR_DisplayConfig disp, OSVR_ViewerCount viewer, OSVR_EyeCount eye,
+ OSVR_SurfaceCount surface, OSVR_ViewportDimension* left,
+ OSVR_ViewportDimension* bottom, OSVR_ViewportDimension* width,
+ OSVR_ViewportDimension* height);
+typedef OSVR_ReturnCode (*pfn_osvrClientGetViewerEyeSurfaceProjectionMatrixf)(
+ OSVR_DisplayConfig disp, OSVR_ViewerCount viewer, OSVR_EyeCount eye,
+ OSVR_SurfaceCount surface, float near, float far,
+ OSVR_MatrixConventions flags, float* matrix);
+typedef OSVR_ReturnCode (*pfn_osvrClientCheckDisplayStartup)(
+ OSVR_DisplayConfig disp);
+typedef OSVR_ReturnCode (*pfn_osvrClientSetRoomRotationUsingHead)(
+ OSVR_ClientContext ctx);
+}
+
+static pfn_osvrClientInit osvr_ClientInit = nullptr;
+static pfn_osvrClientShutdown osvr_ClientShutdown = nullptr;
+static pfn_osvrClientUpdate osvr_ClientUpdate = nullptr;
+static pfn_osvrClientCheckStatus osvr_ClientCheckStatus = nullptr;
+static pfn_osvrClientGetInterface osvr_ClientGetInterface = nullptr;
+static pfn_osvrClientFreeInterface osvr_ClientFreeInterface = nullptr;
+static pfn_osvrGetOrientationState osvr_GetOrientationState = nullptr;
+static pfn_osvrGetPositionState osvr_GetPositionState = nullptr;
+static pfn_osvrClientGetDisplay osvr_ClientGetDisplay = nullptr;
+static pfn_osvrClientFreeDisplay osvr_ClientFreeDisplay = nullptr;
+static pfn_osvrClientGetNumEyesForViewer osvr_ClientGetNumEyesForViewer =
+ nullptr;
+static pfn_osvrClientGetViewerEyePose osvr_ClientGetViewerEyePose = nullptr;
+static pfn_osvrClientGetDisplayDimensions osvr_ClientGetDisplayDimensions =
+ nullptr;
+static pfn_osvrClientGetViewerEyeSurfaceProjectionClippingPlanes
+ osvr_ClientGetViewerEyeSurfaceProjectionClippingPlanes = nullptr;
+static pfn_osvrClientGetRelativeViewportForViewerEyeSurface
+ osvr_ClientGetRelativeViewportForViewerEyeSurface = nullptr;
+static pfn_osvrClientGetViewerEyeSurfaceProjectionMatrixf
+ osvr_ClientGetViewerEyeSurfaceProjectionMatrixf = nullptr;
+static pfn_osvrClientCheckDisplayStartup osvr_ClientCheckDisplayStartup =
+ nullptr;
+static pfn_osvrClientSetRoomRotationUsingHead
+ osvr_ClientSetRoomRotationUsingHead = nullptr;
+
+bool LoadOSVRRuntime() {
+ static PRLibrary* osvrUtilLib = nullptr;
+ static PRLibrary* osvrCommonLib = nullptr;
+ static PRLibrary* osvrClientLib = nullptr;
+ static PRLibrary* osvrClientKitLib = nullptr;
+ // this looks up the path in the about:config setting, from greprefs.js or
+ // modules\libpref\init\all.js we need all the libs to be valid
+#ifdef XP_WIN
+ constexpr static auto* pfnGetPathStringPref = mozilla::Preferences::GetString;
+ nsAutoString osvrUtilPath, osvrCommonPath, osvrClientPath, osvrClientKitPath;
+#else
+ constexpr static auto* pfnGetPathStringPref =
+ mozilla::Preferences::GetCString;
+ nsAutoCString osvrUtilPath, osvrCommonPath, osvrClientPath, osvrClientKitPath;
+#endif
+ if (NS_FAILED(pfnGetPathStringPref("gfx.vr.osvr.utilLibPath", osvrUtilPath,
+ PrefValueKind::User)) ||
+ NS_FAILED(pfnGetPathStringPref("gfx.vr.osvr.commonLibPath",
+ osvrCommonPath, PrefValueKind::User)) ||
+ NS_FAILED(pfnGetPathStringPref("gfx.vr.osvr.clientLibPath",
+ osvrClientPath, PrefValueKind::User)) ||
+ NS_FAILED(pfnGetPathStringPref("gfx.vr.osvr.clientKitLibPath",
+ osvrClientKitPath, PrefValueKind::User))) {
+ return false;
+ }
+
+ osvrUtilLib = LoadLibraryWithFlags(osvrUtilPath.get());
+ osvrCommonLib = LoadLibraryWithFlags(osvrCommonPath.get());
+ osvrClientLib = LoadLibraryWithFlags(osvrClientPath.get());
+ osvrClientKitLib = LoadLibraryWithFlags(osvrClientKitPath.get());
+
+ if (!osvrUtilLib) {
+ printf_stderr("[OSVR] Failed to load OSVR Util library!\n");
+ return false;
+ }
+ if (!osvrCommonLib) {
+ printf_stderr("[OSVR] Failed to load OSVR Common library!\n");
+ return false;
+ }
+ if (!osvrClientLib) {
+ printf_stderr("[OSVR] Failed to load OSVR Client library!\n");
+ return false;
+ }
+ if (!osvrClientKitLib) {
+ printf_stderr("[OSVR] Failed to load OSVR ClientKit library!\n");
+ return false;
+ }
+
+// make sure all functions that we'll be using are available
+#define REQUIRE_FUNCTION(_x) \
+ do { \
+ *(void**)&osvr_##_x = (void*)PR_FindSymbol(osvrClientKitLib, "osvr" #_x); \
+ if (!osvr_##_x) { \
+ printf_stderr("osvr" #_x " symbol missing\n"); \
+ goto fail; \
+ } \
+ } while (0)
+
+ REQUIRE_FUNCTION(ClientInit);
+ REQUIRE_FUNCTION(ClientShutdown);
+ REQUIRE_FUNCTION(ClientUpdate);
+ REQUIRE_FUNCTION(ClientCheckStatus);
+ REQUIRE_FUNCTION(ClientGetInterface);
+ REQUIRE_FUNCTION(ClientFreeInterface);
+ REQUIRE_FUNCTION(GetOrientationState);
+ REQUIRE_FUNCTION(GetPositionState);
+ REQUIRE_FUNCTION(ClientGetDisplay);
+ REQUIRE_FUNCTION(ClientFreeDisplay);
+ REQUIRE_FUNCTION(ClientGetNumEyesForViewer);
+ REQUIRE_FUNCTION(ClientGetViewerEyePose);
+ REQUIRE_FUNCTION(ClientGetDisplayDimensions);
+ REQUIRE_FUNCTION(ClientGetViewerEyeSurfaceProjectionClippingPlanes);
+ REQUIRE_FUNCTION(ClientGetRelativeViewportForViewerEyeSurface);
+ REQUIRE_FUNCTION(ClientGetViewerEyeSurfaceProjectionMatrixf);
+ REQUIRE_FUNCTION(ClientCheckDisplayStartup);
+ REQUIRE_FUNCTION(ClientSetRoomRotationUsingHead);
+
+#undef REQUIRE_FUNCTION
+
+ return true;
+
+fail:
+ return false;
+}
+
+} // namespace
+
+mozilla::gfx::VRFieldOfView SetFromTanRadians(double left, double right,
+ double bottom, double top) {
+ mozilla::gfx::VRFieldOfView fovInfo;
+ fovInfo.leftDegrees = atan(left) * 180.0 / M_PI;
+ fovInfo.rightDegrees = atan(right) * 180.0 / M_PI;
+ fovInfo.upDegrees = atan(top) * 180.0 / M_PI;
+ fovInfo.downDegrees = atan(bottom) * 180.0 / M_PI;
+ return fovInfo;
+}
+
+OSVRSession::OSVRSession()
+ : VRSession(),
+ mRuntimeLoaded(false),
+ mOSVRInitialized(false),
+ mClientContextInitialized(false),
+ mDisplayConfigInitialized(false),
+ mInterfaceInitialized(false),
+ m_ctx(nullptr),
+ m_iface(nullptr),
+ m_display(nullptr) {}
+OSVRSession::~OSVRSession() { Shutdown(); }
+
+bool OSVRSession::Initialize(mozilla::gfx::VRSystemState& aSystemState,
+ bool aDetectRuntimesOnly) {
+ if (StaticPrefs::dom_vr_puppet_enabled()) {
+ // Ensure that tests using the VR Puppet do not find real hardware
+ return false;
+ }
+ if (!StaticPrefs::dom_vr_enabled() || !StaticPrefs::dom_vr_osvr_enabled()) {
+ return false;
+ }
+ if (mOSVRInitialized) {
+ return true;
+ }
+ if (!LoadOSVRRuntime()) {
+ return false;
+ }
+ mRuntimeLoaded = true;
+
+ if (aDetectRuntimesOnly) {
+ aSystemState.displayState.capabilityFlags |=
+ VRDisplayCapabilityFlags::Cap_ImmersiveVR;
+ return false;
+ }
+
+ // initialize client context
+ InitializeClientContext();
+ // try to initialize interface
+ InitializeInterface();
+ // try to initialize display object
+ InitializeDisplay();
+ // verify all components are initialized
+ CheckOSVRStatus();
+
+ if (!mOSVRInitialized) {
+ return false;
+ }
+
+ if (!InitState(aSystemState)) {
+ return false;
+ }
+
+ return true;
+}
+
+void OSVRSession::CheckOSVRStatus() {
+ if (mOSVRInitialized) {
+ return;
+ }
+
+ // client context must be initialized first
+ InitializeClientContext();
+
+ // update client context
+ osvr_ClientUpdate(m_ctx);
+
+ // initialize interface and display if they are not initialized yet
+ InitializeInterface();
+ InitializeDisplay();
+
+ // OSVR is fully initialized now
+ if (mClientContextInitialized && mDisplayConfigInitialized &&
+ mInterfaceInitialized) {
+ mOSVRInitialized = true;
+ }
+}
+
+void OSVRSession::InitializeClientContext() {
+ // already initialized
+ if (mClientContextInitialized) {
+ return;
+ }
+
+ // first time creating
+ if (!m_ctx) {
+ // get client context
+ m_ctx = osvr_ClientInit("com.osvr.webvr", 0);
+ // update context
+ osvr_ClientUpdate(m_ctx);
+ // verify we are connected
+ if (OSVR_RETURN_SUCCESS == osvr_ClientCheckStatus(m_ctx)) {
+ mClientContextInitialized = true;
+ }
+ }
+ // client context exists but not up and running yet
+ else {
+ // update context
+ osvr_ClientUpdate(m_ctx);
+ if (OSVR_RETURN_SUCCESS == osvr_ClientCheckStatus(m_ctx)) {
+ mClientContextInitialized = true;
+ }
+ }
+}
+
+void OSVRSession::InitializeInterface() {
+ // already initialized
+ if (mInterfaceInitialized) {
+ return;
+ }
+ // Client context must be initialized before getting interface
+ if (mClientContextInitialized) {
+ // m_iface will remain nullptr if no interface is returned
+ if (OSVR_RETURN_SUCCESS ==
+ osvr_ClientGetInterface(m_ctx, "/me/head", &m_iface)) {
+ mInterfaceInitialized = true;
+ }
+ }
+}
+
+void OSVRSession::InitializeDisplay() {
+ // display is fully configured
+ if (mDisplayConfigInitialized) {
+ return;
+ }
+
+ // Client context must be initialized before getting interface
+ if (mClientContextInitialized) {
+ // first time creating display object
+ if (m_display == nullptr) {
+ OSVR_ReturnCode ret = osvr_ClientGetDisplay(m_ctx, &m_display);
+
+ if (ret == OSVR_RETURN_SUCCESS) {
+ osvr_ClientUpdate(m_ctx);
+ // display object may have been created but not fully startup
+ if (OSVR_RETURN_SUCCESS == osvr_ClientCheckDisplayStartup(m_display)) {
+ mDisplayConfigInitialized = true;
+ }
+ }
+
+ // Typically once we get Display object, pose data is available after
+ // clientUpdate but sometimes it takes ~ 200 ms to get
+ // a succesfull connection, so we might have to run a few update cycles
+ } else {
+ if (OSVR_RETURN_SUCCESS == osvr_ClientCheckDisplayStartup(m_display)) {
+ mDisplayConfigInitialized = true;
+ }
+ }
+ }
+}
+
+bool OSVRSession::InitState(mozilla::gfx::VRSystemState& aSystemState) {
+ VRDisplayState& state = aSystemState.displayState;
+ strncpy(state.displayName, "OSVR HMD", kVRDisplayNameMaxLen);
+ state.eightCC = GFX_VR_EIGHTCC('O', 'S', 'V', 'R', ' ', ' ', ' ', ' ');
+ state.isConnected = true;
+ state.isMounted = false;
+ state.capabilityFlags = (VRDisplayCapabilityFlags)(
+ (int)VRDisplayCapabilityFlags::Cap_None |
+ (int)VRDisplayCapabilityFlags::Cap_Orientation |
+ (int)VRDisplayCapabilityFlags::Cap_Position |
+ (int)VRDisplayCapabilityFlags::Cap_External |
+ (int)VRDisplayCapabilityFlags::Cap_Present |
+ (int)VRDisplayCapabilityFlags::Cap_ImmersiveVR);
+ state.blendMode = VRDisplayBlendMode::Opaque;
+ state.reportsDroppedFrames = false;
+
+ // XXX OSVR display topology allows for more than one viewer
+ // will assume only one viewer for now (most likely stay that way)
+
+ OSVR_EyeCount numEyes;
+ osvr_ClientGetNumEyesForViewer(m_display, 0, &numEyes);
+
+ for (uint8_t eye = 0; eye < numEyes; eye++) {
+ double left, right, bottom, top;
+ // XXX for now there is only one surface per eye
+ osvr_ClientGetViewerEyeSurfaceProjectionClippingPlanes(
+ m_display, 0, eye, 0, &left, &right, &bottom, &top);
+ state.eyeFOV[eye] = SetFromTanRadians(-left, right, -bottom, top);
+ }
+
+ // XXX Assuming there is only one display input for now
+ // however, it's possible to have more than one (dSight with 2 HDMI inputs)
+ OSVR_DisplayDimension width, height;
+ osvr_ClientGetDisplayDimensions(m_display, 0, &width, &height);
+
+ for (uint8_t eye = 0; eye < numEyes; eye++) {
+ OSVR_ViewportDimension l, b, w, h;
+ osvr_ClientGetRelativeViewportForViewerEyeSurface(m_display, 0, eye, 0, &l,
+ &b, &w, &h);
+ state.eyeResolution.width = w;
+ state.eyeResolution.height = h;
+ OSVR_Pose3 eyePose;
+ // Viewer eye pose may not be immediately available, update client context
+ // until we get it
+ OSVR_ReturnCode ret =
+ osvr_ClientGetViewerEyePose(m_display, 0, eye, &eyePose);
+ while (ret != OSVR_RETURN_SUCCESS) {
+ osvr_ClientUpdate(m_ctx);
+ ret = osvr_ClientGetViewerEyePose(m_display, 0, eye, &eyePose);
+ }
+ state.eyeTranslation[eye].x = eyePose.translation.data[0];
+ state.eyeTranslation[eye].y = eyePose.translation.data[1];
+ state.eyeTranslation[eye].z = eyePose.translation.data[2];
+
+ Matrix4x4 pose;
+ pose.SetRotationFromQuaternion(gfx::Quaternion(
+ osvrQuatGetX(&eyePose.rotation), osvrQuatGetY(&eyePose.rotation),
+ osvrQuatGetZ(&eyePose.rotation), osvrQuatGetW(&eyePose.rotation)));
+ pose.PreTranslate(eyePose.translation.data[0], eyePose.translation.data[1],
+ eyePose.translation.data[2]);
+ pose.Invert();
+ mHeadToEye[eye] = pose;
+ }
+
+ // default to an identity quaternion
+ VRHMDSensorState& sensorState = aSystemState.sensorState;
+ sensorState.flags = (VRDisplayCapabilityFlags)(
+ (int)VRDisplayCapabilityFlags::Cap_Orientation |
+ (int)VRDisplayCapabilityFlags::Cap_Position);
+ sensorState.pose.orientation[3] = 1.0f; // Default to an identity quaternion
+
+ return true;
+}
+
+void OSVRSession::Shutdown() {
+ if (!mRuntimeLoaded) {
+ return;
+ }
+ mOSVRInitialized = false;
+ // client context may not have been initialized
+ if (m_ctx) {
+ osvr_ClientFreeDisplay(m_display);
+ }
+ // osvr checks that m_ctx or m_iface are not null
+ osvr_ClientFreeInterface(m_ctx, m_iface);
+ osvr_ClientShutdown(m_ctx);
+}
+
+void OSVRSession::ProcessEvents(mozilla::gfx::VRSystemState& aSystemState) {}
+
+void OSVRSession::StartFrame(mozilla::gfx::VRSystemState& aSystemState) {
+ UpdateHeadsetPose(aSystemState);
+}
+
+void OSVRSession::UpdateHeadsetPose(mozilla::gfx::VRSystemState& aState) {
+ // Update client context before anything
+ // this usually goes into app's mainloop
+ osvr_ClientUpdate(m_ctx);
+
+ VRHMDSensorState result{};
+ OSVR_TimeValue timestamp;
+
+ OSVR_OrientationState orientation;
+
+ OSVR_ReturnCode ret =
+ osvr_GetOrientationState(m_iface, &timestamp, &orientation);
+
+ aState.sensorState.timestamp = timestamp.seconds;
+
+ if (ret == OSVR_RETURN_SUCCESS) {
+ result.flags |= VRDisplayCapabilityFlags::Cap_Orientation;
+ result.pose.orientation[0] = orientation.data[1];
+ result.pose.orientation[1] = orientation.data[2];
+ result.pose.orientation[2] = orientation.data[3];
+ result.pose.orientation[3] = orientation.data[0];
+ } else {
+ // default to an identity quaternion
+ result.pose.orientation[3] = 1.0f;
+ }
+
+ OSVR_PositionState position;
+ ret = osvr_GetPositionState(m_iface, &timestamp, &position);
+ if (ret == OSVR_RETURN_SUCCESS) {
+ result.flags |= VRDisplayCapabilityFlags::Cap_Position;
+ result.pose.position[0] = position.data[0];
+ result.pose.position[1] = position.data[1];
+ result.pose.position[2] = position.data[2];
+ }
+
+ result.CalcViewMatrices(mHeadToEye);
+}
+
+bool OSVRSession::StartPresentation() {
+ return false;
+ // TODO Implement
+}
+
+void OSVRSession::StopPresentation() {
+ // TODO Implement
+}
+
+#if defined(XP_WIN)
+bool OSVRSession::SubmitFrame(
+ const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer,
+ ID3D11Texture2D* aTexture) {
+ return false;
+ // TODO Implement
+}
+#elif defined(XP_MACOSX)
+bool OSVRSession::SubmitFrame(
+ const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer,
+ const VRLayerTextureHandle& aTexture) {
+ return false;
+ // TODO Implement
+}
+#endif
+
+void OSVRSession::VibrateHaptic(uint32_t aControllerIdx, uint32_t aHapticIndex,
+ float aIntensity, float aDuration) {}
+
+void OSVRSession::StopVibrateHaptic(uint32_t aControllerIdx) {}
+
+void OSVRSession::StopAllHaptics() {}
diff --git a/gfx/vr/service/OSVRSession.h b/gfx/vr/service/OSVRSession.h
new file mode 100644
index 0000000000..d98553ff33
--- /dev/null
+++ b/gfx/vr/service/OSVRSession.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_SERVICE_OSVRSESSION_H
+#define GFX_VR_SERVICE_OSVRSESSION_H
+
+#include "VRSession.h"
+
+#include "mozilla/gfx/2D.h"
+#include "mozilla/TimeStamp.h"
+#include "moz_external_vr.h"
+
+#include <osvr/ClientKit/ClientKitC.h>
+#include <osvr/ClientKit/DisplayC.h>
+
+#if defined(XP_WIN)
+# include <d3d11_1.h>
+#endif
+
+namespace mozilla {
+namespace gfx {
+
+class OSVRSession : public VRSession {
+ public:
+ OSVRSession();
+ virtual ~OSVRSession();
+
+ bool Initialize(mozilla::gfx::VRSystemState& aSystemState,
+ bool aDetectRuntimesOnly) override;
+ void Shutdown() override;
+ void ProcessEvents(mozilla::gfx::VRSystemState& aSystemState) override;
+ void StartFrame(mozilla::gfx::VRSystemState& aSystemState) override;
+ bool StartPresentation() override;
+ void StopPresentation() override;
+ void VibrateHaptic(uint32_t aControllerIdx, uint32_t aHapticIndex,
+ float aIntensity, float aDuration) override;
+ void StopVibrateHaptic(uint32_t aControllerIdx) override;
+ void StopAllHaptics() override;
+
+ protected:
+#if defined(XP_WIN)
+ bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer,
+ ID3D11Texture2D* aTexture) override;
+#elif defined(XP_MACOSX)
+ bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer,
+ const VRLayerTextureHandle& aTexture) override;
+#endif
+
+ private:
+ bool InitState(mozilla::gfx::VRSystemState& aSystemState);
+ void UpdateHeadsetPose(mozilla::gfx::VRSystemState& aState);
+ bool mRuntimeLoaded;
+ bool mOSVRInitialized;
+ bool mClientContextInitialized;
+ bool mDisplayConfigInitialized;
+ bool mInterfaceInitialized;
+ OSVR_ClientContext m_ctx;
+ OSVR_ClientInterface m_iface;
+ OSVR_DisplayConfig m_display;
+ gfx::Matrix4x4 mHeadToEye[2];
+
+ // check if all components are initialized
+ // and if not, it will try to initialize them
+ void CheckOSVRStatus();
+ void InitializeClientContext();
+ void InitializeDisplay();
+ void InitializeInterface();
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // GFX_VR_SERVICE_OSVRSESSION_H
diff --git a/gfx/vr/service/OculusSession.cpp b/gfx/vr/service/OculusSession.cpp
new file mode 100644
index 0000000000..741798e15d
--- /dev/null
+++ b/gfx/vr/service/OculusSession.cpp
@@ -0,0 +1,1526 @@
+/* -*- 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 XP_WIN
+# error "Oculus support only available for Windows"
+#endif
+
+#include <math.h>
+#include <d3d11.h>
+
+#include "mozilla/StaticPrefs_dom.h"
+#include "mozilla/dom/GamepadEventTypes.h"
+#include "mozilla/dom/GamepadBinding.h"
+#include "mozilla/gfx/DeviceManagerDx.h"
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/SharedLibrary.h"
+#include "OculusSession.h"
+
+/** XXX The DX11 objects and quad blitting could be encapsulated
+ * into a separate object if either Oculus starts supporting
+ * non-Windows platforms or the blit is needed by other HMD\
+ * drivers.
+ * Alternately, we could remove the extra blit for
+ * Oculus as well with some more refactoring.
+ */
+
+// See CompositorD3D11Shaders.h
+namespace mozilla {
+namespace layers {
+struct ShaderBytes {
+ const void* mData;
+ size_t mLength;
+};
+extern ShaderBytes sRGBShader;
+extern ShaderBytes sLayerQuadVS;
+} // namespace layers
+} // namespace mozilla
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+using namespace mozilla::layers;
+
+namespace {
+
+static pfn_ovr_Initialize ovr_Initialize = nullptr;
+static pfn_ovr_Shutdown ovr_Shutdown = nullptr;
+static pfn_ovr_GetLastErrorInfo ovr_GetLastErrorInfo = nullptr;
+static pfn_ovr_GetVersionString ovr_GetVersionString = nullptr;
+static pfn_ovr_TraceMessage ovr_TraceMessage = nullptr;
+static pfn_ovr_IdentifyClient ovr_IdentifyClient = nullptr;
+static pfn_ovr_GetHmdDesc ovr_GetHmdDesc = nullptr;
+static pfn_ovr_GetTrackerCount ovr_GetTrackerCount = nullptr;
+static pfn_ovr_GetTrackerDesc ovr_GetTrackerDesc = nullptr;
+static pfn_ovr_Create ovr_Create = nullptr;
+static pfn_ovr_Destroy ovr_Destroy = nullptr;
+static pfn_ovr_GetSessionStatus ovr_GetSessionStatus = nullptr;
+static pfn_ovr_IsExtensionSupported ovr_IsExtensionSupported = nullptr;
+static pfn_ovr_EnableExtension ovr_EnableExtension = nullptr;
+static pfn_ovr_SetTrackingOriginType ovr_SetTrackingOriginType = nullptr;
+static pfn_ovr_GetTrackingOriginType ovr_GetTrackingOriginType = nullptr;
+static pfn_ovr_RecenterTrackingOrigin ovr_RecenterTrackingOrigin = nullptr;
+static pfn_ovr_SpecifyTrackingOrigin ovr_SpecifyTrackingOrigin = nullptr;
+static pfn_ovr_ClearShouldRecenterFlag ovr_ClearShouldRecenterFlag = nullptr;
+static pfn_ovr_GetTrackingState ovr_GetTrackingState = nullptr;
+static pfn_ovr_GetDevicePoses ovr_GetDevicePoses = nullptr;
+static pfn_ovr_GetTrackerPose ovr_GetTrackerPose = nullptr;
+static pfn_ovr_GetInputState ovr_GetInputState = nullptr;
+static pfn_ovr_GetConnectedControllerTypes ovr_GetConnectedControllerTypes =
+ nullptr;
+static pfn_ovr_GetTouchHapticsDesc ovr_GetTouchHapticsDesc = nullptr;
+static pfn_ovr_SetControllerVibration ovr_SetControllerVibration = nullptr;
+static pfn_ovr_SubmitControllerVibration ovr_SubmitControllerVibration =
+ nullptr;
+static pfn_ovr_GetControllerVibrationState ovr_GetControllerVibrationState =
+ nullptr;
+static pfn_ovr_TestBoundary ovr_TestBoundary = nullptr;
+static pfn_ovr_TestBoundaryPoint ovr_TestBoundaryPoint = nullptr;
+static pfn_ovr_SetBoundaryLookAndFeel ovr_SetBoundaryLookAndFeel = nullptr;
+static pfn_ovr_ResetBoundaryLookAndFeel ovr_ResetBoundaryLookAndFeel = nullptr;
+static pfn_ovr_GetBoundaryGeometry ovr_GetBoundaryGeometry = nullptr;
+static pfn_ovr_GetBoundaryDimensions ovr_GetBoundaryDimensions = nullptr;
+static pfn_ovr_GetBoundaryVisible ovr_GetBoundaryVisible = nullptr;
+static pfn_ovr_RequestBoundaryVisible ovr_RequestBoundaryVisible = nullptr;
+static pfn_ovr_GetTextureSwapChainLength ovr_GetTextureSwapChainLength =
+ nullptr;
+static pfn_ovr_GetTextureSwapChainCurrentIndex
+ ovr_GetTextureSwapChainCurrentIndex = nullptr;
+static pfn_ovr_GetTextureSwapChainDesc ovr_GetTextureSwapChainDesc = nullptr;
+static pfn_ovr_CommitTextureSwapChain ovr_CommitTextureSwapChain = nullptr;
+static pfn_ovr_DestroyTextureSwapChain ovr_DestroyTextureSwapChain = nullptr;
+static pfn_ovr_DestroyMirrorTexture ovr_DestroyMirrorTexture = nullptr;
+static pfn_ovr_GetFovTextureSize ovr_GetFovTextureSize = nullptr;
+static pfn_ovr_GetRenderDesc2 ovr_GetRenderDesc2 = nullptr;
+static pfn_ovr_WaitToBeginFrame ovr_WaitToBeginFrame = nullptr;
+static pfn_ovr_BeginFrame ovr_BeginFrame = nullptr;
+static pfn_ovr_EndFrame ovr_EndFrame = nullptr;
+static pfn_ovr_SubmitFrame ovr_SubmitFrame = nullptr;
+static pfn_ovr_GetPerfStats ovr_GetPerfStats = nullptr;
+static pfn_ovr_ResetPerfStats ovr_ResetPerfStats = nullptr;
+static pfn_ovr_GetPredictedDisplayTime ovr_GetPredictedDisplayTime = nullptr;
+static pfn_ovr_GetTimeInSeconds ovr_GetTimeInSeconds = nullptr;
+static pfn_ovr_GetBool ovr_GetBool = nullptr;
+static pfn_ovr_SetBool ovr_SetBool = nullptr;
+static pfn_ovr_GetInt ovr_GetInt = nullptr;
+static pfn_ovr_SetInt ovr_SetInt = nullptr;
+static pfn_ovr_GetFloat ovr_GetFloat = nullptr;
+static pfn_ovr_SetFloat ovr_SetFloat = nullptr;
+static pfn_ovr_GetFloatArray ovr_GetFloatArray = nullptr;
+static pfn_ovr_SetFloatArray ovr_SetFloatArray = nullptr;
+static pfn_ovr_GetString ovr_GetString = nullptr;
+static pfn_ovr_SetString ovr_SetString = nullptr;
+static pfn_ovr_GetExternalCameras ovr_GetExternalCameras = nullptr;
+static pfn_ovr_SetExternalCameraProperties ovr_SetExternalCameraProperties =
+ nullptr;
+
+#ifdef XP_WIN
+static pfn_ovr_CreateTextureSwapChainDX ovr_CreateTextureSwapChainDX = nullptr;
+static pfn_ovr_GetTextureSwapChainBufferDX ovr_GetTextureSwapChainBufferDX =
+ nullptr;
+static pfn_ovr_CreateMirrorTextureDX ovr_CreateMirrorTextureDX = nullptr;
+static pfn_ovr_GetMirrorTextureBufferDX ovr_GetMirrorTextureBufferDX = nullptr;
+#endif
+
+static pfn_ovr_CreateTextureSwapChainGL ovr_CreateTextureSwapChainGL = nullptr;
+static pfn_ovr_GetTextureSwapChainBufferGL ovr_GetTextureSwapChainBufferGL =
+ nullptr;
+static pfn_ovr_CreateMirrorTextureGL ovr_CreateMirrorTextureGL = nullptr;
+static pfn_ovr_GetMirrorTextureBufferGL ovr_GetMirrorTextureBufferGL = nullptr;
+
+#ifdef HAVE_64BIT_BUILD
+# define BUILD_BITS 64
+#else
+# define BUILD_BITS 32
+#endif
+
+#define OVR_PRODUCT_VERSION 1
+#define OVR_MAJOR_VERSION 1
+#define OVR_MINOR_VERSION 19
+
+static const uint32_t kNumOculusButtons = 7;
+static const uint32_t kNumOculusHaptcs = 1;
+static const uint32_t kNumOculusAxes = 4;
+ovrControllerType OculusControllerTypes[2] = {ovrControllerType_LTouch,
+ ovrControllerType_RTouch};
+const char* OculusControllerNames[2] = {"Oculus Touch (Left)",
+ "Oculus Touch (Right)"};
+dom::GamepadHand OculusControllerHand[2] = {dom::GamepadHand::Left,
+ dom::GamepadHand::Right};
+
+ovrButton OculusControllerButtons[2][kNumOculusButtons] = {
+ {(ovrButton)0, (ovrButton)0, (ovrButton)0, ovrButton_LThumb, ovrButton_X,
+ ovrButton_Y, (ovrButton)0},
+ {(ovrButton)0, (ovrButton)0, (ovrButton)0, ovrButton_RThumb, ovrButton_A,
+ ovrButton_B, (ovrButton)0},
+};
+
+ovrTouch OculusControllerTouches[2][kNumOculusButtons] = {
+ {ovrTouch_LIndexTrigger, (ovrTouch)0, (ovrTouch)0, ovrTouch_LThumb,
+ ovrTouch_X, ovrTouch_Y, ovrTouch_LThumbRest},
+ {ovrTouch_RIndexTrigger, (ovrTouch)0, (ovrTouch)0, ovrTouch_RThumb,
+ ovrTouch_A, ovrTouch_B, ovrTouch_RThumbRest},
+};
+
+void UpdateButton(const ovrInputState& aInputState, uint32_t aHandIdx,
+ uint32_t aButtonIdx, VRControllerState& aControllerState) {
+ if (aInputState.Buttons & OculusControllerButtons[aHandIdx][aButtonIdx]) {
+ aControllerState.buttonPressed |= ((uint64_t)1 << aButtonIdx);
+ aControllerState.triggerValue[aButtonIdx] = 1.0f;
+ } else {
+ aControllerState.triggerValue[aButtonIdx] = 0.0f;
+ }
+ if (aInputState.Touches & OculusControllerTouches[aHandIdx][aButtonIdx]) {
+ aControllerState.buttonTouched |= ((uint64_t)1 << aButtonIdx);
+ }
+}
+
+VRFieldOfView FromFovPort(const ovrFovPort& aFOV) {
+ VRFieldOfView fovInfo;
+ fovInfo.leftDegrees = atan(aFOV.LeftTan) * 180.0 / M_PI;
+ fovInfo.rightDegrees = atan(aFOV.RightTan) * 180.0 / M_PI;
+ fovInfo.upDegrees = atan(aFOV.UpTan) * 180.0 / M_PI;
+ fovInfo.downDegrees = atan(aFOV.DownTan) * 180.0 / M_PI;
+ return fovInfo;
+}
+
+} // anonymous namespace
+
+namespace mozilla {
+namespace gfx {
+
+OculusSession::OculusSession()
+ : VRSession(),
+ mOvrLib(nullptr),
+ mSession(nullptr),
+ mInitFlags((ovrInitFlags)0),
+ mTextureSet(nullptr),
+ mQuadVS(nullptr),
+ mQuadPS(nullptr),
+ mLinearSamplerState(nullptr),
+ mVSConstantBuffer(nullptr),
+ mPSConstantBuffer(nullptr),
+ mVertexBuffer(nullptr),
+ mInputLayout(nullptr),
+ mRemainingVibrateTime{},
+ mHapticPulseIntensity{},
+ mIsPresenting(false) {}
+
+OculusSession::~OculusSession() { Shutdown(); }
+
+bool OculusSession::Initialize(mozilla::gfx::VRSystemState& aSystemState,
+ bool aDetectRuntimesOnly) {
+ if (StaticPrefs::dom_vr_puppet_enabled()) {
+ // Ensure that tests using the VR Puppet do not find real hardware
+ return false;
+ }
+ if (!StaticPrefs::dom_vr_enabled() || !StaticPrefs::dom_vr_oculus_enabled()) {
+ return false;
+ }
+
+ if (aDetectRuntimesOnly) {
+ if (LoadOvrLib()) {
+ aSystemState.displayState.capabilityFlags |=
+ VRDisplayCapabilityFlags::Cap_ImmersiveVR;
+ }
+ return false;
+ }
+
+ if (!CreateD3DObjects()) {
+ return false;
+ }
+ if (!CreateShaders()) {
+ return false;
+ }
+ // Ideally, we should move LoadOvrLib() up to the first line to avoid
+ // unnecessary D3D objects creation. But it will cause a WPT fail in Win 7
+ // debug.
+ if (!LoadOvrLib()) {
+ return false;
+ }
+ // We start off with an invisible session, then re-initialize
+ // with visible session once WebVR content starts rendering.
+ if (!ChangeVisibility(false)) {
+ return false;
+ }
+ if (!InitState(aSystemState)) {
+ return false;
+ }
+
+ mPresentationSize = IntSize(aSystemState.displayState.eyeResolution.width * 2,
+ aSystemState.displayState.eyeResolution.height);
+ return true;
+}
+
+void OculusSession::UpdateVisibility() {
+ // Do not immediately re-initialize with an invisible session after
+ // the end of a VR presentation. Waiting for the configured duraction
+ // ensures that the user will not drop to Oculus Home during VR link
+ // traversal.
+ if (mIsPresenting) {
+ // We are currently rendering immersive content.
+ // Avoid interrupting the session
+ return;
+ }
+ if (mInitFlags & ovrInit_Invisible) {
+ // We are already invisible
+ return;
+ }
+ if (mLastPresentationEnd.IsNull()) {
+ // There has been no presentation yet
+ return;
+ }
+
+ TimeDuration duration = TimeStamp::Now() - mLastPresentationEnd;
+ TimeDuration timeout = TimeDuration::FromMilliseconds(
+ StaticPrefs::dom_vr_oculus_present_timeout());
+ if (timeout <= TimeDuration(0) || duration >= timeout) {
+ if (!ChangeVisibility(false)) {
+ gfxWarning() << "OculusSession::ChangeVisibility(false) failed";
+ }
+ }
+}
+
+void OculusSession::CoverTransitions() {
+ // While content is loading or during immersive-mode link
+ // traversal, we need to prevent the user from seeing the
+ // last rendered frame.
+ // We render black frames to cover up the transition.
+ MOZ_ASSERT(mSession);
+ if (mIsPresenting) {
+ // We are currently rendering immersive content.
+ // Avoid interrupting the session
+ return;
+ }
+
+ if (mInitFlags & ovrInit_Invisible) {
+ // We are invisible, nothing to cover up
+ return;
+ }
+
+ // Render a black frame
+ ovrLayerEyeFov layer;
+ memset(&layer, 0, sizeof(layer));
+ layer.Header.Type = ovrLayerType_Disabled;
+ ovrLayerHeader* layers = &layer.Header;
+ ovr_SubmitFrame(mSession, 0, nullptr, &layers, 1);
+}
+
+bool OculusSession::ChangeVisibility(bool bVisible) {
+ ovrInitFlags flags =
+ (ovrInitFlags)(ovrInit_RequestVersion | ovrInit_MixedRendering);
+ if (StaticPrefs::dom_vr_oculus_invisible_enabled() && !bVisible) {
+ flags = (ovrInitFlags)(flags | ovrInit_Invisible);
+ }
+ if (mInitFlags == flags) {
+ // The new state is the same, nothing to do
+ return true;
+ }
+
+ // Tear everything down
+ StopRendering();
+ StopSession();
+ StopLib();
+
+ // Start it back up
+ if (!StartLib(flags)) {
+ return false;
+ }
+ if (!StartSession()) {
+ return false;
+ }
+ return true;
+}
+
+void OculusSession::Shutdown() {
+ StopRendering();
+ StopSession();
+ StopLib();
+ UnloadOvrLib();
+ DestroyShaders();
+}
+
+void OculusSession::ProcessEvents(mozilla::gfx::VRSystemState& aSystemState) {
+ if (!mSession) {
+ return;
+ }
+
+ ovrSessionStatus status;
+ if (OVR_SUCCESS(ovr_GetSessionStatus(mSession, &status))) {
+ aSystemState.displayState.isConnected = status.HmdPresent;
+ aSystemState.displayState.isMounted = status.HmdMounted;
+ mShouldQuit = status.ShouldQuit;
+
+ } else {
+ aSystemState.displayState.isConnected = false;
+ aSystemState.displayState.isMounted = false;
+ }
+ UpdateHaptics();
+ UpdateVisibility();
+ CoverTransitions();
+}
+
+void OculusSession::StartFrame(mozilla::gfx::VRSystemState& aSystemState) {
+ UpdateHeadsetPose(aSystemState);
+ UpdateEyeParameters(aSystemState);
+ UpdateControllers(aSystemState);
+ UpdateTelemetry(aSystemState);
+ aSystemState.sensorState.inputFrameID++;
+}
+
+bool OculusSession::StartPresentation() {
+ /**
+ * XXX - We should resolve fail the promise returned by
+ * VRDisplay.requestPresent() when the DX11 resources fail allocation
+ * in VRDisplayOculus::StartPresentation().
+ * Bailing out here prevents the crash but content should be aware
+ * that frames are not being presented.
+ * See Bug 1299309.
+ **/
+ if (!ChangeVisibility(true)) {
+ return false;
+ }
+ if (!StartRendering()) {
+ StopRendering();
+ return false;
+ }
+ mIsPresenting = true;
+ return true;
+}
+
+void OculusSession::StopPresentation() {
+ mLastPresentationEnd = TimeStamp::Now();
+ mIsPresenting = false;
+}
+
+bool OculusSession::SubmitFrame(
+ const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer,
+ ID3D11Texture2D* aTexture) {
+ if (!IsPresentationReady()) {
+ return false;
+ }
+
+ D3D11_TEXTURE2D_DESC textureDesc = {0};
+ aTexture->GetDesc(&textureDesc);
+
+ int currentRenderTarget = 0;
+ ovrResult orv = ovr_GetTextureSwapChainCurrentIndex(mSession, mTextureSet,
+ &currentRenderTarget);
+ if (orv != ovrSuccess) {
+ NS_WARNING("ovr_GetTextureSwapChainCurrentIndex failed.");
+ return false;
+ }
+
+ ID3D11RenderTargetView* view = mRTView[currentRenderTarget];
+
+ float clear[] = {0.0f, 0.0f, 0.0f, 1.0f};
+ mContext->ClearRenderTargetView(view, clear);
+ mContext->OMSetRenderTargets(1, &view, nullptr);
+
+ Matrix viewMatrix = Matrix::Translation(-1.0, 1.0);
+ viewMatrix.PreScale(2.0f / float(textureDesc.Width),
+ 2.0f / float(textureDesc.Height));
+ viewMatrix.PreScale(1.0f, -1.0f);
+ Matrix4x4 projection = Matrix4x4::From2D(viewMatrix);
+ projection._33 = 0.0f;
+
+ Matrix transform2d;
+ gfx::Matrix4x4 transform = gfx::Matrix4x4::From2D(transform2d);
+
+ D3D11_VIEWPORT viewport;
+ viewport.MinDepth = 0.0f;
+ viewport.MaxDepth = 1.0f;
+ viewport.Width = textureDesc.Width;
+ viewport.Height = textureDesc.Height;
+ viewport.TopLeftX = 0;
+ viewport.TopLeftY = 0;
+
+ D3D11_RECT scissor;
+ scissor.left = 0;
+ scissor.right = textureDesc.Width;
+ scissor.top = 0;
+ scissor.bottom = textureDesc.Height;
+
+ memcpy(&mVSConstants.layerTransform, &transform._11,
+ sizeof(mVSConstants.layerTransform));
+ memcpy(&mVSConstants.projection, &projection._11,
+ sizeof(mVSConstants.projection));
+ mVSConstants.renderTargetOffset[0] = 0.0f;
+ mVSConstants.renderTargetOffset[1] = 0.0f;
+ mVSConstants.layerQuad =
+ Rect(0.0f, 0.0f, textureDesc.Width, textureDesc.Height);
+ mVSConstants.textureCoords = Rect(0.0f, 1.0f, 1.0f, -1.0f);
+
+ mPSConstants.layerOpacity[0] = 1.0f;
+
+ ID3D11Buffer* vbuffer = mVertexBuffer;
+ UINT vsize = sizeof(Vertex);
+ UINT voffset = 0;
+ mContext->IASetVertexBuffers(0, 1, &vbuffer, &vsize, &voffset);
+ mContext->IASetIndexBuffer(nullptr, DXGI_FORMAT_R16_UINT, 0);
+ mContext->IASetInputLayout(mInputLayout);
+ mContext->RSSetViewports(1, &viewport);
+ mContext->RSSetScissorRects(1, &scissor);
+ mContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
+ mContext->VSSetShader(mQuadVS, nullptr, 0);
+ mContext->PSSetShader(mQuadPS, nullptr, 0);
+
+ RefPtr<ID3D11ShaderResourceView> srView;
+ HRESULT hr = mDevice->CreateShaderResourceView(aTexture, nullptr,
+ getter_AddRefs(srView));
+ if (FAILED(hr)) {
+ gfxWarning() << "Could not create shader resource view for Oculus: "
+ << hexa(hr);
+ return false;
+ }
+ ID3D11ShaderResourceView* viewPtr = srView.get();
+ mContext->PSSetShaderResources(0 /* 0 == TexSlot::RGB */, 1, &viewPtr);
+ // XXX Use Constant from TexSlot in CompositorD3D11.cpp?
+
+ ID3D11SamplerState* sampler = mLinearSamplerState;
+ mContext->PSSetSamplers(0, 1, &sampler);
+
+ if (!UpdateConstantBuffers()) {
+ NS_WARNING("Failed to update constant buffers for Oculus");
+ return false;
+ }
+
+ mContext->Draw(4, 0);
+
+ orv = ovr_CommitTextureSwapChain(mSession, mTextureSet);
+ if (orv != ovrSuccess) {
+ NS_WARNING("ovr_CommitTextureSwapChain failed.");
+ return false;
+ }
+
+ ovrLayerEyeFov layer;
+ memset(&layer, 0, sizeof(layer));
+ layer.Header.Type = ovrLayerType_EyeFov;
+ layer.Header.Flags = 0;
+ layer.ColorTexture[0] = mTextureSet;
+ layer.ColorTexture[1] = nullptr;
+ layer.Fov[0] = mFOVPort[0];
+ layer.Fov[1] = mFOVPort[1];
+ layer.Viewport[0].Pos.x = textureDesc.Width * aLayer.leftEyeRect.x;
+ layer.Viewport[0].Pos.y = textureDesc.Height * aLayer.leftEyeRect.y;
+ layer.Viewport[0].Size.w = textureDesc.Width * aLayer.leftEyeRect.width;
+ layer.Viewport[0].Size.h = textureDesc.Height * aLayer.leftEyeRect.height;
+ layer.Viewport[1].Pos.x = textureDesc.Width * aLayer.rightEyeRect.x;
+ layer.Viewport[1].Pos.y = textureDesc.Height * aLayer.rightEyeRect.y;
+ layer.Viewport[1].Size.w = textureDesc.Width * aLayer.rightEyeRect.width;
+ layer.Viewport[1].Size.h = textureDesc.Height * aLayer.rightEyeRect.height;
+
+ for (uint32_t i = 0; i < 2; ++i) {
+ layer.RenderPose[i].Orientation.x = mFrameStartPose[i].Orientation.x;
+ layer.RenderPose[i].Orientation.y = mFrameStartPose[i].Orientation.y;
+ layer.RenderPose[i].Orientation.z = mFrameStartPose[i].Orientation.z;
+ layer.RenderPose[i].Orientation.w = mFrameStartPose[i].Orientation.w;
+ layer.RenderPose[i].Position.x = mFrameStartPose[i].Position.x;
+ layer.RenderPose[i].Position.y = mFrameStartPose[i].Position.y;
+ layer.RenderPose[i].Position.z = mFrameStartPose[i].Position.z;
+ }
+
+ ovrLayerHeader* layers = &layer.Header;
+ orv = ovr_SubmitFrame(mSession, 0, nullptr, &layers, 1);
+ // ovr_SubmitFrame will fail during the Oculus health and safety warning.
+ // and will start succeeding once the warning has been dismissed by the user.
+
+ if (!OVR_UNQUALIFIED_SUCCESS(orv)) {
+ /**
+ * We wish to throttle the framerate for any case that the rendered
+ * result is not visible. In some cases, such as during the Oculus
+ * "health and safety warning", orv will be > 0 (OVR_SUCCESS but not
+ * OVR_UNQUALIFIED_SUCCESS) and ovr_SubmitFrame will not block.
+ * In this case, returning true would have resulted in an unthrottled
+ * render loop hiting excessive frame rates and consuming resources.
+ */
+ return false;
+ }
+
+ return true;
+}
+
+bool OculusSession::LoadOvrLib() {
+ if (mOvrLib) {
+ // Already loaded, early exit
+ return true;
+ }
+#if defined(_WIN32)
+ nsTArray<nsString> libSearchPaths;
+ nsString libName;
+ nsString searchPath;
+
+ for (;;) {
+ UINT requiredLength = ::GetSystemDirectoryW(
+ char16ptr_t(searchPath.BeginWriting()), searchPath.Length());
+ if (!requiredLength) {
+ break;
+ }
+ if (requiredLength < searchPath.Length()) {
+ searchPath.Truncate(requiredLength);
+ libSearchPaths.AppendElement(searchPath);
+ break;
+ }
+ searchPath.SetLength(requiredLength);
+ }
+ libName.AppendPrintf("LibOVRRT%d_%d.dll", BUILD_BITS, OVR_PRODUCT_VERSION);
+
+ // search the path/module dir
+ libSearchPaths.InsertElementsAt(0, 1, u""_ns);
+
+ // If the env var is present, we override libName
+ if (_wgetenv(L"OVR_LIB_PATH")) {
+ searchPath = _wgetenv(L"OVR_LIB_PATH");
+ libSearchPaths.InsertElementsAt(0, 1, searchPath);
+ }
+
+ if (_wgetenv(L"OVR_LIB_NAME")) {
+ libName = _wgetenv(L"OVR_LIB_NAME");
+ }
+
+ if (libName.IsEmpty()) {
+ return false;
+ }
+
+ for (uint32_t i = 0; i < libSearchPaths.Length(); ++i) {
+ nsString& libPath = libSearchPaths[i];
+ nsString fullName;
+ if (libPath.Length() == 0) {
+ fullName.Assign(libName);
+ } else {
+ fullName.Assign(libPath + u"\\"_ns + libName);
+ }
+
+ mOvrLib = LoadLibraryWithFlags(fullName.get());
+ if (mOvrLib) {
+ break;
+ }
+ }
+#else
+# error "Unsupported platform!"
+#endif
+
+ if (!mOvrLib) {
+ return false;
+ }
+
+#define REQUIRE_FUNCTION(_x) \
+ do { \
+ *(void**)&_x = (void*)PR_FindSymbol(mOvrLib, #_x); \
+ if (!_x) { \
+ printf_stderr(#_x " symbol missing\n"); \
+ goto fail; \
+ } \
+ } while (0)
+
+ REQUIRE_FUNCTION(ovr_Initialize);
+ REQUIRE_FUNCTION(ovr_Shutdown);
+ REQUIRE_FUNCTION(ovr_GetLastErrorInfo);
+ REQUIRE_FUNCTION(ovr_GetVersionString);
+ REQUIRE_FUNCTION(ovr_TraceMessage);
+ REQUIRE_FUNCTION(ovr_IdentifyClient);
+ REQUIRE_FUNCTION(ovr_GetHmdDesc);
+ REQUIRE_FUNCTION(ovr_GetTrackerCount);
+ REQUIRE_FUNCTION(ovr_GetTrackerDesc);
+ REQUIRE_FUNCTION(ovr_Create);
+ REQUIRE_FUNCTION(ovr_Destroy);
+ REQUIRE_FUNCTION(ovr_GetSessionStatus);
+ REQUIRE_FUNCTION(ovr_IsExtensionSupported);
+ REQUIRE_FUNCTION(ovr_EnableExtension);
+ REQUIRE_FUNCTION(ovr_SetTrackingOriginType);
+ REQUIRE_FUNCTION(ovr_GetTrackingOriginType);
+ REQUIRE_FUNCTION(ovr_RecenterTrackingOrigin);
+ REQUIRE_FUNCTION(ovr_SpecifyTrackingOrigin);
+ REQUIRE_FUNCTION(ovr_ClearShouldRecenterFlag);
+ REQUIRE_FUNCTION(ovr_GetTrackingState);
+ REQUIRE_FUNCTION(ovr_GetDevicePoses);
+ REQUIRE_FUNCTION(ovr_GetTrackerPose);
+ REQUIRE_FUNCTION(ovr_GetInputState);
+ REQUIRE_FUNCTION(ovr_GetConnectedControllerTypes);
+ REQUIRE_FUNCTION(ovr_GetTouchHapticsDesc);
+ REQUIRE_FUNCTION(ovr_SetControllerVibration);
+ REQUIRE_FUNCTION(ovr_SubmitControllerVibration);
+ REQUIRE_FUNCTION(ovr_GetControllerVibrationState);
+ REQUIRE_FUNCTION(ovr_TestBoundary);
+ REQUIRE_FUNCTION(ovr_TestBoundaryPoint);
+ REQUIRE_FUNCTION(ovr_SetBoundaryLookAndFeel);
+ REQUIRE_FUNCTION(ovr_ResetBoundaryLookAndFeel);
+ REQUIRE_FUNCTION(ovr_GetBoundaryGeometry);
+ REQUIRE_FUNCTION(ovr_GetBoundaryDimensions);
+ REQUIRE_FUNCTION(ovr_GetBoundaryVisible);
+ REQUIRE_FUNCTION(ovr_RequestBoundaryVisible);
+ REQUIRE_FUNCTION(ovr_GetTextureSwapChainLength);
+ REQUIRE_FUNCTION(ovr_GetTextureSwapChainCurrentIndex);
+ REQUIRE_FUNCTION(ovr_GetTextureSwapChainDesc);
+ REQUIRE_FUNCTION(ovr_CommitTextureSwapChain);
+ REQUIRE_FUNCTION(ovr_DestroyTextureSwapChain);
+ REQUIRE_FUNCTION(ovr_DestroyMirrorTexture);
+ REQUIRE_FUNCTION(ovr_GetFovTextureSize);
+ REQUIRE_FUNCTION(ovr_GetRenderDesc2);
+ REQUIRE_FUNCTION(ovr_WaitToBeginFrame);
+ REQUIRE_FUNCTION(ovr_BeginFrame);
+ REQUIRE_FUNCTION(ovr_EndFrame);
+ REQUIRE_FUNCTION(ovr_SubmitFrame);
+ REQUIRE_FUNCTION(ovr_GetPerfStats);
+ REQUIRE_FUNCTION(ovr_ResetPerfStats);
+ REQUIRE_FUNCTION(ovr_GetPredictedDisplayTime);
+ REQUIRE_FUNCTION(ovr_GetTimeInSeconds);
+ REQUIRE_FUNCTION(ovr_GetBool);
+ REQUIRE_FUNCTION(ovr_SetBool);
+ REQUIRE_FUNCTION(ovr_GetInt);
+ REQUIRE_FUNCTION(ovr_SetInt);
+ REQUIRE_FUNCTION(ovr_GetFloat);
+ REQUIRE_FUNCTION(ovr_SetFloat);
+ REQUIRE_FUNCTION(ovr_GetFloatArray);
+ REQUIRE_FUNCTION(ovr_SetFloatArray);
+ REQUIRE_FUNCTION(ovr_GetString);
+ REQUIRE_FUNCTION(ovr_SetString);
+ REQUIRE_FUNCTION(ovr_GetExternalCameras);
+ REQUIRE_FUNCTION(ovr_SetExternalCameraProperties);
+
+#ifdef XP_WIN
+
+ REQUIRE_FUNCTION(ovr_CreateTextureSwapChainDX);
+ REQUIRE_FUNCTION(ovr_GetTextureSwapChainBufferDX);
+ REQUIRE_FUNCTION(ovr_CreateMirrorTextureDX);
+ REQUIRE_FUNCTION(ovr_GetMirrorTextureBufferDX);
+
+#endif
+
+ REQUIRE_FUNCTION(ovr_CreateTextureSwapChainGL);
+ REQUIRE_FUNCTION(ovr_GetTextureSwapChainBufferGL);
+ REQUIRE_FUNCTION(ovr_CreateMirrorTextureGL);
+ REQUIRE_FUNCTION(ovr_GetMirrorTextureBufferGL);
+
+#undef REQUIRE_FUNCTION
+
+ return true;
+
+fail:
+ ovr_Initialize = nullptr;
+ PR_UnloadLibrary(mOvrLib);
+ mOvrLib = nullptr;
+ return false;
+}
+
+void OculusSession::UnloadOvrLib() {
+ if (mOvrLib) {
+ PR_UnloadLibrary(mOvrLib);
+ mOvrLib = nullptr;
+ }
+}
+
+bool OculusSession::StartLib(ovrInitFlags aFlags) {
+ if (mInitFlags == 0) {
+ ovrInitParams params;
+ memset(&params, 0, sizeof(params));
+ params.Flags = aFlags;
+ params.RequestedMinorVersion = OVR_MINOR_VERSION;
+ params.LogCallback = nullptr;
+ params.ConnectionTimeoutMS = 0;
+
+ ovrResult orv = ovr_Initialize(&params);
+
+ if (orv == ovrSuccess) {
+ mInitFlags = aFlags;
+ } else {
+ return false;
+ }
+ }
+ MOZ_ASSERT(mInitFlags == aFlags);
+ return true;
+}
+
+void OculusSession::StopLib() {
+ if (mInitFlags) {
+ ovr_Shutdown();
+ mInitFlags = (ovrInitFlags)0;
+ }
+}
+
+bool OculusSession::StartSession() {
+ // ovr_Create can be slow when no HMD is present and we wish
+ // to keep the same oculus session when possible, so we detect
+ // presence of an HMD with ovr_GetHmdDesc before calling ovr_Create
+ ovrHmdDesc desc = ovr_GetHmdDesc(NULL);
+ if (desc.Type == ovrHmd_None) {
+ // No HMD connected, destroy any existing session
+ if (mSession) {
+ ovr_Destroy(mSession);
+ mSession = nullptr;
+ }
+ return false;
+ }
+ if (mSession != nullptr) {
+ // HMD Detected and we already have a session, let's keep using it.
+ return true;
+ }
+
+ // HMD Detected and we don't have a session yet,
+ // try to create a new session
+ ovrSession session;
+ ovrGraphicsLuid luid;
+ ovrResult orv = ovr_Create(&session, &luid);
+ if (orv == ovrSuccess) {
+ orv = ovr_SetTrackingOriginType(session, ovrTrackingOrigin_FloorLevel);
+ if (orv != ovrSuccess) {
+ NS_WARNING("ovr_SetTrackingOriginType failed.\n");
+ }
+ mSession = session;
+ return true;
+ }
+
+ // Failed to create a session for the HMD
+ return false;
+}
+
+void OculusSession::StopSession() {
+ if (mSession) {
+ ovr_Destroy(mSession);
+ mSession = nullptr;
+ }
+}
+
+bool OculusSession::CreateD3DObjects() {
+ RefPtr<ID3D11Device> device = gfx::DeviceManagerDx::Get()->GetVRDevice();
+ if (!device) {
+ return false;
+ }
+ if (!CreateD3DContext(device)) {
+ return false;
+ }
+ return true;
+}
+
+bool OculusSession::CreateShaders() {
+ if (!mQuadVS) {
+ if (FAILED(mDevice->CreateVertexShader(
+ sLayerQuadVS.mData, sLayerQuadVS.mLength, nullptr, &mQuadVS))) {
+ NS_WARNING("Failed to create vertex shader for Oculus");
+ return false;
+ }
+ }
+
+ if (!mQuadPS) {
+ if (FAILED(mDevice->CreatePixelShader(sRGBShader.mData, sRGBShader.mLength,
+ nullptr, &mQuadPS))) {
+ NS_WARNING("Failed to create pixel shader for Oculus");
+ return false;
+ }
+ }
+
+ CD3D11_BUFFER_DESC cBufferDesc(sizeof(layers::VertexShaderConstants),
+ D3D11_BIND_CONSTANT_BUFFER,
+ D3D11_USAGE_DYNAMIC, D3D11_CPU_ACCESS_WRITE);
+
+ if (!mVSConstantBuffer) {
+ if (FAILED(mDevice->CreateBuffer(&cBufferDesc, nullptr,
+ getter_AddRefs(mVSConstantBuffer)))) {
+ NS_WARNING("Failed to vertex shader constant buffer for Oculus");
+ return false;
+ }
+ }
+
+ if (!mPSConstantBuffer) {
+ cBufferDesc.ByteWidth = sizeof(layers::PixelShaderConstants);
+ if (FAILED(mDevice->CreateBuffer(&cBufferDesc, nullptr,
+ getter_AddRefs(mPSConstantBuffer)))) {
+ NS_WARNING("Failed to pixel shader constant buffer for Oculus");
+ return false;
+ }
+ }
+
+ if (!mLinearSamplerState) {
+ CD3D11_SAMPLER_DESC samplerDesc(D3D11_DEFAULT);
+ if (FAILED(mDevice->CreateSamplerState(
+ &samplerDesc, getter_AddRefs(mLinearSamplerState)))) {
+ NS_WARNING("Failed to create sampler state for Oculus");
+ return false;
+ }
+ }
+
+ if (!mInputLayout) {
+ D3D11_INPUT_ELEMENT_DESC layout[] = {
+ {"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0,
+ D3D11_INPUT_PER_VERTEX_DATA, 0},
+ };
+
+ if (FAILED(mDevice->CreateInputLayout(
+ layout, sizeof(layout) / sizeof(D3D11_INPUT_ELEMENT_DESC),
+ sLayerQuadVS.mData, sLayerQuadVS.mLength,
+ getter_AddRefs(mInputLayout)))) {
+ NS_WARNING("Failed to create input layout for Oculus");
+ return false;
+ }
+ }
+
+ if (!mVertexBuffer) {
+ Vertex vertices[] = {
+ {{0.0, 0.0}}, {{1.0, 0.0}}, {{0.0, 1.0}}, {{1.0, 1.0}}};
+ CD3D11_BUFFER_DESC bufferDesc(sizeof(vertices), D3D11_BIND_VERTEX_BUFFER);
+ D3D11_SUBRESOURCE_DATA data;
+ data.pSysMem = (void*)vertices;
+
+ if (FAILED(mDevice->CreateBuffer(&bufferDesc, &data,
+ getter_AddRefs(mVertexBuffer)))) {
+ NS_WARNING("Failed to create vertex buffer for Oculus");
+ return false;
+ }
+ }
+
+ memset(&mVSConstants, 0, sizeof(mVSConstants));
+ memset(&mPSConstants, 0, sizeof(mPSConstants));
+ return true;
+}
+
+void OculusSession::DestroyShaders() {}
+
+bool OculusSession::UpdateConstantBuffers() {
+ HRESULT hr;
+ D3D11_MAPPED_SUBRESOURCE resource;
+ resource.pData = nullptr;
+
+ hr = mContext->Map(mVSConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0,
+ &resource);
+ if (FAILED(hr) || !resource.pData) {
+ return false;
+ }
+ *(VertexShaderConstants*)resource.pData = mVSConstants;
+ mContext->Unmap(mVSConstantBuffer, 0);
+ resource.pData = nullptr;
+
+ hr = mContext->Map(mPSConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0,
+ &resource);
+ if (FAILED(hr) || !resource.pData) {
+ return false;
+ }
+ *(PixelShaderConstants*)resource.pData = mPSConstants;
+ mContext->Unmap(mPSConstantBuffer, 0);
+
+ ID3D11Buffer* buffer = mVSConstantBuffer;
+ mContext->VSSetConstantBuffers(0, 1, &buffer);
+ buffer = mPSConstantBuffer;
+ mContext->PSSetConstantBuffers(0, 1, &buffer);
+ return true;
+}
+
+bool OculusSession::StartRendering() {
+ if (!mTextureSet) {
+ /**
+ * The presentation format is determined by content, which describes the
+ * left and right eye rectangles in the VRLayer. The default, if no
+ * coordinates are passed is to place the left and right eye textures
+ * side-by-side within the buffer.
+ *
+ * XXX - An optimization would be to dynamically resize this buffer
+ * to accomodate sites that are choosing to render in a lower
+ * resolution or are using space outside of the left and right
+ * eye textures for other purposes. (Bug 1291443)
+ */
+
+ ovrTextureSwapChainDesc desc;
+ memset(&desc, 0, sizeof(desc));
+ desc.Type = ovrTexture_2D;
+ desc.ArraySize = 1;
+ desc.Format = OVR_FORMAT_B8G8R8A8_UNORM_SRGB;
+ desc.Width = mPresentationSize.width;
+ desc.Height = mPresentationSize.height;
+ desc.MipLevels = 1;
+ desc.SampleCount = 1;
+ desc.StaticImage = false;
+ desc.MiscFlags = ovrTextureMisc_DX_Typeless;
+ desc.BindFlags = ovrTextureBind_DX_RenderTarget;
+
+ ovrResult orv =
+ ovr_CreateTextureSwapChainDX(mSession, mDevice, &desc, &mTextureSet);
+ if (orv != ovrSuccess) {
+ NS_WARNING("ovr_CreateTextureSwapChainDX failed");
+ return false;
+ }
+
+ int textureCount = 0;
+ orv = ovr_GetTextureSwapChainLength(mSession, mTextureSet, &textureCount);
+ if (orv != ovrSuccess) {
+ NS_WARNING("ovr_GetTextureSwapChainLength failed");
+ return false;
+ }
+ mTexture.SetLength(textureCount);
+ mRTView.SetLength(textureCount);
+ mSRV.SetLength(textureCount);
+ for (int i = 0; i < textureCount; ++i) {
+ ID3D11Texture2D* texture = nullptr;
+ orv = ovr_GetTextureSwapChainBufferDX(mSession, mTextureSet, i,
+ IID_PPV_ARGS(&texture));
+ if (orv != ovrSuccess) {
+ NS_WARNING("Failed to create Oculus texture swap chain.");
+ return false;
+ }
+
+ RefPtr<ID3D11RenderTargetView> rtView;
+ CD3D11_RENDER_TARGET_VIEW_DESC rtvDesc(D3D11_RTV_DIMENSION_TEXTURE2D,
+ DXGI_FORMAT_B8G8R8A8_UNORM);
+ HRESULT hr = mDevice->CreateRenderTargetView(texture, &rtvDesc,
+ getter_AddRefs(rtView));
+ if (FAILED(hr)) {
+ NS_WARNING(
+ "Failed to create RenderTargetView for Oculus texture swap chain.");
+ texture->Release();
+ return false;
+ }
+
+ RefPtr<ID3D11ShaderResourceView> srv;
+ CD3D11_SHADER_RESOURCE_VIEW_DESC srvDesc(D3D11_SRV_DIMENSION_TEXTURE2D,
+ DXGI_FORMAT_B8G8R8A8_UNORM);
+ hr = mDevice->CreateShaderResourceView(texture, &srvDesc,
+ getter_AddRefs(srv));
+ if (FAILED(hr)) {
+ NS_WARNING(
+ "Failed to create ShaderResourceView for Oculus texture swap "
+ "chain.");
+ texture->Release();
+ return false;
+ }
+
+ mTexture[i] = texture;
+ mRTView[i] = rtView;
+ mSRV[i] = srv;
+ texture->Release();
+ }
+ }
+ return true;
+}
+
+bool OculusSession::IsPresentationReady() const {
+ return mTextureSet != nullptr;
+}
+
+void OculusSession::StopRendering() {
+ mSRV.Clear();
+ mRTView.Clear();
+ mTexture.Clear();
+
+ if (mTextureSet && mSession) {
+ ovr_DestroyTextureSwapChain(mSession, mTextureSet);
+ }
+ mTextureSet = nullptr;
+ mIsPresenting = false;
+}
+
+bool OculusSession::InitState(VRSystemState& aSystemState) {
+ VRDisplayState& state = aSystemState.displayState;
+ strncpy(state.displayName, "Oculus VR HMD", kVRDisplayNameMaxLen);
+ state.isConnected = true;
+ state.isMounted = false;
+
+ ovrHmdDesc desc = ovr_GetHmdDesc(mSession);
+
+ state.capabilityFlags = VRDisplayCapabilityFlags::Cap_None;
+ if (desc.AvailableTrackingCaps & ovrTrackingCap_Orientation) {
+ state.capabilityFlags |= VRDisplayCapabilityFlags::Cap_Orientation;
+ state.capabilityFlags |= VRDisplayCapabilityFlags::Cap_AngularAcceleration;
+ }
+ if (desc.AvailableTrackingCaps & ovrTrackingCap_Position) {
+ state.capabilityFlags |= VRDisplayCapabilityFlags::Cap_Position;
+ state.capabilityFlags |= VRDisplayCapabilityFlags::Cap_LinearAcceleration;
+ state.capabilityFlags |= VRDisplayCapabilityFlags::Cap_StageParameters;
+ }
+ state.capabilityFlags |= VRDisplayCapabilityFlags::Cap_External;
+ state.capabilityFlags |= VRDisplayCapabilityFlags::Cap_MountDetection;
+ state.capabilityFlags |= VRDisplayCapabilityFlags::Cap_Present;
+ state.capabilityFlags |= VRDisplayCapabilityFlags::Cap_ImmersiveVR;
+ state.blendMode = VRDisplayBlendMode::Opaque;
+ state.reportsDroppedFrames = true;
+
+ mFOVPort[VRDisplayState::Eye_Left] = desc.DefaultEyeFov[ovrEye_Left];
+ mFOVPort[VRDisplayState::Eye_Right] = desc.DefaultEyeFov[ovrEye_Right];
+
+ state.eyeFOV[VRDisplayState::Eye_Left] =
+ FromFovPort(mFOVPort[VRDisplayState::Eye_Left]);
+ state.eyeFOV[VRDisplayState::Eye_Right] =
+ FromFovPort(mFOVPort[VRDisplayState::Eye_Right]);
+
+ float pixelsPerDisplayPixel = 1.0;
+ ovrSizei texSize[2];
+
+ // get eye texture sizes
+ for (uint32_t eye = 0; eye < VRDisplayState::NumEyes; eye++) {
+ texSize[eye] = ovr_GetFovTextureSize(mSession, (ovrEyeType)eye,
+ mFOVPort[eye], pixelsPerDisplayPixel);
+ }
+
+ // take the max of both for eye resolution
+ state.eyeResolution.width = std::max(texSize[VRDisplayState::Eye_Left].w,
+ texSize[VRDisplayState::Eye_Right].w);
+ state.eyeResolution.height = std::max(texSize[VRDisplayState::Eye_Left].h,
+ texSize[VRDisplayState::Eye_Right].h);
+ state.nativeFramebufferScaleFactor = 1.0f;
+
+ // default to an identity quaternion
+ aSystemState.sensorState.pose.orientation[3] = 1.0f;
+
+ UpdateStageParameters(state);
+ UpdateEyeParameters(aSystemState);
+
+ VRHMDSensorState& sensorState = aSystemState.sensorState;
+ sensorState.flags = (VRDisplayCapabilityFlags)(
+ (int)VRDisplayCapabilityFlags::Cap_Orientation |
+ (int)VRDisplayCapabilityFlags::Cap_Position);
+ sensorState.pose.orientation[3] = 1.0f; // Default to an identity quaternion
+
+ return true;
+}
+
+void OculusSession::UpdateStageParameters(VRDisplayState& aState) {
+ ovrVector3f playArea;
+ ovrResult res =
+ ovr_GetBoundaryDimensions(mSession, ovrBoundary_PlayArea, &playArea);
+ if (res == ovrSuccess) {
+ aState.stageSize.width = playArea.x;
+ aState.stageSize.height = playArea.z;
+ } else {
+ // If we fail, fall back to reasonable defaults.
+ // 1m x 1m space
+ aState.stageSize.width = 1.0f;
+ aState.stageSize.height = 1.0f;
+ }
+
+ float eyeHeight =
+ ovr_GetFloat(mSession, OVR_KEY_EYE_HEIGHT, OVR_DEFAULT_EYE_HEIGHT);
+
+ aState.sittingToStandingTransform[0] = 1.0f;
+ aState.sittingToStandingTransform[1] = 0.0f;
+ aState.sittingToStandingTransform[2] = 0.0f;
+ aState.sittingToStandingTransform[3] = 0.0f;
+
+ aState.sittingToStandingTransform[4] = 0.0f;
+ aState.sittingToStandingTransform[5] = 1.0f;
+ aState.sittingToStandingTransform[6] = 0.0f;
+ aState.sittingToStandingTransform[7] = 0.0f;
+
+ aState.sittingToStandingTransform[8] = 0.0f;
+ aState.sittingToStandingTransform[9] = 0.0f;
+ aState.sittingToStandingTransform[10] = 1.0f;
+ aState.sittingToStandingTransform[11] = 0.0f;
+
+ aState.sittingToStandingTransform[12] = 0.0f;
+ aState.sittingToStandingTransform[13] = eyeHeight;
+ aState.sittingToStandingTransform[14] = 0.0f;
+ aState.sittingToStandingTransform[15] = 1.0f;
+}
+
+void OculusSession::UpdateEyeParameters(VRSystemState& aState) {
+ if (!mSession) {
+ return;
+ }
+ // This must be called every frame in order to
+ // account for continuous adjustments to ipd.
+ gfx::Matrix4x4 headToEyeTransforms[2];
+ for (uint32_t eye = 0; eye < VRDisplayState::NumEyes; eye++) {
+ // As of Oculus 1.17 SDK, we must use the ovr_GetRenderDesc2 function to
+ // return the updated version of ovrEyeRenderDesc. This is normally done by
+ // the Oculus static lib shim, but we need to do this explicitly as we are
+ // loading the Oculus runtime dll directly.
+ ovrEyeRenderDesc renderDesc =
+ ovr_GetRenderDesc2(mSession, (ovrEyeType)eye, mFOVPort[eye]);
+ aState.displayState.eyeTranslation[eye].x =
+ renderDesc.HmdToEyePose.Position.x;
+ aState.displayState.eyeTranslation[eye].y =
+ renderDesc.HmdToEyePose.Position.y;
+ aState.displayState.eyeTranslation[eye].z =
+ renderDesc.HmdToEyePose.Position.z;
+
+ Matrix4x4 pose;
+ pose.SetRotationFromQuaternion(
+ gfx::Quaternion(-renderDesc.HmdToEyePose.Orientation.x,
+ -renderDesc.HmdToEyePose.Orientation.y,
+ -renderDesc.HmdToEyePose.Orientation.z,
+ renderDesc.HmdToEyePose.Orientation.w));
+ pose.PreTranslate(renderDesc.HmdToEyePose.Position.x,
+ renderDesc.HmdToEyePose.Position.y,
+ renderDesc.HmdToEyePose.Position.z);
+ pose.Invert();
+ headToEyeTransforms[eye] = pose;
+ }
+ aState.sensorState.CalcViewMatrices(headToEyeTransforms);
+
+ Matrix4x4 matView[2];
+ memcpy(matView[0].components, aState.sensorState.leftViewMatrix,
+ sizeof(float) * 16);
+ memcpy(matView[1].components, aState.sensorState.rightViewMatrix,
+ sizeof(float) * 16);
+
+ for (uint32_t eye = 0; eye < VRDisplayState::NumEyes; eye++) {
+ Point3D eyeTranslation;
+ Quaternion eyeRotation;
+ Point3D eyeScale;
+ if (!matView[eye].Decompose(eyeTranslation, eyeRotation, eyeScale)) {
+ NS_WARNING("Failed to decompose eye pose matrix for Oculus");
+ }
+
+ eyeRotation.Invert();
+ mFrameStartPose[eye].Orientation.x = eyeRotation.x;
+ mFrameStartPose[eye].Orientation.y = eyeRotation.y;
+ mFrameStartPose[eye].Orientation.z = eyeRotation.z;
+ mFrameStartPose[eye].Orientation.w = eyeRotation.w;
+ mFrameStartPose[eye].Position.x = eyeTranslation.x;
+ mFrameStartPose[eye].Position.y = eyeTranslation.y;
+ mFrameStartPose[eye].Position.z = eyeTranslation.z;
+ }
+}
+
+void OculusSession::UpdateHeadsetPose(VRSystemState& aState) {
+ if (!mSession) {
+ return;
+ }
+ double predictedFrameTime = 0.0f;
+ if (StaticPrefs::dom_vr_poseprediction_enabled()) {
+ // XXX We might need to call ovr_GetPredictedDisplayTime even if we don't
+ // use the result. If we don't call it, the Oculus driver will spew out many
+ // warnings...
+ predictedFrameTime = ovr_GetPredictedDisplayTime(mSession, 0);
+ }
+ ovrTrackingState trackingState =
+ ovr_GetTrackingState(mSession, predictedFrameTime, true);
+ ovrPoseStatef& pose(trackingState.HeadPose);
+
+ aState.sensorState.timestamp = pose.TimeInSeconds;
+
+ if (trackingState.StatusFlags & ovrStatus_OrientationTracked) {
+ aState.sensorState.flags |= VRDisplayCapabilityFlags::Cap_Orientation;
+
+ aState.sensorState.pose.orientation[0] = pose.ThePose.Orientation.x;
+ aState.sensorState.pose.orientation[1] = pose.ThePose.Orientation.y;
+ aState.sensorState.pose.orientation[2] = pose.ThePose.Orientation.z;
+ aState.sensorState.pose.orientation[3] = pose.ThePose.Orientation.w;
+
+ aState.sensorState.pose.angularVelocity[0] = pose.AngularVelocity.x;
+ aState.sensorState.pose.angularVelocity[1] = pose.AngularVelocity.y;
+ aState.sensorState.pose.angularVelocity[2] = pose.AngularVelocity.z;
+
+ aState.sensorState.flags |=
+ VRDisplayCapabilityFlags::Cap_AngularAcceleration;
+
+ aState.sensorState.pose.angularAcceleration[0] = pose.AngularAcceleration.x;
+ aState.sensorState.pose.angularAcceleration[1] = pose.AngularAcceleration.y;
+ aState.sensorState.pose.angularAcceleration[2] = pose.AngularAcceleration.z;
+ } else {
+ // default to an identity quaternion
+ aState.sensorState.pose.orientation[3] = 1.0f;
+ }
+
+ if (trackingState.StatusFlags & ovrStatus_PositionTracked) {
+ float eyeHeight =
+ ovr_GetFloat(mSession, OVR_KEY_EYE_HEIGHT, OVR_DEFAULT_EYE_HEIGHT);
+ aState.sensorState.flags |= VRDisplayCapabilityFlags::Cap_Position;
+
+ aState.sensorState.pose.position[0] = pose.ThePose.Position.x;
+ aState.sensorState.pose.position[1] = pose.ThePose.Position.y - eyeHeight;
+ aState.sensorState.pose.position[2] = pose.ThePose.Position.z;
+
+ aState.sensorState.pose.linearVelocity[0] = pose.LinearVelocity.x;
+ aState.sensorState.pose.linearVelocity[1] = pose.LinearVelocity.y;
+ aState.sensorState.pose.linearVelocity[2] = pose.LinearVelocity.z;
+
+ aState.sensorState.flags |=
+ VRDisplayCapabilityFlags::Cap_LinearAcceleration;
+
+ aState.sensorState.pose.linearAcceleration[0] = pose.LinearAcceleration.x;
+ aState.sensorState.pose.linearAcceleration[1] = pose.LinearAcceleration.y;
+ aState.sensorState.pose.linearAcceleration[2] = pose.LinearAcceleration.z;
+ }
+ aState.sensorState.flags |= VRDisplayCapabilityFlags::Cap_External;
+ aState.sensorState.flags |= VRDisplayCapabilityFlags::Cap_MountDetection;
+ aState.sensorState.flags |= VRDisplayCapabilityFlags::Cap_Present;
+}
+
+void OculusSession::UpdateControllers(VRSystemState& aState) {
+ if (!mSession) {
+ return;
+ }
+
+ ovrInputState inputState;
+ bool hasInputState = ovr_GetInputState(mSession, ovrControllerType_Touch,
+ &inputState) == ovrSuccess;
+
+ if (!hasInputState) {
+ return;
+ }
+
+ EnumerateControllers(aState, inputState);
+ UpdateControllerInputs(aState, inputState);
+ UpdateControllerPose(aState, inputState);
+}
+
+void OculusSession::UpdateControllerPose(VRSystemState& aState,
+ const ovrInputState& aInputState) {
+ ovrTrackingState trackingState = ovr_GetTrackingState(mSession, 0.0, false);
+ for (uint32_t handIdx = 0; handIdx < 2; handIdx++) {
+ // Left Touch Controller will always be at index 0 and
+ // and Right Touch Controller will always be at index 1
+ VRControllerState& controllerState = aState.controllerState[handIdx];
+ if (aInputState.ControllerType & OculusControllerTypes[handIdx]) {
+ ovrPoseStatef& pose = trackingState.HandPoses[handIdx];
+ bool bNewController = !(controllerState.flags &
+ dom::GamepadCapabilityFlags::Cap_Orientation);
+ if (bNewController) {
+ controllerState.flags |= dom::GamepadCapabilityFlags::Cap_Orientation;
+ controllerState.flags |= dom::GamepadCapabilityFlags::Cap_Position;
+ controllerState.flags |=
+ dom::GamepadCapabilityFlags::Cap_AngularAcceleration;
+ controllerState.flags |=
+ dom::GamepadCapabilityFlags::Cap_LinearAcceleration;
+ controllerState.flags |=
+ dom::GamepadCapabilityFlags::Cap_GripSpacePosition;
+ }
+
+ if (bNewController || trackingState.HandStatusFlags[handIdx] &
+ ovrStatus_OrientationTracked) {
+ controllerState.pose.orientation[0] = pose.ThePose.Orientation.x;
+ controllerState.pose.orientation[1] = pose.ThePose.Orientation.y;
+ controllerState.pose.orientation[2] = pose.ThePose.Orientation.z;
+ controllerState.pose.orientation[3] = pose.ThePose.Orientation.w;
+ controllerState.pose.angularVelocity[0] = pose.AngularVelocity.x;
+ controllerState.pose.angularVelocity[1] = pose.AngularVelocity.y;
+ controllerState.pose.angularVelocity[2] = pose.AngularVelocity.z;
+ controllerState.pose.angularAcceleration[0] =
+ pose.AngularAcceleration.x;
+ controllerState.pose.angularAcceleration[1] =
+ pose.AngularAcceleration.y;
+ controllerState.pose.angularAcceleration[2] =
+ pose.AngularAcceleration.z;
+ controllerState.isOrientationValid = true;
+ } else {
+ controllerState.isOrientationValid = false;
+ }
+ if (bNewController ||
+ trackingState.HandStatusFlags[handIdx] & ovrStatus_PositionTracked) {
+ controllerState.pose.position[0] = pose.ThePose.Position.x;
+ controllerState.pose.position[1] = pose.ThePose.Position.y;
+ controllerState.pose.position[2] = pose.ThePose.Position.z;
+ controllerState.pose.linearVelocity[0] = pose.LinearVelocity.x;
+ controllerState.pose.linearVelocity[1] = pose.LinearVelocity.y;
+ controllerState.pose.linearVelocity[2] = pose.LinearVelocity.z;
+ controllerState.pose.linearAcceleration[0] = pose.LinearAcceleration.x;
+ controllerState.pose.linearAcceleration[1] = pose.LinearAcceleration.y;
+ controllerState.pose.linearAcceleration[2] = pose.LinearAcceleration.z;
+
+ float eyeHeight =
+ ovr_GetFloat(mSession, OVR_KEY_EYE_HEIGHT, OVR_DEFAULT_EYE_HEIGHT);
+ controllerState.pose.position[1] -= eyeHeight;
+ controllerState.isPositionValid = true;
+ } else {
+ controllerState.isPositionValid = false;
+ }
+ controllerState.targetRayPose = controllerState.pose;
+ }
+ }
+}
+
+void OculusSession::EnumerateControllers(VRSystemState& aState,
+ const ovrInputState& aInputState) {
+ for (uint32_t handIdx = 0; handIdx < 2; handIdx++) {
+ // Left Touch Controller will always be at index 0 and
+ // and Right Touch Controller will always be at index 1
+ VRControllerState& controllerState = aState.controllerState[handIdx];
+ if (aInputState.ControllerType & OculusControllerTypes[handIdx]) {
+ bool bNewController = false;
+ // Touch Controller detected
+ if (controllerState.controllerName[0] == '\0') {
+ // Controller has been just enumerated
+ strncpy(controllerState.controllerName, OculusControllerNames[handIdx],
+ kVRControllerNameMaxLen);
+ controllerState.hand = OculusControllerHand[handIdx];
+ controllerState.targetRayMode = gfx::TargetRayMode::TrackedPointer;
+ controllerState.numButtons = kNumOculusButtons;
+ controllerState.numAxes = kNumOculusAxes;
+ controllerState.numHaptics = kNumOculusHaptcs;
+ controllerState.type = VRControllerType::OculusTouch;
+ bNewController = true;
+ }
+ } else {
+ // Touch Controller not detected
+ if (controllerState.controllerName[0] != '\0') {
+ // Clear any newly disconnected ontrollers
+ memset(&controllerState, 0, sizeof(VRControllerState));
+ }
+ }
+ }
+}
+
+void OculusSession::UpdateControllerInputs(VRSystemState& aState,
+ const ovrInputState& aInputState) {
+ const float triggerThreshold =
+ StaticPrefs::dom_vr_controller_trigger_threshold();
+
+ for (uint32_t handIdx = 0; handIdx < 2; handIdx++) {
+ // Left Touch Controller will always be at index 0 and
+ // and Right Touch Controller will always be at index 1
+ VRControllerState& controllerState = aState.controllerState[handIdx];
+ if (aInputState.ControllerType & OculusControllerTypes[handIdx]) {
+ // Update Button States
+ controllerState.buttonPressed = 0;
+ controllerState.buttonTouched = 0;
+ uint32_t buttonIdx = 0;
+
+ // Button 0: Trigger
+ VRSession::UpdateTrigger(controllerState, buttonIdx,
+ aInputState.IndexTrigger[handIdx],
+ triggerThreshold);
+ ++buttonIdx;
+ // Button 1: Grip
+ VRSession::UpdateTrigger(controllerState, buttonIdx,
+ aInputState.HandTrigger[handIdx],
+ triggerThreshold);
+ ++buttonIdx;
+ // Button 2: a placeholder button for trackpad.
+ UpdateButton(aInputState, handIdx, buttonIdx, controllerState);
+ ++buttonIdx;
+ // Button 3: Thumbstick
+ UpdateButton(aInputState, handIdx, buttonIdx, controllerState);
+ ++buttonIdx;
+ // Button 4: A
+ UpdateButton(aInputState, handIdx, buttonIdx, controllerState);
+ ++buttonIdx;
+ // Button 5: B
+ UpdateButton(aInputState, handIdx, buttonIdx, controllerState);
+ ++buttonIdx;
+ // Button 6: ThumbRest
+ UpdateButton(aInputState, handIdx, buttonIdx, controllerState);
+ ++buttonIdx;
+
+ MOZ_ASSERT(buttonIdx == kNumOculusButtons);
+
+ // Update Thumbstick axis
+ uint32_t axisIdx = 0;
+ // Axis 0, 1: placeholder axes for trackpad.
+ axisIdx += 2;
+
+ // Axis 2, 3: placeholder axes for thumbstick.
+ float axisValue = aInputState.Thumbstick[handIdx].x;
+ if (abs(axisValue) < 0.0000009f) {
+ axisValue = 0.0f; // Clear noise signal
+ }
+ controllerState.axisValue[axisIdx] = axisValue;
+ axisIdx++;
+
+ // Note that y axis is intentionally inverted!
+ axisValue = -aInputState.Thumbstick[handIdx].y;
+ if (abs(axisValue) < 0.0000009f) {
+ axisValue = 0.0f; // Clear noise signal
+ }
+ controllerState.axisValue[axisIdx] = axisValue;
+ axisIdx++;
+
+ MOZ_ASSERT(axisIdx == kNumOculusAxes);
+ }
+ SetControllerSelectionAndSqueezeFrameId(
+ controllerState, aState.displayState.lastSubmittedFrameId);
+ }
+}
+
+void OculusSession::UpdateTelemetry(VRSystemState& aSystemState) {
+ if (!mSession) {
+ return;
+ }
+ ovrPerfStats perfStats;
+ if (ovr_GetPerfStats(mSession, &perfStats) == ovrSuccess) {
+ if (perfStats.FrameStatsCount) {
+ aSystemState.displayState.droppedFrameCount =
+ perfStats.FrameStats[0].AppDroppedFrameCount;
+ }
+ }
+}
+
+void OculusSession::VibrateHaptic(uint32_t aControllerIdx,
+ uint32_t aHapticIndex, float aIntensity,
+ float aDuration) {
+ if (!mSession) {
+ return;
+ }
+
+ if (aDuration <= 0.0f) {
+ StopVibrateHaptic(aControllerIdx);
+ return;
+ }
+
+ // Vibration amplitude in the [0.0, 1.0] range
+ MOZ_ASSERT(aControllerIdx >= 0 && aControllerIdx <= 1);
+ mHapticPulseIntensity[aControllerIdx] = aIntensity > 1.0 ? 1.0 : aIntensity;
+ mRemainingVibrateTime[aControllerIdx] = aDuration;
+ ovrControllerType hand = OculusControllerTypes[aControllerIdx];
+
+ // The gamepad extensions API does not yet have independent control
+ // of frequency and amplitude. We are always sending 0.0f (160hz)
+ // to the frequency argument.
+ ovrResult result = ovr_SetControllerVibration(
+ mSession, hand, 0.0f, mHapticPulseIntensity[aControllerIdx]);
+ if (result != ovrSuccess) {
+ // This may happen if called when not presenting.
+ gfxWarning() << "ovr_SetControllerVibration failed.";
+ }
+}
+
+void OculusSession::StopVibrateHaptic(uint32_t aControllerIdx) {
+ if (!mSession) {
+ return;
+ }
+ MOZ_ASSERT(aControllerIdx >= 0 && aControllerIdx <= 1);
+ ovrControllerType hand = OculusControllerTypes[aControllerIdx];
+ mRemainingVibrateTime[aControllerIdx] = 0.0f;
+ mHapticPulseIntensity[aControllerIdx] = 0.0f;
+
+ ovrResult result = ovr_SetControllerVibration(mSession, hand, 0.0f, 0.0f);
+ if (result != ovrSuccess) {
+ // This may happen if called when not presenting.
+ gfxWarning() << "ovr_SetControllerVibration failed.";
+ }
+}
+
+void OculusSession::StopAllHaptics() {
+ // Left Oculus Touch
+ StopVibrateHaptic(0);
+ // Right Oculus Touch
+ StopVibrateHaptic(1);
+}
+
+void OculusSession::UpdateHaptics() {
+ if (!mSession) {
+ return;
+ }
+ // The Oculus API and hardware takes at least 33ms to respond
+ // to haptic state changes, so it is not beneficial to create
+ // a dedicated haptic feedback thread and update multiple
+ // times per frame.
+ // If we wish to support more accurate effects with sub-frame timing,
+ // we should use the buffered haptic feedback API's.
+
+ TimeStamp now = TimeStamp::Now();
+ if (mLastHapticUpdate.IsNull()) {
+ mLastHapticUpdate = now;
+ return;
+ }
+ float deltaTime = (float)(now - mLastHapticUpdate).ToSeconds();
+ mLastHapticUpdate = now;
+ for (int i = 0; i < 2; i++) {
+ if (mRemainingVibrateTime[i] <= 0.0f) {
+ continue;
+ }
+ mRemainingVibrateTime[i] -= deltaTime;
+ ovrControllerType hand = OculusControllerTypes[i];
+ if (mRemainingVibrateTime[i] > 0.0f) {
+ ovrResult result = ovr_SetControllerVibration(mSession, hand, 0.0f,
+ mHapticPulseIntensity[i]);
+ if (result != ovrSuccess) {
+ // This may happen if called when not presenting.
+ gfxWarning() << "ovr_SetControllerVibration failed.";
+ }
+ } else {
+ StopVibrateHaptic(i);
+ }
+ }
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/vr/service/OculusSession.h b/gfx/vr/service/OculusSession.h
new file mode 100644
index 0000000000..e1feeb2052
--- /dev/null
+++ b/gfx/vr/service/OculusSession.h
@@ -0,0 +1,114 @@
+/* -*- 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_SERVICE_OCULUSSESSION_H
+#define GFX_VR_SERVICE_OCULUSSESSION_H
+
+#include "VRSession.h"
+
+#include "mozilla/gfx/2D.h"
+#include "moz_external_vr.h"
+#include "nsTArray.h"
+#include "oculus/ovr_capi_dynamic.h"
+#include "prlink.h"
+#include "ShaderDefinitionsD3D11.h" // for VertexShaderConstants and PixelShaderConstants
+
+struct ID3D11Device;
+
+namespace mozilla {
+namespace layers {
+struct VertexShaderConstants;
+struct PixelShaderConstants;
+} // namespace layers
+namespace gfx {
+
+class OculusSession : public VRSession {
+ public:
+ OculusSession();
+ virtual ~OculusSession();
+
+ bool Initialize(mozilla::gfx::VRSystemState& aSystemState,
+ bool aDetectRuntimesOnly) override;
+ void Shutdown() override;
+ void ProcessEvents(mozilla::gfx::VRSystemState& aSystemState) override;
+ void StartFrame(mozilla::gfx::VRSystemState& aSystemState) override;
+ bool StartPresentation() override;
+ void StopPresentation() override;
+ bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer,
+ ID3D11Texture2D* aTexture) override;
+ void VibrateHaptic(uint32_t aControllerIdx, uint32_t aHapticIndex,
+ float aIntensity, float aDuration) override;
+ void StopVibrateHaptic(uint32_t aControllerIdx) override;
+ void StopAllHaptics() override;
+
+ private:
+ bool LoadOvrLib();
+ void UnloadOvrLib();
+ bool StartLib(ovrInitFlags aFlags);
+ void StopLib();
+ bool StartSession();
+ void StopSession();
+ bool StartRendering();
+ void StopRendering();
+ bool CreateD3DObjects();
+ bool CreateShaders();
+ void DestroyShaders();
+ void CoverTransitions();
+ void UpdateVisibility();
+ bool ChangeVisibility(bool bVisible);
+ bool InitState(mozilla::gfx::VRSystemState& aSystemState);
+ void UpdateStageParameters(mozilla::gfx::VRDisplayState& aState);
+ void UpdateEyeParameters(mozilla::gfx::VRSystemState& aState);
+ void UpdateHeadsetPose(mozilla::gfx::VRSystemState& aState);
+ void UpdateControllers(VRSystemState& aState);
+ void UpdateControllerInputs(VRSystemState& aState,
+ const ovrInputState& aInputState);
+ void UpdateHaptics();
+ void EnumerateControllers(VRSystemState& aState,
+ const ovrInputState& aInputState);
+ void UpdateControllerPose(VRSystemState& aState,
+ const ovrInputState& aInputState);
+ void UpdateTelemetry(VRSystemState& aSystemState);
+ bool IsPresentationReady() const;
+ bool UpdateConstantBuffers();
+
+ PRLibrary* mOvrLib;
+ ovrSession mSession;
+ ovrInitFlags mInitFlags;
+ ovrTextureSwapChain mTextureSet;
+ nsTArray<RefPtr<ID3D11RenderTargetView>> mRTView;
+ nsTArray<RefPtr<ID3D11Texture2D>> mTexture;
+ nsTArray<RefPtr<ID3D11ShaderResourceView>> mSRV;
+
+ ID3D11VertexShader* mQuadVS;
+ ID3D11PixelShader* mQuadPS;
+ RefPtr<ID3D11SamplerState> mLinearSamplerState;
+ layers::VertexShaderConstants mVSConstants;
+ layers::PixelShaderConstants mPSConstants;
+ RefPtr<ID3D11Buffer> mVSConstantBuffer;
+ RefPtr<ID3D11Buffer> mPSConstantBuffer;
+ RefPtr<ID3D11Buffer> mVertexBuffer;
+ RefPtr<ID3D11InputLayout> mInputLayout;
+
+ IntSize mPresentationSize;
+ ovrFovPort mFOVPort[2];
+
+ // Most recent HMD eye poses, from start of frame
+ ovrPosef mFrameStartPose[2];
+
+ float mRemainingVibrateTime[2];
+ float mHapticPulseIntensity[2];
+ TimeStamp mLastHapticUpdate;
+
+ // The timestamp of the last ending presentation
+ TimeStamp mLastPresentationEnd;
+ bool mIsPresenting;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // GFX_VR_SERVICE_OCULUSSESSION_H
diff --git a/gfx/vr/service/OpenVRControllerMapper.cpp b/gfx/vr/service/OpenVRControllerMapper.cpp
new file mode 100644
index 0000000000..7cb4cfb563
--- /dev/null
+++ b/gfx/vr/service/OpenVRControllerMapper.cpp
@@ -0,0 +1,89 @@
+/* -*- 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 "OpenVRControllerMapper.h"
+#include "mozilla/StaticPrefs_dom.h"
+
+#include "VRSession.h"
+
+namespace mozilla::gfx {
+
+OpenVRControllerMapper::OpenVRControllerMapper()
+ : mNumButtons(0), mNumAxes(0) {}
+
+void OpenVRControllerMapper::GetButtonValueFromAction(
+ VRControllerState& aControllerState, const ControllerAction& aPressAction,
+ const ControllerAction& aTouchAction) {
+ vr::InputDigitalActionData_t actionData = {};
+ bool bPressed = false;
+ bool bTouched = false;
+ uint64_t mask = 0;
+
+ if (aPressAction.handle &&
+ vr::VRInput()->GetDigitalActionData(
+ aPressAction.handle, &actionData, sizeof(actionData),
+ vr::k_ulInvalidInputValueHandle) == vr::VRInputError_None &&
+ actionData.bActive) {
+ bPressed = actionData.bState;
+ mask = (1ULL << mNumButtons);
+ aControllerState.triggerValue[mNumButtons] = bPressed ? 1.0 : 0.0f;
+ if (bPressed) {
+ aControllerState.buttonPressed |= mask;
+ } else {
+ aControllerState.buttonPressed &= ~mask;
+ }
+ if (aTouchAction.handle &&
+ vr::VRInput()->GetDigitalActionData(
+ aTouchAction.handle, &actionData, sizeof(actionData),
+ vr::k_ulInvalidInputValueHandle) == vr::VRInputError_None) {
+ bTouched = actionData.bActive && actionData.bState;
+ mask = (1ULL << mNumButtons);
+ if (bTouched) {
+ aControllerState.buttonTouched |= mask;
+ } else {
+ aControllerState.buttonTouched &= ~mask;
+ }
+ }
+ ++mNumButtons;
+ }
+}
+
+void OpenVRControllerMapper::GetTriggerValueFromAction(
+ VRControllerState& aControllerState, const ControllerAction& aAction) {
+ vr::InputAnalogActionData_t analogData = {};
+ const float triggerThreshold =
+ StaticPrefs::dom_vr_controller_trigger_threshold();
+
+ if (aAction.handle &&
+ vr::VRInput()->GetAnalogActionData(
+ aAction.handle, &analogData, sizeof(analogData),
+ vr::k_ulInvalidInputValueHandle) == vr::VRInputError_None &&
+ analogData.bActive) {
+ VRSession::UpdateTrigger(aControllerState, mNumButtons, analogData.x,
+ triggerThreshold);
+ ++mNumButtons;
+ }
+}
+
+void OpenVRControllerMapper::GetAxisValueFromAction(
+ VRControllerState& aControllerState, const ControllerAction& aAction,
+ bool aInvertAxis) {
+ vr::InputAnalogActionData_t analogData = {};
+ const float yAxisInvert = (aInvertAxis) ? -1.0f : 1.0f;
+
+ if (aAction.handle &&
+ vr::VRInput()->GetAnalogActionData(
+ aAction.handle, &analogData, sizeof(analogData),
+ vr::k_ulInvalidInputValueHandle) == vr::VRInputError_None &&
+ analogData.bActive) {
+ aControllerState.axisValue[mNumAxes] = analogData.x;
+ ++mNumAxes;
+ aControllerState.axisValue[mNumAxes] = analogData.y * yAxisInvert;
+ ++mNumAxes;
+ }
+}
+
+} // namespace mozilla::gfx
diff --git a/gfx/vr/service/OpenVRControllerMapper.h b/gfx/vr/service/OpenVRControllerMapper.h
new file mode 100644
index 0000000000..0fe96c8bbe
--- /dev/null
+++ b/gfx/vr/service/OpenVRControllerMapper.h
@@ -0,0 +1,96 @@
+/* -*- 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_SERVICE_OPENVRCONTROLLERMAPPER_H
+#define GFX_VR_SERVICE_OPENVRCONTROLLERMAPPER_H
+
+#include "openvr.h"
+#include "nsString.h"
+
+#include "moz_external_vr.h"
+
+namespace mozilla {
+namespace gfx {
+
+struct ControllerAction {
+ nsCString name;
+ nsCString type;
+ vr::VRActionHandle_t handle = vr::k_ulInvalidActionHandle;
+
+ ControllerAction() = default;
+
+ ControllerAction(const char* aName, const char* aType)
+ : name(aName), type(aType) {}
+};
+
+struct ControllerInfo {
+ vr::VRInputValueHandle_t mSource = vr::k_ulInvalidInputValueHandle;
+
+ ControllerAction mActionPose;
+ ControllerAction mActionHaptic;
+
+ ControllerAction mActionTrackpad_Analog;
+ ControllerAction mActionTrackpad_Pressed;
+ ControllerAction mActionTrackpad_Touched;
+
+ ControllerAction mActionTrigger_Value;
+
+ ControllerAction mActionGrip_Pressed;
+ ControllerAction mActionGrip_Touched;
+ ControllerAction mActionMenu_Pressed;
+ ControllerAction mActionMenu_Touched;
+ // It seems like there's no way to get response from a sys. btn.
+ ControllerAction mActionSystem_Pressed;
+ ControllerAction mActionSystem_Touched;
+
+ // --- Knuckles & Cosmos
+ ControllerAction mActionA_Pressed;
+ ControllerAction mActionA_Touched;
+ ControllerAction mActionB_Pressed;
+ ControllerAction mActionB_Touched;
+
+ // --- Knuckles, Cosmos, and WMR
+ ControllerAction mActionThumbstick_Analog;
+ ControllerAction mActionThumbstick_Pressed;
+ ControllerAction mActionThumbstick_Touched;
+
+ // --- Knuckles
+ ControllerAction mActionFingerIndex_Value;
+ ControllerAction mActionFingerMiddle_Value;
+ ControllerAction mActionFingerRing_Value;
+ ControllerAction mActionFingerPinky_Value;
+
+ // --- Cosmos
+ ControllerAction mActionBumper_Pressed;
+};
+
+class OpenVRControllerMapper {
+ public:
+ OpenVRControllerMapper();
+ virtual ~OpenVRControllerMapper() = default;
+
+ virtual void UpdateButtons(VRControllerState& aControllerState,
+ ControllerInfo& aControllerInfo) = 0;
+ uint32_t GetButtonCount() { return mNumButtons; }
+ uint32_t GetAxisCount() { return mNumAxes; }
+
+ protected:
+ void GetButtonValueFromAction(VRControllerState& aControllerState,
+ const ControllerAction& aPressAction,
+ const ControllerAction& aTouchAction);
+ void GetTriggerValueFromAction(VRControllerState& aControllerState,
+ const ControllerAction& aAction);
+ void GetAxisValueFromAction(VRControllerState& aControllerState,
+ const ControllerAction& aAction,
+ bool aInvertAxis = false);
+ uint32_t mNumButtons;
+ uint32_t mNumAxes;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // GFX_VR_SERVICE_OPENVRCONTROLLERMAPPER_H
diff --git a/gfx/vr/service/OpenVRCosmosMapper.cpp b/gfx/vr/service/OpenVRCosmosMapper.cpp
new file mode 100644
index 0000000000..8c5c87253d
--- /dev/null
+++ b/gfx/vr/service/OpenVRCosmosMapper.cpp
@@ -0,0 +1,51 @@
+/* -*- 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 "OpenVRCosmosMapper.h"
+
+#include "moz_external_vr.h"
+#include "VRSession.h"
+
+namespace mozilla::gfx {
+
+void OpenVRCosmosMapper::UpdateButtons(VRControllerState& aControllerState,
+ ControllerInfo& aControllerInfo) {
+ mNumButtons = mNumAxes = 0;
+ // Button 0: Trigger
+ GetTriggerValueFromAction(aControllerState,
+ aControllerInfo.mActionTrigger_Value);
+ // Button 1: Grip
+ GetButtonValueFromAction(aControllerState,
+ aControllerInfo.mActionGrip_Pressed,
+ aControllerInfo.mActionGrip_Touched);
+ // Button 2: a placeholder button for trackpad.
+ ++mNumButtons;
+ // Button 3: Thumbstick
+ GetButtonValueFromAction(aControllerState,
+ aControllerInfo.mActionThumbstick_Pressed,
+ aControllerInfo.mActionThumbstick_Touched);
+ // Button 4: A
+ GetButtonValueFromAction(aControllerState, aControllerInfo.mActionA_Pressed,
+ aControllerInfo.mActionA_Touched);
+ // Button 5: B
+ GetButtonValueFromAction(aControllerState, aControllerInfo.mActionB_Pressed,
+ aControllerInfo.mActionB_Touched);
+ // Button 6: Bumper
+ GetButtonValueFromAction(aControllerState,
+ aControllerInfo.mActionBumper_Pressed,
+ aControllerInfo.mActionBumper_Pressed);
+
+ // Axis 0, 1: placeholder axes for touchpad.
+ mNumAxes += 2;
+ // Axis 2, 3: Thumbstick
+ GetAxisValueFromAction(aControllerState,
+ aControllerInfo.mActionThumbstick_Analog);
+
+ aControllerState.numButtons = mNumButtons;
+ aControllerState.numAxes = mNumAxes;
+}
+
+} // namespace mozilla::gfx
diff --git a/gfx/vr/service/OpenVRCosmosMapper.h b/gfx/vr/service/OpenVRCosmosMapper.h
new file mode 100644
index 0000000000..3796cd1c6d
--- /dev/null
+++ b/gfx/vr/service/OpenVRCosmosMapper.h
@@ -0,0 +1,26 @@
+/* -*- 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_SERVICE_OPENVRCOSMOSMAPPER_H
+#define GFX_VR_SERVICE_OPENVRCOSMOSMAPPER_H
+
+#include "OpenVRControllerMapper.h"
+
+namespace mozilla {
+namespace gfx {
+
+class OpenVRCosmosMapper : public OpenVRControllerMapper {
+ public:
+ OpenVRCosmosMapper() = default;
+ virtual ~OpenVRCosmosMapper() = default;
+ virtual void UpdateButtons(VRControllerState& aControllerState,
+ ControllerInfo& aControllerInfo);
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // GFX_VR_SERVICE_OPENVRCOSMOSMAPPER_H
diff --git a/gfx/vr/service/OpenVRDefaultMapper.cpp b/gfx/vr/service/OpenVRDefaultMapper.cpp
new file mode 100644
index 0000000000..ac65369475
--- /dev/null
+++ b/gfx/vr/service/OpenVRDefaultMapper.cpp
@@ -0,0 +1,70 @@
+/* -*- 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 "OpenVRDefaultMapper.h"
+
+#include "moz_external_vr.h"
+#include "VRSession.h"
+
+namespace mozilla::gfx {
+
+void OpenVRDefaultMapper::UpdateButtons(VRControllerState& aControllerState,
+ ControllerInfo& aControllerInfo) {
+ mNumButtons = mNumAxes = 0;
+ // Button 0: Trigger
+ GetTriggerValueFromAction(aControllerState,
+ aControllerInfo.mActionTrigger_Value);
+ // Button 1: Grip
+ GetButtonValueFromAction(aControllerState,
+ aControllerInfo.mActionGrip_Pressed,
+ aControllerInfo.mActionGrip_Touched);
+ // Button 2: Touchpad.
+ GetButtonValueFromAction(aControllerState,
+ aControllerInfo.mActionTrackpad_Pressed,
+ aControllerInfo.mActionTrackpad_Touched);
+ // Button 3: Thumbstick
+ GetButtonValueFromAction(aControllerState,
+ aControllerInfo.mActionThumbstick_Pressed,
+ aControllerInfo.mActionThumbstick_Touched);
+ // Button 4: Menu
+ GetButtonValueFromAction(aControllerState,
+ aControllerInfo.mActionMenu_Pressed,
+ aControllerInfo.mActionMenu_Touched);
+ // Button 5: A
+ GetButtonValueFromAction(aControllerState, aControllerInfo.mActionA_Pressed,
+ aControllerInfo.mActionA_Touched);
+ // Button 6: B
+ GetButtonValueFromAction(aControllerState, aControllerInfo.mActionB_Pressed,
+ aControllerInfo.mActionB_Touched);
+ // Button 7: Bumper
+ GetButtonValueFromAction(aControllerState,
+ aControllerInfo.mActionBumper_Pressed,
+ aControllerInfo.mActionBumper_Pressed);
+ // Button 8: Finger index
+ GetTriggerValueFromAction(aControllerState,
+ aControllerInfo.mActionFingerIndex_Value);
+ // Button 9: Finger middle
+ GetTriggerValueFromAction(aControllerState,
+ aControllerInfo.mActionFingerMiddle_Value);
+ // Button 10: Finger ring
+ GetTriggerValueFromAction(aControllerState,
+ aControllerInfo.mActionFingerRing_Value);
+ // Button 11: Finger pinky
+ GetTriggerValueFromAction(aControllerState,
+ aControllerInfo.mActionFingerPinky_Value);
+
+ // Axis 0, 1: Touchpad
+ GetAxisValueFromAction(aControllerState,
+ aControllerInfo.mActionTrackpad_Analog);
+ // Axis 2, 3: Thumbstick
+ GetAxisValueFromAction(aControllerState,
+ aControllerInfo.mActionThumbstick_Analog);
+
+ aControllerState.numButtons = mNumButtons;
+ aControllerState.numAxes = mNumAxes;
+}
+
+} // namespace mozilla::gfx
diff --git a/gfx/vr/service/OpenVRDefaultMapper.h b/gfx/vr/service/OpenVRDefaultMapper.h
new file mode 100644
index 0000000000..0478a90687
--- /dev/null
+++ b/gfx/vr/service/OpenVRDefaultMapper.h
@@ -0,0 +1,26 @@
+/* -*- 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_SERVICE_OPENVRDEFAULTMAPPER_H
+#define GFX_VR_SERVICE_OPENVRDEFAULTMAPPER_H
+
+#include "OpenVRControllerMapper.h"
+
+namespace mozilla {
+namespace gfx {
+
+class OpenVRDefaultMapper : public OpenVRControllerMapper {
+ public:
+ OpenVRDefaultMapper() = default;
+ virtual ~OpenVRDefaultMapper() = default;
+ virtual void UpdateButtons(VRControllerState& aControllerState,
+ ControllerInfo& aControllerInfo);
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // GFX_VR_SERVICE_OPENVRDEFAULTMAPPER_H
diff --git a/gfx/vr/service/OpenVRKnucklesMapper.cpp b/gfx/vr/service/OpenVRKnucklesMapper.cpp
new file mode 100644
index 0000000000..74f38c820c
--- /dev/null
+++ b/gfx/vr/service/OpenVRKnucklesMapper.cpp
@@ -0,0 +1,62 @@
+/* -*- 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 "OpenVRKnucklesMapper.h"
+
+#include "moz_external_vr.h"
+#include "VRSession.h"
+
+namespace mozilla::gfx {
+
+void OpenVRKnucklesMapper::UpdateButtons(VRControllerState& aControllerState,
+ ControllerInfo& aControllerInfo) {
+ mNumButtons = mNumAxes = 0;
+ // Button 0: Trigger
+ GetTriggerValueFromAction(aControllerState,
+ aControllerInfo.mActionTrigger_Value);
+ // Button 1: Grip
+ GetButtonValueFromAction(aControllerState,
+ aControllerInfo.mActionGrip_Pressed,
+ aControllerInfo.mActionGrip_Touched);
+ // Button 2: Touchpad.
+ GetButtonValueFromAction(aControllerState,
+ aControllerInfo.mActionTrackpad_Pressed,
+ aControllerInfo.mActionTrackpad_Touched);
+ // Button 3: Thumbstick
+ GetButtonValueFromAction(aControllerState,
+ aControllerInfo.mActionThumbstick_Pressed,
+ aControllerInfo.mActionThumbstick_Touched);
+ // Button 4: A
+ GetButtonValueFromAction(aControllerState, aControllerInfo.mActionA_Pressed,
+ aControllerInfo.mActionA_Touched);
+ // Button 5: B
+ GetButtonValueFromAction(aControllerState, aControllerInfo.mActionB_Pressed,
+ aControllerInfo.mActionB_Touched);
+ // Button 6: Finger index
+ GetTriggerValueFromAction(aControllerState,
+ aControllerInfo.mActionFingerIndex_Value);
+ // Button 7: Finger middle
+ GetTriggerValueFromAction(aControllerState,
+ aControllerInfo.mActionFingerMiddle_Value);
+ // Button 8: Finger ring
+ GetTriggerValueFromAction(aControllerState,
+ aControllerInfo.mActionFingerRing_Value);
+ // Button 9: Finger pinky
+ GetTriggerValueFromAction(aControllerState,
+ aControllerInfo.mActionFingerPinky_Value);
+
+ // Axis 0, 1: Touchpad
+ GetAxisValueFromAction(aControllerState,
+ aControllerInfo.mActionTrackpad_Analog);
+ // Axis 2, 3: Thumbstick
+ GetAxisValueFromAction(aControllerState,
+ aControllerInfo.mActionThumbstick_Analog);
+
+ aControllerState.numButtons = mNumButtons;
+ aControllerState.numAxes = mNumAxes;
+}
+
+} // namespace mozilla::gfx
diff --git a/gfx/vr/service/OpenVRKnucklesMapper.h b/gfx/vr/service/OpenVRKnucklesMapper.h
new file mode 100644
index 0000000000..45168e7749
--- /dev/null
+++ b/gfx/vr/service/OpenVRKnucklesMapper.h
@@ -0,0 +1,26 @@
+/* -*- 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_SERVICE_OPENVRKNUCKLESMAPPER_H
+#define GFX_VR_SERVICE_OPENVRKNUCKLESMAPPER_H
+
+#include "OpenVRControllerMapper.h"
+
+namespace mozilla {
+namespace gfx {
+
+class OpenVRKnucklesMapper : public OpenVRControllerMapper {
+ public:
+ OpenVRKnucklesMapper() = default;
+ virtual ~OpenVRKnucklesMapper() = default;
+ virtual void UpdateButtons(VRControllerState& aControllerState,
+ ControllerInfo& aControllerInfo);
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // GFX_VR_SERVICE_OPENVRKNUCKLESMAPPER_H
diff --git a/gfx/vr/service/OpenVRSession.cpp b/gfx/vr/service/OpenVRSession.cpp
new file mode 100644
index 0000000000..7a50561ffd
--- /dev/null
+++ b/gfx/vr/service/OpenVRSession.cpp
@@ -0,0 +1,1480 @@
+/* -*- 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 <fstream>
+#include "mozilla/JSONWriter.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "nsIThread.h"
+#include "nsString.h"
+
+#include "OpenVRSession.h"
+#include "mozilla/StaticPrefs_dom.h"
+
+#if defined(XP_WIN)
+# include <d3d11.h>
+# include "mozilla/gfx/DeviceManagerDx.h"
+#elif defined(XP_MACOSX)
+# include "mozilla/gfx/MacIOSurface.h"
+#endif
+
+#if !defined(XP_WIN)
+# include <sys/stat.h> // for umask()
+#endif
+
+#include "mozilla/dom/GamepadEventTypes.h"
+#include "mozilla/dom/GamepadBinding.h"
+#include "binding/OpenVRCosmosBinding.h"
+#include "binding/OpenVRKnucklesBinding.h"
+#include "binding/OpenVRViveBinding.h"
+#include "OpenVRCosmosMapper.h"
+#include "OpenVRDefaultMapper.h"
+#include "OpenVRKnucklesMapper.h"
+#include "OpenVRViveMapper.h"
+#if defined(XP_WIN) // Windows Mixed Reality is only available in Windows.
+# include "OpenVRWMRMapper.h"
+# include "binding/OpenVRWMRBinding.h"
+#endif
+
+#include "VRParent.h"
+#include "VRProcessChild.h"
+#include "VRThread.h"
+
+#if !defined(M_PI)
+# define M_PI 3.14159265358979323846264338327950288
+#endif
+
+#define BTN_MASK_FROM_ID(_id) ::vr::ButtonMaskFromId(vr::EVRButtonId::_id)
+
+// Haptic feedback is updated every 5ms, as this is
+// the minimum period between new haptic pulse requests.
+// Effectively, this results in a pulse width modulation
+// with an interval of 5ms. Through experimentation, the
+// maximum duty cycle was found to be about 3.9ms
+const uint32_t kVRHapticUpdateInterval = 5;
+
+using namespace mozilla::gfx;
+
+namespace mozilla::gfx {
+
+namespace {
+
+// This is for controller action file writer.
+struct StringWriteFunc : public JSONWriteFunc {
+ nsACString& mBuffer; // This struct must not outlive this buffer
+
+ explicit StringWriteFunc(nsACString& buffer) : mBuffer(buffer) {}
+
+ void Write(const Span<const char>& aStr) override { mBuffer.Append(aStr); }
+};
+
+class ControllerManifestFile {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ControllerManifestFile)
+
+ public:
+ static already_AddRefed<ControllerManifestFile> CreateManifest() {
+ RefPtr<ControllerManifestFile> manifest = new ControllerManifestFile();
+ return manifest.forget();
+ }
+
+ bool IsExisting() {
+ if (mFileName.IsEmpty() ||
+ !std::ifstream(mFileName.BeginReading()).good()) {
+ return false;
+ }
+ return true;
+ }
+
+ void SetFileName(const char* aName) { mFileName = aName; }
+
+ const char* GetFileName() const { return mFileName.BeginReading(); }
+
+ private:
+ ControllerManifestFile() = default;
+
+ ~ControllerManifestFile() {
+ if (!mFileName.IsEmpty() && remove(mFileName.BeginReading()) != 0) {
+ MOZ_ASSERT(false, "Delete controller manifest file failed.");
+ }
+ mFileName = "";
+ }
+
+ nsCString mFileName;
+};
+
+// We wanna keep these temporary files existing
+// until Firefox is closed instead of following OpenVRSession's lifetime.
+StaticRefPtr<ControllerManifestFile> sCosmosBindingFile;
+StaticRefPtr<ControllerManifestFile> sKnucklesBindingFile;
+StaticRefPtr<ControllerManifestFile> sViveBindingFile;
+#if defined(XP_WIN)
+StaticRefPtr<ControllerManifestFile> sWMRBindingFile;
+#endif
+StaticRefPtr<ControllerManifestFile> sControllerActionFile;
+
+dom::GamepadHand GetControllerHandFromControllerRole(OpenVRHand aRole) {
+ dom::GamepadHand hand;
+ switch (aRole) {
+ case OpenVRHand::None:
+ hand = dom::GamepadHand::_empty;
+ break;
+ case OpenVRHand::Left:
+ hand = dom::GamepadHand::Left;
+ break;
+ case OpenVRHand::Right:
+ hand = dom::GamepadHand::Right;
+ break;
+ default:
+ hand = dom::GamepadHand::_empty;
+ MOZ_ASSERT(false);
+ break;
+ }
+
+ return hand;
+}
+
+bool FileIsExisting(const nsCString& aPath) {
+ if (aPath.IsEmpty() || !std::ifstream(aPath.BeginReading()).good()) {
+ return false;
+ }
+ return true;
+}
+
+}; // anonymous namespace
+
+#if defined(XP_WIN)
+bool GenerateTempFileName(nsCString& aPath) {
+ TCHAR tempPathBuffer[MAX_PATH];
+ TCHAR tempFileName[MAX_PATH];
+
+ // Gets the temp path env string (no guarantee it's a valid path).
+ DWORD dwRetVal = GetTempPath(MAX_PATH, tempPathBuffer);
+ if (dwRetVal > MAX_PATH || (dwRetVal == 0)) {
+ NS_WARNING("OpenVR - Creating temp path failed.");
+ return false;
+ }
+
+ // Generates a temporary file name.
+ UINT uRetVal = GetTempFileName(tempPathBuffer, // directory for tmp files
+ TEXT("mozvr"), // temp file name prefix
+ 0, // create unique name
+ tempFileName); // buffer for name
+ if (uRetVal == 0) {
+ NS_WARNING("OpenVR - Creating temp file failed.");
+ return false;
+ }
+
+ aPath.Assign(NS_ConvertUTF16toUTF8(tempFileName));
+ return true;
+}
+#else
+bool GenerateTempFileName(nsCString& aPath) {
+ const char tmp[] = "/tmp/mozvrXXXXXX";
+ char fileName[PATH_MAX];
+
+ strcpy(fileName, tmp);
+ const mode_t prevMask = umask(S_IXUSR | S_IRWXO | S_IRWXG);
+ const int fd = mkstemp(fileName);
+ umask(prevMask);
+ if (fd == -1) {
+ NS_WARNING(nsPrintfCString("OpenVR - Creating temp file failed: %s",
+ strerror(errno))
+ .get());
+ return false;
+ }
+ close(fd);
+
+ aPath.Assign(fileName);
+ return true;
+}
+#endif // defined(XP_WIN)
+
+OpenVRSession::OpenVRSession()
+ : VRSession(),
+ mVRSystem(nullptr),
+ mVRChaperone(nullptr),
+ mVRCompositor(nullptr),
+ mHapticPulseRemaining{},
+ mHapticPulseIntensity{},
+ mIsWindowsMR(false),
+ mControllerHapticStateMutex(
+ "OpenVRSession::mControllerHapticStateMutex") {
+ std::fill_n(mControllerDeviceIndex, kVRControllerMaxCount, OpenVRHand::None);
+}
+
+OpenVRSession::~OpenVRSession() {
+ mActionsetFirefox = ::vr::k_ulInvalidActionSetHandle;
+ Shutdown();
+}
+
+bool OpenVRSession::Initialize(mozilla::gfx::VRSystemState& aSystemState,
+ bool aDetectRuntimesOnly) {
+ if (StaticPrefs::dom_vr_puppet_enabled()) {
+ // Ensure that tests using the VR Puppet do not find real hardware
+ return false;
+ }
+ if (!StaticPrefs::dom_vr_enabled() || !StaticPrefs::dom_vr_openvr_enabled()) {
+ return false;
+ }
+ if (mVRSystem != nullptr) {
+ // Already initialized
+ return true;
+ }
+ if (!::vr::VR_IsRuntimeInstalled()) {
+ return false;
+ }
+ if (aDetectRuntimesOnly) {
+ aSystemState.displayState.capabilityFlags |=
+ VRDisplayCapabilityFlags::Cap_ImmersiveVR;
+ return false;
+ }
+ if (!::vr::VR_IsHmdPresent()) {
+ return false;
+ }
+
+ ::vr::HmdError err;
+
+ ::vr::VR_Init(&err, ::vr::EVRApplicationType::VRApplication_Scene);
+ if (err) {
+ return false;
+ }
+
+ mVRSystem = (::vr::IVRSystem*)::vr::VR_GetGenericInterface(
+ ::vr::IVRSystem_Version, &err);
+ if (err || !mVRSystem) {
+ Shutdown();
+ return false;
+ }
+ mVRChaperone = (::vr::IVRChaperone*)::vr::VR_GetGenericInterface(
+ ::vr::IVRChaperone_Version, &err);
+ if (err || !mVRChaperone) {
+ Shutdown();
+ return false;
+ }
+ mVRCompositor = (::vr::IVRCompositor*)::vr::VR_GetGenericInterface(
+ ::vr::IVRCompositor_Version, &err);
+ if (err || !mVRCompositor) {
+ Shutdown();
+ return false;
+ }
+
+#if defined(XP_WIN)
+ if (!CreateD3DObjects()) {
+ Shutdown();
+ return false;
+ }
+
+#endif
+
+ // Configure coordinate system
+ mVRCompositor->SetTrackingSpace(::vr::TrackingUniverseSeated);
+
+ if (!InitState(aSystemState)) {
+ Shutdown();
+ return false;
+ }
+ if (!SetupContollerActions()) {
+ return false;
+ }
+
+ // Succeeded
+ return true;
+}
+
+// "actions": [] Action paths must take the form: "/actions/<action
+// set>/in|out/<action>"
+#define CreateControllerAction(hand, name, type) \
+ ControllerAction("/actions/firefox/in/" #hand "Hand_" #name, #type)
+#define CreateControllerOutAction(hand, name, type) \
+ ControllerAction("/actions/firefox/out/" #hand "Hand_" #name, #type)
+
+bool OpenVRSession::SetupContollerActions() {
+ if (!vr::VRInput()) {
+ NS_WARNING("OpenVR - vr::VRInput() is null.");
+ return false;
+ }
+
+ // Check if this device binding file has been created.
+ // If it didn't exist yet, create a new temp file.
+ nsCString controllerAction;
+ nsCString viveManifest;
+ nsCString WMRManifest;
+ nsCString knucklesManifest;
+ nsCString cosmosManifest;
+
+ // Getting / Generating manifest file paths.
+ if (StaticPrefs::dom_vr_process_enabled_AtStartup()) {
+ VRParent* vrParent = VRProcessChild::GetVRParent();
+ nsCString output;
+
+ if (vrParent->GetOpenVRControllerActionPath(&output)) {
+ controllerAction = output;
+ }
+
+ if (vrParent->GetOpenVRControllerManifestPath(VRControllerType::HTCVive,
+ &output)) {
+ viveManifest = output;
+ }
+ if (!viveManifest.Length() || !FileIsExisting(viveManifest)) {
+ if (!GenerateTempFileName(viveManifest)) {
+ return false;
+ }
+ OpenVRViveBinding viveBinding;
+ std::ofstream viveBindingFile(viveManifest.BeginReading());
+ if (viveBindingFile.is_open()) {
+ viveBindingFile << viveBinding.binding;
+ viveBindingFile.close();
+ }
+ }
+
+#if defined(XP_WIN)
+ if (vrParent->GetOpenVRControllerManifestPath(VRControllerType::MSMR,
+ &output)) {
+ WMRManifest = output;
+ }
+ if (!WMRManifest.Length() || !FileIsExisting(WMRManifest)) {
+ if (!GenerateTempFileName(WMRManifest)) {
+ return false;
+ }
+ OpenVRWMRBinding WMRBinding;
+ std::ofstream WMRBindingFile(WMRManifest.BeginReading());
+ if (WMRBindingFile.is_open()) {
+ WMRBindingFile << WMRBinding.binding;
+ WMRBindingFile.close();
+ }
+ }
+#endif
+ if (vrParent->GetOpenVRControllerManifestPath(VRControllerType::ValveIndex,
+ &output)) {
+ knucklesManifest = output;
+ }
+ if (!knucklesManifest.Length() || !FileIsExisting(knucklesManifest)) {
+ if (!GenerateTempFileName(knucklesManifest)) {
+ return false;
+ }
+ OpenVRKnucklesBinding knucklesBinding;
+ std::ofstream knucklesBindingFile(knucklesManifest.BeginReading());
+ if (knucklesBindingFile.is_open()) {
+ knucklesBindingFile << knucklesBinding.binding;
+ knucklesBindingFile.close();
+ }
+ }
+ if (vrParent->GetOpenVRControllerManifestPath(
+ VRControllerType::HTCViveCosmos, &output)) {
+ cosmosManifest = output;
+ }
+ if (!cosmosManifest.Length() || !FileIsExisting(cosmosManifest)) {
+ if (!GenerateTempFileName(cosmosManifest)) {
+ return false;
+ }
+ OpenVRCosmosBinding cosmosBinding;
+ std::ofstream cosmosBindingFile(cosmosManifest.BeginReading());
+ if (cosmosBindingFile.is_open()) {
+ cosmosBindingFile << cosmosBinding.binding;
+ cosmosBindingFile.close();
+ }
+ }
+ } else {
+ // Without using VR process
+ if (!sControllerActionFile) {
+ sControllerActionFile = ControllerManifestFile::CreateManifest();
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "ClearOnShutdown ControllerManifestFile",
+ []() { ClearOnShutdown(&sControllerActionFile); }));
+ }
+ controllerAction = sControllerActionFile->GetFileName();
+
+ if (!sViveBindingFile) {
+ sViveBindingFile = ControllerManifestFile::CreateManifest();
+ NS_DispatchToMainThread(
+ NS_NewRunnableFunction("ClearOnShutdown ControllerManifestFile",
+ []() { ClearOnShutdown(&sViveBindingFile); }));
+ }
+ if (!sViveBindingFile->IsExisting()) {
+ nsCString viveBindingPath;
+ if (!GenerateTempFileName(viveBindingPath)) {
+ return false;
+ }
+ sViveBindingFile->SetFileName(viveBindingPath.BeginReading());
+ OpenVRViveBinding viveBinding;
+ std::ofstream viveBindingFile(sViveBindingFile->GetFileName());
+ if (viveBindingFile.is_open()) {
+ viveBindingFile << viveBinding.binding;
+ viveBindingFile.close();
+ }
+ }
+ viveManifest = sViveBindingFile->GetFileName();
+
+ if (!sKnucklesBindingFile) {
+ sKnucklesBindingFile = ControllerManifestFile::CreateManifest();
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "ClearOnShutdown ControllerManifestFile",
+ []() { ClearOnShutdown(&sKnucklesBindingFile); }));
+ }
+ if (!sKnucklesBindingFile->IsExisting()) {
+ nsCString knucklesBindingPath;
+ if (!GenerateTempFileName(knucklesBindingPath)) {
+ return false;
+ }
+ sKnucklesBindingFile->SetFileName(knucklesBindingPath.BeginReading());
+ OpenVRKnucklesBinding knucklesBinding;
+ std::ofstream knucklesBindingFile(sKnucklesBindingFile->GetFileName());
+ if (knucklesBindingFile.is_open()) {
+ knucklesBindingFile << knucklesBinding.binding;
+ knucklesBindingFile.close();
+ }
+ }
+ knucklesManifest = sKnucklesBindingFile->GetFileName();
+
+ if (!sCosmosBindingFile) {
+ sCosmosBindingFile = ControllerManifestFile::CreateManifest();
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "ClearOnShutdown ControllerManifestFile",
+ []() { ClearOnShutdown(&sCosmosBindingFile); }));
+ }
+ if (!sCosmosBindingFile->IsExisting()) {
+ nsCString cosmosBindingPath;
+ if (!GenerateTempFileName(cosmosBindingPath)) {
+ return false;
+ }
+ sCosmosBindingFile->SetFileName(cosmosBindingPath.BeginReading());
+ OpenVRCosmosBinding cosmosBinding;
+ std::ofstream cosmosBindingFile(sCosmosBindingFile->GetFileName());
+ if (cosmosBindingFile.is_open()) {
+ cosmosBindingFile << cosmosBinding.binding;
+ cosmosBindingFile.close();
+ }
+ }
+ cosmosManifest = sCosmosBindingFile->GetFileName();
+#if defined(XP_WIN)
+ if (!sWMRBindingFile) {
+ sWMRBindingFile = ControllerManifestFile::CreateManifest();
+ NS_DispatchToMainThread(
+ NS_NewRunnableFunction("ClearOnShutdown ControllerManifestFile",
+ []() { ClearOnShutdown(&sWMRBindingFile); }));
+ }
+ if (!sWMRBindingFile->IsExisting()) {
+ nsCString WMRBindingPath;
+ if (!GenerateTempFileName(WMRBindingPath)) {
+ return false;
+ }
+ sWMRBindingFile->SetFileName(WMRBindingPath.BeginReading());
+ OpenVRWMRBinding WMRBinding;
+ std::ofstream WMRBindingFile(sWMRBindingFile->GetFileName());
+ if (WMRBindingFile.is_open()) {
+ WMRBindingFile << WMRBinding.binding;
+ WMRBindingFile.close();
+ }
+ }
+ WMRManifest = sWMRBindingFile->GetFileName();
+#endif
+ }
+ // End of Getting / Generating manifest file paths.
+
+ // Setup controller actions.
+ ControllerInfo leftContollerInfo;
+ leftContollerInfo.mActionPose = CreateControllerAction(L, pose, pose);
+ leftContollerInfo.mActionTrackpad_Analog =
+ CreateControllerAction(L, trackpad_analog, vector2);
+ leftContollerInfo.mActionTrackpad_Pressed =
+ CreateControllerAction(L, trackpad_pressed, boolean);
+ leftContollerInfo.mActionTrackpad_Touched =
+ CreateControllerAction(L, trackpad_touched, boolean);
+ leftContollerInfo.mActionTrigger_Value =
+ CreateControllerAction(L, trigger_value, vector1);
+ leftContollerInfo.mActionGrip_Pressed =
+ CreateControllerAction(L, grip_pressed, boolean);
+ leftContollerInfo.mActionGrip_Touched =
+ CreateControllerAction(L, grip_touched, boolean);
+ leftContollerInfo.mActionMenu_Pressed =
+ CreateControllerAction(L, menu_pressed, boolean);
+ leftContollerInfo.mActionMenu_Touched =
+ CreateControllerAction(L, menu_touched, boolean);
+ leftContollerInfo.mActionSystem_Pressed =
+ CreateControllerAction(L, system_pressed, boolean);
+ leftContollerInfo.mActionSystem_Touched =
+ CreateControllerAction(L, system_touched, boolean);
+ leftContollerInfo.mActionA_Pressed =
+ CreateControllerAction(L, A_pressed, boolean);
+ leftContollerInfo.mActionA_Touched =
+ CreateControllerAction(L, A_touched, boolean);
+ leftContollerInfo.mActionB_Pressed =
+ CreateControllerAction(L, B_pressed, boolean);
+ leftContollerInfo.mActionB_Touched =
+ CreateControllerAction(L, B_touched, boolean);
+ leftContollerInfo.mActionThumbstick_Analog =
+ CreateControllerAction(L, thumbstick_analog, vector2);
+ leftContollerInfo.mActionThumbstick_Pressed =
+ CreateControllerAction(L, thumbstick_pressed, boolean);
+ leftContollerInfo.mActionThumbstick_Touched =
+ CreateControllerAction(L, thumbstick_touched, boolean);
+ leftContollerInfo.mActionFingerIndex_Value =
+ CreateControllerAction(L, finger_index_value, vector1);
+ leftContollerInfo.mActionFingerMiddle_Value =
+ CreateControllerAction(L, finger_middle_value, vector1);
+ leftContollerInfo.mActionFingerRing_Value =
+ CreateControllerAction(L, finger_ring_value, vector1);
+ leftContollerInfo.mActionFingerPinky_Value =
+ CreateControllerAction(L, finger_pinky_value, vector1);
+ leftContollerInfo.mActionBumper_Pressed =
+ CreateControllerAction(L, bumper_pressed, boolean);
+ leftContollerInfo.mActionHaptic =
+ CreateControllerOutAction(L, haptic, vibration);
+
+ ControllerInfo rightContollerInfo;
+ rightContollerInfo.mActionPose = CreateControllerAction(R, pose, pose);
+ rightContollerInfo.mActionTrackpad_Analog =
+ CreateControllerAction(R, trackpad_analog, vector2);
+ rightContollerInfo.mActionTrackpad_Pressed =
+ CreateControllerAction(R, trackpad_pressed, boolean);
+ rightContollerInfo.mActionTrackpad_Touched =
+ CreateControllerAction(R, trackpad_touched, boolean);
+ rightContollerInfo.mActionTrigger_Value =
+ CreateControllerAction(R, trigger_value, vector1);
+ rightContollerInfo.mActionGrip_Pressed =
+ CreateControllerAction(R, grip_pressed, boolean);
+ rightContollerInfo.mActionGrip_Touched =
+ CreateControllerAction(R, grip_touched, boolean);
+ rightContollerInfo.mActionMenu_Pressed =
+ CreateControllerAction(R, menu_pressed, boolean);
+ rightContollerInfo.mActionMenu_Touched =
+ CreateControllerAction(R, menu_touched, boolean);
+ rightContollerInfo.mActionSystem_Pressed =
+ CreateControllerAction(R, system_pressed, boolean);
+ rightContollerInfo.mActionSystem_Touched =
+ CreateControllerAction(R, system_touched, boolean);
+ rightContollerInfo.mActionA_Pressed =
+ CreateControllerAction(R, A_pressed, boolean);
+ rightContollerInfo.mActionA_Touched =
+ CreateControllerAction(R, A_touched, boolean);
+ rightContollerInfo.mActionB_Pressed =
+ CreateControllerAction(R, B_pressed, boolean);
+ rightContollerInfo.mActionB_Touched =
+ CreateControllerAction(R, B_touched, boolean);
+ rightContollerInfo.mActionThumbstick_Analog =
+ CreateControllerAction(R, thumbstick_analog, vector2);
+ rightContollerInfo.mActionThumbstick_Pressed =
+ CreateControllerAction(R, thumbstick_pressed, boolean);
+ rightContollerInfo.mActionThumbstick_Touched =
+ CreateControllerAction(R, thumbstick_touched, boolean);
+ rightContollerInfo.mActionFingerIndex_Value =
+ CreateControllerAction(R, finger_index_value, vector1);
+ rightContollerInfo.mActionFingerMiddle_Value =
+ CreateControllerAction(R, finger_middle_value, vector1);
+ rightContollerInfo.mActionFingerRing_Value =
+ CreateControllerAction(R, finger_ring_value, vector1);
+ rightContollerInfo.mActionFingerPinky_Value =
+ CreateControllerAction(R, finger_pinky_value, vector1);
+ rightContollerInfo.mActionBumper_Pressed =
+ CreateControllerAction(R, bumper_pressed, boolean);
+ rightContollerInfo.mActionHaptic =
+ CreateControllerOutAction(R, haptic, vibration);
+
+ mControllerHand[OpenVRHand::Left] = leftContollerInfo;
+ mControllerHand[OpenVRHand::Right] = rightContollerInfo;
+
+ if (!controllerAction.Length() || !FileIsExisting(controllerAction)) {
+ if (!GenerateTempFileName(controllerAction)) {
+ return false;
+ }
+ nsCString actionData;
+ JSONWriter actionWriter(MakeUnique<StringWriteFunc>(actionData));
+ actionWriter.Start();
+
+ actionWriter.StringProperty("version",
+ "0.1.0"); // TODO: adding a version check.
+ // "default_bindings": []
+ actionWriter.StartArrayProperty("default_bindings");
+
+ auto SetupActionWriterByControllerType = [&](const char* aType,
+ const nsCString& aManifest) {
+ actionWriter.StartObjectElement();
+ actionWriter.StringProperty("controller_type", MakeStringSpan(aType));
+ actionWriter.StringProperty("binding_url", aManifest);
+ actionWriter.EndObject();
+ };
+ SetupActionWriterByControllerType("vive_controller", viveManifest);
+ SetupActionWriterByControllerType("knuckles", knucklesManifest);
+ SetupActionWriterByControllerType("vive_cosmos_controller", cosmosManifest);
+#if defined(XP_WIN)
+ SetupActionWriterByControllerType("holographic_controller", WMRManifest);
+#endif
+ actionWriter.EndArray(); // End "default_bindings": []
+
+ actionWriter.StartArrayProperty("actions");
+
+ for (auto& controller : mControllerHand) {
+ auto SetActionsToWriter = [&](const ControllerAction& aAction) {
+ actionWriter.StartObjectElement();
+ actionWriter.StringProperty("name", aAction.name);
+ actionWriter.StringProperty("type", aAction.type);
+ actionWriter.EndObject();
+ };
+
+ SetActionsToWriter(controller.mActionPose);
+ SetActionsToWriter(controller.mActionTrackpad_Analog);
+ SetActionsToWriter(controller.mActionTrackpad_Pressed);
+ SetActionsToWriter(controller.mActionTrackpad_Touched);
+ SetActionsToWriter(controller.mActionTrigger_Value);
+ SetActionsToWriter(controller.mActionGrip_Pressed);
+ SetActionsToWriter(controller.mActionGrip_Touched);
+ SetActionsToWriter(controller.mActionMenu_Pressed);
+ SetActionsToWriter(controller.mActionMenu_Touched);
+ SetActionsToWriter(controller.mActionSystem_Pressed);
+ SetActionsToWriter(controller.mActionSystem_Touched);
+ SetActionsToWriter(controller.mActionA_Pressed);
+ SetActionsToWriter(controller.mActionA_Touched);
+ SetActionsToWriter(controller.mActionB_Pressed);
+ SetActionsToWriter(controller.mActionB_Touched);
+ SetActionsToWriter(controller.mActionThumbstick_Analog);
+ SetActionsToWriter(controller.mActionThumbstick_Pressed);
+ SetActionsToWriter(controller.mActionThumbstick_Touched);
+ SetActionsToWriter(controller.mActionFingerIndex_Value);
+ SetActionsToWriter(controller.mActionFingerMiddle_Value);
+ SetActionsToWriter(controller.mActionFingerRing_Value);
+ SetActionsToWriter(controller.mActionFingerPinky_Value);
+ SetActionsToWriter(controller.mActionBumper_Pressed);
+ SetActionsToWriter(controller.mActionHaptic);
+ }
+ actionWriter.EndArray(); // End "actions": []
+ actionWriter.End();
+
+ std::ofstream actionfile(controllerAction.BeginReading());
+ nsCString actionResult(actionData.get());
+ if (actionfile.is_open()) {
+ actionfile << actionResult.get();
+ actionfile.close();
+ }
+ }
+
+ vr::EVRInputError err =
+ vr::VRInput()->SetActionManifestPath(controllerAction.BeginReading());
+ if (err != vr::VRInputError_None) {
+ NS_WARNING("OpenVR - SetActionManifestPath failed.");
+ return false;
+ }
+ // End of setup controller actions.
+
+ // Notify the parent process these manifest files are already been recorded.
+ if (StaticPrefs::dom_vr_process_enabled_AtStartup()) {
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "SendOpenVRControllerActionPathToParent",
+ [controllerAction, viveManifest, WMRManifest, knucklesManifest,
+ cosmosManifest]() {
+ VRParent* vrParent = VRProcessChild::GetVRParent();
+ Unused << vrParent->SendOpenVRControllerActionPathToParent(
+ controllerAction);
+ Unused << vrParent->SendOpenVRControllerManifestPathToParent(
+ VRControllerType::HTCVive, viveManifest);
+ Unused << vrParent->SendOpenVRControllerManifestPathToParent(
+ VRControllerType::MSMR, WMRManifest);
+ Unused << vrParent->SendOpenVRControllerManifestPathToParent(
+ VRControllerType::ValveIndex, knucklesManifest);
+ Unused << vrParent->SendOpenVRControllerManifestPathToParent(
+ VRControllerType::HTCViveCosmos, cosmosManifest);
+ }));
+ } else {
+ sControllerActionFile->SetFileName(controllerAction.BeginReading());
+ }
+
+ return true;
+}
+
+#if defined(XP_WIN)
+bool OpenVRSession::CreateD3DObjects() {
+ RefPtr<ID3D11Device> device = gfx::DeviceManagerDx::Get()->GetVRDevice();
+ if (!device) {
+ return false;
+ }
+ if (!CreateD3DContext(device)) {
+ return false;
+ }
+ return true;
+}
+#endif
+
+void OpenVRSession::Shutdown() {
+ StopHapticTimer();
+ StopHapticThread();
+ if (mVRSystem || mVRCompositor || mVRChaperone) {
+ ::vr::VR_Shutdown();
+ mVRCompositor = nullptr;
+ mVRChaperone = nullptr;
+ mVRSystem = nullptr;
+ }
+}
+
+bool OpenVRSession::InitState(VRSystemState& aSystemState) {
+ VRDisplayState& state = aSystemState.displayState;
+ strncpy(state.displayName, "OpenVR HMD", kVRDisplayNameMaxLen);
+ state.eightCC = GFX_VR_EIGHTCC('O', 'p', 'e', 'n', 'V', 'R', ' ', ' ');
+ state.isConnected =
+ mVRSystem->IsTrackedDeviceConnected(::vr::k_unTrackedDeviceIndex_Hmd);
+ state.isMounted = false;
+ state.capabilityFlags = (VRDisplayCapabilityFlags)(
+ (int)VRDisplayCapabilityFlags::Cap_None |
+ (int)VRDisplayCapabilityFlags::Cap_Orientation |
+ (int)VRDisplayCapabilityFlags::Cap_Position |
+ (int)VRDisplayCapabilityFlags::Cap_External |
+ (int)VRDisplayCapabilityFlags::Cap_Present |
+ (int)VRDisplayCapabilityFlags::Cap_StageParameters |
+ (int)VRDisplayCapabilityFlags::Cap_ImmersiveVR);
+ state.blendMode = VRDisplayBlendMode::Opaque;
+ state.reportsDroppedFrames = true;
+
+ ::vr::ETrackedPropertyError err;
+ bool bHasProximitySensor = mVRSystem->GetBoolTrackedDeviceProperty(
+ ::vr::k_unTrackedDeviceIndex_Hmd, ::vr::Prop_ContainsProximitySensor_Bool,
+ &err);
+ if (err == ::vr::TrackedProp_Success && bHasProximitySensor) {
+ state.capabilityFlags = (VRDisplayCapabilityFlags)(
+ (int)state.capabilityFlags |
+ (int)VRDisplayCapabilityFlags::Cap_MountDetection);
+ }
+
+ uint32_t w, h;
+ mVRSystem->GetRecommendedRenderTargetSize(&w, &h);
+ state.eyeResolution.width = w;
+ state.eyeResolution.height = h;
+ state.nativeFramebufferScaleFactor = 1.0f;
+
+ // default to an identity quaternion
+ aSystemState.sensorState.pose.orientation[3] = 1.0f;
+
+ UpdateStageParameters(state);
+ UpdateEyeParameters(aSystemState);
+
+ VRHMDSensorState& sensorState = aSystemState.sensorState;
+ sensorState.flags = (VRDisplayCapabilityFlags)(
+ (int)VRDisplayCapabilityFlags::Cap_Orientation |
+ (int)VRDisplayCapabilityFlags::Cap_Position);
+ sensorState.pose.orientation[3] = 1.0f; // Default to an identity quaternion
+
+ return true;
+}
+
+void OpenVRSession::UpdateStageParameters(VRDisplayState& aState) {
+ float sizeX = 0.0f;
+ float sizeZ = 0.0f;
+ if (mVRChaperone->GetPlayAreaSize(&sizeX, &sizeZ)) {
+ ::vr::HmdMatrix34_t t =
+ mVRSystem->GetSeatedZeroPoseToStandingAbsoluteTrackingPose();
+ aState.stageSize.width = sizeX;
+ aState.stageSize.height = sizeZ;
+
+ aState.sittingToStandingTransform[0] = t.m[0][0];
+ aState.sittingToStandingTransform[1] = t.m[1][0];
+ aState.sittingToStandingTransform[2] = t.m[2][0];
+ aState.sittingToStandingTransform[3] = 0.0f;
+
+ aState.sittingToStandingTransform[4] = t.m[0][1];
+ aState.sittingToStandingTransform[5] = t.m[1][1];
+ aState.sittingToStandingTransform[6] = t.m[2][1];
+ aState.sittingToStandingTransform[7] = 0.0f;
+
+ aState.sittingToStandingTransform[8] = t.m[0][2];
+ aState.sittingToStandingTransform[9] = t.m[1][2];
+ aState.sittingToStandingTransform[10] = t.m[2][2];
+ aState.sittingToStandingTransform[11] = 0.0f;
+
+ aState.sittingToStandingTransform[12] = t.m[0][3];
+ aState.sittingToStandingTransform[13] = t.m[1][3];
+ aState.sittingToStandingTransform[14] = t.m[2][3];
+ aState.sittingToStandingTransform[15] = 1.0f;
+ } else {
+ // If we fail, fall back to reasonable defaults.
+ // 1m x 1m space, 0.75m high in seated position
+ aState.stageSize.width = 1.0f;
+ aState.stageSize.height = 1.0f;
+
+ aState.sittingToStandingTransform[0] = 1.0f;
+ aState.sittingToStandingTransform[1] = 0.0f;
+ aState.sittingToStandingTransform[2] = 0.0f;
+ aState.sittingToStandingTransform[3] = 0.0f;
+
+ aState.sittingToStandingTransform[4] = 0.0f;
+ aState.sittingToStandingTransform[5] = 1.0f;
+ aState.sittingToStandingTransform[6] = 0.0f;
+ aState.sittingToStandingTransform[7] = 0.0f;
+
+ aState.sittingToStandingTransform[8] = 0.0f;
+ aState.sittingToStandingTransform[9] = 0.0f;
+ aState.sittingToStandingTransform[10] = 1.0f;
+ aState.sittingToStandingTransform[11] = 0.0f;
+
+ aState.sittingToStandingTransform[12] = 0.0f;
+ aState.sittingToStandingTransform[13] = 0.75f;
+ aState.sittingToStandingTransform[14] = 0.0f;
+ aState.sittingToStandingTransform[15] = 1.0f;
+ }
+}
+
+void OpenVRSession::UpdateEyeParameters(VRSystemState& aState) {
+ // This must be called every frame in order to
+ // account for continuous adjustments to ipd.
+ gfx::Matrix4x4 headToEyeTransforms[2];
+
+ for (uint32_t eye = 0; eye < 2; ++eye) {
+ ::vr::HmdMatrix34_t eyeToHead =
+ mVRSystem->GetEyeToHeadTransform(static_cast<::vr::Hmd_Eye>(eye));
+ aState.displayState.eyeTranslation[eye].x = eyeToHead.m[0][3];
+ aState.displayState.eyeTranslation[eye].y = eyeToHead.m[1][3];
+ aState.displayState.eyeTranslation[eye].z = eyeToHead.m[2][3];
+
+ float left, right, up, down;
+ mVRSystem->GetProjectionRaw(static_cast<::vr::Hmd_Eye>(eye), &left, &right,
+ &up, &down);
+ aState.displayState.eyeFOV[eye].upDegrees = atan(-up) * 180.0 / M_PI;
+ aState.displayState.eyeFOV[eye].rightDegrees = atan(right) * 180.0 / M_PI;
+ aState.displayState.eyeFOV[eye].downDegrees = atan(down) * 180.0 / M_PI;
+ aState.displayState.eyeFOV[eye].leftDegrees = atan(-left) * 180.0 / M_PI;
+
+ Matrix4x4 pose;
+ // NOTE! eyeToHead.m is a 3x4 matrix, not 4x4. But
+ // because of its arrangement, we can copy the 12 elements in and
+ // then transpose them to the right place.
+ memcpy(&pose._11, &eyeToHead.m, sizeof(eyeToHead.m));
+ pose.Transpose();
+ pose.Invert();
+ headToEyeTransforms[eye] = pose;
+ }
+ aState.sensorState.CalcViewMatrices(headToEyeTransforms);
+}
+
+void OpenVRSession::UpdateHeadsetPose(VRSystemState& aState) {
+ const uint32_t posesSize = ::vr::k_unTrackedDeviceIndex_Hmd + 1;
+ ::vr::TrackedDevicePose_t poses[posesSize];
+ // Note: We *must* call WaitGetPoses in order for any rendering to happen at
+ // all.
+ mVRCompositor->WaitGetPoses(poses, posesSize, nullptr, 0);
+
+ ::vr::Compositor_FrameTiming timing;
+ timing.m_nSize = sizeof(::vr::Compositor_FrameTiming);
+ if (mVRCompositor->GetFrameTiming(&timing)) {
+ aState.sensorState.timestamp = timing.m_flSystemTimeInSeconds;
+ } else {
+ // This should not happen, but log it just in case
+ fprintf(stderr, "OpenVR - IVRCompositor::GetFrameTiming failed");
+ }
+
+ if (poses[::vr::k_unTrackedDeviceIndex_Hmd].bDeviceIsConnected &&
+ poses[::vr::k_unTrackedDeviceIndex_Hmd].bPoseIsValid &&
+ poses[::vr::k_unTrackedDeviceIndex_Hmd].eTrackingResult ==
+ ::vr::TrackingResult_Running_OK) {
+ const ::vr::TrackedDevicePose_t& pose =
+ poses[::vr::k_unTrackedDeviceIndex_Hmd];
+
+ gfx::Matrix4x4 m;
+ // NOTE! mDeviceToAbsoluteTracking is a 3x4 matrix, not 4x4. But
+ // because of its arrangement, we can copy the 12 elements in and
+ // then transpose them to the right place. We do this so we can
+ // pull out a Quaternion.
+ memcpy(&m._11, &pose.mDeviceToAbsoluteTracking,
+ sizeof(pose.mDeviceToAbsoluteTracking));
+ m.Transpose();
+
+ gfx::Quaternion rot;
+ rot.SetFromRotationMatrix(m);
+
+ aState.sensorState.flags = (VRDisplayCapabilityFlags)(
+ (int)aState.sensorState.flags |
+ (int)VRDisplayCapabilityFlags::Cap_Orientation);
+ aState.sensorState.pose.orientation[0] = rot.x;
+ aState.sensorState.pose.orientation[1] = rot.y;
+ aState.sensorState.pose.orientation[2] = rot.z;
+ aState.sensorState.pose.orientation[3] = rot.w;
+ aState.sensorState.pose.angularVelocity[0] = pose.vAngularVelocity.v[0];
+ aState.sensorState.pose.angularVelocity[1] = pose.vAngularVelocity.v[1];
+ aState.sensorState.pose.angularVelocity[2] = pose.vAngularVelocity.v[2];
+
+ aState.sensorState.flags =
+ (VRDisplayCapabilityFlags)((int)aState.sensorState.flags |
+ (int)VRDisplayCapabilityFlags::Cap_Position);
+ aState.sensorState.pose.position[0] = m._41;
+ aState.sensorState.pose.position[1] = m._42;
+ aState.sensorState.pose.position[2] = m._43;
+ aState.sensorState.pose.linearVelocity[0] = pose.vVelocity.v[0];
+ aState.sensorState.pose.linearVelocity[1] = pose.vVelocity.v[1];
+ aState.sensorState.pose.linearVelocity[2] = pose.vVelocity.v[2];
+ }
+}
+
+void OpenVRSession::EnumerateControllers(VRSystemState& aState) {
+ MOZ_ASSERT(mVRSystem);
+
+ MutexAutoLock lock(mControllerHapticStateMutex);
+
+ bool controllerPresent[kVRControllerMaxCount] = {false};
+ uint32_t stateIndex = 0;
+ mActionsetFirefox = vr::k_ulInvalidActionSetHandle;
+ VRControllerType controllerType = VRControllerType::_empty;
+
+ if (vr::VRInput()->GetActionSetHandle(
+ "/actions/firefox", &mActionsetFirefox) != vr::VRInputError_None) {
+ return;
+ }
+
+ for (int8_t handIndex = 0; handIndex < OpenVRHand::Total; ++handIndex) {
+ if (handIndex == OpenVRHand::Left) {
+ if (vr::VRInput()->GetInputSourceHandle(
+ "/user/hand/left", &mControllerHand[OpenVRHand::Left].mSource) !=
+ vr::VRInputError_None) {
+ continue;
+ }
+ } else if (handIndex == OpenVRHand::Right) {
+ if (vr::VRInput()->GetInputSourceHandle(
+ "/user/hand/right",
+ &mControllerHand[OpenVRHand::Right].mSource) !=
+ vr::VRInputError_None) {
+ continue;
+ }
+ } else {
+ MOZ_ASSERT(false, "Unknown OpenVR hand type.");
+ }
+
+ vr::InputOriginInfo_t originInfo;
+ if (vr::VRInput()->GetOriginTrackedDeviceInfo(
+ mControllerHand[handIndex].mSource, &originInfo,
+ sizeof(originInfo)) == vr::VRInputError_None &&
+ originInfo.trackedDeviceIndex != vr::k_unTrackedDeviceIndexInvalid &&
+ mVRSystem->IsTrackedDeviceConnected(originInfo.trackedDeviceIndex)) {
+ const ::vr::ETrackedDeviceClass deviceType =
+ mVRSystem->GetTrackedDeviceClass(originInfo.trackedDeviceIndex);
+ if (deviceType != ::vr::TrackedDeviceClass_Controller &&
+ deviceType != ::vr::TrackedDeviceClass_GenericTracker) {
+ continue;
+ }
+
+ if (mControllerDeviceIndex[stateIndex] != handIndex) {
+ VRControllerState& controllerState = aState.controllerState[stateIndex];
+
+ // Get controllers' action handles.
+ auto SetActionsToWriter = [&](ControllerAction& aAction) {
+ vr::VRInput()->GetActionHandle(aAction.name.BeginReading(),
+ &aAction.handle);
+ };
+
+ SetActionsToWriter(mControllerHand[handIndex].mActionPose);
+ SetActionsToWriter(mControllerHand[handIndex].mActionHaptic);
+ SetActionsToWriter(mControllerHand[handIndex].mActionTrackpad_Analog);
+ SetActionsToWriter(mControllerHand[handIndex].mActionTrackpad_Pressed);
+ SetActionsToWriter(mControllerHand[handIndex].mActionTrackpad_Touched);
+ SetActionsToWriter(mControllerHand[handIndex].mActionTrigger_Value);
+ SetActionsToWriter(mControllerHand[handIndex].mActionGrip_Pressed);
+ SetActionsToWriter(mControllerHand[handIndex].mActionGrip_Touched);
+ SetActionsToWriter(mControllerHand[handIndex].mActionMenu_Pressed);
+ SetActionsToWriter(mControllerHand[handIndex].mActionMenu_Touched);
+ SetActionsToWriter(mControllerHand[handIndex].mActionSystem_Pressed);
+ SetActionsToWriter(mControllerHand[handIndex].mActionSystem_Touched);
+ SetActionsToWriter(mControllerHand[handIndex].mActionA_Pressed);
+ SetActionsToWriter(mControllerHand[handIndex].mActionA_Touched);
+ SetActionsToWriter(mControllerHand[handIndex].mActionB_Pressed);
+ SetActionsToWriter(mControllerHand[handIndex].mActionB_Touched);
+ SetActionsToWriter(mControllerHand[handIndex].mActionThumbstick_Analog);
+ SetActionsToWriter(
+ mControllerHand[handIndex].mActionThumbstick_Pressed);
+ SetActionsToWriter(
+ mControllerHand[handIndex].mActionThumbstick_Touched);
+ SetActionsToWriter(mControllerHand[handIndex].mActionFingerIndex_Value);
+ SetActionsToWriter(
+ mControllerHand[handIndex].mActionFingerMiddle_Value);
+ SetActionsToWriter(mControllerHand[handIndex].mActionFingerRing_Value);
+ SetActionsToWriter(mControllerHand[handIndex].mActionFingerPinky_Value);
+ SetActionsToWriter(mControllerHand[handIndex].mActionBumper_Pressed);
+
+ nsCString deviceId;
+ VRControllerType contrlType = VRControllerType::_empty;
+ GetControllerDeviceId(deviceType, originInfo.trackedDeviceIndex,
+ deviceId, contrlType);
+ // Controllers should be the same type with one VR display.
+ MOZ_ASSERT(controllerType == contrlType ||
+ controllerType == VRControllerType::_empty);
+ controllerType = contrlType;
+ strncpy(controllerState.controllerName, deviceId.BeginReading(),
+ kVRControllerNameMaxLen);
+ controllerState.numHaptics = kNumOpenVRHaptics;
+ controllerState.targetRayMode = gfx::TargetRayMode::TrackedPointer;
+ controllerState.type = controllerType;
+ }
+ controllerPresent[stateIndex] = true;
+ mControllerDeviceIndex[stateIndex] = static_cast<OpenVRHand>(handIndex);
+ ++stateIndex;
+ }
+ }
+
+ // Clear out entries for disconnected controllers
+ for (uint32_t stateIndex = 0; stateIndex < kVRControllerMaxCount;
+ stateIndex++) {
+ if (!controllerPresent[stateIndex] &&
+ mControllerDeviceIndex[stateIndex] != OpenVRHand::None) {
+ mControllerDeviceIndex[stateIndex] = OpenVRHand::None;
+ memset(&aState.controllerState[stateIndex], 0, sizeof(VRControllerState));
+ }
+ }
+
+ // Create controller mapper
+ if (controllerType != VRControllerType::_empty) {
+ switch (controllerType) {
+ case VRControllerType::HTCVive:
+ mControllerMapper = MakeUnique<OpenVRViveMapper>();
+ break;
+ case VRControllerType::HTCViveCosmos:
+ mControllerMapper = MakeUnique<OpenVRCosmosMapper>();
+ break;
+#if defined(XP_WIN)
+ case VRControllerType::MSMR:
+ mControllerMapper = MakeUnique<OpenVRWMRMapper>();
+ break;
+#endif
+ case VRControllerType::ValveIndex:
+ mControllerMapper = MakeUnique<OpenVRKnucklesMapper>();
+ break;
+ default:
+ mControllerMapper = MakeUnique<OpenVRDefaultMapper>();
+ NS_WARNING("Undefined controller type");
+ break;
+ }
+ }
+}
+
+void OpenVRSession::UpdateControllerButtons(VRSystemState& aState) {
+ MOZ_ASSERT(mVRSystem);
+
+ for (uint32_t stateIndex = 0; stateIndex < kVRControllerMaxCount;
+ ++stateIndex) {
+ const OpenVRHand role = mControllerDeviceIndex[stateIndex];
+ if (role == OpenVRHand::None) {
+ continue;
+ }
+ VRControllerState& controllerState = aState.controllerState[stateIndex];
+ controllerState.hand = GetControllerHandFromControllerRole(role);
+ mControllerMapper->UpdateButtons(controllerState, mControllerHand[role]);
+ SetControllerSelectionAndSqueezeFrameId(
+ controllerState, aState.displayState.lastSubmittedFrameId);
+ }
+}
+
+void OpenVRSession::UpdateControllerPoses(VRSystemState& aState) {
+ MOZ_ASSERT(mVRSystem);
+
+ for (uint32_t stateIndex = 0; stateIndex < kVRControllerMaxCount;
+ ++stateIndex) {
+ const OpenVRHand role = mControllerDeviceIndex[stateIndex];
+ if (role == OpenVRHand::None) {
+ continue;
+ }
+ VRControllerState& controllerState = aState.controllerState[stateIndex];
+ vr::InputPoseActionData_t poseData;
+ if (vr::VRInput()->GetPoseActionDataRelativeToNow(
+ mControllerHand[role].mActionPose.handle,
+ vr::TrackingUniverseSeated, 0, &poseData, sizeof(poseData),
+ vr::k_ulInvalidInputValueHandle) != vr::VRInputError_None ||
+ !poseData.bActive || !poseData.pose.bPoseIsValid) {
+ controllerState.isOrientationValid = false;
+ controllerState.isPositionValid = false;
+ } else {
+ const ::vr::TrackedDevicePose_t& pose = poseData.pose;
+ if (pose.bDeviceIsConnected) {
+ controllerState.flags =
+ (dom::GamepadCapabilityFlags::Cap_Orientation |
+ dom::GamepadCapabilityFlags::Cap_Position |
+ dom::GamepadCapabilityFlags::Cap_GripSpacePosition);
+ } else {
+ controllerState.flags = dom::GamepadCapabilityFlags::Cap_None;
+ }
+ if (pose.bPoseIsValid &&
+ pose.eTrackingResult == ::vr::TrackingResult_Running_OK) {
+ gfx::Matrix4x4 m;
+
+ // NOTE! mDeviceToAbsoluteTracking is a 3x4 matrix, not 4x4. But
+ // because of its arrangement, we can copy the 12 elements in and
+ // then transpose them to the right place. We do this so we can
+ // pull out a Quaternion.
+ memcpy(&m.components, &pose.mDeviceToAbsoluteTracking,
+ sizeof(pose.mDeviceToAbsoluteTracking));
+ m.Transpose();
+
+ gfx::Quaternion rot;
+ rot.SetFromRotationMatrix(m);
+
+ controllerState.pose.orientation[0] = rot.x;
+ controllerState.pose.orientation[1] = rot.y;
+ controllerState.pose.orientation[2] = rot.z;
+ controllerState.pose.orientation[3] = rot.w;
+ controllerState.pose.angularVelocity[0] = pose.vAngularVelocity.v[0];
+ controllerState.pose.angularVelocity[1] = pose.vAngularVelocity.v[1];
+ controllerState.pose.angularVelocity[2] = pose.vAngularVelocity.v[2];
+ controllerState.pose.angularAcceleration[0] = 0.0f;
+ controllerState.pose.angularAcceleration[1] = 0.0f;
+ controllerState.pose.angularAcceleration[2] = 0.0f;
+ controllerState.isOrientationValid = true;
+
+ controllerState.pose.position[0] = m._41;
+ controllerState.pose.position[1] = m._42;
+ controllerState.pose.position[2] = m._43;
+ controllerState.pose.linearVelocity[0] = pose.vVelocity.v[0];
+ controllerState.pose.linearVelocity[1] = pose.vVelocity.v[1];
+ controllerState.pose.linearVelocity[2] = pose.vVelocity.v[2];
+ controllerState.pose.linearAcceleration[0] = 0.0f;
+ controllerState.pose.linearAcceleration[1] = 0.0f;
+ controllerState.pose.linearAcceleration[2] = 0.0f;
+ controllerState.isPositionValid = true;
+
+ // Calculate its target ray space by shifting degrees in x-axis
+ // for ergonomic.
+ const float kPointerAngleDegrees = -0.698; // 40 degrees.
+ gfx::Matrix4x4 rayMtx(m);
+ rayMtx.RotateX(kPointerAngleDegrees);
+ gfx::Quaternion rayRot;
+ rayRot.SetFromRotationMatrix(rayMtx);
+
+ controllerState.targetRayPose = controllerState.pose;
+ controllerState.targetRayPose.orientation[0] = rayRot.x;
+ controllerState.targetRayPose.orientation[1] = rayRot.y;
+ controllerState.targetRayPose.orientation[2] = rayRot.z;
+ controllerState.targetRayPose.orientation[3] = rayRot.w;
+ controllerState.targetRayPose.position[0] = rayMtx._41;
+ controllerState.targetRayPose.position[1] = rayMtx._42;
+ controllerState.targetRayPose.position[2] = rayMtx._43;
+ }
+ }
+ }
+}
+
+void OpenVRSession::GetControllerDeviceId(
+ ::vr::ETrackedDeviceClass aDeviceType,
+ ::vr::TrackedDeviceIndex_t aDeviceIndex, nsCString& aId,
+ VRControllerType& aControllerType) {
+ switch (aDeviceType) {
+ case ::vr::TrackedDeviceClass_Controller: {
+ ::vr::ETrackedPropertyError err;
+ uint32_t requiredBufferLen;
+ bool isFound = false;
+ char charBuf[128];
+ requiredBufferLen = mVRSystem->GetStringTrackedDeviceProperty(
+ aDeviceIndex, ::vr::Prop_RenderModelName_String, charBuf, 128, &err);
+ if (requiredBufferLen > 128) {
+ MOZ_CRASH("Larger than the buffer size.");
+ }
+ MOZ_ASSERT(requiredBufferLen && err == ::vr::TrackedProp_Success);
+ nsCString deviceId(charBuf);
+ if (deviceId.Find("vr_controller_vive") != kNotFound) {
+ aId.AssignLiteral("OpenVR Gamepad");
+ isFound = true;
+ aControllerType = VRControllerType::HTCVive;
+ } else if (deviceId.Find("knuckles") != kNotFound ||
+ deviceId.Find("valve_controller_knu") != kNotFound) {
+ aId.AssignLiteral("OpenVR Knuckles");
+ isFound = true;
+ aControllerType = VRControllerType::ValveIndex;
+ } else if (deviceId.Find("vive_cosmos_controller") != kNotFound) {
+ aId.AssignLiteral("OpenVR Cosmos");
+ isFound = true;
+ aControllerType = VRControllerType::HTCViveCosmos;
+ }
+ if (!isFound) {
+ requiredBufferLen = mVRSystem->GetStringTrackedDeviceProperty(
+ aDeviceIndex, ::vr::Prop_SerialNumber_String, charBuf, 128, &err);
+ if (requiredBufferLen > 128) {
+ MOZ_CRASH("Larger than the buffer size.");
+ }
+ MOZ_ASSERT(requiredBufferLen && err == ::vr::TrackedProp_Success);
+ deviceId.Assign(charBuf);
+ if (deviceId.Find("MRSOURCE") != kNotFound) {
+ aId.AssignLiteral("Spatial Controller (Spatial Interaction Source) ");
+ mIsWindowsMR = true;
+ isFound = true;
+ aControllerType = VRControllerType::MSMR;
+ }
+ }
+ if (!isFound) {
+ aId.AssignLiteral("OpenVR Undefined");
+ aControllerType = VRControllerType::_empty;
+ }
+ break;
+ }
+ case ::vr::TrackedDeviceClass_GenericTracker: {
+ aId.AssignLiteral("OpenVR Tracker");
+ aControllerType = VRControllerType::_empty;
+ break;
+ }
+ default:
+ MOZ_ASSERT(false);
+ break;
+ }
+}
+
+void OpenVRSession::StartFrame(mozilla::gfx::VRSystemState& aSystemState) {
+ UpdateHeadsetPose(aSystemState);
+ UpdateEyeParameters(aSystemState);
+ EnumerateControllers(aSystemState);
+
+ vr::VRActiveActionSet_t actionSet = {0};
+ actionSet.ulActionSet = mActionsetFirefox;
+ vr::VRInput()->UpdateActionState(&actionSet, sizeof(actionSet), 1);
+ UpdateControllerButtons(aSystemState);
+ UpdateControllerPoses(aSystemState);
+ UpdateTelemetry(aSystemState);
+}
+
+void OpenVRSession::ProcessEvents(mozilla::gfx::VRSystemState& aSystemState) {
+ bool isHmdPresent = ::vr::VR_IsHmdPresent();
+ if (!isHmdPresent) {
+ mShouldQuit = true;
+ }
+
+ ::vr::VREvent_t event;
+ while (mVRSystem && mVRSystem->PollNextEvent(&event, sizeof(event))) {
+ switch (event.eventType) {
+ case ::vr::VREvent_TrackedDeviceUserInteractionStarted:
+ if (event.trackedDeviceIndex == ::vr::k_unTrackedDeviceIndex_Hmd) {
+ aSystemState.displayState.isMounted = true;
+ }
+ break;
+ case ::vr::VREvent_TrackedDeviceUserInteractionEnded:
+ if (event.trackedDeviceIndex == ::vr::k_unTrackedDeviceIndex_Hmd) {
+ aSystemState.displayState.isMounted = false;
+ }
+ break;
+ case ::vr::EVREventType::VREvent_TrackedDeviceActivated:
+ if (event.trackedDeviceIndex == ::vr::k_unTrackedDeviceIndex_Hmd) {
+ aSystemState.displayState.isConnected = true;
+ }
+ break;
+ case ::vr::EVREventType::VREvent_TrackedDeviceDeactivated:
+ if (event.trackedDeviceIndex == ::vr::k_unTrackedDeviceIndex_Hmd) {
+ aSystemState.displayState.isConnected = false;
+ }
+ break;
+ case ::vr::EVREventType::VREvent_DriverRequestedQuit:
+ case ::vr::EVREventType::VREvent_Quit:
+ // When SteamVR runtime haven't been launched before viewing VR,
+ // SteamVR will send a VREvent_ProcessQuit event. It will tell the parent
+ // process to shutdown the VR process, and we need to avoid it.
+ // case ::vr::EVREventType::VREvent_ProcessQuit:
+ case ::vr::EVREventType::VREvent_QuitAcknowledged:
+ mShouldQuit = true;
+ break;
+ default:
+ // ignore
+ break;
+ }
+ }
+}
+
+#if defined(XP_WIN)
+bool OpenVRSession::SubmitFrame(
+ const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer,
+ ID3D11Texture2D* aTexture) {
+ return SubmitFrame((void*)aTexture, ::vr::ETextureType::TextureType_DirectX,
+ aLayer.leftEyeRect, aLayer.rightEyeRect);
+}
+#elif defined(XP_MACOSX)
+bool OpenVRSession::SubmitFrame(
+ const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer,
+ const VRLayerTextureHandle& aTexture) {
+ return SubmitFrame(aTexture, ::vr::ETextureType::TextureType_IOSurface,
+ aLayer.leftEyeRect, aLayer.rightEyeRect);
+}
+#endif
+
+bool OpenVRSession::SubmitFrame(const VRLayerTextureHandle& aTextureHandle,
+ ::vr::ETextureType aTextureType,
+ const VRLayerEyeRect& aLeftEyeRect,
+ const VRLayerEyeRect& aRightEyeRect) {
+ ::vr::Texture_t tex;
+#if defined(XP_MACOSX)
+ // We get aTextureHandle from get_SurfaceDescriptorMacIOSurface() at
+ // VRDisplayExternal. scaleFactor and opaque are skipped because they always
+ // are 1.0 and false.
+ RefPtr<MacIOSurface> surf = MacIOSurface::LookupSurface(aTextureHandle);
+ if (!surf) {
+ NS_WARNING("OpenVRSession::SubmitFrame failed to get a MacIOSurface");
+ return false;
+ }
+
+ CFTypeRefPtr<IOSurfaceRef> ioSurface = surf->GetIOSurfaceRef();
+ tex.handle = (void*)ioSurface.get();
+#else
+ tex.handle = aTextureHandle;
+#endif
+ tex.eType = aTextureType;
+ tex.eColorSpace = ::vr::EColorSpace::ColorSpace_Auto;
+
+ ::vr::VRTextureBounds_t bounds;
+ bounds.uMin = aLeftEyeRect.x;
+ bounds.vMin = 1.0 - aLeftEyeRect.y;
+ bounds.uMax = aLeftEyeRect.x + aLeftEyeRect.width;
+ bounds.vMax = 1.0 - (aLeftEyeRect.y + aLeftEyeRect.height);
+
+ ::vr::EVRCompositorError err;
+ err = mVRCompositor->Submit(::vr::EVREye::Eye_Left, &tex, &bounds);
+ if (err != ::vr::EVRCompositorError::VRCompositorError_None) {
+ printf_stderr("OpenVR Compositor Submit() failed.\n");
+ }
+
+ bounds.uMin = aRightEyeRect.x;
+ bounds.vMin = 1.0 - aRightEyeRect.y;
+ bounds.uMax = aRightEyeRect.x + aRightEyeRect.width;
+ bounds.vMax = 1.0 - (aRightEyeRect.y + aRightEyeRect.height);
+
+ err = mVRCompositor->Submit(::vr::EVREye::Eye_Right, &tex, &bounds);
+ if (err != ::vr::EVRCompositorError::VRCompositorError_None) {
+ printf_stderr("OpenVR Compositor Submit() failed.\n");
+ }
+
+ mVRCompositor->PostPresentHandoff();
+ return true;
+}
+
+void OpenVRSession::StopPresentation() {
+ mVRCompositor->ClearLastSubmittedFrame();
+
+ ::vr::Compositor_CumulativeStats stats;
+ mVRCompositor->GetCumulativeStats(&stats,
+ sizeof(::vr::Compositor_CumulativeStats));
+}
+
+bool OpenVRSession::StartPresentation() { return true; }
+
+void OpenVRSession::VibrateHaptic(uint32_t aControllerIdx,
+ uint32_t aHapticIndex, float aIntensity,
+ float aDuration) {
+ MutexAutoLock lock(mControllerHapticStateMutex);
+
+ // Initilize the haptic thread when the first time to do vibration.
+ if (!mHapticThread) {
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "OpenVRSession::StartHapticThread", [this]() { StartHapticThread(); }));
+ }
+ if (aHapticIndex >= kNumOpenVRHaptics ||
+ aControllerIdx >= kVRControllerMaxCount) {
+ return;
+ }
+
+ const OpenVRHand role = mControllerDeviceIndex[aControllerIdx];
+ if (role == OpenVRHand::None) {
+ return;
+ }
+ mHapticPulseRemaining[aControllerIdx][aHapticIndex] = aDuration;
+ mHapticPulseIntensity[aControllerIdx][aHapticIndex] = aIntensity;
+}
+
+void OpenVRSession::StartHapticThread() {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!mHapticThread) {
+ mHapticThread = new VRThread("VR_OpenVR_Haptics"_ns);
+ }
+ mHapticThread->Start();
+ StartHapticTimer();
+}
+
+void OpenVRSession::StopHapticThread() {
+ if (mHapticThread) {
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "mHapticThread::Shutdown",
+ [thread = mHapticThread]() { thread->Shutdown(); }));
+ mHapticThread = nullptr;
+ }
+}
+
+void OpenVRSession::StartHapticTimer() {
+ if (!mHapticTimer && mHapticThread) {
+ mLastHapticUpdate = TimeStamp();
+ mHapticTimer = NS_NewTimer();
+ mHapticTimer->SetTarget(mHapticThread->GetThread()->EventTarget());
+ mHapticTimer->InitWithNamedFuncCallback(
+ HapticTimerCallback, this, kVRHapticUpdateInterval,
+ nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP,
+ "OpenVRSession::HapticTimerCallback");
+ }
+}
+
+void OpenVRSession::StopHapticTimer() {
+ if (mHapticTimer) {
+ mHapticTimer->Cancel();
+ mHapticTimer = nullptr;
+ }
+}
+
+/*static*/
+void OpenVRSession::HapticTimerCallback(nsITimer* aTimer, void* aClosure) {
+ /**
+ * It is safe to use the pointer passed in aClosure to reference the
+ * OpenVRSession object as the timer is canceled in OpenVRSession::Shutdown,
+ * which is called by the OpenVRSession destructor, guaranteeing
+ * that this function runs if and only if the VRManager object is valid.
+ */
+ OpenVRSession* self = static_cast<OpenVRSession*>(aClosure);
+ MOZ_ASSERT(self);
+ self->UpdateHaptics();
+}
+
+void OpenVRSession::UpdateHaptics() {
+ MOZ_ASSERT(mHapticThread->GetThread() == NS_GetCurrentThread());
+ MOZ_ASSERT(mVRSystem);
+
+ MutexAutoLock lock(mControllerHapticStateMutex);
+
+ TimeStamp now = TimeStamp::Now();
+ if (mLastHapticUpdate.IsNull()) {
+ mLastHapticUpdate = now;
+ return;
+ }
+ float deltaTime = (float)(now - mLastHapticUpdate).ToSeconds();
+ mLastHapticUpdate = now;
+
+ for (uint32_t stateIndex = 0; stateIndex < kVRControllerMaxCount;
+ ++stateIndex) {
+ const OpenVRHand role = mControllerDeviceIndex[stateIndex];
+ if (role == OpenVRHand::None) {
+ continue;
+ }
+ for (uint32_t hapticIdx = 0; hapticIdx < kNumOpenVRHaptics; hapticIdx++) {
+ float intensity = mHapticPulseIntensity[stateIndex][hapticIdx];
+ float duration = mHapticPulseRemaining[stateIndex][hapticIdx];
+ if (duration <= 0.0f || intensity <= 0.0f) {
+ continue;
+ }
+ vr::VRInput()->TriggerHapticVibrationAction(
+ mControllerHand[role].mActionHaptic.handle, 0.0f, deltaTime, 4.0f,
+ intensity > 1.0f ? 1.0f : intensity, vr::k_ulInvalidInputValueHandle);
+
+ duration -= deltaTime;
+ if (duration < 0.0f) {
+ duration = 0.0f;
+ }
+ mHapticPulseRemaining[stateIndex][hapticIdx] = duration;
+ }
+ }
+}
+
+void OpenVRSession::StopVibrateHaptic(uint32_t aControllerIdx) {
+ MutexAutoLock lock(mControllerHapticStateMutex);
+ if (aControllerIdx >= kVRControllerMaxCount) {
+ return;
+ }
+ for (int iHaptic = 0; iHaptic < kNumOpenVRHaptics; iHaptic++) {
+ mHapticPulseRemaining[aControllerIdx][iHaptic] = 0.0f;
+ }
+}
+
+void OpenVRSession::StopAllHaptics() {
+ MutexAutoLock lock(mControllerHapticStateMutex);
+ for (auto& controller : mHapticPulseRemaining) {
+ for (auto& haptic : controller) {
+ haptic = 0.0f;
+ }
+ }
+}
+
+void OpenVRSession::UpdateTelemetry(VRSystemState& aSystemState) {
+ ::vr::Compositor_CumulativeStats stats;
+ mVRCompositor->GetCumulativeStats(&stats,
+ sizeof(::vr::Compositor_CumulativeStats));
+ aSystemState.displayState.droppedFrameCount = stats.m_nNumReprojectedFrames;
+}
+
+} // namespace mozilla::gfx
diff --git a/gfx/vr/service/OpenVRSession.h b/gfx/vr/service/OpenVRSession.h
new file mode 100644
index 0000000000..60ddcf4b29
--- /dev/null
+++ b/gfx/vr/service/OpenVRSession.h
@@ -0,0 +1,112 @@
+/* -*- 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_SERVICE_OPENVRSESSION_H
+#define GFX_VR_SERVICE_OPENVRSESSION_H
+
+#include "VRSession.h"
+
+#include "openvr.h"
+#include "mozilla/TimeStamp.h"
+#include "moz_external_vr.h"
+#include "OpenVRControllerMapper.h"
+
+#if defined(XP_WIN)
+# include <d3d11_1.h>
+#endif
+class nsITimer;
+
+namespace mozilla {
+namespace gfx {
+class VRThread;
+class OpenVRControllerMapper;
+
+static const int kNumOpenVRHaptics = 1;
+
+enum OpenVRHand : int8_t {
+ Left = 0,
+ Right = 1,
+ Total = 2,
+
+ None = -1
+};
+
+class OpenVRSession : public VRSession {
+ public:
+ OpenVRSession();
+ virtual ~OpenVRSession();
+
+ bool Initialize(mozilla::gfx::VRSystemState& aSystemState,
+ bool aDetectRuntimesOnly) override;
+ void Shutdown() override;
+ void ProcessEvents(mozilla::gfx::VRSystemState& aSystemState) override;
+ void StartFrame(mozilla::gfx::VRSystemState& aSystemState) override;
+ bool StartPresentation() override;
+ void StopPresentation() override;
+ void VibrateHaptic(uint32_t aControllerIdx, uint32_t aHapticIndex,
+ float aIntensity, float aDuration) override;
+ void StopVibrateHaptic(uint32_t aControllerIdx) override;
+ void StopAllHaptics() override;
+
+ protected:
+#if defined(XP_WIN)
+ bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer,
+ ID3D11Texture2D* aTexture) override;
+#elif defined(XP_MACOSX)
+ bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer,
+ const VRLayerTextureHandle& aTexture) override;
+#endif
+
+ private:
+ // OpenVR State
+ ::vr::IVRSystem* mVRSystem = nullptr;
+ ::vr::IVRChaperone* mVRChaperone = nullptr;
+ ::vr::IVRCompositor* mVRCompositor = nullptr;
+ ::vr::VRActionSetHandle_t mActionsetFirefox = vr::k_ulInvalidActionSetHandle;
+ OpenVRHand mControllerDeviceIndex[kVRControllerMaxCount];
+ ControllerInfo mControllerHand[OpenVRHand::Total];
+ float mHapticPulseRemaining[kVRControllerMaxCount][kNumOpenVRHaptics];
+ float mHapticPulseIntensity[kVRControllerMaxCount][kNumOpenVRHaptics];
+ bool mIsWindowsMR;
+ TimeStamp mLastHapticUpdate;
+
+ static void HapticTimerCallback(nsITimer* aTimer, void* aClosure);
+ bool InitState(mozilla::gfx::VRSystemState& aSystemState);
+ void UpdateStageParameters(mozilla::gfx::VRDisplayState& aState);
+ void UpdateEyeParameters(mozilla::gfx::VRSystemState& aState);
+ void UpdateHeadsetPose(mozilla::gfx::VRSystemState& aState);
+ void EnumerateControllers(VRSystemState& aState);
+ void UpdateControllerPoses(VRSystemState& aState);
+ void UpdateControllerButtons(VRSystemState& aState);
+ void UpdateTelemetry(VRSystemState& aSystemState);
+ bool SetupContollerActions();
+
+ bool SubmitFrame(const VRLayerTextureHandle& aTextureHandle,
+ ::vr::ETextureType aTextureType,
+ const VRLayerEyeRect& aLeftEyeRect,
+ const VRLayerEyeRect& aRightEyeRect);
+#if defined(XP_WIN)
+ bool CreateD3DObjects();
+#endif
+ void GetControllerDeviceId(::vr::ETrackedDeviceClass aDeviceType,
+ ::vr::TrackedDeviceIndex_t aDeviceIndex,
+ nsCString& aId,
+ mozilla::gfx::VRControllerType& aControllerType);
+ void UpdateHaptics();
+ void StartHapticThread();
+ void StopHapticThread();
+ void StartHapticTimer();
+ void StopHapticTimer();
+ RefPtr<nsITimer> mHapticTimer;
+ RefPtr<VRThread> mHapticThread;
+ mozilla::Mutex mControllerHapticStateMutex;
+ UniquePtr<OpenVRControllerMapper> mControllerMapper;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // GFX_VR_SERVICE_OPENVRSESSION_H
diff --git a/gfx/vr/service/OpenVRViveMapper.cpp b/gfx/vr/service/OpenVRViveMapper.cpp
new file mode 100644
index 0000000000..a5fadd6e37
--- /dev/null
+++ b/gfx/vr/service/OpenVRViveMapper.cpp
@@ -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/. */
+
+#include "OpenVRViveMapper.h"
+
+#include "moz_external_vr.h"
+#include "VRSession.h"
+
+namespace mozilla::gfx {
+
+void OpenVRViveMapper::UpdateButtons(VRControllerState& aControllerState,
+ ControllerInfo& aControllerInfo) {
+ mNumButtons = mNumAxes = 0;
+ // Button 0: Trigger
+ GetTriggerValueFromAction(aControllerState,
+ aControllerInfo.mActionTrigger_Value);
+ // Button 1: Grip
+ GetButtonValueFromAction(aControllerState,
+ aControllerInfo.mActionGrip_Pressed,
+ aControllerInfo.mActionGrip_Touched);
+ // Button 2: Trackpad
+ GetButtonValueFromAction(aControllerState,
+ aControllerInfo.mActionTrackpad_Pressed,
+ aControllerInfo.mActionTrackpad_Touched);
+ // Button 3: a placeholder button for thumbstick.
+ ++mNumButtons;
+ // Button 4: Menu
+ GetButtonValueFromAction(aControllerState,
+ aControllerInfo.mActionMenu_Pressed,
+ aControllerInfo.mActionMenu_Touched);
+
+ // Axis 0, 1: Trackpad
+ GetAxisValueFromAction(aControllerState,
+ aControllerInfo.mActionTrackpad_Analog);
+
+ aControllerState.numButtons = mNumButtons;
+ aControllerState.numAxes = mNumAxes;
+}
+
+} // namespace mozilla::gfx
diff --git a/gfx/vr/service/OpenVRViveMapper.h b/gfx/vr/service/OpenVRViveMapper.h
new file mode 100644
index 0000000000..93b999df5c
--- /dev/null
+++ b/gfx/vr/service/OpenVRViveMapper.h
@@ -0,0 +1,26 @@
+/* -*- 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_SERVICE_OPENVRVIVEMAPPER_H
+#define GFX_VR_SERVICE_OPENVRVIVEMAPPER_H
+
+#include "OpenVRControllerMapper.h"
+
+namespace mozilla {
+namespace gfx {
+
+class OpenVRViveMapper : public OpenVRControllerMapper {
+ public:
+ OpenVRViveMapper() = default;
+ virtual ~OpenVRViveMapper() = default;
+ virtual void UpdateButtons(VRControllerState& aControllerState,
+ ControllerInfo& aControllerInfo);
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // GFX_VR_SERVICE_OPENVRVIVEMAPPER_H
diff --git a/gfx/vr/service/OpenVRWMRMapper.cpp b/gfx/vr/service/OpenVRWMRMapper.cpp
new file mode 100644
index 0000000000..4611d9ee0a
--- /dev/null
+++ b/gfx/vr/service/OpenVRWMRMapper.cpp
@@ -0,0 +1,51 @@
+/* -*- 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 "OpenVRWMRMapper.h"
+
+#include "moz_external_vr.h"
+#include "VRSession.h"
+
+namespace mozilla::gfx {
+
+void OpenVRWMRMapper::UpdateButtons(VRControllerState& aControllerState,
+ ControllerInfo& aControllerInfo) {
+ mNumButtons = mNumAxes = 0;
+ // Button 0: Trigger
+ GetTriggerValueFromAction(aControllerState,
+ aControllerInfo.mActionTrigger_Value);
+ // Button 1: Grip
+ GetButtonValueFromAction(aControllerState,
+ aControllerInfo.mActionGrip_Pressed,
+ aControllerInfo.mActionGrip_Touched);
+ // Button 2: Touchpad
+ GetButtonValueFromAction(aControllerState,
+ aControllerInfo.mActionTrackpad_Pressed,
+ aControllerInfo.mActionTrackpad_Touched);
+ // Button 3: Thumbstick.
+ GetButtonValueFromAction(aControllerState,
+ aControllerInfo.mActionThumbstick_Pressed,
+ aControllerInfo.mActionThumbstick_Touched);
+ // Button 4: Menu
+ GetButtonValueFromAction(aControllerState,
+ aControllerInfo.mActionMenu_Pressed,
+ aControllerInfo.mActionMenu_Touched);
+
+ // Compared to Edge, we have a wrong implementation for the vertical axis
+ // value. In order to not affect the current VR content, we add a workaround
+ // for yAxis.
+ // Axis 0, 1: Trackpad
+ GetAxisValueFromAction(aControllerState,
+ aControllerInfo.mActionTrackpad_Analog, true);
+ // Axis 2, 3: Thumbstick
+ GetAxisValueFromAction(aControllerState,
+ aControllerInfo.mActionThumbstick_Analog, true);
+
+ aControllerState.numButtons = mNumButtons;
+ aControllerState.numAxes = mNumAxes;
+}
+
+} // namespace mozilla::gfx
diff --git a/gfx/vr/service/OpenVRWMRMapper.h b/gfx/vr/service/OpenVRWMRMapper.h
new file mode 100644
index 0000000000..538d1edfeb
--- /dev/null
+++ b/gfx/vr/service/OpenVRWMRMapper.h
@@ -0,0 +1,26 @@
+/* -*- 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_SERVICE_OPENVRWMRMAPPER_H
+#define GFX_VR_SERVICE_OPENVRWMRMAPPER_H
+
+#include "OpenVRControllerMapper.h"
+
+namespace mozilla {
+namespace gfx {
+
+class OpenVRWMRMapper : public OpenVRControllerMapper {
+ public:
+ OpenVRWMRMapper() = default;
+ virtual ~OpenVRWMRMapper() = default;
+ virtual void UpdateButtons(VRControllerState& aControllerState,
+ ControllerInfo& aControllerInfo);
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // GFX_VR_SERVICE_OPENVRWMRMAPPER_H
diff --git a/gfx/vr/service/PuppetSession.cpp b/gfx/vr/service/PuppetSession.cpp
new file mode 100644
index 0000000000..ab00e3c653
--- /dev/null
+++ b/gfx/vr/service/PuppetSession.cpp
@@ -0,0 +1,124 @@
+/* -*- 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 "PuppetSession.h"
+
+#include "nsString.h"
+#include "VRPuppetCommandBuffer.h"
+#include "mozilla/StaticPrefs_dom.h"
+
+#if defined(XP_WIN)
+# include <d3d11.h>
+# include "mozilla/gfx/DeviceManagerDx.h"
+#elif defined(XP_MACOSX)
+# include "mozilla/gfx/MacIOSurface.h"
+#endif
+
+using namespace mozilla::gfx;
+
+namespace mozilla::gfx {
+
+PuppetSession::PuppetSession() : VRSession() {}
+
+PuppetSession::~PuppetSession() { Shutdown(); }
+
+bool PuppetSession::Initialize(mozilla::gfx::VRSystemState& aSystemState,
+ bool aDetectRuntimesOnly) {
+ if (!StaticPrefs::dom_vr_enabled() || !StaticPrefs::dom_vr_puppet_enabled()) {
+ return false;
+ }
+ if (!VRPuppetCommandBuffer::IsCreated()) {
+ // We only want to initialize VRPuppetCommandBuffer on the main thread.
+ // We can assume if it is not initialized, that the puppet display
+ // would not be enumerated.
+ return false;
+ }
+ if (aDetectRuntimesOnly) {
+ aSystemState.displayState.capabilityFlags |=
+ VRDisplayCapabilityFlags::Cap_ImmersiveVR;
+ return false;
+ }
+ VRPuppetCommandBuffer::Get().Run(aSystemState);
+ if (!aSystemState.displayState.isConnected) {
+ return false;
+ }
+#if defined(XP_WIN)
+ if (!CreateD3DObjects()) {
+ Shutdown();
+ return false;
+ }
+#endif
+
+ // Succeeded
+ return true;
+}
+
+#if defined(XP_WIN)
+bool PuppetSession::CreateD3DObjects() {
+ RefPtr<ID3D11Device> device = gfx::DeviceManagerDx::Get()->GetVRDevice();
+ if (!device) {
+ return false;
+ }
+ if (!CreateD3DContext(device)) {
+ return false;
+ }
+ return true;
+}
+#endif
+
+void PuppetSession::Shutdown() {}
+
+void PuppetSession::StartFrame(mozilla::gfx::VRSystemState& aSystemState) {
+ VRPuppetCommandBuffer::Get().Run(aSystemState);
+}
+
+void PuppetSession::ProcessEvents(mozilla::gfx::VRSystemState& aSystemState) {
+ VRPuppetCommandBuffer& puppet = VRPuppetCommandBuffer::Get();
+ puppet.Run(aSystemState);
+ if (!aSystemState.displayState.isConnected) {
+ mShouldQuit = true;
+ }
+}
+
+#if defined(XP_WIN)
+bool PuppetSession::SubmitFrame(
+ const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer,
+ ID3D11Texture2D* aTexture) {
+ return VRPuppetCommandBuffer::Get().SubmitFrame();
+}
+#elif defined(XP_MACOSX)
+bool PuppetSession::SubmitFrame(
+ const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer,
+ const VRLayerTextureHandle& aTexture) {
+ return VRPuppetCommandBuffer::Get().SubmitFrame();
+}
+#endif
+
+void PuppetSession::StopPresentation() {
+ VRPuppetCommandBuffer::Get().StopPresentation();
+}
+
+bool PuppetSession::StartPresentation() {
+ VRPuppetCommandBuffer::Get().StartPresentation();
+ return true;
+}
+
+void PuppetSession::VibrateHaptic(uint32_t aControllerIdx,
+ uint32_t aHapticIndex, float aIntensity,
+ float aDuration) {
+ VRPuppetCommandBuffer::Get().VibrateHaptic(aControllerIdx, aHapticIndex,
+ aIntensity, aDuration);
+}
+
+void PuppetSession::StopVibrateHaptic(uint32_t aControllerIdx) {
+ VRPuppetCommandBuffer::Get().StopVibrateHaptic(aControllerIdx);
+}
+
+void PuppetSession::StopAllHaptics() {
+ VRPuppetCommandBuffer::Get().StopAllHaptics();
+}
+
+} // namespace mozilla::gfx
diff --git a/gfx/vr/service/PuppetSession.h b/gfx/vr/service/PuppetSession.h
new file mode 100644
index 0000000000..fb0a7c3517
--- /dev/null
+++ b/gfx/vr/service/PuppetSession.h
@@ -0,0 +1,58 @@
+/* -*- 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_SERVICE_PUPPETSESSION_H
+#define GFX_VR_SERVICE_PUPPETSESSION_H
+
+#include "VRSession.h"
+
+#include "mozilla/TimeStamp.h"
+#include "moz_external_vr.h"
+
+#if defined(XP_WIN)
+# include <d3d11_1.h>
+#endif
+class nsITimer;
+
+namespace mozilla {
+namespace gfx {
+
+class PuppetSession : public VRSession {
+ public:
+ PuppetSession();
+ virtual ~PuppetSession();
+
+ bool Initialize(mozilla::gfx::VRSystemState& aSystemState,
+ bool aDetectRuntimesOnly) override;
+ void Shutdown() override;
+ void ProcessEvents(mozilla::gfx::VRSystemState& aSystemState) override;
+ void StartFrame(mozilla::gfx::VRSystemState& aSystemState) override;
+ bool StartPresentation() override;
+ void StopPresentation() override;
+ void VibrateHaptic(uint32_t aControllerIdx, uint32_t aHapticIndex,
+ float aIntensity, float aDuration) override;
+ void StopVibrateHaptic(uint32_t aControllerIdx) override;
+ void StopAllHaptics() override;
+
+ protected:
+#if defined(XP_WIN)
+ bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer,
+ ID3D11Texture2D* aTexture) override;
+#elif defined(XP_MACOSX)
+ bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer,
+ const VRLayerTextureHandle& aTexture) override;
+#endif
+
+ private:
+#if defined(XP_WIN)
+ bool CreateD3DObjects();
+#endif
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // GFX_VR_SERVICE_PUPPETSESSION_H
diff --git a/gfx/vr/service/VRService.cpp b/gfx/vr/service/VRService.cpp
new file mode 100644
index 0000000000..2b774c2531
--- /dev/null
+++ b/gfx/vr/service/VRService.cpp
@@ -0,0 +1,418 @@
+/* -*- 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 "VRService.h"
+
+#include <cstring> // for memcmp
+
+#include "../VRShMem.h"
+#include "../gfxVRMutex.h"
+#include "PuppetSession.h"
+#include "mozilla/BackgroundHangMonitor.h"
+#include "mozilla/StaticPrefs_dom.h"
+#include "nsThread.h"
+#include "nsXULAppAPI.h"
+
+#if defined(XP_WIN)
+# include "OculusSession.h"
+#endif
+
+#if defined(XP_WIN) || defined(XP_MACOSX) || \
+ (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
+# include "OpenVRSession.h"
+#endif
+#if !defined(MOZ_WIDGET_ANDROID)
+# include "OSVRSession.h"
+#endif
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+
+namespace {
+
+int64_t FrameIDFromBrowserState(const mozilla::gfx::VRBrowserState& aState) {
+ for (const auto& layer : aState.layerState) {
+ if (layer.type == VRLayerType::LayerType_Stereo_Immersive) {
+ return layer.layer_stereo_immersive.frameId;
+ }
+ }
+ return 0;
+}
+
+bool IsImmersiveContentActive(const mozilla::gfx::VRBrowserState& aState) {
+ for (const auto& layer : aState.layerState) {
+ if (layer.type == VRLayerType::LayerType_Stereo_Immersive) {
+ return true;
+ }
+ }
+ return false;
+}
+
+} // anonymous namespace
+
+/*static*/
+already_AddRefed<VRService> VRService::Create(
+ volatile VRExternalShmem* aShmem) {
+ RefPtr<VRService> service = new VRService(aShmem);
+ return service.forget();
+}
+
+VRService::VRService(volatile VRExternalShmem* aShmem)
+ : mSystemState{},
+ mBrowserState{},
+ mShutdownRequested(false),
+ mLastHapticState{},
+ mFrameStartTime{} {
+ // When we have the VR process, we map the memory
+ // of mAPIShmem from GPU process and pass it to the CTOR.
+ // If we don't have the VR process, we will instantiate
+ // mAPIShmem in VRService.
+ mShmem = new VRShMem(aShmem, aShmem == nullptr /*aRequiresMutex*/);
+}
+
+VRService::~VRService() {
+ // PSA: We must store the value of any staticPrefs preferences as this
+ // destructor will be called after staticPrefs has been shut down.
+ StopInternal(true /*aFromDtor*/);
+}
+
+void VRService::Refresh() {
+ if (mShmem != nullptr && mShmem->IsDisplayStateShutdown()) {
+ Stop();
+ }
+}
+
+void VRService::Start() {
+ if (!mServiceThread) {
+ /**
+ * We must ensure that any time the service is re-started, that
+ * the VRSystemState is reset, including mSystemState.enumerationCompleted
+ * This must happen before VRService::Start returns to the caller, in order
+ * to prevent the WebVR/WebXR promises from being resolved before the
+ * enumeration has been completed.
+ */
+ memset(&mSystemState, 0, sizeof(mSystemState));
+ PushState(mSystemState);
+ RefPtr<VRService> self = this;
+ nsCOMPtr<nsIThread> thread;
+ nsresult rv = NS_NewNamedThread(
+ "VRService", getter_AddRefs(thread),
+ NS_NewRunnableFunction("VRService::ServiceThreadStartup", [self]() {
+ self->mBackgroundHangMonitor =
+ MakeUnique<mozilla::BackgroundHangMonitor>(
+ "VRService",
+ /* Timeout values are powers-of-two to enable us get better
+ data. 128ms is chosen for transient hangs because 8Hz
+ should be the minimally acceptable goal for Compositor
+ responsiveness (normal goal is 60Hz). */
+ 128,
+ /* 2048ms is chosen for permanent hangs because it's longer
+ * than most Compositor hangs seen in the wild, but is short
+ * enough to not miss getting native hang stacks. */
+ 2048);
+ static_cast<nsThread*>(NS_GetCurrentThread())
+ ->SetUseHangMonitor(true);
+ }));
+
+ if (NS_FAILED(rv)) {
+ return;
+ }
+ thread.swap(mServiceThread);
+ // ServiceInitialize needs mServiceThread to be set in order to be able to
+ // assert that it's running on the right thread as well as dispatching new
+ // tasks. It can't be run within the NS_NewRunnableFunction initial event.
+ MOZ_ALWAYS_SUCCEEDS(mServiceThread->Dispatch(
+ NewRunnableMethod("gfx::VRService::ServiceInitialize", this,
+ &VRService::ServiceInitialize)));
+ }
+}
+
+void VRService::Stop() { StopInternal(false /*aFromDtor*/); }
+
+void VRService::StopInternal(bool aFromDtor) {
+ if (mServiceThread) {
+ // We must disable the background hang monitor before we can shutdown this
+ // thread. Dispatched a last task to do so. No task will be allowed to run
+ // on the service thread after this one.
+ mServiceThread->Dispatch(NS_NewRunnableFunction(
+ "VRService::StopInternal", [self = RefPtr<VRService>(this), this] {
+ static_cast<nsThread*>(NS_GetCurrentThread())
+ ->SetUseHangMonitor(false);
+ mBackgroundHangMonitor = nullptr;
+ }));
+ mShutdownRequested = true;
+ mServiceThread->Shutdown();
+ mServiceThread = nullptr;
+ }
+
+ if (mShmem != nullptr && (aFromDtor || !mShmem->IsSharedExternalShmem())) {
+ // Only leave the VRShMem and clean up the pointer when the struct
+ // was not passed in. Otherwise, VRService will no longer have a
+ // way to access that struct if VRService starts again.
+ mShmem->LeaveShMem();
+ delete mShmem;
+ mShmem = nullptr;
+ }
+
+ mSession = nullptr;
+}
+
+bool VRService::InitShmem() { return mShmem->JoinShMem(); }
+
+bool VRService::IsInServiceThread() {
+ return mServiceThread && mServiceThread->IsOnCurrentThread();
+}
+
+void VRService::ServiceInitialize() {
+ MOZ_ASSERT(IsInServiceThread());
+
+ if (!InitShmem()) {
+ return;
+ }
+
+ mShutdownRequested = false;
+ // Get initial state from the browser
+ PullState(mBrowserState);
+
+ // Try to start a VRSession
+ UniquePtr<VRSession> session;
+
+ if (StaticPrefs::dom_vr_puppet_enabled()) {
+ // When the VR Puppet is enabled, we don't want
+ // to enumerate any real devices
+ session = MakeUnique<PuppetSession>();
+ if (!session->Initialize(mSystemState, mBrowserState.detectRuntimesOnly)) {
+ session = nullptr;
+ }
+ } else {
+ // We try Oculus first to ensure we use Oculus
+ // devices trough the most native interface
+ // when possible.
+#if defined(XP_WIN)
+ // Try Oculus
+ if (!session) {
+ session = MakeUnique<OculusSession>();
+ if (!session->Initialize(mSystemState,
+ mBrowserState.detectRuntimesOnly)) {
+ session = nullptr;
+ }
+ }
+#endif
+
+#if defined(XP_WIN) || defined(XP_MACOSX) || \
+ (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
+ // Try OpenVR
+ if (!session) {
+ session = MakeUnique<OpenVRSession>();
+ if (!session->Initialize(mSystemState,
+ mBrowserState.detectRuntimesOnly)) {
+ session = nullptr;
+ }
+ }
+#endif
+#if !defined(MOZ_WIDGET_ANDROID)
+ // Try OSVR
+ if (!session) {
+ session = MakeUnique<OSVRSession>();
+ if (!session->Initialize(mSystemState,
+ mBrowserState.detectRuntimesOnly)) {
+ session = nullptr;
+ }
+ }
+#endif
+
+ } // if (staticPrefs:VRPuppetEnabled())
+
+ if (session) {
+ mSession = std::move(session);
+ // Setting enumerationCompleted to true indicates to the browser
+ // that it should resolve any promises in the WebVR/WebXR API
+ // waiting for hardware detection.
+ mSystemState.enumerationCompleted = true;
+ PushState(mSystemState);
+
+ mServiceThread->Dispatch(
+ NewRunnableMethod("gfx::VRService::ServiceWaitForImmersive", this,
+ &VRService::ServiceWaitForImmersive));
+ } else {
+ // VR hardware was not detected.
+ // We must inform the browser of the failure so it may try again
+ // later and resolve WebVR promises. A failure or shutdown is
+ // indicated by enumerationCompleted being set to true, with all
+ // other fields remaining zeroed out.
+ VRDisplayCapabilityFlags capFlags =
+ mSystemState.displayState.capabilityFlags;
+ memset(&mSystemState, 0, sizeof(mSystemState));
+ mSystemState.enumerationCompleted = true;
+
+ if (mBrowserState.detectRuntimesOnly) {
+ mSystemState.displayState.capabilityFlags = capFlags;
+ } else {
+ mSystemState.displayState.minRestartInterval =
+ StaticPrefs::dom_vr_external_notdetected_timeout();
+ }
+ mSystemState.displayState.shutdown = true;
+ PushState(mSystemState);
+ }
+}
+
+void VRService::ServiceShutdown() {
+ MOZ_ASSERT(IsInServiceThread());
+
+ // Notify the browser that we have shut down.
+ // This is indicated by enumerationCompleted being set
+ // to true, with all other fields remaining zeroed out.
+ memset(&mSystemState, 0, sizeof(mSystemState));
+ mSystemState.enumerationCompleted = true;
+ mSystemState.displayState.shutdown = true;
+ if (mSession && mSession->ShouldQuit()) {
+ mSystemState.displayState.minRestartInterval =
+ StaticPrefs::dom_vr_external_quit_timeout();
+ }
+ PushState(mSystemState);
+ mSession = nullptr;
+}
+
+void VRService::ServiceWaitForImmersive() {
+ MOZ_ASSERT(IsInServiceThread());
+ MOZ_ASSERT(mSession);
+
+ mSession->ProcessEvents(mSystemState);
+ PushState(mSystemState);
+ PullState(mBrowserState);
+
+ if (mSession->ShouldQuit() || mShutdownRequested) {
+ // Shut down
+ mServiceThread->Dispatch(NewRunnableMethod(
+ "gfx::VRService::ServiceShutdown", this, &VRService::ServiceShutdown));
+ } else if (IsImmersiveContentActive(mBrowserState)) {
+ // Enter Immersive Mode
+ mSession->StartPresentation();
+ mSession->StartFrame(mSystemState);
+ PushState(mSystemState);
+
+ mServiceThread->Dispatch(
+ NewRunnableMethod("gfx::VRService::ServiceImmersiveMode", this,
+ &VRService::ServiceImmersiveMode));
+ } else {
+ // Continue waiting for immersive mode
+ mServiceThread->Dispatch(
+ NewRunnableMethod("gfx::VRService::ServiceWaitForImmersive", this,
+ &VRService::ServiceWaitForImmersive));
+ }
+}
+
+void VRService::ServiceImmersiveMode() {
+ MOZ_ASSERT(IsInServiceThread());
+ MOZ_ASSERT(mSession);
+
+ mSession->ProcessEvents(mSystemState);
+ UpdateHaptics();
+ PushState(mSystemState);
+ PullState(mBrowserState);
+
+ if (mSession->ShouldQuit() || mShutdownRequested) {
+ // Shut down
+ mServiceThread->Dispatch(NewRunnableMethod(
+ "gfx::VRService::ServiceShutdown", this, &VRService::ServiceShutdown));
+ return;
+ }
+
+ if (!IsImmersiveContentActive(mBrowserState)) {
+ // Exit immersive mode
+ mSession->StopAllHaptics();
+ mSession->StopPresentation();
+ mServiceThread->Dispatch(
+ NewRunnableMethod("gfx::VRService::ServiceWaitForImmersive", this,
+ &VRService::ServiceWaitForImmersive));
+ return;
+ }
+
+ uint64_t newFrameId = FrameIDFromBrowserState(mBrowserState);
+ if (newFrameId != mSystemState.displayState.lastSubmittedFrameId) {
+ // A new immersive frame has been received.
+ // Submit the textures to the VR system compositor.
+ bool success = false;
+ for (const auto& layer : mBrowserState.layerState) {
+ if (layer.type == VRLayerType::LayerType_Stereo_Immersive) {
+ // SubmitFrame may block in order to control the timing for
+ // the next frame start
+ success = mSession->SubmitFrame(layer.layer_stereo_immersive);
+ break;
+ }
+ }
+
+ // Changing mLastSubmittedFrameId triggers a new frame to start
+ // rendering. Changes to mLastSubmittedFrameId and the values
+ // used for rendering, such as headset pose, must be pushed
+ // atomically to the browser.
+ mSystemState.displayState.lastSubmittedFrameId = newFrameId;
+ mSystemState.displayState.lastSubmittedFrameSuccessful = success;
+
+ // StartFrame may block to control the timing for the next frame start
+ mSession->StartFrame(mSystemState);
+ mSystemState.sensorState.inputFrameID++;
+ size_t historyIndex =
+ mSystemState.sensorState.inputFrameID % ArrayLength(mFrameStartTime);
+ mFrameStartTime[historyIndex] = TimeStamp::Now();
+ PushState(mSystemState);
+ }
+
+ // Continue immersive mode
+ mServiceThread->Dispatch(
+ NewRunnableMethod("gfx::VRService::ServiceImmersiveMode", this,
+ &VRService::ServiceImmersiveMode));
+}
+
+void VRService::UpdateHaptics() {
+ MOZ_ASSERT(IsInServiceThread());
+ MOZ_ASSERT(mSession);
+
+ for (size_t i = 0; i < ArrayLength(mBrowserState.hapticState); i++) {
+ VRHapticState& state = mBrowserState.hapticState[i];
+ VRHapticState& lastState = mLastHapticState[i];
+ // Note that VRHapticState is asserted to be a POD type, thus memcmp is safe
+ if (memcmp(&state, &lastState, sizeof(VRHapticState)) == 0) {
+ // No change since the last update
+ continue;
+ }
+ if (state.inputFrameID == 0) {
+ // The haptic feedback was stopped
+ mSession->StopVibrateHaptic(state.controllerIndex);
+ } else {
+ TimeStamp now;
+ if (now.IsNull()) {
+ // TimeStamp::Now() is expensive, so we
+ // must call it only when needed and save the
+ // output for further loop iterations.
+ now = TimeStamp::Now();
+ }
+ // This is a new haptic pulse, or we are overriding a prior one
+ size_t historyIndex = state.inputFrameID % ArrayLength(mFrameStartTime);
+ float startOffset =
+ (float)(now - mFrameStartTime[historyIndex]).ToSeconds();
+
+ // state.pulseStart is guaranteed never to be in the future
+ mSession->VibrateHaptic(
+ state.controllerIndex, state.hapticIndex, state.pulseIntensity,
+ state.pulseDuration + state.pulseStart - startOffset);
+ }
+ // Record the state for comparison in the next run
+ memcpy(&lastState, &state, sizeof(VRHapticState));
+ }
+}
+
+void VRService::PushState(const mozilla::gfx::VRSystemState& aState) {
+ if (mShmem != nullptr) {
+ mShmem->PushSystemState(aState);
+ }
+}
+
+void VRService::PullState(mozilla::gfx::VRBrowserState& aState) {
+ if (mShmem != nullptr) {
+ mShmem->PullBrowserState(aState);
+ }
+}
diff --git a/gfx/vr/service/VRService.h b/gfx/vr/service/VRService.h
new file mode 100644
index 0000000000..f964d4eea9
--- /dev/null
+++ b/gfx/vr/service/VRService.h
@@ -0,0 +1,92 @@
+/* -*- 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_SERVICE_VRSERVICE_H
+#define GFX_VR_SERVICE_VRSERVICE_H
+
+#include "moz_external_vr.h"
+#include "base/process.h" // for base::ProcessHandle
+#include "mozilla/TimeStamp.h"
+#include "mozilla/UniquePtr.h"
+#include "nsCOMPtr.h"
+
+class nsIThread;
+namespace mozilla {
+class BackgroundHangMonitor;
+namespace gfx {
+
+class VRSession;
+class VRShMem;
+
+static const int kVRFrameTimingHistoryDepth = 100;
+
+class VRService {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VRService)
+ static already_AddRefed<VRService> Create(
+ volatile VRExternalShmem* aShmem = nullptr);
+
+ void Refresh();
+ void Start();
+ void Stop();
+
+ private:
+ explicit VRService(volatile VRExternalShmem* aShmem);
+ ~VRService();
+
+ void StopInternal(bool aFromDtor);
+
+ bool InitShmem();
+ void PushState(const mozilla::gfx::VRSystemState& aState);
+ void PullState(mozilla::gfx::VRBrowserState& aState);
+
+ /**
+ * VRSystemState contains the most recent state of the VR
+ * system, to be shared with the browser by Shmem.
+ * mSystemState is the VR Service copy of this data, which
+ * is memcpy'ed atomically to the Shmem.
+ * VRSystemState is written by the VR Service, but read-only
+ * by the browser.
+ */
+ VRSystemState mSystemState;
+ /**
+ * VRBrowserState contains the most recent state of the browser.
+ * mBrowserState is memcpy'ed from the Shmem atomically
+ */
+ VRBrowserState mBrowserState;
+
+ UniquePtr<VRSession> mSession;
+ nsCOMPtr<nsIThread> mServiceThread;
+ // Only ever accessed on the service thread.
+ UniquePtr<mozilla::BackgroundHangMonitor> mBackgroundHangMonitor;
+
+ bool mShutdownRequested;
+
+ // Note: mShmem doesn't support RefPtr; thus, do not share this private
+ // pointer so that its lifetime can still be controlled by VRService
+ VRShMem* mShmem;
+ VRHapticState mLastHapticState[kVRHapticsMaxCount];
+ TimeStamp mFrameStartTime[kVRFrameTimingHistoryDepth];
+
+ bool IsInServiceThread();
+ void UpdateHaptics();
+
+ /**
+ * The VR Service thread is a state machine that always has one
+ * task queued depending on the state.
+ *
+ * VR Service thread state task functions:
+ */
+ void ServiceInitialize();
+ void ServiceShutdown();
+ void ServiceWaitForImmersive();
+ void ServiceImmersiveMode();
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // GFX_VR_SERVICE_VRSERVICE_H
diff --git a/gfx/vr/service/VRSession.cpp b/gfx/vr/service/VRSession.cpp
new file mode 100644
index 0000000000..44f9fb14bc
--- /dev/null
+++ b/gfx/vr/service/VRSession.cpp
@@ -0,0 +1,196 @@
+/* -*- 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 "VRSession.h"
+
+#include "moz_external_vr.h"
+
+#if defined(XP_WIN)
+# include <d3d11.h>
+#endif // defined(XP_WIN)
+
+#if defined(MOZILLA_INTERNAL_API)
+# if defined(XP_WIN)
+# include "mozilla/gfx/Logging.h"
+# endif
+#else
+# define NS_WARNING(s)
+#endif
+
+using namespace mozilla::gfx;
+
+VRSession::VRSession()
+ : mShouldQuit(false)
+#ifdef XP_WIN
+ ,
+ mDevice(nullptr),
+ mContext(nullptr),
+ mDeviceContextState(nullptr)
+#endif
+{
+}
+
+#ifdef XP_WIN
+VRSession::~VRSession() {
+ if (mDevice != nullptr) {
+ mDevice->Release();
+ mDevice = nullptr;
+ }
+
+ if (mContext != nullptr) {
+ mContext->Release();
+ mContext = nullptr;
+ }
+
+ if (mDeviceContextState != nullptr) {
+ mDeviceContextState->Release();
+ mDeviceContextState = nullptr;
+ }
+}
+#endif
+
+#if defined(XP_WIN)
+bool VRSession::CreateD3DContext(ID3D11Device* aDevice) {
+ if (!mDevice) {
+ if (!aDevice) {
+ NS_WARNING("VRSession::CreateD3DObjects failed to get a D3D11Device");
+ return false;
+ }
+ if (FAILED(aDevice->QueryInterface(IID_PPV_ARGS(&mDevice)))) {
+ NS_WARNING("VRSession::CreateD3DObjects failed to get a D3D11Device1");
+ return false;
+ }
+ }
+ if (!mContext) {
+ mDevice->GetImmediateContext1(&mContext);
+ if (!mContext) {
+ NS_WARNING(
+ "VRSession::CreateD3DObjects failed to get an immediate context");
+ return false;
+ }
+ }
+ if (!mDeviceContextState) {
+ D3D_FEATURE_LEVEL featureLevels[]{D3D_FEATURE_LEVEL_11_1,
+ D3D_FEATURE_LEVEL_11_0};
+ mDevice->CreateDeviceContextState(0, featureLevels, 2, D3D11_SDK_VERSION,
+ __uuidof(ID3D11Device1), nullptr,
+ &mDeviceContextState);
+ }
+ if (!mDeviceContextState) {
+ NS_WARNING(
+ "VRSession::CreateD3DObjects failed to get a D3D11DeviceContextState");
+ return false;
+ }
+ return true;
+}
+
+ID3D11Device1* VRSession::GetD3DDevice() { return mDevice; }
+
+ID3D11DeviceContext1* VRSession::GetD3DDeviceContext() { return mContext; }
+
+ID3DDeviceContextState* VRSession::GetD3DDeviceContextState() {
+ return mDeviceContextState;
+}
+
+#endif // defined(XP_WIN)
+
+bool VRSession::SubmitFrame(
+ const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer) {
+#if defined(XP_WIN)
+ bool success = false;
+ if (aLayer.textureType ==
+ VRLayerTextureType::LayerTextureType_D3D10SurfaceDescriptor) {
+ ID3D11Texture2D* dxTexture = nullptr;
+ HRESULT hr = mDevice->OpenSharedResource((HANDLE)aLayer.textureHandle,
+ IID_PPV_ARGS(&dxTexture));
+ if (SUCCEEDED(hr) && dxTexture != nullptr) {
+ // Similar to LockD3DTexture in TextureD3D11.cpp
+ IDXGIKeyedMutex* mutex = nullptr;
+ hr = dxTexture->QueryInterface(IID_PPV_ARGS(&mutex));
+ if (SUCCEEDED(hr)) {
+ hr = mutex->AcquireSync(0, 1000);
+# ifdef MOZILLA_INTERNAL_API
+ if (hr == WAIT_TIMEOUT) {
+ gfxDevCrash(LogReason::D3DLockTimeout) << "D3D lock mutex timeout";
+ } else if (hr == WAIT_ABANDONED) {
+ gfxCriticalNote << "GFX: D3D11 lock mutex abandoned";
+ }
+# endif
+ if (SUCCEEDED(hr)) {
+ success = SubmitFrame(aLayer, dxTexture);
+ hr = mutex->ReleaseSync(0);
+ if (FAILED(hr)) {
+ NS_WARNING("Failed to unlock the texture");
+ }
+ } else {
+ NS_WARNING("Failed to lock the texture");
+ }
+
+ mutex->Release();
+ mutex = nullptr;
+ }
+
+ dxTexture->Release();
+ dxTexture = nullptr;
+ } else {
+ NS_WARNING("Failed to open shared texture");
+ }
+
+ return SUCCEEDED(hr) && success;
+ }
+
+#elif defined(XP_MACOSX)
+
+ if (aLayer.textureType == VRLayerTextureType::LayerTextureType_MacIOSurface) {
+ return SubmitFrame(aLayer, aLayer.textureHandle);
+ }
+
+#endif
+
+ return false;
+}
+
+void VRSession::UpdateTrigger(VRControllerState& aState, uint32_t aButtonIndex,
+ float aValue, float aThreshold) {
+ // For OpenVR, the threshold value of ButtonPressed and ButtonTouched is 0.55.
+ // We prefer to let developers to set their own threshold for the adjustment.
+ // Therefore, we don't check ButtonPressed and ButtonTouched with ButtonMask
+ // here. we just check the button value is larger than the threshold value or
+ // not.
+ uint64_t mask = (1ULL << aButtonIndex);
+ aState.triggerValue[aButtonIndex] = aValue;
+ if (aValue > aThreshold) {
+ aState.buttonPressed |= mask;
+ aState.buttonTouched |= mask;
+ } else {
+ aState.buttonPressed &= ~mask;
+ aState.buttonTouched &= ~mask;
+ }
+}
+
+void VRSession::SetControllerSelectionAndSqueezeFrameId(
+ VRControllerState& controllerState, uint64_t aFrameId) {
+ // The 1st button, trigger, is its selection action.
+ const bool selectionPressed = controllerState.buttonPressed & 1ULL;
+ if (selectionPressed && controllerState.selectActionStopFrameId >=
+ controllerState.selectActionStartFrameId) {
+ controllerState.selectActionStartFrameId = aFrameId;
+ } else if (!selectionPressed && controllerState.selectActionStartFrameId >
+ controllerState.selectActionStopFrameId) {
+ controllerState.selectActionStopFrameId = aFrameId;
+ }
+ // The 2nd button, squeeze, is its squeeze action.
+ const bool squeezePressed = controllerState.buttonPressed & (1ULL << 1);
+ if (squeezePressed && controllerState.squeezeActionStopFrameId >=
+ controllerState.squeezeActionStartFrameId) {
+ controllerState.squeezeActionStartFrameId = aFrameId;
+ } else if (!squeezePressed && controllerState.squeezeActionStartFrameId >
+ controllerState.squeezeActionStopFrameId) {
+ controllerState.squeezeActionStopFrameId = aFrameId;
+ }
+}
+
+bool VRSession::ShouldQuit() const { return mShouldQuit; }
diff --git a/gfx/vr/service/VRSession.h b/gfx/vr/service/VRSession.h
new file mode 100644
index 0000000000..ab446a26d7
--- /dev/null
+++ b/gfx/vr/service/VRSession.h
@@ -0,0 +1,94 @@
+/* -*- 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_SERVICE_VRSESSION_H
+#define GFX_VR_SERVICE_VRSESSION_H
+
+#include "moz_external_vr.h"
+
+#if defined(XP_WIN)
+# include <d3d11_1.h>
+#elif defined(XP_MACOSX)
+class MacIOSurface;
+#endif
+
+namespace mozilla {
+namespace gfx {
+
+class VRSession {
+ public:
+ VRSession();
+
+ // Since this class doesn't use smartpointers for its refcounted
+ // members (so that it can compile outside of mozilla-central),
+ // prevent copying the addresses without increasing the refcount.
+ VRSession(const VRSession&) = delete;
+ VRSession& operator=(const VRSession&) = delete;
+
+#ifdef XP_WIN
+ virtual ~VRSession();
+#else
+ virtual ~VRSession() = default;
+#endif
+
+ static void UpdateTrigger(VRControllerState& aState, uint32_t aButtonIndex,
+ float aValue, float aThreshold);
+ /**
+ * In order to support WebXR's navigator.xr.IsSessionSupported call without
+ * displaying any permission dialogue, it is necessary to have a safe way to
+ * detect the capability of running a VR or AR session without activating XR
+ * runtimes or powering on hardware.
+ *
+ * API's such as OpenVR make no guarantee that hardware and software won't be
+ * left activated after enumerating devices, so each backend in gfx/vr/service
+ * must allow for more granular detection of capabilities.
+ *
+ * By passing true to bDetectRuntimesOnly, the initialization exits early
+ * after reporting the presence of XR runtime software. The Initialize method
+ * will only enumerate hardware and possibly return true when
+ * aDetectRuntimesOnly is false.
+ */
+ virtual bool Initialize(mozilla::gfx::VRSystemState& aSystemState,
+ bool aDetectRuntimesOnly) = 0;
+ virtual void Shutdown() = 0;
+ virtual void ProcessEvents(mozilla::gfx::VRSystemState& aSystemState) = 0;
+ virtual void StartFrame(mozilla::gfx::VRSystemState& aSystemState) = 0;
+ virtual bool StartPresentation() = 0;
+ virtual void StopPresentation() = 0;
+ virtual void VibrateHaptic(uint32_t aControllerIdx, uint32_t aHapticIndex,
+ float aIntensity, float aDuration) = 0;
+ virtual void StopVibrateHaptic(uint32_t aControllerIdx) = 0;
+ virtual void StopAllHaptics() = 0;
+ bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer);
+ bool ShouldQuit() const;
+
+ protected:
+ bool mShouldQuit;
+#if defined(XP_WIN)
+ virtual bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer,
+ ID3D11Texture2D* aTexture) = 0;
+ bool CreateD3DContext(ID3D11Device* aDevice);
+
+ ID3D11Device1* GetD3DDevice();
+ ID3D11DeviceContext1* GetD3DDeviceContext();
+ ID3DDeviceContextState* GetD3DDeviceContextState();
+
+ ID3D11Device1* mDevice;
+ ID3D11DeviceContext1* mContext;
+ ID3DDeviceContextState* mDeviceContextState;
+
+#elif defined(XP_MACOSX)
+ virtual bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer,
+ const VRLayerTextureHandle& aTexture) = 0;
+#endif
+ void SetControllerSelectionAndSqueezeFrameId(
+ VRControllerState& controllerState, uint64_t aFrameId);
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // GFX_VR_SERVICE_VRSESSION_H
diff --git a/gfx/vr/service/binding/OpenVRCosmosBinding.h b/gfx/vr/service/binding/OpenVRCosmosBinding.h
new file mode 100644
index 0000000000..97980bb087
--- /dev/null
+++ b/gfx/vr/service/binding/OpenVRCosmosBinding.h
@@ -0,0 +1,204 @@
+/* -*- 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_BLINDING_OPENVRCOSMOSBINDING_H
+#define GFX_VR_BLINDING_OPENVRCOSMOSBINDING_H
+
+namespace mozilla {
+namespace gfx {
+
+struct OpenVRCosmosBinding {
+ const char* binding =
+ // clang-format off
+ "{\n"
+ " \"version\" : \"0.1\", \n"
+ " \"controller_type\" : \"vive_cosmos_controller\", \n"
+ " \"description\" : \"Bindings for Firefox OpenVR for the Vive Cosmos controller\", \n"
+ " \"name\" : \"Firefox bindings for Vive Cosmos Controller\", \n"
+ " \"bindings\" : { \n"
+ " \"/actions/firefox\" : { \n"
+ " \"poses\" : [ \n"
+ " { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_pose\", \n"
+ " \"path\" : \"/user/hand/left/pose/raw\" \n"
+ " },\n"
+ " { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_pose\", \n"
+ " \"path\" : \"/user/hand/right/pose/raw\" \n"
+ " }\n"
+ " ],\n"
+ " \"haptics\" : [ \n"
+ " {\n"
+ " \"output\" : \"/actions/firefox/out/LHand_haptic\", \n"
+ " \"path\" : \"/user/hand/left/output/haptic\" \n"
+ " },\n"
+ " { \n"
+ " \"output\" : \"/actions/firefox/out/RHand_haptic\", \n"
+ " \"path\" : \"/user/hand/right/output/haptic\" \n"
+ " }\n"
+ " ],\n"
+ " \"sources\" : [ \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"pull\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_trigger_value\" \n"
+ " } \n"
+ " },\n"
+ " \"mode\" : \"trigger\", \n"
+ " \"path\" : \"/user/hand/left/input/trigger\" \n"
+ " }, \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"pull\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_trigger_value\" \n"
+ " } \n"
+ " },\n"
+ " \"mode\" : \"trigger\", \n"
+ " \"path\" : \"/user/hand/right/input/trigger\" \n"
+ " }, \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"click\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_grip_pressed\" \n"
+ " }, \n"
+ " \"touch\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_grip_touched\" \n"
+ " } \n"
+ " }, \n"
+ " \"mode\" : \"button\", \n"
+ " \"path\" : \"/user/hand/left/input/grip\" \n"
+ " }, \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"click\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_grip_pressed\" \n"
+ " }, \n"
+ " \"touch\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_grip_touched\" \n"
+ " } \n"
+ " }, \n"
+ " \"mode\" : \"button\", \n"
+ " \"path\" : \"/user/hand/right/input/grip\" \n"
+ " }, \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"click\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_system_pressed\" \n"
+ " }, \n"
+ " \"touch\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_system_touched\" \n"
+ " } \n"
+ " },\n"
+ " \"mode\" : \"button\", \n"
+ " \"path\" : \"/user/hand/left/input/system\" \n"
+ " }, \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"click\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_system_pressed\" \n"
+ " }, \n"
+ " \"touch\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_system_touched\" \n"
+ " } \n"
+ " },\n"
+ " \"mode\" : \"button\", \n"
+ " \"path\" : \"/user/hand/right/input/system\" \n"
+ " }, \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"click\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_a_pressed\" \n"
+ " } \n"
+ " },\n"
+ " \"mode\" : \"button\", \n"
+ " \"path\" : \"/user/hand/left/input/x\" \n"
+ " }, \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"click\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_a_pressed\" \n"
+ " } \n"
+ " },\n"
+ " \"mode\" : \"button\", \n"
+ " \"path\" : \"/user/hand/right/input/a\" \n"
+ " }, \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"click\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_b_pressed\" \n"
+ " } \n"
+ " },\n"
+ " \"mode\" : \"button\", \n"
+ " \"path\" : \"/user/hand/left/input/y\" \n"
+ " }, \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"click\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_b_pressed\" \n"
+ " } \n"
+ " },\n"
+ " \"mode\" : \"button\", \n"
+ " \"path\" : \"/user/hand/right/input/b\" \n"
+ " }, \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"position\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_thumbstick_analog\" \n"
+ " }, \n"
+ " \"click\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_thumbstick_pressed\" \n"
+ " }, \n"
+ " \"touch\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_thumbstick_touched\" \n"
+ " } \n"
+ " }, \n"
+ " \"mode\" : \"joystick\", \n"
+ " \"path\" : \"/user/hand/left/input/joystick\" \n"
+ " },\n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"position\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_thumbstick_analog\" \n"
+ " }, \n"
+ " \"click\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_thumbstick_pressed\" \n"
+ " }, \n"
+ " \"touch\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_thumbstick_touched\" \n"
+ " } \n"
+ " }, \n"
+ " \"mode\" : \"joystick\", \n"
+ " \"path\" : \"/user/hand/right/input/joystick\" \n"
+ " }, \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"click\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_bumper_pressed\" \n"
+ " } \n"
+ " },\n"
+ " \"mode\" : \"button\", \n"
+ " \"path\" : \"/user/hand/left/input/bumper\" \n"
+ " }, \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"click\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_bumper_pressed\" \n"
+ " } \n"
+ " },\n"
+ " \"mode\" : \"button\", \n"
+ " \"path\" : \"/user/hand/right/input/bumper\" \n"
+ " } \n"
+ " ]\n"
+ " }\n"
+ " }\n"
+ "}";
+ // clang-format on
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // GFX_VR_BLINDING_OPENVRCOSMOSBINDING_H
diff --git a/gfx/vr/service/binding/OpenVRKnucklesBinding.h b/gfx/vr/service/binding/OpenVRKnucklesBinding.h
new file mode 100644
index 0000000000..508e5f64c7
--- /dev/null
+++ b/gfx/vr/service/binding/OpenVRKnucklesBinding.h
@@ -0,0 +1,300 @@
+/* -*- 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_BLINDING_OPENVRKNUCKLESBINDING_H
+#define GFX_VR_BLINDING_OPENVRKNUCKLESBINDING_H
+
+namespace mozilla {
+namespace gfx {
+
+struct OpenVRKnucklesBinding {
+ const char* binding =
+ // clang-format off
+ "{\n"
+ " \"version\" : \"0.1\", \n"
+ " \"controller_type\" : \"knuckles\", \n"
+ " \"description\" : \"Bindings for Firefox OpenVR for the Knuckles controller\", \n"
+ " \"name\" : \"Firefox bindings for Knuckles Controller\", \n"
+ " \"bindings\" : { \n"
+ " \"/actions/firefox\" : { \n"
+ " \"poses\" : [ \n"
+ " { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_pose\", \n"
+ " \"path\" : \"/user/hand/left/pose/raw\" \n"
+ " },\n"
+ " { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_pose\", \n"
+ " \"path\" : \"/user/hand/right/pose/raw\" \n"
+ " }\n"
+ " ],\n"
+ " \"haptics\" : [ \n"
+ " {\n"
+ " \"output\" : \"/actions/firefox/out/LHand_haptic\", \n"
+ " \"path\" : \"/user/hand/left/output/haptic\" \n"
+ " },\n"
+ " { \n"
+ " \"output\" : \"/actions/firefox/out/RHand_haptic\", \n"
+ " \"path\" : \"/user/hand/right/output/haptic\" \n"
+ " }\n"
+ " ],\n"
+ " \"sources\" : [ \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"position\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_trackpad_analog\" \n"
+ " }, \n"
+ " \"click\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_trackpad_pressed\" \n"
+ " }, \n"
+ " \"touch\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_trackpad_touched\" \n"
+ " } \n"
+ " }, \n"
+ " \"mode\" : \"trackpad\", \n"
+ " \"path\" : \"/user/hand/left/input/trackpad\" \n"
+ " },\n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"position\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_trackpad_analog\" \n"
+ " }, \n"
+ " \"click\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_trackpad_pressed\" \n"
+ " }, \n"
+ " \"touch\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_trackpad_touched\" \n"
+ " } \n"
+ " }, \n"
+ " \"mode\" : \"trackpad\", \n"
+ " \"path\" : \"/user/hand/right/input/trackpad\" \n"
+ " }, \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"pull\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_trigger_value\" \n"
+ " } \n"
+ " },\n"
+ " \"mode\" : \"trigger\", \n"
+ " \"path\" : \"/user/hand/left/input/trigger\" \n"
+ " }, \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"pull\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_trigger_value\" \n"
+ " } \n"
+ " },\n"
+ " \"mode\" : \"trigger\", \n"
+ " \"path\" : \"/user/hand/right/input/trigger\" \n"
+ " }, \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"click\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_grip_pressed\" \n"
+ " }, \n"
+ " \"touch\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_grip_touched\" \n"
+ " } \n"
+ " }, \n"
+ " \"mode\" : \"button\", \n"
+ " \"path\" : \"/user/hand/left/input/grip\" \n"
+ " }, \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"click\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_grip_pressed\" \n"
+ " }, \n"
+ " \"touch\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_grip_touched\" \n"
+ " } \n"
+ " }, \n"
+ " \"mode\" : \"button\", \n"
+ " \"path\" : \"/user/hand/right/input/grip\" \n"
+ " }, \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"click\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_system_pressed\" \n"
+ " }, \n"
+ " \"touch\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_system_touched\" \n"
+ " } \n"
+ " },\n"
+ " \"mode\" : \"button\", \n"
+ " \"path\" : \"/user/hand/left/input/system\" \n"
+ " }, \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"click\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_system_pressed\" \n"
+ " }, \n"
+ " \"touch\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_system_touched\" \n"
+ " } \n"
+ " },\n"
+ " \"mode\" : \"button\", \n"
+ " \"path\" : \"/user/hand/right/input/system\" \n"
+ " }, \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"click\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_a_pressed\" \n"
+ " }, \n"
+ " \"touch\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_a_touched\" \n"
+ " } \n"
+ " },\n"
+ " \"mode\" : \"button\", \n"
+ " \"path\" : \"/user/hand/left/input/a\" \n"
+ " }, \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"click\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_a_pressed\" \n"
+ " }, \n"
+ " \"touch\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_a_touched\" \n"
+ " } \n"
+ " },\n"
+ " \"mode\" : \"button\", \n"
+ " \"path\" : \"/user/hand/right/input/a\" \n"
+ " }, \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"click\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_b_pressed\" \n"
+ " }, \n"
+ " \"touch\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_b_touched\" \n"
+ " } \n"
+ " },\n"
+ " \"mode\" : \"button\", \n"
+ " \"path\" : \"/user/hand/left/input/b\" \n"
+ " }, \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"click\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_b_pressed\" \n"
+ " }, \n"
+ " \"touch\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_b_touched\" \n"
+ " } \n"
+ " },\n"
+ " \"mode\" : \"button\", \n"
+ " \"path\" : \"/user/hand/right/input/b\" \n"
+ " }, \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"position\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_thumbstick_analog\" \n"
+ " }, \n"
+ " \"click\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_thumbstick_pressed\" \n"
+ " }, \n"
+ " \"touch\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_thumbstick_touched\" \n"
+ " } \n"
+ " }, \n"
+ " \"mode\" : \"joystick\", \n"
+ " \"path\" : \"/user/hand/left/input/thumbstick\" \n"
+ " },\n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"position\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_thumbstick_analog\" \n"
+ " }, \n"
+ " \"click\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_thumbstick_pressed\" \n"
+ " }, \n"
+ " \"touch\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_thumbstick_touched\" \n"
+ " } \n"
+ " }, \n"
+ " \"mode\" : \"joystick\", \n"
+ " \"path\" : \"/user/hand/right/input/thumbstick\" \n"
+ " }, \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"pull\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_finger_index_value\" \n"
+ " } \n"
+ " }, \n"
+ " \"mode\" : \"trigger\", \n"
+ " \"path\" : \"/user/hand/left/input/finger/index\" \n"
+ " }, \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"pull\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_finger_index_value\" \n"
+ " } \n"
+ " }, \n"
+ " \"mode\" : \"trigger\", \n"
+ " \"path\" : \"/user/hand/right/input/finger/index\" \n"
+ " }, \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"pull\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_finger_middle_value\" \n"
+ " } \n"
+ " }, \n"
+ " \"mode\" : \"trigger\", \n"
+ " \"path\" : \"/user/hand/left/input/finger/middle\" \n"
+ " }, \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"pull\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_finger_middle_value\" \n"
+ " } \n"
+ " }, \n"
+ " \"mode\" : \"trigger\", \n"
+ " \"path\" : \"/user/hand/right/input/finger/middle\" \n"
+ " }, \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"pull\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_finger_ring_value\" \n"
+ " } \n"
+ " }, \n"
+ " \"mode\" : \"trigger\", \n"
+ " \"path\" : \"/user/hand/left/input/finger/ring\" \n"
+ " }, \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"pull\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_finger_ring_value\" \n"
+ " } \n"
+ " }, \n"
+ " \"mode\" : \"trigger\", \n"
+ " \"path\" : \"/user/hand/right/input/finger/ring\" \n"
+ " }, \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"pull\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_finger_pinky_value\" \n"
+ " } \n"
+ " }, \n"
+ " \"mode\" : \"trigger\", \n"
+ " \"path\" : \"/user/hand/left/input/finger/pinky\" \n"
+ " }, \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"pull\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_finger_pinky_value\" \n"
+ " } \n"
+ " }, \n"
+ " \"mode\" : \"trigger\", \n"
+ " \"path\" : \"/user/hand/right/input/finger/pinky\" \n"
+ " } \n"
+ " ]\n"
+ " }\n"
+ " }\n"
+ "}";
+ // clang-format on
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // GFX_VR_BLINDING_OPENVRKNUCKLESBINDING_H
diff --git a/gfx/vr/service/binding/OpenVRViveBinding.h b/gfx/vr/service/binding/OpenVRViveBinding.h
new file mode 100644
index 0000000000..c3fb7fd6e1
--- /dev/null
+++ b/gfx/vr/service/binding/OpenVRViveBinding.h
@@ -0,0 +1,174 @@
+/* -*- 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_BLINDING_OPENVRVIVEBINDING_H
+#define GFX_VR_BLINDING_OPENVRVIVEBINDING_H
+
+namespace mozilla {
+namespace gfx {
+
+struct OpenVRViveBinding {
+ const char* binding =
+ // clang-format off
+ "{\n"
+ " \"version\" : \"0.1\", \n"
+ " \"controller_type\" : \"vive_controller\", \n"
+ " \"description\" : \"Bindings for Firefox OpenVR for the Vive controller\", \n"
+ " \"name\" : \"Firefox bindings for Vive Controller\", \n"
+ " \"bindings\" : { \n"
+ " \"/actions/firefox\" : { \n"
+ " \"poses\" : [ \n"
+ " { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_pose\", \n"
+ " \"path\" : \"/user/hand/left/pose/raw\" \n"
+ " },\n"
+ " { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_pose\", \n"
+ " \"path\" : \"/user/hand/right/pose/raw\" \n"
+ " }\n"
+ " ],\n"
+ " \"haptics\" : [ \n"
+ " {\n"
+ " \"output\" : \"/actions/firefox/out/LHand_haptic\", \n"
+ " \"path\" : \"/user/hand/left/output/haptic\" \n"
+ " },\n"
+ " { \n"
+ " \"output\" : \"/actions/firefox/out/RHand_haptic\", \n"
+ " \"path\" : \"/user/hand/right/output/haptic\" \n"
+ " }\n"
+ " ],\n"
+ " \"sources\" : [ \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"position\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_trackpad_analog\" \n"
+ " }, \n"
+ " \"click\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_trackpad_pressed\" \n"
+ " }, \n"
+ " \"touch\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_trackpad_touched\" \n"
+ " } \n"
+ " }, \n"
+ " \"mode\" : \"trackpad\", \n"
+ " \"path\" : \"/user/hand/left/input/trackpad\" \n"
+ " },\n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"position\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_trackpad_analog\" \n"
+ " }, \n"
+ " \"click\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_trackpad_pressed\" \n"
+ " }, \n"
+ " \"touch\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_trackpad_touched\" \n"
+ " } \n"
+ " }, \n"
+ " \"mode\" : \"trackpad\", \n"
+ " \"path\" : \"/user/hand/right/input/trackpad\" \n"
+ " }, \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"pull\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_trigger_value\" \n"
+ " } \n"
+ " },\n"
+ " \"mode\" : \"trigger\", \n"
+ " \"path\" : \"/user/hand/left/input/trigger\" \n"
+ " }, \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"pull\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_trigger_value\" \n"
+ " } \n"
+ " },\n"
+ " \"mode\" : \"trigger\", \n"
+ " \"path\" : \"/user/hand/right/input/trigger\" \n"
+ " }, \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"click\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_grip_pressed\" \n"
+ " }, \n"
+ " \"touch\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_grip_touched\" \n"
+ " } \n"
+ " }, \n"
+ " \"mode\" : \"button\", \n"
+ " \"path\" : \"/user/hand/left/input/grip\" \n"
+ " }, \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"click\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_grip_pressed\" \n"
+ " }, \n"
+ " \"touch\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_grip_touched\" \n"
+ " } \n"
+ " }, \n"
+ " \"mode\" : \"button\", \n"
+ " \"path\" : \"/user/hand/right/input/grip\" \n"
+ " }, \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"click\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_menu_pressed\" \n"
+ " }, \n"
+ " \"touch\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_menu_touched\" \n"
+ " } \n"
+ " },\n"
+ " \"mode\" : \"button\", \n"
+ " \"path\" : \"/user/hand/left/input/application_menu\" \n"
+ " }, \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"click\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_menu_pressed\" \n"
+ " }, \n"
+ " \"touch\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_menu_touched\" \n"
+ " } \n"
+ " },\n"
+ " \"mode\" : \"button\", \n"
+ " \"path\" : \"/user/hand/right/input/application_menu\" \n"
+ " }, \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"click\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_system_pressed\" \n"
+ " }, \n"
+ " \"touch\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_system_touched\" \n"
+ " } \n"
+ " },\n"
+ " \"mode\" : \"button\", \n"
+ " \"path\" : \"/user/hand/left/input/system\" \n"
+ " }, \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"click\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_system_pressed\" \n"
+ " }, \n"
+ " \"touch\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_system_touched\" \n"
+ " } \n"
+ " },\n"
+ " \"mode\" : \"button\", \n"
+ " \"path\" : \"/user/hand/right/input/system\" \n"
+ " } \n"
+ " ]\n"
+ " }\n"
+ " }\n"
+ "}";
+ // clang-format on
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // GFX_VR_BLINDING_OPENVRVIVEBINDING_H
diff --git a/gfx/vr/service/binding/OpenVRWMRBinding.h b/gfx/vr/service/binding/OpenVRWMRBinding.h
new file mode 100644
index 0000000000..09d1529b86
--- /dev/null
+++ b/gfx/vr/service/binding/OpenVRWMRBinding.h
@@ -0,0 +1,192 @@
+/* -*- 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_BLINDING_OPENVRWMRBINDING_H
+#define GFX_VR_BLINDING_OPENVRWMRBINDING_H
+
+namespace mozilla {
+namespace gfx {
+
+struct OpenVRWMRBinding {
+ const char* binding =
+ // clang-format off
+ "{\n"
+ " \"version\" : \"0.1\", \n"
+ " \"controller_type\" : \"holographic_controller\", \n"
+ " \"description\" : \"Bindings for Firefox OpenVR for the Windows Mixed Reality controller\", \n"
+ " \"name\" : \"Firefox bindings for Windows Mixed Reality Controller\", \n"
+ " \"bindings\" : { \n"
+ " \"/actions/firefox\" : { \n"
+ " \"poses\" : [ \n"
+ " { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_pose\", \n"
+ " \"path\" : \"/user/hand/left/pose/raw\" \n"
+ " },\n"
+ " { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_pose\", \n"
+ " \"path\" : \"/user/hand/right/pose/raw\" \n"
+ " }\n"
+ " ],\n"
+ " \"haptics\" : [ \n"
+ " {\n"
+ " \"output\" : \"/actions/firefox/out/LHand_haptic\", \n"
+ " \"path\" : \"/user/hand/left/output/haptic\" \n"
+ " },\n"
+ " { \n"
+ " \"output\" : \"/actions/firefox/out/RHand_haptic\", \n"
+ " \"path\" : \"/user/hand/right/output/haptic\" \n"
+ " }\n"
+ " ],\n"
+ " \"sources\" : [ \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"position\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_trackpad_analog\" \n"
+ " }, \n"
+ " \"click\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_trackpad_pressed\" \n"
+ " }, \n"
+ " \"touch\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_trackpad_touched\" \n"
+ " } \n"
+ " }, \n"
+ " \"mode\" : \"trackpad\", \n"
+ " \"path\" : \"/user/hand/left/input/trackpad\" \n"
+ " },\n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"position\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_trackpad_analog\" \n"
+ " }, \n"
+ " \"click\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_trackpad_pressed\" \n"
+ " }, \n"
+ " \"touch\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_trackpad_touched\" \n"
+ " } \n"
+ " }, \n"
+ " \"mode\" : \"trackpad\", \n"
+ " \"path\" : \"/user/hand/right/input/trackpad\" \n"
+ " }, \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"pull\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_trigger_value\" \n"
+ " } \n"
+ " },\n"
+ " \"mode\" : \"trigger\", \n"
+ " \"path\" : \"/user/hand/left/input/trigger\" \n"
+ " }, \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"pull\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_trigger_value\" \n"
+ " } \n"
+ " },\n"
+ " \"mode\" : \"trigger\", \n"
+ " \"path\" : \"/user/hand/right/input/trigger\" \n"
+ " }, \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"click\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_grip_pressed\" \n"
+ " }, \n"
+ " \"touch\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_grip_touched\" \n"
+ " } \n"
+ " }, \n"
+ " \"mode\" : \"button\", \n"
+ " \"path\" : \"/user/hand/left/input/grip\" \n"
+ " }, \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"click\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_grip_pressed\" \n"
+ " }, \n"
+ " \"touch\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_grip_touched\" \n"
+ " } \n"
+ " }, \n"
+ " \"mode\" : \"button\", \n"
+ " \"path\" : \"/user/hand/right/input/grip\" \n"
+ " }, \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"click\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_menu_pressed\" \n"
+ " }, \n"
+ " \"touch\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_menu_touched\" \n"
+ " } \n"
+ " },\n"
+ " \"mode\" : \"button\", \n"
+ " \"path\" : \"/user/hand/left/input/application_menu\" \n"
+ " }, \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"click\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_menu_pressed\" \n"
+ " }, \n"
+ " \"touch\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_menu_touched\" \n"
+ " } \n"
+ " },\n"
+ " \"mode\" : \"button\", \n"
+ " \"path\" : \"/user/hand/right/input/application_menu\" \n"
+ " }, \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"click\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_system_pressed\" \n"
+ " }, \n"
+ " \"touch\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_system_touched\" \n"
+ " } \n"
+ " },\n"
+ " \"mode\" : \"button\", \n"
+ " \"path\" : \"/user/hand/left/input/system\" \n"
+ " }, \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"click\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_system_pressed\" \n"
+ " }, \n"
+ " \"touch\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_system_touched\" \n"
+ " } \n"
+ " },\n"
+ " \"mode\" : \"button\", \n"
+ " \"path\" : \"/user/hand/right/input/system\" \n"
+ " }, \n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"position\" : { \n"
+ " \"output\" : \"/actions/firefox/in/LHand_thumbstick_analog\" \n"
+ " } \n"
+ " }, \n"
+ " \"mode\" : \"joystick\", \n"
+ " \"path\" : \"/user/hand/left/input/joystick\" \n"
+ " },\n"
+ " {\n"
+ " \"inputs\" : { \n"
+ " \"position\" : { \n"
+ " \"output\" : \"/actions/firefox/in/RHand_thumbstick_analog\" \n"
+ " } \n"
+ " }, \n"
+ " \"mode\" : \"joystick\", \n"
+ " \"path\" : \"/user/hand/right/input/joystick\" \n"
+ " } \n"
+ " ]\n"
+ " }\n"
+ " }\n"
+ "}";
+ // clang-format on
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // GFX_VR_BLINDING_OPENVRWMRBINDING_H
diff --git a/gfx/vr/service/moz.build b/gfx/vr/service/moz.build
new file mode 100644
index 0000000000..0edd36ceec
--- /dev/null
+++ b/gfx/vr/service/moz.build
@@ -0,0 +1,48 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+# Build Oculus support on Windows only
+if CONFIG["OS_TARGET"] == "WINNT":
+ SOURCES += [
+ "OculusSession.cpp",
+ "OpenVRWMRMapper.cpp",
+ ]
+
+# Build OSVR on all platforms except Android
+if CONFIG["OS_TARGET"] != "Android":
+ UNIFIED_SOURCES += [
+ "OSVRSession.cpp",
+ "VRService.cpp",
+ "VRSession.cpp",
+ ]
+ # PuppetSession includes MacIOSurface.h which includes Mac headers
+ # which define Size and Points types in the root namespace that
+ # often conflict with our own types.
+ SOURCES += [
+ "PuppetSession.cpp",
+ ]
+ include("/ipc/chromium/chromium-config.mozbuild")
+
+# Build OpenVR on Windows, Linux, and macOS desktop targets
+if CONFIG["OS_TARGET"] in ("WINNT", "Linux", "Darwin"):
+ DIRS += [
+ "openvr",
+ ]
+ LOCAL_INCLUDES += ["/dom/base", "/gfx/layers/d3d11"]
+
+ # OpenVRSession includes MacIOSurface.h which includes Mac headers
+ # which define Size and Points types in the root namespace that
+ # often conflict with our own types.
+ SOURCES += [
+ "OpenVRControllerMapper.cpp",
+ "OpenVRCosmosMapper.cpp",
+ "OpenVRDefaultMapper.cpp",
+ "OpenVRKnucklesMapper.cpp",
+ "OpenVRSession.cpp",
+ "OpenVRViveMapper.cpp",
+ ]
+
+FINAL_LIBRARY = "xul"
diff --git a/gfx/vr/service/oculus/ovr_capi_dynamic.h b/gfx/vr/service/oculus/ovr_capi_dynamic.h
new file mode 100644
index 0000000000..77eb0a3aa0
--- /dev/null
+++ b/gfx/vr/service/oculus/ovr_capi_dynamic.h
@@ -0,0 +1,984 @@
+/* -*- 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/. */
+
+/* This file contains just the needed struct definitions for
+ * interacting with the Oculus VR C API, without needing to #include
+ * OVR_CAPI.h directly. Note that it uses the same type names as the
+ * CAPI, and cannot be #included at the same time as OVR_CAPI.h. It
+ * does not include the entire C API, just want's needed.
+ */
+
+#ifdef OVR_CAPI_h
+# ifdef _MSC_VER
+# pragma message( \
+ "ovr_capi_dyanmic.h: OVR_CAPI.h included before ovr_capi_dynamic.h, skipping this")
+# else
+# warning OVR_CAPI.h included before ovr_capi_dynamic.h, skipping this
+# endif
+# define mozilla_ovr_capi_dynamic_h_
+
+#else
+
+# ifndef mozilla_ovr_capi_dynamic_h_
+# define mozilla_ovr_capi_dynamic_h_
+
+# ifdef HAVE_64BIT_BUILD
+# define OVR_PTR_SIZE 8
+# define OVR_ON64(x) x
+# else
+# define OVR_PTR_SIZE 4
+# define OVR_ON64(x) /**/
+# endif
+
+# if defined(_WIN32)
+# define OVR_PFN __cdecl
+# else
+# define OVR_PFN
+# endif
+
+# if !defined(OVR_ALIGNAS)
+# if defined(__GNUC__) || defined(__clang__)
+# define OVR_ALIGNAS(n) __attribute__((aligned(n)))
+# elif defined(_MSC_VER) || defined(__INTEL_COMPILER)
+# define OVR_ALIGNAS(n) __declspec(align(n))
+# elif defined(__CC_ARM)
+# define OVR_ALIGNAS(n) __align(n)
+# else
+# error Need to define OVR_ALIGNAS
+# endif
+# endif
+
+# if !defined(OVR_UNUSED_STRUCT_PAD)
+# define OVR_UNUSED_STRUCT_PAD(padName, size) char padName[size];
+# endif
+
+# ifdef __cplusplus
+extern "C" {
+# endif
+
+typedef int32_t ovrResult;
+
+typedef enum {
+ ovrSuccess = 0,
+} ovrSuccessType;
+
+typedef char ovrBool;
+typedef struct OVR_ALIGNAS(4) {
+ float r, g, b, a;
+} ovrColorf;
+typedef struct OVR_ALIGNAS(4) {
+ int x, y;
+} ovrVector2i;
+typedef struct OVR_ALIGNAS(4) {
+ int w, h;
+} ovrSizei;
+typedef struct OVR_ALIGNAS(4) {
+ ovrVector2i Pos;
+ ovrSizei Size;
+} ovrRecti;
+typedef struct OVR_ALIGNAS(4) {
+ float x, y, z, w;
+} ovrQuatf;
+typedef struct OVR_ALIGNAS(4) {
+ float x, y;
+} ovrVector2f;
+typedef struct OVR_ALIGNAS(4) {
+ float x, y, z;
+} ovrVector3f;
+typedef struct OVR_ALIGNAS(4) {
+ float M[4][4];
+} ovrMatrix4f;
+
+typedef struct OVR_ALIGNAS(4) {
+ ovrQuatf Orientation;
+ ovrVector3f Position;
+} ovrPosef;
+
+typedef struct OVR_ALIGNAS(8) {
+ ovrPosef ThePose;
+ ovrVector3f AngularVelocity;
+ ovrVector3f LinearVelocity;
+ ovrVector3f AngularAcceleration;
+ ovrVector3f LinearAcceleration;
+ OVR_UNUSED_STRUCT_PAD(pad0, 4)
+ double TimeInSeconds;
+} ovrPoseStatef;
+
+typedef struct {
+ float UpTan;
+ float DownTan;
+ float LeftTan;
+ float RightTan;
+} ovrFovPort;
+
+typedef enum {
+ ovrHmd_None = 0,
+ ovrHmd_DK1 = 3,
+ ovrHmd_DKHD = 4,
+ ovrHmd_DK2 = 6,
+ ovrHmd_CB = 8,
+ ovrHmd_Other = 9,
+ ovrHmd_E3_2015 = 10,
+ ovrHmd_ES06 = 11,
+ ovrHmd_ES09 = 12,
+ ovrHmd_ES11 = 13,
+ ovrHmd_CV1 = 14,
+ ovrHmd_EnumSize = 0x7fffffff
+} ovrHmdType;
+
+typedef enum {
+ ovrHmdCap_DebugDevice = 0x0010,
+ ovrHmdCap_EnumSize = 0x7fffffff
+} ovrHmdCaps;
+
+typedef enum {
+ ovrTrackingCap_Orientation = 0x0010,
+ ovrTrackingCap_MagYawCorrection = 0x0020,
+ ovrTrackingCap_Position = 0x0040,
+ ovrTrackingCap_EnumSize = 0x7fffffff
+} ovrTrackingCaps;
+
+typedef enum {
+ ovrExtension_TextureLayout_Octilinear = 0,
+ ovrExtension_Count,
+ ovrExtension_EnumSize = 0x7fffffff
+} ovrExtensions;
+
+typedef enum {
+ ovrEye_Left = 0,
+ ovrEye_Right = 1,
+ ovrEye_Count = 2,
+ ovrEye_EnumSize = 0x7fffffff
+} ovrEyeType;
+
+typedef enum {
+ ovrTrackingOrigin_EyeLevel = 0,
+ ovrTrackingOrigin_FloorLevel = 1,
+ ovrTrackingOrigin_Count = 2, ///< \internal Count of enumerated elements.
+ ovrTrackingOrigin_EnumSize = 0x7fffffff ///< \internal Force type int32_t.
+} ovrTrackingOrigin;
+
+typedef struct OVR_ALIGNAS(OVR_PTR_SIZE) {
+ char Reserved[8];
+} ovrGraphicsLuid;
+
+typedef struct OVR_ALIGNAS(OVR_PTR_SIZE) {
+ ovrHmdType Type;
+ OVR_ON64(OVR_UNUSED_STRUCT_PAD(pad0, 4))
+ char ProductName[64];
+ char Manufacturer[64];
+ short VendorId;
+ short ProductId;
+ char SerialNumber[24];
+ short FirmwareMajor;
+ short FirmwareMinor;
+ unsigned int AvailableHmdCaps;
+ unsigned int DefaultHmdCaps;
+ unsigned int AvailableTrackingCaps;
+ unsigned int DefaultTrackingCaps;
+ ovrFovPort DefaultEyeFov[ovrEye_Count];
+ ovrFovPort MaxEyeFov[ovrEye_Count];
+ ovrSizei Resolution;
+ float DisplayRefreshRate;
+ OVR_ON64(OVR_UNUSED_STRUCT_PAD(pad1, 4))
+} ovrHmdDesc;
+
+typedef struct ovrHmdStruct* ovrSession;
+
+# ifdef XP_WIN
+typedef uint32_t ovrProcessId;
+# else
+typedef pid_t ovrProcessId;
+# endif
+
+typedef enum {
+ ovrStatus_OrientationTracked = 0x0001,
+ ovrStatus_PositionTracked = 0x0002,
+ ovrStatus_EnumSize = 0x7fffffff
+} ovrStatusBits;
+
+typedef struct OVR_ALIGNAS(OVR_PTR_SIZE) {
+ float FrustumHFovInRadians;
+ float FrustumVFovInRadians;
+ float FrustumNearZInMeters;
+ float FrustumFarZInMeters;
+} ovrTrackerDesc;
+
+typedef enum {
+ ovrTracker_Connected = 0x0020,
+ ovrTracker_PoseTracked = 0x0004
+} ovrTrackerFlags;
+
+typedef struct OVR_ALIGNAS(8) {
+ unsigned int TrackerFlags;
+ ovrPosef Pose;
+ ovrPosef LeveledPose;
+ OVR_UNUSED_STRUCT_PAD(pad0, 4)
+} ovrTrackerPose;
+
+typedef struct OVR_ALIGNAS(8) {
+ ovrPoseStatef HeadPose;
+ unsigned int StatusFlags;
+ ovrPoseStatef HandPoses[2];
+ unsigned int HandStatusFlags[2];
+ ovrPosef CalibratedOrigin;
+} ovrTrackingState;
+
+typedef struct OVR_ALIGNAS(4) {
+ ovrEyeType Eye;
+ ovrFovPort Fov;
+ ovrRecti DistortedViewport;
+ ovrVector2f PixelsPerTanAngleAtCenter;
+ ovrPosef HmdToEyePose;
+} ovrEyeRenderDesc;
+
+typedef struct OVR_ALIGNAS(4) {
+ float Projection22;
+ float Projection23;
+ float Projection32;
+} ovrTimewarpProjectionDesc;
+
+typedef struct OVR_ALIGNAS(4) {
+ ovrPosef HmdToEyePose[ovrEye_Count];
+ float HmdSpaceToWorldScaleInMeters;
+} ovrViewScaleDesc;
+
+typedef enum {
+ ovrTexture_2D,
+ ovrTexture_2D_External,
+ ovrTexture_Cube,
+ ovrTexture_Count,
+ ovrTexture_EnumSize = 0x7fffffff
+} ovrTextureType;
+
+typedef enum {
+ ovrTextureBind_None,
+ ovrTextureBind_DX_RenderTarget = 0x0001,
+ ovrTextureBind_DX_UnorderedAccess = 0x0002,
+ ovrTextureBind_DX_DepthStencil = 0x0004,
+ ovrTextureBind_EnumSize = 0x7fffffff
+} ovrTextureBindFlags;
+
+typedef enum {
+ OVR_FORMAT_UNKNOWN = 0,
+ OVR_FORMAT_B5G6R5_UNORM = 1,
+ OVR_FORMAT_B5G5R5A1_UNORM = 2,
+ OVR_FORMAT_B4G4R4A4_UNORM = 3,
+ OVR_FORMAT_R8G8B8A8_UNORM = 4,
+ OVR_FORMAT_R8G8B8A8_UNORM_SRGB = 5,
+ OVR_FORMAT_B8G8R8A8_UNORM = 6,
+ OVR_FORMAT_B8G8R8A8_UNORM_SRGB = 7,
+ OVR_FORMAT_B8G8R8X8_UNORM = 8,
+ OVR_FORMAT_B8G8R8X8_UNORM_SRGB = 9,
+ OVR_FORMAT_R16G16B16A16_FLOAT = 10,
+ OVR_FORMAT_R11G11B10_FLOAT = 25,
+ OVR_FORMAT_D16_UNORM = 11,
+ OVR_FORMAT_D24_UNORM_S8_UINT = 12,
+ OVR_FORMAT_D32_FLOAT = 13,
+ OVR_FORMAT_D32_FLOAT_S8X24_UINT = 14,
+ OVR_FORMAT_BC1_UNORM = 15,
+ OVR_FORMAT_BC1_UNORM_SRGB = 16,
+ OVR_FORMAT_BC2_UNORM = 17,
+ OVR_FORMAT_BC2_UNORM_SRGB = 18,
+ OVR_FORMAT_BC3_UNORM = 19,
+ OVR_FORMAT_BC3_UNORM_SRGB = 20,
+ OVR_FORMAT_BC6H_UF16 = 21,
+ OVR_FORMAT_BC6H_SF16 = 22,
+ OVR_FORMAT_BC7_UNORM = 23,
+ OVR_FORMAT_BC7_UNORM_SRGB = 24,
+
+ OVR_FORMAT_ENUMSIZE = 0x7fffffff
+} ovrTextureFormat;
+
+typedef enum {
+ ovrTextureMisc_None,
+ ovrTextureMisc_DX_Typeless = 0x0001,
+ ovrTextureMisc_AllowGenerateMips = 0x0002,
+ ovrTextureMisc_ProtectedContent = 0x0004,
+ ovrTextureMisc_AutoGenerateMips = 0x0008,
+ ovrTextureMisc_EnumSize = 0x7fffffff
+} ovrTextureFlags;
+
+typedef struct {
+ ovrTextureType Type;
+ ovrTextureFormat Format;
+ int ArraySize;
+ int Width;
+ int Height;
+ int MipLevels;
+ int SampleCount;
+ ovrBool StaticImage;
+ unsigned int MiscFlags;
+ unsigned int BindFlags;
+} ovrTextureSwapChainDesc;
+
+typedef struct {
+ ovrTextureFormat Format;
+ int Width;
+ int Height;
+ unsigned int MiscFlags;
+ unsigned int MirrorOptions;
+} ovrMirrorTextureDesc;
+
+typedef struct ovrTextureSwapChainData* ovrTextureSwapChain;
+typedef struct ovrMirrorTextureData* ovrMirrorTexture;
+
+typedef enum {
+ ovrButton_A = 0x00000001,
+ ovrButton_B = 0x00000002,
+ ovrButton_RThumb = 0x00000004,
+ ovrButton_RShoulder = 0x00000008,
+ ovrButton_X = 0x00000100,
+ ovrButton_Y = 0x00000200,
+ ovrButton_LThumb = 0x00000400,
+ ovrButton_LShoulder = 0x00000800,
+ ovrButton_Up = 0x00010000,
+ ovrButton_Down = 0x00020000,
+ ovrButton_Left = 0x00040000,
+ ovrButton_Right = 0x00080000,
+ ovrButton_Enter = 0x00100000,
+ ovrButton_Back = 0x00200000,
+ ovrButton_VolUp = 0x00400000,
+ ovrButton_VolDown = 0x00800000,
+ ovrButton_Home = 0x01000000,
+ ovrButton_Private = ovrButton_VolUp | ovrButton_VolDown | ovrButton_Home,
+ ovrButton_RMask =
+ ovrButton_A | ovrButton_B | ovrButton_RThumb | ovrButton_RShoulder,
+ ovrButton_LMask = ovrButton_X | ovrButton_Y | ovrButton_LThumb |
+ ovrButton_LShoulder | ovrButton_Enter,
+ ovrButton_EnumSize = 0x7fffffff
+} ovrButton;
+
+typedef enum {
+ ovrTouch_A = ovrButton_A,
+ ovrTouch_B = ovrButton_B,
+ ovrTouch_RThumb = ovrButton_RThumb,
+ ovrTouch_RThumbRest = 0x00000008,
+ ovrTouch_RIndexTrigger = 0x00000010,
+ ovrTouch_RButtonMask = ovrTouch_A | ovrTouch_B | ovrTouch_RThumb |
+ ovrTouch_RThumbRest | ovrTouch_RIndexTrigger,
+ ovrTouch_X = ovrButton_X,
+ ovrTouch_Y = ovrButton_Y,
+ ovrTouch_LThumb = ovrButton_LThumb,
+ ovrTouch_LThumbRest = 0x00000800,
+ ovrTouch_LIndexTrigger = 0x00001000,
+ ovrTouch_LButtonMask = ovrTouch_X | ovrTouch_Y | ovrTouch_LThumb |
+ ovrTouch_LThumbRest | ovrTouch_LIndexTrigger,
+ ovrTouch_RIndexPointing = 0x00000020,
+ ovrTouch_RThumbUp = 0x00000040,
+ ovrTouch_LIndexPointing = 0x00002000,
+ ovrTouch_LThumbUp = 0x00004000,
+ ovrTouch_RPoseMask = ovrTouch_RIndexPointing | ovrTouch_RThumbUp,
+ ovrTouch_LPoseMask = ovrTouch_LIndexPointing | ovrTouch_LThumbUp,
+ ovrTouch_EnumSize = 0x7fffffff
+} ovrTouch;
+
+typedef struct OVR_ALIGNAS(OVR_PTR_SIZE) {
+ int SampleRateHz;
+ int SampleSizeInBytes;
+ int QueueMinSizeToAvoidStarvation;
+ int SubmitMinSamples;
+ int SubmitMaxSamples;
+ int SubmitOptimalSamples;
+} ovrTouchHapticsDesc;
+
+typedef enum {
+ ovrControllerType_None = 0x0000,
+ ovrControllerType_LTouch = 0x0001,
+ ovrControllerType_RTouch = 0x0002,
+ ovrControllerType_Touch =
+ (ovrControllerType_LTouch | ovrControllerType_RTouch),
+ ovrControllerType_Remote = 0x0004,
+ ovrControllerType_XBox = 0x0010,
+ ovrControllerType_Object0 = 0x0100,
+ ovrControllerType_Object1 = 0x0200,
+ ovrControllerType_Object2 = 0x0400,
+ ovrControllerType_Object3 = 0x0800,
+ ovrControllerType_Active = 0xffffffff,
+ ovrControllerType_EnumSize = 0x7fffffff
+} ovrControllerType;
+
+typedef enum { ovrHapticsBufferSubmit_Enqueue } ovrHapticsBufferSubmitMode;
+
+# define OVR_HAPTICS_BUFFER_SAMPLES_MAX 256
+
+typedef struct {
+ const void* Samples;
+ int SamplesCount;
+ ovrHapticsBufferSubmitMode SubmitMode;
+} ovrHapticsBuffer;
+
+typedef struct {
+ int RemainingQueueSpace;
+ int SamplesQueued;
+} ovrHapticsPlaybackState;
+
+typedef enum {
+ ovrTrackedDevice_None = 0x0000,
+ ovrTrackedDevice_HMD = 0x0001,
+ ovrTrackedDevice_LTouch = 0x0002,
+ ovrTrackedDevice_RTouch = 0x0004,
+ ovrTrackedDevice_Touch = (ovrTrackedDevice_LTouch | ovrTrackedDevice_RTouch),
+ ovrTrackedDevice_Object0 = 0x0010,
+ ovrTrackedDevice_Object1 = 0x0020,
+ ovrTrackedDevice_Object2 = 0x0040,
+ ovrTrackedDevice_Object3 = 0x0080,
+ ovrTrackedDevice_All = 0xFFFF,
+} ovrTrackedDeviceType;
+
+typedef enum {
+ ovrBoundary_Outer = 0x0001,
+ ovrBoundary_PlayArea = 0x0100,
+} ovrBoundaryType;
+
+typedef struct {
+ ovrColorf Color;
+} ovrBoundaryLookAndFeel;
+
+typedef struct {
+ ovrBool IsTriggering;
+ float ClosestDistance;
+ ovrVector3f ClosestPoint;
+ ovrVector3f ClosestPointNormal;
+} ovrBoundaryTestResult;
+
+typedef enum {
+ ovrHand_Left = 0,
+ ovrHand_Right = 1,
+ ovrHand_Count = 2,
+ ovrHand_EnumSize = 0x7fffffff
+} ovrHandType;
+
+typedef struct {
+ double TimeInSeconds;
+ unsigned int Buttons;
+ unsigned int Touches;
+ float IndexTrigger[ovrHand_Count];
+ float HandTrigger[ovrHand_Count];
+ ovrVector2f Thumbstick[ovrHand_Count];
+ ovrControllerType ControllerType;
+ float IndexTriggerNoDeadzone[ovrHand_Count];
+ float HandTriggerNoDeadzone[ovrHand_Count];
+ ovrVector2f ThumbstickNoDeadzone[ovrHand_Count];
+ float IndexTriggerRaw[ovrHand_Count];
+ float HandTriggerRaw[ovrHand_Count];
+ ovrVector2f ThumbstickRaw[ovrHand_Count];
+} ovrInputState;
+
+typedef struct {
+ double LastChangedTime;
+ ovrFovPort FOVPort;
+ float VirtualNearPlaneDistanceMeters;
+ float VirtualFarPlaneDistanceMeters;
+ ovrSizei ImageSensorPixelResolution;
+ ovrMatrix4f LensDistortionMatrix;
+ double ExposurePeriodSeconds;
+ double ExposureDurationSeconds;
+} ovrCameraIntrinsics;
+
+typedef enum {
+ ovrCameraStatus_None = 0x0,
+ ovrCameraStatus_Connected = 0x1,
+ ovrCameraStatus_Calibrating = 0x2,
+ ovrCameraStatus_CalibrationFailed = 0x4,
+ ovrCameraStatus_Calibrated = 0x8,
+ ovrCameraStatus_EnumSize = 0x7fffffff
+} ovrCameraStatusFlags;
+
+typedef struct {
+ double LastChangedTimeSeconds;
+ unsigned int CameraStatusFlags;
+ ovrTrackedDeviceType AttachedToDevice;
+ ovrPosef RelativePose;
+ double LastExposureTimeSeconds;
+ double ExposureLatencySeconds;
+ double AdditionalLatencySeconds;
+} ovrCameraExtrinsics;
+
+# define OVR_EXTERNAL_CAMERA_NAME_SIZE 32
+typedef struct {
+ char Name[OVR_EXTERNAL_CAMERA_NAME_SIZE];
+ ovrCameraIntrinsics Intrinsics;
+ ovrCameraExtrinsics Extrinsics;
+} ovrExternalCamera;
+
+typedef enum {
+ ovrInit_Debug = 0x00000001,
+ ovrInit_RequestVersion = 0x00000004,
+ ovrInit_Invisible = 0x00000010,
+ ovrInit_MixedRendering = 0x00000020,
+ ovrInit_FocusAware = 0x00000040,
+ ovrinit_WritableBits = 0x00ffffff,
+ ovrInit_EnumSize = 0x7fffffff
+} ovrInitFlags;
+
+typedef enum {
+ ovrLogLevel_Debug = 0,
+ ovrLogLevel_Info = 1,
+ ovrLogLevel_Error = 2,
+ ovrLogLevel_EnumSize = 0x7fffffff
+} ovrLogLevel;
+
+typedef void(OVR_PFN* ovrLogCallback)(uintptr_t userData, int level,
+ const char* message);
+
+typedef struct OVR_ALIGNAS(8) {
+ uint32_t Flags;
+ uint32_t RequestedMinorVersion;
+ ovrLogCallback LogCallback;
+ uintptr_t UserData;
+ uint32_t ConnectionTimeoutMS;
+ OVR_ON64(OVR_UNUSED_STRUCT_PAD(pad0, 4))
+} ovrInitParams;
+
+typedef ovrResult(OVR_PFN* pfn_ovr_Initialize)(const ovrInitParams* params);
+typedef void(OVR_PFN* pfn_ovr_Shutdown)();
+
+typedef struct {
+ ovrResult Result;
+ char ErrorString[512];
+} ovrErrorInfo;
+
+typedef void(OVR_PFN* pfn_ovr_GetLastErrorInfo)(ovrErrorInfo* errorInfo);
+typedef const char*(OVR_PFN* pfn_ovr_GetVersionString)();
+typedef int(OVR_PFN* pfn_ovr_TraceMessage)(int level, const char* message);
+typedef ovrResult(OVR_PFN* pfn_ovr_IdentifyClient)(const char* identity);
+typedef ovrHmdDesc(OVR_PFN* pfn_ovr_GetHmdDesc)(ovrSession session);
+typedef unsigned int(OVR_PFN* pfn_ovr_GetTrackerCount)(ovrSession session);
+typedef ovrTrackerDesc(OVR_PFN* pfn_ovr_GetTrackerDesc)(
+ ovrSession session, unsigned int trackerDescIndex);
+typedef ovrResult(OVR_PFN* pfn_ovr_Create)(ovrSession* pSession,
+ ovrGraphicsLuid* pLuid);
+typedef void(OVR_PFN* pfn_ovr_Destroy)(ovrSession session);
+
+typedef struct {
+ ovrBool IsVisible;
+ ovrBool HmdPresent;
+ ovrBool HmdMounted;
+ ovrBool DisplayLost;
+ ovrBool ShouldQuit;
+ ovrBool ShouldRecenter;
+ ovrBool HasInputFocus;
+ ovrBool OverlayPresent;
+} ovrSessionStatus;
+
+typedef ovrResult(OVR_PFN* pfn_ovr_GetSessionStatus)(
+ ovrSession session, ovrSessionStatus* sessionStatus);
+typedef ovrResult(OVR_PFN* pfn_ovr_IsExtensionSupported)(
+ ovrSession session, ovrExtensions extension,
+ ovrBool* outExtensionSupported);
+typedef ovrResult(OVR_PFN* pfn_ovr_EnableExtension)(ovrSession session,
+ ovrExtensions extension);
+typedef ovrResult(OVR_PFN* pfn_ovr_SetTrackingOriginType)(
+ ovrSession session, ovrTrackingOrigin origin);
+typedef ovrTrackingOrigin(OVR_PFN* pfn_ovr_GetTrackingOriginType)(
+ ovrSession session);
+typedef ovrResult(OVR_PFN* pfn_ovr_RecenterTrackingOrigin)(ovrSession session);
+typedef ovrResult(OVR_PFN* pfn_ovr_SpecifyTrackingOrigin)(ovrSession session,
+ ovrPosef originPose);
+typedef void(OVR_PFN* pfn_ovr_ClearShouldRecenterFlag)(ovrSession session);
+typedef ovrTrackingState(OVR_PFN* pfn_ovr_GetTrackingState)(
+ ovrSession session, double absTime, ovrBool latencyMarker);
+typedef ovrResult(OVR_PFN* pfn_ovr_GetDevicePoses)(
+ ovrSession session, ovrTrackedDeviceType* deviceTypes, int deviceCount,
+ double absTime, ovrPoseStatef* outDevicePoses);
+typedef ovrTrackerPose(OVR_PFN* pfn_ovr_GetTrackerPose)(
+ ovrSession session, unsigned int trackerPoseIndex);
+typedef ovrResult(OVR_PFN* pfn_ovr_GetInputState)(
+ ovrSession session, ovrControllerType controllerType,
+ ovrInputState* inputState);
+typedef unsigned int(OVR_PFN* pfn_ovr_GetConnectedControllerTypes)(
+ ovrSession session);
+typedef ovrTouchHapticsDesc(OVR_PFN* pfn_ovr_GetTouchHapticsDesc)(
+ ovrSession session, ovrControllerType controllerType);
+typedef ovrResult(OVR_PFN* pfn_ovr_SetControllerVibration)(
+ ovrSession session, ovrControllerType controllerType, float frequency,
+ float amplitude);
+typedef ovrResult(OVR_PFN* pfn_ovr_SubmitControllerVibration)(
+ ovrSession session, ovrControllerType controllerType,
+ const ovrHapticsBuffer* buffer);
+typedef ovrResult(OVR_PFN* pfn_ovr_GetControllerVibrationState)(
+ ovrSession session, ovrControllerType controllerType,
+ ovrHapticsPlaybackState* outState);
+typedef ovrResult(OVR_PFN* pfn_ovr_TestBoundary)(
+ ovrSession session, ovrTrackedDeviceType deviceBitmask,
+ ovrBoundaryType boundaryType, ovrBoundaryTestResult* outTestResult);
+typedef ovrResult(OVR_PFN* pfn_ovr_TestBoundaryPoint)(
+ ovrSession session, const ovrVector3f* point,
+ ovrBoundaryType singleBoundaryType, ovrBoundaryTestResult* outTestResult);
+typedef ovrResult(OVR_PFN* pfn_ovr_SetBoundaryLookAndFeel)(
+ ovrSession session, const ovrBoundaryLookAndFeel* lookAndFeel);
+typedef ovrResult(OVR_PFN* pfn_ovr_ResetBoundaryLookAndFeel)(
+ ovrSession session);
+typedef ovrResult(OVR_PFN* pfn_ovr_GetBoundaryGeometry)(
+ ovrSession session, ovrBoundaryType boundaryType,
+ ovrVector3f* outFloorPoints, int* outFloorPointsCount);
+typedef ovrResult(OVR_PFN* pfn_ovr_GetBoundaryDimensions)(
+ ovrSession session, ovrBoundaryType boundaryType,
+ ovrVector3f* outDimensions);
+typedef ovrResult(OVR_PFN* pfn_ovr_GetBoundaryVisible)(ovrSession session,
+ ovrBool* outIsVisible);
+typedef ovrResult(OVR_PFN* pfn_ovr_RequestBoundaryVisible)(ovrSession session,
+ ovrBool visible);
+
+enum { ovrMaxLayerCount = 16 };
+
+typedef enum {
+ ovrLayerType_Disabled = 0,
+ ovrLayerType_EyeFov = 1,
+ ovrLayerType_Quad = 3,
+ ovrLayerType_EyeMatrix = 5,
+ ovrLayerType_EyeFovMultires = 7,
+ ovrLayerType_Cube = 10,
+ ovrLayerType_EnumSize = 0x7fffffff
+} ovrLayerType;
+
+typedef enum {
+ ovrLayerFlag_HighQuality = 0x01,
+ ovrLayerFlag_TextureOriginAtBottomLeft = 0x02,
+ ovrLayerFlag_HeadLocked = 0x04
+} ovrLayerFlags;
+
+typedef struct OVR_ALIGNAS(OVR_PTR_SIZE) {
+ ovrLayerType Type;
+ unsigned Flags;
+} ovrLayerHeader;
+
+typedef struct OVR_ALIGNAS(OVR_PTR_SIZE) {
+ ovrLayerHeader Header;
+ ovrTextureSwapChain ColorTexture[ovrEye_Count];
+ ovrRecti Viewport[ovrEye_Count];
+ ovrFovPort Fov[ovrEye_Count];
+ ovrPosef RenderPose[ovrEye_Count];
+ double SensorSampleTime;
+} ovrLayerEyeFov;
+
+typedef enum {
+ ovrTextureLayout_Rectilinear = 0,
+ ovrTextureLayout_Octilinear = 1,
+ ovrTextureLayout_EnumSize = 0x7fffffff
+} ovrTextureLayout;
+
+typedef struct OVR_ALIGNAS(OVR_PTR_SIZE) {
+ float WarpLeft;
+ float WarpRight;
+ float WarpUp;
+ float WarpDown;
+ float SizeLeft;
+ float SizeRight;
+ float SizeUp;
+ float SizeDown;
+
+} ovrTextureLayoutOctilinear;
+
+typedef union OVR_ALIGNAS(OVR_PTR_SIZE) {
+ ovrTextureLayoutOctilinear Octilinear[ovrEye_Count];
+} ovrTextureLayoutDesc_Union;
+
+typedef struct OVR_ALIGNAS(OVR_PTR_SIZE) {
+ ovrLayerHeader Header;
+ ovrTextureSwapChain ColorTexture[ovrEye_Count];
+ ovrRecti Viewport[ovrEye_Count];
+ ovrFovPort Fov[ovrEye_Count];
+ ovrPosef RenderPose[ovrEye_Count];
+ double SensorSampleTime;
+ ovrTextureLayout TextureLayout;
+ ovrTextureLayoutDesc_Union TextureLayoutDesc;
+} ovrLayerEyeFovMultires;
+
+typedef struct OVR_ALIGNAS(OVR_PTR_SIZE) {
+ ovrLayerHeader Header;
+ ovrTextureSwapChain ColorTexture[ovrEye_Count];
+ ovrRecti Viewport[ovrEye_Count];
+ ovrPosef RenderPose[ovrEye_Count];
+ ovrMatrix4f Matrix[ovrEye_Count];
+ double SensorSampleTime;
+} ovrLayerEyeMatrix;
+
+typedef struct OVR_ALIGNAS(OVR_PTR_SIZE) {
+ ovrLayerHeader Header;
+ ovrTextureSwapChain ColorTexture;
+ ovrRecti Viewport;
+ ovrPosef QuadPoseCenter;
+ ovrVector2f QuadSize;
+} ovrLayerQuad;
+
+typedef struct OVR_ALIGNAS(OVR_PTR_SIZE) {
+ ovrLayerHeader Header;
+ ovrQuatf Orientation;
+ ovrTextureSwapChain CubeMapTexture;
+} ovrLayerCube;
+
+typedef union {
+ ovrLayerHeader Header;
+ ovrLayerEyeFov EyeFov;
+ ovrLayerQuad Quad;
+ ovrLayerEyeFovMultires Multires;
+ ovrLayerCube Cube;
+} ovrLayer_Union;
+
+typedef ovrResult(OVR_PFN* pfn_ovr_GetTextureSwapChainLength)(
+ ovrSession session, ovrTextureSwapChain chain, int* out_Length);
+typedef ovrResult(OVR_PFN* pfn_ovr_GetTextureSwapChainCurrentIndex)(
+ ovrSession session, ovrTextureSwapChain chain, int* out_Index);
+typedef ovrResult(OVR_PFN* pfn_ovr_GetTextureSwapChainDesc)(
+ ovrSession session, ovrTextureSwapChain chain,
+ ovrTextureSwapChainDesc* out_Desc);
+typedef ovrResult(OVR_PFN* pfn_ovr_CommitTextureSwapChain)(
+ ovrSession session, ovrTextureSwapChain chain);
+typedef void(OVR_PFN* pfn_ovr_DestroyTextureSwapChain)(
+ ovrSession session, ovrTextureSwapChain chain);
+typedef void(OVR_PFN* pfn_ovr_DestroyMirrorTexture)(
+ ovrSession session, ovrMirrorTexture mirrorTexture);
+typedef ovrSizei(OVR_PFN* pfn_ovr_GetFovTextureSize)(
+ ovrSession session, ovrEyeType eye, ovrFovPort fov,
+ float pixelsPerDisplayPixel);
+typedef ovrEyeRenderDesc(OVR_PFN* pfn_ovr_GetRenderDesc2)(ovrSession session,
+ ovrEyeType eyeType,
+ ovrFovPort fov);
+typedef ovrResult(OVR_PFN* pfn_ovr_WaitToBeginFrame)(ovrSession session,
+ long long frameIndex);
+typedef ovrResult(OVR_PFN* pfn_ovr_BeginFrame)(ovrSession session,
+ long long frameIndex);
+typedef ovrResult(OVR_PFN* pfn_ovr_EndFrame)(
+ ovrSession session, long long frameIndex,
+ const ovrViewScaleDesc* viewScaleDesc,
+ ovrLayerHeader const* const* layerPtrList, unsigned int layerCount);
+typedef ovrResult(OVR_PFN* pfn_ovr_SubmitFrame)(
+ ovrSession session, long long frameIndex,
+ const ovrViewScaleDesc* viewScaleDesc,
+ ovrLayerHeader const* const* layerPtrList, unsigned int layerCount);
+
+typedef struct OVR_ALIGNAS(4) {
+ int HmdVsyncIndex;
+ int AppFrameIndex;
+ int AppDroppedFrameCount;
+ float AppMotionToPhotonLatency;
+ float AppQueueAheadTime;
+ float AppCpuElapsedTime;
+ float AppGpuElapsedTime;
+ int CompositorFrameIndex;
+ int CompositorDroppedFrameCount;
+ float CompositorLatency;
+ float CompositorCpuElapsedTime;
+ float CompositorGpuElapsedTime;
+ float CompositorCpuStartToGpuEndElapsedTime;
+ float CompositorGpuEndToVsyncElapsedTime;
+ ovrBool AswIsActive;
+ int AswActivatedToggleCount;
+ int AswPresentedFrameCount;
+ int AswFailedFrameCount;
+} ovrPerfStatsPerCompositorFrame;
+
+enum { ovrMaxProvidedFrameStats = 5 };
+
+typedef struct OVR_ALIGNAS(4) {
+ ovrPerfStatsPerCompositorFrame FrameStats[ovrMaxProvidedFrameStats];
+ int FrameStatsCount;
+ ovrBool AnyFrameStatsDropped;
+ float AdaptiveGpuPerformanceScale;
+ ovrBool AswIsAvailable;
+ ovrProcessId VisibleProcessId;
+} ovrPerfStats;
+
+typedef ovrResult(OVR_PFN* pfn_ovr_GetPerfStats)(ovrSession session,
+ ovrPerfStats* outStats);
+typedef ovrResult(OVR_PFN* pfn_ovr_ResetPerfStats)(ovrSession session);
+typedef double(OVR_PFN* pfn_ovr_GetPredictedDisplayTime)(ovrSession session,
+ long long frameIndex);
+typedef double(OVR_PFN* pfn_ovr_GetTimeInSeconds)();
+
+typedef enum {
+ ovrPerfHud_Off = 0,
+ ovrPerfHud_PerfSummary = 1,
+ ovrPerfHud_LatencyTiming = 2,
+ ovrPerfHud_AppRenderTiming = 3,
+ ovrPerfHud_CompRenderTiming = 4,
+ ovrPerfHud_AswStats = 6,
+ ovrPerfHud_VersionInfo = 5,
+ ovrPerfHud_Count = 7,
+ ovrPerfHud_EnumSize = 0x7fffffff
+} ovrPerfHudMode;
+
+typedef enum {
+ ovrLayerHud_Off = 0,
+ ovrLayerHud_Info = 1,
+ ovrLayerHud_EnumSize = 0x7fffffff
+} ovrLayerHudMode;
+
+typedef enum {
+ ovrDebugHudStereo_Off = 0,
+ ovrDebugHudStereo_Quad = 1,
+ ovrDebugHudStereo_QuadWithCrosshair = 2,
+ ovrDebugHudStereo_CrosshairAtInfinity = 3,
+ ovrDebugHudStereo_Count,
+ ovrDebugHudStereo_EnumSize = 0x7fffffff
+} ovrDebugHudStereoMode;
+
+typedef ovrBool(OVR_PFN* pfn_ovr_GetBool)(ovrSession session,
+ const char* propertyName,
+ ovrBool defaultVal);
+typedef ovrBool(OVR_PFN* pfn_ovr_SetBool)(ovrSession session,
+ const char* propertyName,
+ ovrBool value);
+typedef int(OVR_PFN* pfn_ovr_GetInt)(ovrSession session,
+ const char* propertyName, int defaultVal);
+typedef ovrBool(OVR_PFN* pfn_ovr_SetInt)(ovrSession session,
+ const char* propertyName, int value);
+typedef float(OVR_PFN* pfn_ovr_GetFloat)(ovrSession session,
+ const char* propertyName,
+ float defaultVal);
+typedef ovrBool(OVR_PFN* pfn_ovr_SetFloat)(ovrSession session,
+ const char* propertyName,
+ float value);
+typedef unsigned int(OVR_PFN* pfn_ovr_GetFloatArray)(
+ ovrSession session, const char* propertyName, float values[],
+ unsigned int valuesCapacity);
+typedef ovrBool(OVR_PFN* pfn_ovr_SetFloatArray)(ovrSession session,
+ const char* propertyName,
+ const float values[],
+ unsigned int valuesSize);
+typedef const char*(OVR_PFN* pfn_ovr_GetString)(ovrSession session,
+ const char* propertyName,
+ const char* defaultVal);
+typedef ovrBool(OVR_PFN* pfn_ovr_SetString)(ovrSession session,
+ const char* propertyName,
+ const char* value);
+
+typedef ovrResult(OVR_PFN* pfn_ovr_GetExternalCameras)(
+ ovrSession session, ovrExternalCamera* cameras,
+ unsigned int* inoutCameraCount);
+typedef ovrResult(OVR_PFN* pfn_ovr_SetExternalCameraProperties)(
+ ovrSession session, const char* name,
+ const ovrCameraIntrinsics* const intrinsics,
+ const ovrCameraExtrinsics* const extrinsics);
+
+typedef enum {
+ ovrSuccess_NotVisible = 1000,
+ ovrSuccess_BoundaryInvalid = 1001,
+ ovrSuccess_DeviceUnavailable = 1002,
+} ovrSuccessTypes;
+
+typedef enum {
+ ovrError_MemoryAllocationFailure = -1000,
+ ovrError_InvalidSession = -1002,
+ ovrError_Timeout = -1003,
+ ovrError_NotInitialized = -1004,
+ ovrError_InvalidParameter = -1005,
+ ovrError_ServiceError = -1006,
+ ovrError_NoHmd = -1007,
+ ovrError_Unsupported = -1009,
+ ovrError_DeviceUnavailable = -1010,
+ ovrError_InvalidHeadsetOrientation = -1011,
+ ovrError_ClientSkippedDestroy = -1012,
+ ovrError_ClientSkippedShutdown = -1013,
+ ovrError_ServiceDeadlockDetected = -1014,
+ ovrError_InvalidOperation = -1015,
+ ovrError_InsufficientArraySize = -1016,
+ ovrError_NoExternalCameraInfo = -1017,
+ ovrError_LostTracking = -1018,
+ ovrError_AudioDeviceNotFound = -2001,
+ ovrError_AudioComError = -2002,
+ ovrError_Initialize = -3000,
+ ovrError_LibLoad = -3001,
+ ovrError_LibVersion = -3002,
+ ovrError_ServiceConnection = -3003,
+ ovrError_ServiceVersion = -3004,
+ ovrError_IncompatibleOS = -3005,
+ ovrError_DisplayInit = -3006,
+ ovrError_ServerStart = -3007,
+ ovrError_Reinitialization = -3008,
+ ovrError_MismatchedAdapters = -3009,
+ ovrError_LeakingResources = -3010,
+ ovrError_ClientVersion = -3011,
+ ovrError_OutOfDateOS = -3012,
+ ovrError_OutOfDateGfxDriver = -3013,
+ ovrError_IncompatibleGPU = -3014,
+ ovrError_NoValidVRDisplaySystem = -3015,
+ ovrError_Obsolete = -3016,
+ ovrError_DisabledOrDefaultAdapter = -3017,
+ ovrError_HybridGraphicsNotSupported = -3018,
+ ovrError_DisplayManagerInit = -3019,
+ ovrError_TrackerDriverInit = -3020,
+ ovrError_LibSignCheck = -3021,
+ ovrError_LibPath = -3022,
+ ovrError_LibSymbols = -3023,
+ ovrError_RemoteSession = -3024,
+ ovrError_InitializeVulkan = -3025,
+ ovrError_DisplayLost = -6000,
+ ovrError_TextureSwapChainFull = -6001,
+ ovrError_TextureSwapChainInvalid = -6002,
+ ovrError_GraphicsDeviceReset = -6003,
+ ovrError_DisplayRemoved = -6004,
+ ovrError_ContentProtectionNotAvailable = -6005,
+ ovrError_ApplicationInvisible = -6006,
+ ovrError_Disallowed = -6007,
+ ovrError_DisplayPluggedIncorrectly = -6008,
+ ovrError_RuntimeException = -7000,
+ ovrError_NoCalibration = -9000,
+ ovrError_OldVersion = -9001,
+ ovrError_MisformattedBlock = -9002,
+} ovrErrorType;
+
+# ifdef XP_WIN
+
+struct IUnknown;
+
+typedef ovrResult(OVR_PFN* pfn_ovr_CreateTextureSwapChainDX)(
+ ovrSession session, IUnknown* d3dPtr, const ovrTextureSwapChainDesc* desc,
+ ovrTextureSwapChain* out_TextureSwapChain);
+
+typedef ovrResult(OVR_PFN* pfn_ovr_GetTextureSwapChainBufferDX)(
+ ovrSession session, ovrTextureSwapChain chain, int index, IID iid,
+ void** out_Buffer);
+
+typedef ovrResult(OVR_PFN* pfn_ovr_CreateMirrorTextureDX)(
+ ovrSession session, IUnknown* d3dPtr, const ovrMirrorTextureDesc* desc,
+ ovrMirrorTexture* out_MirrorTexture);
+
+typedef ovrResult(OVR_PFN* pfn_ovr_GetMirrorTextureBufferDX)(
+ ovrSession session, ovrMirrorTexture mirrorTexture, IID iid,
+ void** out_Buffer);
+
+# endif
+
+typedef ovrResult(OVR_PFN* pfn_ovr_CreateTextureSwapChainGL)(
+ ovrSession session, const ovrTextureSwapChainDesc* desc,
+ ovrTextureSwapChain* out_TextureSwapChain);
+
+typedef ovrResult(OVR_PFN* pfn_ovr_GetTextureSwapChainBufferGL)(
+ ovrSession session, ovrTextureSwapChain chain, int index,
+ unsigned int* out_TexId);
+
+typedef ovrResult(OVR_PFN* pfn_ovr_CreateMirrorTextureGL)(
+ ovrSession session, const ovrMirrorTextureDesc* desc,
+ ovrMirrorTexture* out_MirrorTexture);
+
+typedef ovrResult(OVR_PFN* pfn_ovr_GetMirrorTextureBufferGL)(
+ ovrSession session, ovrMirrorTexture mirrorTexture,
+ unsigned int* out_TexId);
+
+# define OVR_KEY_EYE_HEIGHT "EyeHeight" // float meters
+# define OVR_DEFAULT_EYE_HEIGHT 1.675f
+
+# if !defined(OVR_SUCCESS)
+# define OVR_SUCCESS(result) (result >= 0)
+# endif
+
+# if !defined(OVR_UNQUALIFIED_SUCCESS)
+# define OVR_UNQUALIFIED_SUCCESS(result) (result == ovrSuccess)
+# endif
+
+# if !defined(OVR_FAILURE)
+# define OVR_FAILURE(result) (!OVR_SUCCESS(result))
+# endif
+
+# ifdef __cplusplus
+}
+# endif
+
+# endif /* mozilla_ovr_capi_dynamic_h_ */
+#endif /* OVR_CAPI_h */
diff --git a/gfx/vr/service/openvr/LICENSE b/gfx/vr/service/openvr/LICENSE
new file mode 100644
index 0000000000..ee83337d7f
--- /dev/null
+++ b/gfx/vr/service/openvr/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2015, Valve Corporation
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation and/or
+other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its contributors
+may be used to endorse or promote products derived from this software without
+specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/gfx/vr/service/openvr/README.md b/gfx/vr/service/openvr/README.md
new file mode 100644
index 0000000000..2161e0dba9
--- /dev/null
+++ b/gfx/vr/service/openvr/README.md
@@ -0,0 +1,13 @@
+OpenVR SDK
+---
+
+OpenVR is an API and runtime that allows access to VR hardware from multiple
+vendors without requiring that applications have specific knowledge of the
+hardware they are targeting. This repository is an SDK that contains the API
+and samples. The runtime is under SteamVR in Tools on Steam.
+
+### Documentation
+
+Documentation for the API is available on the [Github Wiki](https://github.com/ValveSoftware/openvr/wiki/API-Documentation)
+
+More information on OpenVR and SteamVR can be found on http://steamvr.com
diff --git a/gfx/vr/service/openvr/README.mozilla b/gfx/vr/service/openvr/README.mozilla
new file mode 100644
index 0000000000..5a8e502192
--- /dev/null
+++ b/gfx/vr/service/openvr/README.mozilla
@@ -0,0 +1,80 @@
+This directory contains files from the OpenVR SDK, version 1.11.11.
+
+This SDK contains the OpenVR API interface headers and functions to load the
+OpenVR runtime libraries which actually implement the functionality. The
+loading functions parse a .json file in a pre-defined location on the
+end-user's machine to get details used to bind the correct runtime library.
+The OpenVR implementation ensures forward and backwards compatibility as of
+the current version.
+
+Updated versions of the OpenVR SDK are available on Github:
+
+https://github.com/ValveSoftware/openvr
+
+
+We only use some files from the SDK:
+
+- We strip out files such as C# headers, plain C API versions, and files
+ needed only when implementing OpenVR drivers.
+
+- CMake related files, such as CMakeLists.txt are skipped as we use moz.build
+ files to configure the library.
+
+- The "src/jsoncpp.cpp" file and the "src/json" directory can be skipped. OpenVR
+ uses the jsoncpp library, which we have already imported elsewhere. If
+ warnings about using deprecated jsoncpp classes show up during compilation
+ you might need to reapply the patch in bug 1598288. It replaces uses of the
+ `Json::Reader` and `Json::StyledWriter` classes with the more modern
+ `Json::CharReaderBuilder` and `Json::StreamWriterBuilder`.
+
+
+Steps to update the library:
+
+- Copy "README.md" from the root of the openvr repo to the "gfx/vr/service/openvr"
+ directory.
+
+- Copy "headers/openvr.h" to "gfx/vr/service/openvr/headers" directory. The other files
+ in this directory can be ignored.
+
+- The rest of the files in the "src" directory and the "src/vrcommon" are copied
+ to the "gfx/vr/service/openvr/src" directory.
+
+- Update "gfx/vr/service/openvr/moz.build" when files are added or removed.
+
+- Update the "strtools_public.h" and "strtools_public.cpp" files, commenting out
+ the "Uint64ToString", "wcsncpy_s", and "strncpy_s" functions.
+ The "Uint64ToString" function name conflicts with another used in Gecko and
+ the "errno_t" return type returned by the other functions is not defined in
+ Mozilla's macOS continuous integration build environments. Fortunately, the
+ OpenVR SDK does not use these functions.
+
+- Replace the #define VR_INTERFACE in openvr.h to avoid extern'ing the functions.
+ Unlike the usual OpenVR API builds, we are not building a separate dll.
+
+- Add explicit in CVRSettingHelper constructor.
+
+- In strtools_public.cpp/.h, ensure that UTF16to8 and UTF8to16 are only
+ compiled under
+ #if defined( _WIN32 )
+ and redefine those functions to use ::WideCharToMultiByte and
+ MultiByteToWideChar, respectively. These are modified because the original
+ implementations contain unsupported try-catch.
+
+- In strtools_public.cpp, remove the definition of convert_type.
+
+- In strtools_public.cpp, remove the include of <codecvt>, as this causes
+ problems in compiling on Linux.
+
+- In pathtools_public.cpp/.h, comment out Path_UrlToFilePath and
+ Path_FilePathToUrl to avoid a compile error because 'alloca' isn't defined.
+
+- In vrpathregistry_public.cpp, CVRPathRegistry_Public::BLoadFromFile contains
+ a try-catch, which is not permitted. This code is simply commented out, but
+ Bug 1640068 - OpenVR code can fail JSON parsing and raise exceptions
+ is filed to address a safe fallback in the error condition.
+
+- Update this README.mozilla file with the new OpenVR SDK version and any
+ additional steps needed for newer versions.
+
+- Ensure that any changes made within the OpenVR files have comments including
+ the string "Mozilla" and reference this file for easy identification.
diff --git a/gfx/vr/service/openvr/headers/openvr.h b/gfx/vr/service/openvr/headers/openvr.h
new file mode 100644
index 0000000000..b95fb1db7e
--- /dev/null
+++ b/gfx/vr/service/openvr/headers/openvr.h
@@ -0,0 +1,5509 @@
+#pragma once
+
+// openvr.h
+//========= Copyright Valve Corporation ============//
+// Dynamically generated file. Do not modify this file directly.
+
+#ifndef _OPENVR_API
+#define _OPENVR_API
+
+#include <stdint.h>
+
+
+
+// version.h
+namespace vr
+{
+ static const uint32_t k_nSteamVRVersionMajor = 1;
+ static const uint32_t k_nSteamVRVersionMinor = 11;
+ static const uint32_t k_nSteamVRVersionBuild = 11;
+} // namespace vr
+
+// vrtypes.h
+#ifndef _INCLUDE_VRTYPES_H
+#define _INCLUDE_VRTYPES_H
+
+// Forward declarations to avoid requiring vulkan.h
+struct VkDevice_T;
+struct VkPhysicalDevice_T;
+struct VkInstance_T;
+struct VkQueue_T;
+
+// Forward declarations to avoid requiring d3d12.h
+struct ID3D12Resource;
+struct ID3D12CommandQueue;
+
+namespace vr
+{
+#pragma pack( push, 8 )
+
+/** A handle for a spatial anchor. This handle is only valid during the session it was created in.
+* Anchors that live beyond one session should be saved by their string descriptors. */
+typedef uint32_t SpatialAnchorHandle_t;
+
+typedef void* glSharedTextureHandle_t;
+typedef int32_t glInt_t;
+typedef uint32_t glUInt_t;
+
+// right-handed system
+// +y is up
+// +x is to the right
+// -z is forward
+// Distance unit is meters
+struct HmdMatrix34_t
+{
+ float m[3][4];
+};
+
+struct HmdMatrix33_t
+{
+ float m[3][3];
+};
+
+struct HmdMatrix44_t
+{
+ float m[4][4];
+};
+
+struct HmdVector3_t
+{
+ float v[3];
+};
+
+struct HmdVector4_t
+{
+ float v[4];
+};
+
+struct HmdVector3d_t
+{
+ double v[3];
+};
+
+struct HmdVector2_t
+{
+ float v[2];
+};
+
+struct HmdQuaternion_t
+{
+ double w, x, y, z;
+};
+
+struct HmdQuaternionf_t
+{
+ float w, x, y, z;
+};
+
+struct HmdColor_t
+{
+ float r, g, b, a;
+};
+
+struct HmdQuad_t
+{
+ HmdVector3_t vCorners[ 4 ];
+};
+
+struct HmdRect2_t
+{
+ HmdVector2_t vTopLeft;
+ HmdVector2_t vBottomRight;
+};
+
+/** Used to return the post-distortion UVs for each color channel.
+* UVs range from 0 to 1 with 0,0 in the upper left corner of the
+* source render target. The 0,0 to 1,1 range covers a single eye. */
+struct DistortionCoordinates_t
+{
+ float rfRed[2];
+ float rfGreen[2];
+ float rfBlue[2];
+};
+
+enum EVREye
+{
+ Eye_Left = 0,
+ Eye_Right = 1
+};
+
+enum ETextureType
+{
+ TextureType_Invalid = -1, // Handle has been invalidated
+ TextureType_DirectX = 0, // Handle is an ID3D11Texture
+ TextureType_OpenGL = 1, // Handle is an OpenGL texture name or an OpenGL render buffer name, depending on submit flags
+ TextureType_Vulkan = 2, // Handle is a pointer to a VRVulkanTextureData_t structure
+ TextureType_IOSurface = 3, // Handle is a macOS cross-process-sharable IOSurfaceRef, deprecated in favor of TextureType_Metal on supported platforms
+ TextureType_DirectX12 = 4, // Handle is a pointer to a D3D12TextureData_t structure
+ TextureType_DXGISharedHandle = 5, // Handle is a HANDLE DXGI share handle, only supported for Overlay render targets.
+ // this texture is used directly by our renderer, so only perform atomic (copyresource or resolve) on it
+ TextureType_Metal = 6, // Handle is a MTLTexture conforming to the MTLSharedTexture protocol. Textures submitted to IVRCompositor::Submit which
+ // are of type MTLTextureType2DArray assume layer 0 is the left eye texture (vr::EVREye::Eye_left), layer 1 is the right
+ // eye texture (vr::EVREye::Eye_Right)
+};
+
+enum EColorSpace
+{
+ ColorSpace_Auto = 0, // Assumes 'gamma' for 8-bit per component formats, otherwise 'linear'. This mirrors the DXGI formats which have _SRGB variants.
+ ColorSpace_Gamma = 1, // Texture data can be displayed directly on the display without any conversion (a.k.a. display native format).
+ ColorSpace_Linear = 2, // Same as gamma but has been converted to a linear representation using DXGI's sRGB conversion algorithm.
+};
+
+struct Texture_t
+{
+ void* handle; // See ETextureType definition above
+ ETextureType eType;
+ EColorSpace eColorSpace;
+};
+
+// Handle to a shared texture (HANDLE on Windows obtained using OpenSharedResource).
+typedef uint64_t SharedTextureHandle_t;
+#define INVALID_SHARED_TEXTURE_HANDLE ((vr::SharedTextureHandle_t)0)
+
+enum ETrackingResult
+{
+ TrackingResult_Uninitialized = 1,
+
+ TrackingResult_Calibrating_InProgress = 100,
+ TrackingResult_Calibrating_OutOfRange = 101,
+
+ TrackingResult_Running_OK = 200,
+ TrackingResult_Running_OutOfRange = 201,
+
+ TrackingResult_Fallback_RotationOnly = 300,
+};
+
+typedef uint32_t DriverId_t;
+static const uint32_t k_nDriverNone = 0xFFFFFFFF;
+
+static const uint32_t k_unMaxDriverDebugResponseSize = 32768;
+
+/** Used to pass device IDs to API calls */
+typedef uint32_t TrackedDeviceIndex_t;
+static const uint32_t k_unTrackedDeviceIndex_Hmd = 0;
+static const uint32_t k_unMaxTrackedDeviceCount = 64;
+static const uint32_t k_unTrackedDeviceIndexOther = 0xFFFFFFFE;
+static const uint32_t k_unTrackedDeviceIndexInvalid = 0xFFFFFFFF;
+
+/** Describes what kind of object is being tracked at a given ID */
+enum ETrackedDeviceClass
+{
+ TrackedDeviceClass_Invalid = 0, // the ID was not valid.
+ TrackedDeviceClass_HMD = 1, // Head-Mounted Displays
+ TrackedDeviceClass_Controller = 2, // Tracked controllers
+ TrackedDeviceClass_GenericTracker = 3, // Generic trackers, similar to controllers
+ TrackedDeviceClass_TrackingReference = 4, // Camera and base stations that serve as tracking reference points
+ TrackedDeviceClass_DisplayRedirect = 5, // Accessories that aren't necessarily tracked themselves, but may redirect video output from other tracked devices
+
+ TrackedDeviceClass_Max
+};
+
+
+/** Describes what specific role associated with a tracked device */
+enum ETrackedControllerRole
+{
+ TrackedControllerRole_Invalid = 0, // Invalid value for controller type
+ TrackedControllerRole_LeftHand = 1, // Tracked device associated with the left hand
+ TrackedControllerRole_RightHand = 2, // Tracked device associated with the right hand
+ TrackedControllerRole_OptOut = 3, // Tracked device is opting out of left/right hand selection
+ TrackedControllerRole_Treadmill = 4, // Tracked device is a treadmill or other locomotion device
+ TrackedControllerRole_Stylus = 5, // Tracked device is a stylus
+ TrackedControllerRole_Max = 5
+};
+
+
+/** Returns true if the tracked controller role is allowed to be a hand */
+inline bool IsRoleAllowedAsHand( ETrackedControllerRole eRole )
+{
+ switch ( eRole )
+ {
+ case TrackedControllerRole_Invalid:
+ case TrackedControllerRole_LeftHand:
+ case TrackedControllerRole_RightHand:
+ return true;
+ default:
+ return false;
+ }
+}
+
+
+/** describes a single pose for a tracked object */
+struct TrackedDevicePose_t
+{
+ HmdMatrix34_t mDeviceToAbsoluteTracking;
+ HmdVector3_t vVelocity; // velocity in tracker space in m/s
+ HmdVector3_t vAngularVelocity; // angular velocity in radians/s (?)
+ ETrackingResult eTrackingResult;
+ bool bPoseIsValid;
+
+ // This indicates that there is a device connected for this spot in the pose array.
+ // It could go from true to false if the user unplugs the device.
+ bool bDeviceIsConnected;
+};
+
+/** Identifies which style of tracking origin the application wants to use
+* for the poses it is requesting */
+enum ETrackingUniverseOrigin
+{
+ TrackingUniverseSeated = 0, // Poses are provided relative to the seated zero pose
+ TrackingUniverseStanding = 1, // Poses are provided relative to the safe bounds configured by the user
+ TrackingUniverseRawAndUncalibrated = 2, // Poses are provided in the coordinate system defined by the driver. It has Y up and is unified for devices of the same driver. You usually don't want this one.
+};
+
+enum EAdditionalRadioFeatures
+{
+ AdditionalRadioFeatures_None = 0x00000000,
+ AdditionalRadioFeatures_HTCLinkBox = 0x00000001,
+ AdditionalRadioFeatures_InternalDongle = 0x00000002,
+ AdditionalRadioFeatures_ExternalDongle = 0x00000004,
+};
+
+typedef uint64_t WebConsoleHandle_t;
+#define INVALID_WEB_CONSOLE_HANDLE ((vr::WebConsoleHandle_t)0)
+
+// Refers to a single container of properties
+typedef uint64_t PropertyContainerHandle_t;
+typedef uint32_t PropertyTypeTag_t;
+
+static const PropertyContainerHandle_t k_ulInvalidPropertyContainer = 0;
+static const PropertyTypeTag_t k_unInvalidPropertyTag = 0;
+
+typedef PropertyContainerHandle_t DriverHandle_t;
+static const PropertyContainerHandle_t k_ulInvalidDriverHandle = 0;
+
+// Use these tags to set/get common types as struct properties
+static const PropertyTypeTag_t k_unFloatPropertyTag = 1;
+static const PropertyTypeTag_t k_unInt32PropertyTag = 2;
+static const PropertyTypeTag_t k_unUint64PropertyTag = 3;
+static const PropertyTypeTag_t k_unBoolPropertyTag = 4;
+static const PropertyTypeTag_t k_unStringPropertyTag = 5;
+static const PropertyTypeTag_t k_unErrorPropertyTag = 6;
+static const PropertyTypeTag_t k_unDoublePropertyTag = 7;
+
+static const PropertyTypeTag_t k_unHmdMatrix34PropertyTag = 20;
+static const PropertyTypeTag_t k_unHmdMatrix44PropertyTag = 21;
+static const PropertyTypeTag_t k_unHmdVector3PropertyTag = 22;
+static const PropertyTypeTag_t k_unHmdVector4PropertyTag = 23;
+static const PropertyTypeTag_t k_unHmdVector2PropertyTag = 24;
+static const PropertyTypeTag_t k_unHmdQuadPropertyTag = 25;
+
+static const PropertyTypeTag_t k_unHiddenAreaPropertyTag = 30;
+static const PropertyTypeTag_t k_unPathHandleInfoTag = 31;
+static const PropertyTypeTag_t k_unActionPropertyTag = 32;
+static const PropertyTypeTag_t k_unInputValuePropertyTag = 33;
+static const PropertyTypeTag_t k_unWildcardPropertyTag = 34;
+static const PropertyTypeTag_t k_unHapticVibrationPropertyTag = 35;
+static const PropertyTypeTag_t k_unSkeletonPropertyTag = 36;
+
+static const PropertyTypeTag_t k_unSpatialAnchorPosePropertyTag = 40;
+static const PropertyTypeTag_t k_unJsonPropertyTag = 41;
+static const PropertyTypeTag_t k_unActiveActionSetPropertyTag = 42;
+
+static const PropertyTypeTag_t k_unOpenVRInternalReserved_Start = 1000;
+static const PropertyTypeTag_t k_unOpenVRInternalReserved_End = 10000;
+
+
+/** Each entry in this enum represents a property that can be retrieved about a
+* tracked device. Many fields are only valid for one ETrackedDeviceClass. */
+enum ETrackedDeviceProperty
+{
+ Prop_Invalid = 0,
+
+ // general properties that apply to all device classes
+ Prop_TrackingSystemName_String = 1000,
+ Prop_ModelNumber_String = 1001,
+ Prop_SerialNumber_String = 1002,
+ Prop_RenderModelName_String = 1003,
+ Prop_WillDriftInYaw_Bool = 1004,
+ Prop_ManufacturerName_String = 1005,
+ Prop_TrackingFirmwareVersion_String = 1006,
+ Prop_HardwareRevision_String = 1007,
+ Prop_AllWirelessDongleDescriptions_String = 1008,
+ Prop_ConnectedWirelessDongle_String = 1009,
+ Prop_DeviceIsWireless_Bool = 1010,
+ Prop_DeviceIsCharging_Bool = 1011,
+ Prop_DeviceBatteryPercentage_Float = 1012, // 0 is empty, 1 is full
+ Prop_StatusDisplayTransform_Matrix34 = 1013,
+ Prop_Firmware_UpdateAvailable_Bool = 1014,
+ Prop_Firmware_ManualUpdate_Bool = 1015,
+ Prop_Firmware_ManualUpdateURL_String = 1016,
+ Prop_HardwareRevision_Uint64 = 1017,
+ Prop_FirmwareVersion_Uint64 = 1018,
+ Prop_FPGAVersion_Uint64 = 1019,
+ Prop_VRCVersion_Uint64 = 1020,
+ Prop_RadioVersion_Uint64 = 1021,
+ Prop_DongleVersion_Uint64 = 1022,
+ Prop_BlockServerShutdown_Bool = 1023,
+ Prop_CanUnifyCoordinateSystemWithHmd_Bool = 1024,
+ Prop_ContainsProximitySensor_Bool = 1025,
+ Prop_DeviceProvidesBatteryStatus_Bool = 1026,
+ Prop_DeviceCanPowerOff_Bool = 1027,
+ Prop_Firmware_ProgrammingTarget_String = 1028,
+ Prop_DeviceClass_Int32 = 1029,
+ Prop_HasCamera_Bool = 1030,
+ Prop_DriverVersion_String = 1031,
+ Prop_Firmware_ForceUpdateRequired_Bool = 1032,
+ Prop_ViveSystemButtonFixRequired_Bool = 1033,
+ Prop_ParentDriver_Uint64 = 1034,
+ Prop_ResourceRoot_String = 1035,
+ Prop_RegisteredDeviceType_String = 1036,
+ Prop_InputProfilePath_String = 1037, // input profile to use for this device in the input system. Will default to tracking system name if this isn't provided
+ Prop_NeverTracked_Bool = 1038, // Used for devices that will never have a valid pose by design
+ Prop_NumCameras_Int32 = 1039,
+ Prop_CameraFrameLayout_Int32 = 1040, // EVRTrackedCameraFrameLayout value
+ Prop_CameraStreamFormat_Int32 = 1041, // ECameraVideoStreamFormat value
+ Prop_AdditionalDeviceSettingsPath_String = 1042, // driver-relative path to additional device and global configuration settings
+ Prop_Identifiable_Bool = 1043, // Whether device supports being identified from vrmonitor (e.g. blink LED, vibrate haptics, etc)
+ Prop_BootloaderVersion_Uint64 = 1044,
+ Prop_AdditionalSystemReportData_String = 1045, // additional string to include in system reports about a tracked device
+ Prop_CompositeFirmwareVersion_String = 1046, // additional FW components from a device that gets propagated into reports
+ Prop_Firmware_RemindUpdate_Bool = 1047,
+ Prop_PeripheralApplicationVersion_Uint64 = 1048,
+ Prop_ManufacturerSerialNumber_String = 1049,
+ Prop_ComputedSerialNumber_String = 1050,
+ Prop_EstimatedDeviceFirstUseTime_Int32 = 1051,
+
+ // Properties that are unique to TrackedDeviceClass_HMD
+ Prop_ReportsTimeSinceVSync_Bool = 2000,
+ Prop_SecondsFromVsyncToPhotons_Float = 2001,
+ Prop_DisplayFrequency_Float = 2002,
+ Prop_UserIpdMeters_Float = 2003,
+ Prop_CurrentUniverseId_Uint64 = 2004,
+ Prop_PreviousUniverseId_Uint64 = 2005,
+ Prop_DisplayFirmwareVersion_Uint64 = 2006,
+ Prop_IsOnDesktop_Bool = 2007,
+ Prop_DisplayMCType_Int32 = 2008,
+ Prop_DisplayMCOffset_Float = 2009,
+ Prop_DisplayMCScale_Float = 2010,
+ Prop_EdidVendorID_Int32 = 2011,
+ Prop_DisplayMCImageLeft_String = 2012,
+ Prop_DisplayMCImageRight_String = 2013,
+ Prop_DisplayGCBlackClamp_Float = 2014,
+ Prop_EdidProductID_Int32 = 2015,
+ Prop_CameraToHeadTransform_Matrix34 = 2016,
+ Prop_DisplayGCType_Int32 = 2017,
+ Prop_DisplayGCOffset_Float = 2018,
+ Prop_DisplayGCScale_Float = 2019,
+ Prop_DisplayGCPrescale_Float = 2020,
+ Prop_DisplayGCImage_String = 2021,
+ Prop_LensCenterLeftU_Float = 2022,
+ Prop_LensCenterLeftV_Float = 2023,
+ Prop_LensCenterRightU_Float = 2024,
+ Prop_LensCenterRightV_Float = 2025,
+ Prop_UserHeadToEyeDepthMeters_Float = 2026,
+ Prop_CameraFirmwareVersion_Uint64 = 2027,
+ Prop_CameraFirmwareDescription_String = 2028,
+ Prop_DisplayFPGAVersion_Uint64 = 2029,
+ Prop_DisplayBootloaderVersion_Uint64 = 2030,
+ Prop_DisplayHardwareVersion_Uint64 = 2031,
+ Prop_AudioFirmwareVersion_Uint64 = 2032,
+ Prop_CameraCompatibilityMode_Int32 = 2033,
+ Prop_ScreenshotHorizontalFieldOfViewDegrees_Float = 2034,
+ Prop_ScreenshotVerticalFieldOfViewDegrees_Float = 2035,
+ Prop_DisplaySuppressed_Bool = 2036,
+ Prop_DisplayAllowNightMode_Bool = 2037,
+ Prop_DisplayMCImageWidth_Int32 = 2038,
+ Prop_DisplayMCImageHeight_Int32 = 2039,
+ Prop_DisplayMCImageNumChannels_Int32 = 2040,
+ Prop_DisplayMCImageData_Binary = 2041,
+ Prop_SecondsFromPhotonsToVblank_Float = 2042,
+ Prop_DriverDirectModeSendsVsyncEvents_Bool = 2043,
+ Prop_DisplayDebugMode_Bool = 2044,
+ Prop_GraphicsAdapterLuid_Uint64 = 2045,
+ Prop_DriverProvidedChaperonePath_String = 2048,
+ Prop_ExpectedTrackingReferenceCount_Int32 = 2049, // expected number of sensors or basestations to reserve UI space for
+ Prop_ExpectedControllerCount_Int32 = 2050, // expected number of tracked controllers to reserve UI space for
+ Prop_NamedIconPathControllerLeftDeviceOff_String = 2051, // placeholder icon for "left" controller if not yet detected/loaded
+ Prop_NamedIconPathControllerRightDeviceOff_String = 2052, // placeholder icon for "right" controller if not yet detected/loaded
+ Prop_NamedIconPathTrackingReferenceDeviceOff_String = 2053, // placeholder icon for sensor/base if not yet detected/loaded
+ Prop_DoNotApplyPrediction_Bool = 2054, // currently no effect. was used to disable HMD pose prediction on MR, which is now done by MR driver setting velocity=0
+ Prop_CameraToHeadTransforms_Matrix34_Array = 2055,
+ Prop_DistortionMeshResolution_Int32 = 2056, // custom resolution of compositor calls to IVRSystem::ComputeDistortion
+ Prop_DriverIsDrawingControllers_Bool = 2057,
+ Prop_DriverRequestsApplicationPause_Bool = 2058,
+ Prop_DriverRequestsReducedRendering_Bool = 2059,
+ Prop_MinimumIpdStepMeters_Float = 2060,
+ Prop_AudioBridgeFirmwareVersion_Uint64 = 2061,
+ Prop_ImageBridgeFirmwareVersion_Uint64 = 2062,
+ Prop_ImuToHeadTransform_Matrix34 = 2063,
+ Prop_ImuFactoryGyroBias_Vector3 = 2064,
+ Prop_ImuFactoryGyroScale_Vector3 = 2065,
+ Prop_ImuFactoryAccelerometerBias_Vector3 = 2066,
+ Prop_ImuFactoryAccelerometerScale_Vector3 = 2067,
+ // reserved 2068
+ Prop_ConfigurationIncludesLighthouse20Features_Bool = 2069,
+ Prop_AdditionalRadioFeatures_Uint64 = 2070,
+ Prop_CameraWhiteBalance_Vector4_Array = 2071, // Prop_NumCameras_Int32-sized array of float[4] RGBG white balance calibration data (max size is vr::k_unMaxCameras)
+ Prop_CameraDistortionFunction_Int32_Array = 2072, // Prop_NumCameras_Int32-sized array of vr::EVRDistortionFunctionType values (max size is vr::k_unMaxCameras)
+ Prop_CameraDistortionCoefficients_Float_Array = 2073, // Prop_NumCameras_Int32-sized array of double[vr::k_unMaxDistortionFunctionParameters] (max size is vr::k_unMaxCameras)
+ Prop_ExpectedControllerType_String = 2074,
+ Prop_HmdTrackingStyle_Int32 = 2075, // one of EHmdTrackingStyle
+ Prop_DriverProvidedChaperoneVisibility_Bool = 2076,
+ Prop_HmdColumnCorrectionSettingPrefix_String = 2077,
+ Prop_CameraSupportsCompatibilityModes_Bool = 2078,
+
+ Prop_DisplayAvailableFrameRates_Float_Array = 2080, // populated by compositor from actual EDID list when available from GPU driver
+ Prop_DisplaySupportsMultipleFramerates_Bool = 2081, // if this is true but Prop_DisplayAvailableFrameRates_Float_Array is empty, explain to user
+ Prop_DisplayColorMultLeft_Vector3 = 2082,
+ Prop_DisplayColorMultRight_Vector3 = 2083,
+ Prop_DisplaySupportsRuntimeFramerateChange_Bool = 2084,
+ Prop_DisplaySupportsAnalogGain_Bool = 2085,
+ Prop_DisplayMinAnalogGain_Float = 2086,
+ Prop_DisplayMaxAnalogGain_Float = 2087,
+
+ // Prop_DashboardLayoutPathName_String = 2090, // DELETED
+ Prop_DashboardScale_Float = 2091,
+ Prop_IpdUIRangeMinMeters_Float = 2100,
+ Prop_IpdUIRangeMaxMeters_Float = 2101,
+
+ // Driver requested mura correction properties
+ Prop_DriverRequestedMuraCorrectionMode_Int32 = 2200,
+ Prop_DriverRequestedMuraFeather_InnerLeft_Int32 = 2201,
+ Prop_DriverRequestedMuraFeather_InnerRight_Int32 = 2202,
+ Prop_DriverRequestedMuraFeather_InnerTop_Int32 = 2203,
+ Prop_DriverRequestedMuraFeather_InnerBottom_Int32 = 2204,
+ Prop_DriverRequestedMuraFeather_OuterLeft_Int32 = 2205,
+ Prop_DriverRequestedMuraFeather_OuterRight_Int32 = 2206,
+ Prop_DriverRequestedMuraFeather_OuterTop_Int32 = 2207,
+ Prop_DriverRequestedMuraFeather_OuterBottom_Int32 = 2208,
+
+ Prop_Audio_DefaultPlaybackDeviceId_String = 2300,
+ Prop_Audio_DefaultRecordingDeviceId_String = 2301,
+ Prop_Audio_DefaultPlaybackDeviceVolume_Float = 2302,
+
+ // Properties that are unique to TrackedDeviceClass_Controller
+ Prop_AttachedDeviceId_String = 3000,
+ Prop_SupportedButtons_Uint64 = 3001,
+ Prop_Axis0Type_Int32 = 3002, // Return value is of type EVRControllerAxisType
+ Prop_Axis1Type_Int32 = 3003, // Return value is of type EVRControllerAxisType
+ Prop_Axis2Type_Int32 = 3004, // Return value is of type EVRControllerAxisType
+ Prop_Axis3Type_Int32 = 3005, // Return value is of type EVRControllerAxisType
+ Prop_Axis4Type_Int32 = 3006, // Return value is of type EVRControllerAxisType
+ Prop_ControllerRoleHint_Int32 = 3007, // Return value is of type ETrackedControllerRole
+
+ // Properties that are unique to TrackedDeviceClass_TrackingReference
+ Prop_FieldOfViewLeftDegrees_Float = 4000,
+ Prop_FieldOfViewRightDegrees_Float = 4001,
+ Prop_FieldOfViewTopDegrees_Float = 4002,
+ Prop_FieldOfViewBottomDegrees_Float = 4003,
+ Prop_TrackingRangeMinimumMeters_Float = 4004,
+ Prop_TrackingRangeMaximumMeters_Float = 4005,
+ Prop_ModeLabel_String = 4006,
+ Prop_CanWirelessIdentify_Bool = 4007, // volatile, based on radio presence and fw discovery
+ Prop_Nonce_Int32 = 4008,
+
+ // Properties that are used for user interface like icons names
+ Prop_IconPathName_String = 5000, // DEPRECATED. Value not referenced. Now expected to be part of icon path properties.
+ Prop_NamedIconPathDeviceOff_String = 5001, // {driver}/icons/icon_filename - PNG for static icon, or GIF for animation, 50x32 for headsets and 32x32 for others
+ Prop_NamedIconPathDeviceSearching_String = 5002, // {driver}/icons/icon_filename - PNG for static icon, or GIF for animation, 50x32 for headsets and 32x32 for others
+ Prop_NamedIconPathDeviceSearchingAlert_String = 5003, // {driver}/icons/icon_filename - PNG for static icon, or GIF for animation, 50x32 for headsets and 32x32 for others
+ Prop_NamedIconPathDeviceReady_String = 5004, // {driver}/icons/icon_filename - PNG for static icon, or GIF for animation, 50x32 for headsets and 32x32 for others
+ Prop_NamedIconPathDeviceReadyAlert_String = 5005, // {driver}/icons/icon_filename - PNG for static icon, or GIF for animation, 50x32 for headsets and 32x32 for others
+ Prop_NamedIconPathDeviceNotReady_String = 5006, // {driver}/icons/icon_filename - PNG for static icon, or GIF for animation, 50x32 for headsets and 32x32 for others
+ Prop_NamedIconPathDeviceStandby_String = 5007, // {driver}/icons/icon_filename - PNG for static icon, or GIF for animation, 50x32 for headsets and 32x32 for others
+ Prop_NamedIconPathDeviceAlertLow_String = 5008, // {driver}/icons/icon_filename - PNG for static icon, or GIF for animation, 50x32 for headsets and 32x32 for others
+ Prop_NamedIconPathDeviceStandbyAlert_String = 5009, // {driver}/icons/icon_filename - PNG for static icon, or GIF for animation, 50x32 for headsets and 32x32 for others
+
+ // Properties that are used by helpers, but are opaque to applications
+ Prop_DisplayHiddenArea_Binary_Start = 5100,
+ Prop_DisplayHiddenArea_Binary_End = 5150,
+ Prop_ParentContainer = 5151,
+ Prop_OverrideContainer_Uint64 = 5152,
+
+ // Properties that are unique to drivers
+ Prop_UserConfigPath_String = 6000,
+ Prop_InstallPath_String = 6001,
+ Prop_HasDisplayComponent_Bool = 6002,
+ Prop_HasControllerComponent_Bool = 6003,
+ Prop_HasCameraComponent_Bool = 6004,
+ Prop_HasDriverDirectModeComponent_Bool = 6005,
+ Prop_HasVirtualDisplayComponent_Bool = 6006,
+ Prop_HasSpatialAnchorsSupport_Bool = 6007,
+
+ // Properties that are set internally based on other information provided by drivers
+ Prop_ControllerType_String = 7000,
+ //Prop_LegacyInputProfile_String = 7001, // This is no longer used. See "legacy_binding" in the input profile instead.
+ Prop_ControllerHandSelectionPriority_Int32 = 7002, // Allows hand assignments to prefer some controllers over others. High numbers are selected over low numbers
+
+ // Vendors are free to expose private debug data in this reserved region
+ Prop_VendorSpecific_Reserved_Start = 10000,
+ Prop_VendorSpecific_Reserved_End = 10999,
+
+ Prop_TrackedDeviceProperty_Max = 1000000,
+};
+
+/** No string property will ever be longer than this length */
+static const uint32_t k_unMaxPropertyStringSize = 32 * 1024;
+
+/** Used to return errors that occur when reading properties. */
+enum ETrackedPropertyError
+{
+ TrackedProp_Success = 0,
+ TrackedProp_WrongDataType = 1,
+ TrackedProp_WrongDeviceClass = 2,
+ TrackedProp_BufferTooSmall = 3,
+ TrackedProp_UnknownProperty = 4, // Driver has not set the property (and may not ever).
+ TrackedProp_InvalidDevice = 5,
+ TrackedProp_CouldNotContactServer = 6,
+ TrackedProp_ValueNotProvidedByDevice = 7,
+ TrackedProp_StringExceedsMaximumLength = 8,
+ TrackedProp_NotYetAvailable = 9, // The property value isn't known yet, but is expected soon. Call again later.
+ TrackedProp_PermissionDenied = 10,
+ TrackedProp_InvalidOperation = 11,
+ TrackedProp_CannotWriteToWildcards = 12,
+ TrackedProp_IPCReadFailure = 13,
+ TrackedProp_OutOfMemory = 14,
+ TrackedProp_InvalidContainer = 15,
+};
+
+/** Used to drive certain text in the UI when talking about the tracking system for the HMD */
+enum EHmdTrackingStyle
+{
+ HmdTrackingStyle_Unknown = 0,
+
+ HmdTrackingStyle_Lighthouse = 1, // base stations and lasers
+ HmdTrackingStyle_OutsideInCameras = 2, // Cameras and LED, Rift 1 style
+ HmdTrackingStyle_InsideOutCameras = 3, // Cameras on HMD looking at the world
+};
+
+typedef uint64_t VRActionHandle_t;
+typedef uint64_t VRActionSetHandle_t;
+typedef uint64_t VRInputValueHandle_t;
+
+static const VRActionHandle_t k_ulInvalidActionHandle = 0;
+static const VRActionSetHandle_t k_ulInvalidActionSetHandle = 0;
+static const VRInputValueHandle_t k_ulInvalidInputValueHandle = 0;
+
+
+/** Allows the application to control what part of the provided texture will be used in the
+* frame buffer. */
+struct VRTextureBounds_t
+{
+ float uMin, vMin;
+ float uMax, vMax;
+};
+
+/** Allows specifying pose used to render provided scene texture (if different from value returned by WaitGetPoses). */
+struct VRTextureWithPose_t : public Texture_t
+{
+ HmdMatrix34_t mDeviceToAbsoluteTracking; // Actual pose used to render scene textures.
+};
+
+struct VRTextureDepthInfo_t
+{
+ void* handle; // See ETextureType definition above
+ HmdMatrix44_t mProjection;
+ HmdVector2_t vRange; // 0..1
+};
+
+struct VRTextureWithDepth_t : public Texture_t
+{
+ VRTextureDepthInfo_t depth;
+};
+
+struct VRTextureWithPoseAndDepth_t : public VRTextureWithPose_t
+{
+ VRTextureDepthInfo_t depth;
+};
+
+/** Allows the application to control how scene textures are used by the compositor when calling Submit. */
+enum EVRSubmitFlags
+{
+ // Simple render path. App submits rendered left and right eye images with no lens distortion correction applied.
+ Submit_Default = 0x00,
+
+ // App submits final left and right eye images with lens distortion already applied (lens distortion makes the images appear
+ // barrel distorted with chromatic aberration correction applied). The app would have used the data returned by
+ // vr::IVRSystem::ComputeDistortion() to apply the correct distortion to the rendered images before calling Submit().
+ Submit_LensDistortionAlreadyApplied = 0x01,
+
+ // If the texture pointer passed in is actually a renderbuffer (e.g. for MSAA in OpenGL) then set this flag.
+ Submit_GlRenderBuffer = 0x02,
+
+ // Do not use
+ Submit_Reserved = 0x04,
+
+ // Set to indicate that pTexture is a pointer to a VRTextureWithPose_t.
+ // This flag can be combined with Submit_TextureWithDepth to pass a VRTextureWithPoseAndDepth_t.
+ Submit_TextureWithPose = 0x08,
+
+ // Set to indicate that pTexture is a pointer to a VRTextureWithDepth_t.
+ // This flag can be combined with Submit_TextureWithPose to pass a VRTextureWithPoseAndDepth_t.
+ Submit_TextureWithDepth = 0x10,
+
+ // Set to indicate a discontinuity between this and the last frame.
+ // This will prevent motion smoothing from attempting to extrapolate using the pair.
+ Submit_FrameDiscontinuty = 0x20,
+};
+
+/** Data required for passing Vulkan textures to IVRCompositor::Submit.
+* Be sure to call OpenVR_Shutdown before destroying these resources.
+* Please see https://github.com/ValveSoftware/openvr/wiki/Vulkan for Vulkan-specific documentation */
+struct VRVulkanTextureData_t
+{
+ uint64_t m_nImage; // VkImage
+ VkDevice_T *m_pDevice;
+ VkPhysicalDevice_T *m_pPhysicalDevice;
+ VkInstance_T *m_pInstance;
+ VkQueue_T *m_pQueue;
+ uint32_t m_nQueueFamilyIndex;
+ uint32_t m_nWidth, m_nHeight, m_nFormat, m_nSampleCount;
+};
+
+/** Data required for passing D3D12 textures to IVRCompositor::Submit.
+* Be sure to call OpenVR_Shutdown before destroying these resources. */
+struct D3D12TextureData_t
+{
+ ID3D12Resource *m_pResource;
+ ID3D12CommandQueue *m_pCommandQueue;
+ uint32_t m_nNodeMask;
+};
+
+/** Status of the overall system or tracked objects */
+enum EVRState
+{
+ VRState_Undefined = -1,
+ VRState_Off = 0,
+ VRState_Searching = 1,
+ VRState_Searching_Alert = 2,
+ VRState_Ready = 3,
+ VRState_Ready_Alert = 4,
+ VRState_NotReady = 5,
+ VRState_Standby = 6,
+ VRState_Ready_Alert_Low = 7,
+};
+
+/** The types of events that could be posted (and what the parameters mean for each event type) */
+enum EVREventType
+{
+ VREvent_None = 0,
+
+ VREvent_TrackedDeviceActivated = 100,
+ VREvent_TrackedDeviceDeactivated = 101,
+ VREvent_TrackedDeviceUpdated = 102,
+ VREvent_TrackedDeviceUserInteractionStarted = 103,
+ VREvent_TrackedDeviceUserInteractionEnded = 104,
+ VREvent_IpdChanged = 105,
+ VREvent_EnterStandbyMode = 106,
+ VREvent_LeaveStandbyMode = 107,
+ VREvent_TrackedDeviceRoleChanged = 108,
+ VREvent_WatchdogWakeUpRequested = 109,
+ VREvent_LensDistortionChanged = 110,
+ VREvent_PropertyChanged = 111,
+ VREvent_WirelessDisconnect = 112,
+ VREvent_WirelessReconnect = 113,
+
+ VREvent_ButtonPress = 200, // data is controller
+ VREvent_ButtonUnpress = 201, // data is controller
+ VREvent_ButtonTouch = 202, // data is controller
+ VREvent_ButtonUntouch = 203, // data is controller
+
+ // VREvent_DualAnalog_Press = 250, // No longer sent
+ // VREvent_DualAnalog_Unpress = 251, // No longer sent
+ // VREvent_DualAnalog_Touch = 252, // No longer sent
+ // VREvent_DualAnalog_Untouch = 253, // No longer sent
+ // VREvent_DualAnalog_Move = 254, // No longer sent
+ // VREvent_DualAnalog_ModeSwitch1 = 255, // No longer sent
+ // VREvent_DualAnalog_ModeSwitch2 = 256, // No longer sent
+ VREvent_Modal_Cancel = 257, // Sent to overlays with the
+
+ VREvent_MouseMove = 300, // data is mouse
+ VREvent_MouseButtonDown = 301, // data is mouse
+ VREvent_MouseButtonUp = 302, // data is mouse
+ VREvent_FocusEnter = 303, // data is overlay
+ VREvent_FocusLeave = 304, // data is overlay
+ VREvent_ScrollDiscrete = 305, // data is scroll
+ VREvent_TouchPadMove = 306, // data is mouse
+ VREvent_OverlayFocusChanged = 307, // data is overlay, global event
+ VREvent_ReloadOverlays = 308,
+ VREvent_ScrollSmooth = 309, // data is scroll
+ VREvent_LockMousePosition = 310,
+ VREvent_UnlockMousePosition = 311,
+
+ VREvent_InputFocusCaptured = 400, // data is process DEPRECATED
+ VREvent_InputFocusReleased = 401, // data is process DEPRECATED
+ // VREvent_SceneFocusLost = 402, // data is process
+ // VREvent_SceneFocusGained = 403, // data is process
+ VREvent_SceneApplicationChanged = 404, // data is process - The App actually drawing the scene changed (usually to or from the compositor)
+ VREvent_SceneFocusChanged = 405, // data is process - New app got access to draw the scene
+ VREvent_InputFocusChanged = 406, // data is process
+ // VREvent_SceneApplicationSecondaryRenderingStarted = 407,
+ VREvent_SceneApplicationUsingWrongGraphicsAdapter = 408, // data is process
+ VREvent_ActionBindingReloaded = 409, // data is process - The App that action binds reloaded for
+
+ VREvent_HideRenderModels = 410, // Sent to the scene application to request hiding render models temporarily
+ VREvent_ShowRenderModels = 411, // Sent to the scene application to request restoring render model visibility
+
+ VREvent_SceneApplicationStateChanged = 412, // No data; but query VRApplications()->GetSceneApplicationState();
+
+ VREvent_ConsoleOpened = 420,
+ VREvent_ConsoleClosed = 421,
+
+ VREvent_OverlayShown = 500,
+ VREvent_OverlayHidden = 501,
+ VREvent_DashboardActivated = 502,
+ VREvent_DashboardDeactivated = 503,
+ //VREvent_DashboardThumbSelected = 504, // Sent to the overlay manager - data is overlay - No longer sent
+ VREvent_DashboardRequested = 505, // Sent to the overlay manager - data is overlay
+ VREvent_ResetDashboard = 506, // Send to the overlay manager
+ //VREvent_RenderToast = 507, // Send to the dashboard to render a toast - data is the notification ID -- no longer sent
+ VREvent_ImageLoaded = 508, // Sent to overlays when a SetOverlayRaw or SetOverlayFromFile call finishes loading
+ VREvent_ShowKeyboard = 509, // Sent to keyboard renderer in the dashboard to invoke it
+ VREvent_HideKeyboard = 510, // Sent to keyboard renderer in the dashboard to hide it
+ VREvent_OverlayGamepadFocusGained = 511, // Sent to an overlay when IVROverlay::SetFocusOverlay is called on it
+ VREvent_OverlayGamepadFocusLost = 512, // Send to an overlay when it previously had focus and IVROverlay::SetFocusOverlay is called on something else
+ VREvent_OverlaySharedTextureChanged = 513,
+ //VREvent_DashboardGuideButtonDown = 514, // These are no longer sent
+ //VREvent_DashboardGuideButtonUp = 515,
+ VREvent_ScreenshotTriggered = 516, // Screenshot button combo was pressed, Dashboard should request a screenshot
+ VREvent_ImageFailed = 517, // Sent to overlays when a SetOverlayRaw or SetOverlayfromFail fails to load
+ VREvent_DashboardOverlayCreated = 518,
+ VREvent_SwitchGamepadFocus = 519,
+
+ // Screenshot API
+ VREvent_RequestScreenshot = 520, // Sent by vrclient application to compositor to take a screenshot
+ VREvent_ScreenshotTaken = 521, // Sent by compositor to the application that the screenshot has been taken
+ VREvent_ScreenshotFailed = 522, // Sent by compositor to the application that the screenshot failed to be taken
+ VREvent_SubmitScreenshotToDashboard = 523, // Sent by compositor to the dashboard that a completed screenshot was submitted
+ VREvent_ScreenshotProgressToDashboard = 524, // Sent by compositor to the dashboard that a completed screenshot was submitted
+
+ VREvent_PrimaryDashboardDeviceChanged = 525,
+ VREvent_RoomViewShown = 526, // Sent by compositor whenever room-view is enabled
+ VREvent_RoomViewHidden = 527, // Sent by compositor whenever room-view is disabled
+ VREvent_ShowUI = 528, // data is showUi
+ VREvent_ShowDevTools = 529, // data is showDevTools
+
+ VREvent_Notification_Shown = 600,
+ VREvent_Notification_Hidden = 601,
+ VREvent_Notification_BeginInteraction = 602,
+ VREvent_Notification_Destroyed = 603,
+
+ VREvent_Quit = 700, // data is process
+ VREvent_ProcessQuit = 701, // data is process
+ //VREvent_QuitAborted_UserPrompt = 702, // data is process
+ VREvent_QuitAcknowledged = 703, // data is process
+ VREvent_DriverRequestedQuit = 704, // The driver has requested that SteamVR shut down
+ VREvent_RestartRequested = 705, // A driver or other component wants the user to restart SteamVR
+
+ VREvent_ChaperoneDataHasChanged = 800, // this will never happen with the new chaperone system
+ VREvent_ChaperoneUniverseHasChanged = 801,
+ VREvent_ChaperoneTempDataHasChanged = 802, // this will never happen with the new chaperone system
+ VREvent_ChaperoneSettingsHaveChanged = 803,
+ VREvent_SeatedZeroPoseReset = 804,
+ VREvent_ChaperoneFlushCache = 805, // Sent when the process needs to reload any cached data it retrieved from VRChaperone()
+ VREvent_ChaperoneRoomSetupStarting = 806, // Triggered by CVRChaperoneClient::RoomSetupStarting
+ VREvent_ChaperoneRoomSetupFinished = 807, // Triggered by CVRChaperoneClient::CommitWorkingCopy
+
+ VREvent_AudioSettingsHaveChanged = 820,
+
+ VREvent_BackgroundSettingHasChanged = 850,
+ VREvent_CameraSettingsHaveChanged = 851,
+ VREvent_ReprojectionSettingHasChanged = 852,
+ VREvent_ModelSkinSettingsHaveChanged = 853,
+ VREvent_EnvironmentSettingsHaveChanged = 854,
+ VREvent_PowerSettingsHaveChanged = 855,
+ VREvent_EnableHomeAppSettingsHaveChanged = 856,
+ VREvent_SteamVRSectionSettingChanged = 857,
+ VREvent_LighthouseSectionSettingChanged = 858,
+ VREvent_NullSectionSettingChanged = 859,
+ VREvent_UserInterfaceSectionSettingChanged = 860,
+ VREvent_NotificationsSectionSettingChanged = 861,
+ VREvent_KeyboardSectionSettingChanged = 862,
+ VREvent_PerfSectionSettingChanged = 863,
+ VREvent_DashboardSectionSettingChanged = 864,
+ VREvent_WebInterfaceSectionSettingChanged = 865,
+ VREvent_TrackersSectionSettingChanged = 866,
+ VREvent_LastKnownSectionSettingChanged = 867,
+ VREvent_DismissedWarningsSectionSettingChanged = 868,
+ VREvent_GpuSpeedSectionSettingChanged = 869,
+ VREvent_WindowsMRSectionSettingChanged = 870,
+ VREvent_OtherSectionSettingChanged = 871,
+
+ VREvent_StatusUpdate = 900,
+
+ VREvent_WebInterface_InstallDriverCompleted = 950,
+
+ VREvent_MCImageUpdated = 1000,
+
+ VREvent_FirmwareUpdateStarted = 1100,
+ VREvent_FirmwareUpdateFinished = 1101,
+
+ VREvent_KeyboardClosed = 1200,
+ VREvent_KeyboardCharInput = 1201,
+ VREvent_KeyboardDone = 1202, // Sent when DONE button clicked on keyboard
+
+ //VREvent_ApplicationTransitionStarted = 1300,
+ //VREvent_ApplicationTransitionAborted = 1301,
+ //VREvent_ApplicationTransitionNewAppStarted = 1302,
+ VREvent_ApplicationListUpdated = 1303,
+ VREvent_ApplicationMimeTypeLoad = 1304,
+ // VREvent_ApplicationTransitionNewAppLaunchComplete = 1305,
+ VREvent_ProcessConnected = 1306,
+ VREvent_ProcessDisconnected = 1307,
+
+ //VREvent_Compositor_MirrorWindowShown = 1400, // DEPRECATED
+ //VREvent_Compositor_MirrorWindowHidden = 1401, // DEPRECATED
+ VREvent_Compositor_ChaperoneBoundsShown = 1410,
+ VREvent_Compositor_ChaperoneBoundsHidden = 1411,
+ VREvent_Compositor_DisplayDisconnected = 1412,
+ VREvent_Compositor_DisplayReconnected = 1413,
+ VREvent_Compositor_HDCPError = 1414, // data is hdcpError
+ VREvent_Compositor_ApplicationNotResponding = 1415,
+ VREvent_Compositor_ApplicationResumed = 1416,
+ VREvent_Compositor_OutOfVideoMemory = 1417,
+ VREvent_Compositor_DisplayModeNotSupported = 1418, // k_pch_SteamVR_PreferredRefreshRate
+ VREvent_Compositor_StageOverrideReady = 1419,
+
+ VREvent_TrackedCamera_StartVideoStream = 1500,
+ VREvent_TrackedCamera_StopVideoStream = 1501,
+ VREvent_TrackedCamera_PauseVideoStream = 1502,
+ VREvent_TrackedCamera_ResumeVideoStream = 1503,
+ VREvent_TrackedCamera_EditingSurface = 1550,
+
+ VREvent_PerformanceTest_EnableCapture = 1600,
+ VREvent_PerformanceTest_DisableCapture = 1601,
+ VREvent_PerformanceTest_FidelityLevel = 1602,
+
+ VREvent_MessageOverlay_Closed = 1650,
+ VREvent_MessageOverlayCloseRequested = 1651,
+
+ VREvent_Input_HapticVibration = 1700, // data is hapticVibration
+ VREvent_Input_BindingLoadFailed = 1701, // data is inputBinding
+ VREvent_Input_BindingLoadSuccessful = 1702, // data is inputBinding
+ VREvent_Input_ActionManifestReloaded = 1703, // no data
+ VREvent_Input_ActionManifestLoadFailed = 1704, // data is actionManifest
+ VREvent_Input_ProgressUpdate = 1705, // data is progressUpdate
+ VREvent_Input_TrackerActivated = 1706,
+ VREvent_Input_BindingsUpdated = 1707,
+ VREvent_Input_BindingSubscriptionChanged = 1708,
+
+ VREvent_SpatialAnchors_PoseUpdated = 1800, // data is spatialAnchor. broadcast
+ VREvent_SpatialAnchors_DescriptorUpdated = 1801, // data is spatialAnchor. broadcast
+ VREvent_SpatialAnchors_RequestPoseUpdate = 1802, // data is spatialAnchor. sent to specific driver
+ VREvent_SpatialAnchors_RequestDescriptorUpdate = 1803, // data is spatialAnchor. sent to specific driver
+
+ VREvent_SystemReport_Started = 1900, // user or system initiated generation of a system report. broadcast
+
+ VREvent_Monitor_ShowHeadsetView = 2000, // data is process
+ VREvent_Monitor_HideHeadsetView = 2001, // data is process
+
+ // Vendors are free to expose private events in this reserved region
+ VREvent_VendorSpecific_Reserved_Start = 10000,
+ VREvent_VendorSpecific_Reserved_End = 19999,
+};
+
+
+/** Level of Hmd activity */
+// UserInteraction_Timeout means the device is in the process of timing out.
+// InUse = ( k_EDeviceActivityLevel_UserInteraction || k_EDeviceActivityLevel_UserInteraction_Timeout )
+// VREvent_TrackedDeviceUserInteractionStarted fires when the devices transitions from Standby -> UserInteraction or Idle -> UserInteraction.
+// VREvent_TrackedDeviceUserInteractionEnded fires when the devices transitions from UserInteraction_Timeout -> Idle
+enum EDeviceActivityLevel
+{
+ k_EDeviceActivityLevel_Unknown = -1,
+ k_EDeviceActivityLevel_Idle = 0, // No activity for the last 10 seconds
+ k_EDeviceActivityLevel_UserInteraction = 1, // Activity (movement or prox sensor) is happening now
+ k_EDeviceActivityLevel_UserInteraction_Timeout = 2, // No activity for the last 0.5 seconds
+ k_EDeviceActivityLevel_Standby = 3, // Idle for at least 5 seconds (configurable in Settings -> Power Management)
+ k_EDeviceActivityLevel_Idle_Timeout = 4,
+};
+
+
+/** VR controller button and axis IDs */
+enum EVRButtonId
+{
+ k_EButton_System = 0,
+ k_EButton_ApplicationMenu = 1,
+ k_EButton_Grip = 2,
+ k_EButton_DPad_Left = 3,
+ k_EButton_DPad_Up = 4,
+ k_EButton_DPad_Right = 5,
+ k_EButton_DPad_Down = 6,
+ k_EButton_A = 7,
+
+ k_EButton_ProximitySensor = 31,
+
+ k_EButton_Axis0 = 32,
+ k_EButton_Axis1 = 33,
+ k_EButton_Axis2 = 34,
+ k_EButton_Axis3 = 35,
+ k_EButton_Axis4 = 36,
+
+ // aliases for well known controllers
+ k_EButton_SteamVR_Touchpad = k_EButton_Axis0,
+ k_EButton_SteamVR_Trigger = k_EButton_Axis1,
+
+ k_EButton_Dashboard_Back = k_EButton_Grip,
+
+ k_EButton_IndexController_A = k_EButton_Grip,
+ k_EButton_IndexController_B = k_EButton_ApplicationMenu,
+ k_EButton_IndexController_JoyStick = k_EButton_Axis3,
+
+ k_EButton_Max = 64
+};
+
+inline uint64_t ButtonMaskFromId( EVRButtonId id ) { return 1ull << id; }
+
+/** used for controller button events */
+struct VREvent_Controller_t
+{
+ uint32_t button; // EVRButtonId enum
+};
+
+
+/** used for simulated mouse events in overlay space */
+enum EVRMouseButton
+{
+ VRMouseButton_Left = 0x0001,
+ VRMouseButton_Right = 0x0002,
+ VRMouseButton_Middle = 0x0004,
+};
+
+
+/** used for simulated mouse events in overlay space */
+struct VREvent_Mouse_t
+{
+ float x, y; // co-ords are in GL space, bottom left of the texture is 0,0
+ uint32_t button; // EVRMouseButton enum
+};
+
+/** used for simulated mouse wheel scroll */
+struct VREvent_Scroll_t
+{
+ float xdelta, ydelta;
+ uint32_t unused;
+ float viewportscale; // For scrolling on an overlay with laser mouse, this is the overlay's vertical size relative to the overlay height. Range: [0,1]
+};
+
+/** when in mouse input mode you can receive data from the touchpad, these events are only sent if the users finger
+ is on the touchpad (or just released from it). These events are sent to overlays with the VROverlayFlags_SendVRTouchpadEvents
+ flag set.
+**/
+struct VREvent_TouchPadMove_t
+{
+ // true if the users finger is detected on the touch pad
+ bool bFingerDown;
+
+ // How long the finger has been down in seconds
+ float flSecondsFingerDown;
+
+ // These values indicate the starting finger position (so you can do some basic swipe stuff)
+ float fValueXFirst;
+ float fValueYFirst;
+
+ // This is the raw sampled coordinate without deadzoning
+ float fValueXRaw;
+ float fValueYRaw;
+};
+
+/** notification related events. Details will still change at this point */
+struct VREvent_Notification_t
+{
+ uint64_t ulUserValue;
+ uint32_t notificationId;
+};
+
+/** Used for events about processes */
+struct VREvent_Process_t
+{
+ uint32_t pid;
+ uint32_t oldPid;
+ bool bForced;
+ // If the associated event was triggered by a connection loss
+ bool bConnectionLost;
+};
+
+
+/** Used for a few events about overlays */
+struct VREvent_Overlay_t
+{
+ uint64_t overlayHandle;
+ uint64_t devicePath;
+};
+
+
+/** Used for a few events about overlays */
+struct VREvent_Status_t
+{
+ uint32_t statusState; // EVRState enum
+};
+
+/** Used for keyboard events **/
+struct VREvent_Keyboard_t
+{
+ char cNewInput[8]; // Up to 11 bytes of new input
+ uint64_t uUserValue; // Possible flags about the new input
+};
+
+struct VREvent_Ipd_t
+{
+ float ipdMeters;
+};
+
+struct VREvent_Chaperone_t
+{
+ uint64_t m_nPreviousUniverse;
+ uint64_t m_nCurrentUniverse;
+};
+
+/** Not actually used for any events */
+struct VREvent_Reserved_t
+{
+ uint64_t reserved0;
+ uint64_t reserved1;
+ uint64_t reserved2;
+ uint64_t reserved3;
+ uint64_t reserved4;
+ uint64_t reserved5;
+};
+
+struct VREvent_PerformanceTest_t
+{
+ uint32_t m_nFidelityLevel;
+};
+
+struct VREvent_SeatedZeroPoseReset_t
+{
+ bool bResetBySystemMenu;
+};
+
+struct VREvent_Screenshot_t
+{
+ uint32_t handle;
+ uint32_t type;
+};
+
+struct VREvent_ScreenshotProgress_t
+{
+ float progress;
+};
+
+struct VREvent_ApplicationLaunch_t
+{
+ uint32_t pid;
+ uint32_t unArgsHandle;
+};
+
+struct VREvent_EditingCameraSurface_t
+{
+ uint64_t overlayHandle;
+ uint32_t nVisualMode;
+};
+
+struct VREvent_MessageOverlay_t
+{
+ uint32_t unVRMessageOverlayResponse; // vr::VRMessageOverlayResponse enum
+};
+
+struct VREvent_Property_t
+{
+ PropertyContainerHandle_t container;
+ ETrackedDeviceProperty prop;
+};
+
+struct VREvent_HapticVibration_t
+{
+ uint64_t containerHandle; // property container handle of the device with the haptic component
+ uint64_t componentHandle; // Which haptic component needs to vibrate
+ float fDurationSeconds;
+ float fFrequency;
+ float fAmplitude;
+};
+
+struct VREvent_WebConsole_t
+{
+ WebConsoleHandle_t webConsoleHandle;
+};
+
+struct VREvent_InputBindingLoad_t
+{
+ vr::PropertyContainerHandle_t ulAppContainer;
+ uint64_t pathMessage;
+ uint64_t pathUrl;
+ uint64_t pathControllerType;
+};
+
+struct VREvent_InputActionManifestLoad_t
+{
+ uint64_t pathAppKey;
+ uint64_t pathMessage;
+ uint64_t pathMessageParam;
+ uint64_t pathManifestPath;
+};
+
+struct VREvent_SpatialAnchor_t
+{
+ SpatialAnchorHandle_t unHandle;
+};
+
+struct VREvent_ProgressUpdate_t
+{
+ uint64_t ulApplicationPropertyContainer;
+ uint64_t pathDevice;
+ uint64_t pathInputSource;
+ uint64_t pathProgressAction;
+ uint64_t pathIcon;
+ float fProgress;
+};
+
+enum EShowUIType
+{
+ ShowUI_ControllerBinding = 0,
+ ShowUI_ManageTrackers = 1,
+ // ShowUI_QuickStart = 2, // Deprecated
+ ShowUI_Pairing = 3,
+ ShowUI_Settings = 4,
+ ShowUI_DebugCommands = 5,
+ ShowUI_FullControllerBinding = 6,
+ ShowUI_ManageDrivers = 7,
+};
+
+struct VREvent_ShowUI_t
+{
+ EShowUIType eType;
+};
+
+struct VREvent_ShowDevTools_t
+{
+ int32_t nBrowserIdentifier;
+};
+
+enum EHDCPError
+{
+ HDCPError_None = 0,
+ HDCPError_LinkLost = 1,
+ HDCPError_Tampered = 2,
+ HDCPError_DeviceRevoked = 3,
+ HDCPError_Unknown = 4
+};
+
+struct VREvent_HDCPError_t
+{
+ EHDCPError eCode;
+};
+
+typedef union
+{
+ VREvent_Reserved_t reserved;
+ VREvent_Controller_t controller;
+ VREvent_Mouse_t mouse;
+ VREvent_Scroll_t scroll;
+ VREvent_Process_t process;
+ VREvent_Notification_t notification;
+ VREvent_Overlay_t overlay;
+ VREvent_Status_t status;
+ VREvent_Keyboard_t keyboard;
+ VREvent_Ipd_t ipd;
+ VREvent_Chaperone_t chaperone;
+ VREvent_PerformanceTest_t performanceTest;
+ VREvent_TouchPadMove_t touchPadMove;
+ VREvent_SeatedZeroPoseReset_t seatedZeroPoseReset;
+ VREvent_Screenshot_t screenshot;
+ VREvent_ScreenshotProgress_t screenshotProgress;
+ VREvent_ApplicationLaunch_t applicationLaunch;
+ VREvent_EditingCameraSurface_t cameraSurface;
+ VREvent_MessageOverlay_t messageOverlay;
+ VREvent_Property_t property;
+ VREvent_HapticVibration_t hapticVibration;
+ VREvent_WebConsole_t webConsole;
+ VREvent_InputBindingLoad_t inputBinding;
+ VREvent_InputActionManifestLoad_t actionManifest;
+ VREvent_SpatialAnchor_t spatialAnchor;
+ VREvent_ProgressUpdate_t progressUpdate;
+ VREvent_ShowUI_t showUi;
+ VREvent_ShowDevTools_t showDevTools;
+ VREvent_HDCPError_t hdcpError;
+ /** NOTE!!! If you change this you MUST manually update openvr_interop.cs.py */
+} VREvent_Data_t;
+
+
+#if defined(__linux__) || defined(__APPLE__)
+// This structure was originally defined mis-packed on Linux, preserved for
+// compatibility.
+#pragma pack( push, 4 )
+#endif
+
+/** An event posted by the server to all running applications */
+struct VREvent_t
+{
+ uint32_t eventType; // EVREventType enum
+ TrackedDeviceIndex_t trackedDeviceIndex;
+ float eventAgeSeconds;
+ // event data must be the end of the struct as its size is variable
+ VREvent_Data_t data;
+};
+
+#if defined(__linux__) || defined(__APPLE__)
+#pragma pack( pop )
+#endif
+
+typedef uint32_t VRComponentProperties;
+
+enum EVRComponentProperty
+{
+ VRComponentProperty_IsStatic = (1 << 0),
+ VRComponentProperty_IsVisible = (1 << 1),
+ VRComponentProperty_IsTouched = (1 << 2),
+ VRComponentProperty_IsPressed = (1 << 3),
+ VRComponentProperty_IsScrolled = (1 << 4),
+ VRComponentProperty_IsHighlighted = (1 << 5),
+};
+
+
+/** Describes state information about a render-model component, including transforms and other dynamic properties */
+struct RenderModel_ComponentState_t
+{
+ HmdMatrix34_t mTrackingToComponentRenderModel; // Transform required when drawing the component render model
+ HmdMatrix34_t mTrackingToComponentLocal; // Transform available for attaching to a local component coordinate system (-Z out from surface )
+ VRComponentProperties uProperties;
+};
+
+
+enum EVRInputError
+{
+ VRInputError_None = 0,
+ VRInputError_NameNotFound = 1,
+ VRInputError_WrongType = 2,
+ VRInputError_InvalidHandle = 3,
+ VRInputError_InvalidParam = 4,
+ VRInputError_NoSteam = 5,
+ VRInputError_MaxCapacityReached = 6,
+ VRInputError_IPCError = 7,
+ VRInputError_NoActiveActionSet = 8,
+ VRInputError_InvalidDevice = 9,
+ VRInputError_InvalidSkeleton = 10,
+ VRInputError_InvalidBoneCount = 11,
+ VRInputError_InvalidCompressedData = 12,
+ VRInputError_NoData = 13,
+ VRInputError_BufferTooSmall = 14,
+ VRInputError_MismatchedActionManifest = 15,
+ VRInputError_MissingSkeletonData = 16,
+ VRInputError_InvalidBoneIndex = 17,
+ VRInputError_InvalidPriority = 18,
+ VRInputError_PermissionDenied = 19,
+ VRInputError_InvalidRenderModel = 20,
+};
+
+enum EVRSpatialAnchorError
+{
+ VRSpatialAnchorError_Success = 0,
+ VRSpatialAnchorError_Internal = 1,
+ VRSpatialAnchorError_UnknownHandle = 2,
+ VRSpatialAnchorError_ArrayTooSmall = 3,
+ VRSpatialAnchorError_InvalidDescriptorChar = 4,
+ VRSpatialAnchorError_NotYetAvailable = 5,
+ VRSpatialAnchorError_NotAvailableInThisUniverse = 6,
+ VRSpatialAnchorError_PermanentlyUnavailable = 7,
+ VRSpatialAnchorError_WrongDriver = 8,
+ VRSpatialAnchorError_DescriptorTooLong = 9,
+ VRSpatialAnchorError_Unknown = 10,
+ VRSpatialAnchorError_NoRoomCalibration = 11,
+ VRSpatialAnchorError_InvalidArgument = 12,
+ VRSpatialAnchorError_UnknownDriver = 13,
+};
+
+/** The mesh to draw into the stencil (or depth) buffer to perform
+* early stencil (or depth) kills of pixels that will never appear on the HMD.
+* This mesh draws on all the pixels that will be hidden after distortion.
+*
+* If the HMD does not provide a visible area mesh pVertexData will be
+* NULL and unTriangleCount will be 0. */
+struct HiddenAreaMesh_t
+{
+ const HmdVector2_t *pVertexData;
+ uint32_t unTriangleCount;
+};
+
+
+enum EHiddenAreaMeshType
+{
+ k_eHiddenAreaMesh_Standard = 0,
+ k_eHiddenAreaMesh_Inverse = 1,
+ k_eHiddenAreaMesh_LineLoop = 2,
+
+ k_eHiddenAreaMesh_Max = 3,
+};
+
+
+/** Identifies what kind of axis is on the controller at index n. Read this type
+* with pVRSystem->Get( nControllerDeviceIndex, Prop_Axis0Type_Int32 + n );
+*/
+enum EVRControllerAxisType
+{
+ k_eControllerAxis_None = 0,
+ k_eControllerAxis_TrackPad = 1,
+ k_eControllerAxis_Joystick = 2,
+ k_eControllerAxis_Trigger = 3, // Analog trigger data is in the X axis
+};
+
+
+/** contains information about one axis on the controller */
+struct VRControllerAxis_t
+{
+ float x; // Ranges from -1.0 to 1.0 for joysticks and track pads. Ranges from 0.0 to 1.0 for triggers were 0 is fully released.
+ float y; // Ranges from -1.0 to 1.0 for joysticks and track pads. Is always 0.0 for triggers.
+};
+
+
+/** the number of axes in the controller state */
+static const uint32_t k_unControllerStateAxisCount = 5;
+
+
+#if defined(__linux__) || defined(__APPLE__)
+// This structure was originally defined mis-packed on Linux, preserved for
+// compatibility.
+#pragma pack( push, 4 )
+#endif
+
+/** Holds all the state of a controller at one moment in time. */
+struct VRControllerState001_t
+{
+ // If packet num matches that on your prior call, then the controller state hasn't been changed since
+ // your last call and there is no need to process it
+ uint32_t unPacketNum;
+
+ // bit flags for each of the buttons. Use ButtonMaskFromId to turn an ID into a mask
+ uint64_t ulButtonPressed;
+ uint64_t ulButtonTouched;
+
+ // Axis data for the controller's analog inputs
+ VRControllerAxis_t rAxis[ k_unControllerStateAxisCount ];
+};
+#if defined(__linux__) || defined(__APPLE__)
+#pragma pack( pop )
+#endif
+
+
+typedef VRControllerState001_t VRControllerState_t;
+
+
+/** determines how to provide output to the application of various event processing functions. */
+enum EVRControllerEventOutputType
+{
+ ControllerEventOutput_OSEvents = 0,
+ ControllerEventOutput_VREvents = 1,
+};
+
+
+
+/** Collision Bounds Style */
+enum ECollisionBoundsStyle
+{
+ COLLISION_BOUNDS_STYLE_BEGINNER = 0,
+ COLLISION_BOUNDS_STYLE_INTERMEDIATE,
+ COLLISION_BOUNDS_STYLE_SQUARES,
+ COLLISION_BOUNDS_STYLE_ADVANCED,
+ COLLISION_BOUNDS_STYLE_NONE,
+
+ COLLISION_BOUNDS_STYLE_COUNT
+};
+
+/** used to refer to a single VR overlay */
+typedef uint64_t VROverlayHandle_t;
+
+static const VROverlayHandle_t k_ulOverlayHandleInvalid = 0;
+
+/** Errors that can occur around VR overlays */
+enum EVROverlayError
+{
+ VROverlayError_None = 0,
+
+ VROverlayError_UnknownOverlay = 10,
+ VROverlayError_InvalidHandle = 11,
+ VROverlayError_PermissionDenied = 12,
+ VROverlayError_OverlayLimitExceeded = 13, // No more overlays could be created because the maximum number already exist
+ VROverlayError_WrongVisibilityType = 14,
+ VROverlayError_KeyTooLong = 15,
+ VROverlayError_NameTooLong = 16,
+ VROverlayError_KeyInUse = 17,
+ VROverlayError_WrongTransformType = 18,
+ VROverlayError_InvalidTrackedDevice = 19,
+ VROverlayError_InvalidParameter = 20,
+ VROverlayError_ThumbnailCantBeDestroyed = 21,
+ VROverlayError_ArrayTooSmall = 22,
+ VROverlayError_RequestFailed = 23,
+ VROverlayError_InvalidTexture = 24,
+ VROverlayError_UnableToLoadFile = 25,
+ VROverlayError_KeyboardAlreadyInUse = 26,
+ VROverlayError_NoNeighbor = 27,
+ VROverlayError_TooManyMaskPrimitives = 29,
+ VROverlayError_BadMaskPrimitive = 30,
+ VROverlayError_TextureAlreadyLocked = 31,
+ VROverlayError_TextureLockCapacityReached = 32,
+ VROverlayError_TextureNotLocked = 33,
+};
+
+/** enum values to pass in to VR_Init to identify whether the application will
+* draw a 3D scene. */
+enum EVRApplicationType
+{
+ VRApplication_Other = 0, // Some other kind of application that isn't covered by the other entries
+ VRApplication_Scene = 1, // Application will submit 3D frames
+ VRApplication_Overlay = 2, // Application only interacts with overlays
+ VRApplication_Background = 3, // Application should not start SteamVR if it's not already running, and should not
+ // keep it running if everything else quits.
+ VRApplication_Utility = 4, // Init should not try to load any drivers. The application needs access to utility
+ // interfaces (like IVRSettings and IVRApplications) but not hardware.
+ VRApplication_VRMonitor = 5, // Reserved for vrmonitor
+ VRApplication_SteamWatchdog = 6,// Reserved for Steam
+ VRApplication_Bootstrapper = 7, // reserved for vrstartup
+ VRApplication_WebHelper = 8, // reserved for vrwebhelper
+
+ VRApplication_Max
+};
+
+
+/** error codes for firmware */
+enum EVRFirmwareError
+{
+ VRFirmwareError_None = 0,
+ VRFirmwareError_Success = 1,
+ VRFirmwareError_Fail = 2,
+};
+
+
+/** error codes for notifications */
+enum EVRNotificationError
+{
+ VRNotificationError_OK = 0,
+ VRNotificationError_InvalidNotificationId = 100,
+ VRNotificationError_NotificationQueueFull = 101,
+ VRNotificationError_InvalidOverlayHandle = 102,
+ VRNotificationError_SystemWithUserValueAlreadyExists = 103,
+};
+
+
+enum EVRSkeletalMotionRange
+{
+ // The range of motion of the skeleton takes into account any physical limits imposed by
+ // the controller itself. This will tend to be the most accurate pose compared to the user's
+ // actual hand pose, but might not allow a closed fist for example
+ VRSkeletalMotionRange_WithController = 0,
+
+ // Retarget the range of motion provided by the input device to make the hand appear to move
+ // as if it was not holding a controller. eg: map "hand grasping controller" to "closed fist"
+ VRSkeletalMotionRange_WithoutController = 1,
+};
+
+enum EVRSkeletalTrackingLevel
+{
+ // body part location can't be directly determined by the device. Any skeletal pose provided by
+ // the device is estimated by assuming the position required to active buttons, triggers, joysticks,
+ // or other input sensors.
+ // E.g. Vive Controller, Gamepad
+ VRSkeletalTracking_Estimated = 0,
+
+ // body part location can be measured directly but with fewer degrees of freedom than the actual body
+ // part. Certain body part positions may be unmeasured by the device and estimated from other input data.
+ // E.g. Index Controllers, gloves that only measure finger curl
+ VRSkeletalTracking_Partial = 1,
+
+ // Body part location can be measured directly throughout the entire range of motion of the body part.
+ // E.g. Mocap suit for the full body, gloves that measure rotation of each finger segment
+ VRSkeletalTracking_Full = 2,
+
+ VRSkeletalTrackingLevel_Count,
+ VRSkeletalTrackingLevel_Max = VRSkeletalTrackingLevel_Count - 1
+};
+
+
+
+/** Holds the transform for a single bone */
+struct VRBoneTransform_t
+{
+ HmdVector4_t position;
+ HmdQuaternionf_t orientation;
+};
+
+/** Type used for referring to bones by their index */
+typedef int32_t BoneIndex_t;
+const BoneIndex_t k_unInvalidBoneIndex = -1;
+
+
+/** error codes returned by Vr_Init */
+
+// Please add adequate error description to https://developer.valvesoftware.com/w/index.php?title=Category:SteamVRHelp
+enum EVRInitError
+{
+ VRInitError_None = 0,
+ VRInitError_Unknown = 1,
+
+ VRInitError_Init_InstallationNotFound = 100,
+ VRInitError_Init_InstallationCorrupt = 101,
+ VRInitError_Init_VRClientDLLNotFound = 102,
+ VRInitError_Init_FileNotFound = 103,
+ VRInitError_Init_FactoryNotFound = 104,
+ VRInitError_Init_InterfaceNotFound = 105,
+ VRInitError_Init_InvalidInterface = 106,
+ VRInitError_Init_UserConfigDirectoryInvalid = 107,
+ VRInitError_Init_HmdNotFound = 108,
+ VRInitError_Init_NotInitialized = 109,
+ VRInitError_Init_PathRegistryNotFound = 110,
+ VRInitError_Init_NoConfigPath = 111,
+ VRInitError_Init_NoLogPath = 112,
+ VRInitError_Init_PathRegistryNotWritable = 113,
+ VRInitError_Init_AppInfoInitFailed = 114,
+ VRInitError_Init_Retry = 115, // Used internally to cause retries to vrserver
+ VRInitError_Init_InitCanceledByUser = 116, // The calling application should silently exit. The user canceled app startup
+ VRInitError_Init_AnotherAppLaunching = 117,
+ VRInitError_Init_SettingsInitFailed = 118,
+ VRInitError_Init_ShuttingDown = 119,
+ VRInitError_Init_TooManyObjects = 120,
+ VRInitError_Init_NoServerForBackgroundApp = 121,
+ VRInitError_Init_NotSupportedWithCompositor = 122,
+ VRInitError_Init_NotAvailableToUtilityApps = 123,
+ VRInitError_Init_Internal = 124,
+ VRInitError_Init_HmdDriverIdIsNone = 125,
+ VRInitError_Init_HmdNotFoundPresenceFailed = 126,
+ VRInitError_Init_VRMonitorNotFound = 127,
+ VRInitError_Init_VRMonitorStartupFailed = 128,
+ VRInitError_Init_LowPowerWatchdogNotSupported = 129,
+ VRInitError_Init_InvalidApplicationType = 130,
+ VRInitError_Init_NotAvailableToWatchdogApps = 131,
+ VRInitError_Init_WatchdogDisabledInSettings = 132,
+ VRInitError_Init_VRDashboardNotFound = 133,
+ VRInitError_Init_VRDashboardStartupFailed = 134,
+ VRInitError_Init_VRHomeNotFound = 135,
+ VRInitError_Init_VRHomeStartupFailed = 136,
+ VRInitError_Init_RebootingBusy = 137,
+ VRInitError_Init_FirmwareUpdateBusy = 138,
+ VRInitError_Init_FirmwareRecoveryBusy = 139,
+ VRInitError_Init_USBServiceBusy = 140,
+ VRInitError_Init_VRWebHelperStartupFailed = 141,
+ VRInitError_Init_TrackerManagerInitFailed = 142,
+ VRInitError_Init_AlreadyRunning = 143,
+ VRInitError_Init_FailedForVrMonitor = 144,
+ VRInitError_Init_PropertyManagerInitFailed = 145,
+ VRInitError_Init_WebServerFailed = 146,
+
+ VRInitError_Driver_Failed = 200,
+ VRInitError_Driver_Unknown = 201,
+ VRInitError_Driver_HmdUnknown = 202,
+ VRInitError_Driver_NotLoaded = 203,
+ VRInitError_Driver_RuntimeOutOfDate = 204,
+ VRInitError_Driver_HmdInUse = 205,
+ VRInitError_Driver_NotCalibrated = 206,
+ VRInitError_Driver_CalibrationInvalid = 207,
+ VRInitError_Driver_HmdDisplayNotFound = 208,
+ VRInitError_Driver_TrackedDeviceInterfaceUnknown = 209,
+ // VRInitError_Driver_HmdDisplayNotFoundAfterFix = 210, // not needed: here for historic reasons
+ VRInitError_Driver_HmdDriverIdOutOfBounds = 211,
+ VRInitError_Driver_HmdDisplayMirrored = 212,
+ VRInitError_Driver_HmdDisplayNotFoundLaptop = 213,
+ // Never make error 259 because we return it from main and it would conflict with STILL_ACTIVE
+
+ VRInitError_IPC_ServerInitFailed = 300,
+ VRInitError_IPC_ConnectFailed = 301,
+ VRInitError_IPC_SharedStateInitFailed = 302,
+ VRInitError_IPC_CompositorInitFailed = 303,
+ VRInitError_IPC_MutexInitFailed = 304,
+ VRInitError_IPC_Failed = 305,
+ VRInitError_IPC_CompositorConnectFailed = 306,
+ VRInitError_IPC_CompositorInvalidConnectResponse = 307,
+ VRInitError_IPC_ConnectFailedAfterMultipleAttempts = 308,
+ VRInitError_IPC_ConnectFailedAfterTargetExited = 309,
+ VRInitError_IPC_NamespaceUnavailable = 310,
+
+ VRInitError_Compositor_Failed = 400,
+ VRInitError_Compositor_D3D11HardwareRequired = 401,
+ VRInitError_Compositor_FirmwareRequiresUpdate = 402,
+ VRInitError_Compositor_OverlayInitFailed = 403,
+ VRInitError_Compositor_ScreenshotsInitFailed = 404,
+ VRInitError_Compositor_UnableToCreateDevice = 405,
+ VRInitError_Compositor_SharedStateIsNull = 406,
+ VRInitError_Compositor_NotificationManagerIsNull = 407,
+ VRInitError_Compositor_ResourceManagerClientIsNull = 408,
+ VRInitError_Compositor_MessageOverlaySharedStateInitFailure = 409,
+ VRInitError_Compositor_PropertiesInterfaceIsNull = 410,
+ VRInitError_Compositor_CreateFullscreenWindowFailed = 411,
+ VRInitError_Compositor_SettingsInterfaceIsNull = 412,
+ VRInitError_Compositor_FailedToShowWindow = 413,
+ VRInitError_Compositor_DistortInterfaceIsNull = 414,
+ VRInitError_Compositor_DisplayFrequencyFailure = 415,
+ VRInitError_Compositor_RendererInitializationFailed = 416,
+ VRInitError_Compositor_DXGIFactoryInterfaceIsNull = 417,
+ VRInitError_Compositor_DXGIFactoryCreateFailed = 418,
+ VRInitError_Compositor_DXGIFactoryQueryFailed = 419,
+ VRInitError_Compositor_InvalidAdapterDesktop = 420,
+ VRInitError_Compositor_InvalidHmdAttachment = 421,
+ VRInitError_Compositor_InvalidOutputDesktop = 422,
+ VRInitError_Compositor_InvalidDeviceProvided = 423,
+ VRInitError_Compositor_D3D11RendererInitializationFailed = 424,
+ VRInitError_Compositor_FailedToFindDisplayMode = 425,
+ VRInitError_Compositor_FailedToCreateSwapChain = 426,
+ VRInitError_Compositor_FailedToGetBackBuffer = 427,
+ VRInitError_Compositor_FailedToCreateRenderTarget = 428,
+ VRInitError_Compositor_FailedToCreateDXGI2SwapChain = 429,
+ VRInitError_Compositor_FailedtoGetDXGI2BackBuffer = 430,
+ VRInitError_Compositor_FailedToCreateDXGI2RenderTarget = 431,
+ VRInitError_Compositor_FailedToGetDXGIDeviceInterface = 432,
+ VRInitError_Compositor_SelectDisplayMode = 433,
+ VRInitError_Compositor_FailedToCreateNvAPIRenderTargets = 434,
+ VRInitError_Compositor_NvAPISetDisplayMode = 435,
+ VRInitError_Compositor_FailedToCreateDirectModeDisplay = 436,
+ VRInitError_Compositor_InvalidHmdPropertyContainer = 437,
+ VRInitError_Compositor_UpdateDisplayFrequency = 438,
+ VRInitError_Compositor_CreateRasterizerState = 439,
+ VRInitError_Compositor_CreateWireframeRasterizerState = 440,
+ VRInitError_Compositor_CreateSamplerState = 441,
+ VRInitError_Compositor_CreateClampToBorderSamplerState = 442,
+ VRInitError_Compositor_CreateAnisoSamplerState = 443,
+ VRInitError_Compositor_CreateOverlaySamplerState = 444,
+ VRInitError_Compositor_CreatePanoramaSamplerState = 445,
+ VRInitError_Compositor_CreateFontSamplerState = 446,
+ VRInitError_Compositor_CreateNoBlendState = 447,
+ VRInitError_Compositor_CreateBlendState = 448,
+ VRInitError_Compositor_CreateAlphaBlendState = 449,
+ VRInitError_Compositor_CreateBlendStateMaskR = 450,
+ VRInitError_Compositor_CreateBlendStateMaskG = 451,
+ VRInitError_Compositor_CreateBlendStateMaskB = 452,
+ VRInitError_Compositor_CreateDepthStencilState = 453,
+ VRInitError_Compositor_CreateDepthStencilStateNoWrite = 454,
+ VRInitError_Compositor_CreateDepthStencilStateNoDepth = 455,
+ VRInitError_Compositor_CreateFlushTexture = 456,
+ VRInitError_Compositor_CreateDistortionSurfaces = 457,
+ VRInitError_Compositor_CreateConstantBuffer = 458,
+ VRInitError_Compositor_CreateHmdPoseConstantBuffer = 459,
+ VRInitError_Compositor_CreateHmdPoseStagingConstantBuffer = 460,
+ VRInitError_Compositor_CreateSharedFrameInfoConstantBuffer = 461,
+ VRInitError_Compositor_CreateOverlayConstantBuffer = 462,
+ VRInitError_Compositor_CreateSceneTextureIndexConstantBuffer = 463,
+ VRInitError_Compositor_CreateReadableSceneTextureIndexConstantBuffer = 464,
+ VRInitError_Compositor_CreateLayerGraphicsTextureIndexConstantBuffer = 465,
+ VRInitError_Compositor_CreateLayerComputeTextureIndexConstantBuffer = 466,
+ VRInitError_Compositor_CreateLayerComputeSceneTextureIndexConstantBuffer = 467,
+ VRInitError_Compositor_CreateComputeHmdPoseConstantBuffer = 468,
+ VRInitError_Compositor_CreateGeomConstantBuffer = 469,
+ VRInitError_Compositor_CreatePanelMaskConstantBuffer = 470,
+ VRInitError_Compositor_CreatePixelSimUBO = 471,
+ VRInitError_Compositor_CreateMSAARenderTextures = 472,
+ VRInitError_Compositor_CreateResolveRenderTextures = 473,
+ VRInitError_Compositor_CreateComputeResolveRenderTextures = 474,
+ VRInitError_Compositor_CreateDriverDirectModeResolveTextures = 475,
+ VRInitError_Compositor_OpenDriverDirectModeResolveTextures = 476,
+ VRInitError_Compositor_CreateFallbackSyncTexture = 477,
+ VRInitError_Compositor_ShareFallbackSyncTexture = 478,
+ VRInitError_Compositor_CreateOverlayIndexBuffer = 479,
+ VRInitError_Compositor_CreateOverlayVertexBuffer = 480,
+ VRInitError_Compositor_CreateTextVertexBuffer = 481,
+ VRInitError_Compositor_CreateTextIndexBuffer = 482,
+ VRInitError_Compositor_CreateMirrorTextures = 483,
+ VRInitError_Compositor_CreateLastFrameRenderTexture = 484,
+ VRInitError_Compositor_CreateMirrorOverlay = 485,
+ VRInitError_Compositor_FailedToCreateVirtualDisplayBackbuffer = 486,
+ VRInitError_Compositor_DisplayModeNotSupported = 487,
+ VRInitError_Compositor_CreateOverlayInvalidCall = 488,
+ VRInitError_Compositor_CreateOverlayAlreadyInitialized = 489,
+ VRInitError_Compositor_FailedToCreateMailbox = 490,
+
+ VRInitError_VendorSpecific_UnableToConnectToOculusRuntime = 1000,
+ VRInitError_VendorSpecific_WindowsNotInDevMode = 1001,
+
+ VRInitError_VendorSpecific_HmdFound_CantOpenDevice = 1101,
+ VRInitError_VendorSpecific_HmdFound_UnableToRequestConfigStart = 1102,
+ VRInitError_VendorSpecific_HmdFound_NoStoredConfig = 1103,
+ VRInitError_VendorSpecific_HmdFound_ConfigTooBig = 1104,
+ VRInitError_VendorSpecific_HmdFound_ConfigTooSmall = 1105,
+ VRInitError_VendorSpecific_HmdFound_UnableToInitZLib = 1106,
+ VRInitError_VendorSpecific_HmdFound_CantReadFirmwareVersion = 1107,
+ VRInitError_VendorSpecific_HmdFound_UnableToSendUserDataStart = 1108,
+ VRInitError_VendorSpecific_HmdFound_UnableToGetUserDataStart = 1109,
+ VRInitError_VendorSpecific_HmdFound_UnableToGetUserDataNext = 1110,
+ VRInitError_VendorSpecific_HmdFound_UserDataAddressRange = 1111,
+ VRInitError_VendorSpecific_HmdFound_UserDataError = 1112,
+ VRInitError_VendorSpecific_HmdFound_ConfigFailedSanityCheck = 1113,
+ VRInitError_VendorSpecific_OculusRuntimeBadInstall = 1114,
+
+ VRInitError_Steam_SteamInstallationNotFound = 2000,
+
+ // Strictly a placeholder
+ VRInitError_LastError
+};
+
+enum EVRScreenshotType
+{
+ VRScreenshotType_None = 0,
+ VRScreenshotType_Mono = 1, // left eye only
+ VRScreenshotType_Stereo = 2,
+ VRScreenshotType_Cubemap = 3,
+ VRScreenshotType_MonoPanorama = 4,
+ VRScreenshotType_StereoPanorama = 5
+};
+
+enum EVRScreenshotPropertyFilenames
+{
+ VRScreenshotPropertyFilenames_Preview = 0,
+ VRScreenshotPropertyFilenames_VR = 1,
+};
+
+enum EVRTrackedCameraError
+{
+ VRTrackedCameraError_None = 0,
+ VRTrackedCameraError_OperationFailed = 100,
+ VRTrackedCameraError_InvalidHandle = 101,
+ VRTrackedCameraError_InvalidFrameHeaderVersion = 102,
+ VRTrackedCameraError_OutOfHandles = 103,
+ VRTrackedCameraError_IPCFailure = 104,
+ VRTrackedCameraError_NotSupportedForThisDevice = 105,
+ VRTrackedCameraError_SharedMemoryFailure = 106,
+ VRTrackedCameraError_FrameBufferingFailure = 107,
+ VRTrackedCameraError_StreamSetupFailure = 108,
+ VRTrackedCameraError_InvalidGLTextureId = 109,
+ VRTrackedCameraError_InvalidSharedTextureHandle = 110,
+ VRTrackedCameraError_FailedToGetGLTextureId = 111,
+ VRTrackedCameraError_SharedTextureFailure = 112,
+ VRTrackedCameraError_NoFrameAvailable = 113,
+ VRTrackedCameraError_InvalidArgument = 114,
+ VRTrackedCameraError_InvalidFrameBufferSize = 115,
+};
+
+enum EVRTrackedCameraFrameLayout
+{
+ EVRTrackedCameraFrameLayout_Mono = 0x0001,
+ EVRTrackedCameraFrameLayout_Stereo = 0x0002,
+ EVRTrackedCameraFrameLayout_VerticalLayout = 0x0010, // Stereo frames are Top/Bottom (left/right)
+ EVRTrackedCameraFrameLayout_HorizontalLayout = 0x0020, // Stereo frames are Left/Right
+};
+
+enum EVRTrackedCameraFrameType
+{
+ VRTrackedCameraFrameType_Distorted = 0, // This is the camera video frame size in pixels, still distorted.
+ VRTrackedCameraFrameType_Undistorted, // In pixels, an undistorted inscribed rectangle region without invalid regions. This size is subject to changes shortly.
+ VRTrackedCameraFrameType_MaximumUndistorted, // In pixels, maximum undistorted with invalid regions. Non zero alpha component identifies valid regions.
+ MAX_CAMERA_FRAME_TYPES
+};
+
+enum EVRDistortionFunctionType
+{
+ VRDistortionFunctionType_None,
+ VRDistortionFunctionType_FTheta,
+ VRDistortionFunctionType_Extended_FTheta,
+ MAX_DISTORTION_FUNCTION_TYPES,
+};
+
+static const uint32_t k_unMaxDistortionFunctionParameters = 8;
+
+typedef uint64_t TrackedCameraHandle_t;
+#define INVALID_TRACKED_CAMERA_HANDLE ((vr::TrackedCameraHandle_t)0)
+
+struct CameraVideoStreamFrameHeader_t
+{
+ EVRTrackedCameraFrameType eFrameType;
+
+ uint32_t nWidth;
+ uint32_t nHeight;
+ uint32_t nBytesPerPixel;
+
+ uint32_t nFrameSequence;
+
+ TrackedDevicePose_t trackedDevicePose;
+
+ uint64_t ulFrameExposureTime; // mid-point of the exposure of the image in host system ticks
+};
+
+// Screenshot types
+typedef uint32_t ScreenshotHandle_t;
+
+static const uint32_t k_unScreenshotHandleInvalid = 0;
+
+/** Compositor frame timing reprojection flags. */
+const uint32_t VRCompositor_ReprojectionReason_Cpu = 0x01;
+const uint32_t VRCompositor_ReprojectionReason_Gpu = 0x02;
+const uint32_t VRCompositor_ReprojectionAsync = 0x04; // This flag indicates the async reprojection mode is active,
+ // but does not indicate if reprojection actually happened or not.
+ // Use the ReprojectionReason flags above to check if reprojection
+ // was actually applied (i.e. scene texture was reused).
+ // NumFramePresents > 1 also indicates the scene texture was reused,
+ // and also the number of times that it was presented in total.
+
+const uint32_t VRCompositor_ReprojectionMotion = 0x08; // This flag indicates whether or not motion smoothing was triggered for this frame
+
+const uint32_t VRCompositor_PredictionMask = 0x30; // The runtime may predict more than one frame (up to four) ahead if
+ // it detects the application is taking too long to render. These two
+ // bits will contain the count of additional frames (normally zero).
+ // Use the VR_COMPOSITOR_ADDITIONAL_PREDICTED_FRAMES macro to read from
+ // the latest frame timing entry.
+
+const uint32_t VRCompositor_ThrottleMask = 0xC0; // Number of frames the compositor is throttling the application.
+ // Use the VR_COMPOSITOR_NUMBER_OF_THROTTLED_FRAMES macro to read from
+ // the latest frame timing entry.
+
+#define VR_COMPOSITOR_ADDITIONAL_PREDICTED_FRAMES( timing ) ( ( ( timing ).m_nReprojectionFlags & vr::VRCompositor_PredictionMask ) >> 4 )
+#define VR_COMPOSITOR_NUMBER_OF_THROTTLED_FRAMES( timing ) ( ( ( timing ).m_nReprojectionFlags & vr::VRCompositor_ThrottleMask ) >> 6 )
+
+/** Provides a single frame's timing information to the app */
+struct Compositor_FrameTiming
+{
+ uint32_t m_nSize; // Set to sizeof( Compositor_FrameTiming )
+ uint32_t m_nFrameIndex;
+ uint32_t m_nNumFramePresents; // number of times this frame was presented
+ uint32_t m_nNumMisPresented; // number of times this frame was presented on a vsync other than it was originally predicted to
+ uint32_t m_nNumDroppedFrames; // number of additional times previous frame was scanned out
+ uint32_t m_nReprojectionFlags;
+
+ /** Absolute time reference for comparing frames. This aligns with the vsync that running start is relative to. */
+ double m_flSystemTimeInSeconds;
+
+ /** These times may include work from other processes due to OS scheduling.
+ * The fewer packets of work these are broken up into, the less likely this will happen.
+ * GPU work can be broken up by calling Flush. This can sometimes be useful to get the GPU started
+ * processing that work earlier in the frame. */
+ float m_flPreSubmitGpuMs; // time spent rendering the scene (gpu work submitted between WaitGetPoses and second Submit)
+ float m_flPostSubmitGpuMs; // additional time spent rendering by application (e.g. companion window)
+ float m_flTotalRenderGpuMs; // time between work submitted immediately after present (ideally vsync) until the end of compositor submitted work
+ float m_flCompositorRenderGpuMs; // time spend performing distortion correction, rendering chaperone, overlays, etc.
+ float m_flCompositorRenderCpuMs; // time spent on cpu submitting the above work for this frame
+ float m_flCompositorIdleCpuMs; // time spent waiting for running start (application could have used this much more time)
+
+ /** Miscellaneous measured intervals. */
+ float m_flClientFrameIntervalMs; // time between calls to WaitGetPoses
+ float m_flPresentCallCpuMs; // time blocked on call to present (usually 0.0, but can go long)
+ float m_flWaitForPresentCpuMs; // time spent spin-waiting for frame index to change (not near-zero indicates wait object failure)
+ float m_flSubmitFrameMs; // time spent in IVRCompositor::Submit (not near-zero indicates driver issue)
+
+ /** The following are all relative to this frame's SystemTimeInSeconds */
+ float m_flWaitGetPosesCalledMs;
+ float m_flNewPosesReadyMs;
+ float m_flNewFrameReadyMs; // second call to IVRCompositor::Submit
+ float m_flCompositorUpdateStartMs;
+ float m_flCompositorUpdateEndMs;
+ float m_flCompositorRenderStartMs;
+
+ vr::TrackedDevicePose_t m_HmdPose; // pose used by app to render this frame
+
+ uint32_t m_nNumVSyncsReadyForUse;
+ uint32_t m_nNumVSyncsToFirstView;
+};
+
+/** Provides compositor benchmark results to the app */
+struct Compositor_BenchmarkResults
+{
+ float m_flMegaPixelsPerSecond; // Measurement of GPU MP/s performed by compositor benchmark
+ float m_flHmdRecommendedMegaPixelsPerSecond; // Recommended default MP/s given the HMD resolution, refresh, and panel mask.
+};
+
+/** Frame timing data provided by direct mode drivers. */
+struct DriverDirectMode_FrameTiming
+{
+ uint32_t m_nSize; // Set to sizeof( DriverDirectMode_FrameTiming )
+ uint32_t m_nNumFramePresents; // number of times frame was presented
+ uint32_t m_nNumMisPresented; // number of times frame was presented on a vsync other than it was originally predicted to
+ uint32_t m_nNumDroppedFrames; // number of additional times previous frame was scanned out (i.e. compositor missed vsync)
+ uint32_t m_nReprojectionFlags;
+};
+
+/** These flags will be set on DriverDirectMode_FrameTiming::m_nReprojectionFlags when IVRDriverDirectModeComponent::GetFrameTiming is called for drivers to optionally respond to. */
+const uint32_t VRCompositor_ReprojectionMotion_Enabled = 0x100; // Motion Smoothing is enabled in the UI for the currently running application
+const uint32_t VRCompositor_ReprojectionMotion_ForcedOn = 0x200; // Motion Smoothing is forced on in the UI for the currently running application
+const uint32_t VRCompositor_ReprojectionMotion_AppThrottled = 0x400; // Application is requesting throttling via ForceInterleavedReprojectionOn
+
+
+enum EVSync
+{
+ VSync_None,
+ VSync_WaitRender, // block following render work until vsync
+ VSync_NoWaitRender, // do not block following render work (allow to get started early)
+};
+
+enum EVRMuraCorrectionMode
+{
+ EVRMuraCorrectionMode_Default = 0,
+ EVRMuraCorrectionMode_NoCorrection
+};
+
+/** raw IMU data provided by IVRIOBuffer from paths to tracked devices with IMUs */
+enum Imu_OffScaleFlags
+{
+ OffScale_AccelX = 0x01,
+ OffScale_AccelY = 0x02,
+ OffScale_AccelZ = 0x04,
+ OffScale_GyroX = 0x08,
+ OffScale_GyroY = 0x10,
+ OffScale_GyroZ = 0x20,
+};
+
+struct ImuSample_t
+{
+ double fSampleTime;
+ HmdVector3d_t vAccel;
+ HmdVector3d_t vGyro;
+ uint32_t unOffScaleFlags;
+};
+
+#pragma pack( pop )
+
+#define VR_INTERFACE
+
+// Mozilla: see README.mozilla for more details
+// figure out how to import from the VR API dll
+// #if defined(_WIN32)
+
+// #if !defined(OPENVR_BUILD_STATIC)
+// #ifdef VR_API_EXPORT
+// #define VR_INTERFACE extern "C" __declspec( dllexport )
+// #else
+// #define VR_INTERFACE extern "C" __declspec( dllimport )
+// #endif
+// #else
+// #define VR_INTERFACE extern "C"
+// #endif
+
+// #elif defined(__GNUC__) || defined(COMPILER_GCC) || defined(__APPLE__)
+
+// #ifdef VR_API_EXPORT
+// #define VR_INTERFACE extern "C" __attribute__((visibility("default")))
+// #else
+// #define VR_INTERFACE extern "C"
+// #endif
+
+// #else
+// #error "Unsupported Platform."
+// #endif
+
+
+#if defined( _WIN32 )
+ #define VR_CALLTYPE __cdecl
+#else
+ #define VR_CALLTYPE
+#endif
+
+} // namespace vr
+
+#endif // _INCLUDE_VRTYPES_H
+
+
+// vrannotation.h
+#ifdef API_GEN
+# define VR_CLANG_ATTR(ATTR) __attribute__((annotate( ATTR )))
+#else
+# define VR_CLANG_ATTR(ATTR)
+#endif
+
+#define VR_METHOD_DESC(DESC) VR_CLANG_ATTR( "desc:" #DESC ";" )
+#define VR_IGNOREATTR() VR_CLANG_ATTR( "ignore" )
+#define VR_OUT_STRUCT() VR_CLANG_ATTR( "out_struct: ;" )
+#define VR_OUT_STRING() VR_CLANG_ATTR( "out_string: ;" )
+#define VR_OUT_ARRAY_CALL(COUNTER,FUNCTION,PARAMS) VR_CLANG_ATTR( "out_array_call:" #COUNTER "," #FUNCTION "," #PARAMS ";" )
+#define VR_OUT_ARRAY_COUNT(COUNTER) VR_CLANG_ATTR( "out_array_count:" #COUNTER ";" )
+#define VR_ARRAY_COUNT(COUNTER) VR_CLANG_ATTR( "array_count:" #COUNTER ";" )
+#define VR_ARRAY_COUNT_D(COUNTER, DESC) VR_CLANG_ATTR( "array_count:" #COUNTER ";desc:" #DESC )
+#define VR_BUFFER_COUNT(COUNTER) VR_CLANG_ATTR( "buffer_count:" #COUNTER ";" )
+#define VR_OUT_BUFFER_COUNT(COUNTER) VR_CLANG_ATTR( "out_buffer_count:" #COUNTER ";" )
+#define VR_OUT_STRING_COUNT(COUNTER) VR_CLANG_ATTR( "out_string_count:" #COUNTER ";" )
+
+// ivrsystem.h
+namespace vr
+{
+
+class IVRSystem
+{
+public:
+
+
+ // ------------------------------------
+ // Display Methods
+ // ------------------------------------
+
+ /** Suggested size for the intermediate render target that the distortion pulls from. */
+ virtual void GetRecommendedRenderTargetSize( uint32_t *pnWidth, uint32_t *pnHeight ) = 0;
+
+ /** The projection matrix for the specified eye */
+ virtual HmdMatrix44_t GetProjectionMatrix( EVREye eEye, float fNearZ, float fFarZ ) = 0;
+
+ /** The components necessary to build your own projection matrix in case your
+ * application is doing something fancy like infinite Z */
+ virtual void GetProjectionRaw( EVREye eEye, float *pfLeft, float *pfRight, float *pfTop, float *pfBottom ) = 0;
+
+ /** Gets the result of the distortion function for the specified eye and input UVs. UVs go from 0,0 in
+ * the upper left of that eye's viewport and 1,1 in the lower right of that eye's viewport.
+ * Returns true for success. Otherwise, returns false, and distortion coordinates are not suitable. */
+ virtual bool ComputeDistortion( EVREye eEye, float fU, float fV, DistortionCoordinates_t *pDistortionCoordinates ) = 0;
+
+ /** Returns the transform from eye space to the head space. Eye space is the per-eye flavor of head
+ * space that provides stereo disparity. Instead of Model * View * Projection the sequence is Model * View * Eye^-1 * Projection.
+ * Normally View and Eye^-1 will be multiplied together and treated as View in your application.
+ */
+ virtual HmdMatrix34_t GetEyeToHeadTransform( EVREye eEye ) = 0;
+
+ /** Returns the number of elapsed seconds since the last recorded vsync event. This
+ * will come from a vsync timer event in the timer if possible or from the application-reported
+ * time if that is not available. If no vsync times are available the function will
+ * return zero for vsync time and frame counter and return false from the method. */
+ virtual bool GetTimeSinceLastVsync( float *pfSecondsSinceLastVsync, uint64_t *pulFrameCounter ) = 0;
+
+ /** [D3D9 Only]
+ * Returns the adapter index that the user should pass into CreateDevice to set up D3D9 in such
+ * a way that it can go full screen exclusive on the HMD. Returns -1 if there was an error.
+ */
+ virtual int32_t GetD3D9AdapterIndex() = 0;
+
+ /** [D3D10/11 Only]
+ * Returns the adapter index that the user should pass into EnumAdapters to create the device
+ * and swap chain in DX10 and DX11. If an error occurs the index will be set to -1.
+ */
+ virtual void GetDXGIOutputInfo( int32_t *pnAdapterIndex ) = 0;
+
+ /**
+ * Returns platform- and texture-type specific adapter identification so that applications and the
+ * compositor are creating textures and swap chains on the same GPU. If an error occurs the device
+ * will be set to 0.
+ * pInstance is an optional parameter that is required only when textureType is TextureType_Vulkan.
+ * [D3D10/11/12 Only (D3D9 Not Supported)]
+ * Returns the adapter LUID that identifies the GPU attached to the HMD. The user should
+ * enumerate all adapters using IDXGIFactory::EnumAdapters and IDXGIAdapter::GetDesc to find
+ * the adapter with the matching LUID, or use IDXGIFactory4::EnumAdapterByLuid.
+ * The discovered IDXGIAdapter should be used to create the device and swap chain.
+ * [Vulkan Only]
+ * Returns the VkPhysicalDevice that should be used by the application.
+ * pInstance must be the instance the application will use to query for the VkPhysicalDevice. The application
+ * must create the VkInstance with extensions returned by IVRCompositor::GetVulkanInstanceExtensionsRequired enabled.
+ * [macOS Only]
+ * For TextureType_IOSurface returns the id<MTLDevice> that should be used by the application.
+ * On 10.13+ for TextureType_OpenGL returns the 'registryId' of the renderer which should be used
+ * by the application. See Apple Technical Q&A QA1168 for information on enumerating GL Renderers, and the
+ * new kCGLRPRegistryIDLow and kCGLRPRegistryIDHigh CGLRendererProperty values in the 10.13 SDK.
+ * Pre 10.13 for TextureType_OpenGL returns 0, as there is no dependable way to correlate the HMDs MTLDevice
+ * with a GL Renderer.
+ */
+ virtual void GetOutputDevice( uint64_t *pnDevice, ETextureType textureType, VkInstance_T *pInstance = nullptr ) = 0;
+
+ // ------------------------------------
+ // Display Mode methods
+ // ------------------------------------
+
+ /** Use to determine if the headset display is part of the desktop (i.e. extended) or hidden (i.e. direct mode). */
+ virtual bool IsDisplayOnDesktop() = 0;
+
+ /** Set the display visibility (true = extended, false = direct mode). Return value of true indicates that the change was successful. */
+ virtual bool SetDisplayVisibility( bool bIsVisibleOnDesktop ) = 0;
+
+ // ------------------------------------
+ // Tracking Methods
+ // ------------------------------------
+
+ /** The pose that the tracker thinks that the HMD will be in at the specified number of seconds into the
+ * future. Pass 0 to get the state at the instant the method is called. Most of the time the application should
+ * calculate the time until the photons will be emitted from the display and pass that time into the method.
+ *
+ * This is roughly analogous to the inverse of the view matrix in most applications, though
+ * many games will need to do some additional rotation or translation on top of the rotation
+ * and translation provided by the head pose.
+ *
+ * For devices where bPoseIsValid is true the application can use the pose to position the device
+ * in question. The provided array can be any size up to k_unMaxTrackedDeviceCount.
+ *
+ * Seated experiences should call this method with TrackingUniverseSeated and receive poses relative
+ * to the seated zero pose. Standing experiences should call this method with TrackingUniverseStanding
+ * and receive poses relative to the Chaperone Play Area. TrackingUniverseRawAndUncalibrated should
+ * probably not be used unless the application is the Chaperone calibration tool itself, but will provide
+ * poses relative to the hardware-specific coordinate system in the driver.
+ */
+ virtual void GetDeviceToAbsoluteTrackingPose( ETrackingUniverseOrigin eOrigin, float fPredictedSecondsToPhotonsFromNow, VR_ARRAY_COUNT(unTrackedDevicePoseArrayCount) TrackedDevicePose_t *pTrackedDevicePoseArray, uint32_t unTrackedDevicePoseArrayCount ) = 0;
+
+ /** Sets the zero pose for the seated tracker coordinate system to the current position and yaw of the HMD. After
+ * ResetSeatedZeroPose all GetDeviceToAbsoluteTrackingPose calls that pass TrackingUniverseSeated as the origin
+ * will be relative to this new zero pose. The new zero coordinate system will not change the fact that the Y axis
+ * is up in the real world, so the next pose returned from GetDeviceToAbsoluteTrackingPose after a call to
+ * ResetSeatedZeroPose may not be exactly an identity matrix.
+ *
+ * NOTE: This function overrides the user's previously saved seated zero pose and should only be called as the result of a user action.
+ * Users are also able to set their seated zero pose via the OpenVR Dashboard.
+ **/
+ virtual void ResetSeatedZeroPose() = 0;
+
+ /** Returns the transform from the seated zero pose to the standing absolute tracking system. This allows
+ * applications to represent the seated origin to used or transform object positions from one coordinate
+ * system to the other.
+ *
+ * The seated origin may or may not be inside the Play Area or Collision Bounds returned by IVRChaperone. Its position
+ * depends on what the user has set from the Dashboard settings and previous calls to ResetSeatedZeroPose. */
+ virtual HmdMatrix34_t GetSeatedZeroPoseToStandingAbsoluteTrackingPose() = 0;
+
+ /** Returns the transform from the tracking origin to the standing absolute tracking system. This allows
+ * applications to convert from raw tracking space to the calibrated standing coordinate system. */
+ virtual HmdMatrix34_t GetRawZeroPoseToStandingAbsoluteTrackingPose() = 0;
+
+ /** Get a sorted array of device indices of a given class of tracked devices (e.g. controllers). Devices are sorted right to left
+ * relative to the specified tracked device (default: hmd -- pass in -1 for absolute tracking space). Returns the number of devices
+ * in the list, or the size of the array needed if not large enough. */
+ virtual uint32_t GetSortedTrackedDeviceIndicesOfClass( ETrackedDeviceClass eTrackedDeviceClass, VR_ARRAY_COUNT(unTrackedDeviceIndexArrayCount) vr::TrackedDeviceIndex_t *punTrackedDeviceIndexArray, uint32_t unTrackedDeviceIndexArrayCount, vr::TrackedDeviceIndex_t unRelativeToTrackedDeviceIndex = k_unTrackedDeviceIndex_Hmd ) = 0;
+
+ /** Returns the level of activity on the device. */
+ virtual EDeviceActivityLevel GetTrackedDeviceActivityLevel( vr::TrackedDeviceIndex_t unDeviceId ) = 0;
+
+ /** Convenience utility to apply the specified transform to the specified pose.
+ * This properly transforms all pose components, including velocity and angular velocity
+ */
+ virtual void ApplyTransform( TrackedDevicePose_t *pOutputPose, const TrackedDevicePose_t *pTrackedDevicePose, const HmdMatrix34_t *pTransform ) = 0;
+
+ /** Returns the device index associated with a specific role, for example the left hand or the right hand. This function is deprecated in favor of the new IVRInput system. */
+ virtual vr::TrackedDeviceIndex_t GetTrackedDeviceIndexForControllerRole( vr::ETrackedControllerRole unDeviceType ) = 0;
+
+ /** Returns the controller type associated with a device index. This function is deprecated in favor of the new IVRInput system. */
+ virtual vr::ETrackedControllerRole GetControllerRoleForTrackedDeviceIndex( vr::TrackedDeviceIndex_t unDeviceIndex ) = 0;
+
+ // ------------------------------------
+ // Property methods
+ // ------------------------------------
+
+ /** Returns the device class of a tracked device. If there has not been a device connected in this slot
+ * since the application started this function will return TrackedDevice_Invalid. For previous detected
+ * devices the function will return the previously observed device class.
+ *
+ * To determine which devices exist on the system, just loop from 0 to k_unMaxTrackedDeviceCount and check
+ * the device class. Every device with something other than TrackedDevice_Invalid is associated with an
+ * actual tracked device. */
+ virtual ETrackedDeviceClass GetTrackedDeviceClass( vr::TrackedDeviceIndex_t unDeviceIndex ) = 0;
+
+ /** Returns true if there is a device connected in this slot. */
+ virtual bool IsTrackedDeviceConnected( vr::TrackedDeviceIndex_t unDeviceIndex ) = 0;
+
+ /** Returns a bool property. If the device index is not valid or the property is not a bool type this function will return false. */
+ virtual bool GetBoolTrackedDeviceProperty( vr::TrackedDeviceIndex_t unDeviceIndex, ETrackedDeviceProperty prop, ETrackedPropertyError *pError = 0L ) = 0;
+
+ /** Returns a float property. If the device index is not valid or the property is not a float type this function will return 0. */
+ virtual float GetFloatTrackedDeviceProperty( vr::TrackedDeviceIndex_t unDeviceIndex, ETrackedDeviceProperty prop, ETrackedPropertyError *pError = 0L ) = 0;
+
+ /** Returns an int property. If the device index is not valid or the property is not a int type this function will return 0. */
+ virtual int32_t GetInt32TrackedDeviceProperty( vr::TrackedDeviceIndex_t unDeviceIndex, ETrackedDeviceProperty prop, ETrackedPropertyError *pError = 0L ) = 0;
+
+ /** Returns a uint64 property. If the device index is not valid or the property is not a uint64 type this function will return 0. */
+ virtual uint64_t GetUint64TrackedDeviceProperty( vr::TrackedDeviceIndex_t unDeviceIndex, ETrackedDeviceProperty prop, ETrackedPropertyError *pError = 0L ) = 0;
+
+ /** Returns a matrix property. If the device index is not valid or the property is not a matrix type, this function will return identity. */
+ virtual HmdMatrix34_t GetMatrix34TrackedDeviceProperty( vr::TrackedDeviceIndex_t unDeviceIndex, ETrackedDeviceProperty prop, ETrackedPropertyError *pError = 0L ) = 0;
+
+ /** Returns an array of one type of property. If the device index is not valid or the property is not a single value or an array of the specified type,
+ * this function will return 0. Otherwise it returns the number of bytes necessary to hold the array of properties. If unBufferSize is
+ * greater than the returned size and pBuffer is non-NULL, pBuffer is filled with the contents of array of properties. */
+ virtual uint32_t GetArrayTrackedDeviceProperty( vr::TrackedDeviceIndex_t unDeviceIndex, ETrackedDeviceProperty prop, PropertyTypeTag_t propType, void *pBuffer, uint32_t unBufferSize, ETrackedPropertyError *pError = 0L ) = 0;
+
+ /** Returns a string property. If the device index is not valid or the property is not a string type this function will
+ * return 0. Otherwise it returns the length of the number of bytes necessary to hold this string including the trailing
+ * null. Strings will always fit in buffers of k_unMaxPropertyStringSize characters. */
+ virtual uint32_t GetStringTrackedDeviceProperty( vr::TrackedDeviceIndex_t unDeviceIndex, ETrackedDeviceProperty prop, VR_OUT_STRING() char *pchValue, uint32_t unBufferSize, ETrackedPropertyError *pError = 0L ) = 0;
+
+ /** returns a string that corresponds with the specified property error. The string will be the name
+ * of the error enum value for all valid error codes */
+ virtual const char *GetPropErrorNameFromEnum( ETrackedPropertyError error ) = 0;
+
+ // ------------------------------------
+ // Event methods
+ // ------------------------------------
+
+ /** Returns true and fills the event with the next event on the queue if there is one. If there are no events
+ * this method returns false. uncbVREvent should be the size in bytes of the VREvent_t struct */
+ virtual bool PollNextEvent( VREvent_t *pEvent, uint32_t uncbVREvent ) = 0;
+
+ /** Returns true and fills the event with the next event on the queue if there is one. If there are no events
+ * this method returns false. Fills in the pose of the associated tracked device in the provided pose struct.
+ * This pose will always be older than the call to this function and should not be used to render the device.
+ uncbVREvent should be the size in bytes of the VREvent_t struct */
+ virtual bool PollNextEventWithPose( ETrackingUniverseOrigin eOrigin, VREvent_t *pEvent, uint32_t uncbVREvent, vr::TrackedDevicePose_t *pTrackedDevicePose ) = 0;
+
+ /** returns the name of an EVREvent enum value */
+ virtual const char *GetEventTypeNameFromEnum( EVREventType eType ) = 0;
+
+ // ------------------------------------
+ // Rendering helper methods
+ // ------------------------------------
+
+ /** Returns the hidden area mesh for the current HMD. The pixels covered by this mesh will never be seen by the user after the lens distortion is
+ * applied based on visibility to the panels. If this HMD does not have a hidden area mesh, the vertex data and count will be NULL and 0 respectively.
+ * This mesh is meant to be rendered into the stencil buffer (or into the depth buffer setting nearz) before rendering each eye's view.
+ * This will improve performance by letting the GPU early-reject pixels the user will never see before running the pixel shader.
+ * NOTE: Render this mesh with backface culling disabled since the winding order of the vertices can be different per-HMD or per-eye.
+ * Setting the bInverse argument to true will produce the visible area mesh that is commonly used in place of full-screen quads. The visible area mesh covers all of the pixels the hidden area mesh does not cover.
+ * Setting the bLineLoop argument will return a line loop of vertices in HiddenAreaMesh_t->pVertexData with HiddenAreaMesh_t->unTriangleCount set to the number of vertices.
+ */
+ virtual HiddenAreaMesh_t GetHiddenAreaMesh( EVREye eEye, EHiddenAreaMeshType type = k_eHiddenAreaMesh_Standard ) = 0;
+
+ // ------------------------------------
+ // Controller methods
+ // ------------------------------------
+
+ /** Fills the supplied struct with the current state of the controller. Returns false if the controller index
+ * is invalid. This function is deprecated in favor of the new IVRInput system. */
+ virtual bool GetControllerState( vr::TrackedDeviceIndex_t unControllerDeviceIndex, vr::VRControllerState_t *pControllerState, uint32_t unControllerStateSize ) = 0;
+
+ /** fills the supplied struct with the current state of the controller and the provided pose with the pose of
+ * the controller when the controller state was updated most recently. Use this form if you need a precise controller
+ * pose as input to your application when the user presses or releases a button. This function is deprecated in favor of the new IVRInput system. */
+ virtual bool GetControllerStateWithPose( ETrackingUniverseOrigin eOrigin, vr::TrackedDeviceIndex_t unControllerDeviceIndex, vr::VRControllerState_t *pControllerState, uint32_t unControllerStateSize, TrackedDevicePose_t *pTrackedDevicePose ) = 0;
+
+ /** Trigger a single haptic pulse on a controller. After this call the application may not trigger another haptic pulse on this controller
+ * and axis combination for 5ms. This function is deprecated in favor of the new IVRInput system. */
+ virtual void TriggerHapticPulse( vr::TrackedDeviceIndex_t unControllerDeviceIndex, uint32_t unAxisId, unsigned short usDurationMicroSec ) = 0;
+
+ /** returns the name of an EVRButtonId enum value. This function is deprecated in favor of the new IVRInput system. */
+ virtual const char *GetButtonIdNameFromEnum( EVRButtonId eButtonId ) = 0;
+
+ /** returns the name of an EVRControllerAxisType enum value. This function is deprecated in favor of the new IVRInput system. */
+ virtual const char *GetControllerAxisTypeNameFromEnum( EVRControllerAxisType eAxisType ) = 0;
+
+ /** Returns true if this application is receiving input from the system. This would return false if
+ * system-related functionality is consuming the input stream. */
+ virtual bool IsInputAvailable() = 0;
+
+ /** Returns true SteamVR is drawing controllers on top of the application. Applications should consider
+ * not drawing anything attached to the user's hands in this case. */
+ virtual bool IsSteamVRDrawingControllers() = 0;
+
+ /** Returns true if the user has put SteamVR into a mode that is distracting them from the application.
+ * For applications where this is appropriate, the application should pause ongoing activity. */
+ virtual bool ShouldApplicationPause() = 0;
+
+ /** Returns true if SteamVR is doing significant rendering work and the game should do what it can to reduce
+ * its own workload. One common way to do this is to reduce the size of the render target provided for each eye. */
+ virtual bool ShouldApplicationReduceRenderingWork() = 0;
+
+ // ------------------------------------
+ // Firmware methods
+ // ------------------------------------
+
+ /** Performs the actual firmware update if applicable.
+ * The following events will be sent, if VRFirmwareError_None was returned: VREvent_FirmwareUpdateStarted, VREvent_FirmwareUpdateFinished
+ * Use the properties Prop_Firmware_UpdateAvailable_Bool, Prop_Firmware_ManualUpdate_Bool, and Prop_Firmware_ManualUpdateURL_String
+ * to figure our whether a firmware update is available, and to figure out whether its a manual update
+ * Prop_Firmware_ManualUpdateURL_String should point to an URL describing the manual update process */
+ virtual vr::EVRFirmwareError PerformFirmwareUpdate( vr::TrackedDeviceIndex_t unDeviceIndex ) = 0;
+
+ // ------------------------------------
+ // Application life cycle methods
+ // ------------------------------------
+
+ /** Call this to acknowledge to the system that VREvent_Quit has been received and that the process is exiting.
+ * This extends the timeout until the process is killed. */
+ virtual void AcknowledgeQuit_Exiting() = 0;
+
+ // -------------------------------------
+ // App container sandbox methods
+ // -------------------------------------
+
+ /** Retrieves a null-terminated, semicolon-delimited list of UTF8 file paths that an application
+ * must have read access to when running inside of an app container. Returns the number of bytes
+ * needed to hold the list. */
+ virtual uint32_t GetAppContainerFilePaths( VR_OUT_STRING() char *pchBuffer, uint32_t unBufferSize ) = 0;
+
+ // -------------------------------------
+ // System methods
+ // -------------------------------------
+
+ /** Returns the current version of the SteamVR runtime. The returned string will remain valid until VR_Shutdown is called.
+ *
+ * NOTE: Is it not appropriate to use this version to test for the presence of any SteamVR feature. Only use this version
+ * number for logging or showing to a user, and not to try to detect anything at runtime. When appropriate, feature-specific
+ * presence information is provided by other APIs. */
+ virtual const char *GetRuntimeVersion() = 0;
+
+};
+
+static const char * const IVRSystem_Version = "IVRSystem_021";
+
+}
+
+
+// ivrapplications.h
+namespace vr
+{
+
+ /** Used for all errors reported by the IVRApplications interface */
+ enum EVRApplicationError
+ {
+ VRApplicationError_None = 0,
+
+ VRApplicationError_AppKeyAlreadyExists = 100, // Only one application can use any given key
+ VRApplicationError_NoManifest = 101, // the running application does not have a manifest
+ VRApplicationError_NoApplication = 102, // No application is running
+ VRApplicationError_InvalidIndex = 103,
+ VRApplicationError_UnknownApplication = 104, // the application could not be found
+ VRApplicationError_IPCFailed = 105, // An IPC failure caused the request to fail
+ VRApplicationError_ApplicationAlreadyRunning = 106,
+ VRApplicationError_InvalidManifest = 107,
+ VRApplicationError_InvalidApplication = 108,
+ VRApplicationError_LaunchFailed = 109, // the process didn't start
+ VRApplicationError_ApplicationAlreadyStarting = 110, // the system was already starting the same application
+ VRApplicationError_LaunchInProgress = 111, // The system was already starting a different application
+ VRApplicationError_OldApplicationQuitting = 112,
+ VRApplicationError_TransitionAborted = 113,
+ VRApplicationError_IsTemplate = 114, // error when you try to call LaunchApplication() on a template type app (use LaunchTemplateApplication)
+ VRApplicationError_SteamVRIsExiting = 115,
+
+ VRApplicationError_BufferTooSmall = 200, // The provided buffer was too small to fit the requested data
+ VRApplicationError_PropertyNotSet = 201, // The requested property was not set
+ VRApplicationError_UnknownProperty = 202,
+ VRApplicationError_InvalidParameter = 203,
+ };
+
+ /** The maximum length of an application key */
+ static const uint32_t k_unMaxApplicationKeyLength = 128;
+
+ /** these are the properties available on applications. */
+ enum EVRApplicationProperty
+ {
+ VRApplicationProperty_Name_String = 0,
+
+ VRApplicationProperty_LaunchType_String = 11,
+ VRApplicationProperty_WorkingDirectory_String = 12,
+ VRApplicationProperty_BinaryPath_String = 13,
+ VRApplicationProperty_Arguments_String = 14,
+ VRApplicationProperty_URL_String = 15,
+
+ VRApplicationProperty_Description_String = 50,
+ VRApplicationProperty_NewsURL_String = 51,
+ VRApplicationProperty_ImagePath_String = 52,
+ VRApplicationProperty_Source_String = 53,
+ VRApplicationProperty_ActionManifestURL_String = 54,
+
+ VRApplicationProperty_IsDashboardOverlay_Bool = 60,
+ VRApplicationProperty_IsTemplate_Bool = 61,
+ VRApplicationProperty_IsInstanced_Bool = 62,
+ VRApplicationProperty_IsInternal_Bool = 63,
+ VRApplicationProperty_WantsCompositorPauseInStandby_Bool = 64,
+ VRApplicationProperty_IsHidden_Bool = 65,
+
+ VRApplicationProperty_LastLaunchTime_Uint64 = 70,
+ };
+
+ enum EVRSceneApplicationState
+ {
+ EVRSceneApplicationState_None = 0, // Scene Application is not running
+ EVRSceneApplicationState_Starting = 1, // Scene Application is starting
+ EVRSceneApplicationState_Quitting = 2, // Scene Application is quitting
+ EVRSceneApplicationState_Running = 3, // Scene Application is running, and submitting frames, a custom skybox, or a visible overlay
+ EVRSceneApplicationState_Waiting = 4, // Scene Application is running, but not drawing anything
+ };
+
+ struct AppOverrideKeys_t
+ {
+ const char *pchKey;
+ const char *pchValue;
+ };
+
+ /** Currently recognized mime types */
+ static const char * const k_pch_MimeType_HomeApp = "vr/home";
+ static const char * const k_pch_MimeType_GameTheater = "vr/game_theater";
+
+ class IVRApplications
+ {
+ public:
+
+ // --------------- Application management --------------- //
+
+ /** Adds an application manifest to the list to load when building the list of installed applications.
+ * Temporary manifests are not automatically loaded */
+ virtual EVRApplicationError AddApplicationManifest( const char *pchApplicationManifestFullPath, bool bTemporary = false ) = 0;
+
+ /** Removes an application manifest from the list to load when building the list of installed applications. */
+ virtual EVRApplicationError RemoveApplicationManifest( const char *pchApplicationManifestFullPath ) = 0;
+
+ /** Returns true if an application is installed */
+ virtual bool IsApplicationInstalled( const char *pchAppKey ) = 0;
+
+ /** Returns the number of applications available in the list */
+ virtual uint32_t GetApplicationCount() = 0;
+
+ /** Returns the key of the specified application. The index is at least 0 and is less than the return
+ * value of GetApplicationCount(). The buffer should be at least k_unMaxApplicationKeyLength in order to
+ * fit the key. */
+ virtual EVRApplicationError GetApplicationKeyByIndex( uint32_t unApplicationIndex, VR_OUT_STRING() char *pchAppKeyBuffer, uint32_t unAppKeyBufferLen ) = 0;
+
+ /** Returns the key of the application for the specified Process Id. The buffer should be at least
+ * k_unMaxApplicationKeyLength in order to fit the key. */
+ virtual EVRApplicationError GetApplicationKeyByProcessId( uint32_t unProcessId, VR_OUT_STRING() char *pchAppKeyBuffer, uint32_t unAppKeyBufferLen ) = 0;
+
+ /** Launches the application. The existing scene application will exit and then the new application will start.
+ * This call is not valid for dashboard overlay applications. */
+ virtual EVRApplicationError LaunchApplication( const char *pchAppKey ) = 0;
+
+ /** Launches an instance of an application of type template, with its app key being pchNewAppKey (which must be unique) and optionally override sections
+ * from the manifest file via AppOverrideKeys_t
+ */
+ virtual EVRApplicationError LaunchTemplateApplication( const char *pchTemplateAppKey, const char *pchNewAppKey, VR_ARRAY_COUNT( unKeys ) const AppOverrideKeys_t *pKeys, uint32_t unKeys ) = 0;
+
+ /** launches the application currently associated with this mime type and passes it the option args, typically the filename or object name of the item being launched */
+ virtual vr::EVRApplicationError LaunchApplicationFromMimeType( const char *pchMimeType, const char *pchArgs ) = 0;
+
+ /** Launches the dashboard overlay application if it is not already running. This call is only valid for
+ * dashboard overlay applications. */
+ virtual EVRApplicationError LaunchDashboardOverlay( const char *pchAppKey ) = 0;
+
+ /** Cancel a pending launch for an application */
+ virtual bool CancelApplicationLaunch( const char *pchAppKey ) = 0;
+
+ /** Identifies a running application. OpenVR can't always tell which process started in response
+ * to a URL. This function allows a URL handler (or the process itself) to identify the app key
+ * for the now running application. Passing a process ID of 0 identifies the calling process.
+ * The application must be one that's known to the system via a call to AddApplicationManifest. */
+ virtual EVRApplicationError IdentifyApplication( uint32_t unProcessId, const char *pchAppKey ) = 0;
+
+ /** Returns the process ID for an application. Return 0 if the application was not found or is not running. */
+ virtual uint32_t GetApplicationProcessId( const char *pchAppKey ) = 0;
+
+ /** Returns a string for an applications error */
+ virtual const char *GetApplicationsErrorNameFromEnum( EVRApplicationError error ) = 0;
+
+ // --------------- Application properties --------------- //
+
+ /** Returns a value for an application property. The required buffer size to fit this value will be returned. */
+ virtual uint32_t GetApplicationPropertyString( const char *pchAppKey, EVRApplicationProperty eProperty, VR_OUT_STRING() char *pchPropertyValueBuffer, uint32_t unPropertyValueBufferLen, EVRApplicationError *peError = nullptr ) = 0;
+
+ /** Returns a bool value for an application property. Returns false in all error cases. */
+ virtual bool GetApplicationPropertyBool( const char *pchAppKey, EVRApplicationProperty eProperty, EVRApplicationError *peError = nullptr ) = 0;
+
+ /** Returns a uint64 value for an application property. Returns 0 in all error cases. */
+ virtual uint64_t GetApplicationPropertyUint64( const char *pchAppKey, EVRApplicationProperty eProperty, EVRApplicationError *peError = nullptr ) = 0;
+
+ /** Sets the application auto-launch flag. This is only valid for applications which return true for VRApplicationProperty_IsDashboardOverlay_Bool. */
+ virtual EVRApplicationError SetApplicationAutoLaunch( const char *pchAppKey, bool bAutoLaunch ) = 0;
+
+ /** Gets the application auto-launch flag. This is only valid for applications which return true for VRApplicationProperty_IsDashboardOverlay_Bool. */
+ virtual bool GetApplicationAutoLaunch( const char *pchAppKey ) = 0;
+
+ /** Adds this mime-type to the list of supported mime types for this application*/
+ virtual EVRApplicationError SetDefaultApplicationForMimeType( const char *pchAppKey, const char *pchMimeType ) = 0;
+
+ /** return the app key that will open this mime type */
+ virtual bool GetDefaultApplicationForMimeType( const char *pchMimeType, VR_OUT_STRING() char *pchAppKeyBuffer, uint32_t unAppKeyBufferLen ) = 0;
+
+ /** Get the list of supported mime types for this application, comma-delimited */
+ virtual bool GetApplicationSupportedMimeTypes( const char *pchAppKey, VR_OUT_STRING() char *pchMimeTypesBuffer, uint32_t unMimeTypesBuffer ) = 0;
+
+ /** Get the list of app-keys that support this mime type, comma-delimited, the return value is number of bytes you need to return the full string */
+ virtual uint32_t GetApplicationsThatSupportMimeType( const char *pchMimeType, VR_OUT_STRING() char *pchAppKeysThatSupportBuffer, uint32_t unAppKeysThatSupportBuffer ) = 0;
+
+ /** Get the args list from an app launch that had the process already running, you call this when you get a VREvent_ApplicationMimeTypeLoad */
+ virtual uint32_t GetApplicationLaunchArguments( uint32_t unHandle, VR_OUT_STRING() char *pchArgs, uint32_t unArgs ) = 0;
+
+ // --------------- Transition methods --------------- //
+
+ /** Returns the app key for the application that is starting up */
+ virtual EVRApplicationError GetStartingApplication( VR_OUT_STRING() char *pchAppKeyBuffer, uint32_t unAppKeyBufferLen ) = 0;
+
+ /** Returns the application transition state */
+ virtual EVRSceneApplicationState GetSceneApplicationState() = 0;
+
+ /** Returns errors that would prevent the specified application from launching immediately. Calling this function will
+ * cause the current scene application to quit, so only call it when you are actually about to launch something else.
+ * What the caller should do about these failures depends on the failure:
+ * VRApplicationError_OldApplicationQuitting - An existing application has been told to quit. Wait for a VREvent_ProcessQuit
+ * and try again.
+ * VRApplicationError_ApplicationAlreadyStarting - This application is already starting. This is a permanent failure.
+ * VRApplicationError_LaunchInProgress - A different application is already starting. This is a permanent failure.
+ * VRApplicationError_None - Go ahead and launch. Everything is clear.
+ */
+ virtual EVRApplicationError PerformApplicationPrelaunchCheck( const char *pchAppKey ) = 0;
+
+ /** Returns a string for an application transition state */
+ virtual const char *GetSceneApplicationStateNameFromEnum( EVRSceneApplicationState state ) = 0;
+
+ /** Starts a subprocess within the calling application. This
+ * suppresses all application transition UI and automatically identifies the new executable
+ * as part of the same application. On success the calling process should exit immediately.
+ * If working directory is NULL or "" the directory portion of the binary path will be
+ * the working directory. */
+ virtual EVRApplicationError LaunchInternalProcess( const char *pchBinaryPath, const char *pchArguments, const char *pchWorkingDirectory ) = 0;
+
+ /** Returns the current scene process ID according to the application system. A scene process will get scene
+ * focus once it starts rendering, but it will appear here once it calls VR_Init with the Scene application
+ * type. */
+ virtual uint32_t GetCurrentSceneProcessId() = 0;
+ };
+
+ static const char * const IVRApplications_Version = "IVRApplications_007";
+
+} // namespace vr
+
+// ivrsettings.h
+#include <string>
+
+namespace vr
+{
+ enum EVRSettingsError
+ {
+ VRSettingsError_None = 0,
+ VRSettingsError_IPCFailed = 1,
+ VRSettingsError_WriteFailed = 2,
+ VRSettingsError_ReadFailed = 3,
+ VRSettingsError_JsonParseFailed = 4,
+ VRSettingsError_UnsetSettingHasNoDefault = 5, // This will be returned if the setting does not appear in the appropriate default file and has not been set
+ };
+
+ // The maximum length of a settings key
+ static const uint32_t k_unMaxSettingsKeyLength = 128;
+
+ class IVRSettings
+ {
+ public:
+ virtual const char *GetSettingsErrorNameFromEnum( EVRSettingsError eError ) = 0;
+
+ virtual void SetBool( const char *pchSection, const char *pchSettingsKey, bool bValue, EVRSettingsError *peError = nullptr ) = 0;
+ virtual void SetInt32( const char *pchSection, const char *pchSettingsKey, int32_t nValue, EVRSettingsError *peError = nullptr ) = 0;
+ virtual void SetFloat( const char *pchSection, const char *pchSettingsKey, float flValue, EVRSettingsError *peError = nullptr ) = 0;
+ virtual void SetString( const char *pchSection, const char *pchSettingsKey, const char *pchValue, EVRSettingsError *peError = nullptr ) = 0;
+
+ // Users of the system need to provide a proper default in default.vrsettings in the resources/settings/ directory
+ // of either the runtime or the driver_xxx directory. Otherwise the default will be false, 0, 0.0 or ""
+ virtual bool GetBool( const char *pchSection, const char *pchSettingsKey, EVRSettingsError *peError = nullptr ) = 0;
+ virtual int32_t GetInt32( const char *pchSection, const char *pchSettingsKey, EVRSettingsError *peError = nullptr ) = 0;
+ virtual float GetFloat( const char *pchSection, const char *pchSettingsKey, EVRSettingsError *peError = nullptr ) = 0;
+ virtual void GetString( const char *pchSection, const char *pchSettingsKey, VR_OUT_STRING() char *pchValue, uint32_t unValueLen, EVRSettingsError *peError = nullptr ) = 0;
+
+ virtual void RemoveSection( const char *pchSection, EVRSettingsError *peError = nullptr ) = 0;
+ virtual void RemoveKeyInSection( const char *pchSection, const char *pchSettingsKey, EVRSettingsError *peError = nullptr ) = 0;
+ };
+
+ //-----------------------------------------------------------------------------
+ static const char * const IVRSettings_Version = "IVRSettings_003";
+
+ class CVRSettingHelper
+ {
+ IVRSettings *m_pSettings;
+ public:
+ // Mozilla: see README.mozilla for more details
+ explicit CVRSettingHelper( IVRSettings *pSettings )
+ {
+ m_pSettings = pSettings;
+ }
+
+ const char *GetSettingsErrorNameFromEnum( EVRSettingsError eError )
+ {
+ return m_pSettings->GetSettingsErrorNameFromEnum( eError );
+ }
+
+ void SetBool( const char *pchSection, const char *pchSettingsKey, bool bValue, EVRSettingsError *peError = nullptr )
+ {
+ m_pSettings->SetBool( pchSection, pchSettingsKey, bValue, peError );
+ }
+
+ void SetInt32( const char *pchSection, const char *pchSettingsKey, int32_t nValue, EVRSettingsError *peError = nullptr )
+ {
+ m_pSettings->SetInt32( pchSection, pchSettingsKey, nValue, peError );
+ }
+ void SetFloat( const char *pchSection, const char *pchSettingsKey, float flValue, EVRSettingsError *peError = nullptr )
+ {
+ m_pSettings->SetFloat( pchSection, pchSettingsKey, flValue, peError );
+ }
+ void SetString( const char *pchSection, const char *pchSettingsKey, const char *pchValue, EVRSettingsError *peError = nullptr )
+ {
+ m_pSettings->SetString( pchSection, pchSettingsKey, pchValue, peError );
+ }
+ void SetString( const std::string & sSection, const std::string & sSettingsKey, const std::string & sValue, EVRSettingsError *peError = nullptr )
+ {
+ m_pSettings->SetString( sSection.c_str(), sSettingsKey.c_str(), sValue.c_str(), peError );
+ }
+
+ bool GetBool( const char *pchSection, const char *pchSettingsKey, EVRSettingsError *peError = nullptr )
+ {
+ return m_pSettings->GetBool( pchSection, pchSettingsKey, peError );
+ }
+ int32_t GetInt32( const char *pchSection, const char *pchSettingsKey, EVRSettingsError *peError = nullptr )
+ {
+ return m_pSettings->GetInt32( pchSection, pchSettingsKey, peError );
+ }
+ float GetFloat( const char *pchSection, const char *pchSettingsKey, EVRSettingsError *peError = nullptr )
+ {
+ return m_pSettings->GetFloat( pchSection, pchSettingsKey, peError );
+ }
+ void GetString( const char *pchSection, const char *pchSettingsKey, VR_OUT_STRING() char *pchValue, uint32_t unValueLen, EVRSettingsError *peError = nullptr )
+ {
+ m_pSettings->GetString( pchSection, pchSettingsKey, pchValue, unValueLen, peError );
+ }
+ std::string GetString( const std::string & sSection, const std::string & sSettingsKey, EVRSettingsError *peError = nullptr )
+ {
+ char buf[4096];
+ vr::EVRSettingsError eError;
+ m_pSettings->GetString( sSection.c_str(), sSettingsKey.c_str(), buf, sizeof( buf ), &eError );
+ if ( peError )
+ *peError = eError;
+ if ( eError == vr::VRSettingsError_None )
+ return buf;
+ else
+ return "";
+ }
+
+ void RemoveSection( const char *pchSection, EVRSettingsError *peError = nullptr )
+ {
+ m_pSettings->RemoveSection( pchSection, peError );
+ }
+ void RemoveKeyInSection( const char *pchSection, const char *pchSettingsKey, EVRSettingsError *peError = nullptr )
+ {
+ m_pSettings->RemoveKeyInSection( pchSection, pchSettingsKey, peError );
+ }
+ };
+
+
+ //-----------------------------------------------------------------------------
+ // steamvr keys
+ static const char * const k_pch_SteamVR_Section = "steamvr";
+ static const char * const k_pch_SteamVR_RequireHmd_String = "requireHmd";
+ static const char * const k_pch_SteamVR_ForcedDriverKey_String = "forcedDriver";
+ static const char * const k_pch_SteamVR_ForcedHmdKey_String = "forcedHmd";
+ static const char * const k_pch_SteamVR_DisplayDebug_Bool = "displayDebug";
+ static const char * const k_pch_SteamVR_DebugProcessPipe_String = "debugProcessPipe";
+ static const char * const k_pch_SteamVR_DisplayDebugX_Int32 = "displayDebugX";
+ static const char * const k_pch_SteamVR_DisplayDebugY_Int32 = "displayDebugY";
+ static const char * const k_pch_SteamVR_SendSystemButtonToAllApps_Bool= "sendSystemButtonToAllApps";
+ static const char * const k_pch_SteamVR_LogLevel_Int32 = "loglevel";
+ static const char * const k_pch_SteamVR_IPD_Float = "ipd";
+ static const char * const k_pch_SteamVR_Background_String = "background";
+ static const char * const k_pch_SteamVR_BackgroundUseDomeProjection_Bool = "backgroundUseDomeProjection";
+ static const char * const k_pch_SteamVR_BackgroundCameraHeight_Float = "backgroundCameraHeight";
+ static const char * const k_pch_SteamVR_BackgroundDomeRadius_Float = "backgroundDomeRadius";
+ static const char * const k_pch_SteamVR_GridColor_String = "gridColor";
+ static const char * const k_pch_SteamVR_PlayAreaColor_String = "playAreaColor";
+ static const char * const k_pch_SteamVR_TrackingLossColor_String = "trackingLossColor";
+ static const char * const k_pch_SteamVR_ShowStage_Bool = "showStage";
+ static const char * const k_pch_SteamVR_ActivateMultipleDrivers_Bool = "activateMultipleDrivers";
+ static const char * const k_pch_SteamVR_UsingSpeakers_Bool = "usingSpeakers";
+ static const char * const k_pch_SteamVR_SpeakersForwardYawOffsetDegrees_Float = "speakersForwardYawOffsetDegrees";
+ static const char * const k_pch_SteamVR_BaseStationPowerManagement_Int32 = "basestationPowerManagement";
+ static const char * const k_pch_SteamVR_ShowBaseStationPowerManagementTip_Int32 = "ShowBaseStationPowerManagementTip";
+ static const char * const k_pch_SteamVR_NeverKillProcesses_Bool = "neverKillProcesses";
+ static const char * const k_pch_SteamVR_SupersampleScale_Float = "supersampleScale";
+ static const char * const k_pch_SteamVR_MaxRecommendedResolution_Int32 = "maxRecommendedResolution";
+ static const char * const k_pch_SteamVR_MotionSmoothing_Bool = "motionSmoothing";
+ static const char * const k_pch_SteamVR_MotionSmoothingOverride_Int32 = "motionSmoothingOverride";
+ static const char * const k_pch_SteamVR_DisableAsyncReprojection_Bool = "disableAsync";
+ static const char * const k_pch_SteamVR_ForceFadeOnBadTracking_Bool = "forceFadeOnBadTracking";
+ static const char * const k_pch_SteamVR_DefaultMirrorView_Int32 = "mirrorView";
+ static const char * const k_pch_SteamVR_ShowLegacyMirrorView_Bool = "showLegacyMirrorView";
+ static const char * const k_pch_SteamVR_MirrorViewVisibility_Bool = "showMirrorView";
+ static const char * const k_pch_SteamVR_MirrorViewDisplayMode_Int32 = "mirrorViewDisplayMode";
+ static const char * const k_pch_SteamVR_MirrorViewEye_Int32 = "mirrorViewEye";
+ static const char * const k_pch_SteamVR_MirrorViewGeometry_String = "mirrorViewGeometry";
+ static const char * const k_pch_SteamVR_MirrorViewGeometryMaximized_String = "mirrorViewGeometryMaximized";
+ static const char * const k_pch_SteamVR_PerfGraphVisibility_Bool = "showPerfGraph";
+ static const char * const k_pch_SteamVR_StartMonitorFromAppLaunch = "startMonitorFromAppLaunch";
+ static const char * const k_pch_SteamVR_StartCompositorFromAppLaunch_Bool = "startCompositorFromAppLaunch";
+ static const char * const k_pch_SteamVR_StartDashboardFromAppLaunch_Bool = "startDashboardFromAppLaunch";
+ static const char * const k_pch_SteamVR_StartOverlayAppsFromDashboard_Bool = "startOverlayAppsFromDashboard";
+ static const char * const k_pch_SteamVR_EnableHomeApp = "enableHomeApp";
+ static const char * const k_pch_SteamVR_CycleBackgroundImageTimeSec_Int32 = "CycleBackgroundImageTimeSec";
+ static const char * const k_pch_SteamVR_RetailDemo_Bool = "retailDemo";
+ static const char * const k_pch_SteamVR_IpdOffset_Float = "ipdOffset";
+ static const char * const k_pch_SteamVR_AllowSupersampleFiltering_Bool = "allowSupersampleFiltering";
+ static const char * const k_pch_SteamVR_SupersampleManualOverride_Bool = "supersampleManualOverride";
+ static const char * const k_pch_SteamVR_EnableLinuxVulkanAsync_Bool = "enableLinuxVulkanAsync";
+ static const char * const k_pch_SteamVR_AllowDisplayLockedMode_Bool = "allowDisplayLockedMode";
+ static const char * const k_pch_SteamVR_HaveStartedTutorialForNativeChaperoneDriver_Bool = "haveStartedTutorialForNativeChaperoneDriver";
+ static const char * const k_pch_SteamVR_ForceWindows32bitVRMonitor = "forceWindows32BitVRMonitor";
+ static const char * const k_pch_SteamVR_DebugInputBinding = "debugInputBinding";
+ static const char * const k_pch_SteamVR_DoNotFadeToGrid = "doNotFadeToGrid";
+ static const char * const k_pch_SteamVR_RenderCameraMode = "renderCameraMode";
+ static const char * const k_pch_SteamVR_EnableSharedResourceJournaling = "enableSharedResourceJournaling";
+ static const char * const k_pch_SteamVR_EnableSafeMode = "enableSafeMode";
+ static const char * const k_pch_SteamVR_PreferredRefreshRate = "preferredRefreshRate";
+ static const char * const k_pch_SteamVR_LastVersionNotice = "lastVersionNotice";
+ static const char * const k_pch_SteamVR_LastVersionNoticeDate = "lastVersionNoticeDate";
+ static const char * const k_pch_SteamVR_HmdDisplayColorGainR_Float = "hmdDisplayColorGainR";
+ static const char * const k_pch_SteamVR_HmdDisplayColorGainG_Float = "hmdDisplayColorGainG";
+ static const char * const k_pch_SteamVR_HmdDisplayColorGainB_Float = "hmdDisplayColorGainB";
+ static const char * const k_pch_SteamVR_CustomIconStyle_String = "customIconStyle";
+ static const char * const k_pch_SteamVR_CustomOffIconStyle_String = "customOffIconStyle";
+ static const char * const k_pch_SteamVR_CustomIconForceUpdate_String = "customIconForceUpdate";
+ static const char * const k_pch_SteamVR_AllowGlobalActionSetPriority = "globalActionSetPriority";
+ static const char * const k_pch_SteamVR_OverlayRenderQuality = "overlayRenderQuality_2";
+
+ //-----------------------------------------------------------------------------
+ // direct mode keys
+ static const char * const k_pch_DirectMode_Section = "direct_mode";
+ static const char * const k_pch_DirectMode_Enable_Bool = "enable";
+ static const char * const k_pch_DirectMode_Count_Int32 = "count";
+ static const char * const k_pch_DirectMode_EdidVid_Int32 = "edidVid";
+ static const char * const k_pch_DirectMode_EdidPid_Int32 = "edidPid";
+
+ //-----------------------------------------------------------------------------
+ // lighthouse keys
+ static const char * const k_pch_Lighthouse_Section = "driver_lighthouse";
+ static const char * const k_pch_Lighthouse_DisableIMU_Bool = "disableimu";
+ static const char * const k_pch_Lighthouse_DisableIMUExceptHMD_Bool = "disableimuexcepthmd";
+ static const char * const k_pch_Lighthouse_UseDisambiguation_String = "usedisambiguation";
+ static const char * const k_pch_Lighthouse_DisambiguationDebug_Int32 = "disambiguationdebug";
+ static const char * const k_pch_Lighthouse_PrimaryBasestation_Int32 = "primarybasestation";
+ static const char * const k_pch_Lighthouse_DBHistory_Bool = "dbhistory";
+ static const char * const k_pch_Lighthouse_EnableBluetooth_Bool = "enableBluetooth";
+ static const char * const k_pch_Lighthouse_PowerManagedBaseStations_String = "PowerManagedBaseStations";
+ static const char * const k_pch_Lighthouse_PowerManagedBaseStations2_String = "PowerManagedBaseStations2";
+ static const char * const k_pch_Lighthouse_InactivityTimeoutForBaseStations_Int32 = "InactivityTimeoutForBaseStations";
+ static const char * const k_pch_Lighthouse_EnableImuFallback_Bool = "enableImuFallback";
+
+ //-----------------------------------------------------------------------------
+ // null keys
+ static const char * const k_pch_Null_Section = "driver_null";
+ static const char * const k_pch_Null_SerialNumber_String = "serialNumber";
+ static const char * const k_pch_Null_ModelNumber_String = "modelNumber";
+ static const char * const k_pch_Null_WindowX_Int32 = "windowX";
+ static const char * const k_pch_Null_WindowY_Int32 = "windowY";
+ static const char * const k_pch_Null_WindowWidth_Int32 = "windowWidth";
+ static const char * const k_pch_Null_WindowHeight_Int32 = "windowHeight";
+ static const char * const k_pch_Null_RenderWidth_Int32 = "renderWidth";
+ static const char * const k_pch_Null_RenderHeight_Int32 = "renderHeight";
+ static const char * const k_pch_Null_SecondsFromVsyncToPhotons_Float = "secondsFromVsyncToPhotons";
+ static const char * const k_pch_Null_DisplayFrequency_Float = "displayFrequency";
+
+ //-----------------------------------------------------------------------------
+ // Windows MR keys
+ static const char * const k_pch_WindowsMR_Section = "driver_holographic";
+
+ //-----------------------------------------------------------------------------
+ // user interface keys
+ static const char * const k_pch_UserInterface_Section = "userinterface";
+ static const char * const k_pch_UserInterface_StatusAlwaysOnTop_Bool = "StatusAlwaysOnTop";
+ static const char * const k_pch_UserInterface_MinimizeToTray_Bool = "MinimizeToTray";
+ static const char * const k_pch_UserInterface_HidePopupsWhenStatusMinimized_Bool = "HidePopupsWhenStatusMinimized";
+ static const char * const k_pch_UserInterface_Screenshots_Bool = "screenshots";
+ static const char * const k_pch_UserInterface_ScreenshotType_Int = "screenshotType";
+
+ //-----------------------------------------------------------------------------
+ // notification keys
+ static const char * const k_pch_Notifications_Section = "notifications";
+ static const char * const k_pch_Notifications_DoNotDisturb_Bool = "DoNotDisturb";
+
+ //-----------------------------------------------------------------------------
+ // keyboard keys
+ static const char * const k_pch_Keyboard_Section = "keyboard";
+ static const char * const k_pch_Keyboard_TutorialCompletions = "TutorialCompletions";
+ static const char * const k_pch_Keyboard_ScaleX = "ScaleX";
+ static const char * const k_pch_Keyboard_ScaleY = "ScaleY";
+ static const char * const k_pch_Keyboard_OffsetLeftX = "OffsetLeftX";
+ static const char * const k_pch_Keyboard_OffsetRightX = "OffsetRightX";
+ static const char * const k_pch_Keyboard_OffsetY = "OffsetY";
+ static const char * const k_pch_Keyboard_Smoothing = "Smoothing";
+
+ //-----------------------------------------------------------------------------
+ // perf keys
+ static const char * const k_pch_Perf_Section = "perfcheck";
+ static const char * const k_pch_Perf_PerfGraphInHMD_Bool = "perfGraphInHMD";
+ static const char * const k_pch_Perf_AllowTimingStore_Bool = "allowTimingStore";
+ static const char * const k_pch_Perf_SaveTimingsOnExit_Bool = "saveTimingsOnExit";
+ static const char * const k_pch_Perf_TestData_Float = "perfTestData";
+ static const char * const k_pch_Perf_GPUProfiling_Bool = "GPUProfiling";
+
+ //-----------------------------------------------------------------------------
+ // collision bounds keys
+ static const char * const k_pch_CollisionBounds_Section = "collisionBounds";
+ static const char * const k_pch_CollisionBounds_Style_Int32 = "CollisionBoundsStyle";
+ static const char * const k_pch_CollisionBounds_GroundPerimeterOn_Bool = "CollisionBoundsGroundPerimeterOn";
+ static const char * const k_pch_CollisionBounds_CenterMarkerOn_Bool = "CollisionBoundsCenterMarkerOn";
+ static const char * const k_pch_CollisionBounds_PlaySpaceOn_Bool = "CollisionBoundsPlaySpaceOn";
+ static const char * const k_pch_CollisionBounds_FadeDistance_Float = "CollisionBoundsFadeDistance";
+ static const char * const k_pch_CollisionBounds_WallHeight_Float = "CollisionBoundsWallHeight";
+ static const char * const k_pch_CollisionBounds_ColorGammaR_Int32 = "CollisionBoundsColorGammaR";
+ static const char * const k_pch_CollisionBounds_ColorGammaG_Int32 = "CollisionBoundsColorGammaG";
+ static const char * const k_pch_CollisionBounds_ColorGammaB_Int32 = "CollisionBoundsColorGammaB";
+ static const char * const k_pch_CollisionBounds_ColorGammaA_Int32 = "CollisionBoundsColorGammaA";
+ static const char * const k_pch_CollisionBounds_EnableDriverImport = "enableDriverBoundsImport";
+
+ //-----------------------------------------------------------------------------
+ // camera keys
+ static const char * const k_pch_Camera_Section = "camera";
+ static const char * const k_pch_Camera_EnableCamera_Bool = "enableCamera";
+ static const char * const k_pch_Camera_EnableCameraInDashboard_Bool = "enableCameraInDashboard";
+ static const char * const k_pch_Camera_EnableCameraForCollisionBounds_Bool = "enableCameraForCollisionBounds";
+ static const char * const k_pch_Camera_EnableCameraForRoomView_Bool = "enableCameraForRoomView";
+ static const char * const k_pch_Camera_BoundsColorGammaR_Int32 = "cameraBoundsColorGammaR";
+ static const char * const k_pch_Camera_BoundsColorGammaG_Int32 = "cameraBoundsColorGammaG";
+ static const char * const k_pch_Camera_BoundsColorGammaB_Int32 = "cameraBoundsColorGammaB";
+ static const char * const k_pch_Camera_BoundsColorGammaA_Int32 = "cameraBoundsColorGammaA";
+ static const char * const k_pch_Camera_BoundsStrength_Int32 = "cameraBoundsStrength";
+ static const char * const k_pch_Camera_RoomViewMode_Int32 = "cameraRoomViewMode";
+
+ //-----------------------------------------------------------------------------
+ // audio keys
+ static const char * const k_pch_audio_Section = "audio";
+ static const char * const k_pch_audio_SetOsDefaultPlaybackDevice_Bool = "setOsDefaultPlaybackDevice";
+ static const char * const k_pch_audio_EnablePlaybackDeviceOverride_Bool = "enablePlaybackDeviceOverride";
+ static const char * const k_pch_audio_PlaybackDeviceOverride_String = "playbackDeviceOverride";
+ static const char * const k_pch_audio_PlaybackDeviceOverrideName_String = "playbackDeviceOverrideName";
+ static const char * const k_pch_audio_SetOsDefaultRecordingDevice_Bool = "setOsDefaultRecordingDevice";
+ static const char * const k_pch_audio_EnableRecordingDeviceOverride_Bool = "enableRecordingDeviceOverride";
+ static const char * const k_pch_audio_RecordingDeviceOverride_String = "recordingDeviceOverride";
+ static const char * const k_pch_audio_RecordingDeviceOverrideName_String = "recordingDeviceOverrideName";
+ static const char * const k_pch_audio_EnablePlaybackMirror_Bool = "enablePlaybackMirror";
+ static const char * const k_pch_audio_PlaybackMirrorDevice_String = "playbackMirrorDevice";
+ static const char * const k_pch_audio_PlaybackMirrorDeviceName_String = "playbackMirrorDeviceName";
+ static const char * const k_pch_audio_OldPlaybackMirrorDevice_String = "onPlaybackMirrorDevice";
+ static const char * const k_pch_audio_ActiveMirrorDevice_String = "activePlaybackMirrorDevice";
+ static const char * const k_pch_audio_EnablePlaybackMirrorIndependentVolume_Bool = "enablePlaybackMirrorIndependentVolume";
+ static const char * const k_pch_audio_LastHmdPlaybackDeviceId_String = "lastHmdPlaybackDeviceId";
+ static const char * const k_pch_audio_VIVEHDMIGain = "viveHDMIGain";
+
+ //-----------------------------------------------------------------------------
+ // power management keys
+ static const char * const k_pch_Power_Section = "power";
+ static const char * const k_pch_Power_PowerOffOnExit_Bool = "powerOffOnExit";
+ static const char * const k_pch_Power_TurnOffScreensTimeout_Float = "turnOffScreensTimeout";
+ static const char * const k_pch_Power_TurnOffControllersTimeout_Float = "turnOffControllersTimeout";
+ static const char * const k_pch_Power_ReturnToWatchdogTimeout_Float = "returnToWatchdogTimeout";
+ static const char * const k_pch_Power_AutoLaunchSteamVROnButtonPress = "autoLaunchSteamVROnButtonPress";
+ static const char * const k_pch_Power_PauseCompositorOnStandby_Bool = "pauseCompositorOnStandby";
+
+ //-----------------------------------------------------------------------------
+ // dashboard keys
+ static const char * const k_pch_Dashboard_Section = "dashboard";
+ static const char * const k_pch_Dashboard_EnableDashboard_Bool = "enableDashboard";
+ static const char * const k_pch_Dashboard_ArcadeMode_Bool = "arcadeMode";
+ static const char * const k_pch_Dashboard_Position = "position";
+ static const char * const k_pch_Dashboard_DesktopScale = "desktopScale";
+ static const char * const k_pch_Dashboard_DashboardScale = "dashboardScale";
+
+ //-----------------------------------------------------------------------------
+ // model skin keys
+ static const char * const k_pch_modelskin_Section = "modelskins";
+
+ //-----------------------------------------------------------------------------
+ // driver keys - These could be checked in any driver_<name> section
+ static const char * const k_pch_Driver_Enable_Bool = "enable";
+ static const char * const k_pch_Driver_BlockedBySafemode_Bool = "blocked_by_safe_mode";
+ static const char * const k_pch_Driver_LoadPriority_Int32 = "loadPriority";
+
+ //-----------------------------------------------------------------------------
+ // web interface keys
+ static const char* const k_pch_WebInterface_Section = "WebInterface";
+
+ //-----------------------------------------------------------------------------
+ // vrwebhelper keys
+ static const char* const k_pch_VRWebHelper_Section = "VRWebHelper";
+ static const char* const k_pch_VRWebHelper_DebuggerEnabled_Bool = "DebuggerEnabled";
+ static const char* const k_pch_VRWebHelper_DebuggerPort_Int32 = "DebuggerPort";
+
+ //-----------------------------------------------------------------------------
+ // tracking overrides - keys are device paths, values are the device paths their
+ // tracking/pose information overrides
+ static const char* const k_pch_TrackingOverride_Section = "TrackingOverrides";
+
+ //-----------------------------------------------------------------------------
+ // per-app keys - the section name for these is the app key itself. Some of these are prefixed by the controller type
+ static const char* const k_pch_App_BindingAutosaveURLSuffix_String = "AutosaveURL";
+ static const char* const k_pch_App_BindingLegacyAPISuffix_String = "_legacy";
+ static const char* const k_pch_App_BindingSteamVRInputAPISuffix_String = "_steamvrinput";
+ static const char* const k_pch_App_BindingCurrentURLSuffix_String = "CurrentURL";
+ static const char* const k_pch_App_BindingPreviousURLSuffix_String = "PreviousURL";
+ static const char* const k_pch_App_NeedToUpdateAutosaveSuffix_Bool = "NeedToUpdateAutosave";
+ static const char* const k_pch_App_DominantHand_Int32 = "DominantHand";
+
+ //-----------------------------------------------------------------------------
+ // configuration for trackers
+ static const char * const k_pch_Trackers_Section = "trackers";
+
+ //-----------------------------------------------------------------------------
+ // configuration for desktop UI windows
+ static const char * const k_pch_DesktopUI_Section = "DesktopUI";
+
+ //-----------------------------------------------------------------------------
+ // Last known keys for righting recovery
+ static const char * const k_pch_LastKnown_Section = "LastKnown";
+ static const char* const k_pch_LastKnown_HMDManufacturer_String = "HMDManufacturer";
+ static const char* const k_pch_LastKnown_HMDModel_String = "HMDModel";
+
+ //-----------------------------------------------------------------------------
+ // Dismissed warnings
+ static const char * const k_pch_DismissedWarnings_Section = "DismissedWarnings";
+
+ //-----------------------------------------------------------------------------
+ // Input Settings
+ static const char * const k_pch_Input_Section = "input";
+ static const char* const k_pch_Input_LeftThumbstickRotation_Float = "leftThumbstickRotation";
+ static const char* const k_pch_Input_RightThumbstickRotation_Float = "rightThumbstickRotation";
+ static const char* const k_pch_Input_ThumbstickDeadzone_Float = "thumbstickDeadzone";
+
+ //-----------------------------------------------------------------------------
+ // Log of GPU performance
+ static const char * const k_pch_GpuSpeed_Section = "GpuSpeed";
+
+} // namespace vr
+
+// ivrchaperone.h
+namespace vr
+{
+
+#pragma pack( push, 8 )
+
+enum ChaperoneCalibrationState
+{
+ // OK!
+ ChaperoneCalibrationState_OK = 1, // Chaperone is fully calibrated and working correctly
+
+ // Warnings
+ ChaperoneCalibrationState_Warning = 100,
+ ChaperoneCalibrationState_Warning_BaseStationMayHaveMoved = 101, // A base station thinks that it might have moved
+ ChaperoneCalibrationState_Warning_BaseStationRemoved = 102, // There are less base stations than when calibrated
+ ChaperoneCalibrationState_Warning_SeatedBoundsInvalid = 103, // Seated bounds haven't been calibrated for the current tracking center
+
+ // Errors
+ ChaperoneCalibrationState_Error = 200, // The UniverseID is invalid
+ ChaperoneCalibrationState_Error_BaseStationUninitialized = 201, // Tracking center hasn't be calibrated for at least one of the base stations
+ ChaperoneCalibrationState_Error_BaseStationConflict = 202, // Tracking center is calibrated, but base stations disagree on the tracking space
+ ChaperoneCalibrationState_Error_PlayAreaInvalid = 203, // Play Area hasn't been calibrated for the current tracking center
+ ChaperoneCalibrationState_Error_CollisionBoundsInvalid = 204, // Collision Bounds haven't been calibrated for the current tracking center
+};
+
+
+/** HIGH LEVEL TRACKING SPACE ASSUMPTIONS:
+* 0,0,0 is the preferred standing area center.
+* 0Y is the floor height.
+* -Z is the preferred forward facing direction. */
+class IVRChaperone
+{
+public:
+
+ /** Get the current state of Chaperone calibration. This state can change at any time during a session due to physical base station changes. **/
+ virtual ChaperoneCalibrationState GetCalibrationState() = 0;
+
+ /** Returns the width and depth of the Play Area (formerly named Soft Bounds) in X and Z.
+ * Tracking space center (0,0,0) is the center of the Play Area. **/
+ virtual bool GetPlayAreaSize( float *pSizeX, float *pSizeZ ) = 0;
+
+ /** Returns the 4 corner positions of the Play Area (formerly named Soft Bounds).
+ * Corners are in counter-clockwise order.
+ * Standing center (0,0,0) is the center of the Play Area.
+ * It's a rectangle.
+ * 2 sides are parallel to the X axis and 2 sides are parallel to the Z axis.
+ * Height of every corner is 0Y (on the floor). **/
+ virtual bool GetPlayAreaRect( HmdQuad_t *rect ) = 0;
+
+ /** Reload Chaperone data from the .vrchap file on disk. */
+ virtual void ReloadInfo( void ) = 0;
+
+ /** Optionally give the chaperone system a hit about the color and brightness in the scene **/
+ virtual void SetSceneColor( HmdColor_t color ) = 0;
+
+ /** Get the current chaperone bounds draw color and brightness **/
+ virtual void GetBoundsColor( HmdColor_t *pOutputColorArray, int nNumOutputColors, float flCollisionBoundsFadeDistance, HmdColor_t *pOutputCameraColor ) = 0;
+
+ /** Determine whether the bounds are showing right now **/
+ virtual bool AreBoundsVisible() = 0;
+
+ /** Force the bounds to show, mostly for utilities **/
+ virtual void ForceBoundsVisible( bool bForce ) = 0;
+};
+
+static const char * const IVRChaperone_Version = "IVRChaperone_003";
+
+#pragma pack( pop )
+
+}
+
+// ivrchaperonesetup.h
+namespace vr
+{
+
+enum EChaperoneConfigFile
+{
+ EChaperoneConfigFile_Live = 1, // The live chaperone config, used by most applications and games
+ EChaperoneConfigFile_Temp = 2, // The temporary chaperone config, used to live-preview collision bounds in room setup
+};
+
+enum EChaperoneImportFlags
+{
+ EChaperoneImport_BoundsOnly = 0x0001,
+};
+
+/** Manages the working copy of the chaperone info. By default this will be the same as the
+* live copy. Any changes made with this interface will stay in the working copy until
+* CommitWorkingCopy() is called, at which point the working copy and the live copy will be
+* the same again. */
+class IVRChaperoneSetup
+{
+public:
+
+ /** Saves the current working copy to disk */
+ virtual bool CommitWorkingCopy( EChaperoneConfigFile configFile ) = 0;
+
+ /** Reverts the working copy to match the live chaperone calibration.
+ * To modify existing data this MUST be do WHILE getting a non-error ChaperoneCalibrationStatus.
+ * Only after this should you do gets and sets on the existing data. */
+ virtual void RevertWorkingCopy() = 0;
+
+ /** Returns the width and depth of the Play Area (formerly named Soft Bounds) in X and Z from the working copy.
+ * Tracking space center (0,0,0) is the center of the Play Area. */
+ virtual bool GetWorkingPlayAreaSize( float *pSizeX, float *pSizeZ ) = 0;
+
+ /** Returns the 4 corner positions of the Play Area (formerly named Soft Bounds) from the working copy.
+ * Corners are in clockwise order.
+ * Tracking space center (0,0,0) is the center of the Play Area.
+ * It's a rectangle.
+ * 2 sides are parallel to the X axis and 2 sides are parallel to the Z axis.
+ * Height of every corner is 0Y (on the floor). **/
+ virtual bool GetWorkingPlayAreaRect( HmdQuad_t *rect ) = 0;
+
+ /** Returns the number of Quads if the buffer points to null. Otherwise it returns Quads
+ * into the buffer up to the max specified from the working copy. */
+ virtual bool GetWorkingCollisionBoundsInfo( VR_OUT_ARRAY_COUNT(punQuadsCount) HmdQuad_t *pQuadsBuffer, uint32_t* punQuadsCount ) = 0;
+
+ /** Returns the number of Quads if the buffer points to null. Otherwise it returns Quads
+ * into the buffer up to the max specified. */
+ virtual bool GetLiveCollisionBoundsInfo( VR_OUT_ARRAY_COUNT(punQuadsCount) HmdQuad_t *pQuadsBuffer, uint32_t* punQuadsCount ) = 0;
+
+ /** Returns the preferred seated position from the working copy. */
+ virtual bool GetWorkingSeatedZeroPoseToRawTrackingPose( HmdMatrix34_t *pmatSeatedZeroPoseToRawTrackingPose ) = 0;
+
+ /** Returns the standing origin from the working copy. */
+ virtual bool GetWorkingStandingZeroPoseToRawTrackingPose( HmdMatrix34_t *pmatStandingZeroPoseToRawTrackingPose ) = 0;
+
+ /** Sets the Play Area in the working copy. */
+ virtual void SetWorkingPlayAreaSize( float sizeX, float sizeZ ) = 0;
+
+ /** Sets the Collision Bounds in the working copy. Note: ceiling height is ignored. */
+ virtual void SetWorkingCollisionBoundsInfo( VR_ARRAY_COUNT(unQuadsCount) HmdQuad_t *pQuadsBuffer, uint32_t unQuadsCount ) = 0;
+
+ /** Sets the Collision Bounds in the working copy. */
+ virtual void SetWorkingPerimeter( VR_ARRAY_COUNT( unPointCount ) HmdVector2_t *pPointBuffer, uint32_t unPointCount ) = 0;
+
+ /** Sets the preferred seated position in the working copy. */
+ virtual void SetWorkingSeatedZeroPoseToRawTrackingPose( const HmdMatrix34_t *pMatSeatedZeroPoseToRawTrackingPose ) = 0;
+
+ /** Sets the preferred standing position in the working copy. */
+ virtual void SetWorkingStandingZeroPoseToRawTrackingPose( const HmdMatrix34_t *pMatStandingZeroPoseToRawTrackingPose ) = 0;
+
+ /** Tear everything down and reload it from the file on disk */
+ virtual void ReloadFromDisk( EChaperoneConfigFile configFile ) = 0;
+
+ /** Returns the preferred seated position. */
+ virtual bool GetLiveSeatedZeroPoseToRawTrackingPose( HmdMatrix34_t *pmatSeatedZeroPoseToRawTrackingPose ) = 0;
+
+ virtual bool ExportLiveToBuffer( VR_OUT_STRING() char *pBuffer, uint32_t *pnBufferLength ) = 0;
+ virtual bool ImportFromBufferToWorking( const char *pBuffer, uint32_t nImportFlags ) = 0;
+
+ /** Shows the chaperone data in the working set to preview in the compositor.*/
+ virtual void ShowWorkingSetPreview() = 0;
+
+ /** Hides the chaperone data in the working set to preview in the compositor (if it was visible).*/
+ virtual void HideWorkingSetPreview() = 0;
+
+ /** Fire an event that the tracking system can use to know room setup is about to begin. This lets the tracking
+ * system make any last minute adjustments that should be incorporated into the new setup. If the user is adjusting
+ * live in HMD using a tweak tool, keep in mind that calling this might cause the user to see the room jump. */
+ virtual void RoomSetupStarting() = 0;
+};
+
+static const char * const IVRChaperoneSetup_Version = "IVRChaperoneSetup_006";
+
+
+}
+
+// ivrcompositor.h
+namespace vr
+{
+
+#pragma pack( push, 8 )
+
+/** Errors that can occur with the VR compositor */
+enum EVRCompositorError
+{
+ VRCompositorError_None = 0,
+ VRCompositorError_RequestFailed = 1,
+ VRCompositorError_IncompatibleVersion = 100,
+ VRCompositorError_DoNotHaveFocus = 101,
+ VRCompositorError_InvalidTexture = 102,
+ VRCompositorError_IsNotSceneApplication = 103,
+ VRCompositorError_TextureIsOnWrongDevice = 104,
+ VRCompositorError_TextureUsesUnsupportedFormat = 105,
+ VRCompositorError_SharedTexturesNotSupported = 106,
+ VRCompositorError_IndexOutOfRange = 107,
+ VRCompositorError_AlreadySubmitted = 108,
+ VRCompositorError_InvalidBounds = 109,
+ VRCompositorError_AlreadySet = 110,
+};
+
+/** Timing mode passed to SetExplicitTimingMode(); see that function for documentation */
+enum EVRCompositorTimingMode
+{
+ VRCompositorTimingMode_Implicit = 0,
+ VRCompositorTimingMode_Explicit_RuntimePerformsPostPresentHandoff = 1,
+ VRCompositorTimingMode_Explicit_ApplicationPerformsPostPresentHandoff = 2,
+};
+
+/** Cumulative stats for current application. These are not cleared until a new app connects,
+* but they do stop accumulating once the associated app disconnects. */
+struct Compositor_CumulativeStats
+{
+ uint32_t m_nPid; // Process id associated with these stats (may no longer be running).
+ uint32_t m_nNumFramePresents; // total number of times we called present (includes reprojected frames)
+ uint32_t m_nNumDroppedFrames; // total number of times an old frame was re-scanned out (without reprojection)
+ uint32_t m_nNumReprojectedFrames; // total number of times a frame was scanned out a second time (with reprojection)
+
+ /** Values recorded at startup before application has fully faded in the first time. */
+ uint32_t m_nNumFramePresentsOnStartup;
+ uint32_t m_nNumDroppedFramesOnStartup;
+ uint32_t m_nNumReprojectedFramesOnStartup;
+
+ /** Applications may explicitly fade to the compositor. This is usually to handle level transitions, and loading often causes
+ * system wide hitches. The following stats are collected during this period. Does not include values recorded during startup. */
+ uint32_t m_nNumLoading;
+ uint32_t m_nNumFramePresentsLoading;
+ uint32_t m_nNumDroppedFramesLoading;
+ uint32_t m_nNumReprojectedFramesLoading;
+
+ /** If we don't get a new frame from the app in less than 2.5 frames, then we assume the app has hung and start
+ * fading back to the compositor. The following stats are a result of this, and are a subset of those recorded above.
+ * Does not include values recorded during start up or loading. */
+ uint32_t m_nNumTimedOut;
+ uint32_t m_nNumFramePresentsTimedOut;
+ uint32_t m_nNumDroppedFramesTimedOut;
+ uint32_t m_nNumReprojectedFramesTimedOut;
+};
+
+struct Compositor_StageRenderSettings
+{
+ /** Primary color is applied as a tint to (i.e. multiplied with) the model's texture */
+ HmdColor_t m_PrimaryColor;
+ HmdColor_t m_SecondaryColor;
+
+ /** Vignette radius is in meters and is used to fade to the specified secondary solid color over
+ * that 3D distance from the origin of the playspace. */
+ float m_flVignetteInnerRadius;
+ float m_flVignetteOuterRadius;
+
+ /** Fades to the secondary color based on view incidence. This variable controls the linearity
+ * of the effect. It is mutually exclusive with vignette. Additionally, it treats the mesh as faceted. */
+ float m_flFresnelStrength;
+
+ /** Controls backface culling. */
+ bool m_bBackfaceCulling;
+
+ /** Converts the render model's texture to luma and applies to rgb equally. This is useful to
+ * combat compression artifacts that can occur on desaturated source material. */
+ bool m_bGreyscale;
+
+ /** Renders mesh as a wireframe. */
+ bool m_bWireframe;
+};
+
+static inline Compositor_StageRenderSettings DefaultStageRenderSettings()
+{
+ Compositor_StageRenderSettings settings;
+ settings.m_PrimaryColor.r = 1.0f;
+ settings.m_PrimaryColor.g = 1.0f;
+ settings.m_PrimaryColor.b = 1.0f;
+ settings.m_PrimaryColor.a = 1.0f;
+ settings.m_SecondaryColor.r = 1.0f;
+ settings.m_SecondaryColor.g = 1.0f;
+ settings.m_SecondaryColor.b = 1.0f;
+ settings.m_SecondaryColor.a = 1.0f;
+ settings.m_flVignetteInnerRadius = 0.0f;
+ settings.m_flVignetteOuterRadius = 0.0f;
+ settings.m_flFresnelStrength = 0.0f;
+ settings.m_bBackfaceCulling = false;
+ settings.m_bGreyscale = false;
+ settings.m_bWireframe = false;
+ return settings;
+}
+
+#pragma pack( pop )
+
+/** Allows the application to interact with the compositor */
+class IVRCompositor
+{
+public:
+ /** Sets tracking space returned by WaitGetPoses */
+ virtual void SetTrackingSpace( ETrackingUniverseOrigin eOrigin ) = 0;
+
+ /** Gets current tracking space returned by WaitGetPoses */
+ virtual ETrackingUniverseOrigin GetTrackingSpace() = 0;
+
+ /** Scene applications should call this function to get poses to render with (and optionally poses predicted an additional frame out to use for gameplay).
+ * This function will block until "running start" milliseconds before the start of the frame, and should be called at the last moment before needing to
+ * start rendering.
+ *
+ * Return codes:
+ * - IsNotSceneApplication (make sure to call VR_Init with VRApplicaiton_Scene)
+ * - DoNotHaveFocus (some other app has taken focus - this will throttle the call to 10hz to reduce the impact on that app)
+ */
+ virtual EVRCompositorError WaitGetPoses( VR_ARRAY_COUNT( unRenderPoseArrayCount ) TrackedDevicePose_t* pRenderPoseArray, uint32_t unRenderPoseArrayCount,
+ VR_ARRAY_COUNT( unGamePoseArrayCount ) TrackedDevicePose_t* pGamePoseArray, uint32_t unGamePoseArrayCount ) = 0;
+
+ /** Get the last set of poses returned by WaitGetPoses. */
+ virtual EVRCompositorError GetLastPoses( VR_ARRAY_COUNT( unRenderPoseArrayCount ) TrackedDevicePose_t* pRenderPoseArray, uint32_t unRenderPoseArrayCount,
+ VR_ARRAY_COUNT( unGamePoseArrayCount ) TrackedDevicePose_t* pGamePoseArray, uint32_t unGamePoseArrayCount ) = 0;
+
+ /** Interface for accessing last set of poses returned by WaitGetPoses one at a time.
+ * Returns VRCompositorError_IndexOutOfRange if unDeviceIndex not less than k_unMaxTrackedDeviceCount otherwise VRCompositorError_None.
+ * It is okay to pass NULL for either pose if you only want one of the values. */
+ virtual EVRCompositorError GetLastPoseForTrackedDeviceIndex( TrackedDeviceIndex_t unDeviceIndex, TrackedDevicePose_t *pOutputPose, TrackedDevicePose_t *pOutputGamePose ) = 0;
+
+ /** Updated scene texture to display. If pBounds is NULL the entire texture will be used. If called from an OpenGL app, consider adding a glFlush after
+ * Submitting both frames to signal the driver to start processing, otherwise it may wait until the command buffer fills up, causing the app to miss frames.
+ *
+ * OpenGL dirty state:
+ * glBindTexture
+ *
+ * Return codes:
+ * - IsNotSceneApplication (make sure to call VR_Init with VRApplicaiton_Scene)
+ * - DoNotHaveFocus (some other app has taken focus)
+ * - TextureIsOnWrongDevice (application did not use proper AdapterIndex - see IVRSystem.GetDXGIOutputInfo)
+ * - SharedTexturesNotSupported (application needs to call CreateDXGIFactory1 or later before creating DX device)
+ * - TextureUsesUnsupportedFormat (scene textures must be compatible with DXGI sharing rules - e.g. uncompressed, no mips, etc.)
+ * - InvalidTexture (usually means bad arguments passed in)
+ * - AlreadySubmitted (app has submitted two left textures or two right textures in a single frame - i.e. before calling WaitGetPoses again)
+ */
+ virtual EVRCompositorError Submit( EVREye eEye, const Texture_t *pTexture, const VRTextureBounds_t* pBounds = 0, EVRSubmitFlags nSubmitFlags = Submit_Default ) = 0;
+
+ /** Clears the frame that was sent with the last call to Submit. This will cause the
+ * compositor to show the grid until Submit is called again. */
+ virtual void ClearLastSubmittedFrame() = 0;
+
+ /** Call immediately after presenting your app's window (i.e. companion window) to unblock the compositor.
+ * This is an optional call, which only needs to be used if you can't instead call WaitGetPoses immediately after Present.
+ * For example, if your engine's render and game loop are not on separate threads, or blocking the render thread until 3ms before the next vsync would
+ * introduce a deadlock of some sort. This function tells the compositor that you have finished all rendering after having Submitted buffers for both
+ * eyes, and it is free to start its rendering work. This should only be called from the same thread you are rendering on. */
+ virtual void PostPresentHandoff() = 0;
+
+ /** Returns true if timing data is filled it. Sets oldest timing info if nFramesAgo is larger than the stored history.
+ * Be sure to set timing.size = sizeof(Compositor_FrameTiming) on struct passed in before calling this function. */
+ virtual bool GetFrameTiming( Compositor_FrameTiming *pTiming, uint32_t unFramesAgo = 0 ) = 0;
+
+ /** Interface for copying a range of timing data. Frames are returned in ascending order (oldest to newest) with the last being the most recent frame.
+ * Only the first entry's m_nSize needs to be set, as the rest will be inferred from that. Returns total number of entries filled out. */
+ virtual uint32_t GetFrameTimings( VR_ARRAY_COUNT( nFrames ) Compositor_FrameTiming *pTiming, uint32_t nFrames ) = 0;
+
+ /** Returns the time in seconds left in the current (as identified by FrameTiming's frameIndex) frame.
+ * Due to "running start", this value may roll over to the next frame before ever reaching 0.0. */
+ virtual float GetFrameTimeRemaining() = 0;
+
+ /** Fills out stats accumulated for the last connected application. Pass in sizeof( Compositor_CumulativeStats ) as second parameter. */
+ virtual void GetCumulativeStats( Compositor_CumulativeStats *pStats, uint32_t nStatsSizeInBytes ) = 0;
+
+ /** Fades the view on the HMD to the specified color. The fade will take fSeconds, and the color values are between
+ * 0.0 and 1.0. This color is faded on top of the scene based on the alpha parameter. Removing the fade color instantly
+ * would be FadeToColor( 0.0, 0.0, 0.0, 0.0, 0.0 ). Values are in un-premultiplied alpha space. */
+ virtual void FadeToColor( float fSeconds, float fRed, float fGreen, float fBlue, float fAlpha, bool bBackground = false ) = 0;
+
+ /** Get current fade color value. */
+ virtual HmdColor_t GetCurrentFadeColor( bool bBackground = false ) = 0;
+
+ /** Fading the Grid in or out in fSeconds */
+ virtual void FadeGrid( float fSeconds, bool bFadeIn ) = 0;
+
+ /** Get current alpha value of grid. */
+ virtual float GetCurrentGridAlpha() = 0;
+
+ /** Override the skybox used in the compositor (e.g. for during level loads when the app can't feed scene images fast enough)
+ * Order is Front, Back, Left, Right, Top, Bottom. If only a single texture is passed, it is assumed in lat-long format.
+ * If two are passed, it is assumed a lat-long stereo pair. */
+ virtual EVRCompositorError SetSkyboxOverride( VR_ARRAY_COUNT( unTextureCount ) const Texture_t *pTextures, uint32_t unTextureCount ) = 0;
+
+ /** Resets compositor skybox back to defaults. */
+ virtual void ClearSkyboxOverride() = 0;
+
+ /** Brings the compositor window to the front. This is useful for covering any other window that may be on the HMD
+ * and is obscuring the compositor window. */
+ virtual void CompositorBringToFront() = 0;
+
+ /** Pushes the compositor window to the back. This is useful for allowing other applications to draw directly to the HMD. */
+ virtual void CompositorGoToBack() = 0;
+
+ /** Tells the compositor process to clean up and exit. You do not need to call this function at shutdown. Under normal
+ * circumstances the compositor will manage its own life cycle based on what applications are running. */
+ virtual void CompositorQuit() = 0;
+
+ /** Return whether the compositor is fullscreen */
+ virtual bool IsFullscreen() = 0;
+
+ /** Returns the process ID of the process that is currently rendering the scene */
+ virtual uint32_t GetCurrentSceneFocusProcess() = 0;
+
+ /** Returns the process ID of the process that rendered the last frame (or 0 if the compositor itself rendered the frame.)
+ * Returns 0 when fading out from an app and the app's process Id when fading into an app. */
+ virtual uint32_t GetLastFrameRenderer() = 0;
+
+ /** Returns true if the current process has the scene focus */
+ virtual bool CanRenderScene() = 0;
+
+ /** DEPRECATED: Opens the headset view (as either a window or docked widget depending on user's preferences) that displays what the user
+ * sees in the headset. */
+ virtual void ShowMirrorWindow() = 0;
+
+ /** DEPRECATED: Closes the headset view, either as a window or docked widget. */
+ virtual void HideMirrorWindow() = 0;
+
+ /** DEPRECATED: Returns true if the headset view (either as a window or docked widget) is shown. */
+ virtual bool IsMirrorWindowVisible() = 0;
+
+ /** Writes back buffer and stereo left/right pair from the application to a 'screenshots' folder in the SteamVR runtime root. */
+ virtual void CompositorDumpImages() = 0;
+
+ /** Let an app know it should be rendering with low resources. */
+ virtual bool ShouldAppRenderWithLowResources() = 0;
+
+ /** Override interleaved reprojection logic to force on. */
+ virtual void ForceInterleavedReprojectionOn( bool bOverride ) = 0;
+
+ /** Force reconnecting to the compositor process. */
+ virtual void ForceReconnectProcess() = 0;
+
+ /** Temporarily suspends rendering (useful for finer control over scene transitions). */
+ virtual void SuspendRendering( bool bSuspend ) = 0;
+
+ /** Opens a shared D3D11 texture with the undistorted composited image for each eye. Use ReleaseMirrorTextureD3D11 when finished
+ * instead of calling Release on the resource itself. */
+ virtual vr::EVRCompositorError GetMirrorTextureD3D11( vr::EVREye eEye, void *pD3D11DeviceOrResource, void **ppD3D11ShaderResourceView ) = 0;
+ virtual void ReleaseMirrorTextureD3D11( void *pD3D11ShaderResourceView ) = 0;
+
+ /** Access to mirror textures from OpenGL. */
+ virtual vr::EVRCompositorError GetMirrorTextureGL( vr::EVREye eEye, vr::glUInt_t *pglTextureId, vr::glSharedTextureHandle_t *pglSharedTextureHandle ) = 0;
+ virtual bool ReleaseSharedGLTexture( vr::glUInt_t glTextureId, vr::glSharedTextureHandle_t glSharedTextureHandle ) = 0;
+ virtual void LockGLSharedTextureForAccess( vr::glSharedTextureHandle_t glSharedTextureHandle ) = 0;
+ virtual void UnlockGLSharedTextureForAccess( vr::glSharedTextureHandle_t glSharedTextureHandle ) = 0;
+
+ /** [Vulkan Only]
+ * return 0. Otherwise it returns the length of the number of bytes necessary to hold this string including the trailing
+ * null. The string will be a space separated list of-required instance extensions to enable in VkCreateInstance */
+ virtual uint32_t GetVulkanInstanceExtensionsRequired( VR_OUT_STRING() char *pchValue, uint32_t unBufferSize ) = 0;
+
+ /** [Vulkan only]
+ * return 0. Otherwise it returns the length of the number of bytes necessary to hold this string including the trailing
+ * null. The string will be a space separated list of required device extensions to enable in VkCreateDevice */
+ virtual uint32_t GetVulkanDeviceExtensionsRequired( VkPhysicalDevice_T *pPhysicalDevice, VR_OUT_STRING() char *pchValue, uint32_t unBufferSize ) = 0;
+
+ /** [ Vulkan/D3D12 Only ]
+ * There are two purposes for SetExplicitTimingMode:
+ * 1. To get a more accurate GPU timestamp for when the frame begins in Vulkan/D3D12 applications.
+ * 2. (Optional) To avoid having WaitGetPoses access the Vulkan queue so that the queue can be accessed from
+ * another thread while WaitGetPoses is executing.
+ *
+ * More accurate GPU timestamp for the start of the frame is achieved by the application calling
+ * SubmitExplicitTimingData immediately before its first submission to the Vulkan/D3D12 queue.
+ * This is more accurate because normally this GPU timestamp is recorded during WaitGetPoses. In D3D11,
+ * WaitGetPoses queues a GPU timestamp write, but it does not actually get submitted to the GPU until the
+ * application flushes. By using SubmitExplicitTimingData, the timestamp is recorded at the same place for
+ * Vulkan/D3D12 as it is for D3D11, resulting in a more accurate GPU time measurement for the frame.
+ *
+ * Avoiding WaitGetPoses accessing the Vulkan queue can be achieved using SetExplicitTimingMode as well. If this is desired,
+ * the application should set the timing mode to Explicit_ApplicationPerformsPostPresentHandoff and *MUST* call PostPresentHandoff
+ * itself. If these conditions are met, then WaitGetPoses is guaranteed not to access the queue. Note that PostPresentHandoff
+ * and SubmitExplicitTimingData will access the queue, so only WaitGetPoses becomes safe for accessing the queue from another
+ * thread. */
+ virtual void SetExplicitTimingMode( EVRCompositorTimingMode eTimingMode ) = 0;
+
+ /** [ Vulkan/D3D12 Only ]
+ * Submit explicit timing data. When SetExplicitTimingMode is true, this must be called immediately before
+ * the application's first vkQueueSubmit (Vulkan) or ID3D12CommandQueue::ExecuteCommandLists (D3D12) of each frame.
+ * This function will insert a GPU timestamp write just before the application starts its rendering. This function
+ * will perform a vkQueueSubmit on Vulkan so must not be done simultaneously with VkQueue operations on another thread.
+ * Returns VRCompositorError_RequestFailed if SetExplicitTimingMode is not enabled. */
+ virtual EVRCompositorError SubmitExplicitTimingData() = 0;
+
+ /** Indicates whether or not motion smoothing is enabled by the user settings.
+ * If you want to know if motion smoothing actually triggered due to a late frame, check Compositor_FrameTiming
+ * m_nReprojectionFlags & VRCompositor_ReprojectionMotion instead. */
+ virtual bool IsMotionSmoothingEnabled() = 0;
+
+ /** Indicates whether or not motion smoothing is supported by the current hardware. */
+ virtual bool IsMotionSmoothingSupported() = 0;
+
+ /** Indicates whether or not the current scene focus app is currently loading. This is inferred from its use of FadeGrid to
+ * explicitly fade to the compositor to cover up the fact that it cannot render at a sustained full framerate during this time. */
+ virtual bool IsCurrentSceneFocusAppLoading() = 0;
+
+ /** Override the stage model used in the compositor to replace the grid. RenderModelPath is a full path the an OBJ file to load.
+ * This file will be loaded asynchronously from disk and uploaded to the gpu by the runtime. Once ready for rendering, the
+ * VREvent StageOverrideReady will be sent. Use FadeToGrid to reveal. Call ClearStageOverride to free the associated resources when finished. */
+ virtual EVRCompositorError SetStageOverride_Async( const char *pchRenderModelPath, const HmdMatrix34_t *pTransform = 0,
+ const Compositor_StageRenderSettings *pRenderSettings = 0, uint32_t nSizeOfRenderSettings = 0 ) = 0;
+
+ /** Resets the stage to its default user specified setting. */
+ virtual void ClearStageOverride() = 0;
+
+ /** Returns true if pBenchmarkResults is filled it. Sets pBenchmarkResults with the result of the compositor benchmark.
+ * nSizeOfBenchmarkResults should be set to sizeof(Compositor_BenchmarkResults) */
+ virtual bool GetCompositorBenchmarkResults( Compositor_BenchmarkResults *pBenchmarkResults, uint32_t nSizeOfBenchmarkResults ) = 0;
+
+ /** Returns the frame id associated with the poses last returned by WaitGetPoses. Deltas between IDs correspond to number of headset vsync intervals. */
+ virtual EVRCompositorError GetLastPosePredictionIDs( uint32_t *pRenderPosePredictionID, uint32_t *pGamePosePredictionID ) = 0;
+
+ /** Get the most up-to-date predicted (or recorded - up to 100ms old) set of poses for a given frame id. */
+ virtual EVRCompositorError GetPosesForFrame( uint32_t unPosePredictionID, VR_ARRAY_COUNT( unPoseArrayCount ) TrackedDevicePose_t* pPoseArray, uint32_t unPoseArrayCount ) = 0;
+};
+
+static const char * const IVRCompositor_Version = "IVRCompositor_026";
+
+} // namespace vr
+
+
+
+// ivrheadsetview.h
+namespace vr
+{
+ enum HeadsetViewMode_t
+ {
+ HeadsetViewMode_Left = 0,
+ HeadsetViewMode_Right,
+ HeadsetViewMode_Both
+ };
+
+ class IVRHeadsetView
+ {
+ public:
+ /** Sets the resolution in pixels to render the headset view. These values are clamped to k_unHeadsetViewMaxWidth
+ * and k_unHeadsetViewMaxHeight respectively. For cropped views, the rendered output will be fit to aspect ratio
+ * defined by the the specified dimensions. For uncropped views, the caller should use GetHeadsetViewAspectRation
+ * to adjust the requested render size to avoid squashing or stretching, and then apply letterboxing to compensate
+ * when displaying the results. */
+ virtual void SetHeadsetViewSize( uint32_t nWidth, uint32_t nHeight ) = 0;
+
+ /** Gets the current resolution used to render the headset view. */
+ virtual void GetHeadsetViewSize( uint32_t *pnWidth, uint32_t *pnHeight ) = 0;
+
+ /** Set the mode used to render the headset view. */
+ virtual void SetHeadsetViewMode( HeadsetViewMode_t eHeadsetViewMode ) = 0;
+
+ /** Get the current mode used to render the headset view. */
+ virtual HeadsetViewMode_t GetHeadsetViewMode() = 0;
+
+ /** Set whether or not the headset view should be rendered cropped to hide the hidden area mesh or not. */
+ virtual void SetHeadsetViewCropped( bool bCropped ) = 0;
+
+ /** Get the current cropping status of the headset view. */
+ virtual bool GetHeadsetViewCropped() = 0;
+
+ /** Get the aspect ratio (width:height) of the uncropped headset view (accounting for the current set mode). */
+ virtual float GetHeadsetViewAspectRatio() = 0;
+
+ /** Set the range [0..1] that the headset view blends across the stereo overlapped area in cropped both mode. */
+ virtual void SetHeadsetViewBlendRange( float flStartPct, float flEndPct ) = 0;
+
+ /** Get the current range [0..1] that the headset view blends across the stereo overlapped area in cropped both mode. */
+ virtual void GetHeadsetViewBlendRange( float *pStartPct, float *pEndPct ) = 0;
+ };
+
+ static const uint32_t k_unHeadsetViewMaxWidth = 3840;
+ static const uint32_t k_unHeadsetViewMaxHeight = 2160;
+ static const char * const k_pchHeadsetViewOverlayKey = "system.HeadsetView";
+
+ static const char * const IVRHeadsetView_Version = "IVRHeadsetView_001";
+
+ /** Returns the current IVRHeadsetView pointer or NULL the interface could not be found. */
+ VR_INTERFACE vr::IVRHeadsetView *VR_CALLTYPE VRHeadsetView();
+
+} // namespace vr
+
+
+// ivrnotifications.h
+namespace vr
+{
+
+#pragma pack( push, 8 )
+
+// Used for passing graphic data
+struct NotificationBitmap_t
+{
+ NotificationBitmap_t()
+ : m_pImageData( nullptr )
+ , m_nWidth( 0 )
+ , m_nHeight( 0 )
+ , m_nBytesPerPixel( 0 )
+ {
+ }
+
+ void *m_pImageData;
+ int32_t m_nWidth;
+ int32_t m_nHeight;
+ int32_t m_nBytesPerPixel;
+};
+
+
+/** Be aware that the notification type is used as 'priority' to pick the next notification */
+enum EVRNotificationType
+{
+ /** Transient notifications are automatically hidden after a period of time set by the user.
+ * They are used for things like information and chat messages that do not require user interaction. */
+ EVRNotificationType_Transient = 0,
+
+ /** Persistent notifications are shown to the user until they are hidden by calling RemoveNotification().
+ * They are used for things like phone calls and alarms that require user interaction. */
+ EVRNotificationType_Persistent = 1,
+
+ /** System notifications are shown no matter what. It is expected, that the ulUserValue is used as ID.
+ * If there is already a system notification in the queue with that ID it is not accepted into the queue
+ * to prevent spamming with system notification */
+ EVRNotificationType_Transient_SystemWithUserValue = 2,
+};
+
+enum EVRNotificationStyle
+{
+ /** Creates a notification with minimal external styling. */
+ EVRNotificationStyle_None = 0,
+
+ /** Used for notifications about overlay-level status. In Steam this is used for events like downloads completing. */
+ EVRNotificationStyle_Application = 100,
+
+ /** Used for notifications about contacts that are unknown or not available. In Steam this is used for friend invitations and offline friends. */
+ EVRNotificationStyle_Contact_Disabled = 200,
+
+ /** Used for notifications about contacts that are available but inactive. In Steam this is used for friends that are online but not playing a game. */
+ EVRNotificationStyle_Contact_Enabled = 201,
+
+ /** Used for notifications about contacts that are available and active. In Steam this is used for friends that are online and currently running a game. */
+ EVRNotificationStyle_Contact_Active = 202,
+};
+
+static const uint32_t k_unNotificationTextMaxSize = 256;
+
+typedef uint32_t VRNotificationId;
+
+
+
+#pragma pack( pop )
+
+/** Allows notification sources to interact with the VR system
+ This current interface is not yet implemented. Do not use yet. */
+class IVRNotifications
+{
+public:
+ /** Create a notification and enqueue it to be shown to the user.
+ * An overlay handle is required to create a notification, as otherwise it would be impossible for a user to act on it.
+ * To create a two-line notification, use a line break ('\n') to split the text into two lines.
+ * The pImage argument may be NULL, in which case the specified overlay's icon will be used instead. */
+ virtual EVRNotificationError CreateNotification( VROverlayHandle_t ulOverlayHandle, uint64_t ulUserValue, EVRNotificationType type, const char *pchText, EVRNotificationStyle style, const NotificationBitmap_t *pImage, /* out */ VRNotificationId *pNotificationId ) = 0;
+
+ /** Destroy a notification, hiding it first if it currently shown to the user. */
+ virtual EVRNotificationError RemoveNotification( VRNotificationId notificationId ) = 0;
+
+};
+
+static const char * const IVRNotifications_Version = "IVRNotifications_002";
+
+} // namespace vr
+
+
+
+// ivroverlay.h
+namespace vr
+{
+
+ /** The maximum length of an overlay key in bytes, counting the terminating null character. */
+ static const uint32_t k_unVROverlayMaxKeyLength = 128;
+
+ /** The maximum length of an overlay name in bytes, counting the terminating null character. */
+ static const uint32_t k_unVROverlayMaxNameLength = 128;
+
+ /** The maximum number of overlays that can exist in the system at one time. */
+ static const uint32_t k_unMaxOverlayCount = 128;
+
+ /** The maximum number of overlay intersection mask primitives per overlay */
+ static const uint32_t k_unMaxOverlayIntersectionMaskPrimitivesCount = 32;
+
+ /** Types of input supported by VR Overlays */
+ enum VROverlayInputMethod
+ {
+ VROverlayInputMethod_None = 0, // No input events will be generated automatically for this overlay
+ VROverlayInputMethod_Mouse = 1, // Tracked controllers will get mouse events automatically
+ // VROverlayInputMethod_DualAnalog = 2, // No longer supported
+ };
+
+ /** Allows the caller to figure out which overlay transform getter to call. */
+ enum VROverlayTransformType
+ {
+ VROverlayTransform_Invalid = -1,
+ VROverlayTransform_Absolute = 0,
+ VROverlayTransform_TrackedDeviceRelative = 1,
+ VROverlayTransform_SystemOverlay = 2,
+ VROverlayTransform_TrackedComponent = 3,
+ VROverlayTransform_Cursor = 4,
+ VROverlayTransform_DashboardTab = 5,
+ VROverlayTransform_DashboardThumb = 6,
+ VROverlayTransform_Mountable = 7,
+ };
+
+ /** Overlay control settings */
+ enum VROverlayFlags
+ {
+ // Set this flag on a dashboard overlay to prevent a tab from showing up for that overlay
+ VROverlayFlags_NoDashboardTab = 1 << 3,
+
+ // When this is set the overlay will receive VREvent_ScrollDiscrete events like a mouse wheel.
+ // Requires mouse input mode.
+ VROverlayFlags_SendVRDiscreteScrollEvents = 1 << 6,
+
+ // Indicates that the overlay would like to receive
+ VROverlayFlags_SendVRTouchpadEvents = 1 << 7,
+
+ // If set this will render a vertical scroll wheel on the primary controller,
+ // only needed if not using VROverlayFlags_SendVRScrollEvents but you still want to represent a scroll wheel
+ VROverlayFlags_ShowTouchPadScrollWheel = 1 << 8,
+
+ // If this is set ownership and render access to the overlay are transferred
+ // to the new scene process on a call to IVRApplications::LaunchInternalProcess
+ VROverlayFlags_TransferOwnershipToInternalProcess = 1 << 9,
+
+ // If set, renders 50% of the texture in each eye, side by side
+ VROverlayFlags_SideBySide_Parallel = 1 << 10, // Texture is left/right
+ VROverlayFlags_SideBySide_Crossed = 1 << 11, // Texture is crossed and right/left
+
+ VROverlayFlags_Panorama = 1 << 12, // Texture is a panorama
+ VROverlayFlags_StereoPanorama = 1 << 13, // Texture is a stereo panorama
+
+ // If this is set on an overlay owned by the scene application that overlay
+ // will be sorted with the "Other" overlays on top of all other scene overlays
+ VROverlayFlags_SortWithNonSceneOverlays = 1 << 14,
+
+ // If set, the overlay will be shown in the dashboard, otherwise it will be hidden.
+ VROverlayFlags_VisibleInDashboard = 1 << 15,
+
+ // If this is set and the overlay's input method is not none, the system-wide laser mouse
+ // mode will be activated whenever this overlay is visible.
+ VROverlayFlags_MakeOverlaysInteractiveIfVisible = 1 << 16,
+
+ // If this is set the overlay will receive smooth VREvent_ScrollSmooth that emulate trackpad scrolling.
+ // Requires mouse input mode.
+ VROverlayFlags_SendVRSmoothScrollEvents = 1 << 17,
+
+ // If this is set, the overlay texture will be protected content, preventing unauthorized reads.
+ VROverlayFlags_ProtectedContent = 1 << 18,
+
+ // If this is set, the laser mouse splat will not be drawn over this overlay. The overlay will
+ // be responsible for drawing its own "cursor".
+ VROverlayFlags_HideLaserIntersection = 1 << 19,
+
+ // If this is set, clicking away from the overlay will cause it to receive a VREvent_Modal_Cancel event.
+ // This is ignored for dashboard overlays.
+ VROverlayFlags_WantsModalBehavior = 1 << 20,
+
+ // If this is set, alpha composition assumes the texture is pre-multiplied
+ VROverlayFlags_IsPremultiplied = 1 << 21,
+ };
+
+ enum VRMessageOverlayResponse
+ {
+ VRMessageOverlayResponse_ButtonPress_0 = 0,
+ VRMessageOverlayResponse_ButtonPress_1 = 1,
+ VRMessageOverlayResponse_ButtonPress_2 = 2,
+ VRMessageOverlayResponse_ButtonPress_3 = 3,
+ VRMessageOverlayResponse_CouldntFindSystemOverlay = 4,
+ VRMessageOverlayResponse_CouldntFindOrCreateClientOverlay= 5,
+ VRMessageOverlayResponse_ApplicationQuit = 6
+ };
+
+ struct VROverlayIntersectionParams_t
+ {
+ HmdVector3_t vSource;
+ HmdVector3_t vDirection;
+ ETrackingUniverseOrigin eOrigin;
+ };
+
+ struct VROverlayIntersectionResults_t
+ {
+ HmdVector3_t vPoint;
+ HmdVector3_t vNormal;
+ HmdVector2_t vUVs;
+ float fDistance;
+ };
+
+ // Input modes for the Big Picture gamepad text entry
+ enum EGamepadTextInputMode
+ {
+ k_EGamepadTextInputModeNormal = 0,
+ k_EGamepadTextInputModePassword = 1,
+ k_EGamepadTextInputModeSubmit = 2,
+ };
+
+ // Controls number of allowed lines for the Big Picture gamepad text entry
+ enum EGamepadTextInputLineMode
+ {
+ k_EGamepadTextInputLineModeSingleLine = 0,
+ k_EGamepadTextInputLineModeMultipleLines = 1
+ };
+
+ enum EVROverlayIntersectionMaskPrimitiveType
+ {
+ OverlayIntersectionPrimitiveType_Rectangle,
+ OverlayIntersectionPrimitiveType_Circle,
+ };
+
+ struct IntersectionMaskRectangle_t
+ {
+ float m_flTopLeftX;
+ float m_flTopLeftY;
+ float m_flWidth;
+ float m_flHeight;
+ };
+
+ struct IntersectionMaskCircle_t
+ {
+ float m_flCenterX;
+ float m_flCenterY;
+ float m_flRadius;
+ };
+
+ /** NOTE!!! If you change this you MUST manually update openvr_interop.cs.py and openvr_api_flat.h.py */
+ typedef union
+ {
+ IntersectionMaskRectangle_t m_Rectangle;
+ IntersectionMaskCircle_t m_Circle;
+ } VROverlayIntersectionMaskPrimitive_Data_t;
+
+ struct VROverlayIntersectionMaskPrimitive_t
+ {
+ EVROverlayIntersectionMaskPrimitiveType m_nPrimitiveType;
+ VROverlayIntersectionMaskPrimitive_Data_t m_Primitive;
+ };
+
+ enum EKeyboardFlags
+ {
+ KeyboardFlag_Minimal = 1 << 0, // makes the keyboard send key events immediately instead of accumulating a buffer
+ KeyboardFlag_Modal = 2 << 0, // makes the keyboard take all focus and dismiss when clicking off the panel
+ };
+
+
+ class IVROverlay
+ {
+ public:
+
+ // ---------------------------------------------
+ // Overlay management methods
+ // ---------------------------------------------
+
+ /** Finds an existing overlay with the specified key. */
+ virtual EVROverlayError FindOverlay( const char *pchOverlayKey, VROverlayHandle_t * pOverlayHandle ) = 0;
+
+ /** Creates a new named overlay. All overlays start hidden and with default settings. */
+ virtual EVROverlayError CreateOverlay( const char *pchOverlayKey, const char *pchOverlayName, VROverlayHandle_t * pOverlayHandle ) = 0;
+
+ /** Destroys the specified overlay. When an application calls VR_Shutdown all overlays created by that app are
+ * automatically destroyed. */
+ virtual EVROverlayError DestroyOverlay( VROverlayHandle_t ulOverlayHandle ) = 0;
+
+ /** Fills the provided buffer with the string key of the overlay. Returns the size of buffer required to store the key, including
+ * the terminating null character. k_unVROverlayMaxKeyLength will be enough bytes to fit the string. */
+ virtual uint32_t GetOverlayKey( VROverlayHandle_t ulOverlayHandle, VR_OUT_STRING() char *pchValue, uint32_t unBufferSize, EVROverlayError *pError = 0L ) = 0;
+
+ /** Fills the provided buffer with the friendly name of the overlay. Returns the size of buffer required to store the key, including
+ * the terminating null character. k_unVROverlayMaxNameLength will be enough bytes to fit the string. */
+ virtual uint32_t GetOverlayName( VROverlayHandle_t ulOverlayHandle, VR_OUT_STRING() char *pchValue, uint32_t unBufferSize, EVROverlayError *pError = 0L ) = 0;
+
+ /** set the name to use for this overlay */
+ virtual EVROverlayError SetOverlayName( VROverlayHandle_t ulOverlayHandle, const char *pchName ) = 0;
+
+ /** Gets the raw image data from an overlay. Overlay image data is always returned as RGBA data, 4 bytes per pixel. If the buffer is not large enough, width and height
+ * will be set and VROverlayError_ArrayTooSmall is returned. */
+ virtual EVROverlayError GetOverlayImageData( VROverlayHandle_t ulOverlayHandle, void *pvBuffer, uint32_t unBufferSize, uint32_t *punWidth, uint32_t *punHeight ) = 0;
+
+ /** returns a string that corresponds with the specified overlay error. The string will be the name
+ * of the error enum value for all valid error codes */
+ virtual const char *GetOverlayErrorNameFromEnum( EVROverlayError error ) = 0;
+
+ // ---------------------------------------------
+ // Overlay rendering methods
+ // ---------------------------------------------
+
+ /** Sets the pid that is allowed to render to this overlay (the creator pid is always allow to render),
+ * by default this is the pid of the process that made the overlay */
+ virtual EVROverlayError SetOverlayRenderingPid( VROverlayHandle_t ulOverlayHandle, uint32_t unPID ) = 0;
+
+ /** Gets the pid that is allowed to render to this overlay */
+ virtual uint32_t GetOverlayRenderingPid( VROverlayHandle_t ulOverlayHandle ) = 0;
+
+ /** Specify flag setting for a given overlay */
+ virtual EVROverlayError SetOverlayFlag( VROverlayHandle_t ulOverlayHandle, VROverlayFlags eOverlayFlag, bool bEnabled ) = 0;
+
+ /** Sets flag setting for a given overlay */
+ virtual EVROverlayError GetOverlayFlag( VROverlayHandle_t ulOverlayHandle, VROverlayFlags eOverlayFlag, bool *pbEnabled ) = 0;
+
+ /** Gets all the flags for a given overlay */
+ virtual EVROverlayError GetOverlayFlags( VROverlayHandle_t ulOverlayHandle, uint32_t *pFlags ) = 0;
+
+ /** Sets the color tint of the overlay quad. Use 0.0 to 1.0 per channel. */
+ virtual EVROverlayError SetOverlayColor( VROverlayHandle_t ulOverlayHandle, float fRed, float fGreen, float fBlue ) = 0;
+
+ /** Gets the color tint of the overlay quad. */
+ virtual EVROverlayError GetOverlayColor( VROverlayHandle_t ulOverlayHandle, float *pfRed, float *pfGreen, float *pfBlue ) = 0;
+
+ /** Sets the alpha of the overlay quad. Use 1.0 for 100 percent opacity to 0.0 for 0 percent opacity. */
+ virtual EVROverlayError SetOverlayAlpha( VROverlayHandle_t ulOverlayHandle, float fAlpha ) = 0;
+
+ /** Gets the alpha of the overlay quad. By default overlays are rendering at 100 percent alpha (1.0). */
+ virtual EVROverlayError GetOverlayAlpha( VROverlayHandle_t ulOverlayHandle, float *pfAlpha ) = 0;
+
+ /** Sets the aspect ratio of the texels in the overlay. 1.0 means the texels are square. 2.0 means the texels
+ * are twice as wide as they are tall. Defaults to 1.0. */
+ virtual EVROverlayError SetOverlayTexelAspect( VROverlayHandle_t ulOverlayHandle, float fTexelAspect ) = 0;
+
+ /** Gets the aspect ratio of the texels in the overlay. Defaults to 1.0 */
+ virtual EVROverlayError GetOverlayTexelAspect( VROverlayHandle_t ulOverlayHandle, float *pfTexelAspect ) = 0;
+
+ /** Sets the rendering sort order for the overlay. Overlays are rendered this order:
+ * Overlays owned by the scene application
+ * Overlays owned by some other application
+ *
+ * Within a category overlays are rendered lowest sort order to highest sort order. Overlays with the same
+ * sort order are rendered back to front base on distance from the HMD.
+ *
+ * Sort order defaults to 0. */
+ virtual EVROverlayError SetOverlaySortOrder( VROverlayHandle_t ulOverlayHandle, uint32_t unSortOrder ) = 0;
+
+ /** Gets the sort order of the overlay. See SetOverlaySortOrder for how this works. */
+ virtual EVROverlayError GetOverlaySortOrder( VROverlayHandle_t ulOverlayHandle, uint32_t *punSortOrder ) = 0;
+
+ /** Sets the width of the overlay quad in meters. By default overlays are rendered on a quad that is 1 meter across */
+ virtual EVROverlayError SetOverlayWidthInMeters( VROverlayHandle_t ulOverlayHandle, float fWidthInMeters ) = 0;
+
+ /** Returns the width of the overlay quad in meters. By default overlays are rendered on a quad that is 1 meter across */
+ virtual EVROverlayError GetOverlayWidthInMeters( VROverlayHandle_t ulOverlayHandle, float *pfWidthInMeters ) = 0;
+
+ /** Use to draw overlay as a curved surface. Curvature is a percentage from (0..1] where 1 is a fully closed cylinder.
+ * For a specific radius, curvature can be computed as: overlay.width / (2 PI r). */
+ virtual EVROverlayError SetOverlayCurvature( VROverlayHandle_t ulOverlayHandle, float fCurvature ) = 0;
+
+ /** Returns the curvature of the overlay as a percentage from (0..1] where 1 is a fully closed cylinder. */
+ virtual EVROverlayError GetOverlayCurvature( VROverlayHandle_t ulOverlayHandle, float *pfCurvature ) = 0;
+
+ /** Sets the colorspace the overlay texture's data is in. Defaults to 'auto'.
+ * If the texture needs to be resolved, you should call SetOverlayTexture with the appropriate colorspace instead. */
+ virtual EVROverlayError SetOverlayTextureColorSpace( VROverlayHandle_t ulOverlayHandle, EColorSpace eTextureColorSpace ) = 0;
+
+ /** Gets the overlay's current colorspace setting. */
+ virtual EVROverlayError GetOverlayTextureColorSpace( VROverlayHandle_t ulOverlayHandle, EColorSpace *peTextureColorSpace ) = 0;
+
+ /** Sets the part of the texture to use for the overlay. UV Min is the upper left corner and UV Max is the lower right corner. */
+ virtual EVROverlayError SetOverlayTextureBounds( VROverlayHandle_t ulOverlayHandle, const VRTextureBounds_t *pOverlayTextureBounds ) = 0;
+
+ /** Gets the part of the texture to use for the overlay. UV Min is the upper left corner and UV Max is the lower right corner. */
+ virtual EVROverlayError GetOverlayTextureBounds( VROverlayHandle_t ulOverlayHandle, VRTextureBounds_t *pOverlayTextureBounds ) = 0;
+
+ /** Returns the transform type of this overlay. */
+ virtual EVROverlayError GetOverlayTransformType( VROverlayHandle_t ulOverlayHandle, VROverlayTransformType *peTransformType ) = 0;
+
+ /** Sets the transform to absolute tracking origin. */
+ virtual EVROverlayError SetOverlayTransformAbsolute( VROverlayHandle_t ulOverlayHandle, ETrackingUniverseOrigin eTrackingOrigin, const HmdMatrix34_t *pmatTrackingOriginToOverlayTransform ) = 0;
+
+ /** Gets the transform if it is absolute. Returns an error if the transform is some other type. */
+ virtual EVROverlayError GetOverlayTransformAbsolute( VROverlayHandle_t ulOverlayHandle, ETrackingUniverseOrigin *peTrackingOrigin, HmdMatrix34_t *pmatTrackingOriginToOverlayTransform ) = 0;
+
+ /** Sets the transform to relative to the transform of the specified tracked device. */
+ virtual EVROverlayError SetOverlayTransformTrackedDeviceRelative( VROverlayHandle_t ulOverlayHandle, TrackedDeviceIndex_t unTrackedDevice, const HmdMatrix34_t *pmatTrackedDeviceToOverlayTransform ) = 0;
+
+ /** Gets the transform if it is relative to a tracked device. Returns an error if the transform is some other type. */
+ virtual EVROverlayError GetOverlayTransformTrackedDeviceRelative( VROverlayHandle_t ulOverlayHandle, TrackedDeviceIndex_t *punTrackedDevice, HmdMatrix34_t *pmatTrackedDeviceToOverlayTransform ) = 0;
+
+ /** Sets the transform to draw the overlay on a rendermodel component mesh instead of a quad. This will only draw when the system is
+ * drawing the device. Overlays with this transform type cannot receive mouse events. */
+ virtual EVROverlayError SetOverlayTransformTrackedDeviceComponent( VROverlayHandle_t ulOverlayHandle, TrackedDeviceIndex_t unDeviceIndex, const char *pchComponentName ) = 0;
+
+ /** Gets the transform information when the overlay is rendering on a component. */
+ virtual EVROverlayError GetOverlayTransformTrackedDeviceComponent( VROverlayHandle_t ulOverlayHandle, TrackedDeviceIndex_t *punDeviceIndex, VR_OUT_STRING() char *pchComponentName, uint32_t unComponentNameSize ) = 0;
+
+ /** Gets the transform if it is relative to another overlay. Returns an error if the transform is some other type. */
+ virtual vr::EVROverlayError GetOverlayTransformOverlayRelative( VROverlayHandle_t ulOverlayHandle, VROverlayHandle_t *ulOverlayHandleParent, HmdMatrix34_t *pmatParentOverlayToOverlayTransform ) = 0;
+
+ /** Sets the transform to relative to the transform of the specified overlay. This overlays visibility will also track the parents visibility */
+ virtual vr::EVROverlayError SetOverlayTransformOverlayRelative( VROverlayHandle_t ulOverlayHandle, VROverlayHandle_t ulOverlayHandleParent, const HmdMatrix34_t *pmatParentOverlayToOverlayTransform ) = 0;
+
+ /** Sets the hotspot for the specified overlay when that overlay is used as a cursor. These are in texture space with 0,0 in the upper left corner of
+ * the texture and 1,1 in the lower right corner of the texture. */
+ virtual EVROverlayError SetOverlayTransformCursor( VROverlayHandle_t ulCursorOverlayHandle, const HmdVector2_t *pvHotspot ) = 0;
+
+ /** Gets cursor hotspot/transform for the specified overlay */
+ virtual vr::EVROverlayError GetOverlayTransformCursor( VROverlayHandle_t ulOverlayHandle, HmdVector2_t *pvHotspot ) = 0;
+
+ /** Shows the VR overlay. For dashboard overlays, only the Dashboard Manager is allowed to call this. */
+ virtual EVROverlayError ShowOverlay( VROverlayHandle_t ulOverlayHandle ) = 0;
+
+ /** Hides the VR overlay. For dashboard overlays, only the Dashboard Manager is allowed to call this. */
+ virtual EVROverlayError HideOverlay( VROverlayHandle_t ulOverlayHandle ) = 0;
+
+ /** Returns true if the overlay is visible. */
+ virtual bool IsOverlayVisible( VROverlayHandle_t ulOverlayHandle ) = 0;
+
+ /** Get the transform in 3d space associated with a specific 2d point in the overlay's coordinate space (where 0,0 is the lower left). -Z points out of the overlay */
+ virtual EVROverlayError GetTransformForOverlayCoordinates( VROverlayHandle_t ulOverlayHandle, ETrackingUniverseOrigin eTrackingOrigin, HmdVector2_t coordinatesInOverlay, HmdMatrix34_t *pmatTransform ) = 0;
+
+ // ---------------------------------------------
+ // Overlay input methods
+ // ---------------------------------------------
+
+ /** Returns true and fills the event with the next event on the overlay's event queue, if there is one.
+ * If there are no events this method returns false. uncbVREvent should be the size in bytes of the VREvent_t struct */
+ virtual bool PollNextOverlayEvent( VROverlayHandle_t ulOverlayHandle, VREvent_t *pEvent, uint32_t uncbVREvent ) = 0;
+
+ /** Returns the current input settings for the specified overlay. */
+ virtual EVROverlayError GetOverlayInputMethod( VROverlayHandle_t ulOverlayHandle, VROverlayInputMethod *peInputMethod ) = 0;
+
+ /** Sets the input settings for the specified overlay. */
+ virtual EVROverlayError SetOverlayInputMethod( VROverlayHandle_t ulOverlayHandle, VROverlayInputMethod eInputMethod ) = 0;
+
+ /** Gets the mouse scaling factor that is used for mouse events. The actual texture may be a different size, but this is
+ * typically the size of the underlying UI in pixels. */
+ virtual EVROverlayError GetOverlayMouseScale( VROverlayHandle_t ulOverlayHandle, HmdVector2_t *pvecMouseScale ) = 0;
+
+ /** Sets the mouse scaling factor that is used for mouse events. The actual texture may be a different size, but this is
+ * typically the size of the underlying UI in pixels (not in world space). */
+ virtual EVROverlayError SetOverlayMouseScale( VROverlayHandle_t ulOverlayHandle, const HmdVector2_t *pvecMouseScale ) = 0;
+
+ /** Computes the overlay-space pixel coordinates of where the ray intersects the overlay with the
+ * specified settings. Returns false if there is no intersection. */
+ virtual bool ComputeOverlayIntersection( VROverlayHandle_t ulOverlayHandle, const VROverlayIntersectionParams_t *pParams, VROverlayIntersectionResults_t *pResults ) = 0;
+
+ /** Returns true if the specified overlay is the hover target. An overlay is the hover target when it is the last overlay "moused over"
+ * by the virtual mouse pointer */
+ virtual bool IsHoverTargetOverlay( VROverlayHandle_t ulOverlayHandle ) = 0;
+
+ /** Sets a list of primitives to be used for controller ray intersection
+ * typically the size of the underlying UI in pixels (not in world space). */
+ virtual EVROverlayError SetOverlayIntersectionMask( VROverlayHandle_t ulOverlayHandle, VROverlayIntersectionMaskPrimitive_t *pMaskPrimitives, uint32_t unNumMaskPrimitives, uint32_t unPrimitiveSize = sizeof( VROverlayIntersectionMaskPrimitive_t ) ) = 0;
+
+ /** Triggers a haptic event on the laser mouse controller for the specified overlay */
+ virtual EVROverlayError TriggerLaserMouseHapticVibration( VROverlayHandle_t ulOverlayHandle, float fDurationSeconds, float fFrequency, float fAmplitude ) = 0;
+
+ /** Sets the cursor to use for the specified overlay. This will be drawn instead of the generic blob when the laser mouse is pointed at the specified overlay */
+ virtual EVROverlayError SetOverlayCursor( VROverlayHandle_t ulOverlayHandle, VROverlayHandle_t ulCursorHandle ) = 0;
+
+ /** Sets the override cursor position to use for this overlay in overlay mouse coordinates. This position will be used to draw the cursor
+ * instead of whatever the laser mouse cursor position is. */
+ virtual EVROverlayError SetOverlayCursorPositionOverride( VROverlayHandle_t ulOverlayHandle, const HmdVector2_t *pvCursor ) = 0;
+
+ /** Clears the override cursor position for this overlay */
+ virtual EVROverlayError ClearOverlayCursorPositionOverride( VROverlayHandle_t ulOverlayHandle ) = 0;
+
+ // ---------------------------------------------
+ // Overlay texture methods
+ // ---------------------------------------------
+
+ /** Texture to draw for the overlay. This function can only be called by the overlay's creator or renderer process (see SetOverlayRenderingPid) .
+ *
+ * OpenGL dirty state:
+ * glBindTexture
+ */
+ virtual EVROverlayError SetOverlayTexture( VROverlayHandle_t ulOverlayHandle, const Texture_t *pTexture ) = 0;
+
+ /** Use this to tell the overlay system to release the texture set for this overlay. */
+ virtual EVROverlayError ClearOverlayTexture( VROverlayHandle_t ulOverlayHandle ) = 0;
+
+ /** Separate interface for providing the data as a stream of bytes, but there is an upper bound on data
+ * that can be sent. This function can only be called by the overlay's renderer process. */
+ virtual EVROverlayError SetOverlayRaw( VROverlayHandle_t ulOverlayHandle, void *pvBuffer, uint32_t unWidth, uint32_t unHeight, uint32_t unBytesPerPixel ) = 0;
+
+ /** Separate interface for providing the image through a filename: can be png or jpg, and should not be bigger than 1920x1080.
+ * This function can only be called by the overlay's renderer process */
+ virtual EVROverlayError SetOverlayFromFile( VROverlayHandle_t ulOverlayHandle, const char *pchFilePath ) = 0;
+
+ /** Get the native texture handle/device for an overlay you have created.
+ * On windows this handle will be a ID3D11ShaderResourceView with a ID3D11Texture2D bound.
+ *
+ * The texture will always be sized to match the backing texture you supplied in SetOverlayTexture above.
+ *
+ * You MUST call ReleaseNativeOverlayHandle() with pNativeTextureHandle once you are done with this texture.
+ *
+ * pNativeTextureHandle is an OUTPUT, it will be a pointer to a ID3D11ShaderResourceView *.
+ * pNativeTextureRef is an INPUT and should be a ID3D11Resource *. The device used by pNativeTextureRef will be used to bind pNativeTextureHandle.
+ */
+ virtual EVROverlayError GetOverlayTexture( VROverlayHandle_t ulOverlayHandle, void **pNativeTextureHandle, void *pNativeTextureRef, uint32_t *pWidth, uint32_t *pHeight, uint32_t *pNativeFormat, ETextureType *pAPIType, EColorSpace *pColorSpace, VRTextureBounds_t *pTextureBounds ) = 0;
+
+ /** Release the pNativeTextureHandle provided from the GetOverlayTexture call, this allows the system to free the underlying GPU resources for this object,
+ * so only do it once you stop rendering this texture.
+ */
+ virtual EVROverlayError ReleaseNativeOverlayHandle( VROverlayHandle_t ulOverlayHandle, void *pNativeTextureHandle ) = 0;
+
+ /** Get the size of the overlay texture */
+ virtual EVROverlayError GetOverlayTextureSize( VROverlayHandle_t ulOverlayHandle, uint32_t *pWidth, uint32_t *pHeight ) = 0;
+
+ // ----------------------------------------------
+ // Dashboard Overlay Methods
+ // ----------------------------------------------
+
+ /** Creates a dashboard overlay and returns its handle */
+ virtual EVROverlayError CreateDashboardOverlay( const char *pchOverlayKey, const char *pchOverlayFriendlyName, VROverlayHandle_t * pMainHandle, VROverlayHandle_t *pThumbnailHandle ) = 0;
+
+ /** Returns true if the dashboard is visible */
+ virtual bool IsDashboardVisible() = 0;
+
+ /** returns true if the dashboard is visible and the specified overlay is the active system Overlay */
+ virtual bool IsActiveDashboardOverlay( VROverlayHandle_t ulOverlayHandle ) = 0;
+
+ /** Sets the dashboard overlay to only appear when the specified process ID has scene focus */
+ virtual EVROverlayError SetDashboardOverlaySceneProcess( VROverlayHandle_t ulOverlayHandle, uint32_t unProcessId ) = 0;
+
+ /** Gets the process ID that this dashboard overlay requires to have scene focus */
+ virtual EVROverlayError GetDashboardOverlaySceneProcess( VROverlayHandle_t ulOverlayHandle, uint32_t *punProcessId ) = 0;
+
+ /** Shows the dashboard. */
+ virtual void ShowDashboard( const char *pchOverlayToShow ) = 0;
+
+ /** Returns the tracked device that has the laser pointer in the dashboard */
+ virtual vr::TrackedDeviceIndex_t GetPrimaryDashboardDevice() = 0;
+
+ // ---------------------------------------------
+ // Keyboard methods
+ // ---------------------------------------------
+
+ /** Show the virtual keyboard to accept input. In most cases, you should pass KeyboardFlag_Modal to enable modal overlay
+ * behavior on the keyboard itself. See EKeyboardFlags for more. */
+ virtual EVROverlayError ShowKeyboard( EGamepadTextInputMode eInputMode, EGamepadTextInputLineMode eLineInputMode, uint32_t unFlags,
+ const char *pchDescription, uint32_t unCharMax, const char *pchExistingText, uint64_t uUserValue ) = 0;
+
+ /** Show the virtual keyboard to accept input for an overlay. In most cases, you should pass KeyboardFlag_Modal to enable modal
+ * overlay behavior on the keyboard itself. See EKeyboardFlags for more. */
+ virtual EVROverlayError ShowKeyboardForOverlay( VROverlayHandle_t ulOverlayHandle, EGamepadTextInputMode eInputMode,
+ EGamepadTextInputLineMode eLineInputMode, uint32_t unFlags, const char *pchDescription, uint32_t unCharMax,
+ const char *pchExistingText, uint64_t uUserValue ) = 0;
+
+ /** Get the text that was entered into the text input **/
+ virtual uint32_t GetKeyboardText( VR_OUT_STRING() char *pchText, uint32_t cchText ) = 0;
+
+ /** Hide the virtual keyboard **/
+ virtual void HideKeyboard() = 0;
+
+ /** Set the position of the keyboard in world space **/
+ virtual void SetKeyboardTransformAbsolute( ETrackingUniverseOrigin eTrackingOrigin, const HmdMatrix34_t *pmatTrackingOriginToKeyboardTransform ) = 0;
+
+ /** Set the position of the keyboard in overlay space by telling it to avoid a rectangle in the overlay. Rectangle coords have (0,0) in the bottom left **/
+ virtual void SetKeyboardPositionForOverlay( VROverlayHandle_t ulOverlayHandle, HmdRect2_t avoidRect ) = 0;
+
+ // ---------------------------------------------
+ // Message box methods
+ // ---------------------------------------------
+
+ /** Show the message overlay. This will block and return you a result. **/
+ virtual VRMessageOverlayResponse ShowMessageOverlay( const char* pchText, const char* pchCaption, const char* pchButton0Text, const char* pchButton1Text = nullptr, const char* pchButton2Text = nullptr, const char* pchButton3Text = nullptr ) = 0;
+
+ /** If the calling process owns the overlay and it's open, this will close it. **/
+ virtual void CloseMessageOverlay() = 0;
+ };
+
+ static const char * const IVROverlay_Version = "IVROverlay_024";
+
+} // namespace vr
+
+// ivroverlayview.h
+namespace vr
+{
+ struct VROverlayView_t
+ {
+ VROverlayHandle_t overlayHandle;
+ Texture_t texture;
+ VRTextureBounds_t textureBounds;
+ };
+
+ enum EDeviceType
+ {
+ DeviceType_Invalid = -1, // Invalid handle
+ DeviceType_DirectX11 = 0, // Handle is an ID3D11Device
+ DeviceType_Vulkan = 1, // Handle is a pointer to a VRVulkanDevice_t structure
+ };
+
+ struct VRVulkanDevice_t
+ {
+ VkInstance_T *m_pInstance;
+ VkDevice_T *m_pDevice;
+ VkPhysicalDevice_T *m_pPhysicalDevice;
+ VkQueue_T *m_pQueue;
+ uint32_t m_uQueueFamilyIndex;
+ };
+
+ struct VRNativeDevice_t
+ {
+ void *handle; // See EDeviceType definition above
+ EDeviceType eType;
+ };
+
+ class IVROverlayView
+ {
+ public:
+ /** Acquire an OverlayView_t from an overlay handle
+ *
+ * The overlay view can be used to sample the contents directly by a native API. The
+ * contents of the OverlayView_t will remain unchanged through the lifetime of the
+ * OverlayView_t.
+ *
+ * The caller acquires read privileges over the OverlayView_t, but should not
+ * write to it.
+ *
+ * AcquireOverlayView() may be called on the same ulOverlayHandle multiple times to
+ * refresh the image contents. In this case the caller is strongly encouraged to re-use
+ * the same pOverlayView for all re-acquisition calls.
+ *
+ * If the producer has not yet queued an image, AcquireOverlayView will return success,
+ * and the Texture_t will have the expected ETextureType. However, the Texture_t->handle
+ * will be nullptr. Once the producer generates the first overlay frame, Texture_t->handle
+ * will become a valid handle.
+ */
+ virtual EVROverlayError AcquireOverlayView(VROverlayHandle_t ulOverlayHandle, VRNativeDevice_t *pNativeDevice, VROverlayView_t *pOverlayView, uint32_t unOverlayViewSize ) = 0;
+
+ /** Release an acquired OverlayView_t
+ *
+ * Denotes that pOverlayView will no longer require access to the resources it acquired in
+ * all previous calls to AcquireOverlayView().
+ *
+ * All OverlayView_t*'s provided to AcquireOverlayView() as pOverlayViews must be
+ * passed into ReleaseOverlayView() in order for the underlying GPU resources to be freed.
+ */
+ virtual EVROverlayError ReleaseOverlayView(VROverlayView_t *pOverlayView) = 0;
+
+ /** Posts an overlay event */
+ virtual void PostOverlayEvent(VROverlayHandle_t ulOverlayHandle, const VREvent_t *pvrEvent) = 0;
+
+ /** Determines whether this process is permitted to view an overlay's content. */
+ virtual bool IsViewingPermitted( VROverlayHandle_t ulOverlayHandle ) = 0;
+
+ };
+
+ static const char * const IVROverlayView_Version = "IVROverlayView_003";
+
+}
+
+// ivrrendermodels.h
+namespace vr
+{
+
+static const char * const k_pch_Controller_Component_GDC2015 = "gdc2015"; // Canonical coordinate system of the gdc 2015 wired controller, provided for backwards compatibility
+static const char * const k_pch_Controller_Component_Base = "base"; // For controllers with an unambiguous 'base'.
+static const char * const k_pch_Controller_Component_Tip = "tip"; // For controllers with an unambiguous 'tip' (used for 'laser-pointing')
+static const char * const k_pch_Controller_Component_HandGrip = "handgrip"; // Neutral, ambidextrous hand-pose when holding controller. On plane between neutrally posed index finger and thumb
+static const char * const k_pch_Controller_Component_Status = "status"; // 1:1 aspect ratio status area, with canonical [0,1] uv mapping
+
+#pragma pack( push, 8 )
+
+/** Errors that can occur with the VR compositor */
+enum EVRRenderModelError
+{
+ VRRenderModelError_None = 0,
+ VRRenderModelError_Loading = 100,
+ VRRenderModelError_NotSupported = 200,
+ VRRenderModelError_InvalidArg = 300,
+ VRRenderModelError_InvalidModel = 301,
+ VRRenderModelError_NoShapes = 302,
+ VRRenderModelError_MultipleShapes = 303,
+ VRRenderModelError_TooManyVertices = 304,
+ VRRenderModelError_MultipleTextures = 305,
+ VRRenderModelError_BufferTooSmall = 306,
+ VRRenderModelError_NotEnoughNormals = 307,
+ VRRenderModelError_NotEnoughTexCoords = 308,
+
+ VRRenderModelError_InvalidTexture = 400,
+};
+
+enum EVRRenderModelTextureFormat
+{
+ VRRenderModelTextureFormat_RGBA8_SRGB = 0, // RGBA with 8 bits per channel per pixel. Data size is width * height * 4ub
+ VRRenderModelTextureFormat_BC2,
+ VRRenderModelTextureFormat_BC4,
+ VRRenderModelTextureFormat_BC7,
+ VRRenderModelTextureFormat_BC7_SRGB
+};
+
+/** A single vertex in a render model */
+struct RenderModel_Vertex_t
+{
+ HmdVector3_t vPosition; // position in meters in device space
+ HmdVector3_t vNormal;
+ float rfTextureCoord[2];
+};
+
+/** A texture map for use on a render model */
+#if defined(__linux__) || defined(__APPLE__)
+// This structure was originally defined mis-packed on Linux, preserved for
+// compatibility.
+#pragma pack( push, 4 )
+#endif
+
+struct RenderModel_TextureMap_t
+{
+ uint16_t unWidth, unHeight; // width and height of the texture map in pixels
+ const uint8_t *rubTextureMapData; // Map texture data.
+ EVRRenderModelTextureFormat format; // Refer to EVRRenderModelTextureFormat
+};
+#if defined(__linux__) || defined(__APPLE__)
+#pragma pack( pop )
+#endif
+
+/** Session unique texture identifier. Rendermodels which share the same texture will have the same id.
+IDs <0 denote the texture is not present */
+
+typedef int32_t TextureID_t;
+
+const TextureID_t INVALID_TEXTURE_ID = -1;
+
+#if defined(__linux__) || defined(__APPLE__)
+// This structure was originally defined mis-packed on Linux, preserved for
+// compatibility.
+#pragma pack( push, 4 )
+#endif
+
+struct RenderModel_t
+{
+ const RenderModel_Vertex_t *rVertexData; // Vertex data for the mesh
+ uint32_t unVertexCount; // Number of vertices in the vertex data
+ const uint16_t *rIndexData; // Indices into the vertex data for each triangle
+ uint32_t unTriangleCount; // Number of triangles in the mesh. Index count is 3 * TriangleCount
+ TextureID_t diffuseTextureId; // Session unique texture identifier. Rendermodels which share the same texture will have the same id. <0 == texture not present
+};
+#if defined(__linux__) || defined(__APPLE__)
+#pragma pack( pop )
+#endif
+
+
+struct RenderModel_ControllerMode_State_t
+{
+ bool bScrollWheelVisible; // is this controller currently set to be in a scroll wheel mode
+};
+
+#pragma pack( pop )
+
+class IVRRenderModels
+{
+public:
+
+ /** Loads and returns a render model for use in the application. pchRenderModelName should be a render model name
+ * from the Prop_RenderModelName_String property or an absolute path name to a render model on disk.
+ *
+ * The resulting render model is valid until VR_Shutdown() is called or until FreeRenderModel() is called. When the
+ * application is finished with the render model it should call FreeRenderModel() to free the memory associated
+ * with the model.
+ *
+ * The method returns VRRenderModelError_Loading while the render model is still being loaded.
+ * The method returns VRRenderModelError_None once loaded successfully, otherwise will return an error. */
+ virtual EVRRenderModelError LoadRenderModel_Async( const char *pchRenderModelName, RenderModel_t **ppRenderModel ) = 0;
+
+ /** Frees a previously returned render model
+ * It is safe to call this on a null ptr. */
+ virtual void FreeRenderModel( RenderModel_t *pRenderModel ) = 0;
+
+ /** Loads and returns a texture for use in the application. */
+ virtual EVRRenderModelError LoadTexture_Async( TextureID_t textureId, RenderModel_TextureMap_t **ppTexture ) = 0;
+
+ /** Frees a previously returned texture
+ * It is safe to call this on a null ptr. */
+ virtual void FreeTexture( RenderModel_TextureMap_t *pTexture ) = 0;
+
+ /** Creates a D3D11 texture and loads data into it. */
+ virtual EVRRenderModelError LoadTextureD3D11_Async( TextureID_t textureId, void *pD3D11Device, void **ppD3D11Texture2D ) = 0;
+
+ /** Helper function to copy the bits into an existing texture. */
+ virtual EVRRenderModelError LoadIntoTextureD3D11_Async( TextureID_t textureId, void *pDstTexture ) = 0;
+
+ /** Use this to free textures created with LoadTextureD3D11_Async instead of calling Release on them. */
+ virtual void FreeTextureD3D11( void *pD3D11Texture2D ) = 0;
+
+ /** Use this to get the names of available render models. Index does not correlate to a tracked device index, but
+ * is only used for iterating over all available render models. If the index is out of range, this function will return 0.
+ * Otherwise, it will return the size of the buffer required for the name. */
+ virtual uint32_t GetRenderModelName( uint32_t unRenderModelIndex, VR_OUT_STRING() char *pchRenderModelName, uint32_t unRenderModelNameLen ) = 0;
+
+ /** Returns the number of available render models. */
+ virtual uint32_t GetRenderModelCount() = 0;
+
+
+ /** Returns the number of components of the specified render model.
+ * Components are useful when client application wish to draw, label, or otherwise interact with components of tracked objects.
+ * Examples controller components:
+ * renderable things such as triggers, buttons
+ * non-renderable things which include coordinate systems such as 'tip', 'base', a neutral controller agnostic hand-pose
+ * If all controller components are enumerated and rendered, it will be equivalent to drawing the traditional render model
+ * Returns 0 if components not supported, >0 otherwise */
+ virtual uint32_t GetComponentCount( const char *pchRenderModelName ) = 0;
+
+ /** Use this to get the names of available components. Index does not correlate to a tracked device index, but
+ * is only used for iterating over all available components. If the index is out of range, this function will return 0.
+ * Otherwise, it will return the size of the buffer required for the name. */
+ virtual uint32_t GetComponentName( const char *pchRenderModelName, uint32_t unComponentIndex, VR_OUT_STRING( ) char *pchComponentName, uint32_t unComponentNameLen ) = 0;
+
+ /** Get the button mask for all buttons associated with this component
+ * If no buttons (or axes) are associated with this component, return 0
+ * Note: multiple components may be associated with the same button. Ex: two grip buttons on a single controller.
+ * Note: A single component may be associated with multiple buttons. Ex: A trackpad which also provides "D-pad" functionality */
+ virtual uint64_t GetComponentButtonMask( const char *pchRenderModelName, const char *pchComponentName ) = 0;
+
+ /** Use this to get the render model name for the specified rendermode/component combination, to be passed to LoadRenderModel.
+ * If the component name is out of range, this function will return 0.
+ * Otherwise, it will return the size of the buffer required for the name. */
+ virtual uint32_t GetComponentRenderModelName( const char *pchRenderModelName, const char *pchComponentName, VR_OUT_STRING( ) char *pchComponentRenderModelName, uint32_t unComponentRenderModelNameLen ) = 0;
+
+ /** Use this to query information about the component, as a function of the controller state.
+ *
+ * For dynamic controller components (ex: trigger) values will reflect component motions
+ * For static components this will return a consistent value independent of the VRControllerState_t
+ *
+ * If the pchRenderModelName or pchComponentName is invalid, this will return false (and transforms will be set to identity).
+ * Otherwise, return true
+ * Note: For dynamic objects, visibility may be dynamic. (I.e., true/false will be returned based on controller state and controller mode state ) */
+ virtual bool GetComponentStateForDevicePath( const char *pchRenderModelName, const char *pchComponentName, vr::VRInputValueHandle_t devicePath, const vr::RenderModel_ControllerMode_State_t *pState, vr::RenderModel_ComponentState_t *pComponentState ) = 0;
+
+ /** This version of GetComponentState takes a controller state block instead of an action origin. This function is deprecated. You should use the new input system and GetComponentStateForDevicePath instead. */
+ virtual bool GetComponentState( const char *pchRenderModelName, const char *pchComponentName, const vr::VRControllerState_t *pControllerState, const RenderModel_ControllerMode_State_t *pState, RenderModel_ComponentState_t *pComponentState ) = 0;
+
+ /** Returns true if the render model has a component with the specified name */
+ virtual bool RenderModelHasComponent( const char *pchRenderModelName, const char *pchComponentName ) = 0;
+
+ /** Returns the URL of the thumbnail image for this rendermodel */
+ virtual uint32_t GetRenderModelThumbnailURL( const char *pchRenderModelName, VR_OUT_STRING() char *pchThumbnailURL, uint32_t unThumbnailURLLen, vr::EVRRenderModelError *peError ) = 0;
+
+ /** Provides a render model path that will load the unskinned model if the model name provided has been replace by the user. If the model
+ * hasn't been replaced the path value will still be a valid path to load the model. Pass this to LoadRenderModel_Async, etc. to load the
+ * model. */
+ virtual uint32_t GetRenderModelOriginalPath( const char *pchRenderModelName, VR_OUT_STRING() char *pchOriginalPath, uint32_t unOriginalPathLen, vr::EVRRenderModelError *peError ) = 0;
+
+ /** Returns a string for a render model error */
+ virtual const char *GetRenderModelErrorNameFromEnum( vr::EVRRenderModelError error ) = 0;
+};
+
+static const char * const IVRRenderModels_Version = "IVRRenderModels_006";
+
+}
+
+
+// ivrextendeddisplay.h
+namespace vr
+{
+
+ /** NOTE: Use of this interface is not recommended in production applications. It will not work for displays which use
+ * direct-to-display mode. Creating our own window is also incompatible with the VR compositor and is not available when the compositor is running. */
+ class IVRExtendedDisplay
+ {
+ public:
+
+ /** Size and position that the window needs to be on the VR display. */
+ virtual void GetWindowBounds( int32_t *pnX, int32_t *pnY, uint32_t *pnWidth, uint32_t *pnHeight ) = 0;
+
+ /** Gets the viewport in the frame buffer to draw the output of the distortion into */
+ virtual void GetEyeOutputViewport( EVREye eEye, uint32_t *pnX, uint32_t *pnY, uint32_t *pnWidth, uint32_t *pnHeight ) = 0;
+
+ /** [D3D10/11 Only]
+ * Returns the adapter index and output index that the user should pass into EnumAdapters and EnumOutputs
+ * to create the device and swap chain in DX10 and DX11. If an error occurs both indices will be set to -1.
+ */
+ virtual void GetDXGIOutputInfo( int32_t *pnAdapterIndex, int32_t *pnAdapterOutputIndex ) = 0;
+
+ };
+
+ static const char * const IVRExtendedDisplay_Version = "IVRExtendedDisplay_001";
+
+}
+
+
+// ivrtrackedcamera.h
+namespace vr
+{
+
+class IVRTrackedCamera
+{
+public:
+ /** Returns a string for an error */
+ virtual const char *GetCameraErrorNameFromEnum( vr::EVRTrackedCameraError eCameraError ) = 0;
+
+ /** For convenience, same as tracked property request Prop_HasCamera_Bool */
+ virtual vr::EVRTrackedCameraError HasCamera( vr::TrackedDeviceIndex_t nDeviceIndex, bool *pHasCamera ) = 0;
+
+ /** Gets size of the image frame. */
+ virtual vr::EVRTrackedCameraError GetCameraFrameSize( vr::TrackedDeviceIndex_t nDeviceIndex, vr::EVRTrackedCameraFrameType eFrameType, uint32_t *pnWidth, uint32_t *pnHeight, uint32_t *pnFrameBufferSize ) = 0;
+
+ virtual vr::EVRTrackedCameraError GetCameraIntrinsics( vr::TrackedDeviceIndex_t nDeviceIndex, uint32_t nCameraIndex, vr::EVRTrackedCameraFrameType eFrameType, vr::HmdVector2_t *pFocalLength, vr::HmdVector2_t *pCenter ) = 0;
+
+ virtual vr::EVRTrackedCameraError GetCameraProjection( vr::TrackedDeviceIndex_t nDeviceIndex, uint32_t nCameraIndex, vr::EVRTrackedCameraFrameType eFrameType, float flZNear, float flZFar, vr::HmdMatrix44_t *pProjection ) = 0;
+
+ /** Acquiring streaming service permits video streaming for the caller. Releasing hints the system that video services do not need to be maintained for this client.
+ * If the camera has not already been activated, a one time spin up may incur some auto exposure as well as initial streaming frame delays.
+ * The camera should be considered a global resource accessible for shared consumption but not exclusive to any caller.
+ * The camera may go inactive due to lack of active consumers or headset idleness. */
+ virtual vr::EVRTrackedCameraError AcquireVideoStreamingService( vr::TrackedDeviceIndex_t nDeviceIndex, vr::TrackedCameraHandle_t *pHandle ) = 0;
+ virtual vr::EVRTrackedCameraError ReleaseVideoStreamingService( vr::TrackedCameraHandle_t hTrackedCamera ) = 0;
+
+ /** Copies the image frame into a caller's provided buffer. The image data is currently provided as RGBA data, 4 bytes per pixel.
+ * A caller can provide null for the framebuffer or frameheader if not desired. Requesting the frame header first, followed by the frame buffer allows
+ * the caller to determine if the frame as advanced per the frame header sequence.
+ * If there is no frame available yet, due to initial camera spinup or re-activation, the error will be VRTrackedCameraError_NoFrameAvailable.
+ * Ideally a caller should be polling at ~16ms intervals */
+ virtual vr::EVRTrackedCameraError GetVideoStreamFrameBuffer( vr::TrackedCameraHandle_t hTrackedCamera, vr::EVRTrackedCameraFrameType eFrameType, void *pFrameBuffer, uint32_t nFrameBufferSize, vr::CameraVideoStreamFrameHeader_t *pFrameHeader, uint32_t nFrameHeaderSize ) = 0;
+
+ /** Gets size of the image frame. */
+ virtual vr::EVRTrackedCameraError GetVideoStreamTextureSize( vr::TrackedDeviceIndex_t nDeviceIndex, vr::EVRTrackedCameraFrameType eFrameType, vr::VRTextureBounds_t *pTextureBounds, uint32_t *pnWidth, uint32_t *pnHeight ) = 0;
+
+ /** Access a shared D3D11 texture for the specified tracked camera stream.
+ * The camera frame type VRTrackedCameraFrameType_Undistorted is not supported directly as a shared texture. It is an interior subregion of the shared texture VRTrackedCameraFrameType_MaximumUndistorted.
+ * Instead, use GetVideoStreamTextureSize() with VRTrackedCameraFrameType_Undistorted to determine the proper interior subregion bounds along with GetVideoStreamTextureD3D11() with
+ * VRTrackedCameraFrameType_MaximumUndistorted to provide the texture. The VRTrackedCameraFrameType_MaximumUndistorted will yield an image where the invalid regions are decoded
+ * by the alpha channel having a zero component. The valid regions all have a non-zero alpha component. The subregion as described by VRTrackedCameraFrameType_Undistorted
+ * guarantees a rectangle where all pixels are valid. */
+ virtual vr::EVRTrackedCameraError GetVideoStreamTextureD3D11( vr::TrackedCameraHandle_t hTrackedCamera, vr::EVRTrackedCameraFrameType eFrameType, void *pD3D11DeviceOrResource, void **ppD3D11ShaderResourceView, vr::CameraVideoStreamFrameHeader_t *pFrameHeader, uint32_t nFrameHeaderSize ) = 0;
+
+ /** Access a shared GL texture for the specified tracked camera stream */
+ virtual vr::EVRTrackedCameraError GetVideoStreamTextureGL( vr::TrackedCameraHandle_t hTrackedCamera, vr::EVRTrackedCameraFrameType eFrameType, vr::glUInt_t *pglTextureId, vr::CameraVideoStreamFrameHeader_t *pFrameHeader, uint32_t nFrameHeaderSize ) = 0;
+ virtual vr::EVRTrackedCameraError ReleaseVideoStreamTextureGL( vr::TrackedCameraHandle_t hTrackedCamera, vr::glUInt_t glTextureId ) = 0;
+ virtual void SetCameraTrackingSpace( vr::ETrackingUniverseOrigin eUniverse ) = 0;
+ virtual vr::ETrackingUniverseOrigin GetCameraTrackingSpace( ) = 0;
+};
+
+static const char * const IVRTrackedCamera_Version = "IVRTrackedCamera_006";
+
+} // namespace vr
+
+
+// ivrscreenshots.h
+namespace vr
+{
+
+/** Errors that can occur with the VR compositor */
+enum EVRScreenshotError
+{
+ VRScreenshotError_None = 0,
+ VRScreenshotError_RequestFailed = 1,
+ VRScreenshotError_IncompatibleVersion = 100,
+ VRScreenshotError_NotFound = 101,
+ VRScreenshotError_BufferTooSmall = 102,
+ VRScreenshotError_ScreenshotAlreadyInProgress = 108,
+};
+
+/** Allows the application to generate screenshots */
+class IVRScreenshots
+{
+public:
+ /** Request a screenshot of the requested type.
+ * A request of the VRScreenshotType_Stereo type will always
+ * work. Other types will depend on the underlying application
+ * support.
+ * The first file name is for the preview image and should be a
+ * regular screenshot (ideally from the left eye). The second
+ * is the VR screenshot in the correct format. They should be
+ * in the same aspect ratio. Formats per type:
+ * VRScreenshotType_Mono: the VR filename is ignored (can be
+ * nullptr), this is a normal flat single shot.
+ * VRScreenshotType_Stereo: The VR image should be a
+ * side-by-side with the left eye image on the left.
+ * VRScreenshotType_Cubemap: The VR image should be six square
+ * images composited horizontally.
+ * VRScreenshotType_StereoPanorama: above/below with left eye
+ * panorama being the above image. Image is typically square
+ * with the panorama being 2x horizontal.
+ *
+ * Note that the VR dashboard will call this function when
+ * the user presses the screenshot binding (currently System
+ * Button + Trigger). If Steam is running, the destination
+ * file names will be in %TEMP% and will be copied into
+ * Steam's screenshot library for the running application
+ * once SubmitScreenshot() is called.
+ * If Steam is not running, the paths will be in the user's
+ * documents folder under Documents\SteamVR\Screenshots.
+ * Other VR applications can call this to initiate a
+ * screenshot outside of user control.
+ * The destination file names do not need an extension,
+ * will be replaced with the correct one for the format
+ * which is currently .png. */
+ virtual vr::EVRScreenshotError RequestScreenshot( vr::ScreenshotHandle_t *pOutScreenshotHandle, vr::EVRScreenshotType type, const char *pchPreviewFilename, const char *pchVRFilename ) = 0;
+
+ /** Called by the running VR application to indicate that it
+ * wishes to be in charge of screenshots. If the
+ * application does not call this, the Compositor will only
+ * support VRScreenshotType_Stereo screenshots that will be
+ * captured without notification to the running app.
+ * Once hooked your application will receive a
+ * VREvent_RequestScreenshot event when the user presses the
+ * buttons to take a screenshot. */
+ virtual vr::EVRScreenshotError HookScreenshot( VR_ARRAY_COUNT( numTypes ) const vr::EVRScreenshotType *pSupportedTypes, int numTypes ) = 0;
+
+ /** When your application receives a
+ * VREvent_RequestScreenshot event, call these functions to get
+ * the details of the screenshot request. */
+ virtual vr::EVRScreenshotType GetScreenshotPropertyType( vr::ScreenshotHandle_t screenshotHandle, vr::EVRScreenshotError *pError ) = 0;
+
+ /** Get the filename for the preview or vr image (see
+ * vr::EScreenshotPropertyFilenames). The return value is
+ * the size of the string. */
+ virtual uint32_t GetScreenshotPropertyFilename( vr::ScreenshotHandle_t screenshotHandle, vr::EVRScreenshotPropertyFilenames filenameType, VR_OUT_STRING() char *pchFilename, uint32_t cchFilename, vr::EVRScreenshotError *pError ) = 0;
+
+ /** Call this if the application is taking the screen shot
+ * will take more than a few ms processing. This will result
+ * in an overlay being presented that shows a completion
+ * bar. */
+ virtual vr::EVRScreenshotError UpdateScreenshotProgress( vr::ScreenshotHandle_t screenshotHandle, float flProgress ) = 0;
+
+ /** Tells the compositor to take an internal screenshot of
+ * type VRScreenshotType_Stereo. It will take the current
+ * submitted scene textures of the running application and
+ * write them into the preview image and a side-by-side file
+ * for the VR image.
+ * This is similar to request screenshot, but doesn't ever
+ * talk to the application, just takes the shot and submits. */
+ virtual vr::EVRScreenshotError TakeStereoScreenshot( vr::ScreenshotHandle_t *pOutScreenshotHandle, const char *pchPreviewFilename, const char *pchVRFilename ) = 0;
+
+ /** Submit the completed screenshot. If Steam is running
+ * this will call into the Steam client and upload the
+ * screenshot to the screenshots section of the library for
+ * the running application. If Steam is not running, this
+ * function will display a notification to the user that the
+ * screenshot was taken. The paths should be full paths with
+ * extensions.
+ * File paths should be absolute including extensions.
+ * screenshotHandle can be k_unScreenshotHandleInvalid if this
+ * was a new shot taking by the app to be saved and not
+ * initiated by a user (achievement earned or something) */
+ virtual vr::EVRScreenshotError SubmitScreenshot( vr::ScreenshotHandle_t screenshotHandle, vr::EVRScreenshotType type, const char *pchSourcePreviewFilename, const char *pchSourceVRFilename ) = 0;
+};
+
+static const char * const IVRScreenshots_Version = "IVRScreenshots_001";
+
+} // namespace vr
+
+
+
+// ivrresources.h
+namespace vr
+{
+
+class IVRResources
+{
+public:
+
+ // ------------------------------------
+ // Shared Resource Methods
+ // ------------------------------------
+
+ /** Loads the specified resource into the provided buffer if large enough.
+ * Returns the size in bytes of the buffer required to hold the specified resource. */
+ virtual uint32_t LoadSharedResource( const char *pchResourceName, char *pchBuffer, uint32_t unBufferLen ) = 0;
+
+ /** Provides the full path to the specified resource. Resource names can include named directories for
+ * drivers and other things, and this resolves all of those and returns the actual physical path.
+ * pchResourceTypeDirectory is the subdirectory of resources to look in. */
+ virtual uint32_t GetResourceFullPath( const char *pchResourceName, const char *pchResourceTypeDirectory, VR_OUT_STRING() char *pchPathBuffer, uint32_t unBufferLen ) = 0;
+};
+
+static const char * const IVRResources_Version = "IVRResources_001";
+
+
+}
+// ivrdrivermanager.h
+namespace vr
+{
+
+class IVRDriverManager
+{
+public:
+ virtual uint32_t GetDriverCount() const = 0;
+
+ /** Returns the length of the number of bytes necessary to hold this string including the trailing null. */
+ virtual uint32_t GetDriverName( vr::DriverId_t nDriver, VR_OUT_STRING() char *pchValue, uint32_t unBufferSize ) = 0;
+
+ virtual DriverHandle_t GetDriverHandle( const char *pchDriverName ) = 0;
+
+ virtual bool IsEnabled( vr::DriverId_t nDriver ) const = 0;
+};
+
+static const char * const IVRDriverManager_Version = "IVRDriverManager_001";
+
+} // namespace vr
+
+
+
+// ivrinput.h
+namespace vr
+{
+ // Maximum number of characters in an action name, including the trailing null
+ static const uint32_t k_unMaxActionNameLength = 64;
+
+ // Maximum number of characters in an action set name, including the trailing null
+ static const uint32_t k_unMaxActionSetNameLength = 64;
+
+ // Maximum number of origins for an action
+ static const uint32_t k_unMaxActionOriginCount = 16;
+
+ // Maximum number of characters in a bone name, including the trailing null
+ static const uint32_t k_unMaxBoneNameLength = 32;
+
+ enum EVRSkeletalTransformSpace
+ {
+ VRSkeletalTransformSpace_Model = 0,
+ VRSkeletalTransformSpace_Parent = 1
+ };
+
+ enum EVRSkeletalReferencePose
+ {
+ VRSkeletalReferencePose_BindPose = 0,
+ VRSkeletalReferencePose_OpenHand,
+ VRSkeletalReferencePose_Fist,
+ VRSkeletalReferencePose_GripLimit
+ };
+
+ enum EVRFinger
+ {
+ VRFinger_Thumb = 0,
+ VRFinger_Index,
+ VRFinger_Middle,
+ VRFinger_Ring,
+ VRFinger_Pinky,
+ VRFinger_Count
+ };
+
+ enum EVRFingerSplay
+ {
+ VRFingerSplay_Thumb_Index = 0,
+ VRFingerSplay_Index_Middle,
+ VRFingerSplay_Middle_Ring,
+ VRFingerSplay_Ring_Pinky,
+ VRFingerSplay_Count
+ };
+
+ enum EVRSummaryType
+ {
+ // The skeletal summary data will match the animated bone transforms for the action.
+ VRSummaryType_FromAnimation = 0,
+
+ // The skeletal summary data will include unprocessed data directly from the device when available.
+ // This data is generally less latent than the data that is computed from the animations.
+ VRSummaryType_FromDevice = 1,
+ };
+
+ enum EVRInputFilterCancelType
+ {
+ VRInputFilterCancel_Timers = 0,
+ VRInputFilterCancel_Momentum = 1,
+ };
+
+ enum EVRInputStringBits
+ {
+ VRInputString_Hand = 0x01,
+ VRInputString_ControllerType = 0x02,
+ VRInputString_InputSource = 0x04,
+
+ VRInputString_All = 0xFFFFFFFF
+ };
+
+ struct InputAnalogActionData_t
+ {
+ /** Whether or not this action is currently available to be bound in the active action set */
+ bool bActive;
+
+ /** The origin that caused this action's current state */
+ VRInputValueHandle_t activeOrigin;
+
+ /** The current state of this action; will be delta updates for mouse actions */
+ float x, y, z;
+
+ /** Deltas since the previous call to UpdateActionState() */
+ float deltaX, deltaY, deltaZ;
+
+ /** Time relative to now when this event happened. Will be negative to indicate a past time. */
+ float fUpdateTime;
+ };
+
+ struct InputDigitalActionData_t
+ {
+ /** Whether or not this action is currently available to be bound in the active action set */
+ bool bActive;
+
+ /** The origin that caused this action's current state */
+ VRInputValueHandle_t activeOrigin;
+
+ /** The current state of this action; will be true if currently pressed */
+ bool bState;
+
+ /** This is true if the state has changed since the last frame */
+ bool bChanged;
+
+ /** Time relative to now when this event happened. Will be negative to indicate a past time. */
+ float fUpdateTime;
+ };
+
+ struct InputPoseActionData_t
+ {
+ /** Whether or not this action is currently available to be bound in the active action set */
+ bool bActive;
+
+ /** The origin that caused this action's current state */
+ VRInputValueHandle_t activeOrigin;
+
+ /** The current state of this action */
+ TrackedDevicePose_t pose;
+ };
+
+ struct InputSkeletalActionData_t
+ {
+ /** Whether or not this action is currently available to be bound in the active action set */
+ bool bActive;
+
+ /** The origin that caused this action's current state */
+ VRInputValueHandle_t activeOrigin;
+ };
+
+ struct InputOriginInfo_t
+ {
+ VRInputValueHandle_t devicePath;
+ TrackedDeviceIndex_t trackedDeviceIndex;
+ char rchRenderModelComponentName[128];
+ };
+
+ struct InputBindingInfo_t
+ {
+ char rchDevicePathName[128];
+ char rchInputPathName[128];
+ char rchModeName[128];
+ char rchSlotName[128];
+ char rchInputSourceType[ 32 ];
+ };
+
+ // * Experimental global action set priority *
+ // These constants are part of the experimental support in SteamVR for overlay
+ // apps selectively overriding input in the base scene application. This may be
+ // useful for overlay applications that need to use part or all of a controller
+ // without taking away all input to the game. This system must be enabled by the
+ // "Experimental overlay input overrides" setting in the developer section of
+ // SteamVR settings.
+ //
+ // To use this system, set the nPriority field of an action set to any number in
+ // this range.
+ static const int32_t k_nActionSetOverlayGlobalPriorityMin = 0x01000000;
+ static const int32_t k_nActionSetOverlayGlobalPriorityMax = 0x01FFFFFF;
+
+ static const int32_t k_nActionSetPriorityReservedMin = 0x02000000;
+
+ struct VRActiveActionSet_t
+ {
+ /** This is the handle of the action set to activate for this frame. */
+ VRActionSetHandle_t ulActionSet;
+
+ /** This is the handle of a device path that this action set should be active for. To
+ * activate for all devices, set this to k_ulInvalidInputValueHandle. */
+ VRInputValueHandle_t ulRestrictedToDevice;
+
+ /** The action set to activate for all devices other than ulRestrictedDevice. If
+ * ulRestrictedToDevice is set to k_ulInvalidInputValueHandle, this parameter is
+ * ignored. */
+ VRActionSetHandle_t ulSecondaryActionSet;
+
+ // This field is ignored
+ uint32_t unPadding;
+
+ /** The priority of this action set relative to other action sets. Any inputs
+ * bound to a source (e.g. trackpad, joystick, trigger) will disable bindings in
+ * other active action sets with a smaller priority.
+ *
+ * Overlay applications (i.e. ApplicationType_Overlay) may set their action set priority
+ * to a value between k_nActionSetOverlayGlobalPriorityMin and k_nActionSetOverlayGlobalPriorityMax
+ * to cause any inputs bound to a source used by that action set to be disabled in scene applications.
+ *
+ * No action set priority may value may be larger than k_nActionSetPriorityReservedMin
+ */
+ int32_t nPriority;
+ };
+
+ /** Contains summary information about the current skeletal pose */
+ struct VRSkeletalSummaryData_t
+ {
+ /** The amount that each finger is 'curled' inwards towards the palm. In the case of the thumb,
+ * this represents how much the thumb is wrapped around the fist.
+ * 0 means straight, 1 means fully curled */
+ float flFingerCurl[ VRFinger_Count ];
+
+ /** The amount that each pair of adjacent fingers are separated.
+ * 0 means the digits are touching, 1 means they are fully separated.
+ */
+ float flFingerSplay[ VRFingerSplay_Count ];
+ };
+
+
+ class IVRInput
+ {
+ public:
+
+ // --------------- Handle management --------------- //
+
+ /** Sets the path to the action manifest JSON file that is used by this application. If this information
+ * was set on the Steam partner site, calls to this function are ignored. If the Steam partner site
+ * setting and the path provided by this call are different, VRInputError_MismatchedActionManifest is returned.
+ * This call must be made before the first call to UpdateActionState or IVRSystem::PollNextEvent. */
+ virtual EVRInputError SetActionManifestPath( const char *pchActionManifestPath ) = 0;
+
+ /** Returns a handle for an action set. This handle is used for all performance-sensitive calls. */
+ virtual EVRInputError GetActionSetHandle( const char *pchActionSetName, VRActionSetHandle_t *pHandle ) = 0;
+
+ /** Returns a handle for an action. This handle is used for all performance-sensitive calls. */
+ virtual EVRInputError GetActionHandle( const char *pchActionName, VRActionHandle_t *pHandle ) = 0;
+
+ /** Returns a handle for any path in the input system. E.g. /user/hand/right */
+ virtual EVRInputError GetInputSourceHandle( const char *pchInputSourcePath, VRInputValueHandle_t *pHandle ) = 0;
+
+
+
+ // --------------- Reading action state ------------------- //
+
+ /** Reads the current state into all actions. After this call, the results of Get*Action calls
+ * will be the same until the next call to UpdateActionState. */
+ virtual EVRInputError UpdateActionState( VR_ARRAY_COUNT( unSetCount ) VRActiveActionSet_t *pSets, uint32_t unSizeOfVRSelectedActionSet_t, uint32_t unSetCount ) = 0;
+
+ /** Reads the state of a digital action given its handle. This will return VRInputError_WrongType if the type of
+ * action is something other than digital */
+ virtual EVRInputError GetDigitalActionData( VRActionHandle_t action, InputDigitalActionData_t *pActionData, uint32_t unActionDataSize, VRInputValueHandle_t ulRestrictToDevice ) = 0;
+
+ /** Reads the state of an analog action given its handle. This will return VRInputError_WrongType if the type of
+ * action is something other than analog */
+ virtual EVRInputError GetAnalogActionData( VRActionHandle_t action, InputAnalogActionData_t *pActionData, uint32_t unActionDataSize, VRInputValueHandle_t ulRestrictToDevice ) = 0;
+
+ /** Reads the state of a pose action given its handle for the number of seconds relative to now. This
+ * will generally be called with negative times from the fUpdateTime fields in other actions. */
+ virtual EVRInputError GetPoseActionDataRelativeToNow( VRActionHandle_t action, ETrackingUniverseOrigin eOrigin, float fPredictedSecondsFromNow, InputPoseActionData_t *pActionData, uint32_t unActionDataSize, VRInputValueHandle_t ulRestrictToDevice ) = 0;
+
+ /** Reads the state of a pose action given its handle. The returned values will match the values returned
+ * by the last call to IVRCompositor::WaitGetPoses(). */
+ virtual EVRInputError GetPoseActionDataForNextFrame( VRActionHandle_t action, ETrackingUniverseOrigin eOrigin, InputPoseActionData_t *pActionData, uint32_t unActionDataSize, VRInputValueHandle_t ulRestrictToDevice ) = 0;
+
+ /** Reads the state of a skeletal action given its handle. */
+ virtual EVRInputError GetSkeletalActionData( VRActionHandle_t action, InputSkeletalActionData_t *pActionData, uint32_t unActionDataSize ) = 0;
+
+ /** Returns the current dominant hand for the user for this application. This function will only return success for applications
+ * which include "supports_dominant_hand_setting": true in their action manifests. The dominant hand will only change after
+ * a call to UpdateActionState, and the action data returned after that point will use the new dominant hand. */
+ virtual EVRInputError GetDominantHand( ETrackedControllerRole *peDominantHand ) = 0;
+
+ /** Sets the dominant hand for the user for this application. */
+ virtual EVRInputError SetDominantHand( ETrackedControllerRole eDominantHand ) = 0;
+
+ // --------------- Static Skeletal Data ------------------- //
+
+ /** Reads the number of bones in skeleton associated with the given action */
+ virtual EVRInputError GetBoneCount( VRActionHandle_t action, uint32_t* pBoneCount ) = 0;
+
+ /** Fills the given array with the index of each bone's parent in the skeleton associated with the given action */
+ virtual EVRInputError GetBoneHierarchy( VRActionHandle_t action, VR_ARRAY_COUNT( unIndexArayCount ) BoneIndex_t* pParentIndices, uint32_t unIndexArayCount ) = 0;
+
+ /** Fills the given buffer with the name of the bone at the given index in the skeleton associated with the given action */
+ virtual EVRInputError GetBoneName( VRActionHandle_t action, BoneIndex_t nBoneIndex, VR_OUT_STRING() char* pchBoneName, uint32_t unNameBufferSize ) = 0;
+
+ /** Fills the given buffer with the transforms for a specific static skeletal reference pose */
+ virtual EVRInputError GetSkeletalReferenceTransforms( VRActionHandle_t action, EVRSkeletalTransformSpace eTransformSpace, EVRSkeletalReferencePose eReferencePose, VR_ARRAY_COUNT( unTransformArrayCount ) VRBoneTransform_t *pTransformArray, uint32_t unTransformArrayCount ) = 0;
+
+ /** Reads the level of accuracy to which the controller is able to track the user to recreate a skeletal pose */
+ virtual EVRInputError GetSkeletalTrackingLevel( VRActionHandle_t action, EVRSkeletalTrackingLevel* pSkeletalTrackingLevel ) = 0;
+
+ // --------------- Dynamic Skeletal Data ------------------- //
+
+ /** Reads the state of the skeletal bone data associated with this action and copies it into the given buffer. */
+ virtual EVRInputError GetSkeletalBoneData( VRActionHandle_t action, EVRSkeletalTransformSpace eTransformSpace, EVRSkeletalMotionRange eMotionRange, VR_ARRAY_COUNT( unTransformArrayCount ) VRBoneTransform_t *pTransformArray, uint32_t unTransformArrayCount ) = 0;
+
+ /** Reads summary information about the current pose of the skeleton associated with the given action. */
+ virtual EVRInputError GetSkeletalSummaryData( VRActionHandle_t action, EVRSummaryType eSummaryType, VRSkeletalSummaryData_t * pSkeletalSummaryData ) = 0;
+
+ /** Reads the state of the skeletal bone data in a compressed form that is suitable for
+ * sending over the network. The required buffer size will never exceed ( sizeof(VR_BoneTransform_t)*boneCount + 2).
+ * Usually the size will be much smaller. */
+ virtual EVRInputError GetSkeletalBoneDataCompressed( VRActionHandle_t action, EVRSkeletalMotionRange eMotionRange, VR_OUT_BUFFER_COUNT( unCompressedSize ) void *pvCompressedData, uint32_t unCompressedSize, uint32_t *punRequiredCompressedSize ) = 0;
+
+ /** Turns a compressed buffer from GetSkeletalBoneDataCompressed and turns it back into a bone transform array. */
+ virtual EVRInputError DecompressSkeletalBoneData( const void *pvCompressedBuffer, uint32_t unCompressedBufferSize, EVRSkeletalTransformSpace eTransformSpace, VR_ARRAY_COUNT( unTransformArrayCount ) VRBoneTransform_t *pTransformArray, uint32_t unTransformArrayCount ) = 0;
+
+ // --------------- Haptics ------------------- //
+
+ /** Triggers a haptic event as described by the specified action */
+ virtual EVRInputError TriggerHapticVibrationAction( VRActionHandle_t action, float fStartSecondsFromNow, float fDurationSeconds, float fFrequency, float fAmplitude, VRInputValueHandle_t ulRestrictToDevice ) = 0;
+
+ // --------------- Action Origins ---------------- //
+
+ /** Retrieve origin handles for an action */
+ virtual EVRInputError GetActionOrigins( VRActionSetHandle_t actionSetHandle, VRActionHandle_t digitalActionHandle, VR_ARRAY_COUNT( originOutCount ) VRInputValueHandle_t *originsOut, uint32_t originOutCount ) = 0;
+
+ /** Retrieves the name of the origin in the current language. unStringSectionsToInclude is a bitfield of values in EVRInputStringBits that allows the
+ application to specify which parts of the origin's information it wants a string for. */
+ virtual EVRInputError GetOriginLocalizedName( VRInputValueHandle_t origin, VR_OUT_STRING() char *pchNameArray, uint32_t unNameArraySize, int32_t unStringSectionsToInclude ) = 0;
+
+ /** Retrieves useful information for the origin of this action */
+ virtual EVRInputError GetOriginTrackedDeviceInfo( VRInputValueHandle_t origin, InputOriginInfo_t *pOriginInfo, uint32_t unOriginInfoSize ) = 0;
+
+ /** Retrieves useful information about the bindings for an action */
+ virtual EVRInputError GetActionBindingInfo( VRActionHandle_t action, InputBindingInfo_t *pOriginInfo, uint32_t unBindingInfoSize, uint32_t unBindingInfoCount, uint32_t *punReturnedBindingInfoCount ) = 0;
+
+ /** Shows the current binding for the action in-headset */
+ virtual EVRInputError ShowActionOrigins( VRActionSetHandle_t actionSetHandle, VRActionHandle_t ulActionHandle ) = 0;
+
+ /** Shows the current binding all the actions in the specified action sets */
+ virtual EVRInputError ShowBindingsForActionSet( VR_ARRAY_COUNT( unSetCount ) VRActiveActionSet_t *pSets, uint32_t unSizeOfVRSelectedActionSet_t, uint32_t unSetCount, VRInputValueHandle_t originToHighlight ) = 0;
+
+ /** Use this to query what action on the component returned by GetOriginTrackedDeviceInfo would trigger this binding. */
+ virtual EVRInputError GetComponentStateForBinding( const char *pchRenderModelName, const char *pchComponentName,
+ const InputBindingInfo_t *pOriginInfo, uint32_t unBindingInfoSize, uint32_t unBindingInfoCount,
+ vr::RenderModel_ComponentState_t *pComponentState ) = 0;
+
+
+ // --------------- Legacy Input ------------------- //
+ virtual bool IsUsingLegacyInput() = 0;
+
+
+ // --------------- Utilities ------------------- //
+
+ /** Opens the binding user interface. If no app key is provided it will use the key from the calling process.
+ * If no set is provided it will open to the root of the app binding page. */
+ virtual EVRInputError OpenBindingUI( const char* pchAppKey, VRActionSetHandle_t ulActionSetHandle, VRInputValueHandle_t ulDeviceHandle, bool bShowOnDesktop ) = 0;
+
+ /** Returns the variant set in the current bindings. If the binding doesn't include a variant setting, this function
+ * will return an empty string */
+ virtual EVRInputError GetBindingVariant( vr::VRInputValueHandle_t ulDevicePath,
+ VR_OUT_STRING() char *pchVariantArray, uint32_t unVariantArraySize ) = 0;
+
+ };
+
+ static const char * const IVRInput_Version = "IVRInput_010";
+
+} // namespace vr
+
+// ivriobuffer.h
+namespace vr
+{
+
+typedef uint64_t IOBufferHandle_t;
+static const uint64_t k_ulInvalidIOBufferHandle = 0;
+
+ enum EIOBufferError
+ {
+ IOBuffer_Success = 0,
+ IOBuffer_OperationFailed = 100,
+ IOBuffer_InvalidHandle = 101,
+ IOBuffer_InvalidArgument = 102,
+ IOBuffer_PathExists = 103,
+ IOBuffer_PathDoesNotExist = 104,
+ IOBuffer_Permission = 105,
+ };
+
+ enum EIOBufferMode
+ {
+ IOBufferMode_Read = 0x0001,
+ IOBufferMode_Write = 0x0002,
+ IOBufferMode_Create = 0x0200,
+ };
+
+ // ----------------------------------------------------------------------------------------------
+ // Purpose:
+ // ----------------------------------------------------------------------------------------------
+ class IVRIOBuffer
+ {
+ public:
+ /** opens an existing or creates a new IOBuffer of unSize bytes */
+ virtual vr::EIOBufferError Open( const char *pchPath, vr::EIOBufferMode mode, uint32_t unElementSize, uint32_t unElements, vr::IOBufferHandle_t *pulBuffer ) = 0;
+
+ /** closes a previously opened or created buffer */
+ virtual vr::EIOBufferError Close( vr::IOBufferHandle_t ulBuffer ) = 0;
+
+ /** reads up to unBytes from buffer into *pDst, returning number of bytes read in *punRead */
+ virtual vr::EIOBufferError Read( vr::IOBufferHandle_t ulBuffer, void *pDst, uint32_t unBytes, uint32_t *punRead ) = 0;
+
+ /** writes unBytes of data from *pSrc into a buffer. */
+ virtual vr::EIOBufferError Write( vr::IOBufferHandle_t ulBuffer, void *pSrc, uint32_t unBytes ) = 0;
+
+ /** retrieves the property container of an buffer. */
+ virtual vr::PropertyContainerHandle_t PropertyContainer( vr::IOBufferHandle_t ulBuffer ) = 0;
+
+ /** inexpensively checks for readers to allow writers to fast-fail potentially expensive copies and writes. */
+ virtual bool HasReaders( vr::IOBufferHandle_t ulBuffer ) = 0;
+ };
+
+ static const char *IVRIOBuffer_Version = "IVRIOBuffer_002";
+}
+
+// ivrspatialanchors.h
+namespace vr
+{
+ static const SpatialAnchorHandle_t k_ulInvalidSpatialAnchorHandle = 0;
+
+ struct SpatialAnchorPose_t
+ {
+ HmdMatrix34_t mAnchorToAbsoluteTracking;
+ };
+
+ class IVRSpatialAnchors
+ {
+ public:
+
+ /** Returns a handle for an spatial anchor described by "descriptor". On success, pHandle
+ * will contain a handle valid for this session. Caller can wait for an event or occasionally
+ * poll GetSpatialAnchorPose() to find the virtual coordinate associated with this anchor. */
+ virtual EVRSpatialAnchorError CreateSpatialAnchorFromDescriptor( const char *pchDescriptor, SpatialAnchorHandle_t *pHandleOut ) = 0;
+
+ /** Returns a handle for an new spatial anchor at pPose. On success, pHandle
+ * will contain a handle valid for this session. Caller can wait for an event or occasionally
+ * poll GetSpatialAnchorDescriptor() to find the permanent descriptor for this pose.
+ * The result of GetSpatialAnchorPose() may evolve from this initial position if the driver chooses
+ * to update it.
+ * The anchor will be associated with the driver that provides unDeviceIndex, and the driver may use that specific
+ * device as a hint for how to best create the anchor.
+ * The eOrigin must match whatever tracking origin you are working in (seated/standing/raw).
+ * This should be called when the user is close to (and ideally looking at/interacting with) the target physical
+ * location. At that moment, the driver will have the most information about how to recover that physical point
+ * in the future, and the quality of the anchor (when the descriptor is re-used) will be highest.
+ * The caller may decide to apply offsets from this initial pose, but is advised to stay relatively close to the
+ * original pose location for highest fidelity. */
+ virtual EVRSpatialAnchorError CreateSpatialAnchorFromPose( TrackedDeviceIndex_t unDeviceIndex, ETrackingUniverseOrigin eOrigin, SpatialAnchorPose_t *pPose, SpatialAnchorHandle_t *pHandleOut ) = 0;
+
+ /** Get the pose for a given handle. This is intended to be cheap enough to call every frame (or fairly often)
+ * so that the driver can refine this position when it has more information available. */
+ virtual EVRSpatialAnchorError GetSpatialAnchorPose( SpatialAnchorHandle_t unHandle, ETrackingUniverseOrigin eOrigin, SpatialAnchorPose_t *pPoseOut ) = 0;
+
+ /** Get the descriptor for a given handle. This will be empty for handles where the driver has not
+ * yet built a descriptor. It will be the application-supplied descriptor for previously saved anchors
+ * that the application is requesting poses for. If the driver has called UpdateSpatialAnchorDescriptor()
+ * already in this session, it will be the descriptor provided by the driver.
+ * Returns true if the descriptor fits into the buffer, else false. Buffer size should be at least
+ * k_unMaxSpatialAnchorDescriptorSize. */
+ virtual EVRSpatialAnchorError GetSpatialAnchorDescriptor( SpatialAnchorHandle_t unHandle, VR_OUT_STRING() char *pchDescriptorOut, uint32_t *punDescriptorBufferLenInOut ) = 0;
+
+ };
+
+ static const char * const IVRSpatialAnchors_Version = "IVRSpatialAnchors_001";
+
+} // namespace vr
+
+// ivrdebug.h
+namespace vr
+{
+ enum EVRDebugError
+ {
+ VRDebugError_Success = 0,
+ VRDebugError_BadParameter
+ };
+
+ /** Handle for vr profiler events */
+ typedef uint64_t VrProfilerEventHandle_t;
+
+ class IVRDebug
+ {
+ public:
+
+ /** Create a vr profiler discrete event (point)
+ * The event will be associated with the message provided in pchMessage, and the current
+ * time will be used as the event timestamp. */
+ virtual EVRDebugError EmitVrProfilerEvent( const char *pchMessage ) = 0;
+
+ /** Create an vr profiler duration event (line)
+ * The current time will be used as the timestamp for the start of the line.
+ * On success, pHandleOut will contain a handle valid for terminating this event. */
+ virtual EVRDebugError BeginVrProfilerEvent( VrProfilerEventHandle_t *pHandleOut ) = 0;
+
+ /** Terminate a vr profiler event
+ * The event associated with hHandle will be considered completed when this method is called.
+ * The current time will be used assocaited to the termination time of the event, and
+ * pchMessage will be used as the event title. */
+ virtual EVRDebugError FinishVrProfilerEvent( VrProfilerEventHandle_t hHandle, const char *pchMessage ) = 0;
+
+ /** Sends a request to the driver for the specified device and returns the response. The maximum response size is 32k,
+ * but this method can be called with a smaller buffer. If the response exceeds the size of the buffer, it is truncated.
+ * The size of the response including its terminating null is returned. */
+ virtual uint32_t DriverDebugRequest( vr::TrackedDeviceIndex_t unDeviceIndex, const char *pchRequest, VR_OUT_STRING() char *pchResponseBuffer, uint32_t unResponseBufferSize ) = 0;
+
+ };
+
+ static const char * const IVRDebug_Version = "IVRDebug_001";
+
+} // namespace vr
+// End
+
+#endif // _OPENVR_API
+
+
+
+namespace vr
+{
+#if !defined( OPENVR_INTERFACE_INTERNAL )
+
+ /** Finds the active installation of the VR API and initializes it. The provided path must be absolute
+ * or relative to the current working directory. These are the local install versions of the equivalent
+ * functions in steamvr.h and will work without a local Steam install.
+ *
+ * This path is to the "root" of the VR API install. That's the directory with
+ * the "drivers" directory and a platform (i.e. "win32") directory in it, not the directory with the DLL itself.
+ *
+ * pStartupInfo is reserved for future use.
+ */
+ inline IVRSystem *VR_Init( EVRInitError *peError, EVRApplicationType eApplicationType, const char *pStartupInfo = nullptr );
+
+ /** unloads vrclient.dll. Any interface pointers from the interface are
+ * invalid after this point */
+ inline void VR_Shutdown();
+
+ /** Returns true if there is an HMD attached. This check is as lightweight as possible and
+ * can be called outside of VR_Init/VR_Shutdown. It should be used when an application wants
+ * to know if initializing VR is a possibility but isn't ready to take that step yet.
+ */
+ VR_INTERFACE bool VR_CALLTYPE VR_IsHmdPresent();
+
+ /** Returns true if the OpenVR runtime is installed. */
+ VR_INTERFACE bool VR_CALLTYPE VR_IsRuntimeInstalled();
+
+ /** Returns where the OpenVR runtime is installed. */
+ VR_INTERFACE bool VR_GetRuntimePath( VR_OUT_STRING() char *pchPathBuffer, uint32_t unBufferSize, uint32_t *punRequiredBufferSize );
+
+ /** Returns the name of the enum value for an EVRInitError. This function may be called outside of VR_Init()/VR_Shutdown(). */
+ VR_INTERFACE const char *VR_CALLTYPE VR_GetVRInitErrorAsSymbol( EVRInitError error );
+
+ /** Returns an English string for an EVRInitError. Applications should call VR_GetVRInitErrorAsSymbol instead and
+ * use that as a key to look up their own localized error message. This function may be called outside of VR_Init()/VR_Shutdown(). */
+ VR_INTERFACE const char *VR_CALLTYPE VR_GetVRInitErrorAsEnglishDescription( EVRInitError error );
+
+ /** Returns the interface of the specified version. This method must be called after VR_Init. The
+ * pointer returned is valid until VR_Shutdown is called.
+ */
+ VR_INTERFACE void *VR_CALLTYPE VR_GetGenericInterface( const char *pchInterfaceVersion, EVRInitError *peError );
+
+ /** Returns whether the interface of the specified version exists.
+ */
+ VR_INTERFACE bool VR_CALLTYPE VR_IsInterfaceVersionValid( const char *pchInterfaceVersion );
+
+ /** Returns a token that represents whether the VR interface handles need to be reloaded */
+ VR_INTERFACE uint32_t VR_CALLTYPE VR_GetInitToken();
+
+ // These typedefs allow old enum names from SDK 0.9.11 to be used in applications.
+ // They will go away in the future.
+ typedef EVRInitError HmdError;
+ typedef EVREye Hmd_Eye;
+ typedef EColorSpace ColorSpace;
+ typedef ETrackingResult HmdTrackingResult;
+ typedef ETrackedDeviceClass TrackedDeviceClass;
+ typedef ETrackingUniverseOrigin TrackingUniverseOrigin;
+ typedef ETrackedDeviceProperty TrackedDeviceProperty;
+ typedef ETrackedPropertyError TrackedPropertyError;
+ typedef EVRSubmitFlags VRSubmitFlags_t;
+ typedef EVRState VRState_t;
+ typedef ECollisionBoundsStyle CollisionBoundsStyle_t;
+ typedef EVROverlayError VROverlayError;
+ typedef EVRFirmwareError VRFirmwareError;
+ typedef EVRCompositorError VRCompositorError;
+ typedef EVRScreenshotError VRScreenshotsError;
+
+ inline uint32_t &VRToken()
+ {
+ static uint32_t token;
+ return token;
+ }
+
+ class COpenVRContext
+ {
+ public:
+ COpenVRContext() { Clear(); }
+ void Clear();
+
+ inline void CheckClear()
+ {
+ if ( VRToken() != VR_GetInitToken() )
+ {
+ Clear();
+ VRToken() = VR_GetInitToken();
+ }
+ }
+
+ IVRSystem *VRSystem()
+ {
+ CheckClear();
+ if ( m_pVRSystem == nullptr )
+ {
+ EVRInitError eError;
+ m_pVRSystem = ( IVRSystem * )VR_GetGenericInterface( IVRSystem_Version, &eError );
+ }
+ return m_pVRSystem;
+ }
+ IVRChaperone *VRChaperone()
+ {
+ CheckClear();
+ if ( m_pVRChaperone == nullptr )
+ {
+ EVRInitError eError;
+ m_pVRChaperone = ( IVRChaperone * )VR_GetGenericInterface( IVRChaperone_Version, &eError );
+ }
+ return m_pVRChaperone;
+ }
+
+ IVRChaperoneSetup *VRChaperoneSetup()
+ {
+ CheckClear();
+ if ( m_pVRChaperoneSetup == nullptr )
+ {
+ EVRInitError eError;
+ m_pVRChaperoneSetup = ( IVRChaperoneSetup * )VR_GetGenericInterface( IVRChaperoneSetup_Version, &eError );
+ }
+ return m_pVRChaperoneSetup;
+ }
+
+ IVRCompositor *VRCompositor()
+ {
+ CheckClear();
+ if ( m_pVRCompositor == nullptr )
+ {
+ EVRInitError eError;
+ m_pVRCompositor = ( IVRCompositor * )VR_GetGenericInterface( IVRCompositor_Version, &eError );
+ }
+ return m_pVRCompositor;
+ }
+
+ IVROverlay *VROverlay()
+ {
+ CheckClear();
+ if ( m_pVROverlay == nullptr )
+ {
+ EVRInitError eError;
+ m_pVROverlay = ( IVROverlay * )VR_GetGenericInterface( IVROverlay_Version, &eError );
+ }
+ return m_pVROverlay;
+ }
+
+ IVROverlayView *VROverlayView()
+ {
+ CheckClear();
+ if ( m_pVROverlayView == nullptr )
+ {
+ EVRInitError eError;
+ m_pVROverlayView = ( IVROverlayView * ) VR_GetGenericInterface( IVROverlayView_Version, &eError );
+ }
+ return m_pVROverlayView;
+ }
+
+ IVRHeadsetView *VRHeadsetView()
+ {
+ CheckClear();
+ if ( m_pVRHeadsetView == nullptr )
+ {
+ EVRInitError eError;
+ m_pVRHeadsetView = ( IVRHeadsetView * ) VR_GetGenericInterface( IVRHeadsetView_Version, &eError );
+ }
+ return m_pVRHeadsetView;
+ }
+
+ IVRResources *VRResources()
+ {
+ CheckClear();
+ if ( m_pVRResources == nullptr )
+ {
+ EVRInitError eError;
+ m_pVRResources = (IVRResources *)VR_GetGenericInterface( IVRResources_Version, &eError );
+ }
+ return m_pVRResources;
+ }
+
+ IVRScreenshots *VRScreenshots()
+ {
+ CheckClear();
+ if ( m_pVRScreenshots == nullptr )
+ {
+ EVRInitError eError;
+ m_pVRScreenshots = ( IVRScreenshots * )VR_GetGenericInterface( IVRScreenshots_Version, &eError );
+ }
+ return m_pVRScreenshots;
+ }
+
+ IVRRenderModels *VRRenderModels()
+ {
+ CheckClear();
+ if ( m_pVRRenderModels == nullptr )
+ {
+ EVRInitError eError;
+ m_pVRRenderModels = ( IVRRenderModels * )VR_GetGenericInterface( IVRRenderModels_Version, &eError );
+ }
+ return m_pVRRenderModels;
+ }
+
+ IVRExtendedDisplay *VRExtendedDisplay()
+ {
+ CheckClear();
+ if ( m_pVRExtendedDisplay == nullptr )
+ {
+ EVRInitError eError;
+ m_pVRExtendedDisplay = ( IVRExtendedDisplay * )VR_GetGenericInterface( IVRExtendedDisplay_Version, &eError );
+ }
+ return m_pVRExtendedDisplay;
+ }
+
+ IVRSettings *VRSettings()
+ {
+ CheckClear();
+ if ( m_pVRSettings == nullptr )
+ {
+ EVRInitError eError;
+ m_pVRSettings = ( IVRSettings * )VR_GetGenericInterface( IVRSettings_Version, &eError );
+ }
+ return m_pVRSettings;
+ }
+
+ IVRApplications *VRApplications()
+ {
+ CheckClear();
+ if ( m_pVRApplications == nullptr )
+ {
+ EVRInitError eError;
+ m_pVRApplications = ( IVRApplications * )VR_GetGenericInterface( IVRApplications_Version, &eError );
+ }
+ return m_pVRApplications;
+ }
+
+ IVRTrackedCamera *VRTrackedCamera()
+ {
+ CheckClear();
+ if ( m_pVRTrackedCamera == nullptr )
+ {
+ EVRInitError eError;
+ m_pVRTrackedCamera = ( IVRTrackedCamera * )VR_GetGenericInterface( IVRTrackedCamera_Version, &eError );
+ }
+ return m_pVRTrackedCamera;
+ }
+
+ IVRDriverManager *VRDriverManager()
+ {
+ CheckClear();
+ if ( !m_pVRDriverManager )
+ {
+ EVRInitError eError;
+ m_pVRDriverManager = ( IVRDriverManager * )VR_GetGenericInterface( IVRDriverManager_Version, &eError );
+ }
+ return m_pVRDriverManager;
+ }
+
+ IVRInput *VRInput()
+ {
+ CheckClear();
+ if ( !m_pVRInput )
+ {
+ EVRInitError eError;
+ m_pVRInput = (IVRInput *)VR_GetGenericInterface( IVRInput_Version, &eError );
+ }
+ return m_pVRInput;
+ }
+
+ IVRIOBuffer *VRIOBuffer()
+ {
+ if ( !m_pVRIOBuffer )
+ {
+ EVRInitError eError;
+ m_pVRIOBuffer = ( IVRIOBuffer * )VR_GetGenericInterface( IVRIOBuffer_Version, &eError );
+ }
+ return m_pVRIOBuffer;
+ }
+
+ IVRSpatialAnchors *VRSpatialAnchors()
+ {
+ CheckClear();
+ if ( !m_pVRSpatialAnchors )
+ {
+ EVRInitError eError;
+ m_pVRSpatialAnchors = (IVRSpatialAnchors *)VR_GetGenericInterface( IVRSpatialAnchors_Version, &eError );
+ }
+ return m_pVRSpatialAnchors;
+ }
+
+ IVRDebug *VRDebug()
+ {
+ CheckClear();
+ if ( !m_pVRDebug )
+ {
+ EVRInitError eError;
+ m_pVRDebug = (IVRDebug *)VR_GetGenericInterface( IVRDebug_Version, &eError );
+ }
+ return m_pVRDebug;
+ }
+
+ IVRNotifications *VRNotifications()
+ {
+ CheckClear();
+ if ( !m_pVRNotifications )
+ {
+ EVRInitError eError;
+ m_pVRNotifications = ( IVRNotifications * )VR_GetGenericInterface( IVRNotifications_Version, &eError );
+ }
+ return m_pVRNotifications;
+ }
+
+ private:
+ IVRSystem *m_pVRSystem;
+ IVRChaperone *m_pVRChaperone;
+ IVRChaperoneSetup *m_pVRChaperoneSetup;
+ IVRCompositor *m_pVRCompositor;
+ IVRHeadsetView *m_pVRHeadsetView;
+ IVROverlay *m_pVROverlay;
+ IVROverlayView *m_pVROverlayView;
+ IVRResources *m_pVRResources;
+ IVRRenderModels *m_pVRRenderModels;
+ IVRExtendedDisplay *m_pVRExtendedDisplay;
+ IVRSettings *m_pVRSettings;
+ IVRApplications *m_pVRApplications;
+ IVRTrackedCamera *m_pVRTrackedCamera;
+ IVRScreenshots *m_pVRScreenshots;
+ IVRDriverManager *m_pVRDriverManager;
+ IVRInput *m_pVRInput;
+ IVRIOBuffer *m_pVRIOBuffer;
+ IVRSpatialAnchors *m_pVRSpatialAnchors;
+ IVRDebug *m_pVRDebug;
+ IVRNotifications *m_pVRNotifications;
+ };
+
+ inline COpenVRContext &OpenVRInternal_ModuleContext()
+ {
+ static void *ctx[ sizeof( COpenVRContext ) / sizeof( void * ) ];
+ return *( COpenVRContext * )ctx; // bypass zero-init constructor
+ }
+
+ inline IVRSystem *VR_CALLTYPE VRSystem() { return OpenVRInternal_ModuleContext().VRSystem(); }
+ inline IVRChaperone *VR_CALLTYPE VRChaperone() { return OpenVRInternal_ModuleContext().VRChaperone(); }
+ inline IVRChaperoneSetup *VR_CALLTYPE VRChaperoneSetup() { return OpenVRInternal_ModuleContext().VRChaperoneSetup(); }
+ inline IVRCompositor *VR_CALLTYPE VRCompositor() { return OpenVRInternal_ModuleContext().VRCompositor(); }
+ inline IVROverlay *VR_CALLTYPE VROverlay() { return OpenVRInternal_ModuleContext().VROverlay(); }
+ inline IVROverlayView *VR_CALLTYPE VROverlayView() { return OpenVRInternal_ModuleContext().VROverlayView(); }
+ inline IVRHeadsetView *VR_CALLTYPE VRHeadsetView() { return OpenVRInternal_ModuleContext().VRHeadsetView(); }
+ inline IVRScreenshots *VR_CALLTYPE VRScreenshots() { return OpenVRInternal_ModuleContext().VRScreenshots(); }
+ inline IVRRenderModels *VR_CALLTYPE VRRenderModels() { return OpenVRInternal_ModuleContext().VRRenderModels(); }
+ inline IVRApplications *VR_CALLTYPE VRApplications() { return OpenVRInternal_ModuleContext().VRApplications(); }
+ inline IVRSettings *VR_CALLTYPE VRSettings() { return OpenVRInternal_ModuleContext().VRSettings(); }
+ inline IVRResources *VR_CALLTYPE VRResources() { return OpenVRInternal_ModuleContext().VRResources(); }
+ inline IVRExtendedDisplay *VR_CALLTYPE VRExtendedDisplay() { return OpenVRInternal_ModuleContext().VRExtendedDisplay(); }
+ inline IVRTrackedCamera *VR_CALLTYPE VRTrackedCamera() { return OpenVRInternal_ModuleContext().VRTrackedCamera(); }
+ inline IVRDriverManager *VR_CALLTYPE VRDriverManager() { return OpenVRInternal_ModuleContext().VRDriverManager(); }
+ inline IVRInput *VR_CALLTYPE VRInput() { return OpenVRInternal_ModuleContext().VRInput(); }
+ inline IVRIOBuffer *VR_CALLTYPE VRIOBuffer() { return OpenVRInternal_ModuleContext().VRIOBuffer(); }
+ inline IVRSpatialAnchors *VR_CALLTYPE VRSpatialAnchors() { return OpenVRInternal_ModuleContext().VRSpatialAnchors(); }
+ inline IVRNotifications *VR_CALLTYPE VRNotifications() { return OpenVRInternal_ModuleContext().VRNotifications(); }
+ inline IVRDebug *VR_CALLTYPE VRDebug() { return OpenVRInternal_ModuleContext().VRDebug(); }
+
+ inline void COpenVRContext::Clear()
+ {
+ m_pVRSystem = nullptr;
+ m_pVRChaperone = nullptr;
+ m_pVRChaperoneSetup = nullptr;
+ m_pVRCompositor = nullptr;
+ m_pVROverlay = nullptr;
+ m_pVROverlayView = nullptr;
+ m_pVRHeadsetView = nullptr;
+ m_pVRRenderModels = nullptr;
+ m_pVRExtendedDisplay = nullptr;
+ m_pVRSettings = nullptr;
+ m_pVRApplications = nullptr;
+ m_pVRTrackedCamera = nullptr;
+ m_pVRResources = nullptr;
+ m_pVRScreenshots = nullptr;
+ m_pVRDriverManager = nullptr;
+ m_pVRInput = nullptr;
+ m_pVRIOBuffer = nullptr;
+ m_pVRSpatialAnchors = nullptr;
+ m_pVRNotifications = nullptr;
+ m_pVRDebug = nullptr;
+ }
+
+ VR_INTERFACE uint32_t VR_CALLTYPE VR_InitInternal2( EVRInitError *peError, EVRApplicationType eApplicationType, const char *pStartupInfo );
+ VR_INTERFACE void VR_CALLTYPE VR_ShutdownInternal();
+
+ /** Finds the active installation of vrclient.dll and initializes it */
+ inline IVRSystem *VR_Init( EVRInitError *peError, EVRApplicationType eApplicationType, const char *pStartupInfo )
+ {
+ IVRSystem *pVRSystem = nullptr;
+
+ EVRInitError eError;
+ VRToken() = VR_InitInternal2( &eError, eApplicationType, pStartupInfo );
+ COpenVRContext &ctx = OpenVRInternal_ModuleContext();
+ ctx.Clear();
+
+ if ( eError == VRInitError_None )
+ {
+ if ( VR_IsInterfaceVersionValid( IVRSystem_Version ) )
+ {
+ pVRSystem = VRSystem();
+ }
+ else
+ {
+ VR_ShutdownInternal();
+ eError = VRInitError_Init_InterfaceNotFound;
+ }
+ }
+
+ if ( peError )
+ *peError = eError;
+ return pVRSystem;
+ }
+
+ /** unloads vrclient.dll. Any interface pointers from the interface are
+ * invalid after this point */
+ inline void VR_Shutdown()
+ {
+ VR_ShutdownInternal();
+ }
+
+#endif // OPENVR_INTERFACE_INTERNAL
+}
diff --git a/gfx/vr/service/openvr/moz.build b/gfx/vr/service/openvr/moz.build
new file mode 100644
index 0000000000..5ad6d0b9b4
--- /dev/null
+++ b/gfx/vr/service/openvr/moz.build
@@ -0,0 +1,62 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+FINAL_LIBRARY = 'xul'
+
+DEFINES['VR_API_PUBLIC'] = True
+
+# Windows.h wrappers conflict with internal methods in openvr
+DEFINES['MOZ_DISABLE_WINDOWS_WRAPPER'] = True
+
+if CONFIG['OS_ARCH'] == 'WINNT':
+ if CONFIG['HAVE_64BIT_BUILD']:
+ DEFINES['WIN64'] = True
+ else:
+ DEFINES['WIN32'] = True
+
+if CONFIG['OS_ARCH'] == 'Darwin':
+ DEFINES['POSIX'] = True
+ DEFINES['OSX'] = True
+ if CONFIG['CC_TYPE'] in ('clang', 'gcc'):
+ CXXFLAGS += ['-xobjective-c++']
+
+if CONFIG['OS_ARCH'] == 'Linux':
+ DEFINES['POSIX'] = True
+ DEFINES['LINUX'] = True
+ if CONFIG['HAVE_64BIT_BUILD']:
+ DEFINES['LINUX64'] = True
+ else:
+ DEFINES['LINUX32'] = True
+
+LOCAL_INCLUDES += [
+ '/toolkit/components/jsoncpp/include',
+]
+
+USE_LIBS += [
+ 'jsoncpp',
+]
+
+EXPORTS += [
+ 'headers/openvr.h',
+]
+
+SOURCES += [
+ 'src/dirtools_public.cpp',
+ 'src/envvartools_public.cpp',
+ 'src/hmderrors_public.cpp',
+ 'src/openvr_api_public.cpp',
+ 'src/pathtools_public.cpp',
+ 'src/sharedlibtools_public.cpp',
+ 'src/strtools_public.cpp',
+ 'src/vrpathregistry_public.cpp',
+]
+
+if CONFIG['CC_TYPE'] in ('clang', 'gcc'):
+ # Harmless warnings in 3rd party code
+ CXXFLAGS += [
+ '-Wno-error=parentheses',
+ '-Wno-error=unused-variable',
+ ]
diff --git a/gfx/vr/service/openvr/src/README b/gfx/vr/service/openvr/src/README
new file mode 100644
index 0000000000..826c58da79
--- /dev/null
+++ b/gfx/vr/service/openvr/src/README
@@ -0,0 +1,39 @@
+This is the source code for the OpenVR API client binding library which connects
+OpenVR applications to the SteamVR runtime, taking into account the version
+of the OpenVR interface they were compiled against.
+
+The client binding library - openvr_api.dll on Windows, openvr_api.so on
+Linux, and openvr_api.dylib or OpenVR.framework on macOS - knows how to find
+and read the SteamVR runtime installation information which allows it to
+find and dynamically connect to the installed runtime. In combination with the
+interface version identifiers from /include/openvr.h which are baked
+into applications at the time they are built, the OpenVR API client
+binding library captures and conveys to the SteamVR runtime the version
+of the OpenVR API interface behavior that the application expects.
+
+Applications carry with them a private/local copy of the client binding
+library when they ship, and they should install it locally to their
+application. Applications should not install the client binding library
+globally or attempt to link to a globally installed client binding library.
+Doing so negates at least part of the ability for the client binding library
+to accurately reflect the version of the OpenVR API that the application
+was built against, and so hinders compatibility support in the face of
+API changes.
+
+Most applications should simply link to and redistribute with their application
+the pre-built client binding library found in the /bin directory of this
+repository. Some small number of applications which have specific requirements
+around redistributing only binaries they build themselves should build
+the client library from this source and either statically link it into
+their application or redistribute the binary they build.
+
+This is a cmake project, to build it use the version of cmake appropriate
+for your platform. For example, to build on a POSIX system simply perform
+
+ cd src; mkdir _build; cd _build; cmake ..; make
+
+and you will end up with the static library /src/bin/<arch>/libopenvr_api.a
+
+To build a shared library, pass -DBUILD_SHARED=1 to cmake.
+To build as a framework on apple platforms, pass -DBUILD_FRAMEWORK=1 to cmake.
+To see a complete list of configurable build options, use `cmake -LAH`
diff --git a/gfx/vr/service/openvr/src/dirtools_public.cpp b/gfx/vr/service/openvr/src/dirtools_public.cpp
new file mode 100644
index 0000000000..e5cfc02e0c
--- /dev/null
+++ b/gfx/vr/service/openvr/src/dirtools_public.cpp
@@ -0,0 +1,101 @@
+//========= Copyright Valve Corporation ============//
+#include "dirtools_public.h"
+#include "strtools_public.h"
+#include "pathtools_public.h"
+
+#include <errno.h>
+#include <string.h>
+
+#ifdef _WIN32
+#include "windows.h"
+#else
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#endif
+
+#if defined( OSX )
+#include <sys/syslimits.h>
+#endif
+
+
+//-----------------------------------------------------------------------------
+// Purpose: utility function to create dirs & subdirs
+//-----------------------------------------------------------------------------
+bool BCreateDirectoryRecursive( const char *pchPath )
+{
+ // Does it already exist?
+ if ( Path_IsDirectory( pchPath ) )
+ return true;
+
+ // copy the path into something we can munge
+ int len = (int)strlen( pchPath );
+ char *path = (char *)malloc( len + 1 );
+ strcpy( path, pchPath );
+
+ // Walk backwards to first non-existing dir that we find
+ char *s = path + len - 1;
+
+ const char slash = Path_GetSlash();
+ while ( s > path )
+ {
+ if ( *s == slash )
+ {
+ *s = '\0';
+ bool bExists = Path_IsDirectory( path );
+ *s = slash;
+
+ if ( bExists )
+ {
+ ++s;
+ break;
+ }
+ }
+ --s;
+ }
+
+ // and then move forwards from there
+
+ while ( *s )
+ {
+ if ( *s == slash )
+ {
+ *s = '\0';
+ BCreateDirectory( path );
+ *s = slash;
+ }
+ s++;
+ }
+
+ bool bRetVal = BCreateDirectory( path );
+ free( path );
+ return bRetVal;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Creates the directory, returning true if it is created, or if it already existed
+//-----------------------------------------------------------------------------
+bool BCreateDirectory( const char *pchPath )
+{
+#ifdef WIN32
+ std::wstring wPath = UTF8to16( pchPath );
+ if ( ::CreateDirectoryW( wPath.c_str(), NULL ) )
+ return true;
+
+ if ( ::GetLastError() == ERROR_ALREADY_EXISTS )
+ return true;
+
+ return false;
+#else
+ int i = mkdir( pchPath, S_IRWXU | S_IRWXG | S_IRWXO );
+ if ( i == 0 )
+ return true;
+ if ( errno == EEXIST )
+ return true;
+
+ return false;
+#endif
+}
+
diff --git a/gfx/vr/service/openvr/src/dirtools_public.h b/gfx/vr/service/openvr/src/dirtools_public.h
new file mode 100644
index 0000000000..b048b41b78
--- /dev/null
+++ b/gfx/vr/service/openvr/src/dirtools_public.h
@@ -0,0 +1,17 @@
+//========= Copyright Valve Corporation ============//
+#pragma once
+
+#include <stdint.h>
+#include <string>
+
+
+#if !defined(_WIN32)
+#include <sys/types.h>
+#include <sys/stat.h>
+#endif
+
+
+extern bool BCreateDirectoryRecursive( const char *pchPath );
+extern bool BCreateDirectory( const char *pchPath );
+
+
diff --git a/gfx/vr/service/openvr/src/envvartools_public.cpp b/gfx/vr/service/openvr/src/envvartools_public.cpp
new file mode 100644
index 0000000000..4fb4817927
--- /dev/null
+++ b/gfx/vr/service/openvr/src/envvartools_public.cpp
@@ -0,0 +1,88 @@
+//========= Copyright Valve Corporation ============//
+#include "envvartools_public.h"
+#include "strtools_public.h"
+#include <stdlib.h>
+#include <string>
+#include <cctype>
+
+#if defined(_WIN32)
+#include <windows.h>
+
+#undef GetEnvironmentVariable
+#undef SetEnvironmentVariable
+#endif
+
+
+std::string GetEnvironmentVariable( const char *pchVarName )
+{
+#if defined(_WIN32)
+ char rchValue[32767]; // max size for an env var on Windows
+ DWORD cChars = GetEnvironmentVariableA( pchVarName, rchValue, sizeof( rchValue ) );
+ if( cChars == 0 )
+ return "";
+ else
+ return rchValue;
+#elif defined(POSIX)
+ char *pchValue = getenv( pchVarName );
+ if( pchValue )
+ return pchValue;
+ else
+ return "";
+#else
+#error "Unsupported Platform"
+#endif
+}
+
+bool GetEnvironmentVariableAsBool( const char *pchVarName, bool bDefault )
+{
+ std::string sValue = GetEnvironmentVariable( pchVarName );
+
+ if ( sValue.empty() )
+ {
+ return bDefault;
+ }
+
+ sValue = StringToLower( sValue );
+ std::string sYesValues[] = { "y", "yes", "true" };
+ std::string sNoValues[] = { "n", "no", "false" };
+
+ for ( std::string &sMatch : sYesValues )
+ {
+ if ( sMatch == sValue )
+ {
+ return true;
+ }
+ }
+
+ for ( std::string &sMatch : sNoValues )
+ {
+ if ( sMatch == sValue )
+ {
+ return false;
+ }
+ }
+
+ if ( std::isdigit( sValue.at(0) ) )
+ {
+ return atoi( sValue.c_str() ) != 0;
+ }
+
+ fprintf( stderr,
+ "GetEnvironmentVariableAsBool(%s): Unable to parse value '%s', using default %d\n",
+ pchVarName, sValue.c_str(), bDefault );
+ return bDefault;
+}
+
+bool SetEnvironmentVariable( const char *pchVarName, const char *pchVarValue )
+{
+#if defined(_WIN32)
+ return 0 != SetEnvironmentVariableA( pchVarName, pchVarValue );
+#elif defined(POSIX)
+ if( pchVarValue == NULL )
+ return 0 == unsetenv( pchVarName );
+ else
+ return 0 == setenv( pchVarName, pchVarValue, 1 );
+#else
+#error "Unsupported Platform"
+#endif
+}
diff --git a/gfx/vr/service/openvr/src/envvartools_public.h b/gfx/vr/service/openvr/src/envvartools_public.h
new file mode 100644
index 0000000000..7cd4c208e6
--- /dev/null
+++ b/gfx/vr/service/openvr/src/envvartools_public.h
@@ -0,0 +1,8 @@
+//========= Copyright Valve Corporation ============//
+#pragma once
+
+#include <string>
+
+std::string GetEnvironmentVariable( const char *pchVarName );
+bool GetEnvironmentVariableAsBool( const char *pchVarName, bool bDefault );
+bool SetEnvironmentVariable( const char *pchVarName, const char *pchVarValue );
diff --git a/gfx/vr/service/openvr/src/hmderrors_public.cpp b/gfx/vr/service/openvr/src/hmderrors_public.cpp
new file mode 100644
index 0000000000..e02c370cb1
--- /dev/null
+++ b/gfx/vr/service/openvr/src/hmderrors_public.cpp
@@ -0,0 +1,326 @@
+//========= Copyright Valve Corporation ============//
+#include "openvr.h"
+#include "hmderrors_public.h"
+#include <stdio.h>
+#include <algorithm>
+
+using namespace vr;
+
+#define RETURN_ENUM_AS_STRING(enumValue) case enumValue: return #enumValue;
+
+
+const char *GetEnglishStringForHmdError( vr::EVRInitError eError )
+{
+ switch( eError )
+ {
+ case VRInitError_None: return "No Error (0)";
+
+ case VRInitError_Init_InstallationNotFound: return "Installation Not Found (100)";
+ case VRInitError_Init_InstallationCorrupt: return "Installation Corrupt (101)";
+ case VRInitError_Init_VRClientDLLNotFound: return "vrclient Shared Lib Not Found (102)";
+ case VRInitError_Init_FileNotFound: return "File Not Found (103)";
+ case VRInitError_Init_FactoryNotFound: return "Factory Function Not Found (104)";
+ case VRInitError_Init_InterfaceNotFound: return "Interface Not Found (105)";
+ case VRInitError_Init_InvalidInterface: return "Invalid Interface (106)";
+ case VRInitError_Init_UserConfigDirectoryInvalid: return "User Config Directory Invalid (107)";
+ case VRInitError_Init_HmdNotFound: return "Hmd Not Found (108)";
+ case VRInitError_Init_NotInitialized: return "Not Initialized (109)";
+ case VRInitError_Init_PathRegistryNotFound: return "Installation path could not be located (110)";
+ case VRInitError_Init_NoConfigPath: return "Config path could not be located (111)";
+ case VRInitError_Init_NoLogPath: return "Log path could not be located (112)";
+ case VRInitError_Init_PathRegistryNotWritable: return "Unable to write path registry (113)";
+ case VRInitError_Init_AppInfoInitFailed: return "App info manager init failed (114)";
+ case VRInitError_Init_Retry: return "Internal Retry (115)";
+ case VRInitError_Init_InitCanceledByUser: return "User Canceled Init (116)";
+ case VRInitError_Init_AnotherAppLaunching: return "Another app was already launching (117)";
+ case VRInitError_Init_SettingsInitFailed: return "Settings manager init failed (118)";
+ case VRInitError_Init_ShuttingDown: return "VR system shutting down (119)";
+ case VRInitError_Init_TooManyObjects: return "Too many tracked objects (120)";
+ case VRInitError_Init_NoServerForBackgroundApp: return "Not starting vrserver for background app (121)";
+ case VRInitError_Init_NotSupportedWithCompositor: return "The requested interface is incompatible with the compositor and the compositor is running (122)";
+ case VRInitError_Init_NotAvailableToUtilityApps: return "This interface is not available to utility applications (123)";
+ case VRInitError_Init_Internal: return "vrserver internal error (124)";
+ case VRInitError_Init_HmdDriverIdIsNone: return "Hmd DriverId is invalid (125)";
+ case VRInitError_Init_HmdNotFoundPresenceFailed: return "Hmd Not Found Presence Failed (126)";
+ case VRInitError_Init_VRMonitorNotFound: return "VR Monitor Not Found (127)";
+ case VRInitError_Init_VRMonitorStartupFailed: return "VR Monitor startup failed (128)";
+ case VRInitError_Init_LowPowerWatchdogNotSupported: return "Low Power Watchdog Not Supported (129)";
+ case VRInitError_Init_InvalidApplicationType: return "Invalid Application Type (130)";
+ case VRInitError_Init_NotAvailableToWatchdogApps: return "Not available to watchdog apps (131)";
+ case VRInitError_Init_WatchdogDisabledInSettings: return "Watchdog disabled in settings (132)";
+ case VRInitError_Init_VRDashboardNotFound: return "VR Dashboard Not Found (133)";
+ case VRInitError_Init_VRDashboardStartupFailed: return "VR Dashboard startup failed (134)";
+ case VRInitError_Init_VRHomeNotFound: return "VR Home Not Found (135)";
+ case VRInitError_Init_VRHomeStartupFailed: return "VR home startup failed (136)";
+ case VRInitError_Init_RebootingBusy: return "Rebooting In Progress (137)";
+ case VRInitError_Init_FirmwareUpdateBusy: return "Firmware Update In Progress (138)";
+ case VRInitError_Init_FirmwareRecoveryBusy: return "Firmware Recovery In Progress (139)";
+ case VRInitError_Init_USBServiceBusy: return "USB Service Busy (140)";
+
+ case VRInitError_Driver_Failed: return "Driver Failed (200)";
+ case VRInitError_Driver_Unknown: return "Driver Not Known (201)";
+ case VRInitError_Driver_HmdUnknown: return "HMD Not Known (202)";
+ case VRInitError_Driver_NotLoaded: return "Driver Not Loaded (203)";
+ case VRInitError_Driver_RuntimeOutOfDate: return "Driver runtime is out of date (204)";
+ case VRInitError_Driver_HmdInUse: return "HMD already in use by another application (205)";
+ case VRInitError_Driver_NotCalibrated: return "Device is not calibrated (206)";
+ case VRInitError_Driver_CalibrationInvalid: return "Device Calibration is invalid (207)";
+ case VRInitError_Driver_HmdDisplayNotFound: return "HMD detected over USB, but Monitor not found (208)";
+ case VRInitError_Driver_TrackedDeviceInterfaceUnknown: return "Driver Tracked Device Interface unknown (209)";
+ // case VRInitError_Driver_HmdDisplayNotFoundAfterFix: return "HMD detected over USB, but Monitor not found after attempt to fix (210)"; // taken out upon Ben's request: He thinks that there is no need to separate that error from 208
+ case VRInitError_Driver_HmdDriverIdOutOfBounds: return "Hmd DriverId is our of bounds (211)";
+ case VRInitError_Driver_HmdDisplayMirrored: return "HMD detected over USB, but Monitor may be mirrored instead of extended (212)";
+ case VRInitError_Driver_HmdDisplayNotFoundLaptop: return "On laptop, HMD detected over USB, but Monitor not found (213)";
+
+ case VRInitError_IPC_ServerInitFailed: return "VR Server Init Failed (300)";
+ case VRInitError_IPC_ConnectFailed: return "Connect to VR Server Failed (301)";
+ case VRInitError_IPC_SharedStateInitFailed: return "Shared IPC State Init Failed (302)";
+ case VRInitError_IPC_CompositorInitFailed: return "Shared IPC Compositor Init Failed (303)";
+ case VRInitError_IPC_MutexInitFailed: return "Shared IPC Mutex Init Failed (304)";
+ case VRInitError_IPC_Failed: return "Shared IPC Failed (305)";
+ case VRInitError_IPC_CompositorConnectFailed: return "Shared IPC Compositor Connect Failed (306)";
+ case VRInitError_IPC_CompositorInvalidConnectResponse: return "Shared IPC Compositor Invalid Connect Response (307)";
+ case VRInitError_IPC_ConnectFailedAfterMultipleAttempts: return "Shared IPC Connect Failed After Multiple Attempts (308)";
+ case VRInitError_IPC_ConnectFailedAfterTargetExited: return "Shared IPC Connect Failed After Target Exited (309)";
+ case VRInitError_IPC_NamespaceUnavailable: return "Shared IPC Namespace Unavailable (310)";
+
+ case VRInitError_Compositor_Failed: return "Compositor failed to initialize (400)";
+ case VRInitError_Compositor_D3D11HardwareRequired: return "Compositor failed to find DX11 hardware (401)";
+ case VRInitError_Compositor_FirmwareRequiresUpdate: return "Compositor requires mandatory firmware update (402)";
+ case VRInitError_Compositor_OverlayInitFailed: return "Compositor initialization succeeded, but overlay init failed (403)";
+ case VRInitError_Compositor_ScreenshotsInitFailed: return "Compositor initialization succeeded, but screenshot init failed (404)";
+ case VRInitError_Compositor_UnableToCreateDevice: return "Compositor unable to create graphics device (405)";
+
+ // Oculus
+ case VRInitError_VendorSpecific_UnableToConnectToOculusRuntime: return "Unable to connect to Oculus Runtime (1000)";
+ case VRInitError_VendorSpecific_OculusRuntimeBadInstall: return "Unable to connect to Oculus Runtime, possible bad install (1114)";
+
+ // Lighthouse
+ case VRInitError_VendorSpecific_HmdFound_CantOpenDevice: return "HMD found, but can not open device (1101)";
+ case VRInitError_VendorSpecific_HmdFound_UnableToRequestConfigStart: return "HMD found, but unable to request config (1102)";
+ case VRInitError_VendorSpecific_HmdFound_NoStoredConfig: return "HMD found, but no stored config (1103)";
+ case VRInitError_VendorSpecific_HmdFound_ConfigFailedSanityCheck: return "HMD found, but failed configuration check (1113)";
+ case VRInitError_VendorSpecific_HmdFound_ConfigTooBig: return "HMD found, but config too big (1104)";
+ case VRInitError_VendorSpecific_HmdFound_ConfigTooSmall: return "HMD found, but config too small (1105)";
+ case VRInitError_VendorSpecific_HmdFound_UnableToInitZLib: return "HMD found, but unable to init ZLib (1106)";
+ case VRInitError_VendorSpecific_HmdFound_CantReadFirmwareVersion: return "HMD found, but problems with the data (1107)";
+ case VRInitError_VendorSpecific_HmdFound_UnableToSendUserDataStart: return "HMD found, but problems with the data (1108)";
+ case VRInitError_VendorSpecific_HmdFound_UnableToGetUserDataStart: return "HMD found, but problems with the data (1109)";
+ case VRInitError_VendorSpecific_HmdFound_UnableToGetUserDataNext: return "HMD found, but problems with the data (1110)";
+ case VRInitError_VendorSpecific_HmdFound_UserDataAddressRange: return "HMD found, but problems with the data (1111)";
+ case VRInitError_VendorSpecific_HmdFound_UserDataError: return "HMD found, but problems with the data (1112)";
+
+ case VRInitError_Steam_SteamInstallationNotFound: return "Unable to find Steam installation (2000)";
+
+ default:
+ return GetIDForVRInitError( eError );
+ }
+
+}
+
+
+const char *GetIDForVRInitError( vr::EVRInitError eError )
+{
+ switch( eError )
+ {
+ RETURN_ENUM_AS_STRING( VRInitError_None );
+ RETURN_ENUM_AS_STRING( VRInitError_Unknown );
+
+ RETURN_ENUM_AS_STRING( VRInitError_Init_InstallationNotFound );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_InstallationCorrupt );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_VRClientDLLNotFound );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_FileNotFound );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_FactoryNotFound );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_InterfaceNotFound );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_InvalidInterface );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_UserConfigDirectoryInvalid );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_HmdNotFound );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_NotInitialized );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_PathRegistryNotFound );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_NoConfigPath );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_NoLogPath );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_PathRegistryNotWritable );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_AppInfoInitFailed );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_Retry );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_InitCanceledByUser );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_AnotherAppLaunching );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_SettingsInitFailed );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_ShuttingDown );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_TooManyObjects );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_NoServerForBackgroundApp );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_NotSupportedWithCompositor );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_NotAvailableToUtilityApps );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_Internal );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_HmdDriverIdIsNone );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_HmdNotFoundPresenceFailed );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_VRMonitorNotFound );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_VRMonitorStartupFailed );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_LowPowerWatchdogNotSupported );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_InvalidApplicationType );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_NotAvailableToWatchdogApps );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_WatchdogDisabledInSettings );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_VRDashboardNotFound );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_VRDashboardStartupFailed );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_VRHomeNotFound );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_VRHomeStartupFailed );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_RebootingBusy );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_FirmwareUpdateBusy );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_FirmwareRecoveryBusy );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_USBServiceBusy );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_VRWebHelperStartupFailed );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_TrackerManagerInitFailed );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_AlreadyRunning );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_FailedForVrMonitor);
+ RETURN_ENUM_AS_STRING( VRInitError_Init_PropertyManagerInitFailed );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_WebServerFailed );
+
+ RETURN_ENUM_AS_STRING( VRInitError_Driver_Failed );
+ RETURN_ENUM_AS_STRING( VRInitError_Driver_Unknown );
+ RETURN_ENUM_AS_STRING( VRInitError_Driver_HmdUnknown);
+ RETURN_ENUM_AS_STRING( VRInitError_Driver_NotLoaded);
+ RETURN_ENUM_AS_STRING( VRInitError_Driver_RuntimeOutOfDate);
+ RETURN_ENUM_AS_STRING( VRInitError_Driver_HmdInUse);
+ RETURN_ENUM_AS_STRING( VRInitError_Driver_NotCalibrated);
+ RETURN_ENUM_AS_STRING( VRInitError_Driver_CalibrationInvalid);
+ RETURN_ENUM_AS_STRING( VRInitError_Driver_HmdDisplayNotFound);
+ RETURN_ENUM_AS_STRING( VRInitError_Driver_TrackedDeviceInterfaceUnknown );
+ // RETURN_ENUM_AS_STRING( VRInitError_Driver_HmdDisplayNotFoundAfterFix );
+ RETURN_ENUM_AS_STRING( VRInitError_Driver_HmdDriverIdOutOfBounds );
+ RETURN_ENUM_AS_STRING( VRInitError_Driver_HmdDisplayMirrored );
+ RETURN_ENUM_AS_STRING( VRInitError_Driver_HmdDisplayNotFoundLaptop );
+
+ RETURN_ENUM_AS_STRING( VRInitError_IPC_ServerInitFailed);
+ RETURN_ENUM_AS_STRING( VRInitError_IPC_ConnectFailed);
+ RETURN_ENUM_AS_STRING( VRInitError_IPC_SharedStateInitFailed);
+ RETURN_ENUM_AS_STRING( VRInitError_IPC_CompositorInitFailed);
+ RETURN_ENUM_AS_STRING( VRInitError_IPC_MutexInitFailed);
+ RETURN_ENUM_AS_STRING( VRInitError_IPC_Failed);
+ RETURN_ENUM_AS_STRING( VRInitError_IPC_CompositorConnectFailed);
+ RETURN_ENUM_AS_STRING( VRInitError_IPC_CompositorInvalidConnectResponse);
+ RETURN_ENUM_AS_STRING( VRInitError_IPC_ConnectFailedAfterMultipleAttempts );
+ RETURN_ENUM_AS_STRING( VRInitError_IPC_ConnectFailedAfterTargetExited );
+ RETURN_ENUM_AS_STRING( VRInitError_IPC_NamespaceUnavailable );
+
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_Failed );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_D3D11HardwareRequired );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_FirmwareRequiresUpdate );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_OverlayInitFailed );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_ScreenshotsInitFailed );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_UnableToCreateDevice );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_SharedStateIsNull );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_NotificationManagerIsNull );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_ResourceManagerClientIsNull );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_MessageOverlaySharedStateInitFailure );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_PropertiesInterfaceIsNull );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateFullscreenWindowFailed );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_SettingsInterfaceIsNull );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedToShowWindow );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_DistortInterfaceIsNull );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_DisplayFrequencyFailure );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_RendererInitializationFailed );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_DXGIFactoryInterfaceIsNull );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_DXGIFactoryCreateFailed );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_DXGIFactoryQueryFailed );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_InvalidAdapterDesktop );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_InvalidHmdAttachment );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_InvalidOutputDesktop );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_InvalidDeviceProvided );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_D3D11RendererInitializationFailed );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedToFindDisplayMode );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedToCreateSwapChain );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedToGetBackBuffer );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedToCreateRenderTarget );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedToCreateDXGI2SwapChain );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedtoGetDXGI2BackBuffer );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedToCreateDXGI2RenderTarget );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedToGetDXGIDeviceInterface );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_SelectDisplayMode );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedToCreateNvAPIRenderTargets );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_NvAPISetDisplayMode );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedToCreateDirectModeDisplay );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_InvalidHmdPropertyContainer );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_UpdateDisplayFrequency );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateRasterizerState );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateWireframeRasterizerState );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateSamplerState );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateClampToBorderSamplerState );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateAnisoSamplerState );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateOverlaySamplerState );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreatePanoramaSamplerState );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateFontSamplerState );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateNoBlendState );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateBlendState );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateAlphaBlendState );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateBlendStateMaskR );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateBlendStateMaskG );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateBlendStateMaskB );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateDepthStencilState );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateDepthStencilStateNoWrite );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateDepthStencilStateNoDepth );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateFlushTexture );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateDistortionSurfaces );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateConstantBuffer );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateHmdPoseConstantBuffer );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateHmdPoseStagingConstantBuffer );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateSharedFrameInfoConstantBuffer );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateOverlayConstantBuffer );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateSceneTextureIndexConstantBuffer );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateReadableSceneTextureIndexConstantBuffer );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateLayerGraphicsTextureIndexConstantBuffer );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateLayerComputeTextureIndexConstantBuffer );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateLayerComputeSceneTextureIndexConstantBuffer );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateComputeHmdPoseConstantBuffer );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateGeomConstantBuffer );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreatePanelMaskConstantBuffer );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreatePixelSimUBO );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateMSAARenderTextures );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateResolveRenderTextures );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateComputeResolveRenderTextures );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateDriverDirectModeResolveTextures );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_OpenDriverDirectModeResolveTextures );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateFallbackSyncTexture );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_ShareFallbackSyncTexture );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateOverlayIndexBuffer );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateOverlayVertexBuffer );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateTextVertexBuffer );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateTextIndexBuffer );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateMirrorTextures );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateLastFrameRenderTexture );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateMirrorOverlay );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedToCreateVirtualDisplayBackbuffer );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_DisplayModeNotSupported );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateOverlayInvalidCall );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateOverlayAlreadyInitialized );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedToCreateMailbox );
+
+ // Vendor-specific errors
+ RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_UnableToConnectToOculusRuntime);
+ RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_WindowsNotInDevMode );
+ RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_OculusRuntimeBadInstall );
+
+ // Lighthouse
+ RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_CantOpenDevice);
+ RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_UnableToRequestConfigStart);
+ RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_NoStoredConfig);
+ RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_ConfigFailedSanityCheck );
+ RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_ConfigTooBig );
+ RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_ConfigTooSmall );
+ RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_UnableToInitZLib );
+ RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_CantReadFirmwareVersion );
+ RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_UnableToSendUserDataStart );
+ RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_UnableToGetUserDataStart );
+ RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_UnableToGetUserDataNext );
+ RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_UserDataAddressRange );
+ RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_UserDataError );
+
+ RETURN_ENUM_AS_STRING( VRInitError_Steam_SteamInstallationNotFound );
+
+ default:
+ {
+ static char buf[128];
+ sprintf( buf, "Unknown error (%d)", eError );
+ return buf;
+ }
+ }
+}
+
diff --git a/gfx/vr/service/openvr/src/hmderrors_public.h b/gfx/vr/service/openvr/src/hmderrors_public.h
new file mode 100644
index 0000000000..ccd6c8a96c
--- /dev/null
+++ b/gfx/vr/service/openvr/src/hmderrors_public.h
@@ -0,0 +1,6 @@
+//========= Copyright Valve Corporation ============//
+#pragma once
+
+const char *GetEnglishStringForHmdError( vr::EVRInitError eError );
+const char *GetIDForVRInitError( vr::EVRInitError eError );
+
diff --git a/gfx/vr/service/openvr/src/ivrclientcore.h b/gfx/vr/service/openvr/src/ivrclientcore.h
new file mode 100644
index 0000000000..6884e7fbc8
--- /dev/null
+++ b/gfx/vr/service/openvr/src/ivrclientcore.h
@@ -0,0 +1,35 @@
+//========= Copyright Valve Corporation ============//
+#pragma once
+
+namespace vr
+{
+
+class IVRClientCore
+{
+public:
+ /** Initializes the system */
+ virtual EVRInitError Init( vr::EVRApplicationType eApplicationType, const char *pStartupInfo ) = 0;
+
+ /** cleans up everything in vrclient.dll and prepares the DLL to be unloaded */
+ virtual void Cleanup() = 0;
+
+ /** checks to see if the specified interface/version is supported in this vrclient.dll */
+ virtual EVRInitError IsInterfaceVersionValid( const char *pchInterfaceVersion ) = 0;
+
+ /** Retrieves any interface from vrclient.dll */
+ virtual void *GetGenericInterface( const char *pchNameAndVersion, EVRInitError *peError ) = 0;
+
+ /** Returns true if any driver has an HMD attached. Can be called outside of Init/Cleanup */
+ virtual bool BIsHmdPresent() = 0;
+
+ /** Returns an English error string from inside vrclient.dll which might be newer than the API DLL */
+ virtual const char *GetEnglishStringForHmdError( vr::EVRInitError eError ) = 0;
+
+ /** Returns an error symbol from inside vrclient.dll which might be newer than the API DLL */
+ virtual const char *GetIDForVRInitError( vr::EVRInitError eError ) = 0;
+};
+
+static const char * const IVRClientCore_Version = "IVRClientCore_003";
+
+
+}
diff --git a/gfx/vr/service/openvr/src/openvr_api_public.cpp b/gfx/vr/service/openvr/src/openvr_api_public.cpp
new file mode 100644
index 0000000000..54aa555955
--- /dev/null
+++ b/gfx/vr/service/openvr/src/openvr_api_public.cpp
@@ -0,0 +1,352 @@
+//========= Copyright Valve Corporation ============//
+#define VR_API_EXPORT 1
+#include "openvr.h"
+#include "ivrclientcore.h"
+#include "pathtools_public.h"
+#include "sharedlibtools_public.h"
+#include "envvartools_public.h"
+#include "hmderrors_public.h"
+#include "strtools_public.h"
+#include "vrpathregistry_public.h"
+#include <mutex>
+
+using vr::EVRInitError;
+using vr::IVRSystem;
+using vr::IVRClientCore;
+using vr::VRInitError_None;
+
+// figure out how to import from the VR API dll
+#if defined(_WIN32)
+
+#if !defined(OPENVR_BUILD_STATIC)
+#define VR_EXPORT_INTERFACE extern "C" __declspec( dllexport )
+#else
+#define VR_EXPORT_INTERFACE extern "C"
+#endif
+
+#elif defined(__GNUC__) || defined(COMPILER_GCC) || defined(__APPLE__)
+
+#define VR_EXPORT_INTERFACE extern "C" __attribute__((visibility("default")))
+
+#else
+#error "Unsupported Platform."
+#endif
+
+namespace vr
+{
+
+static void *g_pVRModule = NULL;
+static IVRClientCore *g_pHmdSystem = NULL;
+static std::recursive_mutex g_mutexSystem;
+
+
+typedef void* (*VRClientCoreFactoryFn)(const char *pInterfaceName, int *pReturnCode);
+
+static uint32_t g_nVRToken = 0;
+
+uint32_t VR_GetInitToken()
+{
+ return g_nVRToken;
+}
+
+EVRInitError VR_LoadHmdSystemInternal();
+void CleanupInternalInterfaces();
+
+
+uint32_t VR_InitInternal2( EVRInitError *peError, vr::EVRApplicationType eApplicationType, const char *pStartupInfo )
+{
+ std::lock_guard<std::recursive_mutex> lock( g_mutexSystem );
+
+ EVRInitError err = VR_LoadHmdSystemInternal();
+ if ( err == vr::VRInitError_None )
+ {
+ err = g_pHmdSystem->Init( eApplicationType, pStartupInfo );
+ }
+
+ if ( peError )
+ *peError = err;
+
+ if ( err != VRInitError_None )
+ {
+ SharedLib_Unload( g_pVRModule );
+ g_pHmdSystem = NULL;
+ g_pVRModule = NULL;
+
+ return 0;
+ }
+
+ return ++g_nVRToken;
+}
+
+VR_INTERFACE uint32_t VR_CALLTYPE VR_InitInternal( EVRInitError *peError, EVRApplicationType eApplicationType );
+
+uint32_t VR_InitInternal( EVRInitError *peError, vr::EVRApplicationType eApplicationType )
+{
+ return VR_InitInternal2( peError, eApplicationType, nullptr );
+}
+
+void VR_ShutdownInternal()
+{
+ std::lock_guard<std::recursive_mutex> lock( g_mutexSystem );
+
+ if ( g_pHmdSystem )
+ {
+ g_pHmdSystem->Cleanup();
+ g_pHmdSystem = NULL;
+ }
+ if ( g_pVRModule )
+ {
+ SharedLib_Unload( g_pVRModule );
+ g_pVRModule = NULL;
+ }
+
+#if !defined( VR_API_PUBLIC )
+ CleanupInternalInterfaces();
+#endif
+
+ ++g_nVRToken;
+}
+
+EVRInitError VR_LoadHmdSystemInternal()
+{
+ std::string sRuntimePath, sConfigPath, sLogPath;
+
+ bool bReadPathRegistry = CVRPathRegistry_Public::GetPaths( &sRuntimePath, &sConfigPath, &sLogPath, NULL, NULL );
+ if( !bReadPathRegistry )
+ {
+ return vr::VRInitError_Init_PathRegistryNotFound;
+ }
+
+ // figure out where we're going to look for vrclient.dll
+ // see if the specified path actually exists.
+ if( !Path_IsDirectory( sRuntimePath ) )
+ {
+ return vr::VRInitError_Init_InstallationNotFound;
+ }
+
+ // Because we don't have a way to select debug vs. release yet we'll just
+ // use debug if it's there
+#if defined( LINUX64 ) || defined( LINUXARM64 )
+ std::string sTestPath = Path_Join( sRuntimePath, "bin", PLATSUBDIR );
+#else
+ std::string sTestPath = Path_Join( sRuntimePath, "bin" );
+#endif
+ if( !Path_IsDirectory( sTestPath ) )
+ {
+ return vr::VRInitError_Init_InstallationCorrupt;
+ }
+
+#if defined( WIN64 )
+ std::string sDLLPath = Path_Join( sTestPath, "vrclient_x64" DYNAMIC_LIB_EXT );
+#else
+ std::string sDLLPath = Path_Join( sTestPath, "vrclient" DYNAMIC_LIB_EXT );
+#endif
+
+ // only look in the override
+ void *pMod = SharedLib_Load( sDLLPath.c_str() );
+ // nothing more to do if we can't load the DLL
+ if( !pMod )
+ {
+ return vr::VRInitError_Init_VRClientDLLNotFound;
+ }
+
+ VRClientCoreFactoryFn fnFactory = ( VRClientCoreFactoryFn )( SharedLib_GetFunction( pMod, "VRClientCoreFactory" ) );
+ if( !fnFactory )
+ {
+ SharedLib_Unload( pMod );
+ return vr::VRInitError_Init_FactoryNotFound;
+ }
+
+ int nReturnCode = 0;
+ g_pHmdSystem = static_cast< IVRClientCore * > ( fnFactory( vr::IVRClientCore_Version, &nReturnCode ) );
+ if( !g_pHmdSystem )
+ {
+ SharedLib_Unload( pMod );
+ return vr::VRInitError_Init_InterfaceNotFound;
+ }
+
+ g_pVRModule = pMod;
+ return VRInitError_None;
+}
+
+
+void *VR_GetGenericInterface(const char *pchInterfaceVersion, EVRInitError *peError)
+{
+ std::lock_guard<std::recursive_mutex> lock( g_mutexSystem );
+
+ if (!g_pHmdSystem)
+ {
+ if (peError)
+ *peError = vr::VRInitError_Init_NotInitialized;
+ return NULL;
+ }
+
+ return g_pHmdSystem->GetGenericInterface(pchInterfaceVersion, peError);
+}
+
+bool VR_IsInterfaceVersionValid(const char *pchInterfaceVersion)
+{
+ std::lock_guard<std::recursive_mutex> lock( g_mutexSystem );
+
+ if (!g_pHmdSystem)
+ {
+ return false;
+ }
+
+ return g_pHmdSystem->IsInterfaceVersionValid(pchInterfaceVersion) == VRInitError_None;
+}
+
+bool VR_IsHmdPresent()
+{
+ std::lock_guard<std::recursive_mutex> lock( g_mutexSystem );
+
+ if( g_pHmdSystem )
+ {
+ // if we're already initialized, just call through
+ return g_pHmdSystem->BIsHmdPresent();
+ }
+ else
+ {
+ // otherwise we need to do a bit more work
+ EVRInitError err = VR_LoadHmdSystemInternal();
+ if( err != VRInitError_None )
+ return false;
+
+ bool bHasHmd = g_pHmdSystem->BIsHmdPresent();
+
+ g_pHmdSystem = NULL;
+ SharedLib_Unload( g_pVRModule );
+ g_pVRModule = NULL;
+
+ return bHasHmd;
+ }
+}
+
+/** Returns true if the OpenVR runtime is installed. */
+bool VR_IsRuntimeInstalled()
+{
+ std::lock_guard<std::recursive_mutex> lock( g_mutexSystem );
+
+ if( g_pHmdSystem )
+ {
+ // if we're already initialized, OpenVR is obviously installed
+ return true;
+ }
+ else
+ {
+ // otherwise we need to do a bit more work
+ std::string sRuntimePath, sConfigPath, sLogPath;
+
+ bool bReadPathRegistry = CVRPathRegistry_Public::GetPaths( &sRuntimePath, &sConfigPath, &sLogPath, NULL, NULL );
+ if( !bReadPathRegistry )
+ {
+ return false;
+ }
+
+ // figure out where we're going to look for vrclient.dll
+ // see if the specified path actually exists.
+ if( !Path_IsDirectory( sRuntimePath ) )
+ {
+ return false;
+ }
+
+ // the installation may be corrupt in some way, but it certainly looks installed
+ return true;
+ }
+}
+
+
+// -------------------------------------------------------------------------------
+// Purpose: This is the old Runtime Path interface that is no longer exported in the
+// latest header. We still want to export it from the DLL, though, so updating
+// to a new DLL doesn't break old compiled code. This version was not thread
+// safe and could change the buffer pointer to by a previous result on a
+// subsequent call
+// -------------------------------------------------------------------------------
+VR_EXPORT_INTERFACE const char *VR_CALLTYPE VR_RuntimePath();
+
+/** Returns where OpenVR runtime is installed. */
+const char *VR_RuntimePath()
+{
+ static char rchBuffer[1024];
+ uint32_t unRequiredSize;
+ if ( VR_GetRuntimePath( rchBuffer, sizeof( rchBuffer ), &unRequiredSize ) && unRequiredSize < sizeof( rchBuffer ) )
+ {
+ return rchBuffer;
+ }
+ else
+ {
+ return nullptr;
+ }
+}
+
+
+/** Returns where OpenVR runtime is installed. */
+bool VR_GetRuntimePath( char *pchPathBuffer, uint32_t unBufferSize, uint32_t *punRequiredBufferSize )
+{
+ // otherwise we need to do a bit more work
+ std::string sRuntimePath;
+
+ *punRequiredBufferSize = 0;
+
+ bool bReadPathRegistry = CVRPathRegistry_Public::GetPaths( &sRuntimePath, nullptr, nullptr, nullptr, nullptr );
+ if ( !bReadPathRegistry )
+ {
+ return false;
+ }
+
+ // figure out where we're going to look for vrclient.dll
+ // see if the specified path actually exists.
+ if ( !Path_IsDirectory( sRuntimePath ) )
+ {
+ return false;
+ }
+
+ *punRequiredBufferSize = (uint32_t)sRuntimePath.size() + 1;
+ if ( sRuntimePath.size() >= unBufferSize )
+ {
+ *pchPathBuffer = '\0';
+ }
+ else
+ {
+ strcpy_safe( pchPathBuffer, unBufferSize, sRuntimePath.c_str() );
+ }
+
+ return true;
+}
+
+
+/** Returns the symbol version of an HMD error. */
+const char *VR_GetVRInitErrorAsSymbol( EVRInitError error )
+{
+ std::lock_guard<std::recursive_mutex> lock( g_mutexSystem );
+
+ if( g_pHmdSystem )
+ return g_pHmdSystem->GetIDForVRInitError( error );
+ else
+ return GetIDForVRInitError( error );
+}
+
+
+/** Returns the english string version of an HMD error. */
+const char *VR_GetVRInitErrorAsEnglishDescription( EVRInitError error )
+{
+ std::lock_guard<std::recursive_mutex> lock( g_mutexSystem );
+
+ if ( g_pHmdSystem )
+ return g_pHmdSystem->GetEnglishStringForHmdError( error );
+ else
+ return GetEnglishStringForHmdError( error );
+}
+
+
+VR_INTERFACE const char *VR_CALLTYPE VR_GetStringForHmdError( vr::EVRInitError error );
+
+/** Returns the english string version of an HMD error. */
+const char *VR_GetStringForHmdError( EVRInitError error )
+{
+ return VR_GetVRInitErrorAsEnglishDescription( error );
+}
+
+}
+
diff --git a/gfx/vr/service/openvr/src/pathtools_public.cpp b/gfx/vr/service/openvr/src/pathtools_public.cpp
new file mode 100644
index 0000000000..e7f6d6ca1b
--- /dev/null
+++ b/gfx/vr/service/openvr/src/pathtools_public.cpp
@@ -0,0 +1,901 @@
+//========= Copyright Valve Corporation ============//
+#include "strtools_public.h"
+#include "pathtools_public.h"
+
+#if defined( _WIN32)
+#include <windows.h>
+#include <direct.h>
+#include <shobjidl.h>
+#include <knownfolders.h>
+#include <shlobj.h>
+#include <share.h>
+
+#undef GetEnvironmentVariable
+#else
+#include <dlfcn.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <alloca.h>
+#endif
+
+#if defined OSX
+#include <Foundation/Foundation.h>
+#include <AppKit/AppKit.h>
+#include <mach-o/dyld.h>
+#define _S_IFDIR S_IFDIR // really from tier0/platform.h which we dont have yet
+#endif
+
+#include <sys/stat.h>
+
+#include <algorithm>
+
+/** Returns the path (including filename) to the current executable */
+std::string Path_GetExecutablePath()
+{
+#if defined( _WIN32 )
+ wchar_t *pwchPath = new wchar_t[MAX_UNICODE_PATH];
+ char *pchPath = new char[MAX_UNICODE_PATH_IN_UTF8];
+ ::GetModuleFileNameW( NULL, pwchPath, MAX_UNICODE_PATH );
+ WideCharToMultiByte( CP_UTF8, 0, pwchPath, -1, pchPath, MAX_UNICODE_PATH_IN_UTF8, NULL, NULL );
+ delete[] pwchPath;
+
+ std::string sPath = pchPath;
+ delete[] pchPath;
+ return sPath;
+#elif defined( OSX )
+ char rchPath[1024];
+ uint32_t nBuff = sizeof( rchPath );
+ bool bSuccess = _NSGetExecutablePath(rchPath, &nBuff) == 0;
+ rchPath[nBuff-1] = '\0';
+ if( bSuccess )
+ return rchPath;
+ else
+ return "";
+#elif defined LINUX
+ char rchPath[1024];
+ size_t nBuff = sizeof( rchPath );
+ ssize_t nRead = readlink("/proc/self/exe", rchPath, nBuff-1 );
+ if ( nRead != -1 )
+ {
+ rchPath[ nRead ] = 0;
+ return rchPath;
+ }
+ else
+ {
+ return "";
+ }
+#else
+ AssertMsg( false, "Implement Plat_GetExecutablePath" );
+ return "";
+#endif
+
+}
+
+/** Returns the path of the current working directory */
+std::string Path_GetWorkingDirectory()
+{
+ std::string sPath;
+#if defined( _WIN32 )
+ wchar_t buf[MAX_UNICODE_PATH];
+ sPath = UTF16to8( _wgetcwd( buf, MAX_UNICODE_PATH ) );
+#else
+ char buf[ 1024 ];
+ sPath = getcwd( buf, sizeof( buf ) );
+#endif
+ return sPath;
+}
+
+/** Sets the path of the current working directory. Returns true if this was successful. */
+bool Path_SetWorkingDirectory( const std::string & sPath )
+{
+ bool bSuccess;
+#if defined( _WIN32 )
+ std::wstring wsPath = UTF8to16( sPath.c_str() );
+ bSuccess = 0 == _wchdir( wsPath.c_str() );
+#else
+ bSuccess = 0 == chdir( sPath.c_str() );
+#endif
+ return bSuccess;
+}
+
+/** Gets the path to a temporary directory. */
+std::string Path_GetTemporaryDirectory()
+{
+#if defined( _WIN32 )
+ wchar_t buf[MAX_UNICODE_PATH];
+ if ( GetTempPathW( MAX_UNICODE_PATH, buf ) == 0 )
+ return Path_GetWorkingDirectory();
+ return UTF16to8( buf );
+#else
+ const char *pchTmpDir = getenv( "TMPDIR" );
+ if ( pchTmpDir == NULL )
+ {
+ return "";
+ }
+ return pchTmpDir;
+#endif
+}
+
+/** Returns the specified path without its filename */
+std::string Path_StripFilename( const std::string & sPath, char slash )
+{
+ if( slash == 0 )
+ slash = Path_GetSlash();
+
+ std::string::size_type n = sPath.find_last_of( slash );
+ if( n == std::string::npos )
+ return sPath;
+ else
+ return std::string( sPath.begin(), sPath.begin() + n );
+}
+
+/** returns just the filename from the provided full or relative path. */
+std::string Path_StripDirectory( const std::string & sPath, char slash )
+{
+ if( slash == 0 )
+ slash = Path_GetSlash();
+
+ std::string::size_type n = sPath.find_last_of( slash );
+ if( n == std::string::npos )
+ return sPath;
+ else
+ return std::string( sPath.begin() + n + 1, sPath.end() );
+}
+
+/** returns just the filename with no extension of the provided filename.
+* If there is a path the path is left intact. */
+std::string Path_StripExtension( const std::string & sPath )
+{
+ for( std::string::const_reverse_iterator i = sPath.rbegin(); i != sPath.rend(); i++ )
+ {
+ if( *i == '.' )
+ {
+ return std::string( sPath.begin(), i.base() - 1 );
+ }
+
+ // if we find a slash there is no extension
+ if( *i == '\\' || *i == '/' )
+ break;
+ }
+
+ // we didn't find an extension
+ return sPath;
+}
+
+/** returns just extension of the provided filename (if any). */
+std::string Path_GetExtension( const std::string & sPath )
+{
+ for ( std::string::const_reverse_iterator i = sPath.rbegin(); i != sPath.rend(); i++ )
+ {
+ if ( *i == '.' )
+ {
+ return std::string( i.base(), sPath.end() );
+ }
+
+ // if we find a slash there is no extension
+ if ( *i == '\\' || *i == '/' )
+ break;
+ }
+
+ // we didn't find an extension
+ return "";
+}
+
+bool Path_IsAbsolute( const std::string & sPath )
+{
+ if( sPath.empty() )
+ return false;
+
+#if defined( WIN32 )
+ if ( sPath.size() < 3 ) // must be c:\x or \\x at least
+ return false;
+
+ if ( sPath[1] == ':' ) // drive letter plus slash, but must test both slash cases
+ {
+ if ( sPath[2] == '\\' || sPath[2] == '/' )
+ return true;
+ }
+ else if ( sPath[0] == '\\' && sPath[1] == '\\' ) // UNC path
+ return true;
+#else
+ if( sPath[0] == '\\' || sPath[0] == '/' ) // any leading slash
+ return true;
+#endif
+
+ return false;
+}
+
+
+/** Makes an absolute path from a relative path and a base path */
+std::string Path_MakeAbsolute( const std::string & sRelativePath, const std::string & sBasePath )
+{
+ if( Path_IsAbsolute( sRelativePath ) )
+ return Path_Compact( sRelativePath );
+ else
+ {
+ if( !Path_IsAbsolute( sBasePath ) )
+ return "";
+
+ std::string sCompacted = Path_Compact( Path_Join( sBasePath, sRelativePath ) );
+ if( Path_IsAbsolute( sCompacted ) )
+ return sCompacted;
+ else
+ return "";
+ }
+}
+
+
+/** Fixes the directory separators for the current platform */
+std::string Path_FixSlashes( const std::string & sPath, char slash )
+{
+ if( slash == 0 )
+ slash = Path_GetSlash();
+
+ std::string sFixed = sPath;
+ for( std::string::iterator i = sFixed.begin(); i != sFixed.end(); i++ )
+ {
+ if( *i == '/' || *i == '\\' )
+ *i = slash;
+ }
+
+ return sFixed;
+}
+
+
+char Path_GetSlash()
+{
+#if defined(_WIN32)
+ return '\\';
+#else
+ return '/';
+#endif
+}
+
+/** Jams two paths together with the right kind of slash */
+std::string Path_Join( const std::string & first, const std::string & second, char slash )
+{
+ if( slash == 0 )
+ slash = Path_GetSlash();
+
+ // only insert a slash if we don't already have one
+ std::string::size_type nLen = first.length();
+ if( !nLen )
+ return second;
+#if defined(_WIN32)
+ if( first.back() == '\\' || first.back() == '/' )
+ nLen--;
+#else
+ char last_char = first[first.length()-1];
+ if (last_char == '\\' || last_char == '/')
+ nLen--;
+#endif
+
+ return first.substr( 0, nLen ) + std::string( 1, slash ) + second;
+}
+
+
+std::string Path_Join( const std::string & first, const std::string & second, const std::string & third, char slash )
+{
+ return Path_Join( Path_Join( first, second, slash ), third, slash );
+}
+
+std::string Path_Join( const std::string & first, const std::string & second, const std::string & third, const std::string &fourth, char slash )
+{
+ return Path_Join( Path_Join( Path_Join( first, second, slash ), third, slash ), fourth, slash );
+}
+
+std::string Path_Join(
+ const std::string & first,
+ const std::string & second,
+ const std::string & third,
+ const std::string & fourth,
+ const std::string & fifth,
+ char slash )
+{
+ return Path_Join( Path_Join( Path_Join( Path_Join( first, second, slash ), third, slash ), fourth, slash ), fifth, slash );
+}
+
+
+std::string Path_RemoveTrailingSlash( const std::string & sRawPath, char slash )
+{
+ if ( slash == 0 )
+ slash = Path_GetSlash();
+
+ std::string sPath = sRawPath;
+ std::string::size_type nCurrent = sRawPath.length();
+ if ( nCurrent == 0 )
+ return sPath;
+
+ int nLastFound = -1;
+ nCurrent--;
+ while( nCurrent != 0 )
+ {
+ if ( sRawPath[ nCurrent ] == slash )
+ {
+ nLastFound = (int)nCurrent;
+ nCurrent--;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ if ( nLastFound >= 0 )
+ {
+ sPath.erase( nLastFound, std::string::npos );
+ }
+
+ return sPath;
+}
+
+
+/** Removes redundant <dir>/.. elements in the path. Returns an empty path if the
+* specified path has a broken number of directories for its number of ..s */
+std::string Path_Compact( const std::string & sRawPath, char slash )
+{
+ if( slash == 0 )
+ slash = Path_GetSlash();
+
+ std::string sPath = Path_FixSlashes( sRawPath, slash );
+ std::string sSlashString( 1, slash );
+
+ // strip out all /./
+ for( std::string::size_type i = 0; (i + 3) < sPath.length(); )
+ {
+ if( sPath[ i ] == slash && sPath[ i+1 ] == '.' && sPath[ i+2 ] == slash )
+ {
+ sPath.replace( i, 3, sSlashString );
+ }
+ else
+ {
+ ++i;
+ }
+ }
+
+
+ // get rid of trailing /. but leave the path separator
+ if( sPath.length() > 2 )
+ {
+ std::string::size_type len = sPath.length();
+ if( sPath[ len-1 ] == '.' && sPath[ len-2 ] == slash )
+ {
+ sPath.pop_back();
+ //Not sure why the following line of code was used for a while. It causes problems with strlen.
+ //sPath[len-1] = 0; // for now, at least
+ }
+ }
+
+ // get rid of leading ./
+ if( sPath.length() > 2 )
+ {
+ if( sPath[ 0 ] == '.' && sPath[ 1 ] == slash )
+ {
+ sPath.replace( 0, 2, "" );
+ }
+ }
+
+ // each time we encounter .. back up until we've found the previous directory name
+ // then get rid of both
+ std::string::size_type i = 0;
+ while( i < sPath.length() )
+ {
+ if( i > 0 && sPath.length() - i >= 2
+ && sPath[i] == '.'
+ && sPath[i+1] == '.'
+ && ( i + 2 == sPath.length() || sPath[ i+2 ] == slash )
+ && sPath[ i-1 ] == slash )
+ {
+ // check if we've hit the start of the string and have a bogus path
+ if( i == 1 )
+ return "";
+
+ // find the separator before i-1
+ std::string::size_type iDirStart = i-2;
+ while( iDirStart > 0 && sPath[ iDirStart - 1 ] != slash )
+ --iDirStart;
+
+ // remove everything from iDirStart to i+2
+ sPath.replace( iDirStart, (i - iDirStart) + 3, "" );
+
+ // start over
+ i = 0;
+ }
+ else
+ {
+ ++i;
+ }
+ }
+
+ return sPath;
+}
+
+
+/** Returns true if these two paths are the same without respect for internal . or ..
+* sequences, slash type, or case (on case-insensitive platforms). */
+bool Path_IsSamePath( const std::string & sPath1, const std::string & sPath2 )
+{
+ std::string sCompact1 = Path_Compact( sPath1 );
+ std::string sCompact2 = Path_Compact( sPath2 );
+#if defined(WIN32)
+ return !stricmp( sCompact1.c_str(), sCompact2.c_str() );
+#else
+ return !strcmp( sCompact1.c_str(), sCompact2.c_str() );
+#endif
+}
+
+
+/** Returns the path to the current DLL or exe */
+std::string Path_GetThisModulePath()
+{
+ // gets the path of vrclient.dll itself
+#ifdef WIN32
+ HMODULE hmodule = NULL;
+
+ ::GetModuleHandleEx( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, reinterpret_cast<LPCTSTR>(Path_GetThisModulePath), &hmodule );
+
+ wchar_t *pwchPath = new wchar_t[MAX_UNICODE_PATH];
+ char *pchPath = new char[ MAX_UNICODE_PATH_IN_UTF8 ];
+ ::GetModuleFileNameW( hmodule, pwchPath, MAX_UNICODE_PATH );
+ WideCharToMultiByte( CP_UTF8, 0, pwchPath, -1, pchPath, MAX_UNICODE_PATH_IN_UTF8, NULL, NULL );
+ delete[] pwchPath;
+
+ std::string sPath = pchPath;
+ delete [] pchPath;
+ return sPath;
+
+#elif defined( OSX ) || defined( LINUX )
+ // get the addr of a function in vrclient.so and then ask the dlopen system about it
+ Dl_info info;
+ dladdr( (void *)Path_GetThisModulePath, &info );
+ return info.dli_fname;
+#endif
+
+}
+
+
+/** returns true if the specified path exists and is a directory */
+bool Path_IsDirectory( const std::string & sPath )
+{
+ std::string sFixedPath = Path_FixSlashes( sPath );
+ if( sFixedPath.empty() )
+ return false;
+ char cLast = sFixedPath[ sFixedPath.length() - 1 ];
+ if( cLast == '/' || cLast == '\\' )
+ sFixedPath.erase( sFixedPath.end() - 1, sFixedPath.end() );
+
+ // see if the specified path actually exists.
+
+#if defined(POSIX)
+ struct stat buf;
+ if ( stat( sFixedPath.c_str(), &buf ) == -1 )
+ {
+ return false;
+ }
+
+#if defined( LINUX ) || defined( OSX )
+ return S_ISDIR( buf.st_mode );
+#else
+ return (buf.st_mode & _S_IFDIR) != 0;
+#endif
+
+#else
+ struct _stat buf;
+ std::wstring wsFixedPath = UTF8to16( sFixedPath.c_str() );
+ if ( _wstat( wsFixedPath.c_str(), &buf ) == -1 )
+ {
+ return false;
+ }
+
+ return (buf.st_mode & _S_IFDIR) != 0;
+#endif
+}
+
+/** returns true if the specified path represents an app bundle */
+bool Path_IsAppBundle( const std::string & sPath )
+{
+#if defined(OSX)
+ @autoreleasepool {
+ NSBundle *bundle = [ NSBundle bundleWithPath: [ NSString stringWithUTF8String:sPath.c_str() ] ];
+ bool bisAppBundle = ( nullptr != bundle );
+ return bisAppBundle;
+ }
+#else
+ return false;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns true if the the path exists
+//-----------------------------------------------------------------------------
+bool Path_Exists( const std::string & sPath )
+{
+ std::string sFixedPath = Path_FixSlashes( sPath );
+ if( sFixedPath.empty() )
+ return false;
+
+#if defined( WIN32 )
+ struct _stat buf;
+ std::wstring wsFixedPath = UTF8to16( sFixedPath.c_str() );
+ if ( _wstat( wsFixedPath.c_str(), &buf ) == -1 )
+ {
+ return false;
+ }
+#else
+ struct stat buf;
+ if ( stat ( sFixedPath.c_str(), &buf ) == -1)
+ {
+ return false;
+ }
+#endif
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: helper to find a directory upstream from a given path
+//-----------------------------------------------------------------------------
+std::string Path_FindParentDirectoryRecursively( const std::string &strStartDirectory, const std::string &strDirectoryName )
+{
+ std::string strFoundPath = "";
+ std::string strCurrentPath = Path_FixSlashes( strStartDirectory );
+ if ( strCurrentPath.length() == 0 )
+ return "";
+
+ bool bExists = Path_Exists( strCurrentPath );
+ std::string strCurrentDirectoryName = Path_StripDirectory( strCurrentPath );
+ if ( bExists && stricmp( strCurrentDirectoryName.c_str(), strDirectoryName.c_str() ) == 0 )
+ return strCurrentPath;
+
+ while( bExists && strCurrentPath.length() != 0 )
+ {
+ strCurrentPath = Path_StripFilename( strCurrentPath );
+ strCurrentDirectoryName = Path_StripDirectory( strCurrentPath );
+ bExists = Path_Exists( strCurrentPath );
+ if ( bExists && stricmp( strCurrentDirectoryName.c_str(), strDirectoryName.c_str() ) == 0 )
+ return strCurrentPath;
+ }
+
+ return "";
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: helper to find a subdirectory upstream from a given path
+//-----------------------------------------------------------------------------
+std::string Path_FindParentSubDirectoryRecursively( const std::string &strStartDirectory, const std::string &strDirectoryName )
+{
+ std::string strFoundPath = "";
+ std::string strCurrentPath = Path_FixSlashes( strStartDirectory );
+ if ( strCurrentPath.length() == 0 )
+ return "";
+
+ bool bExists = Path_Exists( strCurrentPath );
+ while( bExists && strCurrentPath.length() != 0 )
+ {
+ strCurrentPath = Path_StripFilename( strCurrentPath );
+ bExists = Path_Exists( strCurrentPath );
+
+ if( Path_Exists( Path_Join( strCurrentPath, strDirectoryName ) ) )
+ {
+ strFoundPath = Path_Join( strCurrentPath, strDirectoryName );
+ break;
+ }
+ }
+ return strFoundPath;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: reading and writing files in the vortex directory
+//-----------------------------------------------------------------------------
+unsigned char * Path_ReadBinaryFile( const std::string &strFilename, int *pSize )
+{
+ FILE *f;
+#if defined( POSIX )
+ f = fopen( strFilename.c_str(), "rb" );
+#else
+ std::wstring wstrFilename = UTF8to16( strFilename.c_str() );
+ // the open operation needs to be sharable, therefore use of _wfsopen instead of _wfopen_s
+ f = _wfsopen( wstrFilename.c_str(), L"rb", _SH_DENYNO );
+#endif
+
+ unsigned char* buf = NULL;
+
+ if ( f != NULL )
+ {
+ fseek(f, 0, SEEK_END);
+ int size = ftell(f);
+ fseek(f, 0, SEEK_SET);
+
+ buf = new unsigned char[size];
+ if (buf && fread(buf, size, 1, f) == 1)
+ {
+ if (pSize)
+ *pSize = size;
+ }
+ else
+ {
+ delete[] buf;
+ buf = 0;
+ }
+
+ fclose(f);
+ }
+
+ return buf;
+}
+
+uint32_t Path_ReadBinaryFile( const std::string &strFilename, unsigned char *pBuffer, uint32_t unSize )
+{
+ FILE *f;
+#if defined( POSIX )
+ f = fopen( strFilename.c_str(), "rb" );
+#else
+ std::wstring wstrFilename = UTF8to16( strFilename.c_str() );
+ errno_t err = _wfopen_s( &f, wstrFilename.c_str(), L"rb" );
+ if ( err != 0 )
+ {
+ f = NULL;
+ }
+#endif
+
+ uint32_t unSizeToReturn = 0;
+
+ if ( f != NULL )
+ {
+ fseek( f, 0, SEEK_END );
+ uint32_t size = (uint32_t)ftell( f );
+ fseek( f, 0, SEEK_SET );
+
+ if ( size > unSize || !pBuffer )
+ {
+ unSizeToReturn = (uint32_t)size;
+ }
+ else
+ {
+ if ( fread( pBuffer, size, 1, f ) == 1 )
+ {
+ unSizeToReturn = (uint32_t)size;
+ }
+ }
+
+ fclose( f );
+ }
+
+ return unSizeToReturn;
+}
+
+bool Path_WriteBinaryFile(const std::string &strFilename, unsigned char *pData, unsigned nSize)
+{
+ FILE *f;
+#if defined( POSIX )
+ f = fopen(strFilename.c_str(), "wb");
+#else
+ std::wstring wstrFilename = UTF8to16( strFilename.c_str() );
+ errno_t err = _wfopen_s( &f, wstrFilename.c_str(), L"wb" );
+ if (err != 0)
+ {
+ f = NULL;
+ }
+#endif
+
+ size_t written = 0;
+ if (f != NULL) {
+ written = fwrite(pData, sizeof(unsigned char), nSize, f);
+ fclose(f);
+ }
+
+ return written == nSize ? true : false;
+}
+
+std::string Path_ReadTextFile( const std::string &strFilename )
+{
+ // doing it this way seems backwards, but I don't
+ // see an easy way to do this with C/C++ style IO
+ // that isn't worse...
+ int size;
+ unsigned char* buf = Path_ReadBinaryFile( strFilename, &size );
+ if (!buf)
+ return "";
+
+ // convert CRLF -> LF
+ size_t outsize = 1;
+ for (int i=1; i < size; i++)
+ {
+ if (buf[i] == '\n' && buf[i-1] == '\r') // CRLF
+ buf[outsize-1] = '\n'; // ->LF
+ else
+ buf[outsize++] = buf[i]; // just copy
+ }
+
+ std::string ret((char *)buf, outsize);
+ delete[] buf;
+ return ret;
+}
+
+
+bool Path_MakeWritable( const std::string &strFilename )
+{
+#if defined ( _WIN32 )
+ std::wstring wstrFilename = UTF8to16( strFilename.c_str() );
+
+ DWORD dwAttrs = GetFileAttributesW( wstrFilename.c_str() );
+ if ( dwAttrs != INVALID_FILE_ATTRIBUTES && ( dwAttrs & FILE_ATTRIBUTE_READONLY ) )
+ {
+ return SetFileAttributesW( wstrFilename.c_str(), dwAttrs & ~FILE_ATTRIBUTE_READONLY );
+ }
+#else
+ struct stat sb;
+
+ if ( stat( strFilename.c_str(), &sb ) == 0 && !( sb.st_mode & S_IWUSR ) )
+ {
+ return ( chmod( strFilename.c_str(), sb.st_mode | S_IWUSR ) == 0 );
+ }
+#endif
+
+ return true;
+}
+
+bool Path_WriteStringToTextFile( const std::string &strFilename, const char *pchData )
+{
+ FILE *f;
+#if defined( POSIX )
+ f = fopen( strFilename.c_str(), "w" );
+#else
+ std::wstring wstrFilename = UTF8to16( strFilename.c_str() );
+ errno_t err = _wfopen_s( &f, wstrFilename.c_str(), L"w" );
+ if ( err != 0 )
+ {
+ f = NULL;
+ }
+#endif
+
+ bool ok = false;
+
+ if ( f != NULL )
+ {
+ ok = fputs( pchData, f) >= 0;
+ fclose(f);
+ }
+
+ return ok;
+}
+
+bool Path_WriteStringToTextFileAtomic( const std::string &strFilename, const char *pchData )
+{
+ std::string strTmpFilename = strFilename + ".tmp";
+
+ if ( !Path_WriteStringToTextFile( strTmpFilename, pchData ) )
+ return false;
+
+ // Platform specific atomic file replacement
+#if defined( _WIN32 )
+ std::wstring wsFilename = UTF8to16( strFilename.c_str() );
+ std::wstring wsTmpFilename = UTF8to16( strTmpFilename.c_str() );
+ if ( !::ReplaceFileW( wsFilename.c_str(), wsTmpFilename.c_str(), nullptr, 0, 0, 0 ) )
+ {
+ // if we couldn't ReplaceFile, try a non-atomic write as a fallback
+ if ( !Path_WriteStringToTextFile( strFilename, pchData ) )
+ return false;
+ }
+#elif defined( POSIX )
+ if ( rename( strTmpFilename.c_str(), strFilename.c_str() ) == -1 )
+ return false;
+#else
+#error Do not know how to write atomic file
+#endif
+
+ return true;
+}
+
+
+#if defined(WIN32)
+#define FILE_URL_PREFIX "file:///"
+#else
+#define FILE_URL_PREFIX "file://"
+#endif
+
+// Mozilla: see README.mozilla for more details
+// ----------------------------------------------------------------------------------------------------------------------------
+// Purpose: Turns a path to a file on disk into a URL (or just returns the value if it's already a URL)
+// ----------------------------------------------------------------------------------------------------------------------------
+// std::string Path_FilePathToUrl( const std::string & sRelativePath, const std::string & sBasePath )
+// {
+// if ( StringHasPrefix( sRelativePath, "http://" )
+// || StringHasPrefix( sRelativePath, "https://" )
+// || StringHasPrefix( sRelativePath, "vr-input-workshop://" )
+// || StringHasPrefix( sRelativePath, "file://" )
+// )
+// {
+// return sRelativePath;
+// }
+// else
+// {
+// std::string sAbsolute = Path_MakeAbsolute( sRelativePath, sBasePath );
+// if ( sAbsolute.empty() )
+// return sAbsolute;
+// sAbsolute = Path_FixSlashes( sAbsolute, '/' );
+
+// size_t unBufferSize = sAbsolute.length() * 3;
+// char *pchBuffer = (char *)alloca( unBufferSize );
+// V_URLEncodeFullPath( pchBuffer, (int)unBufferSize, sAbsolute.c_str(), (int)sAbsolute.length() );
+
+// return std::string( FILE_URL_PREFIX ) + pchBuffer;
+// }
+// }
+
+// Mozilla: see README.mozilla for more details
+// -----------------------------------------------------------------------------------------------------
+// Purpose: Strips off file:// off a URL and returns the path. For other kinds of URLs an empty string is returned
+// -----------------------------------------------------------------------------------------------------
+// std::string Path_UrlToFilePath( const std::string & sFileUrl )
+// {
+// if ( !strnicmp( sFileUrl.c_str(), FILE_URL_PREFIX, strlen( FILE_URL_PREFIX ) ) )
+// {
+// char *pchBuffer = (char *)alloca( sFileUrl.length() );
+// V_URLDecodeNoPlusForSpace( pchBuffer, (int)sFileUrl.length(),
+// sFileUrl.c_str() + strlen( FILE_URL_PREFIX ), (int)( sFileUrl.length() - strlen( FILE_URL_PREFIX ) ) );
+
+// return Path_FixSlashes( pchBuffer );
+// }
+// else
+// {
+// return "";
+// }
+// }
+
+
+// -----------------------------------------------------------------------------------------------------
+// Purpose: Returns the root of the directory the system wants us to store user documents in
+// -----------------------------------------------------------------------------------------------------
+std::string GetUserDocumentsPath()
+{
+#if defined( WIN32 )
+ WCHAR rwchPath[MAX_PATH];
+
+ if ( !SUCCEEDED( SHGetFolderPathW( NULL, CSIDL_MYDOCUMENTS | CSIDL_FLAG_CREATE, NULL, 0, rwchPath ) ) )
+ {
+ return "";
+ }
+
+ // Convert the path to UTF-8 and store in the output
+ std::string sUserPath = UTF16to8( rwchPath );
+
+ return sUserPath;
+#elif defined( OSX )
+ @autoreleasepool {
+ NSArray *paths = NSSearchPathForDirectoriesInDomains( NSDocumentDirectory, NSUserDomainMask, YES );
+ if ( [paths count] == 0 )
+ {
+ return "";
+ }
+
+ return [[paths objectAtIndex:0] UTF8String];
+ }
+#elif defined( LINUX )
+ // @todo: not solved/changed as part of OSX - still not real - just removed old class based steam cut and paste
+ const char *pchHome = getenv( "HOME" );
+ if ( pchHome == NULL )
+ {
+ return "";
+ }
+ return pchHome;
+#endif
+}
+
+
+// -----------------------------------------------------------------------------------------------------
+// Purpose: deletes / unlinks a single file
+// -----------------------------------------------------------------------------------------------------
+bool Path_UnlinkFile( const std::string &strFilename )
+{
+#if defined( WIN32 )
+ std::wstring wsFilename = UTF8to16( strFilename.c_str() );
+ return ( 0 != DeleteFileW( wsFilename.c_str() ) );
+#else
+ return ( 0 == unlink( strFilename.c_str() ) );
+#endif
+}
diff --git a/gfx/vr/service/openvr/src/pathtools_public.h b/gfx/vr/service/openvr/src/pathtools_public.h
new file mode 100644
index 0000000000..33688fba2f
--- /dev/null
+++ b/gfx/vr/service/openvr/src/pathtools_public.h
@@ -0,0 +1,150 @@
+//========= Copyright Valve Corporation ============//
+#pragma once
+
+#include <string>
+#include <stdint.h>
+
+/** Returns the path (including filename) to the current executable */
+std::string Path_GetExecutablePath();
+
+/** Returns the path of the current working directory */
+std::string Path_GetWorkingDirectory();
+
+/** Sets the path of the current working directory. Returns true if this was successful. */
+bool Path_SetWorkingDirectory( const std::string & sPath );
+
+/** Gets the path to a temporary directory. */
+std::string Path_GetTemporaryDirectory();
+
+/** returns the path (including filename) of the current shared lib or DLL */
+std::string Path_GetThisModulePath();
+
+/** Returns the specified path without its filename.
+* If slash is unspecified the native path separator of the current platform
+* will be used. */
+std::string Path_StripFilename( const std::string & sPath, char slash = 0 );
+
+/** returns just the filename from the provided full or relative path. */
+std::string Path_StripDirectory( const std::string & sPath, char slash = 0 );
+
+/** returns just the filename with no extension of the provided filename.
+* If there is a path the path is left intact. */
+std::string Path_StripExtension( const std::string & sPath );
+
+/** returns just extension of the provided filename (if any). */
+std::string Path_GetExtension( const std::string & sPath );
+
+/** Returns true if the path is absolute */
+bool Path_IsAbsolute( const std::string & sPath );
+
+/** Makes an absolute path from a relative path and a base path */
+std::string Path_MakeAbsolute( const std::string & sRelativePath, const std::string & sBasePath );
+
+/** Fixes the directory separators for the current platform.
+* If slash is unspecified the native path separator of the current platform
+* will be used. */
+std::string Path_FixSlashes( const std::string & sPath, char slash = 0 );
+
+/** Returns the path separator for the current platform */
+char Path_GetSlash();
+
+/** Jams two paths together with the right kind of slash */
+std::string Path_Join( const std::string & first, const std::string & second, char slash = 0 );
+std::string Path_Join( const std::string & first, const std::string & second, const std::string & third, char slash = 0 );
+std::string Path_Join( const std::string & first, const std::string & second, const std::string & third, const std::string &fourth, char slash = 0 );
+std::string Path_Join(
+ const std::string & first,
+ const std::string & second,
+ const std::string & third,
+ const std::string & fourth,
+ const std::string & fifth,
+ char slash = 0 );
+
+
+/** Removes redundant <dir>/.. elements in the path. Returns an empty path if the
+* specified path has a broken number of directories for its number of ..s.
+* If slash is unspecified the native path separator of the current platform
+* will be used. */
+std::string Path_Compact( const std::string & sRawPath, char slash = 0 );
+
+/** Returns true if these two paths are the same without respect for internal . or ..
+* sequences, slash type, or case (on case-insensitive platforms). */
+bool Path_IsSamePath( const std::string & sPath1, const std::string & sPath2 );
+
+//** Removed trailing slashes */
+std::string Path_RemoveTrailingSlash( const std::string & sRawPath, char slash = 0 );
+
+/** returns true if the specified path exists and is a directory */
+bool Path_IsDirectory( const std::string & sPath );
+
+/** returns true if the specified path represents an app bundle */
+bool Path_IsAppBundle( const std::string & sPath );
+
+/** returns true if the the path exists */
+bool Path_Exists( const std::string & sPath );
+
+/** Helper functions to find parent directories or subdirectories of parent directories */
+std::string Path_FindParentDirectoryRecursively( const std::string &strStartDirectory, const std::string &strDirectoryName );
+std::string Path_FindParentSubDirectoryRecursively( const std::string &strStartDirectory, const std::string &strDirectoryName );
+
+/** Make a text file writable. */
+bool Path_MakeWritable( const std::string &strFilename );
+
+/** Path operations to read or write text/binary files */
+unsigned char * Path_ReadBinaryFile( const std::string &strFilename, int *pSize );
+uint32_t Path_ReadBinaryFile( const std::string &strFilename, unsigned char *pBuffer, uint32_t unSize );
+bool Path_WriteBinaryFile( const std::string &strFilename, unsigned char *pData, unsigned nSize );
+std::string Path_ReadTextFile( const std::string &strFilename );
+bool Path_WriteStringToTextFile( const std::string &strFilename, const char *pchData );
+bool Path_WriteStringToTextFileAtomic( const std::string &strFilename, const char *pchData );
+
+// Mozilla: see README.mozilla for more details
+/** Returns a file:// url for paths, or an http or https url if that's what was provided */
+// std::string Path_FilePathToUrl( const std::string & sRelativePath, const std::string & sBasePath );
+
+/** Strips off file:// off a URL and returns the path. For other kinds of URLs an empty string is returned */
+std::string Path_UrlToFilePath( const std::string & sFileUrl );
+
+/** Returns the root of the directory the system wants us to store user documents in */
+std::string GetUserDocumentsPath();
+
+/** deletes / unlinks a single file */
+bool Path_UnlinkFile( const std::string &strFilename );
+
+#ifndef MAX_UNICODE_PATH
+ #define MAX_UNICODE_PATH 32767
+#endif
+
+#ifndef MAX_UNICODE_PATH_IN_UTF8
+ #define MAX_UNICODE_PATH_IN_UTF8 (MAX_UNICODE_PATH * 4)
+#endif
+
+//-----------------------------------------------------------------------------
+#if defined(WIN32)
+#define DYNAMIC_LIB_EXT ".dll"
+#define PROGRAM_EXT ".exe"
+#ifdef _WIN64
+#define PLATSUBDIR "win64"
+#else
+#define PLATSUBDIR "win32"
+#endif
+#elif defined(OSX)
+#define DYNAMIC_LIB_EXT ".dylib"
+#define PLATSUBDIR "osx32"
+#define PROGRAM_EXT ""
+#elif defined(LINUX)
+#define DYNAMIC_LIB_EXT ".so"
+#define PROGRAM_EXT ""
+#if defined( LINUX32 )
+#define PLATSUBDIR "linux32"
+#elif defined( ANDROIDARM64 )
+#define PLATSUBDIR "androidarm64"
+#elif defined( LINUXARM64 )
+#define PLATSUBDIR "linuxarm64"
+#else
+#define PLATSUBDIR "linux64"
+#endif
+#else
+#warning "Unknown platform for PLATSUBDIR"
+#define PLATSUBDIR "unknown_platform"
+#endif
diff --git a/gfx/vr/service/openvr/src/sharedlibtools_public.cpp b/gfx/vr/service/openvr/src/sharedlibtools_public.cpp
new file mode 100644
index 0000000000..048512a1de
--- /dev/null
+++ b/gfx/vr/service/openvr/src/sharedlibtools_public.cpp
@@ -0,0 +1,43 @@
+//========= Copyright Valve Corporation ============//
+#include "sharedlibtools_public.h"
+#include <string.h>
+
+#if defined(_WIN32)
+#include <windows.h>
+#endif
+
+#if defined(POSIX)
+#include <dlfcn.h>
+#endif
+
+SharedLibHandle SharedLib_Load( const char *pchPath )
+{
+#if defined( _WIN32)
+ return (SharedLibHandle)LoadLibraryEx( pchPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH );
+#elif defined(POSIX)
+ return (SharedLibHandle)dlopen(pchPath, RTLD_LOCAL|RTLD_NOW);
+#endif
+}
+
+void *SharedLib_GetFunction( SharedLibHandle lib, const char *pchFunctionName)
+{
+#if defined( _WIN32)
+ return (void*)GetProcAddress( (HMODULE)lib, pchFunctionName );
+#elif defined(POSIX)
+ return dlsym( lib, pchFunctionName );
+#endif
+}
+
+
+void SharedLib_Unload( SharedLibHandle lib )
+{
+ if ( !lib )
+ return;
+#if defined( _WIN32)
+ FreeLibrary( (HMODULE)lib );
+#elif defined(POSIX)
+ dlclose( lib );
+#endif
+}
+
+
diff --git a/gfx/vr/service/openvr/src/sharedlibtools_public.h b/gfx/vr/service/openvr/src/sharedlibtools_public.h
new file mode 100644
index 0000000000..10163dbfab
--- /dev/null
+++ b/gfx/vr/service/openvr/src/sharedlibtools_public.h
@@ -0,0 +1,10 @@
+//========= Copyright Valve Corporation ============//
+#pragma once
+
+typedef void *SharedLibHandle;
+
+SharedLibHandle SharedLib_Load( const char *pchPath );
+void *SharedLib_GetFunction( SharedLibHandle lib, const char *pchFunctionName);
+void SharedLib_Unload( SharedLibHandle lib );
+
+
diff --git a/gfx/vr/service/openvr/src/strtools_public.cpp b/gfx/vr/service/openvr/src/strtools_public.cpp
new file mode 100644
index 0000000000..f52f8e9004
--- /dev/null
+++ b/gfx/vr/service/openvr/src/strtools_public.cpp
@@ -0,0 +1,571 @@
+//========= Copyright Valve Corporation ============//
+#include "strtools_public.h"
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sstream>
+// Mozilla: see README.mozilla for more details
+// #include <codecvt>
+#include <iostream>
+#include <functional>
+#include <locale>
+// #include <codecvt>
+
+#if defined( _WIN32 )
+#include <windows.h>
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool StringHasPrefix( const std::string & sString, const std::string & sPrefix )
+{
+ return 0 == strnicmp( sString.c_str(), sPrefix.c_str(), sPrefix.length() );
+}
+
+bool StringHasPrefixCaseSensitive( const std::string & sString, const std::string & sPrefix )
+{
+ return 0 == strncmp( sString.c_str(), sPrefix.c_str(), sPrefix.length() );
+}
+
+
+bool StringHasSuffix( const std::string &sString, const std::string &sSuffix )
+{
+ size_t cStrLen = sString.length();
+ size_t cSuffixLen = sSuffix.length();
+
+ if ( cSuffixLen > cStrLen )
+ return false;
+
+ std::string sStringSuffix = sString.substr( cStrLen - cSuffixLen, cSuffixLen );
+
+ return 0 == stricmp( sStringSuffix.c_str(), sSuffix.c_str() );
+}
+
+bool StringHasSuffixCaseSensitive( const std::string &sString, const std::string &sSuffix )
+{
+ size_t cStrLen = sString.length();
+ size_t cSuffixLen = sSuffix.length();
+
+ if ( cSuffixLen > cStrLen )
+ return false;
+
+ std::string sStringSuffix = sString.substr( cStrLen - cSuffixLen, cSuffixLen );
+
+ return 0 == strncmp( sStringSuffix.c_str(), sSuffix.c_str(),cSuffixLen );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+// Mozilla: see README.mozilla for more details
+//typedef std::codecvt_utf8< wchar_t > convert_type;
+
+// Mozilla: see README.mozilla for more details
+#if defined( _WIN32 )
+std::string UTF16to8(const wchar_t * in)
+{
+ int retLength = ::WideCharToMultiByte(CP_UTF8, 0, in, -1, nullptr, 0, nullptr, nullptr);
+ if (retLength == 0)
+ {
+ return std::string();
+ }
+
+ char* retString = new char[retLength];
+ ::WideCharToMultiByte(CP_UTF8, 0, in, -1, retString, retLength, nullptr, nullptr);
+
+ std::string retStringValue(retString);
+
+ delete[] retString;
+
+ return retStringValue;
+
+ // static std::wstring_convert< convert_type, wchar_t > s_converter; // construction of this can be expensive (or even serialized) depending on locale
+
+ // try
+ // {
+ // return s_converter.to_bytes( in );
+ // }
+ // catch ( ... )
+ // {
+ // return std::string();
+ // }
+}
+
+std::string UTF16to8( const std::wstring & in ) { return UTF16to8( in.c_str() ); }
+
+// Mozilla: see README.mozilla for more details
+std::wstring UTF8to16(const char * in)
+{
+ int retLength = ::MultiByteToWideChar(CP_UTF8, 0, in, -1, nullptr, 0);
+ if (retLength == 0)
+ {
+ return std::wstring();
+ }
+
+ wchar_t* retString = new wchar_t[retLength];
+ ::MultiByteToWideChar(CP_UTF8, 0, in, -1, retString, retLength);
+
+ std::wstring retStringValue(retString);
+
+ delete[] retString;
+
+ return retStringValue;
+
+ //static std::wstring_convert< convert_type, wchar_t > s_converter; // construction of this can be expensive (or even serialized) depending on locale
+
+ //try
+ //{
+ // return s_converter.from_bytes( in );
+ //}
+ //catch ( ... )
+ //{
+ // return std::wstring();
+ //}
+}
+
+std::wstring UTF8to16( const std::string & in ) { return UTF8to16( in.c_str() ); }
+#endif
+
+
+#if defined( _WIN32 )
+//-----------------------------------------------------------------------------
+// Purpose: Convert LPSTR in the default CodePage to UTF8
+//-----------------------------------------------------------------------------
+std::string DefaultACPtoUTF8( const char *pszStr )
+{
+ if ( GetACP() == CP_UTF8 )
+ {
+ return pszStr;
+ }
+ else
+ {
+ std::vector<wchar_t> vecBuf( strlen( pszStr ) + 1 ); // should be guaranteed to be enough
+ MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, pszStr, -1, vecBuf.data(), (int) vecBuf.size() );
+ return UTF16to8( vecBuf.data() );
+ }
+}
+#endif
+
+// --------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------
+void strcpy_safe( char *pchBuffer, size_t unBufferSizeBytes, const char *pchSource )
+{
+ strncpy( pchBuffer, pchSource, unBufferSizeBytes - 1 );
+ pchBuffer[unBufferSizeBytes - 1] = '\0';
+}
+
+// --------------------------------------------------------------------
+// Purpose: converts a string to upper case
+// --------------------------------------------------------------------
+std::string StringToUpper( const std::string & sString )
+{
+ std::string sOut;
+ sOut.reserve( sString.size() + 1 );
+ for( std::string::const_iterator i = sString.begin(); i != sString.end(); i++ )
+ {
+ sOut.push_back( (char)toupper( *i ) );
+ }
+
+ return sOut;
+}
+
+
+// --------------------------------------------------------------------
+// Purpose: converts a string to lower case
+// --------------------------------------------------------------------
+std::string StringToLower( const std::string & sString )
+{
+ std::string sOut;
+ sOut.reserve( sString.size() + 1 );
+ for( std::string::const_iterator i = sString.begin(); i != sString.end(); i++ )
+ {
+ sOut.push_back( (char)tolower( *i ) );
+ }
+
+ return sOut;
+}
+
+
+uint32_t ReturnStdString( const std::string & sValue, char *pchBuffer, uint32_t unBufferLen )
+{
+ uint32_t unLen = (uint32_t)sValue.length() + 1;
+ if( !pchBuffer || !unBufferLen )
+ return unLen;
+
+ if( unBufferLen < unLen )
+ {
+ pchBuffer[0] = '\0';
+ }
+ else
+ {
+ memcpy( pchBuffer, sValue.c_str(), unLen );
+ }
+
+ return unLen;
+}
+
+
+/** Returns a std::string from a uint64_t */
+// Mozilla: see README.mozilla for more details
+// std::string Uint64ToString( uint64_t ulValue )
+// {
+// char buf[ 22 ];
+// #if defined( _WIN32 )
+// sprintf_s( buf, "%llu", ulValue );
+// #else
+// snprintf( buf, sizeof( buf ), "%llu", (long long unsigned int ) ulValue );
+// #endif
+// return buf;
+// }
+
+
+/** returns a uint64_t from a string */
+uint64_t StringToUint64( const std::string & sValue )
+{
+ return strtoull( sValue.c_str(), NULL, 0 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Helper for converting a numeric value to a hex digit, value should be 0-15.
+//-----------------------------------------------------------------------------
+char cIntToHexDigit( int nValue )
+{
+ //Assert( nValue >= 0 && nValue <= 15 );
+ return "0123456789ABCDEF"[ nValue & 15 ];
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Helper for converting a hex char value to numeric, return -1 if the char
+// is not a valid hex digit.
+//-----------------------------------------------------------------------------
+int iHexCharToInt( char cValue )
+{
+ int32_t iValue = cValue;
+ if ( (uint32_t)( iValue - '0' ) < 10 )
+ return iValue - '0';
+
+ iValue |= 0x20;
+ if ( (uint32_t)( iValue - 'a' ) < 6 )
+ return iValue - 'a' + 10;
+
+ return -1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: These define the set of characters to filter for components (which
+// need all the escaping we can muster) vs. paths (which don't want
+// / and : escaped so we don't break less compliant URL handling code.
+//-----------------------------------------------------------------------------
+static bool CharNeedsEscape_Component( const char c )
+{
+ return (!(c >= 'a' && c <= 'z') && !(c >= 'A' && c <= 'Z') && !(c >= '0' && c <= '9')
+ && c != '-' && c != '_' && c != '.');
+}
+static bool CharNeedsEscape_FullPath( const char c )
+{
+ return (!(c >= 'a' && c <= 'z') && !(c >= 'A' && c <= 'Z') && !(c >= '0' && c <= '9')
+ && c != '-' && c != '_' && c != '.' && c != '/' && c != ':' );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Internal implementation of encode, works in the strict RFC manner, or
+// with spaces turned to + like HTML form encoding.
+//-----------------------------------------------------------------------------
+void V_URLEncodeInternal( char *pchDest, int nDestLen, const char *pchSource, int nSourceLen,
+ bool bUsePlusForSpace, std::function< bool(const char)> fnNeedsEscape )
+{
+ //AssertMsg( nDestLen > 3*nSourceLen, "Target buffer for V_URLEncode should be 3x source length, plus one for terminating null\n" );
+
+ int iDestPos = 0;
+ for ( int i=0; i < nSourceLen; ++i )
+ {
+ // worst case we need 3 additional chars
+ if( (iDestPos+3) > nDestLen )
+ {
+ pchDest[0] = '\0';
+// AssertMsg( false, "Target buffer too short\n" );
+ return;
+ }
+
+ // We allow only a-z, A-Z, 0-9, period, underscore, and hyphen to pass through unescaped.
+ // These are the characters allowed by both the original RFC 1738 and the latest RFC 3986.
+ // Current specs also allow '~', but that is forbidden under original RFC 1738.
+ if ( fnNeedsEscape( pchSource[i] ) )
+ {
+ if ( bUsePlusForSpace && pchSource[i] == ' ' )
+ {
+ pchDest[iDestPos++] = '+';
+ }
+ else
+ {
+ pchDest[iDestPos++] = '%';
+ uint8_t iValue = pchSource[i];
+ if ( iValue == 0 )
+ {
+ pchDest[iDestPos++] = '0';
+ pchDest[iDestPos++] = '0';
+ }
+ else
+ {
+ char cHexDigit1 = cIntToHexDigit( iValue % 16 );
+ iValue /= 16;
+ char cHexDigit2 = cIntToHexDigit( iValue );
+ pchDest[iDestPos++] = cHexDigit2;
+ pchDest[iDestPos++] = cHexDigit1;
+ }
+ }
+ }
+ else
+ {
+ pchDest[iDestPos++] = pchSource[i];
+ }
+ }
+
+ if( (iDestPos+1) > nDestLen )
+ {
+ pchDest[0] = '\0';
+ //AssertMsg( false, "Target buffer too short to terminate\n" );
+ return;
+ }
+
+ // Null terminate
+ pchDest[iDestPos++] = 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Internal implementation of decode, works in the strict RFC manner, or
+// with spaces turned to + like HTML form encoding.
+//
+// Returns the amount of space used in the output buffer.
+//-----------------------------------------------------------------------------
+size_t V_URLDecodeInternal( char *pchDecodeDest, int nDecodeDestLen, const char *pchEncodedSource, int nEncodedSourceLen, bool bUsePlusForSpace )
+{
+ if ( nDecodeDestLen < nEncodedSourceLen )
+ {
+ //AssertMsg( false, "V_URLDecode needs a dest buffer at least as large as the source" );
+ return 0;
+ }
+
+ int iDestPos = 0;
+ for( int i=0; i < nEncodedSourceLen; ++i )
+ {
+ if ( bUsePlusForSpace && pchEncodedSource[i] == '+' )
+ {
+ pchDecodeDest[ iDestPos++ ] = ' ';
+ }
+ else if ( pchEncodedSource[i] == '%' )
+ {
+ // Percent signifies an encoded value, look ahead for the hex code, convert to numeric, and use that
+
+ // First make sure we have 2 more chars
+ if ( i < nEncodedSourceLen - 2 )
+ {
+ char cHexDigit1 = pchEncodedSource[i+1];
+ char cHexDigit2 = pchEncodedSource[i+2];
+
+ // Turn the chars into a hex value, if they are not valid, then we'll
+ // just place the % and the following two chars direct into the string,
+ // even though this really shouldn't happen, who knows what bad clients
+ // may do with encoding.
+ bool bValid = false;
+ int iValue = iHexCharToInt( cHexDigit1 );
+ if ( iValue != -1 )
+ {
+ iValue *= 16;
+ int iValue2 = iHexCharToInt( cHexDigit2 );
+ if ( iValue2 != -1 )
+ {
+ iValue += iValue2;
+ pchDecodeDest[ iDestPos++ ] = (char)iValue;
+ bValid = true;
+ }
+ }
+
+ if ( !bValid )
+ {
+ pchDecodeDest[ iDestPos++ ] = '%';
+ pchDecodeDest[ iDestPos++ ] = cHexDigit1;
+ pchDecodeDest[ iDestPos++ ] = cHexDigit2;
+ }
+ }
+
+ // Skip ahead
+ i += 2;
+ }
+ else
+ {
+ pchDecodeDest[ iDestPos++ ] = pchEncodedSource[i];
+ }
+ }
+
+ // We may not have extra room to NULL terminate, since this can be used on raw data, but if we do
+ // go ahead and do it as this can avoid bugs.
+ if ( iDestPos < nDecodeDestLen )
+ {
+ pchDecodeDest[iDestPos] = 0;
+ }
+
+ return (size_t)iDestPos;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Encodes a string (or binary data) from URL encoding format, see rfc1738 section 2.2.
+// This version of the call isn't a strict RFC implementation, but uses + for space as is
+// the standard in HTML form encoding, despite it not being part of the RFC.
+//
+// Dest buffer should be at least as large as source buffer to guarantee room for decode.
+//-----------------------------------------------------------------------------
+void V_URLEncode( char *pchDest, int nDestLen, const char *pchSource, int nSourceLen )
+{
+ return V_URLEncodeInternal( pchDest, nDestLen, pchSource, nSourceLen, true, CharNeedsEscape_Component );
+}
+
+
+void V_URLEncodeNoPlusForSpace( char *pchDest, int nDestLen, const char *pchSource, int nSourceLen )
+{
+ return V_URLEncodeInternal( pchDest, nDestLen, pchSource, nSourceLen, false, CharNeedsEscape_Component );
+}
+
+void V_URLEncodeFullPath( char *pchDest, int nDestLen, const char *pchSource, int nSourceLen )
+{
+ return V_URLEncodeInternal( pchDest, nDestLen, pchSource, nSourceLen, false, CharNeedsEscape_FullPath );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Decodes a string (or binary data) from URL encoding format, see rfc1738 section 2.2.
+// This version of the call isn't a strict RFC implementation, but uses + for space as is
+// the standard in HTML form encoding, despite it not being part of the RFC.
+//
+// Dest buffer should be at least as large as source buffer to guarantee room for decode.
+// Dest buffer being the same as the source buffer (decode in-place) is explicitly allowed.
+//-----------------------------------------------------------------------------
+size_t V_URLDecode( char *pchDecodeDest, int nDecodeDestLen, const char *pchEncodedSource, int nEncodedSourceLen )
+{
+ return V_URLDecodeInternal( pchDecodeDest, nDecodeDestLen, pchEncodedSource, nEncodedSourceLen, true );
+}
+
+size_t V_URLDecodeNoPlusForSpace( char *pchDecodeDest, int nDecodeDestLen, const char *pchEncodedSource, int nEncodedSourceLen )
+{
+ return V_URLDecodeInternal( pchDecodeDest, nDecodeDestLen, pchEncodedSource, nEncodedSourceLen, false );
+}
+
+//-----------------------------------------------------------------------------
+void V_StripExtension( std::string &in )
+{
+ // Find the last dot. If it's followed by a dot or a slash, then it's part of a
+ // directory specifier like ../../somedir/./blah.
+ std::string::size_type test = in.rfind( '.' );
+ if ( test != std::string::npos )
+ {
+ // This handles things like ".\blah" or "c:\my@email.com\abc\def\geh"
+ // Which would otherwise wind up with "" and "c:\my@email", respectively.
+ if ( in.rfind( '\\' ) < test && in.rfind( '/' ) < test )
+ {
+ in.resize( test );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Tokenizes a string into a vector of strings
+//-----------------------------------------------------------------------------
+std::vector<std::string> TokenizeString( const std::string & sString, char cToken )
+{
+ std::vector<std::string> vecStrings;
+ std::istringstream stream( sString );
+ std::string s;
+ while ( std::getline( stream, s, cToken ) )
+ {
+ vecStrings.push_back( s );
+ }
+ return vecStrings;
+}
+
+// Mozilla: see README.mozilla for more details
+//-----------------------------------------------------------------------------
+// Purpose: Repairs a should-be-UTF-8 string to a for-sure-is-UTF-8 string, plus return boolean if we subbed in '?' somewhere
+//-----------------------------------------------------------------------------
+// bool RepairUTF8( const char *pbegin, const char *pend, std::string & sOutputUtf8 )
+// {
+// typedef std::codecvt_utf8<char32_t> facet_type;
+// facet_type myfacet;
+
+// std::mbstate_t mystate = std::mbstate_t();
+
+// sOutputUtf8.clear();
+// sOutputUtf8.reserve( pend - pbegin );
+// bool bSqueakyClean = true;
+
+// const char *pmid = pbegin;
+// while ( pmid != pend )
+// {
+// bool bHasError = false;
+// bool bHasValidData = false;
+
+// char32_t out = 0xdeadbeef, *pout;
+// pbegin = pmid;
+// switch ( myfacet.in( mystate, pbegin, pend, pmid, &out, &out + 1, pout ) )
+// {
+// case facet_type::ok:
+// bHasValidData = true;
+// break;
+
+// case facet_type::noconv:
+// // unexpected! always converting type
+// bSqueakyClean = false;
+// break;
+
+// case facet_type::partial:
+// bHasError = pbegin == pmid;
+// if ( bHasError )
+// {
+// bSqueakyClean = false;
+// }
+// else
+// {
+// bHasValidData = true;
+// }
+// break;
+
+// case facet_type::error:
+// bHasError = true;
+// bSqueakyClean = false;
+// break;
+// }
+
+// if ( bHasValidData )
+// {
+// // could convert back, but no need
+// for ( const char *p = pbegin; p != pmid; ++p )
+// {
+// sOutputUtf8 += *p;
+// }
+// }
+
+// if ( bHasError )
+// {
+// sOutputUtf8 += '?';
+// }
+
+// if ( pmid == pbegin )
+// {
+// pmid++;
+// }
+// }
+
+// return bSqueakyClean;
+// }
+
+// //-----------------------------------------------------------------------------
+// // Purpose: Repairs a should-be-UTF-8 string to a for-sure-is-UTF-8 string, plus return boolean if we subbed in '?' somewhere
+// //-----------------------------------------------------------------------------
+// bool RepairUTF8( const std::string & sInputUtf8, std::string & sOutputUtf8 )
+// {
+// return RepairUTF8( sInputUtf8.data(), sInputUtf8.data() + sInputUtf8.size(), sOutputUtf8 );
+// }
diff --git a/gfx/vr/service/openvr/src/strtools_public.h b/gfx/vr/service/openvr/src/strtools_public.h
new file mode 100644
index 0000000000..067bbe1b1b
--- /dev/null
+++ b/gfx/vr/service/openvr/src/strtools_public.h
@@ -0,0 +1,156 @@
+//========= Copyright Valve Corporation ============//
+#pragma once
+
+#include <string>
+#include <stdint.h>
+#include <sys/types.h>
+#include <vector>
+
+/** returns true if the string has the prefix */
+bool StringHasPrefix( const std::string & sString, const std::string & sPrefix );
+bool StringHasPrefixCaseSensitive( const std::string & sString, const std::string & sPrefix );
+
+/** returns if the string has the suffix */
+bool StringHasSuffix( const std::string &sString, const std::string &sSuffix );
+bool StringHasSuffixCaseSensitive( const std::string &sString, const std::string &sSuffix );
+
+// Mozilla: see README.mozilla for more details
+#if defined( _WIN32 )
+/** converts a UTF-16 string to a UTF-8 string */
+std::string UTF16to8( const wchar_t * in );
+std::string UTF16to8( const std::wstring & in );
+
+/** converts a UTF-8 string to a UTF-16 string */
+std::wstring UTF8to16(const char * in);
+std::wstring UTF8to16( const std::string & in );
+#define Utf16FromUtf8 UTF8to16
+#endif
+
+#if defined( _WIN32 )
+std::string DefaultACPtoUTF8( const char *pszStr );
+#endif
+
+/** Repairs a should-be-UTF-8 string to a for-sure-is-UTF-8 string, plus return boolean if we subbed in '?' somewhere */
+bool RepairUTF8( const char *begin, const char *end, std::string & sOutputUtf8 );
+bool RepairUTF8( const std::string & sInputUtf8, std::string & sOutputUtf8 );
+
+/** safely copy a string into a buffer */
+void strcpy_safe( char *pchBuffer, size_t unBufferSizeBytes, const char *pchSource );
+template< size_t bufferSize >
+void strcpy_safe( char (& buffer) [ bufferSize ], const char *pchSource )
+{
+ strcpy_safe( buffer, bufferSize, pchSource );
+}
+
+
+/** converts a string to upper case */
+std::string StringToUpper( const std::string & sString );
+
+/** converts a string to lower case */
+std::string StringToLower( const std::string & sString );
+
+// we stricmp (from WIN) but it isn't POSIX - OSX/LINUX have strcasecmp so just inline bridge to it
+#if defined( OSX ) || defined( LINUX )
+#include <strings.h>
+inline int stricmp(const char *pStr1, const char *pStr2) { return strcasecmp(pStr1,pStr2); }
+#ifndef _stricmp
+#define _stricmp stricmp
+#endif
+inline int strnicmp( const char *pStr1, const char *pStr2, size_t unBufferLen ) { return strncasecmp( pStr1,pStr2, unBufferLen ); }
+#ifndef _strnicmp
+#define _strnicmp strnicmp
+#endif
+
+#ifndef _vsnprintf_s
+#define _vsnprintf_s vsnprintf
+#endif
+
+#define _TRUNCATE ((size_t)-1)
+
+#endif
+
+#if defined( OSX )
+// behaviors ensure NULL-termination at least as well as _TRUNCATE does, but
+// wcsncpy_s/strncpy_s can non-NULL-terminate, wcslcpy/strlcpy can not.
+// inline errno_t wcsncpy_s(wchar_t *strDest, size_t numberOfElements, const wchar_t *strSource, size_t count)
+// {
+// return wcslcpy(strDest, strSource, numberOfElements);
+// }
+
+// inline errno_t strncpy_s(char *strDest, size_t numberOfElements, const char *strSource, size_t count)
+// {
+// return strlcpy(strDest, strSource, numberOfElements);
+// }
+
+#endif
+
+#if defined( LINUX )
+// this implementation does not return whether or not the destination was
+// truncated, but that is straightforward to fix if anybody actually needs the
+// return code.
+#include "string.h"
+inline void wcsncpy_s(wchar_t *strDest, size_t numberOfElements, const wchar_t *strSource, size_t count)
+{
+ wcsncpy(strDest, strSource, numberOfElements);
+ strDest[numberOfElements-1] = '\0';
+}
+
+inline void strncpy_s(char *strDest, size_t numberOfElements, const char *strSource, size_t count)
+{
+ strncpy(strDest, strSource, numberOfElements);
+ strDest[numberOfElements-1] = '\0';
+}
+
+#endif
+
+#if defined( _WIN32 ) && _MSC_VER < 1800
+inline uint64_t strtoull(const char *str, char **endptr, int base) { return _strtoui64( str, endptr, base ); }
+#endif
+
+/* Handles copying a std::string into a buffer as would be provided in an API */
+uint32_t ReturnStdString( const std::string & sValue, char *pchBuffer, uint32_t unBufferLen );
+
+/** Returns a std::string from a uint64_t */
+// Mozilla: see README.mozilla for more details
+//std::string Uint64ToString( uint64_t ulValue );
+
+/** returns a uint64_t from a string */
+uint64_t StringToUint64( const std::string & sValue );
+
+//-----------------------------------------------------------------------------
+// Purpose: Encodes a string (or binary data) from URL encoding format, see rfc1738 section 2.2.
+// This version of the call isn't a strict RFC implementation, but uses + for space as is
+// the standard in HTML form encoding, despite it not being part of the RFC.
+//
+// Dest buffer should be at least as large as source buffer to guarantee room for decode.
+//-----------------------------------------------------------------------------
+void V_URLEncode( char *pchDest, int nDestLen, const char *pchSource, int nSourceLen );
+
+/** Same as V_URLEncode, but without plus for space. */
+void V_URLEncodeNoPlusForSpace( char *pchDest, int nDestLen, const char *pchSource, int nSourceLen );
+
+/** Same as V_URLEncodeNoPlusForSpace, but without escaping / and : */
+void V_URLEncodeFullPath( char *pchDest, int nDestLen, const char *pchSource, int nSourceLen );
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Decodes a string (or binary data) from URL encoding format, see rfc1738 section 2.2.
+// This version of the call isn't a strict RFC implementation, but uses + for space as is
+// the standard in HTML form encoding, despite it not being part of the RFC.
+//
+// Dest buffer should be at least as large as source buffer to guarantee room for decode.
+// Dest buffer being the same as the source buffer (decode in-place) is explicitly allowed.
+//-----------------------------------------------------------------------------
+size_t V_URLDecode( char *pchDecodeDest, int nDecodeDestLen, const char *pchEncodedSource, int nEncodedSourceLen );
+
+/** Same as V_URLDecode, but without plus for space. */
+size_t V_URLDecodeNoPlusForSpace( char *pchDecodeDest, int nDecodeDestLen, const char *pchEncodedSource, int nEncodedSourceLen );
+
+//-----------------------------------------------------------------------------
+// Purpose: strip extension from a path
+//-----------------------------------------------------------------------------
+void V_StripExtension( std::string &in );
+
+
+/** Tokenizes a string into a vector of strings */
+std::vector<std::string> TokenizeString( const std::string & sString, char cToken );
diff --git a/gfx/vr/service/openvr/src/vrpathregistry_public.cpp b/gfx/vr/service/openvr/src/vrpathregistry_public.cpp
new file mode 100644
index 0000000000..f40fc1cda1
--- /dev/null
+++ b/gfx/vr/service/openvr/src/vrpathregistry_public.cpp
@@ -0,0 +1,442 @@
+//========= Copyright Valve Corporation ============//
+
+#include "vrpathregistry_public.h"
+#include "json/json.h"
+#include "pathtools_public.h"
+#include "envvartools_public.h"
+#include "strtools_public.h"
+#include "dirtools_public.h"
+
+#if defined( WIN32 )
+#include <windows.h>
+#include <shlobj.h>
+
+#undef GetEnvironmentVariable
+#elif defined OSX
+#include <Foundation/Foundation.h>
+#include <AppKit/AppKit.h>
+#elif defined(LINUX)
+#include <dlfcn.h>
+#include <stdio.h>
+#endif
+
+#include <algorithm>
+
+#ifndef VRLog
+ #if defined( __MINGW32__ )
+ #define VRLog(args...) fprintf(stderr, args)
+ #elif defined( WIN32 )
+ #define VRLog(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__)
+ #else
+ #define VRLog(args...) fprintf(stderr, args)
+ #endif
+#endif
+
+/** Returns the root of the directory the system wants us to store user config data in */
+static std::string GetAppSettingsPath()
+{
+#if defined( WIN32 )
+ WCHAR rwchPath[MAX_PATH];
+
+ if( !SUCCEEDED( SHGetFolderPathW( NULL, CSIDL_LOCAL_APPDATA | CSIDL_FLAG_CREATE, NULL, 0, rwchPath ) ) )
+ {
+ return "";
+ }
+
+ // Convert the path to UTF-8 and store in the output
+ std::string sUserPath = UTF16to8( rwchPath );
+
+ return sUserPath;
+#elif defined( OSX )
+ std::string sSettingsDir;
+ @autoreleasepool {
+ // Search for the path
+ NSArray *paths = NSSearchPathForDirectoriesInDomains( NSApplicationSupportDirectory, NSUserDomainMask, YES );
+ if ( [paths count] == 0 )
+ {
+ return "";
+ }
+
+ NSString *resolvedPath = [paths objectAtIndex:0];
+ resolvedPath = [resolvedPath stringByAppendingPathComponent: @"OpenVR"];
+
+ if ( ![[NSFileManager defaultManager] createDirectoryAtPath: resolvedPath withIntermediateDirectories:YES attributes:nil error:nil] )
+ {
+ return "";
+ }
+
+ sSettingsDir.assign( [resolvedPath UTF8String] );
+ }
+ return sSettingsDir;
+#elif defined( LINUX )
+
+ // As defined by XDG Base Directory Specification
+ // https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
+
+ const char *pchHome = getenv("XDG_CONFIG_HOME");
+ if ( ( pchHome != NULL) && ( pchHome[0] != '\0' ) )
+ {
+ return pchHome;
+ }
+
+ //
+ // XDG_CONFIG_HOME is not defined, use ~/.config instead
+ //
+ pchHome = getenv( "HOME" );
+ if ( pchHome == NULL )
+ {
+ return "";
+ }
+
+ std::string sUserPath( pchHome );
+ sUserPath = Path_Join( sUserPath, ".config" );
+ return sUserPath;
+#else
+ #warning "Unsupported platform"
+#endif
+}
+
+
+// ---------------------------------------------------------------------------
+// Purpose: Constructor
+// ---------------------------------------------------------------------------
+CVRPathRegistry_Public::CVRPathRegistry_Public()
+{
+
+}
+
+// ---------------------------------------------------------------------------
+// Purpose: Computes the registry filename
+// ---------------------------------------------------------------------------
+std::string CVRPathRegistry_Public::GetOpenVRConfigPath()
+{
+ std::string sConfigPath = GetAppSettingsPath();
+ if( sConfigPath.empty() )
+ return "";
+
+#if defined( _WIN32 ) || defined( LINUX )
+ sConfigPath = Path_Join( sConfigPath, "openvr" );
+#elif defined ( OSX )
+ sConfigPath = Path_Join( sConfigPath, ".openvr" );
+#else
+ #warning "Unsupported platform"
+#endif
+ sConfigPath = Path_FixSlashes( sConfigPath );
+ return sConfigPath;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+std::string CVRPathRegistry_Public::GetVRPathRegistryFilename()
+{
+ std::string sOverridePath = GetEnvironmentVariable( "VR_PATHREG_OVERRIDE" );
+ if ( !sOverridePath.empty() )
+ return sOverridePath;
+
+ std::string sPath = GetOpenVRConfigPath();
+ if ( sPath.empty() )
+ return "";
+
+#if defined( _WIN32 )
+ sPath = Path_Join( sPath, "openvrpaths.vrpath" );
+#elif defined ( POSIX )
+ sPath = Path_Join( sPath, "openvrpaths.vrpath" );
+#else
+ #error "Unsupported platform"
+#endif
+ sPath = Path_FixSlashes( sPath );
+ return sPath;
+}
+
+
+// ---------------------------------------------------------------------------
+// Purpose: Converts JSON to a history array
+// ---------------------------------------------------------------------------
+static void ParseStringListFromJson( std::vector< std::string > *pvecHistory, const Json::Value & root, const char *pchArrayName )
+{
+ if( !root.isMember( pchArrayName ) )
+ return;
+
+ const Json::Value & arrayNode = root[ pchArrayName ];
+ if( !arrayNode )
+ {
+ VRLog( "VR Path Registry node %s is not an array\n", pchArrayName );
+ return;
+ }
+
+ pvecHistory->clear();
+ pvecHistory->reserve( arrayNode.size() );
+ for( uint32_t unIndex = 0; unIndex < arrayNode.size(); unIndex++ )
+ {
+ std::string sPath( arrayNode[ unIndex ].asString() );
+ pvecHistory->push_back( sPath );
+ }
+}
+
+
+// ---------------------------------------------------------------------------
+// Purpose: Converts a history array to JSON
+// ---------------------------------------------------------------------------
+static void StringListToJson( const std::vector< std::string > & vecHistory, Json::Value & root, const char *pchArrayName )
+{
+ Json::Value & arrayNode = root[ pchArrayName ];
+ for( auto i = vecHistory.begin(); i != vecHistory.end(); i++ )
+ {
+ arrayNode.append( *i );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CVRPathRegistry_Public::ToJsonString( std::string &sJsonString )
+{
+ std::string sRegPath = GetVRPathRegistryFilename();
+ if( sRegPath.empty() )
+ return false;
+
+ std::string sRegistryContents = Path_ReadTextFile( sRegPath );
+ if( sRegistryContents.empty() )
+ return false;
+
+ sJsonString = sRegistryContents;
+
+ return true;
+}
+
+// Mozilla: see README.mozilla for more details
+// ---------------------------------------------------------------------------
+// Purpose: Loads the config file from its well known location
+// ---------------------------------------------------------------------------
+bool CVRPathRegistry_Public::BLoadFromFile( std::string *psLoadError )
+{
+ std::string sRegPath = GetVRPathRegistryFilename();
+ if( sRegPath.empty() )
+ {
+ if ( psLoadError )
+ {
+ *psLoadError = "Unable to determine VR Path Registry filename";
+ }
+ return false;
+ }
+
+ std::string sRegistryContents = Path_ReadTextFile( sRegPath );
+ if( sRegistryContents.empty() )
+ {
+ if ( psLoadError )
+ {
+ *psLoadError = "Unable to read VR Path Registry from " + sRegPath;
+ }
+ return false;
+ }
+
+ Json::Value root;
+ Json::CharReaderBuilder builder;
+ std::istringstream istream( sRegistryContents );
+ std::string sErrors;
+
+// try
+ {
+ if ( !parseFromStream( builder, istream, &root, &sErrors ) )
+ {
+ if ( psLoadError )
+ {
+ *psLoadError = "Unable to parse " + sRegPath + ": " + sErrors;
+ }
+ return false;
+ }
+
+ ParseStringListFromJson( &m_vecRuntimePath, root, "runtime" );
+ ParseStringListFromJson( &m_vecConfigPath, root, "config" );
+ ParseStringListFromJson( &m_vecLogPath, root, "log" );
+ if ( root.isMember( "external_drivers" ) && root["external_drivers"].isArray() )
+ {
+ ParseStringListFromJson( &m_vecExternalDrivers, root, "external_drivers" );
+ }
+ }
+// catch ( ... )
+// {
+// if ( psLoadError )
+// {
+// *psLoadError = "Unable to parse " + sRegPath + ": exception thrown in JSON library";
+// }
+// return false;
+// }
+
+ return true;
+}
+
+
+// ---------------------------------------------------------------------------
+// Purpose: Saves the config file to its well known location
+// ---------------------------------------------------------------------------
+bool CVRPathRegistry_Public::BSaveToFile() const
+{
+ std::string sRegPath = GetVRPathRegistryFilename();
+ if( sRegPath.empty() )
+ return false;
+
+ Json::Value root;
+
+ root[ "version" ] = 1;
+ root[ "jsonid" ] = "vrpathreg";
+
+ StringListToJson( m_vecRuntimePath, root, "runtime" );
+ StringListToJson( m_vecConfigPath, root, "config" );
+ StringListToJson( m_vecLogPath, root, "log" );
+ StringListToJson( m_vecExternalDrivers, root, "external_drivers" );
+
+ Json::StreamWriterBuilder builder;
+ std::string sRegistryContents = Json::writeString( builder, root );
+
+ // make sure the directory we're writing into actually exists
+ std::string sRegDirectory = Path_StripFilename( sRegPath );
+ if( !BCreateDirectoryRecursive( sRegDirectory.c_str() ) )
+ {
+ VRLog( "Unable to create path registry directory %s\n", sRegDirectory.c_str() );
+ return false;
+ }
+
+ if( !Path_WriteStringToTextFile( sRegPath, sRegistryContents.c_str() ) )
+ {
+ VRLog( "Unable to write VR path registry to %s\n", sRegPath.c_str() );
+ return false;
+ }
+
+ return true;
+}
+
+
+// ---------------------------------------------------------------------------
+// Purpose: Returns the current runtime path or NULL if no path is configured.
+// ---------------------------------------------------------------------------
+std::string CVRPathRegistry_Public::GetRuntimePath() const
+{
+ if( m_vecRuntimePath.empty() )
+ return "";
+ else
+ return m_vecRuntimePath.front().c_str();
+}
+
+
+// ---------------------------------------------------------------------------
+// Purpose: Returns the current config path or NULL if no path is configured.
+// ---------------------------------------------------------------------------
+std::string CVRPathRegistry_Public::GetConfigPath() const
+{
+ if( m_vecConfigPath.empty() )
+ return "";
+ else
+ return m_vecConfigPath.front().c_str();
+}
+
+
+// ---------------------------------------------------------------------------
+// Purpose: Returns the current log path or NULL if no path is configured.
+// ---------------------------------------------------------------------------
+std::string CVRPathRegistry_Public::GetLogPath() const
+{
+ if( m_vecLogPath.empty() )
+ return "";
+ else
+ return m_vecLogPath.front().c_str();
+}
+
+
+
+// ---------------------------------------------------------------------------
+// Purpose: Returns paths using the path registry and the provided override
+// values. Pass NULL for any paths you don't care about.
+// ---------------------------------------------------------------------------
+bool CVRPathRegistry_Public::GetPaths( std::string *psRuntimePath, std::string *psConfigPath, std::string *psLogPath, const char *pchConfigPathOverride, const char *pchLogPathOverride, std::vector<std::string> *pvecExternalDrivers )
+{
+ std::string sLoadError;
+ CVRPathRegistry_Public pathReg;
+ bool bLoadedRegistry = pathReg.BLoadFromFile( &sLoadError );
+ int nCountEnvironmentVariables = 0;
+ int nRequestedPaths = 0;
+
+ if( psRuntimePath )
+ {
+ nRequestedPaths++;
+ if ( GetEnvironmentVariable( k_pchRuntimeOverrideVar ).length() != 0 )
+ {
+ *psRuntimePath = GetEnvironmentVariable( k_pchRuntimeOverrideVar );
+ nCountEnvironmentVariables++;
+ }
+ else if( !pathReg.GetRuntimePath().empty() )
+ {
+ *psRuntimePath = pathReg.GetRuntimePath();
+ }
+ else
+ {
+ *psRuntimePath = "";
+ }
+ }
+
+ if( psConfigPath )
+ {
+ nRequestedPaths++;
+ if ( GetEnvironmentVariable( k_pchConfigOverrideVar ).length() != 0 )
+ {
+ *psConfigPath = GetEnvironmentVariable( k_pchConfigOverrideVar );
+ nCountEnvironmentVariables++;
+ }
+ else if( pchConfigPathOverride )
+ {
+ *psConfigPath = pchConfigPathOverride;
+ }
+ else if( !pathReg.GetConfigPath().empty() )
+ {
+ *psConfigPath = pathReg.GetConfigPath();
+ }
+ else
+ {
+ *psConfigPath = "";
+ }
+ }
+
+ if( psLogPath )
+ {
+ nRequestedPaths++;
+ if ( GetEnvironmentVariable( k_pchLogOverrideVar ).length() != 0 )
+ {
+ *psLogPath = GetEnvironmentVariable( k_pchLogOverrideVar );
+ nCountEnvironmentVariables++;
+ }
+ else if( pchLogPathOverride )
+ {
+ *psLogPath = pchLogPathOverride;
+ }
+ else if( !pathReg.GetLogPath().empty() )
+ {
+ *psLogPath = pathReg.GetLogPath();
+ }
+ else
+ {
+ *psLogPath = "";
+ }
+ }
+
+ if ( pvecExternalDrivers )
+ {
+ *pvecExternalDrivers = pathReg.m_vecExternalDrivers;
+ }
+
+ if ( nCountEnvironmentVariables == nRequestedPaths )
+ {
+ // all three environment variables were set, so we don't need the physical file
+ return true;
+ }
+ else if( !bLoadedRegistry )
+ {
+ VRLog( "%s\n", sLoadError.c_str() );
+ }
+
+ return bLoadedRegistry;
+}
+
diff --git a/gfx/vr/service/openvr/src/vrpathregistry_public.h b/gfx/vr/service/openvr/src/vrpathregistry_public.h
new file mode 100644
index 0000000000..776935a011
--- /dev/null
+++ b/gfx/vr/service/openvr/src/vrpathregistry_public.h
@@ -0,0 +1,45 @@
+//========= Copyright Valve Corporation ============//
+#pragma once
+
+#include <string>
+#include <vector>
+#include <stdint.h>
+
+static const char *k_pchRuntimeOverrideVar = "VR_OVERRIDE";
+static const char *k_pchConfigOverrideVar = "VR_CONFIG_PATH";
+static const char *k_pchLogOverrideVar = "VR_LOG_PATH";
+
+class CVRPathRegistry_Public
+{
+public:
+ static std::string GetVRPathRegistryFilename();
+ static std::string GetOpenVRConfigPath();
+
+public:
+ CVRPathRegistry_Public();
+
+ /** Returns paths using the path registry and the provided override values. Pass NULL for any paths you don't care about.
+ * Returns false if the path registry could not be read. Valid paths might still be returned based on environment variables. */
+ static bool GetPaths( std::string *psRuntimePath, std::string *psConfigPath, std::string *psLogPath, const char *pchConfigPathOverride, const char *pchLogPathOverride, std::vector<std::string> *pvecExternalDrivers = NULL );
+
+ bool BLoadFromFile( std::string *psError = nullptr );
+ bool BSaveToFile() const;
+
+ bool ToJsonString( std::string &sJsonString );
+
+ // methods to get the current values
+ std::string GetRuntimePath() const;
+ std::string GetConfigPath() const;
+ std::string GetLogPath() const;
+
+protected:
+ typedef std::vector< std::string > StringVector_t;
+
+ // index 0 is the current setting
+ StringVector_t m_vecRuntimePath;
+ StringVector_t m_vecLogPath;
+ StringVector_t m_vecConfigPath;
+
+ // full list of external drivers
+ StringVector_t m_vecExternalDrivers;
+};
diff --git a/gfx/vr/service/osvr/ClientKit/ClientKitC.h b/gfx/vr/service/osvr/ClientKit/ClientKitC.h
new file mode 100644
index 0000000000..8309e890d3
--- /dev/null
+++ b/gfx/vr/service/osvr/ClientKit/ClientKitC.h
@@ -0,0 +1,37 @@
+/** @file
+ @brief Header
+
+ Must be c-safe!
+
+ @date 2014
+
+ @author
+ Sensics, Inc.
+ <http://sensics.com/osvr>
+*/
+
+/*
+// Copyright 2014 Sensics, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#ifndef INCLUDED_ClientKitC_h_GUID_8D7DF104_892D_4CB5_2302_7C6BB5BC985C
+#define INCLUDED_ClientKitC_h_GUID_8D7DF104_892D_4CB5_2302_7C6BB5BC985C
+
+#include <osvr/ClientKit/ContextC.h>
+#include <osvr/ClientKit/InterfaceC.h>
+#include <osvr/ClientKit/InterfaceCallbackC.h>
+#include <osvr/ClientKit/SystemCallbackC.h>
+
+#endif
diff --git a/gfx/vr/service/osvr/ClientKit/ContextC.h b/gfx/vr/service/osvr/ClientKit/ContextC.h
new file mode 100644
index 0000000000..e07e1b4a77
--- /dev/null
+++ b/gfx/vr/service/osvr/ClientKit/ContextC.h
@@ -0,0 +1,96 @@
+/** @file
+ @brief Header
+
+ Must be c-safe!
+
+ @todo Apply annotation macros
+
+ @date 2014
+
+ @author
+ Sensics, Inc.
+ <http://sensics.com/osvr>
+*/
+
+/*
+// Copyright 2014 Sensics, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#ifndef INCLUDED_ContextC_h_GUID_3790F330_2425_4486_4C9F_20C300D7DED3
+#define INCLUDED_ContextC_h_GUID_3790F330_2425_4486_4C9F_20C300D7DED3
+
+/* Internal Includes */
+#include <osvr/ClientKit/Export.h>
+#include <osvr/Util/APIBaseC.h>
+#include <osvr/Util/ReturnCodesC.h>
+#include <osvr/Util/AnnotationMacrosC.h>
+#include <osvr/Util/StdInt.h>
+#include <osvr/Util/ClientOpaqueTypesC.h>
+
+/* Library/third-party includes */
+/* none */
+
+/* Standard includes */
+/* none */
+
+OSVR_EXTERN_C_BEGIN
+
+/** @addtogroup ClientKit
+ @{
+*/
+
+/** @brief Initialize the library.
+
+ @param applicationIdentifier A null terminated string identifying your
+ application. Reverse DNS format strongly suggested.
+ @param flags initialization options (reserved) - pass 0 for now.
+
+ @returns Client context - will be needed for subsequent calls
+*/
+OSVR_CLIENTKIT_EXPORT OSVR_ClientContext osvrClientInit(
+ const char applicationIdentifier[], uint32_t flags OSVR_CPP_ONLY(= 0));
+
+/** @brief Updates the state of the context - call regularly in your mainloop.
+
+ @param ctx Client context
+*/
+OSVR_CLIENTKIT_EXPORT OSVR_ReturnCode osvrClientUpdate(OSVR_ClientContext ctx);
+
+/** @brief Checks to see if the client context is fully started up and connected
+ properly to a server.
+
+ If this reports that the client context is not OK, there may not be a server
+ running, or you may just have to call osvrClientUpdate() a few times to
+ permit startup to finish. The return value of this call will not change from
+ failure to success without calling osvrClientUpdate().
+
+ @param ctx Client context
+
+ @return OSVR_RETURN_FAILURE if not yet fully connected/initialized, or if
+ some other error (null context) occurs.
+*/
+OSVR_CLIENTKIT_EXPORT OSVR_ReturnCode
+osvrClientCheckStatus(OSVR_ClientContext ctx);
+
+/** @brief Shutdown the library.
+ @param ctx Client context
+*/
+OSVR_CLIENTKIT_EXPORT OSVR_ReturnCode
+osvrClientShutdown(OSVR_ClientContext ctx);
+
+/** @} */
+OSVR_EXTERN_C_END
+
+#endif
diff --git a/gfx/vr/service/osvr/ClientKit/DisplayC.h b/gfx/vr/service/osvr/ClientKit/DisplayC.h
new file mode 100644
index 0000000000..fb11ca6b53
--- /dev/null
+++ b/gfx/vr/service/osvr/ClientKit/DisplayC.h
@@ -0,0 +1,506 @@
+/** @file
+ @brief Header
+
+ Must be c-safe!
+
+ @date 2015
+
+ @author
+ Sensics, Inc.
+ <http://sensics.com/osvr>
+*/
+
+/*
+// Copyright 2015 Sensics, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#ifndef INCLUDED_DisplayC_h_GUID_8658EDC9_32A2_49A2_5F5C_10F67852AE74
+#define INCLUDED_DisplayC_h_GUID_8658EDC9_32A2_49A2_5F5C_10F67852AE74
+
+/* Internal Includes */
+#include <osvr/ClientKit/Export.h>
+#include <osvr/Util/APIBaseC.h>
+#include <osvr/Util/ReturnCodesC.h>
+#include <osvr/Util/ClientOpaqueTypesC.h>
+#include <osvr/Util/RenderingTypesC.h>
+#include <osvr/Util/MatrixConventionsC.h>
+#include <osvr/Util/Pose3C.h>
+#include <osvr/Util/BoolC.h>
+#include <osvr/Util/RadialDistortionParametersC.h>
+
+/* Library/third-party includes */
+/* none */
+
+/* Standard includes */
+/* none */
+
+OSVR_EXTERN_C_BEGIN
+/** @addtogroup ClientKit
+ @{
+ @name Display API
+ @{
+*/
+
+/** @brief Opaque type of a display configuration. */
+typedef struct OSVR_DisplayConfigObject* OSVR_DisplayConfig;
+
+/** @brief Allocates a display configuration object populated with data from the
+ OSVR system.
+
+ Before this call will succeed, your application will need to be correctly
+ and fully connected to an OSVR server. You may consider putting this call in
+ a loop alternating with osvrClientUpdate() until this call succeeds.
+
+ Data provided by a display configuration object:
+
+ - The logical display topology (number and relationship of viewers, eyes,
+ and surfaces), which remains constant throughout the life of the
+ configuration object. (A method of notification of change here is TBD).
+ - Pose data for viewers (not required for rendering) and pose/view data for
+ eyes (used for rendering) which is based on tracker data: if used, these
+ should be queried every frame.
+ - Projection matrix data for surfaces, which while in current practice may
+ be relatively unchanging, we are not guaranteeing them to be constant:
+ these should be queried every frame.
+ - Video-input-relative viewport size/location for a surface: would like this
+ to be variable, but probably not feasible. If you have input, please
+ comment on the dev mailing list.
+ - Per-surface distortion strategy priorities/availabilities: constant. Note
+ the following, though...
+ - Per-surface distortion strategy parameters: variable, request each frame.
+ (Could make constant with a notification if needed?)
+
+ Important note: While most of this data is immediately available if you are
+ successful in getting a display config object, the pose-based data (viewer
+ pose, eye pose, eye view matrix) needs tracker state, so at least one (and in
+ practice, typically more) osvrClientUpdate() must be performed before a new
+ tracker report is available to populate that state. See
+ osvrClientCheckDisplayStartup() to query if all startup data is available.
+
+ @todo Decide if relative viewport should be constant in a display config,
+ and update docs accordingly.
+
+ @todo Decide if distortion params should be constant in a display config,
+ and update docs accordingly.
+
+ @return OSVR_RETURN_FAILURE if invalid parameters were passed or some other
+ error occurred, in which case the output argument is unmodified.
+*/
+OSVR_CLIENTKIT_EXPORT OSVR_ReturnCode
+osvrClientGetDisplay(OSVR_ClientContext ctx, OSVR_DisplayConfig* disp);
+
+/** @brief Frees a display configuration object. The corresponding context must
+ still be open.
+
+ If you fail to call this, it will be automatically called as part of
+ clean-up when the corresponding context is closed.
+
+ @return OSVR_RETURN_FAILURE if a null config was passed, or if the given
+ display object was already freed.
+*/
+OSVR_CLIENTKIT_EXPORT OSVR_ReturnCode
+osvrClientFreeDisplay(OSVR_DisplayConfig disp);
+
+/** @brief Checks to see if a display is fully configured and ready, including
+ having received its first pose update.
+
+ Once this first succeeds, it will continue to succeed for the lifetime of
+ the display config object, so it is not necessary to keep calling once you
+ get a successful result.
+
+ @return OSVR_RETURN_FAILURE if a null config was passed, or if the given
+ display config object was otherwise not ready for full use.
+*/
+OSVR_CLIENTKIT_EXPORT OSVR_ReturnCode
+osvrClientCheckDisplayStartup(OSVR_DisplayConfig disp);
+
+/** @brief A display config can have one or more display inputs to pass pixels
+ over (HDMI/DVI connections, etc): retrieve the number of display inputs in
+ the current configuration.
+
+ @param disp Display config object.
+ @param[out] numDisplayInputs Number of display inputs in the logical display
+ topology, **constant** throughout the active, valid lifetime of a display
+ config object.
+
+ @sa OSVR_DisplayInputCount
+
+ @return OSVR_RETURN_FAILURE if invalid parameters were passed, in
+ which case the output argument is unmodified.
+*/
+OSVR_CLIENTKIT_EXPORT OSVR_ReturnCode osvrClientGetNumDisplayInputs(
+ OSVR_DisplayConfig disp, OSVR_DisplayInputCount* numDisplayInputs);
+
+/** @brief Retrieve the pixel dimensions of a given display input for a display
+ config
+
+ @param disp Display config object.
+ @param displayInputIndex The zero-based index of the display input.
+ @param[out] width Width (in pixels) of the display input.
+ @param[out] height Height (in pixels) of the display input.
+
+ The out parameters are **constant** throughout the active, valid lifetime of
+ a display config object.
+
+ @sa OSVR_DisplayDimension
+
+ @return OSVR_RETURN_FAILURE if invalid parameters were passed, in
+ which case the output arguments are unmodified.
+*/
+OSVR_CLIENTKIT_EXPORT OSVR_ReturnCode osvrClientGetDisplayDimensions(
+ OSVR_DisplayConfig disp, OSVR_DisplayInputCount displayInputIndex,
+ OSVR_DisplayDimension* width, OSVR_DisplayDimension* height);
+
+/** @brief A display config can have one (or theoretically more) viewers:
+ retrieve the viewer count.
+
+ @param disp Display config object.
+ @param[out] viewers Number of viewers in the logical display topology,
+ **constant** throughout the active, valid lifetime of a display config
+ object.
+
+ @sa OSVR_ViewerCount
+
+ @return OSVR_RETURN_FAILURE if invalid parameters were passed, in which case
+ the output argument is unmodified.
+*/
+OSVR_CLIENTKIT_EXPORT OSVR_ReturnCode
+osvrClientGetNumViewers(OSVR_DisplayConfig disp, OSVR_ViewerCount* viewers);
+
+/** @brief Get the pose of a viewer in a display config.
+
+ Note that there may not necessarily be any surfaces rendered from this pose
+ (it's the unused "center" eye in a stereo configuration, for instance) so
+ only use this if it makes integration into your engine or existing
+ applications (not originally designed for stereo) easier.
+
+ Will only succeed if osvrClientCheckDisplayStartup() succeeds.
+
+ @return OSVR_RETURN_FAILURE if invalid parameters were passed or no pose was
+ yet available, in which case the pose argument is unmodified.
+*/
+OSVR_CLIENTKIT_EXPORT OSVR_ReturnCode osvrClientGetViewerPose(
+ OSVR_DisplayConfig disp, OSVR_ViewerCount viewer, OSVR_Pose3* pose);
+
+/** @brief Each viewer in a display config can have one or more "eyes" which
+ have a substantially similar pose: get the count.
+
+ @param disp Display config object.
+ @param viewer Viewer ID
+ @param[out] eyes Number of eyes for this viewer in the logical display
+ topology, **constant** throughout the active, valid lifetime of a display
+ config object
+
+ @sa OSVR_EyeCount
+
+ @return OSVR_RETURN_FAILURE if invalid parameters were passed, in which case
+ the output argument is unmodified.
+*/
+OSVR_CLIENTKIT_EXPORT OSVR_ReturnCode osvrClientGetNumEyesForViewer(
+ OSVR_DisplayConfig disp, OSVR_ViewerCount viewer, OSVR_EyeCount* eyes);
+
+/** @brief Get the "viewpoint" for the given eye of a viewer in a display
+ config.
+
+ Will only succeed if osvrClientCheckDisplayStartup() succeeds.
+
+ @param disp Display config object
+ @param viewer Viewer ID
+ @param eye Eye ID
+ @param[out] pose Room-space pose (not relative to pose of the viewer)
+
+ @return OSVR_RETURN_FAILURE if invalid parameters were passed or no pose was
+ yet available, in which case the pose argument is unmodified.
+*/
+OSVR_CLIENTKIT_EXPORT OSVR_ReturnCode
+osvrClientGetViewerEyePose(OSVR_DisplayConfig disp, OSVR_ViewerCount viewer,
+ OSVR_EyeCount eye, OSVR_Pose3* pose);
+
+/** @brief Get the view matrix (inverse of pose) for the given eye of a
+ viewer in a display config - matrix of **doubles**.
+
+ Will only succeed if osvrClientCheckDisplayStartup() succeeds.
+
+ @param disp Display config object
+ @param viewer Viewer ID
+ @param eye Eye ID
+ @param flags Bitwise OR of matrix convention flags (see @ref MatrixFlags)
+ @param[out] mat Pass a double[::OSVR_MATRIX_SIZE] to get the transformation
+ matrix from room space to eye space (not relative to pose of the viewer)
+
+ @return OSVR_RETURN_FAILURE if invalid parameters were passed or no pose was
+ yet available, in which case the output argument is unmodified.
+*/
+OSVR_CLIENTKIT_EXPORT OSVR_ReturnCode osvrClientGetViewerEyeViewMatrixd(
+ OSVR_DisplayConfig disp, OSVR_ViewerCount viewer, OSVR_EyeCount eye,
+ OSVR_MatrixConventions flags, double* mat);
+
+/** @brief Get the view matrix (inverse of pose) for the given eye of a
+ viewer in a display config - matrix of **floats**.
+
+ Will only succeed if osvrClientCheckDisplayStartup() succeeds.
+
+ @param disp Display config object
+ @param viewer Viewer ID
+ @param eye Eye ID
+ @param flags Bitwise OR of matrix convention flags (see @ref MatrixFlags)
+ @param[out] mat Pass a float[::OSVR_MATRIX_SIZE] to get the transformation
+ matrix from room space to eye space (not relative to pose of the viewer)
+
+ @return OSVR_RETURN_FAILURE if invalid parameters were passed or no pose was
+ yet available, in which case the output argument is unmodified.
+*/
+OSVR_CLIENTKIT_EXPORT OSVR_ReturnCode osvrClientGetViewerEyeViewMatrixf(
+ OSVR_DisplayConfig disp, OSVR_ViewerCount viewer, OSVR_EyeCount eye,
+ OSVR_MatrixConventions flags, float* mat);
+
+/** @brief Each eye of each viewer in a display config has one or more surfaces
+ (aka "screens") on which content should be rendered.
+
+ @param disp Display config object
+ @param viewer Viewer ID
+ @param eye Eye ID
+ @param[out] surfaces Number of surfaces (numbered [0, surfaces - 1]) for the
+ given viewer and eye. **Constant** throughout the active, valid lifetime of
+ a display config object.
+
+ @sa OSVR_SurfaceCount
+
+ @return OSVR_RETURN_FAILURE if invalid parameters were passed, in which case
+ the output argument is unmodified.
+*/
+OSVR_CLIENTKIT_EXPORT OSVR_ReturnCode osvrClientGetNumSurfacesForViewerEye(
+ OSVR_DisplayConfig disp, OSVR_ViewerCount viewer, OSVR_EyeCount eye,
+ OSVR_SurfaceCount* surfaces);
+
+/** @brief Get the dimensions/location of the viewport **within the display
+ input** for a surface seen by an eye of a viewer in a display config. (This
+ does not include other video inputs that may be on a single virtual desktop,
+ etc. or explicitly account for display configurations that use multiple
+ video inputs. It does not necessarily indicate that a viewport in the sense
+ of glViewport must be created with these parameters, though the parameter
+ order matches for convenience.)
+
+ @param disp Display config object
+ @param viewer Viewer ID
+ @param eye Eye ID
+ @param surface Surface ID
+ @param[out] left Output: Distance in pixels from the left of the video input
+ to the left of the viewport.
+ @param[out] bottom Output: Distance in pixels from the bottom of the video
+ input to the bottom of the viewport.
+ @param[out] width Output: Width of viewport in pixels.
+ @param[out] height Output: Height of viewport in pixels.
+
+
+ @return OSVR_RETURN_FAILURE if invalid parameters were passed, in which case
+ the output arguments are unmodified.
+*/
+OSVR_CLIENTKIT_EXPORT OSVR_ReturnCode
+osvrClientGetRelativeViewportForViewerEyeSurface(
+ OSVR_DisplayConfig disp, OSVR_ViewerCount viewer, OSVR_EyeCount eye,
+ OSVR_SurfaceCount surface, OSVR_ViewportDimension* left,
+ OSVR_ViewportDimension* bottom, OSVR_ViewportDimension* width,
+ OSVR_ViewportDimension* height);
+
+/** @brief Get the index of the display input for a surface seen by an eye of a
+ viewer in a display config.
+
+ This is the OSVR-assigned display input: it may not (and in practice,
+ usually will not) match any platform-specific display indices. This function
+ exists to associate surfaces with video inputs as enumerated by
+ osvrClientGetNumDisplayInputs().
+
+ @param disp Display config object
+ @param viewer Viewer ID
+ @param eye Eye ID
+ @param surface Surface ID
+ @param[out] displayInput Zero-based index of the display input pixels for
+ this surface are tranmitted over.
+
+ This association is **constant** throughout the active, valid lifetime of a
+ display config object.
+
+ @sa osvrClientGetNumDisplayInputs(),
+ osvrClientGetRelativeViewportForViewerEyeSurface()
+
+ @return OSVR_RETURN_FAILURE if invalid parameters were passed, in which
+ case the output argument is unmodified.
+ */
+OSVR_CLIENTKIT_EXPORT OSVR_ReturnCode
+osvrClientGetViewerEyeSurfaceDisplayInputIndex(
+ OSVR_DisplayConfig disp, OSVR_ViewerCount viewer, OSVR_EyeCount eye,
+ OSVR_SurfaceCount surface, OSVR_DisplayInputCount* displayInput);
+
+/** @brief Get the projection matrix for a surface seen by an eye of a viewer
+ in a display config. (double version)
+
+ @param disp Display config object
+ @param viewer Viewer ID
+ @param eye Eye ID
+ @param surface Surface ID
+ @param near Distance from viewpoint to near clipping plane - must be
+ positive.
+ @param far Distance from viewpoint to far clipping plane - must be positive
+ and not equal to near, typically greater than near.
+ @param flags Bitwise OR of matrix convention flags (see @ref MatrixFlags)
+ @param[out] matrix Output projection matrix: supply an array of 16
+ (::OSVR_MATRIX_SIZE) doubles.
+
+ @return OSVR_RETURN_FAILURE if invalid parameters were passed, in which case
+ the output argument is unmodified.
+*/
+OSVR_CLIENTKIT_EXPORT OSVR_ReturnCode
+osvrClientGetViewerEyeSurfaceProjectionMatrixd(
+ OSVR_DisplayConfig disp, OSVR_ViewerCount viewer, OSVR_EyeCount eye,
+ OSVR_SurfaceCount surface, double near, double far,
+ OSVR_MatrixConventions flags, double* matrix);
+
+/** @brief Get the projection matrix for a surface seen by an eye of a viewer
+ in a display config. (float version)
+
+ @param disp Display config object
+ @param viewer Viewer ID
+ @param eye Eye ID
+ @param surface Surface ID
+ @param near Distance to near clipping plane - must be nonzero, typically
+ positive.
+ @param far Distance to far clipping plane - must be nonzero, typically
+ positive and greater than near.
+ @param flags Bitwise OR of matrix convention flags (see @ref MatrixFlags)
+ @param[out] matrix Output projection matrix: supply an array of 16
+ (::OSVR_MATRIX_SIZE) floats.
+
+ @return OSVR_RETURN_FAILURE if invalid parameters were passed, in which case
+ the output argument is unmodified.
+*/
+OSVR_CLIENTKIT_EXPORT OSVR_ReturnCode
+osvrClientGetViewerEyeSurfaceProjectionMatrixf(
+ OSVR_DisplayConfig disp, OSVR_ViewerCount viewer, OSVR_EyeCount eye,
+ OSVR_SurfaceCount surface, float near, float far,
+ OSVR_MatrixConventions flags, float* matrix);
+
+/** @brief Get the clipping planes (positions at unit distance) for a surface
+ seen by an eye of a viewer
+ in a display config.
+
+ This is only for use in integrations that cannot accept a fully-formulated
+ projection matrix as returned by
+ osvrClientGetViewerEyeSurfaceProjectionMatrixf() or
+ osvrClientGetViewerEyeSurfaceProjectionMatrixd(), and may not necessarily
+ provide the same optimizations.
+
+ As all the planes are given at unit (1) distance, before passing these
+ planes to a consuming function in your application/engine, you will typically
+ divide them by your near clipping plane distance.
+
+ @param disp Display config object
+ @param viewer Viewer ID
+ @param eye Eye ID
+ @param surface Surface ID
+ @param[out] left Distance to left clipping plane
+ @param[out] right Distance to right clipping plane
+ @param[out] bottom Distance to bottom clipping plane
+ @param[out] top Distance to top clipping plane
+
+ @return OSVR_RETURN_FAILURE if invalid parameters were passed, in which case
+ the output arguments are unmodified.
+*/
+OSVR_CLIENTKIT_EXPORT OSVR_ReturnCode
+osvrClientGetViewerEyeSurfaceProjectionClippingPlanes(
+ OSVR_DisplayConfig disp, OSVR_ViewerCount viewer, OSVR_EyeCount eye,
+ OSVR_SurfaceCount surface, double* left, double* right, double* bottom,
+ double* top);
+
+/** @brief Determines if a surface seen by an eye of a viewer in a display
+ config requests some distortion to be performed.
+
+ This simply reports true or false, and does not specify which kind of
+ distortion implementations have been parameterized for this display. For
+ each distortion implementation your application supports, you'll want to
+ call the corresponding priority function to find out if it is available.
+
+ @param disp Display config object
+ @param viewer Viewer ID
+ @param eye Eye ID
+ @param surface Surface ID
+ @param[out] distortionRequested Output parameter: whether distortion is
+ requested. **Constant** throughout the active, valid lifetime of a display
+ config object.
+
+ @return OSVR_RETURN_FAILURE if invalid parameters were passed, in which case
+ the output argument is unmodified.
+*/
+OSVR_CLIENTKIT_EXPORT OSVR_ReturnCode
+osvrClientDoesViewerEyeSurfaceWantDistortion(OSVR_DisplayConfig disp,
+ OSVR_ViewerCount viewer,
+ OSVR_EyeCount eye,
+ OSVR_SurfaceCount surface,
+ OSVR_CBool* distortionRequested);
+
+/** @brief Returns the priority/availability of radial distortion parameters for
+ a surface seen by an eye of a viewer in a display config.
+
+ If osvrClientDoesViewerEyeSurfaceWantDistortion() reports false, then the
+ display does not request distortion of any sort, and thus neither this nor
+ any other distortion strategy priority function will report an "available"
+ priority.
+
+ @param disp Display config object
+ @param viewer Viewer ID
+ @param eye Eye ID
+ @param surface Surface ID
+ @param[out] priority Output: the priority level. Negative values
+ (canonically OSVR_DISTORTION_PRIORITY_UNAVAILABLE) indicate this technique
+ not available, higher values indicate higher preference for the given
+ technique based on the device's description. **Constant** throughout the
+ active, valid lifetime of a display config object.
+
+ @return OSVR_RETURN_FAILURE if invalid parameters were passed, in which case
+ the output argument is unmodified.
+*/
+OSVR_CLIENTKIT_EXPORT OSVR_ReturnCode
+osvrClientGetViewerEyeSurfaceRadialDistortionPriority(
+ OSVR_DisplayConfig disp, OSVR_ViewerCount viewer, OSVR_EyeCount eye,
+ OSVR_SurfaceCount surface, OSVR_DistortionPriority* priority);
+
+/** @brief Returns the radial distortion parameters, if known/requested, for a
+ surface seen by an eye of a viewer in a display config.
+
+ Will only succeed if osvrClientGetViewerEyeSurfaceRadialDistortionPriority()
+ reports a non-negative priority.
+
+ @param disp Display config object
+ @param viewer Viewer ID
+ @param eye Eye ID
+ @param surface Surface ID
+ @param[out] params Output: the parameters for radial distortion
+
+ @return OSVR_RETURN_FAILURE if this surface does not have these parameters
+ described, or if invalid parameters were passed, in which case the output
+ argument is unmodified.
+*/
+OSVR_CLIENTKIT_EXPORT OSVR_ReturnCode
+osvrClientGetViewerEyeSurfaceRadialDistortion(
+ OSVR_DisplayConfig disp, OSVR_ViewerCount viewer, OSVR_EyeCount eye,
+ OSVR_SurfaceCount surface, OSVR_RadialDistortionParameters* params);
+
+/** @}
+ @}
+*/
+
+OSVR_EXTERN_C_END
+
+#endif
diff --git a/gfx/vr/service/osvr/ClientKit/Export.h b/gfx/vr/service/osvr/ClientKit/Export.h
new file mode 100644
index 0000000000..eec95c2737
--- /dev/null
+++ b/gfx/vr/service/osvr/ClientKit/Export.h
@@ -0,0 +1,140 @@
+/** @file
+ @brief Automatically-generated export header - do not edit!
+
+ @date 2016
+
+ @author
+ Sensics, Inc.
+ <http://sensics.com/osvr>
+*/
+
+// Copyright 2016 Sensics, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef OSVR_CLIENTKIT_EXPORT_H
+#define OSVR_CLIENTKIT_EXPORT_H
+
+#ifdef OSVR_CLIENTKIT_STATIC_DEFINE
+# define OSVR_CLIENTKIT_EXPORT
+# define OSVR_CLIENTKIT_NO_EXPORT
+#endif
+
+/* Per-compiler advance preventative definition */
+#if defined(__BORLANDC__) || defined(__CODEGEARC__) || defined(__HP_aCC) || \
+ defined(__PGI) || defined(__WATCOMC__)
+/* Compilers that don't support deprecated, according to CMake. */
+# ifndef OSVR_CLIENTKIT_DEPRECATED
+# define OSVR_CLIENTKIT_DEPRECATED
+# endif
+#endif
+
+/* Check for attribute support */
+#if defined(__INTEL_COMPILER)
+/* Checking before GNUC because Intel implements GNU extensions,
+ * so it chooses to define __GNUC__ as well. */
+# if __INTEL_COMPILER >= 1200
+/* Intel compiler 12.0 or newer can handle these attributes per CMake */
+# define OSVR_CLIENTKIT_EXPORT_HEADER_SUPPORTS_ATTRIBUTES
+# endif
+
+#elif defined(__GNUC__)
+# if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2))
+/* GCC 4.2+ */
+# define OSVR_CLIENTKIT_EXPORT_HEADER_SUPPORTS_ATTRIBUTES
+# endif
+#endif
+
+/* Per-platform defines */
+#if defined(_MSC_VER)
+/* MSVC on Windows */
+
+# ifndef OSVR_CLIENTKIT_EXPORT
+# ifdef osvrClientKit_EXPORTS
+/* We are building this library */
+# define OSVR_CLIENTKIT_EXPORT __declspec(dllexport)
+# else
+/* We are using this library */
+# define OSVR_CLIENTKIT_EXPORT __declspec(dllimport)
+# endif
+# endif
+
+# ifndef OSVR_CLIENTKIT_DEPRECATED
+# define OSVR_CLIENTKIT_DEPRECATED __declspec(deprecated)
+# endif
+
+#elif defined(_WIN32) && defined(__GNUC__)
+/* GCC-compatible on Windows */
+
+# ifndef OSVR_CLIENTKIT_EXPORT
+# ifdef osvrClientKit_EXPORTS
+/* We are building this library */
+# define OSVR_CLIENTKIT_EXPORT __attribute__((dllexport))
+# else
+/* We are using this library */
+# define OSVR_CLIENTKIT_EXPORT __attribute__((dllimport))
+# endif
+# endif
+
+# ifndef OSVR_CLIENTKIT_DEPRECATED
+# define OSVR_CLIENTKIT_DEPRECATED __attribute__((__deprecated__))
+# endif
+
+#elif defined(OSVR_CLIENTKIT_EXPORT_HEADER_SUPPORTS_ATTRIBUTES) || \
+ (defined(__APPLE__) && defined(__MACH__))
+/* GCC4.2+ compatible (assuming something *nix-like) and Mac OS X */
+/* (The first macro is defined at the top of the file, if applicable) */
+/* see https://gcc.gnu.org/wiki/Visibility */
+
+# ifndef OSVR_CLIENTKIT_EXPORT
+/* We are building/using this library */
+# define OSVR_CLIENTKIT_EXPORT __attribute__((visibility("default")))
+# endif
+
+# ifndef OSVR_CLIENTKIT_NO_EXPORT
+# define OSVR_CLIENTKIT_NO_EXPORT __attribute__((visibility("hidden")))
+# endif
+
+# ifndef OSVR_CLIENTKIT_DEPRECATED
+# define OSVR_CLIENTKIT_DEPRECATED __attribute__((__deprecated__))
+# endif
+
+#endif
+/* End of platform ifdefs */
+
+/* fallback def */
+#ifndef OSVR_CLIENTKIT_EXPORT
+# define OSVR_CLIENTKIT_EXPORT
+#endif
+
+/* fallback def */
+#ifndef OSVR_CLIENTKIT_NO_EXPORT
+# define OSVR_CLIENTKIT_NO_EXPORT
+#endif
+
+/* fallback def */
+#ifndef OSVR_CLIENTKIT_DEPRECATED_EXPORT
+# define OSVR_CLIENTKIT_DEPRECATED_EXPORT \
+ OSVR_CLIENTKIT_EXPORT OSVR_CLIENTKIT_DEPRECATED
+#endif
+
+/* fallback def */
+#ifndef OSVR_CLIENTKIT_DEPRECATED_NO_EXPORT
+# define OSVR_CLIENTKIT_DEPRECATED_NO_EXPORT \
+ OSVR_CLIENTKIT_NO_EXPORT OSVR_CLIENTKIT_DEPRECATED
+#endif
+
+/* Clean up after ourselves */
+#undef OSVR_CLIENTKIT_EXPORT_HEADER_SUPPORTS_ATTRIBUTES
+
+#endif
diff --git a/gfx/vr/service/osvr/ClientKit/InterfaceC.h b/gfx/vr/service/osvr/ClientKit/InterfaceC.h
new file mode 100644
index 0000000000..a8523b1733
--- /dev/null
+++ b/gfx/vr/service/osvr/ClientKit/InterfaceC.h
@@ -0,0 +1,74 @@
+/** @file
+ @brief Header
+
+ Must be c-safe!
+
+ @date 2014
+
+ @author
+ Sensics, Inc.
+ <http://sensics.com/osvr>
+*/
+
+/*
+// Copyright 2014 Sensics, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#ifndef INCLUDED_InterfaceC_h_GUID_D90BBAA6_AD62_499D_C023_2F6ED8987C17
+#define INCLUDED_InterfaceC_h_GUID_D90BBAA6_AD62_499D_C023_2F6ED8987C17
+
+/* Internal Includes */
+#include <osvr/ClientKit/Export.h>
+#include <osvr/Util/APIBaseC.h>
+#include <osvr/Util/ReturnCodesC.h>
+#include <osvr/Util/AnnotationMacrosC.h>
+#include <osvr/Util/ClientOpaqueTypesC.h>
+
+/* Library/third-party includes */
+/* none */
+
+/* Standard includes */
+/* none */
+
+OSVR_EXTERN_C_BEGIN
+/** @addtogroup ClientKit
+@{
+*/
+
+/** @brief Get the interface associated with the given path.
+ @param ctx Client context
+ @param path A resource path (null-terminated string)
+ @param[out] iface The interface object. May be freed when no longer needed,
+ otherwise it will be freed when the context is closed.
+*/
+OSVR_CLIENTKIT_EXPORT OSVR_ReturnCode osvrClientGetInterface(
+ OSVR_ClientContext ctx, const char path[], OSVR_ClientInterface* iface);
+
+/** @brief Free an interface object before context closure.
+
+ @param ctx Client context
+ @param iface The interface object
+
+ @returns OSVR_RETURN_SUCCESS unless a null context or interface was passed
+ or the given interface was not found in the context (i.e. had already been
+ freed)
+*/
+OSVR_CLIENTKIT_EXPORT OSVR_ReturnCode
+osvrClientFreeInterface(OSVR_ClientContext ctx, OSVR_ClientInterface iface);
+
+/** @} */
+OSVR_EXTERN_C_END
+
+#endif
diff --git a/gfx/vr/service/osvr/ClientKit/InterfaceCallbackC.h b/gfx/vr/service/osvr/ClientKit/InterfaceCallbackC.h
new file mode 100644
index 0000000000..18e2c135a7
--- /dev/null
+++ b/gfx/vr/service/osvr/ClientKit/InterfaceCallbackC.h
@@ -0,0 +1,77 @@
+/** @file
+ @brief Header
+
+ Must be c-safe!
+
+ @date 2014
+
+ @author
+ Sensics, Inc.
+ <http://sensics.com/osvr>
+*/
+
+/*
+// Copyright 2014 Sensics, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#ifndef INCLUDED_InterfaceCallbacksC_h_GUID_8F16E6CB_F998_4ABC_5B6B_4FC1E4B71BC9
+#define INCLUDED_InterfaceCallbacksC_h_GUID_8F16E6CB_F998_4ABC_5B6B_4FC1E4B71BC9
+
+/* Internal Includes */
+#include <osvr/ClientKit/Export.h>
+#include <osvr/Util/APIBaseC.h>
+#include <osvr/Util/ReturnCodesC.h>
+#include <osvr/Util/AnnotationMacrosC.h>
+#include <osvr/Util/ClientOpaqueTypesC.h>
+#include <osvr/Util/ClientCallbackTypesC.h>
+
+/* Library/third-party includes */
+/* none */
+
+/* Standard includes */
+/* none */
+
+OSVR_EXTERN_C_BEGIN
+
+#define OSVR_INTERFACE_CALLBACK_METHOD(TYPE) \
+ /** @brief Register a callback for TYPE reports on an interface */ \
+ OSVR_CLIENTKIT_EXPORT OSVR_ReturnCode osvrRegister##TYPE##Callback( \
+ OSVR_ClientInterface iface, OSVR_##TYPE##Callback cb, void* userdata);
+
+OSVR_INTERFACE_CALLBACK_METHOD(Pose)
+OSVR_INTERFACE_CALLBACK_METHOD(Position)
+OSVR_INTERFACE_CALLBACK_METHOD(Orientation)
+OSVR_INTERFACE_CALLBACK_METHOD(Velocity)
+OSVR_INTERFACE_CALLBACK_METHOD(LinearVelocity)
+OSVR_INTERFACE_CALLBACK_METHOD(AngularVelocity)
+OSVR_INTERFACE_CALLBACK_METHOD(Acceleration)
+OSVR_INTERFACE_CALLBACK_METHOD(LinearAcceleration)
+OSVR_INTERFACE_CALLBACK_METHOD(AngularAcceleration)
+OSVR_INTERFACE_CALLBACK_METHOD(Button)
+OSVR_INTERFACE_CALLBACK_METHOD(Analog)
+OSVR_INTERFACE_CALLBACK_METHOD(Imaging)
+OSVR_INTERFACE_CALLBACK_METHOD(Location2D)
+OSVR_INTERFACE_CALLBACK_METHOD(Direction)
+OSVR_INTERFACE_CALLBACK_METHOD(EyeTracker2D)
+OSVR_INTERFACE_CALLBACK_METHOD(EyeTracker3D)
+OSVR_INTERFACE_CALLBACK_METHOD(EyeTrackerBlink)
+OSVR_INTERFACE_CALLBACK_METHOD(NaviVelocity)
+OSVR_INTERFACE_CALLBACK_METHOD(NaviPosition)
+
+#undef OSVR_INTERFACE_CALLBACK_METHOD
+
+OSVR_EXTERN_C_END
+
+#endif
diff --git a/gfx/vr/service/osvr/ClientKit/InterfaceStateC.h b/gfx/vr/service/osvr/ClientKit/InterfaceStateC.h
new file mode 100644
index 0000000000..46d0b60393
--- /dev/null
+++ b/gfx/vr/service/osvr/ClientKit/InterfaceStateC.h
@@ -0,0 +1,79 @@
+/** @file
+ @brief Header
+
+ Must be c-safe!
+
+ @date 2014
+
+ @author
+ Sensics, Inc.
+ <http://sensics.com/osvr>
+*/
+
+/*
+// Copyright 2014 Sensics, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#ifndef INCLUDED_InterfaceStateC_h_GUID_8F85D178_74B9_4AA9_4E9E_243089411408
+#define INCLUDED_InterfaceStateC_h_GUID_8F85D178_74B9_4AA9_4E9E_243089411408
+
+/* Internal Includes */
+#include <osvr/ClientKit/Export.h>
+#include <osvr/Util/APIBaseC.h>
+#include <osvr/Util/ReturnCodesC.h>
+#include <osvr/Util/AnnotationMacrosC.h>
+#include <osvr/Util/ClientOpaqueTypesC.h>
+#include <osvr/Util/ClientReportTypesC.h>
+#include <osvr/Util/TimeValueC.h>
+
+/* Library/third-party includes */
+/* none */
+
+/* Standard includes */
+/* none */
+
+OSVR_EXTERN_C_BEGIN
+
+#define OSVR_CALLBACK_METHODS(TYPE) \
+ /** @brief Get TYPE state from an interface, returning failure if none \
+ * exists */ \
+ OSVR_CLIENTKIT_EXPORT OSVR_ReturnCode osvrGet##TYPE##State( \
+ OSVR_ClientInterface iface, struct OSVR_TimeValue* timestamp, \
+ OSVR_##TYPE##State* state);
+
+OSVR_CALLBACK_METHODS(Pose)
+OSVR_CALLBACK_METHODS(Position)
+OSVR_CALLBACK_METHODS(Orientation)
+OSVR_CALLBACK_METHODS(Velocity)
+OSVR_CALLBACK_METHODS(LinearVelocity)
+OSVR_CALLBACK_METHODS(AngularVelocity)
+OSVR_CALLBACK_METHODS(Acceleration)
+OSVR_CALLBACK_METHODS(LinearAcceleration)
+OSVR_CALLBACK_METHODS(AngularAcceleration)
+OSVR_CALLBACK_METHODS(Button)
+OSVR_CALLBACK_METHODS(Analog)
+OSVR_CALLBACK_METHODS(Location2D)
+OSVR_CALLBACK_METHODS(Direction)
+OSVR_CALLBACK_METHODS(EyeTracker2D)
+OSVR_CALLBACK_METHODS(EyeTracker3D)
+OSVR_CALLBACK_METHODS(EyeTrackerBlink)
+OSVR_CALLBACK_METHODS(NaviVelocity)
+OSVR_CALLBACK_METHODS(NaviPosition)
+
+#undef OSVR_CALLBACK_METHODS
+
+OSVR_EXTERN_C_END
+
+#endif
diff --git a/gfx/vr/service/osvr/ClientKit/SystemCallbackC.h b/gfx/vr/service/osvr/ClientKit/SystemCallbackC.h
new file mode 100644
index 0000000000..2476d5f21c
--- /dev/null
+++ b/gfx/vr/service/osvr/ClientKit/SystemCallbackC.h
@@ -0,0 +1,47 @@
+/** @file
+ @brief Header
+
+ Must be c-safe!
+
+ @date 2014
+
+ @author
+ Sensics, Inc.
+ <http://sensics.com/osvr>
+*/
+
+/*
+// Copyright 2014 Sensics, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#ifndef INCLUDED_SystemCallbackC_h_GUID_543F3F04_343E_4389_08A0_DEA988EC23F7
+#define INCLUDED_SystemCallbackC_h_GUID_543F3F04_343E_4389_08A0_DEA988EC23F7
+
+/* Internal Includes */
+#include <osvr/ClientKit/Export.h>
+#include <osvr/Util/APIBaseC.h>
+#include <osvr/Util/ReturnCodesC.h>
+#include <osvr/Util/AnnotationMacrosC.h>
+
+/* Library/third-party includes */
+/* none */
+
+/* Standard includes */
+/* none */
+
+OSVR_EXTERN_C_BEGIN
+OSVR_EXTERN_C_END
+
+#endif
diff --git a/gfx/vr/service/osvr/ClientKit/TransformsC.h b/gfx/vr/service/osvr/ClientKit/TransformsC.h
new file mode 100644
index 0000000000..183497dfd8
--- /dev/null
+++ b/gfx/vr/service/osvr/ClientKit/TransformsC.h
@@ -0,0 +1,75 @@
+/** @file
+ @brief Header controlling the OSVR transformation hierarchy
+
+ Must be c-safe!
+
+ @date 2015
+
+ @author
+ Sensics, Inc.
+ <http://sensics.com/osvr>
+*/
+
+/*
+// Copyright 2015 Sensics, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#ifndef INCLUDED_TransformsC_h_GUID_5B5B7438_42D4_4095_E48A_90E2CC13498E
+#define INCLUDED_TransformsC_h_GUID_5B5B7438_42D4_4095_E48A_90E2CC13498E
+
+/* Internal Includes */
+#include <osvr/ClientKit/Export.h>
+#include <osvr/Util/APIBaseC.h>
+#include <osvr/Util/ReturnCodesC.h>
+#include <osvr/Util/ClientOpaqueTypesC.h>
+
+/* Library/third-party includes */
+/* none */
+
+/* Standard includes */
+/* none */
+
+OSVR_EXTERN_C_BEGIN
+
+/** @addtogroup ClientKit
+ @{
+*/
+
+/** @brief Updates the internal "room to world" transformation (applied to all
+ tracker data for this client context instance) based on the user's head
+ orientation, so that the direction the user is facing becomes -Z to your
+ application. Only rotates about the Y axis (yaw).
+
+ Note that this method internally calls osvrClientUpdate() to get a head pose
+ so your callbacks may be called during its execution!
+
+ @param ctx Client context
+*/
+OSVR_CLIENTKIT_EXPORT OSVR_ReturnCode
+osvrClientSetRoomRotationUsingHead(OSVR_ClientContext ctx);
+
+/** @brief Clears/resets the internal "room to world" transformation back to an
+ identity transformation - that is, clears the effect of any other
+ manipulation of the room to world transform.
+
+ @param ctx Client context
+*/
+OSVR_CLIENTKIT_EXPORT OSVR_ReturnCode
+osvrClientClearRoomToWorldTransform(OSVR_ClientContext ctx);
+
+/** @} */
+OSVR_EXTERN_C_END
+
+#endif
diff --git a/gfx/vr/service/osvr/Util/APIBaseC.h b/gfx/vr/service/osvr/Util/APIBaseC.h
new file mode 100644
index 0000000000..5d133f5c52
--- /dev/null
+++ b/gfx/vr/service/osvr/Util/APIBaseC.h
@@ -0,0 +1,50 @@
+/** @file
+ @brief Header providing basic C macros for defining API headers.
+
+ Must be c-safe!
+
+ @date 2014
+
+ @author
+ Sensics, Inc.
+ <http://sensics.com/osvr>
+*/
+
+/*
+// Copyright 2014 Sensics, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#ifndef INCLUDED_APIBaseC_h_GUID_C5A2E769_2ADC_429E_D250_DF0883E6E5DB
+#define INCLUDED_APIBaseC_h_GUID_C5A2E769_2ADC_429E_D250_DF0883E6E5DB
+
+#ifdef __cplusplus
+# define OSVR_C_ONLY(X)
+# define OSVR_CPP_ONLY(X) X
+# define OSVR_EXTERN_C_BEGIN extern "C" {
+# define OSVR_EXTERN_C_END }
+# define OSVR_INLINE inline
+#else
+# define OSVR_C_ONLY(X) X
+# define OSVR_CPP_ONLY(X)
+# define OSVR_EXTERN_C_BEGIN
+# define OSVR_EXTERN_C_END
+# ifdef _MSC_VER
+# define OSVR_INLINE static __inline
+# else
+# define OSVR_INLINE static inline
+# endif
+#endif
+
+#endif
diff --git a/gfx/vr/service/osvr/Util/AnnotationMacrosC.h b/gfx/vr/service/osvr/Util/AnnotationMacrosC.h
new file mode 100644
index 0000000000..6ce645840c
--- /dev/null
+++ b/gfx/vr/service/osvr/Util/AnnotationMacrosC.h
@@ -0,0 +1,232 @@
+/** @file
+ @brief Header containing macros for source-level annotation.
+
+ In theory, supporting MSVC SAL, as well as compatible GCC and
+ Clang attributes. In practice, expanded as time allows and requires.
+
+ Must be c-safe!
+
+ @date 2014
+
+ @author
+ Sensics, Inc.
+ <http://sensics.com/osvr>
+*/
+
+/*
+// Copyright 2014 Sensics, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#ifndef INCLUDED_AnnotationMacrosC_h_GUID_48538D9B_35E3_4E9A_D2B0_D83D51DD5900
+#define INCLUDED_AnnotationMacrosC_h_GUID_48538D9B_35E3_4E9A_D2B0_D83D51DD5900
+
+#ifndef OSVR_DISABLE_ANALYSIS
+
+# if defined(_MSC_VER) && (_MSC_VER >= 1700)
+/* Visual C++ (2012 and newer) */
+/* Using SAL attribute format:
+ * http://msdn.microsoft.com/en-us/library/ms182032(v=vs.120).aspx */
+
+# include <sal.h>
+
+# define OSVR_IN _In_
+# define OSVR_IN_PTR _In_
+# define OSVR_IN_OPT _In_opt_
+# define OSVR_IN_STRZ _In_z_
+# define OSVR_IN_READS(NUM_ELEMENTS) _In_reads_(NUM_ELEMENTS)
+
+# define OSVR_OUT _Out_
+# define OSVR_OUT_PTR _Outptr_
+# define OSVR_OUT_OPT _Out_opt_
+
+# define OSVR_INOUT _Inout_
+# define OSVR_INOUT_PTR _Inout_
+
+# define OSVR_RETURN_WARN_UNUSED _Must_inspect_result_
+# define OSVR_RETURN_SUCCESS_CONDITION(X) _Return_type_success_(X)
+
+/* end of msvc section */
+# elif defined(__GNUC__) && (__GNUC__ >= 4)
+/* section for GCC and GCC-alikes */
+
+# if defined(__clang__)
+/* clang-specific section */
+# endif
+
+# define OSVR_FUNC_NONNULL(X) __attribute__((__nonnull__ X))
+# define OSVR_RETURN_WARN_UNUSED __attribute__((warn_unused_result))
+
+/* end of gcc section and compiler detection */
+# endif
+
+/* end of ndef disable analysis */
+#endif
+
+/* Fallback declarations */
+/**
+@defgroup annotation_macros Static analysis annotation macros
+@brief Wrappers for Microsoft's SAL annotations and others
+@ingroup Util
+
+Use of these is optional, but recommended particularly for C APIs,
+as well as any methods handling a buffer with a length.
+@{
+*/
+/** @name Parameter annotations
+
+ These indicate the role and valid values for parameters to functions.
+
+ At most one of these should be placed before a parameter's type name in the
+ function parameter list, in both the declaration and definition. (They must
+ match!)
+ @{
+*/
+/** @def OSVR_IN
+ @brief Indicates a required function parameter that serves only as input.
+*/
+#ifndef OSVR_IN
+# define OSVR_IN
+#endif
+
+/** @def OSVR_IN_PTR
+ @brief Indicates a required pointer (non-null) function parameter that
+ serves only as input.
+*/
+#ifndef OSVR_IN_PTR
+# define OSVR_IN_PTR
+#endif
+
+/** @def OSVR_IN_OPT
+ @brief Indicates a function parameter (pointer) that serves only as input,
+ but is optional and might be NULL.
+*/
+#ifndef OSVR_IN_OPT
+# define OSVR_IN_OPT
+#endif
+
+/** @def OSVR_IN_STRZ
+ @brief Indicates a null-terminated string function parameter that serves
+ only as input.
+*/
+#ifndef OSVR_IN_STRZ
+# define OSVR_IN_STRZ
+#endif
+
+/** @def OSVR_IN_READS(NUM_ELEMENTS)
+ @brief Indicates a buffer containing input with the specified number of
+ elements.
+
+ The specified number of elements is typically the name of another parameter.
+*/
+#ifndef OSVR_IN_READS
+# define OSVR_IN_READS(NUM_ELEMENTS)
+#endif
+
+/** @def OSVR_OUT
+ @brief Indicates a required function parameter that serves only as output.
+ In C code, since this usually means "pointer", you probably want
+ OSVR_OUT_PTR instead.
+*/
+#ifndef OSVR_OUT
+# define OSVR_OUT
+#endif
+
+/** @def OSVR_OUT_PTR
+ @brief Indicates a required pointer (non-null) function parameter that
+ serves only as output.
+*/
+#ifndef OSVR_OUT_PTR
+# define OSVR_OUT_PTR
+#endif
+
+/** @def OSVR_OUT_OPT
+ @brief Indicates a function parameter (pointer) that serves only as output,
+ but is optional and might be NULL
+*/
+#ifndef OSVR_OUT_OPT
+# define OSVR_OUT_OPT
+#endif
+
+/** @def OSVR_INOUT
+ @brief Indicates a required function parameter that is both read and written
+ to.
+
+ In C code, since this usually means "pointer", you probably want
+ OSVR_INOUT_PTR instead.
+*/
+#ifndef OSVR_INOUT
+# define OSVR_INOUT
+#endif
+
+/** @def OSVR_INOUT_PTR
+ @brief Indicates a required pointer (non-null) function parameter that is
+ both read and written to.
+*/
+#ifndef OSVR_INOUT_PTR
+# define OSVR_INOUT_PTR
+#endif
+
+/* End of parameter annotations. */
+/** @} */
+
+/** @name Function annotations
+
+ These indicate particular relevant aspects about a function. Some
+ duplicate the effective meaning of parameter annotations: applying both
+ allows the fullest extent of static analysis tools to analyze the code,
+ and in some compilers, generate warnings.
+
+ @{
+*/
+/** @def OSVR_FUNC_NONNULL(X)
+ @brief Indicates the parameter(s) that must be non-null.
+
+ @param X A parenthesized list of parameters by number (1-based index)
+
+ Should be placed after a function declaration (but before the
+ semicolon). Repeating in the definition is not needed.
+*/
+#ifndef OSVR_FUNC_NONNULL
+# define OSVR_FUNC_NONNULL(X)
+#endif
+
+/** @def OSVR_RETURN_WARN_UNUSED
+ @brief Indicates the function has a return value that must be used (either a
+ security problem or an obvious bug if not).
+
+ Should be placed before the return value (and virtual keyword, if
+ applicable) in both declaration and definition.
+*/
+#ifndef OSVR_RETURN_WARN_UNUSED
+# define OSVR_RETURN_WARN_UNUSED
+#endif
+/* End of function annotations. */
+/** @} */
+
+/** @def OSVR_RETURN_SUCCESS_CONDITION
+ @brief Applied to a typedef, indicates the condition for `return` under
+ which a function returning it should be considered to have succeeded (thus
+ holding certain specifications).
+
+ Should be placed before the typename in a typedef, with the parameter
+ including the keyword `return` to substitute for the return value.
+*/
+#ifndef OSVR_RETURN_SUCCESS_CONDITION
+# define OSVR_RETURN_SUCCESS_CONDITION(X)
+#endif
+
+/* End of annotation group. */
+/** @} */
+#endif
diff --git a/gfx/vr/service/osvr/Util/BoolC.h b/gfx/vr/service/osvr/Util/BoolC.h
new file mode 100644
index 0000000000..b50ec7cfd7
--- /dev/null
+++ b/gfx/vr/service/osvr/Util/BoolC.h
@@ -0,0 +1,59 @@
+/** @file
+ @brief Header providing a C-safe "bool" type, because we can't depend on
+ Visual Studio providing proper C99 support in external-facing APIs.
+
+ Must be c-safe!
+
+ @date 2015
+
+ @author
+ Sensics, Inc.
+ <http://sensics.com/osvr>
+*/
+
+/*
+// Copyright 2015 Sensics, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#ifndef INCLUDED_BoolC_h_GUID_4F97BE90_2758_4BA5_B0FC_0CA92DEBA210
+#define INCLUDED_BoolC_h_GUID_4F97BE90_2758_4BA5_B0FC_0CA92DEBA210
+
+/* Internal Includes */
+#include <osvr/Util/APIBaseC.h>
+#include <osvr/Util/StdInt.h>
+
+/* Library/third-party includes */
+/* none */
+
+/* Standard includes */
+/* none */
+
+OSVR_EXTERN_C_BEGIN
+/** @addtogroup Util
+@{
+*/
+
+/** @brief A pre-C99-safe bool type. Canonical values for true and false are
+ * provided. Interpretation of other values is not defined. */
+typedef uint8_t OSVR_CBool;
+/** @brief Canonical "true" value for OSVR_CBool */
+#define OSVR_TRUE (1)
+/** @brief Canonical "false" value for OSVR_CBool */
+#define OSVR_FALSE (0)
+
+/** @} */
+OSVR_EXTERN_C_END
+
+#endif
diff --git a/gfx/vr/service/osvr/Util/ChannelCountC.h b/gfx/vr/service/osvr/Util/ChannelCountC.h
new file mode 100644
index 0000000000..dc49b3b171
--- /dev/null
+++ b/gfx/vr/service/osvr/Util/ChannelCountC.h
@@ -0,0 +1,57 @@
+/** @file
+ @brief Header
+
+ Must be c-safe!
+
+ @date 2015
+
+ @author
+ Sensics, Inc.
+ <http://sensics.com/osvr>
+*/
+
+/*
+// Copyright 2015 Sensics, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#ifndef INCLUDED_ChannelCountC_h_GUID_CF7E5EE7_28B0_4B99_E823_DD701904B5D1
+#define INCLUDED_ChannelCountC_h_GUID_CF7E5EE7_28B0_4B99_E823_DD701904B5D1
+
+/* Internal Includes */
+#include <osvr/Util/StdInt.h>
+#include <osvr/Util/APIBaseC.h>
+
+/* Library/third-party includes */
+/* none */
+
+/* Standard includes */
+/* none */
+
+OSVR_EXTERN_C_BEGIN
+
+/** @addtogroup PluginKit
+@{
+*/
+
+/** @brief The integer type specifying a number of channels/sensors or a
+channel/sensor index.
+*/
+typedef uint32_t OSVR_ChannelCount;
+
+/** @} */
+
+OSVR_EXTERN_C_END
+
+#endif
diff --git a/gfx/vr/service/osvr/Util/ClientCallbackTypesC.h b/gfx/vr/service/osvr/Util/ClientCallbackTypesC.h
new file mode 100644
index 0000000000..4a3c53e822
--- /dev/null
+++ b/gfx/vr/service/osvr/Util/ClientCallbackTypesC.h
@@ -0,0 +1,140 @@
+/** @file
+ @brief Header
+
+ Must be c-safe!
+
+ GENERATED - do not edit by hand!
+
+ @date 2014
+
+ @author
+ Sensics, Inc.
+ <http://sensics.com/osvr>
+*/
+
+/*
+// Copyright 2014 Sensics, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#ifndef INCLUDED_ClientCallbackTypesC_h_GUID_4D43A675_C8A4_4BBF_516F_59E6C785E4EF
+#define INCLUDED_ClientCallbackTypesC_h_GUID_4D43A675_C8A4_4BBF_516F_59E6C785E4EF
+
+/* Internal Includes */
+#include <osvr/Util/ClientReportTypesC.h>
+#include <osvr/Util/ImagingReportTypesC.h>
+#include <osvr/Util/ReturnCodesC.h>
+#include <osvr/Util/TimeValueC.h>
+
+/* Library/third-party includes */
+/* none */
+
+/* Standard includes */
+/* none */
+
+OSVR_EXTERN_C_BEGIN
+
+/** @addtogroup ClientKit
+ @{
+*/
+
+/** @name Report callback types
+ @{
+*/
+
+/* generated file - do not edit! */
+/** @brief C function type for a Pose callback */
+typedef void (*OSVR_PoseCallback)(void* userdata,
+ const struct OSVR_TimeValue* timestamp,
+ const struct OSVR_PoseReport* report);
+/** @brief C function type for a Position callback */
+typedef void (*OSVR_PositionCallback)(void* userdata,
+ const struct OSVR_TimeValue* timestamp,
+ const struct OSVR_PositionReport* report);
+/** @brief C function type for a Orientation callback */
+typedef void (*OSVR_OrientationCallback)(
+ void* userdata, const struct OSVR_TimeValue* timestamp,
+ const struct OSVR_OrientationReport* report);
+/** @brief C function type for a Velocity callback */
+typedef void (*OSVR_VelocityCallback)(void* userdata,
+ const struct OSVR_TimeValue* timestamp,
+ const struct OSVR_VelocityReport* report);
+/** @brief C function type for a LinearVelocity callback */
+typedef void (*OSVR_LinearVelocityCallback)(
+ void* userdata, const struct OSVR_TimeValue* timestamp,
+ const struct OSVR_LinearVelocityReport* report);
+/** @brief C function type for a AngularVelocity callback */
+typedef void (*OSVR_AngularVelocityCallback)(
+ void* userdata, const struct OSVR_TimeValue* timestamp,
+ const struct OSVR_AngularVelocityReport* report);
+/** @brief C function type for a Acceleration callback */
+typedef void (*OSVR_AccelerationCallback)(
+ void* userdata, const struct OSVR_TimeValue* timestamp,
+ const struct OSVR_AccelerationReport* report);
+/** @brief C function type for a LinearAcceleration callback */
+typedef void (*OSVR_LinearAccelerationCallback)(
+ void* userdata, const struct OSVR_TimeValue* timestamp,
+ const struct OSVR_LinearAccelerationReport* report);
+/** @brief C function type for a AngularAcceleration callback */
+typedef void (*OSVR_AngularAccelerationCallback)(
+ void* userdata, const struct OSVR_TimeValue* timestamp,
+ const struct OSVR_AngularAccelerationReport* report);
+/** @brief C function type for a Button callback */
+typedef void (*OSVR_ButtonCallback)(void* userdata,
+ const struct OSVR_TimeValue* timestamp,
+ const struct OSVR_ButtonReport* report);
+/** @brief C function type for a Analog callback */
+typedef void (*OSVR_AnalogCallback)(void* userdata,
+ const struct OSVR_TimeValue* timestamp,
+ const struct OSVR_AnalogReport* report);
+/** @brief C function type for a Imaging callback */
+typedef void (*OSVR_ImagingCallback)(void* userdata,
+ const struct OSVR_TimeValue* timestamp,
+ const struct OSVR_ImagingReport* report);
+/** @brief C function type for a Location2D callback */
+typedef void (*OSVR_Location2DCallback)(
+ void* userdata, const struct OSVR_TimeValue* timestamp,
+ const struct OSVR_Location2DReport* report);
+/** @brief C function type for a Direction callback */
+typedef void (*OSVR_DirectionCallback)(
+ void* userdata, const struct OSVR_TimeValue* timestamp,
+ const struct OSVR_DirectionReport* report);
+/** @brief C function type for a EyeTracker2D callback */
+typedef void (*OSVR_EyeTracker2DCallback)(
+ void* userdata, const struct OSVR_TimeValue* timestamp,
+ const struct OSVR_EyeTracker2DReport* report);
+/** @brief C function type for a EyeTracker3D callback */
+typedef void (*OSVR_EyeTracker3DCallback)(
+ void* userdata, const struct OSVR_TimeValue* timestamp,
+ const struct OSVR_EyeTracker3DReport* report);
+/** @brief C function type for a EyeTrackerBlink callback */
+typedef void (*OSVR_EyeTrackerBlinkCallback)(
+ void* userdata, const struct OSVR_TimeValue* timestamp,
+ const struct OSVR_EyeTrackerBlinkReport* report);
+/** @brief C function type for a NaviVelocity callback */
+typedef void (*OSVR_NaviVelocityCallback)(
+ void* userdata, const struct OSVR_TimeValue* timestamp,
+ const struct OSVR_NaviVelocityReport* report);
+/** @brief C function type for a NaviPosition callback */
+typedef void (*OSVR_NaviPositionCallback)(
+ void* userdata, const struct OSVR_TimeValue* timestamp,
+ const struct OSVR_NaviPositionReport* report);
+
+/** @} */
+
+/** @} */
+
+OSVR_EXTERN_C_END
+
+#endif
diff --git a/gfx/vr/service/osvr/Util/ClientOpaqueTypesC.h b/gfx/vr/service/osvr/Util/ClientOpaqueTypesC.h
new file mode 100644
index 0000000000..84203bb0d4
--- /dev/null
+++ b/gfx/vr/service/osvr/Util/ClientOpaqueTypesC.h
@@ -0,0 +1,69 @@
+/** @file
+ @brief Header declaring opaque types used by @ref Client and @ref ClientKit
+
+ Must be c-safe!
+
+ @date 2014
+
+ @author
+ Sensics, Inc.
+ <http://sensics.com/osvr>
+*/
+
+/*
+// Copyright 2014 Sensics, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#ifndef INCLUDED_ClientOpaqueTypesC_h_GUID_24B79ED2_5751_4BA2_1690_BBD250EBC0C1
+#define INCLUDED_ClientOpaqueTypesC_h_GUID_24B79ED2_5751_4BA2_1690_BBD250EBC0C1
+
+/* Internal Includes */
+#include <osvr/Util/APIBaseC.h>
+
+/* Library/third-party includes */
+/* none */
+
+/* Standard includes */
+/* none */
+
+OSVR_EXTERN_C_BEGIN
+
+/** @addtogroup ClientKit
+ @{
+*/
+/** @brief Opaque handle that should be retained by your application. You need
+ only and exactly one.
+
+ Created by osvrClientInit() at application start.
+
+ You are required to clean up this handle with osvrClientShutdown().
+*/
+typedef struct OSVR_ClientContextObject* OSVR_ClientContext;
+
+/** @brief Opaque handle to an interface used for registering callbacks and
+ getting status.
+
+ You are not required to clean up this handle (it will be automatically
+ cleaned up when the context is), but you can if you are no longer using it,
+ using osvrClientFreeInterface() to inform the context that you no longer need
+ this interface.
+*/
+typedef struct OSVR_ClientInterfaceObject* OSVR_ClientInterface;
+
+/** @} */
+
+OSVR_EXTERN_C_END
+
+#endif
diff --git a/gfx/vr/service/osvr/Util/ClientReportTypesC.h b/gfx/vr/service/osvr/Util/ClientReportTypesC.h
new file mode 100644
index 0000000000..09769d2462
--- /dev/null
+++ b/gfx/vr/service/osvr/Util/ClientReportTypesC.h
@@ -0,0 +1,348 @@
+/** @file
+ @brief Header
+
+ Must be c-safe!
+
+ @date 2014
+
+ @author
+ Sensics, Inc.
+ <http://sensics.com/osvr>
+*/
+
+/*
+// Copyright 2014 Sensics, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#ifndef INCLUDED_ClientReportTypesC_h_GUID_E79DAB07_78B7_4795_1EB9_CA6EEB274AEE
+#define INCLUDED_ClientReportTypesC_h_GUID_E79DAB07_78B7_4795_1EB9_CA6EEB274AEE
+
+/* Internal Includes */
+#include <osvr/Util/APIBaseC.h>
+#include <osvr/Util/Pose3C.h>
+#include <osvr/Util/StdInt.h>
+
+#include <osvr/Util/Vec2C.h>
+#include <osvr/Util/Vec3C.h>
+#include <osvr/Util/ChannelCountC.h>
+#include <osvr/Util/BoolC.h>
+
+/* Library/third-party includes */
+/* none */
+
+/* Standard includes */
+/* none */
+
+OSVR_EXTERN_C_BEGIN
+
+/** @addtogroup ClientKit
+ @{
+*/
+
+/** @name State types
+@{
+*/
+/** @brief Type of position state */
+typedef OSVR_Vec3 OSVR_PositionState;
+
+/** @brief Type of orientation state */
+typedef OSVR_Quaternion OSVR_OrientationState;
+
+/** @brief Type of pose state */
+typedef OSVR_Pose3 OSVR_PoseState;
+
+/** @brief Type of linear velocity state */
+typedef OSVR_Vec3 OSVR_LinearVelocityState;
+
+/** @brief The quaternion represents the incremental rotation taking place over
+ a period of dt seconds. Use of dt (which does not necessarily
+ have to be 1, as other velocity/acceleration representations imply) and an
+ incremental quaternion allows device reports to be scaled to avoid aliasing
+*/
+typedef struct OSVR_IncrementalQuaternion {
+ OSVR_Quaternion incrementalRotation;
+ double dt;
+} OSVR_IncrementalQuaternion;
+
+/** @brief Type of angular velocity state: an incremental quaternion, providing
+ the incremental rotation taking place due to velocity over a period of dt
+ seconds.
+*/
+typedef OSVR_IncrementalQuaternion OSVR_AngularVelocityState;
+
+/** @brief Struct for combined velocity state */
+typedef struct OSVR_VelocityState {
+ OSVR_LinearVelocityState linearVelocity;
+ /** @brief Whether the data source reports valid data for
+ #OSVR_VelocityState::linearVelocity */
+ OSVR_CBool linearVelocityValid;
+
+ OSVR_AngularVelocityState angularVelocity;
+ /** @brief Whether the data source reports valid data for
+ #OSVR_VelocityState::angularVelocity */
+ OSVR_CBool angularVelocityValid;
+} OSVR_VelocityState;
+
+/** @brief Type of linear acceleration state */
+typedef OSVR_Vec3 OSVR_LinearAccelerationState;
+
+/** @brief Type of angular acceleration state
+ */
+typedef OSVR_IncrementalQuaternion OSVR_AngularAccelerationState;
+
+/** @brief Struct for combined acceleration state */
+typedef struct OSVR_AccelerationState {
+ OSVR_LinearAccelerationState linearAcceleration;
+ /** @brief Whether the data source reports valid data for
+ #OSVR_AccelerationState::linearAcceleration */
+ OSVR_CBool linearAccelerationValid;
+
+ OSVR_AngularAccelerationState angularAcceleration;
+ /** @brief Whether the data source reports valid data for
+ #OSVR_AccelerationState::angularAcceleration */
+ OSVR_CBool angularAccelerationValid;
+} OSVR_AccelerationState;
+
+/** @brief Type of button state */
+typedef uint8_t OSVR_ButtonState;
+
+/** @brief OSVR_ButtonState value indicating "button down" */
+#define OSVR_BUTTON_PRESSED (1)
+
+/** @brief OSVR_ButtonState value indicating "button up" */
+#define OSVR_BUTTON_NOT_PRESSED (0)
+
+/** @brief Type of analog channel state */
+typedef double OSVR_AnalogState;
+
+/** @} */
+
+/** @name Report types
+ @{
+*/
+/** @brief Report type for a position callback on a tracker interface */
+typedef struct OSVR_PositionReport {
+ /** @brief Identifies the sensor that the report comes from */
+ int32_t sensor;
+ /** @brief The position vector */
+ OSVR_PositionState xyz;
+} OSVR_PositionReport;
+
+/** @brief Report type for an orientation callback on a tracker interface */
+typedef struct OSVR_OrientationReport {
+ /** @brief Identifies the sensor that the report comes from */
+ int32_t sensor;
+ /** @brief The rotation unit quaternion */
+ OSVR_OrientationState rotation;
+} OSVR_OrientationReport;
+
+/** @brief Report type for a pose (position and orientation) callback on a
+ tracker interface
+*/
+typedef struct OSVR_PoseReport {
+ /** @brief Identifies the sensor that the report comes from */
+ int32_t sensor;
+ /** @brief The pose structure, containing a position vector and a rotation
+ quaternion
+ */
+ OSVR_PoseState pose;
+} OSVR_PoseReport;
+
+/** @brief Report type for a velocity (linear and angular) callback on a
+ tracker interface
+*/
+typedef struct OSVR_VelocityReport {
+ /** @brief Identifies the sensor that the report comes from */
+ int32_t sensor;
+ /** @brief The data state - note that not all fields are neccesarily valid,
+ use the `Valid` members to check the status of the other fields.
+ */
+ OSVR_VelocityState state;
+} OSVR_VelocityReport;
+
+/** @brief Report type for a linear velocity callback on a tracker interface
+ */
+typedef struct OSVR_LinearVelocityReport {
+ /** @brief Identifies the sensor that the report comes from */
+ int32_t sensor;
+ /** @brief The state itself */
+ OSVR_LinearVelocityState state;
+} OSVR_LinearVelocityReport;
+
+/** @brief Report type for an angular velocity callback on a tracker interface
+ */
+typedef struct OSVR_AngularVelocityReport {
+ /** @brief Identifies the sensor that the report comes from */
+ int32_t sensor;
+ /** @brief The state itself */
+ OSVR_AngularVelocityState state;
+} OSVR_AngularVelocityReport;
+
+/** @brief Report type for an acceleration (linear and angular) callback on a
+ tracker interface
+*/
+typedef struct OSVR_AccelerationReport {
+ /** @brief Identifies the sensor that the report comes from */
+ int32_t sensor;
+ /** @brief The data state - note that not all fields are neccesarily valid,
+ use the `Valid` members to check the status of the other fields.
+ */
+ OSVR_AccelerationState state;
+} OSVR_AccelerationReport;
+
+/** @brief Report type for a linear acceleration callback on a tracker interface
+ */
+typedef struct OSVR_LinearAccelerationReport {
+ /** @brief Identifies the sensor that the report comes from */
+ int32_t sensor;
+ /** @brief The state itself */
+ OSVR_LinearAccelerationState state;
+} OSVR_LinearAccelerationReport;
+
+/** @brief Report type for an angular acceleration callback on a tracker
+ interface
+*/
+typedef struct OSVR_AngularAccelerationReport {
+ /** @brief Identifies the sensor that the report comes from */
+ int32_t sensor;
+ /** @brief The state itself */
+ OSVR_AngularAccelerationState state;
+} OSVR_AngularAccelerationReport;
+
+/** @brief Report type for a callback on a button interface */
+typedef struct OSVR_ButtonReport {
+ /** @brief Identifies the sensor that the report comes from */
+ int32_t sensor;
+ /** @brief The button state: 1 is pressed, 0 is not pressed. */
+ OSVR_ButtonState state;
+} OSVR_ButtonReport;
+
+/** @brief Report type for a callback on an analog interface */
+typedef struct OSVR_AnalogReport {
+ /** @brief Identifies the sensor/channel that the report comes from */
+ int32_t sensor;
+ /** @brief The analog state. */
+ OSVR_AnalogState state;
+} OSVR_AnalogReport;
+
+/** @brief Type of location within a 2D region/surface, in normalized
+ coordinates (in range [0, 1] in standard OSVR coordinate system)
+*/
+typedef OSVR_Vec2 OSVR_Location2DState;
+
+/** @brief Report type for 2D location */
+typedef struct OSVR_Location2DReport {
+ OSVR_ChannelCount sensor;
+ OSVR_Location2DState location;
+} OSVR_Location2DReport;
+
+/** @brief Type of unit directional vector in 3D with no particular origin */
+typedef OSVR_Vec3 OSVR_DirectionState;
+
+/** @brief Report type for 3D Direction vector */
+typedef struct OSVR_DirectionReport {
+ OSVR_ChannelCount sensor;
+ OSVR_DirectionState direction;
+} OSVR_DirectionReport;
+
+/** @brief Type of eye gaze direction in 3D which contains 3D vector (position)
+ containing gaze base point of the user's respective eye in 3D device
+ coordinates.
+*/
+typedef OSVR_PositionState OSVR_EyeGazeBasePoint3DState;
+
+/** @brief Type of eye gaze position in 2D which contains users's gaze/point of
+ regard in normalized display coordinates (in range [0, 1] in standard OSVR
+ coordinate system)
+*/
+typedef OSVR_Location2DState OSVR_EyeGazePosition2DState;
+
+// typedef OSVR_DirectionState OSVR_EyeGazeBasePoint3DState;
+
+/** @brief Type of 3D vector (direction vector) containing the normalized gaze
+ direction of user's respective eye */
+typedef OSVR_DirectionState OSVR_EyeGazeDirectionState;
+
+/** @brief State for 3D gaze report */
+typedef struct OSVR_EyeTracker3DState {
+ OSVR_CBool directionValid;
+ OSVR_DirectionState direction;
+ OSVR_CBool basePointValid;
+ OSVR_PositionState basePoint;
+} OSVR_EyeTracker3DState;
+
+/** @brief Report type for 3D gaze report */
+typedef struct OSVR_EyeTracker3DReport {
+ OSVR_ChannelCount sensor;
+ OSVR_EyeTracker3DState state;
+} OSVR_EyeTracker3DReport;
+
+/** @brief State for 2D location report */
+typedef OSVR_Location2DState OSVR_EyeTracker2DState;
+
+/** @brief Report type for 2D location report */
+typedef struct OSVR_EyeTracker2DReport {
+ OSVR_ChannelCount sensor;
+ OSVR_EyeTracker2DState state;
+} OSVR_EyeTracker2DReport;
+
+/** @brief State for a blink event */
+typedef OSVR_ButtonState OSVR_EyeTrackerBlinkState;
+
+/** @brief OSVR_EyeTrackerBlinkState value indicating an eyes blink had occurred
+ */
+#define OSVR_EYE_BLINK (1)
+
+/** @brief OSVR_EyeTrackerBlinkState value indicating eyes are not blinking */
+#define OSVR_EYE_NO_BLINK (0)
+
+/** @brief Report type for a blink event */
+typedef struct OSVR_EyeTrackerBlinkReport {
+ OSVR_ChannelCount sensor;
+ OSVR_EyeTrackerBlinkState state;
+} OSVR_EyeTrackerBlinkReport;
+
+/** @brief Report type for an Imaging callback (forward declaration) */
+struct OSVR_ImagingReport;
+
+/** @brief Type of Navigation Velocity state */
+typedef OSVR_Vec2 OSVR_NaviVelocityState;
+
+/** @brief Type of Navigation Position state */
+typedef OSVR_Vec2 OSVR_NaviPositionState;
+
+/** @brief Report type for an navigation velocity callback on a tracker
+ * interface */
+typedef struct OSVR_NaviVelocityReport {
+ OSVR_ChannelCount sensor;
+ /** @brief The 2D vector in world coordinate system, in meters/second */
+ OSVR_NaviVelocityState state;
+} OSVR_NaviVelocityReport;
+
+/** @brief Report type for an navigation position callback on a tracker
+ * interface */
+typedef struct OSVR_NaviPositionReport {
+ OSVR_ChannelCount sensor;
+ /** @brief The 2D vector in world coordinate system, in meters, relative to
+ * starting position */
+ OSVR_NaviPositionState state;
+} OSVR_NaviPositionReport;
+
+/** @} */
+
+/** @} */
+OSVR_EXTERN_C_END
+
+#endif
diff --git a/gfx/vr/service/osvr/Util/Export.h b/gfx/vr/service/osvr/Util/Export.h
new file mode 100644
index 0000000000..24e8d388df
--- /dev/null
+++ b/gfx/vr/service/osvr/Util/Export.h
@@ -0,0 +1,139 @@
+/** @file
+ @brief Automatically-generated export header - do not edit!
+
+ @date 2016
+
+ @author
+ Sensics, Inc.
+ <http://sensics.com/osvr>
+*/
+
+// Copyright 2016 Sensics, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef OSVR_UTIL_EXPORT_H
+#define OSVR_UTIL_EXPORT_H
+
+#ifdef OSVR_UTIL_STATIC_DEFINE
+# define OSVR_UTIL_EXPORT
+# define OSVR_UTIL_NO_EXPORT
+#endif
+
+/* Per-compiler advance preventative definition */
+#if defined(__BORLANDC__) || defined(__CODEGEARC__) || defined(__HP_aCC) || \
+ defined(__PGI) || defined(__WATCOMC__)
+/* Compilers that don't support deprecated, according to CMake. */
+# ifndef OSVR_UTIL_DEPRECATED
+# define OSVR_UTIL_DEPRECATED
+# endif
+#endif
+
+/* Check for attribute support */
+#if defined(__INTEL_COMPILER)
+/* Checking before GNUC because Intel implements GNU extensions,
+ * so it chooses to define __GNUC__ as well. */
+# if __INTEL_COMPILER >= 1200
+/* Intel compiler 12.0 or newer can handle these attributes per CMake */
+# define OSVR_UTIL_EXPORT_HEADER_SUPPORTS_ATTRIBUTES
+# endif
+
+#elif defined(__GNUC__)
+# if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2))
+/* GCC 4.2+ */
+# define OSVR_UTIL_EXPORT_HEADER_SUPPORTS_ATTRIBUTES
+# endif
+#endif
+
+/* Per-platform defines */
+#if defined(_MSC_VER)
+/* MSVC on Windows */
+
+# ifndef OSVR_UTIL_EXPORT
+# ifdef osvrUtil_EXPORTS
+/* We are building this library */
+# define OSVR_UTIL_EXPORT __declspec(dllexport)
+# else
+/* We are using this library */
+# define OSVR_UTIL_EXPORT __declspec(dllimport)
+# endif
+# endif
+
+# ifndef OSVR_UTIL_DEPRECATED
+# define OSVR_UTIL_DEPRECATED __declspec(deprecated)
+# endif
+
+#elif defined(_WIN32) && defined(__GNUC__)
+/* GCC-compatible on Windows */
+
+# ifndef OSVR_UTIL_EXPORT
+# ifdef osvrUtil_EXPORTS
+/* We are building this library */
+# define OSVR_UTIL_EXPORT __attribute__((dllexport))
+# else
+/* We are using this library */
+# define OSVR_UTIL_EXPORT __attribute__((dllimport))
+# endif
+# endif
+
+# ifndef OSVR_UTIL_DEPRECATED
+# define OSVR_UTIL_DEPRECATED __attribute__((__deprecated__))
+# endif
+
+#elif defined(OSVR_UTIL_EXPORT_HEADER_SUPPORTS_ATTRIBUTES) || \
+ (defined(__APPLE__) && defined(__MACH__))
+/* GCC4.2+ compatible (assuming something *nix-like) and Mac OS X */
+/* (The first macro is defined at the top of the file, if applicable) */
+/* see https://gcc.gnu.org/wiki/Visibility */
+
+# ifndef OSVR_UTIL_EXPORT
+/* We are building/using this library */
+# define OSVR_UTIL_EXPORT __attribute__((visibility("default")))
+# endif
+
+# ifndef OSVR_UTIL_NO_EXPORT
+# define OSVR_UTIL_NO_EXPORT __attribute__((visibility("hidden")))
+# endif
+
+# ifndef OSVR_UTIL_DEPRECATED
+# define OSVR_UTIL_DEPRECATED __attribute__((__deprecated__))
+# endif
+
+#endif
+/* End of platform ifdefs */
+
+/* fallback def */
+#ifndef OSVR_UTIL_EXPORT
+# define OSVR_UTIL_EXPORT
+#endif
+
+/* fallback def */
+#ifndef OSVR_UTIL_NO_EXPORT
+# define OSVR_UTIL_NO_EXPORT
+#endif
+
+/* fallback def */
+#ifndef OSVR_UTIL_DEPRECATED_EXPORT
+# define OSVR_UTIL_DEPRECATED_EXPORT OSVR_UTIL_EXPORT OSVR_UTIL_DEPRECATED
+#endif
+
+/* fallback def */
+#ifndef OSVR_UTIL_DEPRECATED_NO_EXPORT
+# define OSVR_UTIL_DEPRECATED_NO_EXPORT \
+ OSVR_UTIL_NO_EXPORT OSVR_UTIL_DEPRECATED
+#endif
+
+/* Clean up after ourselves */
+#undef OSVR_UTIL_EXPORT_HEADER_SUPPORTS_ATTRIBUTES
+
+#endif
diff --git a/gfx/vr/service/osvr/Util/ImagingReportTypesC.h b/gfx/vr/service/osvr/Util/ImagingReportTypesC.h
new file mode 100644
index 0000000000..c3d1a1dee8
--- /dev/null
+++ b/gfx/vr/service/osvr/Util/ImagingReportTypesC.h
@@ -0,0 +1,91 @@
+/** @file
+ @brief Header
+
+ Must be c-safe!
+
+ @date 2015
+
+ @author
+ Sensics, Inc.
+ <http://sensics.com/osvr>
+*/
+
+/*
+// Copyright 2015 Sensics, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#ifndef INCLUDED_ImagingReportTypesC_h_GUID_746A7BF8_B92D_4585_CA72_DC5391DEDF24
+#define INCLUDED_ImagingReportTypesC_h_GUID_746A7BF8_B92D_4585_CA72_DC5391DEDF24
+
+/* Internal Includes */
+#include <osvr/Util/APIBaseC.h>
+#include <osvr/Util/StdInt.h>
+#include <osvr/Util/ChannelCountC.h>
+
+/* Library/third-party includes */
+/* none */
+
+/* Standard includes */
+/* none */
+
+OSVR_EXTERN_C_BEGIN
+
+/** @addtogroup ClientKit
+ @{
+*/
+typedef uint32_t OSVR_ImageDimension;
+typedef uint8_t OSVR_ImageChannels;
+typedef uint8_t OSVR_ImageDepth;
+
+/** @brief Type for raw buffer access to image data */
+typedef unsigned char OSVR_ImageBufferElement;
+
+typedef enum OSVR_ImagingValueType {
+ OSVR_IVT_UNSIGNED_INT = 0,
+ OSVR_IVT_SIGNED_INT = 1,
+ OSVR_IVT_FLOATING_POINT = 2
+} OSVR_ImagingValueType;
+
+typedef struct OSVR_ImagingMetadata {
+ /** @brief height in pixels */
+ OSVR_ImageDimension height;
+ /** @brief width in pixels */
+ OSVR_ImageDimension width;
+ /** @brief number of channels of data for each pixel */
+ OSVR_ImageChannels channels;
+ /** @brief the depth (size) in bytes of each channel - valid values are 1,
+ * 2, 4, and 8 */
+ OSVR_ImageDepth depth;
+ /** @brief Whether values are unsigned ints, signed ints, or floating point
+ */
+ OSVR_ImagingValueType type;
+
+} OSVR_ImagingMetadata;
+
+typedef struct OSVR_ImagingState {
+ OSVR_ImagingMetadata metadata;
+ OSVR_ImageBufferElement* data;
+} OSVR_ImagingState;
+
+typedef struct OSVR_ImagingReport {
+ OSVR_ChannelCount sensor;
+ OSVR_ImagingState state;
+} OSVR_ImagingReport;
+
+/** @} */
+
+OSVR_EXTERN_C_END
+
+#endif
diff --git a/gfx/vr/service/osvr/Util/MatrixConventionsC.h b/gfx/vr/service/osvr/Util/MatrixConventionsC.h
new file mode 100644
index 0000000000..d9d31ef857
--- /dev/null
+++ b/gfx/vr/service/osvr/Util/MatrixConventionsC.h
@@ -0,0 +1,190 @@
+/** @file
+ @brief Header
+
+ Must be c-safe!
+
+ @date 2015
+
+ @author
+ Sensics, Inc.
+ <http://sensics.com/osvr>
+*/
+
+/*
+// Copyright 2015 Sensics, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#ifndef INCLUDED_MatrixConventionsC_h_GUID_6FC7A4C6_E6C5_4A96_1C28_C3D21B909681
+#define INCLUDED_MatrixConventionsC_h_GUID_6FC7A4C6_E6C5_4A96_1C28_C3D21B909681
+
+/* Internal Includes */
+#include <osvr/Util/Export.h>
+#include <osvr/Util/APIBaseC.h>
+#include <osvr/Util/StdInt.h>
+#include <osvr/Util/Pose3C.h>
+#include <osvr/Util/ReturnCodesC.h>
+
+/* Library/third-party includes */
+/* none */
+
+/* Standard includes */
+/* none */
+
+OSVR_EXTERN_C_BEGIN
+
+/** @defgroup MatrixConvention Matrix conventions and bit flags
+ @ingroup UtilMath
+*/
+
+/** @brief Type for passing matrix convention flags.
+ @ingroup MatrixConvention
+*/
+typedef uint16_t OSVR_MatrixConventions;
+
+#ifndef OSVR_DOXYGEN_EXTERNAL
+/** @brief Bitmasks for testing matrix conventions.
+ @ingroup MatrixConvention
+*/
+typedef enum OSVR_MatrixMasks {
+ OSVR_MATRIX_MASK_ROWMAJOR = 0x1,
+ OSVR_MATRIX_MASK_ROWVECTORS = 0x2,
+ OSVR_MATRIX_MASK_LHINPUT = 0x4,
+ OSVR_MATRIX_MASK_UNSIGNEDZ = 0x8
+} OSVR_MatrixMasks;
+#endif
+
+/** @defgroup MatrixFlags Matrix flags
+ @ingroup MatrixConvention
+
+ Bit flags for specifying matrix options. Only one option may be specified
+ per enum, with all the specified options combined with bitwise-or `|`.
+
+ Most methods that take matrix flags only obey ::OSVR_MatrixOrderingFlags and
+ ::OSVR_MatrixVectorFlags - the flags that affect memory order. The remaining
+ flags are for use with projection matrix generation API methods.
+
+ @{
+*/
+/** @brief Flag bit controlling output memory order */
+typedef enum OSVR_MatrixOrderingFlags {
+ /** @brief Column-major memory order (default) */
+ OSVR_MATRIX_COLMAJOR = 0x0,
+ /** @brief Row-major memory order */
+ OSVR_MATRIX_ROWMAJOR = OSVR_MATRIX_MASK_ROWMAJOR
+} OSVR_MatrixOrderingFlags;
+
+/** @brief Flag bit controlling expected input to matrices.
+ (Related to ::OSVR_MatrixOrderingFlags - setting one to non-default results
+ in an output change, but setting both to non-default results in effectively
+ no change in the output. If this blows your mind, just ignore this aside and
+ carry on.)
+*/
+typedef enum OSVR_MatrixVectorFlags {
+ /** @brief Matrix transforms column vectors (default) */
+ OSVR_MATRIX_COLVECTORS = 0x0,
+ /** @brief Matrix transforms row vectors */
+ OSVR_MATRIX_ROWVECTORS = OSVR_MATRIX_MASK_ROWVECTORS
+} OSVR_MatrixVectorFlags;
+
+/** @brief Flag bit to indicate coordinate system input to projection matrix */
+typedef enum OSVR_ProjectionMatrixInputFlags {
+ /** @brief Matrix takes vectors from a right-handed coordinate system
+ (default) */
+ OSVR_MATRIX_RHINPUT = 0x0,
+ /** @brief Matrix takes vectors from a left-handed coordinate system */
+ OSVR_MATRIX_LHINPUT = OSVR_MATRIX_MASK_LHINPUT
+
+} OSVR_ProjectionMatrixInputFlags;
+
+/** @brief Flag bit to indicate the desired post-projection Z value convention
+ */
+typedef enum OSVR_ProjectionMatrixZFlags {
+ /** @brief Matrix maps the near and far planes to signed Z values (in the
+ range [-1, 1]) (default)*/
+ OSVR_MATRIX_SIGNEDZ = 0x0,
+ /** @brief Matrix maps the near and far planes to unsigned Z values (in the
+ range [0, 1]) */
+ OSVR_MATRIX_UNSIGNEDZ = OSVR_MATRIX_MASK_UNSIGNEDZ
+} OSVR_ProjectionMatrixZFlags;
+/** @} */ /* end of matrix flags group */
+
+enum {
+ /** @brief Constant for the number of elements in the matrices we use - 4x4.
+ @ingroup MatrixConvention
+ */
+ OSVR_MATRIX_SIZE = 16
+};
+
+/** @addtogroup UtilMath
+ @{
+*/
+/** @brief Set a matrix of doubles based on a Pose3.
+ @param pose The Pose3 to convert
+ @param flags Memory ordering flag - see @ref MatrixFlags
+ @param[out] mat an array of 16 doubles
+*/
+OSVR_UTIL_EXPORT OSVR_ReturnCode osvrPose3ToMatrixd(
+ OSVR_Pose3 const* pose, OSVR_MatrixConventions flags, double* mat);
+
+/** @brief Set a matrix of floats based on a Pose3.
+ @param pose The Pose3 to convert
+ @param flags Memory ordering flag - see @ref MatrixFlags
+ @param[out] mat an array of 16 floats
+*/
+OSVR_UTIL_EXPORT OSVR_ReturnCode osvrPose3ToMatrixf(
+ OSVR_Pose3 const* pose, OSVR_MatrixConventions flags, float* mat);
+/** @} */
+
+OSVR_EXTERN_C_END
+
+#ifdef __cplusplus
+/** @brief Set a matrix based on a Pose3. (C++-only overload - detecting scalar
+ * type) */
+inline OSVR_ReturnCode osvrPose3ToMatrix(OSVR_Pose3 const* pose,
+ OSVR_MatrixConventions flags,
+ double* mat) {
+ return osvrPose3ToMatrixd(pose, flags, mat);
+}
+
+/** @brief Set a matrix based on a Pose3. (C++-only overload - detecting scalar
+ * type) */
+inline OSVR_ReturnCode osvrPose3ToMatrix(OSVR_Pose3 const* pose,
+ OSVR_MatrixConventions flags,
+ float* mat) {
+ return osvrPose3ToMatrixf(pose, flags, mat);
+}
+
+/** @brief Set a matrix based on a Pose3. (C++-only overload - detects scalar
+ * and takes array rather than pointer) */
+template <typename Scalar>
+inline OSVR_ReturnCode osvrPose3ToMatrix(OSVR_Pose3 const* pose,
+ OSVR_MatrixConventions flags,
+ Scalar mat[OSVR_MATRIX_SIZE]) {
+ return osvrPose3ToMatrix(pose, flags, &(mat[0]));
+}
+/** @brief Set a matrix based on a Pose3. (C++-only overload - detects scalar,
+ * takes array, takes pose by reference) */
+template <typename Scalar>
+inline OSVR_ReturnCode osvrPose3ToMatrix(OSVR_Pose3 const& pose,
+ OSVR_MatrixConventions flags,
+ Scalar mat[OSVR_MATRIX_SIZE]) {
+ return osvrPose3ToMatrix(&pose, flags, &(mat[0]));
+}
+
+#endif
+
+/** @} */
+
+#endif
diff --git a/gfx/vr/service/osvr/Util/PlatformConfig.h b/gfx/vr/service/osvr/Util/PlatformConfig.h
new file mode 100644
index 0000000000..081712d3a3
--- /dev/null
+++ b/gfx/vr/service/osvr/Util/PlatformConfig.h
@@ -0,0 +1,87 @@
+/** @file
+ @brief Auto-configured header
+
+ If this filename ends in `.h`, don't edit it: your edits will
+ be lost next time this file is regenerated!
+
+ Must be c-safe!
+
+ @date 2014
+
+ @author
+ Sensics, Inc.
+ <http://sensics.com/osvr>
+*/
+
+/*
+// Copyright 2014 Sensics, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#ifndef INCLUDED_PlatformConfig_h_GUID_0D10E644_8114_4294_A839_699F39E1F0E0
+#define INCLUDED_PlatformConfig_h_GUID_0D10E644_8114_4294_A839_699F39E1F0E0
+
+/** @def OSVR_HAVE_STRUCT_TIMEVAL_IN_WINSOCK2_H
+ @brief Does the system have struct timeval in <winsock2.h>?
+*/
+#define OSVR_HAVE_STRUCT_TIMEVAL_IN_WINSOCK2_H
+
+/** @def OSVR_HAVE_STRUCT_TIMEVAL_IN_SYS_TIME_H
+ @brief Does the system have struct timeval in <sys/time.h>?
+*/
+
+/*
+ MinGW and similar environments have both winsock and sys/time.h, so
+ we hide this define for disambiguation at the top level.
+*/
+#ifndef OSVR_HAVE_STRUCT_TIMEVAL_IN_WINSOCK2_H
+/* #undef OSVR_HAVE_STRUCT_TIMEVAL_IN_SYS_TIME_H */
+#endif
+
+#if defined(OSVR_HAVE_STRUCT_TIMEVAL_IN_SYS_TIME_H) || \
+ defined(OSVR_HAVE_STRUCT_TIMEVAL_IN_WINSOCK2_H)
+# define OSVR_HAVE_STRUCT_TIMEVAL
+#endif
+
+/**
+ * Platform-specific variables.
+ *
+ * Prefer testing for specific compiler or platform features instead of relying
+ * on these variables.
+ *
+ */
+//@{
+/* #undef OSVR_AIX */
+/* #undef OSVR_ANDROID */
+/* #undef OSVR_BSDOS */
+/* #undef OSVR_FREEBSD */
+/* #undef OSVR_HPUX */
+/* #undef OSVR_IRIX */
+/* #undef OSVR_LINUX */
+/* #undef OSVR_KFREEBSD */
+/* #undef OSVR_NETBSD */
+/* #undef OSVR_OPENBSD */
+/* #undef OSVR_OFS1 */
+/* #undef OSVR_SCO_SV */
+/* #undef OSVR_UNIXWARE */
+/* #undef OSVR_XENIX */
+/* #undef OSVR_SUNOS */
+/* #undef OSVR_TRU64 */
+/* #undef OSVR_ULTRIX */
+/* #undef OSVR_CYGWIN */
+/* #undef OSVR_MACOSX */
+#define OSVR_WINDOWS
+//@}
+
+#endif
diff --git a/gfx/vr/service/osvr/Util/Pose3C.h b/gfx/vr/service/osvr/Util/Pose3C.h
new file mode 100644
index 0000000000..cf56e7cc53
--- /dev/null
+++ b/gfx/vr/service/osvr/Util/Pose3C.h
@@ -0,0 +1,70 @@
+/** @file
+ @brief Header
+
+ Must be c-safe!
+
+ @date 2014
+
+ @author
+ Sensics, Inc.
+ <http://sensics.com/osvr>
+*/
+
+/*
+// Copyright 2014 Sensics, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#ifndef INCLUDED_Pose3C_h_GUID_066CFCE2_229C_4194_5D2B_2602CCD5C439
+#define INCLUDED_Pose3C_h_GUID_066CFCE2_229C_4194_5D2B_2602CCD5C439
+
+/* Internal Includes */
+
+/* Internal Includes */
+#include <osvr/Util/APIBaseC.h>
+#include <osvr/Util/Vec3C.h>
+#include <osvr/Util/QuaternionC.h>
+
+/* Library/third-party includes */
+/* none */
+
+/* Standard includes */
+/* none */
+
+OSVR_EXTERN_C_BEGIN
+
+/** @addtogroup UtilMath
+ @{
+*/
+
+/** @brief A structure defining a 3D (6DOF) rigid body pose: translation and
+ rotation.
+*/
+typedef struct OSVR_Pose3 {
+ /** @brief Position vector */
+ OSVR_Vec3 translation;
+ /** @brief Orientation as a unit quaternion */
+ OSVR_Quaternion rotation;
+} OSVR_Pose3;
+
+/** @brief Set a pose to identity */
+OSVR_INLINE void osvrPose3SetIdentity(OSVR_Pose3* pose) {
+ osvrQuatSetIdentity(&(pose->rotation));
+ osvrVec3Zero(&(pose->translation));
+}
+/** @} */
+
+OSVR_EXTERN_C_END
+
+#endif
diff --git a/gfx/vr/service/osvr/Util/QuaternionC.h b/gfx/vr/service/osvr/Util/QuaternionC.h
new file mode 100644
index 0000000000..065c68bd4e
--- /dev/null
+++ b/gfx/vr/service/osvr/Util/QuaternionC.h
@@ -0,0 +1,92 @@
+/** @file
+ @brief Header
+
+ Must be c-safe!
+
+ @date 2014
+
+ @author
+ Sensics, Inc.
+ <http://sensics.com/osvr>
+*/
+
+/*
+// Copyright 2014 Sensics, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#ifndef INCLUDED_QuaternionC_h_GUID_1470A5FE_8209_41A6_C19E_46077FDF9C66
+#define INCLUDED_QuaternionC_h_GUID_1470A5FE_8209_41A6_C19E_46077FDF9C66
+
+/* Internal Includes */
+#include <osvr/Util/APIBaseC.h>
+
+/* Library/third-party includes */
+/* none */
+
+/* Standard includes */
+/* none */
+
+OSVR_EXTERN_C_BEGIN
+
+/** @addtogroup UtilMath
+ @{
+*/
+/** @brief A structure defining a quaternion, often a unit quaternion
+ * representing 3D rotation.
+ */
+typedef struct OSVR_Quaternion {
+ /** @brief Internal data - direct access not recommended */
+ double data[4];
+} OSVR_Quaternion;
+
+#define OSVR_QUAT_MEMBER(COMPONENT, INDEX) \
+ /** @brief Accessor for quaternion component COMPONENT */ \
+ OSVR_INLINE double osvrQuatGet##COMPONENT(OSVR_Quaternion const* q) { \
+ return q->data[INDEX]; \
+ } \
+ /** @brief Setter for quaternion component COMPONENT */ \
+ OSVR_INLINE void osvrQuatSet##COMPONENT(OSVR_Quaternion* q, double val) { \
+ q->data[INDEX] = val; \
+ }
+
+OSVR_QUAT_MEMBER(W, 0)
+OSVR_QUAT_MEMBER(X, 1)
+OSVR_QUAT_MEMBER(Y, 2)
+OSVR_QUAT_MEMBER(Z, 3)
+
+#undef OSVR_QUAT_MEMBER
+
+/** @brief Set a quaternion to the identity rotation */
+OSVR_INLINE void osvrQuatSetIdentity(OSVR_Quaternion* q) {
+ osvrQuatSetW(q, 1);
+ osvrQuatSetX(q, 0);
+ osvrQuatSetY(q, 0);
+ osvrQuatSetZ(q, 0);
+}
+
+/** @} */
+
+OSVR_EXTERN_C_END
+
+#ifdef __cplusplus
+template <typename StreamType>
+inline StreamType& operator<<(StreamType& os, OSVR_Quaternion const& quat) {
+ os << "(" << osvrQuatGetW(&quat) << ", (" << osvrQuatGetX(&quat) << ", "
+ << osvrQuatGetY(&quat) << ", " << osvrQuatGetZ(&quat) << "))";
+ return os;
+}
+#endif
+
+#endif
diff --git a/gfx/vr/service/osvr/Util/QuatlibInteropC.h b/gfx/vr/service/osvr/Util/QuatlibInteropC.h
new file mode 100644
index 0000000000..1519f02c10
--- /dev/null
+++ b/gfx/vr/service/osvr/Util/QuatlibInteropC.h
@@ -0,0 +1,84 @@
+/** @file
+ @brief Header
+
+ Must be c-safe!
+
+ @date 2014
+
+ @author
+ Sensics, Inc.
+ <http://sensics.com/osvr>
+*/
+
+/*
+// Copyright 2014 Sensics, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#ifndef INCLUDED_QuatlibInteropC_h_GUID_85D92019_F0CC_419C_5F6D_F5A3134AA5D4
+#define INCLUDED_QuatlibInteropC_h_GUID_85D92019_F0CC_419C_5F6D_F5A3134AA5D4
+
+/* Internal Includes */
+#include <osvr/Util/APIBaseC.h>
+#include <osvr/Util/Pose3C.h>
+
+/* Library/third-party includes */
+#include <quat.h>
+
+/* Standard includes */
+#include <string.h>
+
+OSVR_EXTERN_C_BEGIN
+
+/** @addtogroup UtilMath
+ @{
+*/
+OSVR_INLINE void osvrQuatToQuatlib(q_type dest, OSVR_Quaternion const* src) {
+ dest[Q_W] = osvrQuatGetW(src);
+ dest[Q_X] = osvrQuatGetX(src);
+ dest[Q_Y] = osvrQuatGetY(src);
+ dest[Q_Z] = osvrQuatGetZ(src);
+}
+
+OSVR_INLINE void osvrQuatFromQuatlib(OSVR_Quaternion* dest, q_type const src) {
+ osvrQuatSetW(dest, src[Q_W]);
+ osvrQuatSetX(dest, src[Q_X]);
+ osvrQuatSetY(dest, src[Q_Y]);
+ osvrQuatSetZ(dest, src[Q_Z]);
+}
+
+OSVR_INLINE void osvrVec3ToQuatlib(q_vec_type dest, OSVR_Vec3 const* src) {
+ memcpy((void*)(dest), (void const*)(src->data), sizeof(double) * 3);
+}
+
+OSVR_INLINE void osvrVec3FromQuatlib(OSVR_Vec3* dest, q_vec_type const src) {
+ memcpy((void*)(dest->data), (void const*)(src), sizeof(double) * 3);
+}
+
+OSVR_INLINE void osvrPose3ToQuatlib(q_xyz_quat_type* dest,
+ OSVR_Pose3 const* src) {
+ osvrVec3ToQuatlib(dest->xyz, &(src->translation));
+ osvrQuatToQuatlib(dest->quat, &(src->rotation));
+}
+
+OSVR_INLINE void osvrPose3FromQuatlib(OSVR_Pose3* dest,
+ q_xyz_quat_type const* src) {
+ osvrVec3FromQuatlib(&(dest->translation), src->xyz);
+ osvrQuatFromQuatlib(&(dest->rotation), src->quat);
+}
+
+/** @} */
+
+OSVR_EXTERN_C_END
+#endif
diff --git a/gfx/vr/service/osvr/Util/RadialDistortionParametersC.h b/gfx/vr/service/osvr/Util/RadialDistortionParametersC.h
new file mode 100644
index 0000000000..b3fea12625
--- /dev/null
+++ b/gfx/vr/service/osvr/Util/RadialDistortionParametersC.h
@@ -0,0 +1,62 @@
+/** @file
+ @brief Header
+
+ Must be c-safe!
+
+ @date 2015
+
+ @author
+ Sensics, Inc.
+ <http://sensics.com/osvr>
+*/
+
+/*
+// Copyright 2015 Sensics, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#ifndef INCLUDED_RadialDistortionParametersC_h_GUID_925BCEB1_BACA_4DA7_5133_FFF560C72EBD
+#define INCLUDED_RadialDistortionParametersC_h_GUID_925BCEB1_BACA_4DA7_5133_FFF560C72EBD
+
+/* Internal Includes */
+#include <osvr/Util/APIBaseC.h>
+#include <osvr/Util/Vec2C.h>
+#include <osvr/Util/Vec3C.h>
+
+/* Library/third-party includes */
+/* none */
+
+/* Standard includes */
+/* none */
+
+OSVR_EXTERN_C_BEGIN
+
+/** @addtogroup UtilMath
+@{
+*/
+
+/** @brief Parameters for a per-color-component radial distortion shader
+ */
+typedef struct OSVR_RadialDistortionParameters {
+ /** @brief Vector of K1 coefficients for the R, G, B channels*/
+ OSVR_Vec3 k1;
+ /** @brief Center of projection for the radial distortion, relative to the
+ bounds of this surface.
+ */
+ OSVR_Vec2 centerOfProjection;
+} OSVR_RadialDistortionParameters;
+
+OSVR_EXTERN_C_END
+
+#endif
diff --git a/gfx/vr/service/osvr/Util/RenderingTypesC.h b/gfx/vr/service/osvr/Util/RenderingTypesC.h
new file mode 100644
index 0000000000..103d0396d9
--- /dev/null
+++ b/gfx/vr/service/osvr/Util/RenderingTypesC.h
@@ -0,0 +1,134 @@
+/** @file
+ @brief Header with integer types for Viewer, Eye, and Surface
+ counts/indices, as well as viewport information.
+
+ Must be c-safe!
+
+ @date 2015
+
+ @author
+ Sensics, Inc.
+ <http://sensics.com/osvr>
+*/
+
+/*
+// Copyright 2015 Sensics, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#ifndef INCLUDED_RenderingTypesC_h_GUID_6689A6CA_76AC_48AC_A0D0_2902BC95AC35
+#define INCLUDED_RenderingTypesC_h_GUID_6689A6CA_76AC_48AC_A0D0_2902BC95AC35
+
+/* Internal Includes */
+#include <osvr/Util/StdInt.h>
+#include <osvr/Util/APIBaseC.h>
+
+/* Library/third-party includes */
+/* none */
+
+/* Standard includes */
+/* none */
+
+OSVR_EXTERN_C_BEGIN
+
+/** @addtogroup PluginKit
+@{
+*/
+
+/** @brief A count or index for a display input in a display config.
+ */
+typedef uint8_t OSVR_DisplayInputCount;
+
+/** @brief The integer type used in specification of size or location of a
+ display input, in pixels.
+*/
+typedef int32_t OSVR_DisplayDimension;
+
+/** @brief The integer type specifying a number of viewers in a system.
+
+ A "head" is a viewer (though not all viewers are necessarily heads).
+
+ The count is output from osvrClientGetNumViewers().
+
+ When used as an ID/index, it is zero-based, so values range from 0 to (count
+ - 1) inclusive.
+
+ The most frequent count is 1, though higher values are theoretically
+ possible. If you do not handle higher values, do still check and alert the
+ user if their system reports a higher number, as your application may not
+ behave as the user expects.
+*/
+typedef uint32_t OSVR_ViewerCount;
+
+/** @brief The integer type specifying the number of eyes (viewpoints) of a
+ viewer.
+
+ The count for a given viewer is output from osvrClientGetNumEyesForViewer().
+
+ When used as an ID/index, it is zero-based,so values range from 0 to (count
+ - 1) inclusive, for a given viewer.
+
+ Use as an ID/index is not meaningful except in conjunction with the ID of
+ the corresponding viewer. (that is, there is no overall "eye 0", but "viewer
+ 0, eye 0" is meaningful.)
+
+ In practice, the most frequent counts are 1 (e.g. mono) and 2 (e.g. stereo),
+ and for example the latter results in eyes with ID 0 and 1 for the viewer.
+ There is no innate or consistent semantics/meaning ("left" or "right") to
+ indices guaranteed at this time, and applications should not try to infer
+ any.
+*/
+typedef uint8_t OSVR_EyeCount;
+
+/** @brief The integer type specifying the number of surfaces seen by a viewer's
+ eye.
+
+ The count for a given viewer and eye is output from
+ osvrClientGetNumSurfacesForViewerEye(). Note that the count is not
+ necessarily equal between eyes of a viewer.
+
+ When used as an ID/index, it is zero-based, so values range from 0 to (count
+ - 1) inclusive, for a given viewer and eye.
+
+ Use as an ID/index is not meaningful except in conjunction with the IDs of
+ the corresponding viewer and eye. (that is, there is no overall "surface 0",
+ but "viewer 0, eye 0, surface 0" is meaningful.)
+*/
+typedef uint32_t OSVR_SurfaceCount;
+
+/** @brief The integer type used in specification of size or location of a
+ viewport.
+*/
+typedef int32_t OSVR_ViewportDimension;
+
+/** @brief The integer type used to indicate relative priorities of a display
+ distortion strategy. Negative values are defined to mean that strategy is
+ unavailable.
+
+ @sa OSVR_DISTORTION_PRIORITY_UNAVAILABLE
+*/
+typedef int32_t OSVR_DistortionPriority;
+
+/** @brief The constant to return as an OSVR_DistortionPriority if a given
+ strategy is not available for a surface.
+
+ @sa OSVR_DistortionPriority
+*/
+#define OSVR_DISTORTION_PRIORITY_UNAVAILABLE (-1)
+
+/** @} */
+
+OSVR_EXTERN_C_END
+
+#endif
diff --git a/gfx/vr/service/osvr/Util/ReturnCodesC.h b/gfx/vr/service/osvr/Util/ReturnCodesC.h
new file mode 100644
index 0000000000..971798ea41
--- /dev/null
+++ b/gfx/vr/service/osvr/Util/ReturnCodesC.h
@@ -0,0 +1,57 @@
+/** @file
+ @brief Header declaring a type and values for simple C return codes.
+
+ Must be c-safe!
+
+ @date 2014
+
+ @author
+ Sensics, Inc.
+ <http://sensics.com/osvr>
+*/
+
+/*
+// Copyright 2014 Sensics, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#ifndef INCLUDED_ReturnCodesC_h_GUID_C81A2FDE_E5BB_4AAA_70A4_C616DD7C141A
+#define INCLUDED_ReturnCodesC_h_GUID_C81A2FDE_E5BB_4AAA_70A4_C616DD7C141A
+
+/* Internal Includes */
+#include <osvr/Util/APIBaseC.h>
+#include <osvr/Util/AnnotationMacrosC.h>
+
+OSVR_EXTERN_C_BEGIN
+
+/** @addtogroup PluginKit
+ @{
+*/
+/** @name Return Codes
+ @{
+*/
+/** @brief The "success" value for an OSVR_ReturnCode */
+#define OSVR_RETURN_SUCCESS (0)
+/** @brief The "failure" value for an OSVR_ReturnCode */
+#define OSVR_RETURN_FAILURE (1)
+/** @brief Return type from C API OSVR functions. */
+typedef OSVR_RETURN_SUCCESS_CONDITION(
+ return == OSVR_RETURN_SUCCESS) char OSVR_ReturnCode;
+/** @} */
+
+/** @} */ /* end of group */
+
+OSVR_EXTERN_C_END
+
+#endif
diff --git a/gfx/vr/service/osvr/Util/StdInt.h b/gfx/vr/service/osvr/Util/StdInt.h
new file mode 100644
index 0000000000..4fe7117cb5
--- /dev/null
+++ b/gfx/vr/service/osvr/Util/StdInt.h
@@ -0,0 +1,42 @@
+/** @file
+ @brief Header wrapping the C99 standard `stdint` header.
+
+ Must be c-safe!
+
+ @date 2014
+
+ @author
+ Sensics, Inc.
+ <http://sensics.com/osvr>
+*/
+
+/*
+// Copyright 2014 Sensics, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#ifndef INCLUDED_StdInt_h_GUID_C1AAF35C_C704_4DB7_14AC_615730C4619B
+#define INCLUDED_StdInt_h_GUID_C1AAF35C_C704_4DB7_14AC_615730C4619B
+
+/* IWYU pragma: begin_exports */
+
+#if !defined(_MSC_VER) || (defined(_MSC_VER) && _MSC_VER >= 1600)
+# include <stdint.h>
+#else
+# include "MSStdIntC.h"
+#endif
+
+/* IWYU pragma: end_exports */
+
+#endif
diff --git a/gfx/vr/service/osvr/Util/TimeValueC.h b/gfx/vr/service/osvr/Util/TimeValueC.h
new file mode 100644
index 0000000000..eb6a4f7cec
--- /dev/null
+++ b/gfx/vr/service/osvr/Util/TimeValueC.h
@@ -0,0 +1,267 @@
+/** @file
+ @brief Header defining a dependency-free, cross-platform substitute for
+ struct timeval
+
+ Must be c-safe!
+
+ @date 2014
+
+ @author
+ Sensics, Inc.
+ <http://sensics.com/osvr>
+*/
+
+/*
+// Copyright 2014 Sensics, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#ifndef INCLUDED_TimeValueC_h_GUID_A02C6917_124D_4CB3_E63E_07F2DA7144E9
+#define INCLUDED_TimeValueC_h_GUID_A02C6917_124D_4CB3_E63E_07F2DA7144E9
+
+/* Internal Includes */
+#include <osvr/Util/Export.h>
+#include <osvr/Util/APIBaseC.h>
+#include <osvr/Util/AnnotationMacrosC.h>
+#include <osvr/Util/PlatformConfig.h>
+#include <osvr/Util/StdInt.h>
+#include <osvr/Util/BoolC.h>
+
+/* Library/third-party includes */
+/* none */
+
+/* Standard includes */
+/* none */
+
+OSVR_EXTERN_C_BEGIN
+
+/** @defgroup UtilTime Timestamp interaction
+ @ingroup Util
+
+ This provides a level of interoperability with struct timeval on systems
+ with that facility. It provides a neutral representation with sufficiently
+ large types.
+
+ For C++ code, use of std::chrono or boost::chrono instead is recommended.
+
+ Note that these time values may not necessarily correlate between processes
+ so should not be used to estimate or measure latency, etc.
+
+ @{
+*/
+
+/** @brief The signed integer type storing the seconds in a struct
+ OSVR_TimeValue */
+typedef int64_t OSVR_TimeValue_Seconds;
+/** @brief The signed integer type storing the microseconds in a struct
+ OSVR_TimeValue */
+typedef int32_t OSVR_TimeValue_Microseconds;
+
+/** @brief Standardized, portable parallel to struct timeval for representing
+ both absolute times and time intervals.
+
+ Where interpreted as an absolute time, its meaning is to be considered the
+ same as that of the POSIX struct timeval:
+ time since 00:00 Coordinated Universal Time (UTC), January 1, 1970.
+
+ For best results, please keep normalized. Output of all functions here
+ is normalized.
+ */
+typedef struct OSVR_TimeValue {
+ /** @brief Seconds portion of the time value. */
+ OSVR_TimeValue_Seconds seconds;
+ /** @brief Microseconds portion of the time value. */
+ OSVR_TimeValue_Microseconds microseconds;
+} OSVR_TimeValue;
+
+#ifdef OSVR_HAVE_STRUCT_TIMEVAL
+/** @brief Gets the current time in the TimeValue. Parallel to gettimeofday. */
+OSVR_UTIL_EXPORT void osvrTimeValueGetNow(OSVR_OUT OSVR_TimeValue* dest)
+ OSVR_FUNC_NONNULL((1));
+
+struct timeval; /* forward declaration */
+
+/** @brief Converts from a TimeValue struct to your system's struct timeval.
+
+ @param dest Pointer to an empty struct timeval for your platform.
+ @param src A pointer to an OSVR_TimeValue you'd like to convert from.
+
+ If either parameter is NULL, the function will return without doing
+ anything.
+*/
+OSVR_UTIL_EXPORT void osvrTimeValueToStructTimeval(
+ OSVR_OUT struct timeval* dest, OSVR_IN_PTR const OSVR_TimeValue* src)
+ OSVR_FUNC_NONNULL((1, 2));
+
+/** @brief Converts from a TimeValue struct to your system's struct timeval.
+ @param dest An OSVR_TimeValue destination pointer.
+ @param src Pointer to a struct timeval you'd like to convert from.
+
+ The result is normalized.
+
+ If either parameter is NULL, the function will return without doing
+ anything.
+*/
+OSVR_UTIL_EXPORT void osvrStructTimevalToTimeValue(
+ OSVR_OUT OSVR_TimeValue* dest, OSVR_IN_PTR const struct timeval* src)
+ OSVR_FUNC_NONNULL((1, 2));
+#endif
+
+/** @brief "Normalizes" a time value so that the absolute number of microseconds
+ is less than 1,000,000, and that the sign of both components is the same.
+
+ @param tv Address of a struct TimeValue to normalize in place.
+
+ If the given pointer is NULL, this function returns without doing anything.
+*/
+OSVR_UTIL_EXPORT void osvrTimeValueNormalize(OSVR_INOUT_PTR OSVR_TimeValue* tv)
+ OSVR_FUNC_NONNULL((1));
+
+/** @brief Sums two time values, replacing the first with the result.
+
+ @param tvA Destination and first source.
+ @param tvB second source
+
+ If a given pointer is NULL, this function returns without doing anything.
+
+ Both parameters are expected to be in normalized form.
+*/
+OSVR_UTIL_EXPORT void osvrTimeValueSum(OSVR_INOUT_PTR OSVR_TimeValue* tvA,
+ OSVR_IN_PTR const OSVR_TimeValue* tvB)
+ OSVR_FUNC_NONNULL((1, 2));
+
+/** @brief Computes the difference between two time values, replacing the first
+ with the result.
+
+ Effectively, `*tvA = *tvA - *tvB`
+
+ @param tvA Destination and first source.
+ @param tvB second source
+
+ If a given pointer is NULL, this function returns without doing anything.
+
+ Both parameters are expected to be in normalized form.
+*/
+OSVR_UTIL_EXPORT void osvrTimeValueDifference(
+ OSVR_INOUT_PTR OSVR_TimeValue* tvA, OSVR_IN_PTR const OSVR_TimeValue* tvB)
+ OSVR_FUNC_NONNULL((1, 2));
+
+/** @brief Compares two time values (assumed to be normalized), returning
+ the same values as strcmp
+
+ @return <0 if A is earlier than B, 0 if they are the same, and >0 if A
+ is later than B.
+*/
+OSVR_UTIL_EXPORT int osvrTimeValueCmp(OSVR_IN_PTR const OSVR_TimeValue* tvA,
+ OSVR_IN_PTR const OSVR_TimeValue* tvB)
+ OSVR_FUNC_NONNULL((1, 2));
+
+OSVR_EXTERN_C_END
+
+/** @brief Compute the difference between the two time values, returning the
+ duration as a double-precision floating-point number of seconds.
+
+ Effectively, `ret = *tvA - *tvB`
+
+ @param tvA first source.
+ @param tvB second source
+ @return Duration of timespan in seconds (floating-point)
+*/
+OSVR_INLINE double osvrTimeValueDurationSeconds(
+ OSVR_IN_PTR const OSVR_TimeValue* tvA,
+ OSVR_IN_PTR const OSVR_TimeValue* tvB) {
+ OSVR_TimeValue A = *tvA;
+ osvrTimeValueDifference(&A, tvB);
+ double dt = A.seconds + A.microseconds / 1000000.0;
+ return dt;
+}
+
+/** @brief True if A is later than B */
+OSVR_INLINE OSVR_CBool
+osvrTimeValueGreater(OSVR_IN_PTR const OSVR_TimeValue* tvA,
+ OSVR_IN_PTR const OSVR_TimeValue* tvB) {
+ if (!tvA || !tvB) {
+ return OSVR_FALSE;
+ }
+ return ((tvA->seconds > tvB->seconds) ||
+ (tvA->seconds == tvB->seconds &&
+ tvA->microseconds > tvB->microseconds))
+ ? OSVR_TRUE
+ : OSVR_FALSE;
+}
+
+#ifdef __cplusplus
+
+# include <cmath>
+# include <cassert>
+
+/// Returns true if the time value is normalized. Typically used in assertions.
+inline bool osvrTimeValueIsNormalized(const OSVR_TimeValue& tv) {
+# ifdef __APPLE__
+ // apparently standard library used on mac only has floating-point abs?
+ return std::abs(double(tv.microseconds)) < 1000000 &&
+# else
+ return std::abs(tv.microseconds) < 1000000 &&
+# endif
+ ((tv.seconds > 0) == (tv.microseconds > 0));
+}
+
+/// True if A is later than B
+inline bool osvrTimeValueGreater(const OSVR_TimeValue& tvA,
+ const OSVR_TimeValue& tvB) {
+ assert(osvrTimeValueIsNormalized(tvA) &&
+ "First timevalue argument to comparison was not normalized!");
+ assert(osvrTimeValueIsNormalized(tvB) &&
+ "Second timevalue argument to comparison was not normalized!");
+ return (tvA.seconds > tvB.seconds) ||
+ (tvA.seconds == tvB.seconds && tvA.microseconds > tvB.microseconds);
+}
+
+/// Operator > overload for time values
+inline bool operator>(const OSVR_TimeValue& tvA, const OSVR_TimeValue& tvB) {
+ return osvrTimeValueGreater(tvA, tvB);
+}
+
+/// Operator < overload for time values
+inline bool operator<(const OSVR_TimeValue& tvA, const OSVR_TimeValue& tvB) {
+ // Change the order of arguments before forwarding.
+ return osvrTimeValueGreater(tvB, tvA);
+}
+
+/// Operator == overload for time values
+inline bool operator==(const OSVR_TimeValue& tvA, const OSVR_TimeValue& tvB) {
+ assert(osvrTimeValueIsNormalized(tvA) &&
+ "First timevalue argument to equality comparison was not normalized!");
+ assert(
+ osvrTimeValueIsNormalized(tvB) &&
+ "Second timevalue argument to equality comparison was not normalized!");
+ return (tvA.seconds == tvB.seconds) && (tvA.microseconds == tvB.microseconds);
+}
+/// Operator == overload for time values
+inline bool operator!=(const OSVR_TimeValue& tvA, const OSVR_TimeValue& tvB) {
+ assert(osvrTimeValueIsNormalized(tvA) &&
+ "First timevalue argument to "
+ "inequality comparison was not "
+ "normalized!");
+ assert(osvrTimeValueIsNormalized(tvB) &&
+ "Second timevalue argument to "
+ "inequality comparison was not "
+ "normalized!");
+ return (tvA.seconds != tvB.seconds) || (tvA.microseconds != tvB.microseconds);
+}
+#endif
+
+/** @} */
+
+#endif
diff --git a/gfx/vr/service/osvr/Util/Vec2C.h b/gfx/vr/service/osvr/Util/Vec2C.h
new file mode 100644
index 0000000000..0641270c1f
--- /dev/null
+++ b/gfx/vr/service/osvr/Util/Vec2C.h
@@ -0,0 +1,86 @@
+/** @file
+ @brief Header
+
+ Must be c-safe!
+
+ @date 2014
+
+ @author
+ Sensics, Inc.
+ <http://sensics.com/osvr>
+*/
+
+/*
+// Copyright 2014 Sensics, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#ifndef INCLUDED_Vec2C_h_GUID_F9715DE4_2649_4182_0F4C_D62121235D5F
+#define INCLUDED_Vec2C_h_GUID_F9715DE4_2649_4182_0F4C_D62121235D5F
+
+/* Internal Includes */
+#include <osvr/Util/APIBaseC.h>
+
+/* Library/third-party includes */
+/* none */
+
+/* Standard includes */
+/* none */
+
+OSVR_EXTERN_C_BEGIN
+
+/** @addtogroup UtilMath
+ @{
+*/
+/** @brief A structure defining a 2D vector, which represents position
+ */
+typedef struct OSVR_Vec2 {
+ /** @brief Internal array data. */
+ double data[2];
+} OSVR_Vec2;
+
+#define OSVR_VEC_MEMBER(COMPONENT, INDEX) \
+ /** @brief Accessor for Vec2 component COMPONENT */ \
+ OSVR_INLINE double osvrVec2Get##COMPONENT(OSVR_Vec2 const* v) { \
+ return v->data[INDEX]; \
+ } \
+ /** @brief Setter for Vec2 component COMPONENT */ \
+ OSVR_INLINE void osvrVec2Set##COMPONENT(OSVR_Vec2* v, double val) { \
+ v->data[INDEX] = val; \
+ }
+
+OSVR_VEC_MEMBER(X, 0)
+OSVR_VEC_MEMBER(Y, 1)
+
+#undef OSVR_VEC_MEMBER
+
+/** @brief Set a Vec2 to the zero vector */
+OSVR_INLINE void osvrVec2Zero(OSVR_Vec2* v) {
+ osvrVec2SetX(v, 0);
+ osvrVec2SetY(v, 0);
+}
+
+/** @} */
+
+OSVR_EXTERN_C_END
+
+#ifdef __cplusplus
+template <typename StreamType>
+inline StreamType& operator<<(StreamType& os, OSVR_Vec2 const& vec) {
+ os << "(" << vec.data[0] << ", " << vec.data[1] << ")";
+ return os;
+}
+#endif
+
+#endif // INCLUDED_Vec2C_h_GUID_F9715DE4_2649_4182_0F4C_D62121235D5F
diff --git a/gfx/vr/service/osvr/Util/Vec3C.h b/gfx/vr/service/osvr/Util/Vec3C.h
new file mode 100644
index 0000000000..f72327c05b
--- /dev/null
+++ b/gfx/vr/service/osvr/Util/Vec3C.h
@@ -0,0 +1,88 @@
+/** @file
+ @brief Header
+
+ Must be c-safe!
+
+ @date 2014
+
+ @author
+ Sensics, Inc.
+ <http://sensics.com/osvr>
+*/
+
+/*
+// Copyright 2014 Sensics, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#ifndef INCLUDED_Vec3C_h_GUID_BF4E98ED_74CF_4785_DB61_109A00BA74DE
+#define INCLUDED_Vec3C_h_GUID_BF4E98ED_74CF_4785_DB61_109A00BA74DE
+
+/* Internal Includes */
+#include <osvr/Util/APIBaseC.h>
+
+/* Library/third-party includes */
+/* none */
+
+/* Standard includes */
+/* none */
+
+OSVR_EXTERN_C_BEGIN
+
+/** @addtogroup UtilMath
+ @{
+*/
+/** @brief A structure defining a 3D vector, often a position/translation.
+ */
+typedef struct OSVR_Vec3 {
+ /** @brief Internal array data. */
+ double data[3];
+} OSVR_Vec3;
+
+#define OSVR_VEC_MEMBER(COMPONENT, INDEX) \
+ /** @brief Accessor for Vec3 component COMPONENT */ \
+ OSVR_INLINE double osvrVec3Get##COMPONENT(OSVR_Vec3 const* v) { \
+ return v->data[INDEX]; \
+ } \
+ /** @brief Setter for Vec3 component COMPONENT */ \
+ OSVR_INLINE void osvrVec3Set##COMPONENT(OSVR_Vec3* v, double val) { \
+ v->data[INDEX] = val; \
+ }
+
+OSVR_VEC_MEMBER(X, 0)
+OSVR_VEC_MEMBER(Y, 1)
+OSVR_VEC_MEMBER(Z, 2)
+
+#undef OSVR_VEC_MEMBER
+
+/** @brief Set a Vec3 to the zero vector */
+OSVR_INLINE void osvrVec3Zero(OSVR_Vec3* v) {
+ osvrVec3SetX(v, 0);
+ osvrVec3SetY(v, 0);
+ osvrVec3SetZ(v, 0);
+}
+
+/** @} */
+
+OSVR_EXTERN_C_END
+
+#ifdef __cplusplus
+template <typename StreamType>
+inline StreamType& operator<<(StreamType& os, OSVR_Vec3 const& vec) {
+ os << "(" << vec.data[0] << ", " << vec.data[1] << ", " << vec.data[2] << ")";
+ return os;
+}
+#endif
+
+#endif
diff --git a/gfx/vr/vrhost/moz.build b/gfx/vr/vrhost/moz.build
new file mode 100644
index 0000000000..1fa6e77f03
--- /dev/null
+++ b/gfx/vr/vrhost/moz.build
@@ -0,0 +1,40 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+SOURCES += ["/gfx/vr/service/VRSession.cpp", "/gfx/vr/VRShMem.cpp", "vrhostapi.cpp"]
+
+# Since .def files do not support preprocessing, switch which file is used
+# to declare exports. See comments in the files for more info.
+if CONFIG["NIGHTLY_BUILD"]:
+ DEFFILE = "vrhostnightly.def"
+ SOURCES += ["vrhosttest.cpp"]
+else:
+ DEFFILE = "vrhost.def"
+
+LOCAL_INCLUDES += [
+ "/gfx/vr",
+ "/gfx/vr/external_api",
+ "/gfx/vr/service",
+ "/ipc/chromium/src",
+]
+
+EXPORTS.vrhost = ["vrhostex.h"]
+
+DIRS += ["testhost"]
+
+# this is Windows-only for now
+DEFINES["XP_WIN"] = True
+# fixes "lld-link: error: undefined symbol: __imp_moz_xmalloc"
+DEFINES["MOZ_NO_MOZALLOC"] = True
+# fixes "STL code can only be used with infallible ::operator new()"
+DisableStlWrapping()
+
+# Define UNICODE for default support in this dll
+DEFINES["UNICODE"] = True
+DEFINES["_UNICODE"] = True
+
+# Use SharedLibrary to generate the dll
+SharedLibrary("vrhost")
diff --git a/gfx/vr/vrhost/testhost/moz.build b/gfx/vr/vrhost/testhost/moz.build
new file mode 100644
index 0000000000..5b0d3b16e1
--- /dev/null
+++ b/gfx/vr/vrhost/testhost/moz.build
@@ -0,0 +1,12 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+SOURCES += ["testhost.cpp"]
+
+USE_LIBS += ["vrhost"]
+
+# Use Progam to generate the executable
+Program("vrtesthost")
diff --git a/gfx/vr/vrhost/testhost/testhost.cpp b/gfx/vr/vrhost/testhost/testhost.cpp
new file mode 100644
index 0000000000..ae667aaee4
--- /dev/null
+++ b/gfx/vr/vrhost/testhost/testhost.cpp
@@ -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/. */
+
+#include <windows.h>
+#include "vrhost/vrhostex.h"
+
+int main(int argc, char* argv[], char* envp[]) {
+ HINSTANCE hVR = ::LoadLibraryW(L"vrhost.dll");
+ if (hVR != nullptr) {
+ // Sometimes LoadLibrary can set an error, but, if it returns a handle,
+ // then it succeeded. Clear the last error so that subsequent calls to
+ // GetLastError don't improperly attribute failure to another API.
+ ::SetLastError(0);
+
+ PFN_SAMPLE lpfnSample = (PFN_SAMPLE)GetProcAddress(hVR, "SampleExport");
+ if (lpfnSample != nullptr) {
+ lpfnSample();
+ }
+
+ if (strcmp(argv[1], "-testsvc") == 0) {
+ PFN_SAMPLE lpfnService =
+ (PFN_SAMPLE)GetProcAddress(hVR, "TestTheService");
+
+ lpfnService();
+ } else if (strcmp(argv[1], "-testmgr") == 0) {
+ PFN_SAMPLE lpfnManager =
+ (PFN_SAMPLE)GetProcAddress(hVR, "TestTheManager");
+ lpfnManager();
+ } else if (strcmp(argv[1], "-testvrwin") == 0) {
+ PFN_SAMPLE lpfnManager =
+ (PFN_SAMPLE)GetProcAddress(hVR, "TestCreateVRWindow");
+ lpfnManager();
+ }
+
+ ::FreeLibrary(hVR);
+ hVR = nullptr;
+ }
+
+ return 0;
+}
diff --git a/gfx/vr/vrhost/vrhost.def b/gfx/vr/vrhost/vrhost.def
new file mode 100644
index 0000000000..7a80c3b247
--- /dev/null
+++ b/gfx/vr/vrhost/vrhost.def
@@ -0,0 +1,15 @@
+;+# 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/.
+
+LIBRARY vrhost.dll
+
+;+= Note: this file defines exports that are only available in Beta and
+;+= Release builds so that test exports are not exposed.
+;+= Please ensure that these exports are also declared in vrhostnightly.def.
+EXPORTS
+ CreateVRWindow PRIVATE
+ CloseVRWindow PRIVATE
+ SendUIMessageToVRWindow PRIVATE
+ WaitForVREvent PRIVATE
+ SendVRTelemetry PRIVATE \ No newline at end of file
diff --git a/gfx/vr/vrhost/vrhostapi.cpp b/gfx/vr/vrhost/vrhostapi.cpp
new file mode 100644
index 0000000000..508e379a5f
--- /dev/null
+++ b/gfx/vr/vrhost/vrhostapi.cpp
@@ -0,0 +1,374 @@
+/* -*- 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/. */
+
+// vrhostapi.cpp
+// Definition of functions that are exported from this dll
+
+#include "vrhostex.h"
+#include "VRShMem.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <random>
+#include <queue>
+
+#include "windows.h"
+
+class VRShmemInstance {
+ public:
+ VRShmemInstance() = delete;
+ VRShmemInstance(const VRShmemInstance& aRHS) = delete;
+
+ static mozilla::gfx::VRShMem& GetInstance() {
+ static mozilla::gfx::VRShMem shmem(nullptr, true /*aRequiresMutex*/);
+ return shmem;
+ }
+};
+
+// VRWindowManager adds a level of indirection so that system HWND isn't exposed
+// outside of these APIs
+class VRWindowManager {
+ public:
+ HWND GetHWND(uint32_t nId) {
+ if (nId == nWindow) {
+ return hWindow;
+ } else {
+ return nullptr;
+ }
+ }
+
+ uint32_t GetId(HWND hwnd) {
+ if (hwnd == hWindow) {
+ return nWindow;
+ } else {
+ return 0;
+ }
+ }
+
+ HANDLE GetProc(uint32_t nId) {
+ if (nId == nWindow) {
+ return hProc;
+ } else {
+ return nullptr;
+ }
+ }
+
+ HANDLE GetEvent() { return hEvent; }
+
+ uint32_t SetHWND(HWND hwnd, HANDLE hproc, HANDLE hevent) {
+ if (hWindow == nullptr) {
+ MOZ_ASSERT(hwnd != nullptr && hproc != nullptr);
+ hWindow = hwnd;
+ hProc = hproc;
+ hEvent = hevent;
+ nWindow = GetRandomUInt();
+#if defined(DEBUG) && defined(NIGHTLY_BUILD)
+ printf("VRWindowManager: Storing HWND: 0x%p as ID: 0x%X\n", hWindow,
+ nWindow);
+#endif
+ return nWindow;
+ } else {
+ return -1;
+ }
+ }
+
+ uint32_t GetRandomUInt() { return randomGenerator(); }
+
+ static VRWindowManager* GetManager() {
+ if (Instance == nullptr) {
+ Instance = new VRWindowManager();
+ }
+ return Instance;
+ }
+
+ private:
+ static VRWindowManager* Instance;
+
+ // For now, simply store the ID and HWND, and expand
+ // to a map when multiple windows/instances are supported.
+ uint32_t nWindow = 0;
+ HWND hWindow = nullptr;
+ HANDLE hProc = nullptr;
+ HANDLE hEvent = nullptr;
+ std::random_device randomGenerator;
+};
+VRWindowManager* VRWindowManager::Instance = nullptr;
+
+class VRTelemetryManager {
+ public:
+ void SendTelemetry(uint32_t aTelemetryId, uint32_t aValue) {
+ if (!aTelemetryId) {
+ return;
+ }
+
+ mozilla::gfx::VRTelemetryState telemetryState = {0};
+ VRShmemInstance::GetInstance().PullTelemetryState(telemetryState);
+
+ if (telemetryState.uid == 0) {
+ telemetryState.uid = sUid;
+ }
+
+ switch (mozilla::gfx::VRTelemetryId(aTelemetryId)) {
+ case mozilla::gfx::VRTelemetryId::INSTALLED_FROM:
+ MOZ_ASSERT(aValue <= 0x07,
+ "VRTelemetryId::INSTALLED_FROM only allows 3 bits.");
+ telemetryState.installedFrom = true;
+ telemetryState.installedFromValue = aValue;
+ break;
+ case mozilla::gfx::VRTelemetryId::ENTRY_METHOD:
+ MOZ_ASSERT(aValue <= 0x07,
+ "VRTelemetryId::ENTRY_METHOD only allows 3 bits.");
+ telemetryState.entryMethod = true;
+ telemetryState.entryMethodValue = aValue;
+ break;
+ case mozilla::gfx::VRTelemetryId::FIRST_RUN:
+ MOZ_ASSERT(aValue <= 0x01,
+ "VRTelemetryId::FIRST_RUN only allows 1 bit.");
+ telemetryState.firstRun = true;
+ telemetryState.firstRunValue = aValue;
+ break;
+ default:
+ MOZ_CRASH("Undefined VR telemetry type.");
+ break;
+ }
+ VRShmemInstance::GetInstance().PushTelemetryState(telemetryState);
+ ++sUid;
+ }
+
+ static VRTelemetryManager* GetManager() {
+ if (Instance == nullptr) {
+ Instance = new VRTelemetryManager();
+ }
+ return Instance;
+ }
+
+ private:
+ static VRTelemetryManager* Instance;
+ static uint32_t sUid; // It starts from 1, Zero means the data is read yet
+ // from VRManager.
+};
+uint32_t VRTelemetryManager::sUid = 1;
+VRTelemetryManager* VRTelemetryManager::Instance = nullptr;
+
+// Struct to send params to StartFirefoxThreadProc
+struct StartFirefoxParams {
+ char* firefoxFolder;
+ char* firefoxProfileFolder;
+ HANDLE hProcessFx;
+};
+
+// Helper threadproc function for CreateVRWindow
+DWORD StartFirefoxThreadProc(_In_ LPVOID lpParameter) {
+ wchar_t cmd[] = L"%Sfirefox.exe -wait-for-browser -profile %S --fxr";
+
+ StartFirefoxParams* params = static_cast<StartFirefoxParams*>(lpParameter);
+ wchar_t cmdWithPath[MAX_PATH + MAX_PATH] = {0};
+ int err = swprintf_s(cmdWithPath, ARRAYSIZE(cmdWithPath), cmd,
+ params->firefoxFolder, params->firefoxProfileFolder);
+
+ if (err != -1) {
+ PROCESS_INFORMATION procFx = {0};
+ STARTUPINFO startupInfoFx = {0};
+
+#if defined(DEBUG) && defined(NIGHTLY_BUILD)
+ printf("Starting Firefox via: %S\n", cmdWithPath);
+#endif
+
+ // Start Firefox
+ bool fCreateContentProc = ::CreateProcess(nullptr, // lpApplicationName,
+ cmdWithPath,
+ nullptr, // lpProcessAttributes,
+ nullptr, // lpThreadAttributes,
+ TRUE, // bInheritHandles,
+ 0, // dwCreationFlags,
+ nullptr, // lpEnvironment,
+ nullptr, // lpCurrentDirectory,
+ &startupInfoFx, &procFx);
+
+ if (!fCreateContentProc) {
+ printf("Failed to create Firefox process");
+ }
+
+ params->hProcessFx = procFx.hProcess;
+ }
+
+ return 0;
+}
+
+// This export is responsible for starting up a new VR window in Firefox and
+// returning data related to its creation back to the caller.
+// See nsFxrCommandLineHandler::Handle for more details about the bootstrapping
+// process with Firefox.
+void CreateVRWindow(char* firefoxFolderPath, char* firefoxProfilePath,
+ uint32_t dxgiAdapterID, uint32_t widthHost,
+ uint32_t heightHost, uint32_t* windowId, void** hTex,
+ uint32_t* width, uint32_t* height) {
+ mozilla::gfx::VRWindowState windowState = {0};
+
+ int err = sprintf_s(windowState.signalName, ARRAYSIZE(windowState.signalName),
+ "fxr::CreateVRWindow::%X",
+ VRWindowManager::GetManager()->GetRandomUInt());
+
+ if (err > 0) {
+ HANDLE hEvent = ::CreateEventA(nullptr, // attributes
+ FALSE, // bManualReset
+ FALSE, // bInitialState
+ windowState.signalName);
+
+ if (hEvent != nullptr) {
+ // Create Shmem and push state
+ VRShmemInstance::GetInstance().CreateShMem(
+ true /*aCreateOnSharedMemory*/);
+ VRShmemInstance::GetInstance().PushWindowState(windowState);
+
+ // Start Firefox in another thread so that this thread can wait for the
+ // window state to be updated during Firefox startup
+ StartFirefoxParams fxParams = {0};
+ fxParams.firefoxFolder = firefoxFolderPath;
+ fxParams.firefoxProfileFolder = firefoxProfilePath;
+ DWORD dwTid = 0;
+ HANDLE hThreadFx = CreateThread(nullptr, 0, StartFirefoxThreadProc,
+ &fxParams, 0, &dwTid);
+ if (hThreadFx != nullptr) {
+ // Wait for Firefox to populate rest of window state
+ ::WaitForSingleObject(hEvent, INFINITE);
+
+ // Update local WindowState with data from Firefox
+ VRShmemInstance::GetInstance().PullWindowState(windowState);
+
+ (*hTex) = windowState.textureFx;
+ (*windowId) = VRWindowManager::GetManager()->SetHWND(
+ (HWND)windowState.hwndFx, fxParams.hProcessFx, hEvent);
+ (*width) = windowState.widthFx;
+ (*height) = windowState.heightFx;
+ } else {
+ // How do I failfast?
+ }
+ }
+ }
+}
+
+// Keep track of when WaitForVREvent is running to manage shutdown of
+// this vrhost. See CloseVRWindow for more details.
+volatile bool s_WaitingForVREvent = false;
+
+// Blocks until a new event is set on the shmem.
+// Note: this function can be called from any thread.
+void WaitForVREvent(uint32_t& nVRWindowID, uint32_t& eventType,
+ uint32_t& eventData1, uint32_t& eventData2) {
+ MOZ_ASSERT(!s_WaitingForVREvent);
+ s_WaitingForVREvent = true;
+
+ // Initialize all of the out params
+ nVRWindowID = 0;
+ eventType = 0;
+ eventData1 = 0;
+ eventData2 = 0;
+
+ if (VRShmemInstance::GetInstance().HasExternalShmem()) {
+ HANDLE evt = VRWindowManager::GetManager()->GetEvent();
+ const DWORD waitResult = ::WaitForSingleObject(evt, INFINITE);
+ if (waitResult != WAIT_OBJECT_0) {
+ MOZ_ASSERT(false && "Error WaitForVREvent().\n");
+ return;
+ }
+ mozilla::gfx::VRWindowState windowState = {0};
+ VRShmemInstance::GetInstance().PullWindowState(windowState);
+
+ nVRWindowID =
+ VRWindowManager::GetManager()->GetId((HWND)windowState.hwndFx);
+ if (nVRWindowID != 0) {
+ eventType = (uint32_t)windowState.eventType;
+ mozilla::gfx::VRFxEventType fxEvent =
+ mozilla::gfx::VRFxEventType(eventType);
+
+ switch (fxEvent) {
+ case mozilla::gfx::VRFxEventType::IME:
+ eventData1 = (uint32_t)windowState.eventState;
+ break;
+ case mozilla::gfx::VRFxEventType::FULLSCREEN:
+ eventData1 = (uint32_t)windowState.eventState;
+ break;
+ case mozilla::gfx::VRFxEventType::SHUTDOWN:
+ VRShmemInstance::GetInstance().CloseShMem();
+ break;
+ default:
+ MOZ_ASSERT(false && "Undefined VR Fx event.");
+ break;
+ }
+ }
+ }
+ s_WaitingForVREvent = false;
+}
+
+// Sends a message to the VR window to close.
+void CloseVRWindow(uint32_t nVRWindowID, bool waitForTerminate) {
+ HWND hwnd = VRWindowManager::GetManager()->GetHWND(nVRWindowID);
+ if (hwnd != nullptr) {
+ ::SendMessage(hwnd, WM_CLOSE, 0, 0);
+
+ if (waitForTerminate) {
+ // Wait for Firefox main process to exit
+ ::WaitForSingleObject(VRWindowManager::GetManager()->GetProc(nVRWindowID),
+ INFINITE);
+ }
+ }
+
+ // If a thread is currently blocked on WaitForVREvent, then defer closing the
+ // shmem to that thread by sending an async event.
+ // If WaitForVREvent is not running, it is safe to close the shmem now.
+ // Subsequent calls to WaitForVREvent will return early because it does not
+ // have an external shmem.
+ if (s_WaitingForVREvent) {
+ VRShmemInstance::GetInstance().SendShutdowmState(nVRWindowID);
+ } else {
+ VRShmemInstance::GetInstance().CloseShMem();
+ }
+}
+
+// Forwards Win32 UI window messages to the Firefox widget/window associated
+// with nVRWindowID. Note that not all message type is supported (only those
+// allowed in the switch block below).
+void SendUIMessageToVRWindow(uint32_t nVRWindowID, uint32_t msg,
+ uint64_t wparam, uint64_t lparam) {
+ HWND hwnd = VRWindowManager::GetManager()->GetHWND(nVRWindowID);
+ if (hwnd != nullptr) {
+ switch (msg) {
+ case WM_MOUSEWHEEL:
+ // For MOUSEWHEEL, the coordinates are supposed to be at Screen origin
+ // rather than window client origin.
+ // Make the conversion to screen coordinates before posting the message
+ // to the Fx window.
+ POINT pt;
+ POINTSTOPOINT(pt, MAKEPOINTS(lparam));
+ if (!::ClientToScreen(hwnd, &pt)) {
+ break;
+ }
+ // otherwise, fallthrough
+ lparam = POINTTOPOINTS(pt);
+ case WM_MOUSEMOVE:
+ case WM_LBUTTONDOWN:
+ case WM_LBUTTONUP:
+ case WM_CHAR:
+ case WM_KEYDOWN:
+ case WM_KEYUP:
+ ::PostMessage(hwnd, msg, wparam, lparam);
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+void SendVRTelemetry(uint32_t nVRWindowID, uint32_t telemetryId,
+ uint32_t value) {
+ HWND hwnd = VRWindowManager::GetManager()->GetHWND(nVRWindowID);
+ if (hwnd == nullptr) {
+ return;
+ }
+ VRTelemetryManager::GetManager()->SendTelemetry(telemetryId, value);
+} \ No newline at end of file
diff --git a/gfx/vr/vrhost/vrhostex.h b/gfx/vr/vrhost/vrhostex.h
new file mode 100644
index 0000000000..70f4f98fba
--- /dev/null
+++ b/gfx/vr/vrhost/vrhostex.h
@@ -0,0 +1,33 @@
+/* -*- 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/. */
+
+// vrhostex.h
+// This file declares the functions and their typedefs for functions exported
+// by vrhost.dll
+
+#pragma once
+#include <stdint.h>
+
+// void SampleExport();
+typedef void (*PFN_SAMPLE)();
+
+typedef void (*PFN_CREATEVRWINDOW)(char* firefoxFolderPath,
+ char* firefoxProfilePath,
+ uint32_t dxgiAdapterID, uint32_t widthHost,
+ uint32_t heightHost, uint32_t* windowId,
+ void** hTex, uint32_t* width,
+ uint32_t* height);
+
+typedef void (*PFN_CLOSEVRWINDOW)(uint32_t nVRWindowID, bool waitForTerminate);
+
+typedef void (*PFN_SENDUIMSG)(uint32_t nVRWindowID, uint32_t msg,
+ uint64_t wparam, uint64_t lparam);
+
+typedef void (*PFN_WAITFORVREVENT)(uint32_t& nVRWindowID, uint32_t& eventType,
+ uint32_t& eventData1, uint32_t& eventData2);
+
+typedef void (*PFN_SENDVRTELEMETRY)(uint32_t nVRWindowID, uint32_t telemetryId,
+ uint32_t value);
diff --git a/gfx/vr/vrhost/vrhostnightly.def b/gfx/vr/vrhost/vrhostnightly.def
new file mode 100644
index 0000000000..0a849e37ce
--- /dev/null
+++ b/gfx/vr/vrhost/vrhostnightly.def
@@ -0,0 +1,23 @@
+;+# 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/.
+
+LIBRARY vrhost.dll
+
+;+= Note: this file defines exports that are only available in Nightly builds
+;+= so that test exports are not exposed in Beta and Release builds.
+;+= Please ensure that the Public Export APIs in the first section below are
+;+= also declared in vrhost.def.
+EXPORTS
+;+= Public Export APIs for vrhost
+ CreateVRWindow PRIVATE
+ CloseVRWindow PRIVATE
+ SendUIMessageToVRWindow PRIVATE
+ WaitForVREvent PRIVATE
+ SendVRTelemetry PRIVATE
+
+;+= Exports only available in Nightlies for testing
+ SampleExport PRIVATE
+ TestTheManager PRIVATE
+ TestTheService PRIVATE
+ TestCreateVRWindow PRIVATE \ No newline at end of file
diff --git a/gfx/vr/vrhost/vrhosttest.cpp b/gfx/vr/vrhost/vrhosttest.cpp
new file mode 100644
index 0000000000..84c11c10d6
--- /dev/null
+++ b/gfx/vr/vrhost/vrhosttest.cpp
@@ -0,0 +1,370 @@
+/* -*- 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/. */
+
+// vrhosttest.cpp
+// Definition of testing and validation functions that are exported from this
+// dll
+
+#include "vrhostex.h"
+#include "VRShMem.h"
+
+#include <stdio.h>
+#include "windows.h"
+
+static const char s_pszSharedEvent[] = "vrhost_test_event_signal";
+static const DWORD s_dwWFSO_WAIT = 20000;
+
+void SampleExport() { printf("vrhost.cpp hello world\n"); }
+
+// For testing ShMem as Manager and Service:
+// The two processes should output the steps, synchronously, to validate
+// 2-way communication via VRShMem as follows
+// 01 mgr: create mgr
+// 02 mgr: wait for signal
+// 03 svc: create svc
+// 04 svc: send signal
+// 05 svc: wait for signal
+// 06 mgr: push browser
+// 07 mgr: send signal
+// 08 mgr: wait for signal
+// 09 svc: pull browser
+// 10 svc: verify data
+// 11 svc: push system
+// 12 svc: send signal
+// 13 svc: wait for signal
+// 14 mgr: pull system
+// 15 mgr: verify data
+// 16 mgr: push window
+// 17 mgr: send signal
+// 18 mgr: wait for signal
+// 19 svc: pull window
+// 20 svc: verify data
+// 21 svc: push window
+// 22 svc: send signal
+// 23 mgr: pull window
+// 24 mgr: verify data
+// 25 return
+// These tests can be run with two instances of vrtesthost.exe, one first
+// running with -testmgr and the second running with -testsvc.
+// TODO: Bug 1563235 - Convert vrtesthost.exe tests into unit tests
+
+// For testing VRShMem as the Manager (i.e., the one who creates the
+// shmem). The sequence of how it tests with the service is listed above.
+void TestTheManager() {
+ printf("TestTheManager Start\n");
+
+ MOZ_ASSERT(GetLastError() == 0,
+ "TestTheManager should start with no OS errors");
+ HANDLE hEvent = ::CreateEventA(nullptr, // lpEventAttributes
+ FALSE, // bManualReset
+ FALSE, // bInitialState
+ s_pszSharedEvent // lpName
+ );
+
+ printf("\n01 mgr: create mgr\n");
+ mozilla::gfx::VRShMem shmem(nullptr, true /*aRequiresMutex*/);
+ shmem.CreateShMem(true /*aCreateOnSharedMemory*/);
+
+ printf("02 mgr: wait for signal\n");
+ ::WaitForSingleObject(hEvent, s_dwWFSO_WAIT);
+
+ // Set some state to verify on the other side
+ mozilla::gfx::VRBrowserState browserState = {0};
+ browserState.presentationActive = true;
+ browserState.layerState[0].type =
+ mozilla::gfx::VRLayerType::LayerType_2D_Content;
+ browserState.hapticState[0].controllerIndex = 987;
+
+ printf("06 mgr: push browser\n");
+ shmem.PushBrowserState(browserState, true);
+
+ printf("07 mgr: send signal\n");
+ ::SetEvent(hEvent);
+
+ printf("08 mgr: wait for signal\n");
+ ::WaitForSingleObject(hEvent, s_dwWFSO_WAIT);
+
+ printf("14 mgr: pull system\n");
+ mozilla::gfx::VRSystemState state;
+ shmem.PullSystemState(state.displayState, state.sensorState,
+ state.controllerState, state.enumerationCompleted,
+ nullptr);
+
+ printf(
+ "15 mgr: verify data\n"
+ "\tstate.enumerationCompleted = %d\n"
+ "\tstate.displayState.displayName = \"%s\"\n"
+ "\tstate.controllerState[1].hand = %hhu\n"
+ "\tstate.sensorState.inputFrameID = %llu\n",
+ state.enumerationCompleted, state.displayState.displayName,
+ state.controllerState[1].hand, state.sensorState.inputFrameID);
+
+ // Test the WindowState functions as the host
+ mozilla::gfx::VRWindowState windowState = {0};
+ strcpy(windowState.signalName, "randomsignalstring");
+ windowState.dxgiAdapterHost = 99;
+ windowState.heightHost = 42;
+ windowState.widthHost = 24;
+
+ printf("16 mgr: push window\n");
+ shmem.PushWindowState(windowState);
+
+ printf("17 mgr: send signal\n");
+ ::SetEvent(hEvent);
+
+ printf("18 mgr: wait for signal\n");
+ ::WaitForSingleObject(hEvent, s_dwWFSO_WAIT);
+
+ printf("23 mgr: pull window\n");
+ shmem.PullWindowState(windowState);
+
+ printf(
+ "24 svc: verify data\n"
+ "\tstate.hwndFx = 0x%llX\n"
+ "\tstate.heightFx = %d\n"
+ "\tstate.widthFx = %d\n"
+ "\tstate.textureHandle = %p\n",
+ windowState.hwndFx, windowState.heightFx, windowState.widthFx,
+ windowState.textureFx);
+
+ shmem.CloseShMem();
+
+ printf("TestTheManager complete");
+ fflush(nullptr);
+}
+
+// For testing VRShMem as the Service (i.e., the one who consumes the
+// shmem). The sequence of how it tests with the service is listed above.
+void TestTheService() {
+ printf("TestTheService Start\n");
+
+ MOZ_ASSERT(GetLastError() == 0,
+ "TestTheService should start with no OS errors");
+ // Handle created by TestTheManager above.
+ HANDLE hEvent = ::OpenEventA(EVENT_ALL_ACCESS, // dwDesiredAccess
+ FALSE, // bInheritHandle
+ s_pszSharedEvent // lpName
+ );
+
+ printf("\n03 svc: create svc\n");
+ mozilla::gfx::VRShMem shmem(nullptr, true /*aRequiresMutex*/);
+ shmem.JoinShMem();
+
+ printf("04 svc: send signal\n");
+ ::SetEvent(hEvent);
+
+ printf("05 svc: wait for signal\n");
+ ::WaitForSingleObject(hEvent, s_dwWFSO_WAIT);
+
+ printf("09 svc: pull browser\n");
+ mozilla::gfx::VRBrowserState state;
+ shmem.PullBrowserState(state);
+
+ printf(
+ "10 svc: verify data\n"
+ "\tstate.presentationActive = %d\n"
+ "\tstate.layerState[0].type = %hu\n"
+ "\tstate.hapticState[0].controllerIndex = %d\n",
+ state.presentationActive, state.layerState[0].type,
+ state.hapticState[0].controllerIndex);
+
+ // Set some state to verify on the other side
+ mozilla::gfx::VRSystemState systemState;
+ systemState.enumerationCompleted = true;
+ strncpy(systemState.displayState.displayName, "test from vrservice shmem",
+ mozilla::gfx::kVRDisplayNameMaxLen);
+ systemState.controllerState[1].hand = mozilla::gfx::ControllerHand::Left;
+ systemState.sensorState.inputFrameID = 1234567;
+
+ printf("11 svc: push system\n");
+ shmem.PushSystemState(systemState);
+
+ printf("12 svc: send signal\n");
+ ::SetEvent(hEvent);
+
+ printf("13 svc: wait for signal\n");
+ ::WaitForSingleObject(hEvent, s_dwWFSO_WAIT);
+
+ // Test the WindowState functions as Firefox
+ printf("19 svc: pull window\n");
+ mozilla::gfx::VRWindowState windowState;
+ shmem.PullWindowState(windowState);
+
+ printf(
+ "20 svc: verify data\n"
+ "\tstate.signalName = \"%s\"\n"
+ "\tstate.dxgiAdapterHost = %d\n"
+ "\tstate.heightHost = %d\n"
+ "\tstate.widthHost = %d\n",
+ windowState.signalName, windowState.dxgiAdapterHost,
+ windowState.heightHost, windowState.widthHost);
+
+ windowState.hwndFx = 0x1234;
+ windowState.heightFx = 1234;
+ windowState.widthFx = 4321;
+ windowState.textureFx = (HANDLE)0x77777;
+
+ printf("21 svc: push window\n");
+ shmem.PushWindowState(windowState);
+
+ printf("22 svc: send signal\n");
+ ::SetEvent(hEvent);
+
+ shmem.LeaveShMem();
+
+ printf("TestTheService complete");
+ fflush(nullptr);
+}
+
+DWORD TestWaitForVREventThreadProc(_In_ LPVOID lpParameter) {
+ // WaitForVREvent
+ printf("\nStarting TestWaitForVREventThreadProc\n");
+
+ PFN_WAITFORVREVENT fnWaitForVRMsg = (PFN_WAITFORVREVENT)lpParameter;
+
+ uint32_t nVRWindowID = 0;
+ uint32_t eventType = 0;
+ uint32_t eventData1 = 0;
+ uint32_t eventData2 = 0;
+
+ while (eventType != 2) { // FxEvent_SHUTDOWN
+ fnWaitForVRMsg(nVRWindowID, eventType, eventData1, eventData2);
+ printf(
+ "\nWaitForVRMessage:\n\tvrWindowID: %d\n\teventType: %d\n\teventData1: "
+ "%d\n\teventData2: %d\n",
+ nVRWindowID, eventType, eventData1, eventData2);
+ }
+
+ printf("\nReturning from TestWaitForVREventThreadProc\n");
+
+ return 0;
+}
+
+// This function tests the export CreateVRWindow by outputting the return values
+// from the call to the console, as well as testing CloseVRWindow after the data
+// is retrieved.
+void TestCreateVRWindow() {
+ printf("TestCreateVRWindow Start\n");
+
+ // Cache function calls to test real-world export and usage
+ HMODULE hVRHost = ::GetModuleHandleA("vrhost.dll");
+ PFN_CREATEVRWINDOW fnCreate =
+ (PFN_CREATEVRWINDOW)::GetProcAddress(hVRHost, "CreateVRWindow");
+ PFN_CLOSEVRWINDOW fnClose =
+ (PFN_CLOSEVRWINDOW)::GetProcAddress(hVRHost, "CloseVRWindow");
+ PFN_SENDUIMSG fnSendMsg =
+ (PFN_SENDUIMSG)::GetProcAddress(hVRHost, "SendUIMessageToVRWindow");
+ PFN_WAITFORVREVENT fnWaitForVRMsg =
+ (PFN_WAITFORVREVENT)::GetProcAddress(hVRHost, "WaitForVREvent");
+
+ // Create the VR Window and store data from creation
+ char currentDir[MAX_PATH] = {0};
+ char currentDirProfile[MAX_PATH] = {0};
+ DWORD currentDirLength =
+ ::GetCurrentDirectoryA(ARRAYSIZE(currentDir), currentDir);
+ currentDir[currentDirLength] = '\\';
+
+ int err = sprintf_s(currentDirProfile, ARRAYSIZE(currentDirProfile),
+ "%svrhosttest-profile", currentDir);
+ if (err > 0) {
+ printf("Starting Firefox from %s\n", currentDir);
+
+ UINT windowId;
+ HANDLE hTex;
+ UINT width;
+ UINT height;
+ fnCreate(currentDir, currentDirProfile, 0, 100, 200, &windowId, &hTex,
+ &width, &height);
+
+ // Now that the Fx window is created, start a new thread to wait for VR
+ // events to be sent from it
+ DWORD dwTid;
+ HANDLE hThreadWait = CreateThread(nullptr, 0, TestWaitForVREventThreadProc,
+ (void*)fnWaitForVRMsg, 0, &dwTid);
+
+ // Wait for Fx to finish launch
+#ifdef DEBUG
+ ::Sleep(5000);
+#else
+ ::Sleep(2000);
+#endif
+
+ printf(
+ "1. Simulating a click on the Home button, which should look "
+ "pressed\n");
+ POINT pt;
+ pt.x = 180;
+ pt.y = 700;
+ fnSendMsg(windowId, WM_LBUTTONDOWN, 0, POINTTOPOINTS(pt));
+ ::Sleep(3000);
+ fnSendMsg(windowId, WM_LBUTTONUP, 0, POINTTOPOINTS(pt));
+
+ printf(
+ "2. Simulating hovering across the URL bar, which should turn "
+ "blue\n");
+ pt.x = 600;
+ for (int i = 0; i < 100; ++i) {
+ pt.x++;
+ fnSendMsg(windowId, WM_MOUSEMOVE, 0, POINTTOPOINTS(pt));
+ ::Sleep(5);
+ }
+
+ printf(
+ "3. Simulating clicking inside the URL bar, which should "
+ "highlight the text\n");
+ pt.x = 700;
+ pt.y = 700;
+ fnSendMsg(windowId, WM_LBUTTONDOWN, 0, POINTTOPOINTS(pt));
+ fnSendMsg(windowId, WM_LBUTTONUP, 0, POINTTOPOINTS(pt));
+
+ ::Sleep(3000);
+
+ printf("4. Type some UTF16 characters in the URL bar\n");
+ fnSendMsg(windowId, WM_CHAR, 0x4E64, 0);
+ fnSendMsg(windowId, WM_CHAR, 0x312D, 0);
+ fnSendMsg(windowId, WM_CHAR, 0x0BB9, 0);
+ fnSendMsg(windowId, WM_CHAR, 0x2745, 0);
+
+ printf(
+ "5. Simulating clicking outside the URL bar, which should "
+ "send a keyboard blur event\n");
+ pt.x = 20;
+ pt.y = 20;
+ fnSendMsg(windowId, WM_LBUTTONDOWN, 0, POINTTOPOINTS(pt));
+ fnSendMsg(windowId, WM_LBUTTONUP, 0, POINTTOPOINTS(pt));
+
+ ::Sleep(1000);
+
+ printf("6. Simulating scrolling the web content down and then up\n");
+
+ pt.x = 100;
+ pt.y = 100;
+ for (int i = -20; i < 10; ++i) {
+ fnSendMsg(windowId, WM_MOUSEWHEEL,
+ MAKELONG(0, (i < 0) ? -WHEEL_DELTA : WHEEL_DELTA),
+ POINTTOPOINTS(pt));
+ ::Sleep(100);
+ }
+
+ ::Sleep(5000);
+
+ // Close the Firefox VR Window
+ fnClose(windowId, true);
+
+ // Wait for the VR event thread to return
+ ::WaitForSingleObject(hThreadWait, INFINITE);
+
+ // Print output from CreateVRWindow
+ printf(
+ "\n\nTestCreateVRWindow End:\n"
+ "\twindowId = 0x%X\n"
+ "\thTex = 0x%p\n"
+ "\twidth = %d\n"
+ "\theight = %d\n",
+ windowId, hTex, width, height);
+ printf("\n***Note: profile folder created at %s***\n", currentDirProfile);
+ }
+}