1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include <fstream>
8 #include "mozilla/JSONWriter.h"
9 #include "mozilla/ClearOnShutdown.h"
10 #include "nsString.h"
11 
12 #include "OpenVRSession.h"
13 #include "mozilla/StaticPrefs_dom.h"
14 
15 #if defined(XP_WIN)
16 #  include <d3d11.h>
17 #  include "mozilla/gfx/DeviceManagerDx.h"
18 #elif defined(XP_MACOSX)
19 #  include "mozilla/gfx/MacIOSurface.h"
20 #endif
21 
22 #if !defined(XP_WIN)
23 #  include <sys/stat.h>  // for umask()
24 #endif
25 
26 #include "mozilla/dom/GamepadEventTypes.h"
27 #include "mozilla/dom/GamepadBinding.h"
28 #include "binding/OpenVRCosmosBinding.h"
29 #include "binding/OpenVRKnucklesBinding.h"
30 #include "binding/OpenVRViveBinding.h"
31 #include "OpenVRCosmosMapper.h"
32 #include "OpenVRDefaultMapper.h"
33 #include "OpenVRKnucklesMapper.h"
34 #include "OpenVRViveMapper.h"
35 #if defined(XP_WIN)  // Windows Mixed Reality is only available in Windows.
36 #  include "OpenVRWMRMapper.h"
37 #  include "binding/OpenVRWMRBinding.h"
38 #endif
39 
40 #include "VRParent.h"
41 #include "VRProcessChild.h"
42 #include "VRThread.h"
43 
44 #if !defined(M_PI)
45 #  define M_PI 3.14159265358979323846264338327950288
46 #endif
47 
48 #define BTN_MASK_FROM_ID(_id) ::vr::ButtonMaskFromId(vr::EVRButtonId::_id)
49 
50 // Haptic feedback is updated every 5ms, as this is
51 // the minimum period between new haptic pulse requests.
52 // Effectively, this results in a pulse width modulation
53 // with an interval of 5ms.  Through experimentation, the
54 // maximum duty cycle was found to be about 3.9ms
55 const uint32_t kVRHapticUpdateInterval = 5;
56 
57 using namespace mozilla::gfx;
58 
59 namespace mozilla::gfx {
60 
61 namespace {
62 
63 // This is for controller action file writer.
64 struct StringWriteFunc : public JSONWriteFunc {
65   nsACString& mBuffer;  // This struct must not outlive this buffer
66 
StringWriteFuncmozilla::gfx::__anon40a541900111::StringWriteFunc67   explicit StringWriteFunc(nsACString& buffer) : mBuffer(buffer) {}
68 
Writemozilla::gfx::__anon40a541900111::StringWriteFunc69   void Write(const char* aStr) override { mBuffer.Append(aStr); }
Writemozilla::gfx::__anon40a541900111::StringWriteFunc70   void Write(const char* aStr, size_t aLen) override {
71     mBuffer.Append(aStr, aLen);
72   }
73 };
74 
75 class ControllerManifestFile {
76   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ControllerManifestFile)
77 
78  public:
CreateManifest()79   static already_AddRefed<ControllerManifestFile> CreateManifest() {
80     RefPtr<ControllerManifestFile> manifest = new ControllerManifestFile();
81     return manifest.forget();
82   }
83 
IsExisting()84   bool IsExisting() {
85     if (mFileName.IsEmpty() ||
86         !std::ifstream(mFileName.BeginReading()).good()) {
87       return false;
88     }
89     return true;
90   }
91 
SetFileName(const char * aName)92   void SetFileName(const char* aName) { mFileName = aName; }
93 
GetFileName() const94   const char* GetFileName() const { return mFileName.BeginReading(); }
95 
96  private:
97   ControllerManifestFile() = default;
98 
~ControllerManifestFile()99   ~ControllerManifestFile() {
100     if (!mFileName.IsEmpty() && remove(mFileName.BeginReading()) != 0) {
101       MOZ_ASSERT(false, "Delete controller manifest file failed.");
102     }
103     mFileName = "";
104   }
105 
106   nsCString mFileName;
107 };
108 
109 // We wanna keep these temporary files existing
110 // until Firefox is closed instead of following OpenVRSession's lifetime.
111 StaticRefPtr<ControllerManifestFile> sCosmosBindingFile;
112 StaticRefPtr<ControllerManifestFile> sKnucklesBindingFile;
113 StaticRefPtr<ControllerManifestFile> sViveBindingFile;
114 #if defined(XP_WIN)
115 StaticRefPtr<ControllerManifestFile> sWMRBindingFile;
116 #endif
117 StaticRefPtr<ControllerManifestFile> sControllerActionFile;
118 
GetControllerHandFromControllerRole(OpenVRHand aRole)119 dom::GamepadHand GetControllerHandFromControllerRole(OpenVRHand aRole) {
120   dom::GamepadHand hand;
121   switch (aRole) {
122     case OpenVRHand::None:
123       hand = dom::GamepadHand::_empty;
124       break;
125     case OpenVRHand::Left:
126       hand = dom::GamepadHand::Left;
127       break;
128     case OpenVRHand::Right:
129       hand = dom::GamepadHand::Right;
130       break;
131     default:
132       hand = dom::GamepadHand::_empty;
133       MOZ_ASSERT(false);
134       break;
135   }
136 
137   return hand;
138 }
139 
FileIsExisting(const nsCString & aPath)140 bool FileIsExisting(const nsCString& aPath) {
141   if (aPath.IsEmpty() || !std::ifstream(aPath.BeginReading()).good()) {
142     return false;
143   }
144   return true;
145 }
146 
147 };  // anonymous namespace
148 
149 #if defined(XP_WIN)
GenerateTempFileName(nsCString & aPath)150 bool GenerateTempFileName(nsCString& aPath) {
151   TCHAR tempPathBuffer[MAX_PATH];
152   TCHAR tempFileName[MAX_PATH];
153 
154   // Gets the temp path env string (no guarantee it's a valid path).
155   DWORD dwRetVal = GetTempPath(MAX_PATH, tempPathBuffer);
156   if (dwRetVal > MAX_PATH || (dwRetVal == 0)) {
157     NS_WARNING("OpenVR - Creating temp path failed.");
158     return false;
159   }
160 
161   // Generates a temporary file name.
162   UINT uRetVal = GetTempFileName(tempPathBuffer,  // directory for tmp files
163                                  TEXT("mozvr"),   // temp file name prefix
164                                  0,               // create unique name
165                                  tempFileName);   // buffer for name
166   if (uRetVal == 0) {
167     NS_WARNING("OpenVR - Creating temp file failed.");
168     return false;
169   }
170 
171   aPath.Assign(NS_ConvertUTF16toUTF8(tempFileName));
172   return true;
173 }
174 #else
GenerateTempFileName(nsCString & aPath)175 bool GenerateTempFileName(nsCString& aPath) {
176   const char tmp[] = "/tmp/mozvrXXXXXX";
177   char fileName[PATH_MAX];
178 
179   strcpy(fileName, tmp);
180   const mode_t prevMask = umask(S_IXUSR | S_IRWXO | S_IRWXG);
181   const int fd = mkstemp(fileName);
182   umask(prevMask);
183   if (fd == -1) {
184     NS_WARNING(nsPrintfCString("OpenVR - Creating temp file failed: %s",
185                                strerror(errno))
186                    .get());
187     return false;
188   }
189   close(fd);
190 
191   aPath.Assign(fileName);
192   return true;
193 }
194 #endif  // defined(XP_WIN)
195 
OpenVRSession()196 OpenVRSession::OpenVRSession()
197     : VRSession(),
198       mVRSystem(nullptr),
199       mVRChaperone(nullptr),
200       mVRCompositor(nullptr),
201       mHapticPulseRemaining{},
202       mHapticPulseIntensity{},
203       mIsWindowsMR(false),
204       mControllerHapticStateMutex(
205           "OpenVRSession::mControllerHapticStateMutex") {
206   std::fill_n(mControllerDeviceIndex, kVRControllerMaxCount, OpenVRHand::None);
207 }
208 
~OpenVRSession()209 OpenVRSession::~OpenVRSession() {
210   mActionsetFirefox = ::vr::k_ulInvalidActionSetHandle;
211   Shutdown();
212 }
213 
Initialize(mozilla::gfx::VRSystemState & aSystemState,bool aDetectRuntimesOnly)214 bool OpenVRSession::Initialize(mozilla::gfx::VRSystemState& aSystemState,
215                                bool aDetectRuntimesOnly) {
216   if (!StaticPrefs::dom_vr_enabled() ||
217       !StaticPrefs::dom_vr_openvr_enabled_AtStartup()) {
218     return false;
219   }
220   if (mVRSystem != nullptr) {
221     // Already initialized
222     return true;
223   }
224   if (!::vr::VR_IsRuntimeInstalled()) {
225     return false;
226   }
227   if (aDetectRuntimesOnly) {
228     aSystemState.displayState.capabilityFlags |=
229         VRDisplayCapabilityFlags::Cap_ImmersiveVR;
230     return false;
231   }
232   if (!::vr::VR_IsHmdPresent()) {
233     return false;
234   }
235 
236   ::vr::HmdError err;
237 
238   ::vr::VR_Init(&err, ::vr::EVRApplicationType::VRApplication_Scene);
239   if (err) {
240     return false;
241   }
242 
243   mVRSystem = (::vr::IVRSystem*)::vr::VR_GetGenericInterface(
244       ::vr::IVRSystem_Version, &err);
245   if (err || !mVRSystem) {
246     Shutdown();
247     return false;
248   }
249   mVRChaperone = (::vr::IVRChaperone*)::vr::VR_GetGenericInterface(
250       ::vr::IVRChaperone_Version, &err);
251   if (err || !mVRChaperone) {
252     Shutdown();
253     return false;
254   }
255   mVRCompositor = (::vr::IVRCompositor*)::vr::VR_GetGenericInterface(
256       ::vr::IVRCompositor_Version, &err);
257   if (err || !mVRCompositor) {
258     Shutdown();
259     return false;
260   }
261 
262 #if defined(XP_WIN)
263   if (!CreateD3DObjects()) {
264     Shutdown();
265     return false;
266   }
267 
268 #endif
269 
270   // Configure coordinate system
271   mVRCompositor->SetTrackingSpace(::vr::TrackingUniverseSeated);
272 
273   if (!InitState(aSystemState)) {
274     Shutdown();
275     return false;
276   }
277   if (!SetupContollerActions()) {
278     return false;
279   }
280 
281   // Succeeded
282   return true;
283 }
284 
285 // "actions": [] Action paths must take the form: "/actions/<action
286 // set>/in|out/<action>"
287 #define CreateControllerAction(hand, name, type) \
288   ControllerAction("/actions/firefox/in/" #hand "Hand_" #name, #type)
289 #define CreateControllerOutAction(hand, name, type) \
290   ControllerAction("/actions/firefox/out/" #hand "Hand_" #name, #type)
291 
SetupContollerActions()292 bool OpenVRSession::SetupContollerActions() {
293   if (!vr::VRInput()) {
294     NS_WARNING("OpenVR - vr::VRInput() is null.");
295     return false;
296   }
297 
298   // Check if this device binding file has been created.
299   // If it didn't exist yet, create a new temp file.
300   nsCString controllerAction;
301   nsCString viveManifest;
302   nsCString WMRManifest;
303   nsCString knucklesManifest;
304   nsCString cosmosManifest;
305 
306   // Getting / Generating manifest file paths.
307   if (StaticPrefs::dom_vr_process_enabled_AtStartup()) {
308     VRParent* vrParent = VRProcessChild::GetVRParent();
309     nsCString output;
310 
311     if (vrParent->GetOpenVRControllerActionPath(&output)) {
312       controllerAction = output;
313     }
314 
315     if (vrParent->GetOpenVRControllerManifestPath(VRControllerType::HTCVive,
316                                                   &output)) {
317       viveManifest = output;
318     }
319     if (!viveManifest.Length() || !FileIsExisting(viveManifest)) {
320       if (!GenerateTempFileName(viveManifest)) {
321         return false;
322       }
323       OpenVRViveBinding viveBinding;
324       std::ofstream viveBindingFile(viveManifest.BeginReading());
325       if (viveBindingFile.is_open()) {
326         viveBindingFile << viveBinding.binding;
327         viveBindingFile.close();
328       }
329     }
330 
331 #if defined(XP_WIN)
332     if (vrParent->GetOpenVRControllerManifestPath(VRControllerType::MSMR,
333                                                   &output)) {
334       WMRManifest = output;
335     }
336     if (!WMRManifest.Length() || !FileIsExisting(WMRManifest)) {
337       if (!GenerateTempFileName(WMRManifest)) {
338         return false;
339       }
340       OpenVRWMRBinding WMRBinding;
341       std::ofstream WMRBindingFile(WMRManifest.BeginReading());
342       if (WMRBindingFile.is_open()) {
343         WMRBindingFile << WMRBinding.binding;
344         WMRBindingFile.close();
345       }
346     }
347 #endif
348     if (vrParent->GetOpenVRControllerManifestPath(VRControllerType::ValveIndex,
349                                                   &output)) {
350       knucklesManifest = output;
351     }
352     if (!knucklesManifest.Length() || !FileIsExisting(knucklesManifest)) {
353       if (!GenerateTempFileName(knucklesManifest)) {
354         return false;
355       }
356       OpenVRKnucklesBinding knucklesBinding;
357       std::ofstream knucklesBindingFile(knucklesManifest.BeginReading());
358       if (knucklesBindingFile.is_open()) {
359         knucklesBindingFile << knucklesBinding.binding;
360         knucklesBindingFile.close();
361       }
362     }
363     if (vrParent->GetOpenVRControllerManifestPath(
364             VRControllerType::HTCViveCosmos, &output)) {
365       cosmosManifest = output;
366     }
367     if (!cosmosManifest.Length() || !FileIsExisting(cosmosManifest)) {
368       if (!GenerateTempFileName(cosmosManifest)) {
369         return false;
370       }
371       OpenVRCosmosBinding cosmosBinding;
372       std::ofstream cosmosBindingFile(cosmosManifest.BeginReading());
373       if (cosmosBindingFile.is_open()) {
374         cosmosBindingFile << cosmosBinding.binding;
375         cosmosBindingFile.close();
376       }
377     }
378   } else {
379     // Without using VR process
380     if (!sControllerActionFile) {
381       sControllerActionFile = ControllerManifestFile::CreateManifest();
382       NS_DispatchToMainThread(NS_NewRunnableFunction(
383           "ClearOnShutdown ControllerManifestFile",
384           []() { ClearOnShutdown(&sControllerActionFile); }));
385     }
386     controllerAction = sControllerActionFile->GetFileName();
387 
388     if (!sViveBindingFile) {
389       sViveBindingFile = ControllerManifestFile::CreateManifest();
390       NS_DispatchToMainThread(
391           NS_NewRunnableFunction("ClearOnShutdown ControllerManifestFile",
392                                  []() { ClearOnShutdown(&sViveBindingFile); }));
393     }
394     if (!sViveBindingFile->IsExisting()) {
395       nsCString viveBindingPath;
396       if (!GenerateTempFileName(viveBindingPath)) {
397         return false;
398       }
399       sViveBindingFile->SetFileName(viveBindingPath.BeginReading());
400       OpenVRViveBinding viveBinding;
401       std::ofstream viveBindingFile(sViveBindingFile->GetFileName());
402       if (viveBindingFile.is_open()) {
403         viveBindingFile << viveBinding.binding;
404         viveBindingFile.close();
405       }
406     }
407     viveManifest = sViveBindingFile->GetFileName();
408 
409     if (!sKnucklesBindingFile) {
410       sKnucklesBindingFile = ControllerManifestFile::CreateManifest();
411       NS_DispatchToMainThread(NS_NewRunnableFunction(
412           "ClearOnShutdown ControllerManifestFile",
413           []() { ClearOnShutdown(&sKnucklesBindingFile); }));
414     }
415     if (!sKnucklesBindingFile->IsExisting()) {
416       nsCString knucklesBindingPath;
417       if (!GenerateTempFileName(knucklesBindingPath)) {
418         return false;
419       }
420       sKnucklesBindingFile->SetFileName(knucklesBindingPath.BeginReading());
421       OpenVRKnucklesBinding knucklesBinding;
422       std::ofstream knucklesBindingFile(sKnucklesBindingFile->GetFileName());
423       if (knucklesBindingFile.is_open()) {
424         knucklesBindingFile << knucklesBinding.binding;
425         knucklesBindingFile.close();
426       }
427     }
428     knucklesManifest = sKnucklesBindingFile->GetFileName();
429 
430     if (!sCosmosBindingFile) {
431       sCosmosBindingFile = ControllerManifestFile::CreateManifest();
432       NS_DispatchToMainThread(NS_NewRunnableFunction(
433           "ClearOnShutdown ControllerManifestFile",
434           []() { ClearOnShutdown(&sCosmosBindingFile); }));
435     }
436     if (!sCosmosBindingFile->IsExisting()) {
437       nsCString cosmosBindingPath;
438       if (!GenerateTempFileName(cosmosBindingPath)) {
439         return false;
440       }
441       sCosmosBindingFile->SetFileName(cosmosBindingPath.BeginReading());
442       OpenVRCosmosBinding cosmosBinding;
443       std::ofstream cosmosBindingFile(sCosmosBindingFile->GetFileName());
444       if (cosmosBindingFile.is_open()) {
445         cosmosBindingFile << cosmosBinding.binding;
446         cosmosBindingFile.close();
447       }
448     }
449     cosmosManifest = sCosmosBindingFile->GetFileName();
450 #if defined(XP_WIN)
451     if (!sWMRBindingFile) {
452       sWMRBindingFile = ControllerManifestFile::CreateManifest();
453       NS_DispatchToMainThread(
454           NS_NewRunnableFunction("ClearOnShutdown ControllerManifestFile",
455                                  []() { ClearOnShutdown(&sWMRBindingFile); }));
456     }
457     if (!sWMRBindingFile->IsExisting()) {
458       nsCString WMRBindingPath;
459       if (!GenerateTempFileName(WMRBindingPath)) {
460         return false;
461       }
462       sWMRBindingFile->SetFileName(WMRBindingPath.BeginReading());
463       OpenVRWMRBinding WMRBinding;
464       std::ofstream WMRBindingFile(sWMRBindingFile->GetFileName());
465       if (WMRBindingFile.is_open()) {
466         WMRBindingFile << WMRBinding.binding;
467         WMRBindingFile.close();
468       }
469     }
470     WMRManifest = sWMRBindingFile->GetFileName();
471 #endif
472   }
473   // End of Getting / Generating manifest file paths.
474 
475   // Setup controller actions.
476   ControllerInfo leftContollerInfo;
477   leftContollerInfo.mActionPose = CreateControllerAction(L, pose, pose);
478   leftContollerInfo.mActionTrackpad_Analog =
479       CreateControllerAction(L, trackpad_analog, vector2);
480   leftContollerInfo.mActionTrackpad_Pressed =
481       CreateControllerAction(L, trackpad_pressed, boolean);
482   leftContollerInfo.mActionTrackpad_Touched =
483       CreateControllerAction(L, trackpad_touched, boolean);
484   leftContollerInfo.mActionTrigger_Value =
485       CreateControllerAction(L, trigger_value, vector1);
486   leftContollerInfo.mActionGrip_Pressed =
487       CreateControllerAction(L, grip_pressed, boolean);
488   leftContollerInfo.mActionGrip_Touched =
489       CreateControllerAction(L, grip_touched, boolean);
490   leftContollerInfo.mActionMenu_Pressed =
491       CreateControllerAction(L, menu_pressed, boolean);
492   leftContollerInfo.mActionMenu_Touched =
493       CreateControllerAction(L, menu_touched, boolean);
494   leftContollerInfo.mActionSystem_Pressed =
495       CreateControllerAction(L, system_pressed, boolean);
496   leftContollerInfo.mActionSystem_Touched =
497       CreateControllerAction(L, system_touched, boolean);
498   leftContollerInfo.mActionA_Pressed =
499       CreateControllerAction(L, A_pressed, boolean);
500   leftContollerInfo.mActionA_Touched =
501       CreateControllerAction(L, A_touched, boolean);
502   leftContollerInfo.mActionB_Pressed =
503       CreateControllerAction(L, B_pressed, boolean);
504   leftContollerInfo.mActionB_Touched =
505       CreateControllerAction(L, B_touched, boolean);
506   leftContollerInfo.mActionThumbstick_Analog =
507       CreateControllerAction(L, thumbstick_analog, vector2);
508   leftContollerInfo.mActionThumbstick_Pressed =
509       CreateControllerAction(L, thumbstick_pressed, boolean);
510   leftContollerInfo.mActionThumbstick_Touched =
511       CreateControllerAction(L, thumbstick_touched, boolean);
512   leftContollerInfo.mActionFingerIndex_Value =
513       CreateControllerAction(L, finger_index_value, vector1);
514   leftContollerInfo.mActionFingerMiddle_Value =
515       CreateControllerAction(L, finger_middle_value, vector1);
516   leftContollerInfo.mActionFingerRing_Value =
517       CreateControllerAction(L, finger_ring_value, vector1);
518   leftContollerInfo.mActionFingerPinky_Value =
519       CreateControllerAction(L, finger_pinky_value, vector1);
520   leftContollerInfo.mActionBumper_Pressed =
521       CreateControllerAction(L, bumper_pressed, boolean);
522   leftContollerInfo.mActionHaptic =
523       CreateControllerOutAction(L, haptic, vibration);
524 
525   ControllerInfo rightContollerInfo;
526   rightContollerInfo.mActionPose = CreateControllerAction(R, pose, pose);
527   rightContollerInfo.mActionTrackpad_Analog =
528       CreateControllerAction(R, trackpad_analog, vector2);
529   rightContollerInfo.mActionTrackpad_Pressed =
530       CreateControllerAction(R, trackpad_pressed, boolean);
531   rightContollerInfo.mActionTrackpad_Touched =
532       CreateControllerAction(R, trackpad_touched, boolean);
533   rightContollerInfo.mActionTrigger_Value =
534       CreateControllerAction(R, trigger_value, vector1);
535   rightContollerInfo.mActionGrip_Pressed =
536       CreateControllerAction(R, grip_pressed, boolean);
537   rightContollerInfo.mActionGrip_Touched =
538       CreateControllerAction(R, grip_touched, boolean);
539   rightContollerInfo.mActionMenu_Pressed =
540       CreateControllerAction(R, menu_pressed, boolean);
541   rightContollerInfo.mActionMenu_Touched =
542       CreateControllerAction(R, menu_touched, boolean);
543   rightContollerInfo.mActionSystem_Pressed =
544       CreateControllerAction(R, system_pressed, boolean);
545   rightContollerInfo.mActionSystem_Touched =
546       CreateControllerAction(R, system_touched, boolean);
547   rightContollerInfo.mActionA_Pressed =
548       CreateControllerAction(R, A_pressed, boolean);
549   rightContollerInfo.mActionA_Touched =
550       CreateControllerAction(R, A_touched, boolean);
551   rightContollerInfo.mActionB_Pressed =
552       CreateControllerAction(R, B_pressed, boolean);
553   rightContollerInfo.mActionB_Touched =
554       CreateControllerAction(R, B_touched, boolean);
555   rightContollerInfo.mActionThumbstick_Analog =
556       CreateControllerAction(R, thumbstick_analog, vector2);
557   rightContollerInfo.mActionThumbstick_Pressed =
558       CreateControllerAction(R, thumbstick_pressed, boolean);
559   rightContollerInfo.mActionThumbstick_Touched =
560       CreateControllerAction(R, thumbstick_touched, boolean);
561   rightContollerInfo.mActionFingerIndex_Value =
562       CreateControllerAction(R, finger_index_value, vector1);
563   rightContollerInfo.mActionFingerMiddle_Value =
564       CreateControllerAction(R, finger_middle_value, vector1);
565   rightContollerInfo.mActionFingerRing_Value =
566       CreateControllerAction(R, finger_ring_value, vector1);
567   rightContollerInfo.mActionFingerPinky_Value =
568       CreateControllerAction(R, finger_pinky_value, vector1);
569   rightContollerInfo.mActionBumper_Pressed =
570       CreateControllerAction(R, bumper_pressed, boolean);
571   rightContollerInfo.mActionHaptic =
572       CreateControllerOutAction(R, haptic, vibration);
573 
574   mControllerHand[OpenVRHand::Left] = leftContollerInfo;
575   mControllerHand[OpenVRHand::Right] = rightContollerInfo;
576 
577   if (!controllerAction.Length() || !FileIsExisting(controllerAction)) {
578     if (!GenerateTempFileName(controllerAction)) {
579       return false;
580     }
581     nsCString actionData;
582     JSONWriter actionWriter(MakeUnique<StringWriteFunc>(actionData));
583     actionWriter.Start();
584 
585     actionWriter.StringProperty("version",
586                                 "0.1.0");  // TODO: adding a version check.
587     // "default_bindings": []
588     actionWriter.StartArrayProperty("default_bindings");
589 
590     auto SetupActionWriterByControllerType = [&](const char* aType,
591                                                  const nsCString& aManifest) {
592       actionWriter.StartObjectElement();
593       actionWriter.StringProperty("controller_type", aType);
594       actionWriter.StringProperty("binding_url", aManifest.BeginReading());
595       actionWriter.EndObject();
596     };
597     SetupActionWriterByControllerType("vive_controller", viveManifest);
598     SetupActionWriterByControllerType("knuckles", knucklesManifest);
599     SetupActionWriterByControllerType("vive_cosmos_controller", cosmosManifest);
600 #if defined(XP_WIN)
601     SetupActionWriterByControllerType("holographic_controller", WMRManifest);
602 #endif
603     actionWriter.EndArray();  // End "default_bindings": []
604 
605     actionWriter.StartArrayProperty("actions");
606 
607     for (auto& controller : mControllerHand) {
608       auto SetActionsToWriter = [&](const ControllerAction& aAction) {
609         actionWriter.StartObjectElement();
610         actionWriter.StringProperty("name", aAction.name.BeginReading());
611         actionWriter.StringProperty("type", aAction.type.BeginReading());
612         actionWriter.EndObject();
613       };
614 
615       SetActionsToWriter(controller.mActionPose);
616       SetActionsToWriter(controller.mActionTrackpad_Analog);
617       SetActionsToWriter(controller.mActionTrackpad_Pressed);
618       SetActionsToWriter(controller.mActionTrackpad_Touched);
619       SetActionsToWriter(controller.mActionTrigger_Value);
620       SetActionsToWriter(controller.mActionGrip_Pressed);
621       SetActionsToWriter(controller.mActionGrip_Touched);
622       SetActionsToWriter(controller.mActionMenu_Pressed);
623       SetActionsToWriter(controller.mActionMenu_Touched);
624       SetActionsToWriter(controller.mActionSystem_Pressed);
625       SetActionsToWriter(controller.mActionSystem_Touched);
626       SetActionsToWriter(controller.mActionA_Pressed);
627       SetActionsToWriter(controller.mActionA_Touched);
628       SetActionsToWriter(controller.mActionB_Pressed);
629       SetActionsToWriter(controller.mActionB_Touched);
630       SetActionsToWriter(controller.mActionThumbstick_Analog);
631       SetActionsToWriter(controller.mActionThumbstick_Pressed);
632       SetActionsToWriter(controller.mActionThumbstick_Touched);
633       SetActionsToWriter(controller.mActionFingerIndex_Value);
634       SetActionsToWriter(controller.mActionFingerMiddle_Value);
635       SetActionsToWriter(controller.mActionFingerRing_Value);
636       SetActionsToWriter(controller.mActionFingerPinky_Value);
637       SetActionsToWriter(controller.mActionBumper_Pressed);
638       SetActionsToWriter(controller.mActionHaptic);
639     }
640     actionWriter.EndArray();  // End "actions": []
641     actionWriter.End();
642 
643     std::ofstream actionfile(controllerAction.BeginReading());
644     nsCString actionResult(actionData.get());
645     if (actionfile.is_open()) {
646       actionfile << actionResult.get();
647       actionfile.close();
648     }
649   }
650 
651   vr::EVRInputError err =
652       vr::VRInput()->SetActionManifestPath(controllerAction.BeginReading());
653   if (err != vr::VRInputError_None) {
654     NS_WARNING("OpenVR - SetActionManifestPath failed.");
655     return false;
656   }
657   // End of setup controller actions.
658 
659   // Notify the parent process these manifest files are already been recorded.
660   if (StaticPrefs::dom_vr_process_enabled_AtStartup()) {
661     NS_DispatchToMainThread(NS_NewRunnableFunction(
662         "SendOpenVRControllerActionPathToParent",
663         [controllerAction, viveManifest, WMRManifest, knucklesManifest,
664          cosmosManifest]() {
665           VRParent* vrParent = VRProcessChild::GetVRParent();
666           Unused << vrParent->SendOpenVRControllerActionPathToParent(
667               controllerAction);
668           Unused << vrParent->SendOpenVRControllerManifestPathToParent(
669               VRControllerType::HTCVive, viveManifest);
670           Unused << vrParent->SendOpenVRControllerManifestPathToParent(
671               VRControllerType::MSMR, WMRManifest);
672           Unused << vrParent->SendOpenVRControllerManifestPathToParent(
673               VRControllerType::ValveIndex, knucklesManifest);
674           Unused << vrParent->SendOpenVRControllerManifestPathToParent(
675               VRControllerType::HTCViveCosmos, cosmosManifest);
676         }));
677   } else {
678     sControllerActionFile->SetFileName(controllerAction.BeginReading());
679   }
680 
681   return true;
682 }
683 
684 #if defined(XP_WIN)
CreateD3DObjects()685 bool OpenVRSession::CreateD3DObjects() {
686   RefPtr<ID3D11Device> device = gfx::DeviceManagerDx::Get()->GetVRDevice();
687   if (!device) {
688     return false;
689   }
690   if (!CreateD3DContext(device)) {
691     return false;
692   }
693   return true;
694 }
695 #endif
696 
Shutdown()697 void OpenVRSession::Shutdown() {
698   StopHapticTimer();
699   StopHapticThread();
700   if (mVRSystem || mVRCompositor || mVRChaperone) {
701     ::vr::VR_Shutdown();
702     mVRCompositor = nullptr;
703     mVRChaperone = nullptr;
704     mVRSystem = nullptr;
705   }
706 }
707 
InitState(VRSystemState & aSystemState)708 bool OpenVRSession::InitState(VRSystemState& aSystemState) {
709   VRDisplayState& state = aSystemState.displayState;
710   strncpy(state.displayName, "OpenVR HMD", kVRDisplayNameMaxLen);
711   state.eightCC = GFX_VR_EIGHTCC('O', 'p', 'e', 'n', 'V', 'R', ' ', ' ');
712   state.isConnected =
713       mVRSystem->IsTrackedDeviceConnected(::vr::k_unTrackedDeviceIndex_Hmd);
714   state.isMounted = false;
715   state.capabilityFlags = (VRDisplayCapabilityFlags)(
716       (int)VRDisplayCapabilityFlags::Cap_None |
717       (int)VRDisplayCapabilityFlags::Cap_Orientation |
718       (int)VRDisplayCapabilityFlags::Cap_Position |
719       (int)VRDisplayCapabilityFlags::Cap_External |
720       (int)VRDisplayCapabilityFlags::Cap_Present |
721       (int)VRDisplayCapabilityFlags::Cap_StageParameters |
722       (int)VRDisplayCapabilityFlags::Cap_ImmersiveVR);
723   state.blendMode = VRDisplayBlendMode::Opaque;
724   state.reportsDroppedFrames = true;
725 
726   ::vr::ETrackedPropertyError err;
727   bool bHasProximitySensor = mVRSystem->GetBoolTrackedDeviceProperty(
728       ::vr::k_unTrackedDeviceIndex_Hmd, ::vr::Prop_ContainsProximitySensor_Bool,
729       &err);
730   if (err == ::vr::TrackedProp_Success && bHasProximitySensor) {
731     state.capabilityFlags = (VRDisplayCapabilityFlags)(
732         (int)state.capabilityFlags |
733         (int)VRDisplayCapabilityFlags::Cap_MountDetection);
734   }
735 
736   uint32_t w, h;
737   mVRSystem->GetRecommendedRenderTargetSize(&w, &h);
738   state.eyeResolution.width = w;
739   state.eyeResolution.height = h;
740 
741   // default to an identity quaternion
742   aSystemState.sensorState.pose.orientation[3] = 1.0f;
743 
744   UpdateStageParameters(state);
745   UpdateEyeParameters(aSystemState);
746 
747   VRHMDSensorState& sensorState = aSystemState.sensorState;
748   sensorState.flags = (VRDisplayCapabilityFlags)(
749       (int)VRDisplayCapabilityFlags::Cap_Orientation |
750       (int)VRDisplayCapabilityFlags::Cap_Position);
751   sensorState.pose.orientation[3] = 1.0f;  // Default to an identity quaternion
752 
753   return true;
754 }
755 
UpdateStageParameters(VRDisplayState & aState)756 void OpenVRSession::UpdateStageParameters(VRDisplayState& aState) {
757   float sizeX = 0.0f;
758   float sizeZ = 0.0f;
759   if (mVRChaperone->GetPlayAreaSize(&sizeX, &sizeZ)) {
760     ::vr::HmdMatrix34_t t =
761         mVRSystem->GetSeatedZeroPoseToStandingAbsoluteTrackingPose();
762     aState.stageSize.width = sizeX;
763     aState.stageSize.height = sizeZ;
764 
765     aState.sittingToStandingTransform[0] = t.m[0][0];
766     aState.sittingToStandingTransform[1] = t.m[1][0];
767     aState.sittingToStandingTransform[2] = t.m[2][0];
768     aState.sittingToStandingTransform[3] = 0.0f;
769 
770     aState.sittingToStandingTransform[4] = t.m[0][1];
771     aState.sittingToStandingTransform[5] = t.m[1][1];
772     aState.sittingToStandingTransform[6] = t.m[2][1];
773     aState.sittingToStandingTransform[7] = 0.0f;
774 
775     aState.sittingToStandingTransform[8] = t.m[0][2];
776     aState.sittingToStandingTransform[9] = t.m[1][2];
777     aState.sittingToStandingTransform[10] = t.m[2][2];
778     aState.sittingToStandingTransform[11] = 0.0f;
779 
780     aState.sittingToStandingTransform[12] = t.m[0][3];
781     aState.sittingToStandingTransform[13] = t.m[1][3];
782     aState.sittingToStandingTransform[14] = t.m[2][3];
783     aState.sittingToStandingTransform[15] = 1.0f;
784   } else {
785     // If we fail, fall back to reasonable defaults.
786     // 1m x 1m space, 0.75m high in seated position
787     aState.stageSize.width = 1.0f;
788     aState.stageSize.height = 1.0f;
789 
790     aState.sittingToStandingTransform[0] = 1.0f;
791     aState.sittingToStandingTransform[1] = 0.0f;
792     aState.sittingToStandingTransform[2] = 0.0f;
793     aState.sittingToStandingTransform[3] = 0.0f;
794 
795     aState.sittingToStandingTransform[4] = 0.0f;
796     aState.sittingToStandingTransform[5] = 1.0f;
797     aState.sittingToStandingTransform[6] = 0.0f;
798     aState.sittingToStandingTransform[7] = 0.0f;
799 
800     aState.sittingToStandingTransform[8] = 0.0f;
801     aState.sittingToStandingTransform[9] = 0.0f;
802     aState.sittingToStandingTransform[10] = 1.0f;
803     aState.sittingToStandingTransform[11] = 0.0f;
804 
805     aState.sittingToStandingTransform[12] = 0.0f;
806     aState.sittingToStandingTransform[13] = 0.75f;
807     aState.sittingToStandingTransform[14] = 0.0f;
808     aState.sittingToStandingTransform[15] = 1.0f;
809   }
810 }
811 
UpdateEyeParameters(VRSystemState & aState)812 void OpenVRSession::UpdateEyeParameters(VRSystemState& aState) {
813   // This must be called every frame in order to
814   // account for continuous adjustments to ipd.
815   gfx::Matrix4x4 headToEyeTransforms[2];
816 
817   for (uint32_t eye = 0; eye < 2; ++eye) {
818     ::vr::HmdMatrix34_t eyeToHead =
819         mVRSystem->GetEyeToHeadTransform(static_cast<::vr::Hmd_Eye>(eye));
820     aState.displayState.eyeTranslation[eye].x = eyeToHead.m[0][3];
821     aState.displayState.eyeTranslation[eye].y = eyeToHead.m[1][3];
822     aState.displayState.eyeTranslation[eye].z = eyeToHead.m[2][3];
823 
824     float left, right, up, down;
825     mVRSystem->GetProjectionRaw(static_cast<::vr::Hmd_Eye>(eye), &left, &right,
826                                 &up, &down);
827     aState.displayState.eyeFOV[eye].upDegrees = atan(-up) * 180.0 / M_PI;
828     aState.displayState.eyeFOV[eye].rightDegrees = atan(right) * 180.0 / M_PI;
829     aState.displayState.eyeFOV[eye].downDegrees = atan(down) * 180.0 / M_PI;
830     aState.displayState.eyeFOV[eye].leftDegrees = atan(-left) * 180.0 / M_PI;
831 
832     Matrix4x4 pose;
833     // NOTE! eyeToHead.m is a 3x4 matrix, not 4x4.  But
834     // because of its arrangement, we can copy the 12 elements in and
835     // then transpose them to the right place.
836     memcpy(&pose._11, &eyeToHead.m, sizeof(eyeToHead.m));
837     pose.Transpose();
838     pose.Invert();
839     headToEyeTransforms[eye] = pose;
840   }
841   aState.sensorState.CalcViewMatrices(headToEyeTransforms);
842 }
843 
UpdateHeadsetPose(VRSystemState & aState)844 void OpenVRSession::UpdateHeadsetPose(VRSystemState& aState) {
845   const uint32_t posesSize = ::vr::k_unTrackedDeviceIndex_Hmd + 1;
846   ::vr::TrackedDevicePose_t poses[posesSize];
847   // Note: We *must* call WaitGetPoses in order for any rendering to happen at
848   // all.
849   mVRCompositor->WaitGetPoses(poses, posesSize, nullptr, 0);
850 
851   ::vr::Compositor_FrameTiming timing;
852   timing.m_nSize = sizeof(::vr::Compositor_FrameTiming);
853   if (mVRCompositor->GetFrameTiming(&timing)) {
854     aState.sensorState.timestamp = timing.m_flSystemTimeInSeconds;
855   } else {
856     // This should not happen, but log it just in case
857     fprintf(stderr, "OpenVR - IVRCompositor::GetFrameTiming failed");
858   }
859 
860   if (poses[::vr::k_unTrackedDeviceIndex_Hmd].bDeviceIsConnected &&
861       poses[::vr::k_unTrackedDeviceIndex_Hmd].bPoseIsValid &&
862       poses[::vr::k_unTrackedDeviceIndex_Hmd].eTrackingResult ==
863           ::vr::TrackingResult_Running_OK) {
864     const ::vr::TrackedDevicePose_t& pose =
865         poses[::vr::k_unTrackedDeviceIndex_Hmd];
866 
867     gfx::Matrix4x4 m;
868     // NOTE! mDeviceToAbsoluteTracking is a 3x4 matrix, not 4x4.  But
869     // because of its arrangement, we can copy the 12 elements in and
870     // then transpose them to the right place.  We do this so we can
871     // pull out a Quaternion.
872     memcpy(&m._11, &pose.mDeviceToAbsoluteTracking,
873            sizeof(pose.mDeviceToAbsoluteTracking));
874     m.Transpose();
875 
876     gfx::Quaternion rot;
877     rot.SetFromRotationMatrix(m);
878     rot.Invert();
879 
880     aState.sensorState.flags = (VRDisplayCapabilityFlags)(
881         (int)aState.sensorState.flags |
882         (int)VRDisplayCapabilityFlags::Cap_Orientation);
883     aState.sensorState.pose.orientation[0] = rot.x;
884     aState.sensorState.pose.orientation[1] = rot.y;
885     aState.sensorState.pose.orientation[2] = rot.z;
886     aState.sensorState.pose.orientation[3] = rot.w;
887     aState.sensorState.pose.angularVelocity[0] = pose.vAngularVelocity.v[0];
888     aState.sensorState.pose.angularVelocity[1] = pose.vAngularVelocity.v[1];
889     aState.sensorState.pose.angularVelocity[2] = pose.vAngularVelocity.v[2];
890 
891     aState.sensorState.flags =
892         (VRDisplayCapabilityFlags)((int)aState.sensorState.flags |
893                                    (int)VRDisplayCapabilityFlags::Cap_Position);
894     aState.sensorState.pose.position[0] = m._41;
895     aState.sensorState.pose.position[1] = m._42;
896     aState.sensorState.pose.position[2] = m._43;
897     aState.sensorState.pose.linearVelocity[0] = pose.vVelocity.v[0];
898     aState.sensorState.pose.linearVelocity[1] = pose.vVelocity.v[1];
899     aState.sensorState.pose.linearVelocity[2] = pose.vVelocity.v[2];
900   }
901 }
902 
EnumerateControllers(VRSystemState & aState)903 void OpenVRSession::EnumerateControllers(VRSystemState& aState) {
904   MOZ_ASSERT(mVRSystem);
905 
906   MutexAutoLock lock(mControllerHapticStateMutex);
907 
908   bool controllerPresent[kVRControllerMaxCount] = {false};
909   uint32_t stateIndex = 0;
910   mActionsetFirefox = vr::k_ulInvalidActionSetHandle;
911   VRControllerType controllerType = VRControllerType::_empty;
912 
913   if (vr::VRInput()->GetActionSetHandle(
914           "/actions/firefox", &mActionsetFirefox) != vr::VRInputError_None) {
915     return;
916   }
917 
918   for (int8_t handIndex = 0; handIndex < OpenVRHand::Total; ++handIndex) {
919     if (handIndex == OpenVRHand::Left) {
920       if (vr::VRInput()->GetInputSourceHandle(
921               "/user/hand/left", &mControllerHand[OpenVRHand::Left].mSource) !=
922           vr::VRInputError_None) {
923         continue;
924       }
925     } else if (handIndex == OpenVRHand::Right) {
926       if (vr::VRInput()->GetInputSourceHandle(
927               "/user/hand/right",
928               &mControllerHand[OpenVRHand::Right].mSource) !=
929           vr::VRInputError_None) {
930         continue;
931       }
932     } else {
933       MOZ_ASSERT(false, "Unknown OpenVR hand type.");
934     }
935 
936     vr::InputOriginInfo_t originInfo;
937     if (vr::VRInput()->GetOriginTrackedDeviceInfo(
938             mControllerHand[handIndex].mSource, &originInfo,
939             sizeof(originInfo)) == vr::VRInputError_None &&
940         originInfo.trackedDeviceIndex != vr::k_unTrackedDeviceIndexInvalid &&
941         mVRSystem->IsTrackedDeviceConnected(originInfo.trackedDeviceIndex)) {
942       const ::vr::ETrackedDeviceClass deviceType =
943           mVRSystem->GetTrackedDeviceClass(originInfo.trackedDeviceIndex);
944       if (deviceType != ::vr::TrackedDeviceClass_Controller &&
945           deviceType != ::vr::TrackedDeviceClass_GenericTracker) {
946         continue;
947       }
948 
949       if (mControllerDeviceIndex[stateIndex] != handIndex) {
950         VRControllerState& controllerState = aState.controllerState[stateIndex];
951 
952         // Get controllers' action handles.
953         auto SetActionsToWriter = [&](ControllerAction& aAction) {
954           vr::VRInput()->GetActionHandle(aAction.name.BeginReading(),
955                                          &aAction.handle);
956         };
957 
958         SetActionsToWriter(mControllerHand[handIndex].mActionPose);
959         SetActionsToWriter(mControllerHand[handIndex].mActionHaptic);
960         SetActionsToWriter(mControllerHand[handIndex].mActionTrackpad_Analog);
961         SetActionsToWriter(mControllerHand[handIndex].mActionTrackpad_Pressed);
962         SetActionsToWriter(mControllerHand[handIndex].mActionTrackpad_Touched);
963         SetActionsToWriter(mControllerHand[handIndex].mActionTrigger_Value);
964         SetActionsToWriter(mControllerHand[handIndex].mActionGrip_Pressed);
965         SetActionsToWriter(mControllerHand[handIndex].mActionGrip_Touched);
966         SetActionsToWriter(mControllerHand[handIndex].mActionMenu_Pressed);
967         SetActionsToWriter(mControllerHand[handIndex].mActionMenu_Touched);
968         SetActionsToWriter(mControllerHand[handIndex].mActionSystem_Pressed);
969         SetActionsToWriter(mControllerHand[handIndex].mActionSystem_Touched);
970         SetActionsToWriter(mControllerHand[handIndex].mActionA_Pressed);
971         SetActionsToWriter(mControllerHand[handIndex].mActionA_Touched);
972         SetActionsToWriter(mControllerHand[handIndex].mActionB_Pressed);
973         SetActionsToWriter(mControllerHand[handIndex].mActionB_Touched);
974         SetActionsToWriter(mControllerHand[handIndex].mActionThumbstick_Analog);
975         SetActionsToWriter(
976             mControllerHand[handIndex].mActionThumbstick_Pressed);
977         SetActionsToWriter(
978             mControllerHand[handIndex].mActionThumbstick_Touched);
979         SetActionsToWriter(mControllerHand[handIndex].mActionFingerIndex_Value);
980         SetActionsToWriter(
981             mControllerHand[handIndex].mActionFingerMiddle_Value);
982         SetActionsToWriter(mControllerHand[handIndex].mActionFingerRing_Value);
983         SetActionsToWriter(mControllerHand[handIndex].mActionFingerPinky_Value);
984         SetActionsToWriter(mControllerHand[handIndex].mActionBumper_Pressed);
985 
986         nsCString deviceId;
987         VRControllerType contrlType = VRControllerType::_empty;
988         GetControllerDeviceId(deviceType, originInfo.trackedDeviceIndex,
989                               deviceId, contrlType);
990         // Controllers should be the same type with one VR display.
991         MOZ_ASSERT(controllerType == contrlType ||
992                    controllerType == VRControllerType::_empty);
993         controllerType = contrlType;
994         strncpy(controllerState.controllerName, deviceId.BeginReading(),
995                 kVRControllerNameMaxLen);
996         controllerState.numHaptics = kNumOpenVRHaptics;
997         controllerState.targetRayMode = gfx::TargetRayMode::TrackedPointer;
998         controllerState.type = controllerType;
999       }
1000       controllerPresent[stateIndex] = true;
1001       mControllerDeviceIndex[stateIndex] = static_cast<OpenVRHand>(handIndex);
1002       ++stateIndex;
1003     }
1004   }
1005 
1006   // Clear out entries for disconnected controllers
1007   for (uint32_t stateIndex = 0; stateIndex < kVRControllerMaxCount;
1008        stateIndex++) {
1009     if (!controllerPresent[stateIndex] &&
1010         mControllerDeviceIndex[stateIndex] != OpenVRHand::None) {
1011       mControllerDeviceIndex[stateIndex] = OpenVRHand::None;
1012       memset(&aState.controllerState[stateIndex], 0, sizeof(VRControllerState));
1013     }
1014   }
1015 
1016   // Create controller mapper
1017   if (controllerType != VRControllerType::_empty) {
1018     switch (controllerType) {
1019       case VRControllerType::HTCVive:
1020         mControllerMapper = MakeUnique<OpenVRViveMapper>();
1021         break;
1022       case VRControllerType::HTCViveCosmos:
1023         mControllerMapper = MakeUnique<OpenVRCosmosMapper>();
1024         break;
1025 #if defined(XP_WIN)
1026       case VRControllerType::MSMR:
1027         mControllerMapper = MakeUnique<OpenVRWMRMapper>();
1028         break;
1029 #endif
1030       case VRControllerType::ValveIndex:
1031         mControllerMapper = MakeUnique<OpenVRKnucklesMapper>();
1032         break;
1033       default:
1034         mControllerMapper = MakeUnique<OpenVRDefaultMapper>();
1035         NS_WARNING("Undefined controller type");
1036         break;
1037     }
1038   }
1039 }
1040 
UpdateControllerButtons(VRSystemState & aState)1041 void OpenVRSession::UpdateControllerButtons(VRSystemState& aState) {
1042   MOZ_ASSERT(mVRSystem);
1043 
1044   for (uint32_t stateIndex = 0; stateIndex < kVRControllerMaxCount;
1045        ++stateIndex) {
1046     const OpenVRHand role = mControllerDeviceIndex[stateIndex];
1047     if (role == OpenVRHand::None) {
1048       continue;
1049     }
1050     VRControllerState& controllerState = aState.controllerState[stateIndex];
1051     controllerState.hand = GetControllerHandFromControllerRole(role);
1052     mControllerMapper->UpdateButtons(controllerState, mControllerHand[role]);
1053     SetControllerSelectionAndSqueezeFrameId(
1054         controllerState, aState.displayState.lastSubmittedFrameId);
1055   }
1056 }
1057 
UpdateControllerPoses(VRSystemState & aState)1058 void OpenVRSession::UpdateControllerPoses(VRSystemState& aState) {
1059   MOZ_ASSERT(mVRSystem);
1060 
1061   for (uint32_t stateIndex = 0; stateIndex < kVRControllerMaxCount;
1062        ++stateIndex) {
1063     const OpenVRHand role = mControllerDeviceIndex[stateIndex];
1064     if (role == OpenVRHand::None) {
1065       continue;
1066     }
1067     VRControllerState& controllerState = aState.controllerState[stateIndex];
1068     vr::InputPoseActionData_t poseData;
1069     if (vr::VRInput()->GetPoseActionDataRelativeToNow(
1070             mControllerHand[role].mActionPose.handle,
1071             vr::TrackingUniverseSeated, 0, &poseData, sizeof(poseData),
1072             vr::k_ulInvalidInputValueHandle) != vr::VRInputError_None ||
1073         !poseData.bActive || !poseData.pose.bPoseIsValid) {
1074       controllerState.isOrientationValid = false;
1075       controllerState.isPositionValid = false;
1076     } else {
1077       const ::vr::TrackedDevicePose_t& pose = poseData.pose;
1078       if (pose.bDeviceIsConnected) {
1079         controllerState.flags =
1080             (dom::GamepadCapabilityFlags::Cap_Orientation |
1081              dom::GamepadCapabilityFlags::Cap_Position |
1082              dom::GamepadCapabilityFlags::Cap_GripSpacePosition);
1083       } else {
1084         controllerState.flags = dom::GamepadCapabilityFlags::Cap_None;
1085       }
1086       if (pose.bPoseIsValid &&
1087           pose.eTrackingResult == ::vr::TrackingResult_Running_OK) {
1088         gfx::Matrix4x4 m;
1089 
1090         // NOTE! mDeviceToAbsoluteTracking is a 3x4 matrix, not 4x4.  But
1091         // because of its arrangement, we can copy the 12 elements in and
1092         // then transpose them to the right place.  We do this so we can
1093         // pull out a Quaternion.
1094         memcpy(&m.components, &pose.mDeviceToAbsoluteTracking,
1095                sizeof(pose.mDeviceToAbsoluteTracking));
1096         m.Transpose();
1097 
1098         gfx::Quaternion rot;
1099         rot.SetFromRotationMatrix(m);
1100         rot.Invert();
1101 
1102         controllerState.pose.orientation[0] = rot.x;
1103         controllerState.pose.orientation[1] = rot.y;
1104         controllerState.pose.orientation[2] = rot.z;
1105         controllerState.pose.orientation[3] = rot.w;
1106         controllerState.pose.angularVelocity[0] = pose.vAngularVelocity.v[0];
1107         controllerState.pose.angularVelocity[1] = pose.vAngularVelocity.v[1];
1108         controllerState.pose.angularVelocity[2] = pose.vAngularVelocity.v[2];
1109         controllerState.pose.angularAcceleration[0] = 0.0f;
1110         controllerState.pose.angularAcceleration[1] = 0.0f;
1111         controllerState.pose.angularAcceleration[2] = 0.0f;
1112         controllerState.isOrientationValid = true;
1113 
1114         controllerState.pose.position[0] = m._41;
1115         controllerState.pose.position[1] = m._42;
1116         controllerState.pose.position[2] = m._43;
1117         controllerState.pose.linearVelocity[0] = pose.vVelocity.v[0];
1118         controllerState.pose.linearVelocity[1] = pose.vVelocity.v[1];
1119         controllerState.pose.linearVelocity[2] = pose.vVelocity.v[2];
1120         controllerState.pose.linearAcceleration[0] = 0.0f;
1121         controllerState.pose.linearAcceleration[1] = 0.0f;
1122         controllerState.pose.linearAcceleration[2] = 0.0f;
1123         controllerState.isPositionValid = true;
1124 
1125         // Calculate its target ray space by shifting degrees in x-axis
1126         // for ergonomic.
1127         const float kPointerAngleDegrees = -0.698;  // 40 degrees.
1128         gfx::Matrix4x4 rayMtx(m);
1129         rayMtx.RotateX(kPointerAngleDegrees);
1130         gfx::Quaternion rayRot;
1131         rayRot.SetFromRotationMatrix(rayMtx);
1132         rayRot.Invert();
1133 
1134         controllerState.targetRayPose = controllerState.pose;
1135         controllerState.targetRayPose.orientation[0] = rayRot.x;
1136         controllerState.targetRayPose.orientation[1] = rayRot.y;
1137         controllerState.targetRayPose.orientation[2] = rayRot.z;
1138         controllerState.targetRayPose.orientation[3] = rayRot.w;
1139         controllerState.targetRayPose.position[0] = rayMtx._41;
1140         controllerState.targetRayPose.position[1] = rayMtx._42;
1141         controllerState.targetRayPose.position[2] = rayMtx._43;
1142       }
1143     }
1144   }
1145 }
1146 
GetControllerDeviceId(::vr::ETrackedDeviceClass aDeviceType,::vr::TrackedDeviceIndex_t aDeviceIndex,nsCString & aId,VRControllerType & aControllerType)1147 void OpenVRSession::GetControllerDeviceId(
1148     ::vr::ETrackedDeviceClass aDeviceType,
1149     ::vr::TrackedDeviceIndex_t aDeviceIndex, nsCString& aId,
1150     VRControllerType& aControllerType) {
1151   switch (aDeviceType) {
1152     case ::vr::TrackedDeviceClass_Controller: {
1153       ::vr::ETrackedPropertyError err;
1154       uint32_t requiredBufferLen;
1155       bool isFound = false;
1156       char charBuf[128];
1157       requiredBufferLen = mVRSystem->GetStringTrackedDeviceProperty(
1158           aDeviceIndex, ::vr::Prop_RenderModelName_String, charBuf, 128, &err);
1159       if (requiredBufferLen > 128) {
1160         MOZ_CRASH("Larger than the buffer size.");
1161       }
1162       MOZ_ASSERT(requiredBufferLen && err == ::vr::TrackedProp_Success);
1163       nsCString deviceId(charBuf);
1164       if (deviceId.Find("vr_controller_vive") != kNotFound) {
1165         aId.AssignLiteral("OpenVR Gamepad");
1166         isFound = true;
1167         aControllerType = VRControllerType::HTCVive;
1168       } else if (deviceId.Find("knuckles") != kNotFound ||
1169                  deviceId.Find("valve_controller_knu") != kNotFound) {
1170         aId.AssignLiteral("OpenVR Knuckles");
1171         isFound = true;
1172         aControllerType = VRControllerType::ValveIndex;
1173       } else if (deviceId.Find("vive_cosmos_controller") != kNotFound) {
1174         aId.AssignLiteral("OpenVR Cosmos");
1175         isFound = true;
1176         aControllerType = VRControllerType::HTCViveCosmos;
1177       }
1178       if (!isFound) {
1179         requiredBufferLen = mVRSystem->GetStringTrackedDeviceProperty(
1180             aDeviceIndex, ::vr::Prop_SerialNumber_String, charBuf, 128, &err);
1181         if (requiredBufferLen > 128) {
1182           MOZ_CRASH("Larger than the buffer size.");
1183         }
1184         MOZ_ASSERT(requiredBufferLen && err == ::vr::TrackedProp_Success);
1185         deviceId.Assign(charBuf);
1186         if (deviceId.Find("MRSOURCE") != kNotFound) {
1187           aId.AssignLiteral("Spatial Controller (Spatial Interaction Source) ");
1188           mIsWindowsMR = true;
1189           isFound = true;
1190           aControllerType = VRControllerType::MSMR;
1191         }
1192       }
1193       if (!isFound) {
1194         aId.AssignLiteral("OpenVR Undefined");
1195         aControllerType = VRControllerType::_empty;
1196       }
1197       break;
1198     }
1199     case ::vr::TrackedDeviceClass_GenericTracker: {
1200       aId.AssignLiteral("OpenVR Tracker");
1201       aControllerType = VRControllerType::_empty;
1202       break;
1203     }
1204     default:
1205       MOZ_ASSERT(false);
1206       break;
1207   }
1208 }
1209 
StartFrame(mozilla::gfx::VRSystemState & aSystemState)1210 void OpenVRSession::StartFrame(mozilla::gfx::VRSystemState& aSystemState) {
1211   UpdateHeadsetPose(aSystemState);
1212   UpdateEyeParameters(aSystemState);
1213   EnumerateControllers(aSystemState);
1214 
1215   vr::VRActiveActionSet_t actionSet = {0};
1216   actionSet.ulActionSet = mActionsetFirefox;
1217   vr::VRInput()->UpdateActionState(&actionSet, sizeof(actionSet), 1);
1218   UpdateControllerButtons(aSystemState);
1219   UpdateControllerPoses(aSystemState);
1220   UpdateTelemetry(aSystemState);
1221 }
1222 
ProcessEvents(mozilla::gfx::VRSystemState & aSystemState)1223 void OpenVRSession::ProcessEvents(mozilla::gfx::VRSystemState& aSystemState) {
1224   bool isHmdPresent = ::vr::VR_IsHmdPresent();
1225   if (!isHmdPresent) {
1226     mShouldQuit = true;
1227   }
1228 
1229   ::vr::VREvent_t event;
1230   while (mVRSystem && mVRSystem->PollNextEvent(&event, sizeof(event))) {
1231     switch (event.eventType) {
1232       case ::vr::VREvent_TrackedDeviceUserInteractionStarted:
1233         if (event.trackedDeviceIndex == ::vr::k_unTrackedDeviceIndex_Hmd) {
1234           aSystemState.displayState.isMounted = true;
1235         }
1236         break;
1237       case ::vr::VREvent_TrackedDeviceUserInteractionEnded:
1238         if (event.trackedDeviceIndex == ::vr::k_unTrackedDeviceIndex_Hmd) {
1239           aSystemState.displayState.isMounted = false;
1240         }
1241         break;
1242       case ::vr::EVREventType::VREvent_TrackedDeviceActivated:
1243         if (event.trackedDeviceIndex == ::vr::k_unTrackedDeviceIndex_Hmd) {
1244           aSystemState.displayState.isConnected = true;
1245         }
1246         break;
1247       case ::vr::EVREventType::VREvent_TrackedDeviceDeactivated:
1248         if (event.trackedDeviceIndex == ::vr::k_unTrackedDeviceIndex_Hmd) {
1249           aSystemState.displayState.isConnected = false;
1250         }
1251         break;
1252       case ::vr::EVREventType::VREvent_DriverRequestedQuit:
1253       case ::vr::EVREventType::VREvent_Quit:
1254       // When SteamVR runtime haven't been launched before viewing VR,
1255       // SteamVR will send a VREvent_ProcessQuit event. It will tell the parent
1256       // process to shutdown the VR process, and we need to avoid it.
1257       // case ::vr::EVREventType::VREvent_ProcessQuit:
1258       case ::vr::EVREventType::VREvent_QuitAcknowledged:
1259         mShouldQuit = true;
1260         break;
1261       default:
1262         // ignore
1263         break;
1264     }
1265   }
1266 }
1267 
1268 #if defined(XP_WIN)
SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive & aLayer,ID3D11Texture2D * aTexture)1269 bool OpenVRSession::SubmitFrame(
1270     const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer,
1271     ID3D11Texture2D* aTexture) {
1272   return SubmitFrame((void*)aTexture, ::vr::ETextureType::TextureType_DirectX,
1273                      aLayer.leftEyeRect, aLayer.rightEyeRect);
1274 }
1275 #elif defined(XP_MACOSX)
SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive & aLayer,const VRLayerTextureHandle & aTexture)1276 bool OpenVRSession::SubmitFrame(
1277     const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer,
1278     const VRLayerTextureHandle& aTexture) {
1279   return SubmitFrame(aTexture, ::vr::ETextureType::TextureType_IOSurface,
1280                      aLayer.leftEyeRect, aLayer.rightEyeRect);
1281 }
1282 #endif
1283 
SubmitFrame(const VRLayerTextureHandle & aTextureHandle,::vr::ETextureType aTextureType,const VRLayerEyeRect & aLeftEyeRect,const VRLayerEyeRect & aRightEyeRect)1284 bool OpenVRSession::SubmitFrame(const VRLayerTextureHandle& aTextureHandle,
1285                                 ::vr::ETextureType aTextureType,
1286                                 const VRLayerEyeRect& aLeftEyeRect,
1287                                 const VRLayerEyeRect& aRightEyeRect) {
1288   ::vr::Texture_t tex;
1289 #if defined(XP_MACOSX)
1290   // We get aTextureHandle from get_SurfaceDescriptorMacIOSurface() at
1291   // VRDisplayExternal. scaleFactor and opaque are skipped because they always
1292   // are 1.0 and false.
1293   RefPtr<MacIOSurface> surf = MacIOSurface::LookupSurface(aTextureHandle);
1294   if (!surf) {
1295     NS_WARNING("OpenVRSession::SubmitFrame failed to get a MacIOSurface");
1296     return false;
1297   }
1298 
1299   CFTypeRefPtr<IOSurfaceRef> ioSurface = surf->GetIOSurfaceRef();
1300   tex.handle = (void*)ioSurface.get();
1301 #else
1302   tex.handle = aTextureHandle;
1303 #endif
1304   tex.eType = aTextureType;
1305   tex.eColorSpace = ::vr::EColorSpace::ColorSpace_Auto;
1306 
1307   ::vr::VRTextureBounds_t bounds;
1308   bounds.uMin = aLeftEyeRect.x;
1309   bounds.vMin = 1.0 - aLeftEyeRect.y;
1310   bounds.uMax = aLeftEyeRect.x + aLeftEyeRect.width;
1311   bounds.vMax = 1.0 - (aLeftEyeRect.y + aLeftEyeRect.height);
1312 
1313   ::vr::EVRCompositorError err;
1314   err = mVRCompositor->Submit(::vr::EVREye::Eye_Left, &tex, &bounds);
1315   if (err != ::vr::EVRCompositorError::VRCompositorError_None) {
1316     printf_stderr("OpenVR Compositor Submit() failed.\n");
1317   }
1318 
1319   bounds.uMin = aRightEyeRect.x;
1320   bounds.vMin = 1.0 - aRightEyeRect.y;
1321   bounds.uMax = aRightEyeRect.x + aRightEyeRect.width;
1322   bounds.vMax = 1.0 - (aRightEyeRect.y + aRightEyeRect.height);
1323 
1324   err = mVRCompositor->Submit(::vr::EVREye::Eye_Right, &tex, &bounds);
1325   if (err != ::vr::EVRCompositorError::VRCompositorError_None) {
1326     printf_stderr("OpenVR Compositor Submit() failed.\n");
1327   }
1328 
1329   mVRCompositor->PostPresentHandoff();
1330   return true;
1331 }
1332 
StopPresentation()1333 void OpenVRSession::StopPresentation() {
1334   mVRCompositor->ClearLastSubmittedFrame();
1335 
1336   ::vr::Compositor_CumulativeStats stats;
1337   mVRCompositor->GetCumulativeStats(&stats,
1338                                     sizeof(::vr::Compositor_CumulativeStats));
1339 }
1340 
StartPresentation()1341 bool OpenVRSession::StartPresentation() { return true; }
1342 
VibrateHaptic(uint32_t aControllerIdx,uint32_t aHapticIndex,float aIntensity,float aDuration)1343 void OpenVRSession::VibrateHaptic(uint32_t aControllerIdx,
1344                                   uint32_t aHapticIndex, float aIntensity,
1345                                   float aDuration) {
1346   MutexAutoLock lock(mControllerHapticStateMutex);
1347 
1348   // Initilize the haptic thread when the first time to do vibration.
1349   if (!mHapticThread) {
1350     NS_DispatchToMainThread(NS_NewRunnableFunction(
1351         "OpenVRSession::StartHapticThread", [this]() { StartHapticThread(); }));
1352   }
1353   if (aHapticIndex >= kNumOpenVRHaptics ||
1354       aControllerIdx >= kVRControllerMaxCount) {
1355     return;
1356   }
1357 
1358   const OpenVRHand role = mControllerDeviceIndex[aControllerIdx];
1359   if (role == OpenVRHand::None) {
1360     return;
1361   }
1362   mHapticPulseRemaining[aControllerIdx][aHapticIndex] = aDuration;
1363   mHapticPulseIntensity[aControllerIdx][aHapticIndex] = aIntensity;
1364 }
1365 
StartHapticThread()1366 void OpenVRSession::StartHapticThread() {
1367   MOZ_ASSERT(NS_IsMainThread());
1368   if (!mHapticThread) {
1369     mHapticThread = new VRThread(NS_LITERAL_CSTRING("VR_OpenVR_Haptics"));
1370   }
1371   mHapticThread->Start();
1372   StartHapticTimer();
1373 }
1374 
StopHapticThread()1375 void OpenVRSession::StopHapticThread() {
1376   if (mHapticThread) {
1377     NS_DispatchToMainThread(NS_NewRunnableFunction(
1378         "mHapticThread::Shutdown",
1379         [thread = mHapticThread]() { thread->Shutdown(); }));
1380     mHapticThread = nullptr;
1381   }
1382 }
1383 
StartHapticTimer()1384 void OpenVRSession::StartHapticTimer() {
1385   if (!mHapticTimer && mHapticThread) {
1386     mLastHapticUpdate = TimeStamp();
1387     mHapticTimer = NS_NewTimer();
1388     mHapticTimer->SetTarget(mHapticThread->GetThread()->EventTarget());
1389     mHapticTimer->InitWithNamedFuncCallback(
1390         HapticTimerCallback, this, kVRHapticUpdateInterval,
1391         nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP,
1392         "OpenVRSession::HapticTimerCallback");
1393   }
1394 }
1395 
StopHapticTimer()1396 void OpenVRSession::StopHapticTimer() {
1397   if (mHapticTimer) {
1398     mHapticTimer->Cancel();
1399     mHapticTimer = nullptr;
1400   }
1401 }
1402 
1403 /*static*/
HapticTimerCallback(nsITimer * aTimer,void * aClosure)1404 void OpenVRSession::HapticTimerCallback(nsITimer* aTimer, void* aClosure) {
1405   /**
1406    * It is safe to use the pointer passed in aClosure to reference the
1407    * OpenVRSession object as the timer is canceled in OpenVRSession::Shutdown,
1408    * which is called by the OpenVRSession destructor, guaranteeing
1409    * that this function runs if and only if the VRManager object is valid.
1410    */
1411   OpenVRSession* self = static_cast<OpenVRSession*>(aClosure);
1412   MOZ_ASSERT(self);
1413   self->UpdateHaptics();
1414 }
1415 
UpdateHaptics()1416 void OpenVRSession::UpdateHaptics() {
1417   MOZ_ASSERT(mHapticThread->GetThread() == NS_GetCurrentThread());
1418   MOZ_ASSERT(mVRSystem);
1419 
1420   MutexAutoLock lock(mControllerHapticStateMutex);
1421 
1422   TimeStamp now = TimeStamp::Now();
1423   if (mLastHapticUpdate.IsNull()) {
1424     mLastHapticUpdate = now;
1425     return;
1426   }
1427   float deltaTime = (float)(now - mLastHapticUpdate).ToSeconds();
1428   mLastHapticUpdate = now;
1429 
1430   for (uint32_t stateIndex = 0; stateIndex < kVRControllerMaxCount;
1431        ++stateIndex) {
1432     const OpenVRHand role = mControllerDeviceIndex[stateIndex];
1433     if (role == OpenVRHand::None) {
1434       continue;
1435     }
1436     for (uint32_t hapticIdx = 0; hapticIdx < kNumOpenVRHaptics; hapticIdx++) {
1437       float intensity = mHapticPulseIntensity[stateIndex][hapticIdx];
1438       float duration = mHapticPulseRemaining[stateIndex][hapticIdx];
1439       if (duration <= 0.0f || intensity <= 0.0f) {
1440         continue;
1441       }
1442       vr::VRInput()->TriggerHapticVibrationAction(
1443           mControllerHand[role].mActionHaptic.handle, 0.0f, deltaTime, 4.0f,
1444           intensity > 1.0f ? 1.0f : intensity, vr::k_ulInvalidInputValueHandle);
1445 
1446       duration -= deltaTime;
1447       if (duration < 0.0f) {
1448         duration = 0.0f;
1449       }
1450       mHapticPulseRemaining[stateIndex][hapticIdx] = duration;
1451     }
1452   }
1453 }
1454 
StopVibrateHaptic(uint32_t aControllerIdx)1455 void OpenVRSession::StopVibrateHaptic(uint32_t aControllerIdx) {
1456   MutexAutoLock lock(mControllerHapticStateMutex);
1457   if (aControllerIdx >= kVRControllerMaxCount) {
1458     return;
1459   }
1460   for (int iHaptic = 0; iHaptic < kNumOpenVRHaptics; iHaptic++) {
1461     mHapticPulseRemaining[aControllerIdx][iHaptic] = 0.0f;
1462   }
1463 }
1464 
StopAllHaptics()1465 void OpenVRSession::StopAllHaptics() {
1466   MutexAutoLock lock(mControllerHapticStateMutex);
1467   for (auto& controller : mHapticPulseRemaining) {
1468     for (auto& haptic : controller) {
1469       haptic = 0.0f;
1470     }
1471   }
1472 }
1473 
UpdateTelemetry(VRSystemState & aSystemState)1474 void OpenVRSession::UpdateTelemetry(VRSystemState& aSystemState) {
1475   ::vr::Compositor_CumulativeStats stats;
1476   mVRCompositor->GetCumulativeStats(&stats,
1477                                     sizeof(::vr::Compositor_CumulativeStats));
1478   aSystemState.displayState.droppedFrameCount = stats.m_nNumReprojectedFrames;
1479 }
1480 
1481 }  // namespace mozilla::gfx
1482