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