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