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/. */
8 #include "mozilla/JSONStringWriteFuncs.h"
9 #include "mozilla/ClearOnShutdown.h"
10 #include "nsIThread.h"
13 #include "OpenVRSession.h"
14 #include "mozilla/StaticPrefs_dom.h"
18 # include "mozilla/gfx/DeviceManagerDx.h"
19 #elif defined(XP_MACOSX)
20 # include "mozilla/gfx/MacIOSurface.h"
24 # include <sys/stat.h> // for umask()
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"
42 #include "VRProcessChild.h"
46 # define M_PI 3.14159265358979323846264338327950288
49 #define BTN_MASK_FROM_ID(_id) ::vr::ButtonMaskFromId(vr::EVRButtonId::_id)
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;
58 using namespace mozilla::gfx
;
60 namespace mozilla::gfx
{
64 class ControllerManifestFile
{
65 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ControllerManifestFile
)
68 static already_AddRefed
<ControllerManifestFile
> CreateManifest() {
69 RefPtr
<ControllerManifestFile
> manifest
= new ControllerManifestFile();
70 return manifest
.forget();
74 if (mFileName
.IsEmpty() ||
75 !std::ifstream(mFileName
.BeginReading()).good()) {
81 void SetFileName(const char* aName
) { mFileName
= aName
; }
83 const char* GetFileName() const { return mFileName
.BeginReading(); }
86 ControllerManifestFile() = default;
88 ~ControllerManifestFile() {
89 if (!mFileName
.IsEmpty() && remove(mFileName
.BeginReading()) != 0) {
90 MOZ_ASSERT(false, "Delete controller manifest file failed.");
98 // We wanna keep these temporary files existing
99 // until Firefox is closed instead of following OpenVRSession's lifetime.
100 StaticRefPtr
<ControllerManifestFile
> sCosmosBindingFile
;
101 StaticRefPtr
<ControllerManifestFile
> sKnucklesBindingFile
;
102 StaticRefPtr
<ControllerManifestFile
> sViveBindingFile
;
104 StaticRefPtr
<ControllerManifestFile
> sWMRBindingFile
;
106 StaticRefPtr
<ControllerManifestFile
> sControllerActionFile
;
108 dom::GamepadHand
GetControllerHandFromControllerRole(OpenVRHand aRole
) {
109 dom::GamepadHand hand
;
111 case OpenVRHand::None
:
112 hand
= dom::GamepadHand::_empty
;
114 case OpenVRHand::Left
:
115 hand
= dom::GamepadHand::Left
;
117 case OpenVRHand::Right
:
118 hand
= dom::GamepadHand::Right
;
121 hand
= dom::GamepadHand::_empty
;
129 bool FileIsExisting(const nsCString
& aPath
) {
130 if (aPath
.IsEmpty() || !std::ifstream(aPath
.BeginReading()).good()) {
136 }; // anonymous namespace
139 bool GenerateTempFileName(nsCString
& aPath
) {
140 TCHAR tempPathBuffer
[MAX_PATH
];
141 TCHAR tempFileName
[MAX_PATH
];
143 // Gets the temp path env string (no guarantee it's a valid path).
144 DWORD dwRetVal
= GetTempPath(MAX_PATH
, tempPathBuffer
);
145 if (dwRetVal
> MAX_PATH
|| (dwRetVal
== 0)) {
146 NS_WARNING("OpenVR - Creating temp path failed.");
150 // Generates a temporary file name.
151 UINT uRetVal
= GetTempFileName(tempPathBuffer
, // directory for tmp files
152 TEXT("mozvr"), // temp file name prefix
153 0, // create unique name
154 tempFileName
); // buffer for name
156 NS_WARNING("OpenVR - Creating temp file failed.");
160 aPath
.Assign(NS_ConvertUTF16toUTF8(tempFileName
));
164 bool GenerateTempFileName(nsCString
& aPath
) {
165 const char tmp
[] = "/tmp/mozvrXXXXXX";
166 char fileName
[PATH_MAX
];
168 strcpy(fileName
, tmp
);
169 const mode_t prevMask
= umask(S_IXUSR
| S_IRWXO
| S_IRWXG
);
170 const int fd
= mkstemp(fileName
);
173 NS_WARNING(nsPrintfCString("OpenVR - Creating temp file failed: %s",
180 aPath
.Assign(fileName
);
183 #endif // defined(XP_WIN)
185 OpenVRSession::OpenVRSession()
186 : mVRSystem(nullptr),
187 mVRChaperone(nullptr),
188 mVRCompositor(nullptr),
189 mHapticPulseRemaining
{},
190 mHapticPulseIntensity
{},
192 mControllerHapticStateMutex(
193 "OpenVRSession::mControllerHapticStateMutex") {
194 std::fill_n(mControllerDeviceIndex
, kVRControllerMaxCount
, OpenVRHand::None
);
197 OpenVRSession::~OpenVRSession() {
198 mActionsetFirefox
= ::vr::k_ulInvalidActionSetHandle
;
202 bool OpenVRSession::Initialize(mozilla::gfx::VRSystemState
& aSystemState
,
203 bool aDetectRuntimesOnly
) {
204 if (StaticPrefs::dom_vr_puppet_enabled()) {
205 // Ensure that tests using the VR Puppet do not find real hardware
208 if (!StaticPrefs::dom_vr_enabled() || !StaticPrefs::dom_vr_openvr_enabled()) {
211 if (mVRSystem
!= nullptr) {
212 // Already initialized
215 if (!::vr::VR_IsRuntimeInstalled()) {
218 if (aDetectRuntimesOnly
) {
219 aSystemState
.displayState
.capabilityFlags
|=
220 VRDisplayCapabilityFlags::Cap_ImmersiveVR
;
223 if (!::vr::VR_IsHmdPresent()) {
229 ::vr::VR_Init(&err
, ::vr::EVRApplicationType::VRApplication_Scene
);
234 mVRSystem
= (::vr::IVRSystem
*)::vr::VR_GetGenericInterface(
235 ::vr::IVRSystem_Version
, &err
);
236 if (err
|| !mVRSystem
) {
240 mVRChaperone
= (::vr::IVRChaperone
*)::vr::VR_GetGenericInterface(
241 ::vr::IVRChaperone_Version
, &err
);
242 if (err
|| !mVRChaperone
) {
246 mVRCompositor
= (::vr::IVRCompositor
*)::vr::VR_GetGenericInterface(
247 ::vr::IVRCompositor_Version
, &err
);
248 if (err
|| !mVRCompositor
) {
254 if (!CreateD3DObjects()) {
261 // Configure coordinate system
262 mVRCompositor
->SetTrackingSpace(::vr::TrackingUniverseSeated
);
264 if (!InitState(aSystemState
)) {
268 if (!SetupContollerActions()) {
276 // "actions": [] Action paths must take the form: "/actions/<action
277 // set>/in|out/<action>"
278 #define CreateControllerAction(hand, name, type) \
279 ControllerAction("/actions/firefox/in/" #hand "Hand_" #name, #type)
280 #define CreateControllerOutAction(hand, name, type) \
281 ControllerAction("/actions/firefox/out/" #hand "Hand_" #name, #type)
283 bool OpenVRSession::SetupContollerActions() {
284 if (!vr::VRInput()) {
285 NS_WARNING("OpenVR - vr::VRInput() is null.");
289 // Check if this device binding file has been created.
290 // If it didn't exist yet, create a new temp file.
291 nsCString controllerAction
;
292 nsCString viveManifest
;
293 nsCString WMRManifest
;
294 nsCString knucklesManifest
;
295 nsCString cosmosManifest
;
297 // Getting / Generating manifest file paths.
298 if (StaticPrefs::dom_vr_process_enabled_AtStartup()) {
299 VRParent
* vrParent
= VRProcessChild::GetVRParent();
302 if (vrParent
->GetOpenVRControllerActionPath(&output
)) {
303 controllerAction
= output
;
306 if (vrParent
->GetOpenVRControllerManifestPath(VRControllerType::HTCVive
,
308 viveManifest
= output
;
310 if (!viveManifest
.Length() || !FileIsExisting(viveManifest
)) {
311 if (!GenerateTempFileName(viveManifest
)) {
314 OpenVRViveBinding viveBinding
;
315 std::ofstream
viveBindingFile(viveManifest
.BeginReading());
316 if (viveBindingFile
.is_open()) {
317 viveBindingFile
<< viveBinding
.binding
;
318 viveBindingFile
.close();
323 if (vrParent
->GetOpenVRControllerManifestPath(VRControllerType::MSMR
,
325 WMRManifest
= output
;
327 if (!WMRManifest
.Length() || !FileIsExisting(WMRManifest
)) {
328 if (!GenerateTempFileName(WMRManifest
)) {
331 OpenVRWMRBinding WMRBinding
;
332 std::ofstream
WMRBindingFile(WMRManifest
.BeginReading());
333 if (WMRBindingFile
.is_open()) {
334 WMRBindingFile
<< WMRBinding
.binding
;
335 WMRBindingFile
.close();
339 if (vrParent
->GetOpenVRControllerManifestPath(VRControllerType::ValveIndex
,
341 knucklesManifest
= output
;
343 if (!knucklesManifest
.Length() || !FileIsExisting(knucklesManifest
)) {
344 if (!GenerateTempFileName(knucklesManifest
)) {
347 OpenVRKnucklesBinding knucklesBinding
;
348 std::ofstream
knucklesBindingFile(knucklesManifest
.BeginReading());
349 if (knucklesBindingFile
.is_open()) {
350 knucklesBindingFile
<< knucklesBinding
.binding
;
351 knucklesBindingFile
.close();
354 if (vrParent
->GetOpenVRControllerManifestPath(
355 VRControllerType::HTCViveCosmos
, &output
)) {
356 cosmosManifest
= output
;
358 if (!cosmosManifest
.Length() || !FileIsExisting(cosmosManifest
)) {
359 if (!GenerateTempFileName(cosmosManifest
)) {
362 OpenVRCosmosBinding cosmosBinding
;
363 std::ofstream
cosmosBindingFile(cosmosManifest
.BeginReading());
364 if (cosmosBindingFile
.is_open()) {
365 cosmosBindingFile
<< cosmosBinding
.binding
;
366 cosmosBindingFile
.close();
370 // Without using VR process
371 if (!sControllerActionFile
) {
372 sControllerActionFile
= ControllerManifestFile::CreateManifest();
373 NS_DispatchToMainThread(NS_NewRunnableFunction(
374 "ClearOnShutdown ControllerManifestFile",
375 []() { ClearOnShutdown(&sControllerActionFile
); }));
377 controllerAction
= sControllerActionFile
->GetFileName();
379 if (!sViveBindingFile
) {
380 sViveBindingFile
= ControllerManifestFile::CreateManifest();
381 NS_DispatchToMainThread(
382 NS_NewRunnableFunction("ClearOnShutdown ControllerManifestFile",
383 []() { ClearOnShutdown(&sViveBindingFile
); }));
385 if (!sViveBindingFile
->IsExisting()) {
386 nsCString viveBindingPath
;
387 if (!GenerateTempFileName(viveBindingPath
)) {
390 sViveBindingFile
->SetFileName(viveBindingPath
.BeginReading());
391 OpenVRViveBinding viveBinding
;
392 std::ofstream
viveBindingFile(sViveBindingFile
->GetFileName());
393 if (viveBindingFile
.is_open()) {
394 viveBindingFile
<< viveBinding
.binding
;
395 viveBindingFile
.close();
398 viveManifest
= sViveBindingFile
->GetFileName();
400 if (!sKnucklesBindingFile
) {
401 sKnucklesBindingFile
= ControllerManifestFile::CreateManifest();
402 NS_DispatchToMainThread(NS_NewRunnableFunction(
403 "ClearOnShutdown ControllerManifestFile",
404 []() { ClearOnShutdown(&sKnucklesBindingFile
); }));
406 if (!sKnucklesBindingFile
->IsExisting()) {
407 nsCString knucklesBindingPath
;
408 if (!GenerateTempFileName(knucklesBindingPath
)) {
411 sKnucklesBindingFile
->SetFileName(knucklesBindingPath
.BeginReading());
412 OpenVRKnucklesBinding knucklesBinding
;
413 std::ofstream
knucklesBindingFile(sKnucklesBindingFile
->GetFileName());
414 if (knucklesBindingFile
.is_open()) {
415 knucklesBindingFile
<< knucklesBinding
.binding
;
416 knucklesBindingFile
.close();
419 knucklesManifest
= sKnucklesBindingFile
->GetFileName();
421 if (!sCosmosBindingFile
) {
422 sCosmosBindingFile
= ControllerManifestFile::CreateManifest();
423 NS_DispatchToMainThread(NS_NewRunnableFunction(
424 "ClearOnShutdown ControllerManifestFile",
425 []() { ClearOnShutdown(&sCosmosBindingFile
); }));
427 if (!sCosmosBindingFile
->IsExisting()) {
428 nsCString cosmosBindingPath
;
429 if (!GenerateTempFileName(cosmosBindingPath
)) {
432 sCosmosBindingFile
->SetFileName(cosmosBindingPath
.BeginReading());
433 OpenVRCosmosBinding cosmosBinding
;
434 std::ofstream
cosmosBindingFile(sCosmosBindingFile
->GetFileName());
435 if (cosmosBindingFile
.is_open()) {
436 cosmosBindingFile
<< cosmosBinding
.binding
;
437 cosmosBindingFile
.close();
440 cosmosManifest
= sCosmosBindingFile
->GetFileName();
442 if (!sWMRBindingFile
) {
443 sWMRBindingFile
= ControllerManifestFile::CreateManifest();
444 NS_DispatchToMainThread(
445 NS_NewRunnableFunction("ClearOnShutdown ControllerManifestFile",
446 []() { ClearOnShutdown(&sWMRBindingFile
); }));
448 if (!sWMRBindingFile
->IsExisting()) {
449 nsCString WMRBindingPath
;
450 if (!GenerateTempFileName(WMRBindingPath
)) {
453 sWMRBindingFile
->SetFileName(WMRBindingPath
.BeginReading());
454 OpenVRWMRBinding WMRBinding
;
455 std::ofstream
WMRBindingFile(sWMRBindingFile
->GetFileName());
456 if (WMRBindingFile
.is_open()) {
457 WMRBindingFile
<< WMRBinding
.binding
;
458 WMRBindingFile
.close();
461 WMRManifest
= sWMRBindingFile
->GetFileName();
464 // End of Getting / Generating manifest file paths.
466 // Setup controller actions.
467 ControllerInfo leftContollerInfo
;
468 leftContollerInfo
.mActionPose
= CreateControllerAction(L
, pose
, pose
);
469 leftContollerInfo
.mActionTrackpad_Analog
=
470 CreateControllerAction(L
, trackpad_analog
, vector2
);
471 leftContollerInfo
.mActionTrackpad_Pressed
=
472 CreateControllerAction(L
, trackpad_pressed
, boolean
);
473 leftContollerInfo
.mActionTrackpad_Touched
=
474 CreateControllerAction(L
, trackpad_touched
, boolean
);
475 leftContollerInfo
.mActionTrigger_Value
=
476 CreateControllerAction(L
, trigger_value
, vector1
);
477 leftContollerInfo
.mActionGrip_Pressed
=
478 CreateControllerAction(L
, grip_pressed
, boolean
);
479 leftContollerInfo
.mActionGrip_Touched
=
480 CreateControllerAction(L
, grip_touched
, boolean
);
481 leftContollerInfo
.mActionMenu_Pressed
=
482 CreateControllerAction(L
, menu_pressed
, boolean
);
483 leftContollerInfo
.mActionMenu_Touched
=
484 CreateControllerAction(L
, menu_touched
, boolean
);
485 leftContollerInfo
.mActionSystem_Pressed
=
486 CreateControllerAction(L
, system_pressed
, boolean
);
487 leftContollerInfo
.mActionSystem_Touched
=
488 CreateControllerAction(L
, system_touched
, boolean
);
489 leftContollerInfo
.mActionA_Pressed
=
490 CreateControllerAction(L
, A_pressed
, boolean
);
491 leftContollerInfo
.mActionA_Touched
=
492 CreateControllerAction(L
, A_touched
, boolean
);
493 leftContollerInfo
.mActionB_Pressed
=
494 CreateControllerAction(L
, B_pressed
, boolean
);
495 leftContollerInfo
.mActionB_Touched
=
496 CreateControllerAction(L
, B_touched
, boolean
);
497 leftContollerInfo
.mActionThumbstick_Analog
=
498 CreateControllerAction(L
, thumbstick_analog
, vector2
);
499 leftContollerInfo
.mActionThumbstick_Pressed
=
500 CreateControllerAction(L
, thumbstick_pressed
, boolean
);
501 leftContollerInfo
.mActionThumbstick_Touched
=
502 CreateControllerAction(L
, thumbstick_touched
, boolean
);
503 leftContollerInfo
.mActionFingerIndex_Value
=
504 CreateControllerAction(L
, finger_index_value
, vector1
);
505 leftContollerInfo
.mActionFingerMiddle_Value
=
506 CreateControllerAction(L
, finger_middle_value
, vector1
);
507 leftContollerInfo
.mActionFingerRing_Value
=
508 CreateControllerAction(L
, finger_ring_value
, vector1
);
509 leftContollerInfo
.mActionFingerPinky_Value
=
510 CreateControllerAction(L
, finger_pinky_value
, vector1
);
511 leftContollerInfo
.mActionBumper_Pressed
=
512 CreateControllerAction(L
, bumper_pressed
, boolean
);
513 leftContollerInfo
.mActionHaptic
=
514 CreateControllerOutAction(L
, haptic
, vibration
);
516 ControllerInfo rightContollerInfo
;
517 rightContollerInfo
.mActionPose
= CreateControllerAction(R
, pose
, pose
);
518 rightContollerInfo
.mActionTrackpad_Analog
=
519 CreateControllerAction(R
, trackpad_analog
, vector2
);
520 rightContollerInfo
.mActionTrackpad_Pressed
=
521 CreateControllerAction(R
, trackpad_pressed
, boolean
);
522 rightContollerInfo
.mActionTrackpad_Touched
=
523 CreateControllerAction(R
, trackpad_touched
, boolean
);
524 rightContollerInfo
.mActionTrigger_Value
=
525 CreateControllerAction(R
, trigger_value
, vector1
);
526 rightContollerInfo
.mActionGrip_Pressed
=
527 CreateControllerAction(R
, grip_pressed
, boolean
);
528 rightContollerInfo
.mActionGrip_Touched
=
529 CreateControllerAction(R
, grip_touched
, boolean
);
530 rightContollerInfo
.mActionMenu_Pressed
=
531 CreateControllerAction(R
, menu_pressed
, boolean
);
532 rightContollerInfo
.mActionMenu_Touched
=
533 CreateControllerAction(R
, menu_touched
, boolean
);
534 rightContollerInfo
.mActionSystem_Pressed
=
535 CreateControllerAction(R
, system_pressed
, boolean
);
536 rightContollerInfo
.mActionSystem_Touched
=
537 CreateControllerAction(R
, system_touched
, boolean
);
538 rightContollerInfo
.mActionA_Pressed
=
539 CreateControllerAction(R
, A_pressed
, boolean
);
540 rightContollerInfo
.mActionA_Touched
=
541 CreateControllerAction(R
, A_touched
, boolean
);
542 rightContollerInfo
.mActionB_Pressed
=
543 CreateControllerAction(R
, B_pressed
, boolean
);
544 rightContollerInfo
.mActionB_Touched
=
545 CreateControllerAction(R
, B_touched
, boolean
);
546 rightContollerInfo
.mActionThumbstick_Analog
=
547 CreateControllerAction(R
, thumbstick_analog
, vector2
);
548 rightContollerInfo
.mActionThumbstick_Pressed
=
549 CreateControllerAction(R
, thumbstick_pressed
, boolean
);
550 rightContollerInfo
.mActionThumbstick_Touched
=
551 CreateControllerAction(R
, thumbstick_touched
, boolean
);
552 rightContollerInfo
.mActionFingerIndex_Value
=
553 CreateControllerAction(R
, finger_index_value
, vector1
);
554 rightContollerInfo
.mActionFingerMiddle_Value
=
555 CreateControllerAction(R
, finger_middle_value
, vector1
);
556 rightContollerInfo
.mActionFingerRing_Value
=
557 CreateControllerAction(R
, finger_ring_value
, vector1
);
558 rightContollerInfo
.mActionFingerPinky_Value
=
559 CreateControllerAction(R
, finger_pinky_value
, vector1
);
560 rightContollerInfo
.mActionBumper_Pressed
=
561 CreateControllerAction(R
, bumper_pressed
, boolean
);
562 rightContollerInfo
.mActionHaptic
=
563 CreateControllerOutAction(R
, haptic
, vibration
);
565 mControllerHand
[OpenVRHand::Left
] = leftContollerInfo
;
566 mControllerHand
[OpenVRHand::Right
] = rightContollerInfo
;
568 if (!controllerAction
.Length() || !FileIsExisting(controllerAction
)) {
569 if (!GenerateTempFileName(controllerAction
)) {
572 JSONStringWriteFunc
<nsCString
> actionData
;
573 JSONWriter
actionWriter(actionData
);
574 actionWriter
.Start();
576 actionWriter
.StringProperty("version",
577 "0.1.0"); // TODO: adding a version check.
578 // "default_bindings": []
579 actionWriter
.StartArrayProperty("default_bindings");
581 auto SetupActionWriterByControllerType
= [&](const char* aType
,
582 const nsCString
& aManifest
) {
583 actionWriter
.StartObjectElement();
584 actionWriter
.StringProperty("controller_type", MakeStringSpan(aType
));
585 actionWriter
.StringProperty("binding_url", aManifest
);
586 actionWriter
.EndObject();
588 SetupActionWriterByControllerType("vive_controller", viveManifest
);
589 SetupActionWriterByControllerType("knuckles", knucklesManifest
);
590 SetupActionWriterByControllerType("vive_cosmos_controller", cosmosManifest
);
592 SetupActionWriterByControllerType("holographic_controller", WMRManifest
);
594 actionWriter
.EndArray(); // End "default_bindings": []
596 actionWriter
.StartArrayProperty("actions");
598 for (auto& controller
: mControllerHand
) {
599 auto SetActionsToWriter
= [&](const ControllerAction
& aAction
) {
600 actionWriter
.StartObjectElement();
601 actionWriter
.StringProperty("name", aAction
.name
);
602 actionWriter
.StringProperty("type", aAction
.type
);
603 actionWriter
.EndObject();
606 SetActionsToWriter(controller
.mActionPose
);
607 SetActionsToWriter(controller
.mActionTrackpad_Analog
);
608 SetActionsToWriter(controller
.mActionTrackpad_Pressed
);
609 SetActionsToWriter(controller
.mActionTrackpad_Touched
);
610 SetActionsToWriter(controller
.mActionTrigger_Value
);
611 SetActionsToWriter(controller
.mActionGrip_Pressed
);
612 SetActionsToWriter(controller
.mActionGrip_Touched
);
613 SetActionsToWriter(controller
.mActionMenu_Pressed
);
614 SetActionsToWriter(controller
.mActionMenu_Touched
);
615 SetActionsToWriter(controller
.mActionSystem_Pressed
);
616 SetActionsToWriter(controller
.mActionSystem_Touched
);
617 SetActionsToWriter(controller
.mActionA_Pressed
);
618 SetActionsToWriter(controller
.mActionA_Touched
);
619 SetActionsToWriter(controller
.mActionB_Pressed
);
620 SetActionsToWriter(controller
.mActionB_Touched
);
621 SetActionsToWriter(controller
.mActionThumbstick_Analog
);
622 SetActionsToWriter(controller
.mActionThumbstick_Pressed
);
623 SetActionsToWriter(controller
.mActionThumbstick_Touched
);
624 SetActionsToWriter(controller
.mActionFingerIndex_Value
);
625 SetActionsToWriter(controller
.mActionFingerMiddle_Value
);
626 SetActionsToWriter(controller
.mActionFingerRing_Value
);
627 SetActionsToWriter(controller
.mActionFingerPinky_Value
);
628 SetActionsToWriter(controller
.mActionBumper_Pressed
);
629 SetActionsToWriter(controller
.mActionHaptic
);
631 actionWriter
.EndArray(); // End "actions": []
634 std::ofstream
actionfile(controllerAction
.BeginReading());
635 if (actionfile
.is_open()) {
636 actionfile
<< actionData
.StringCRef().get();
641 vr::EVRInputError err
=
642 vr::VRInput()->SetActionManifestPath(controllerAction
.BeginReading());
643 if (err
!= vr::VRInputError_None
) {
644 NS_WARNING("OpenVR - SetActionManifestPath failed.");
647 // End of setup controller actions.
649 // Notify the parent process these manifest files are already been recorded.
650 if (StaticPrefs::dom_vr_process_enabled_AtStartup()) {
651 NS_DispatchToMainThread(NS_NewRunnableFunction(
652 "SendOpenVRControllerActionPathToParent",
653 [controllerAction
, viveManifest
, WMRManifest
, knucklesManifest
,
655 VRParent
* vrParent
= VRProcessChild::GetVRParent();
656 Unused
<< vrParent
->SendOpenVRControllerActionPathToParent(
658 Unused
<< vrParent
->SendOpenVRControllerManifestPathToParent(
659 VRControllerType::HTCVive
, viveManifest
);
660 Unused
<< vrParent
->SendOpenVRControllerManifestPathToParent(
661 VRControllerType::MSMR
, WMRManifest
);
662 Unused
<< vrParent
->SendOpenVRControllerManifestPathToParent(
663 VRControllerType::ValveIndex
, knucklesManifest
);
664 Unused
<< vrParent
->SendOpenVRControllerManifestPathToParent(
665 VRControllerType::HTCViveCosmos
, cosmosManifest
);
668 sControllerActionFile
->SetFileName(controllerAction
.BeginReading());
675 bool OpenVRSession::CreateD3DObjects() {
676 RefPtr
<ID3D11Device
> device
= gfx::DeviceManagerDx::Get()->GetVRDevice();
680 if (!CreateD3DContext(device
)) {
687 void OpenVRSession::Shutdown() {
690 if (mVRSystem
|| mVRCompositor
|| mVRChaperone
) {
692 mVRCompositor
= nullptr;
693 mVRChaperone
= nullptr;
698 bool OpenVRSession::InitState(VRSystemState
& aSystemState
) {
699 VRDisplayState
& state
= aSystemState
.displayState
;
700 strncpy(state
.displayName
, "OpenVR HMD", kVRDisplayNameMaxLen
);
701 state
.eightCC
= GFX_VR_EIGHTCC('O', 'p', 'e', 'n', 'V', 'R', ' ', ' ');
703 mVRSystem
->IsTrackedDeviceConnected(::vr::k_unTrackedDeviceIndex_Hmd
);
704 state
.isMounted
= false;
705 state
.capabilityFlags
=
706 (VRDisplayCapabilityFlags
)((int)VRDisplayCapabilityFlags::Cap_None
|
708 VRDisplayCapabilityFlags::Cap_Orientation
|
709 (int)VRDisplayCapabilityFlags::Cap_Position
|
710 (int)VRDisplayCapabilityFlags::Cap_External
|
711 (int)VRDisplayCapabilityFlags::Cap_Present
|
712 (int)VRDisplayCapabilityFlags::
713 Cap_StageParameters
|
715 VRDisplayCapabilityFlags::Cap_ImmersiveVR
);
716 state
.blendMode
= VRDisplayBlendMode::Opaque
;
717 state
.reportsDroppedFrames
= true;
719 ::vr::ETrackedPropertyError err
;
720 bool bHasProximitySensor
= mVRSystem
->GetBoolTrackedDeviceProperty(
721 ::vr::k_unTrackedDeviceIndex_Hmd
, ::vr::Prop_ContainsProximitySensor_Bool
,
723 if (err
== ::vr::TrackedProp_Success
&& bHasProximitySensor
) {
724 state
.capabilityFlags
=
725 (VRDisplayCapabilityFlags
)((int)state
.capabilityFlags
|
726 (int)VRDisplayCapabilityFlags::
731 mVRSystem
->GetRecommendedRenderTargetSize(&w
, &h
);
732 state
.eyeResolution
.width
= w
;
733 state
.eyeResolution
.height
= h
;
734 state
.nativeFramebufferScaleFactor
= 1.0f
;
736 // default to an identity quaternion
737 aSystemState
.sensorState
.pose
.orientation
[3] = 1.0f
;
739 UpdateStageParameters(state
);
740 UpdateEyeParameters(aSystemState
);
742 VRHMDSensorState
& sensorState
= aSystemState
.sensorState
;
744 (VRDisplayCapabilityFlags
)((int)
745 VRDisplayCapabilityFlags::Cap_Orientation
|
746 (int)VRDisplayCapabilityFlags::Cap_Position
);
747 sensorState
.pose
.orientation
[3] = 1.0f
; // Default to an identity quaternion
752 void OpenVRSession::UpdateStageParameters(VRDisplayState
& aState
) {
755 if (mVRChaperone
->GetPlayAreaSize(&sizeX
, &sizeZ
)) {
756 ::vr::HmdMatrix34_t t
=
757 mVRSystem
->GetSeatedZeroPoseToStandingAbsoluteTrackingPose();
758 aState
.stageSize
.width
= sizeX
;
759 aState
.stageSize
.height
= sizeZ
;
761 aState
.sittingToStandingTransform
[0] = t
.m
[0][0];
762 aState
.sittingToStandingTransform
[1] = t
.m
[1][0];
763 aState
.sittingToStandingTransform
[2] = t
.m
[2][0];
764 aState
.sittingToStandingTransform
[3] = 0.0f
;
766 aState
.sittingToStandingTransform
[4] = t
.m
[0][1];
767 aState
.sittingToStandingTransform
[5] = t
.m
[1][1];
768 aState
.sittingToStandingTransform
[6] = t
.m
[2][1];
769 aState
.sittingToStandingTransform
[7] = 0.0f
;
771 aState
.sittingToStandingTransform
[8] = t
.m
[0][2];
772 aState
.sittingToStandingTransform
[9] = t
.m
[1][2];
773 aState
.sittingToStandingTransform
[10] = t
.m
[2][2];
774 aState
.sittingToStandingTransform
[11] = 0.0f
;
776 aState
.sittingToStandingTransform
[12] = t
.m
[0][3];
777 aState
.sittingToStandingTransform
[13] = t
.m
[1][3];
778 aState
.sittingToStandingTransform
[14] = t
.m
[2][3];
779 aState
.sittingToStandingTransform
[15] = 1.0f
;
781 // If we fail, fall back to reasonable defaults.
782 // 1m x 1m space, 0.75m high in seated position
783 aState
.stageSize
.width
= 1.0f
;
784 aState
.stageSize
.height
= 1.0f
;
786 aState
.sittingToStandingTransform
[0] = 1.0f
;
787 aState
.sittingToStandingTransform
[1] = 0.0f
;
788 aState
.sittingToStandingTransform
[2] = 0.0f
;
789 aState
.sittingToStandingTransform
[3] = 0.0f
;
791 aState
.sittingToStandingTransform
[4] = 0.0f
;
792 aState
.sittingToStandingTransform
[5] = 1.0f
;
793 aState
.sittingToStandingTransform
[6] = 0.0f
;
794 aState
.sittingToStandingTransform
[7] = 0.0f
;
796 aState
.sittingToStandingTransform
[8] = 0.0f
;
797 aState
.sittingToStandingTransform
[9] = 0.0f
;
798 aState
.sittingToStandingTransform
[10] = 1.0f
;
799 aState
.sittingToStandingTransform
[11] = 0.0f
;
801 aState
.sittingToStandingTransform
[12] = 0.0f
;
802 aState
.sittingToStandingTransform
[13] = 0.75f
;
803 aState
.sittingToStandingTransform
[14] = 0.0f
;
804 aState
.sittingToStandingTransform
[15] = 1.0f
;
808 void OpenVRSession::UpdateEyeParameters(VRSystemState
& aState
) {
809 // This must be called every frame in order to
810 // account for continuous adjustments to ipd.
811 gfx::Matrix4x4 headToEyeTransforms
[2];
813 for (uint32_t eye
= 0; eye
< 2; ++eye
) {
814 ::vr::HmdMatrix34_t eyeToHead
=
815 mVRSystem
->GetEyeToHeadTransform(static_cast<::vr::Hmd_Eye
>(eye
));
816 aState
.displayState
.eyeTranslation
[eye
].x
= eyeToHead
.m
[0][3];
817 aState
.displayState
.eyeTranslation
[eye
].y
= eyeToHead
.m
[1][3];
818 aState
.displayState
.eyeTranslation
[eye
].z
= eyeToHead
.m
[2][3];
820 float left
, right
, up
, down
;
821 mVRSystem
->GetProjectionRaw(static_cast<::vr::Hmd_Eye
>(eye
), &left
, &right
,
823 aState
.displayState
.eyeFOV
[eye
].upDegrees
= atan(-up
) * 180.0 / M_PI
;
824 aState
.displayState
.eyeFOV
[eye
].rightDegrees
= atan(right
) * 180.0 / M_PI
;
825 aState
.displayState
.eyeFOV
[eye
].downDegrees
= atan(down
) * 180.0 / M_PI
;
826 aState
.displayState
.eyeFOV
[eye
].leftDegrees
= atan(-left
) * 180.0 / M_PI
;
829 // NOTE! eyeToHead.m is a 3x4 matrix, not 4x4. But
830 // because of its arrangement, we can copy the 12 elements in and
831 // then transpose them to the right place.
832 memcpy(&pose
._11
, &eyeToHead
.m
, sizeof(eyeToHead
.m
));
835 headToEyeTransforms
[eye
] = pose
;
837 aState
.sensorState
.CalcViewMatrices(headToEyeTransforms
);
840 void OpenVRSession::UpdateHeadsetPose(VRSystemState
& aState
) {
841 const uint32_t posesSize
= ::vr::k_unTrackedDeviceIndex_Hmd
+ 1;
842 ::vr::TrackedDevicePose_t poses
[posesSize
];
843 // Note: We *must* call WaitGetPoses in order for any rendering to happen at
845 mVRCompositor
->WaitGetPoses(poses
, posesSize
, nullptr, 0);
847 ::vr::Compositor_FrameTiming timing
;
848 timing
.m_nSize
= sizeof(::vr::Compositor_FrameTiming
);
849 if (mVRCompositor
->GetFrameTiming(&timing
)) {
850 aState
.sensorState
.timestamp
= timing
.m_flSystemTimeInSeconds
;
852 // This should not happen, but log it just in case
853 fprintf(stderr
, "OpenVR - IVRCompositor::GetFrameTiming failed");
856 if (poses
[::vr::k_unTrackedDeviceIndex_Hmd
].bDeviceIsConnected
&&
857 poses
[::vr::k_unTrackedDeviceIndex_Hmd
].bPoseIsValid
&&
858 poses
[::vr::k_unTrackedDeviceIndex_Hmd
].eTrackingResult
==
859 ::vr::TrackingResult_Running_OK
) {
860 const ::vr::TrackedDevicePose_t
& pose
=
861 poses
[::vr::k_unTrackedDeviceIndex_Hmd
];
864 // NOTE! mDeviceToAbsoluteTracking is a 3x4 matrix, not 4x4. But
865 // because of its arrangement, we can copy the 12 elements in and
866 // then transpose them to the right place. We do this so we can
867 // pull out a Quaternion.
868 memcpy(&m
._11
, &pose
.mDeviceToAbsoluteTracking
,
869 sizeof(pose
.mDeviceToAbsoluteTracking
));
873 rot
.SetFromRotationMatrix(m
);
875 aState
.sensorState
.flags
=
876 (VRDisplayCapabilityFlags
)((int)aState
.sensorState
.flags
|
877 (int)VRDisplayCapabilityFlags::
879 aState
.sensorState
.pose
.orientation
[0] = rot
.x
;
880 aState
.sensorState
.pose
.orientation
[1] = rot
.y
;
881 aState
.sensorState
.pose
.orientation
[2] = rot
.z
;
882 aState
.sensorState
.pose
.orientation
[3] = rot
.w
;
883 aState
.sensorState
.pose
.angularVelocity
[0] = pose
.vAngularVelocity
.v
[0];
884 aState
.sensorState
.pose
.angularVelocity
[1] = pose
.vAngularVelocity
.v
[1];
885 aState
.sensorState
.pose
.angularVelocity
[2] = pose
.vAngularVelocity
.v
[2];
887 aState
.sensorState
.flags
=
888 (VRDisplayCapabilityFlags
)((int)aState
.sensorState
.flags
|
889 (int)VRDisplayCapabilityFlags::Cap_Position
);
890 aState
.sensorState
.pose
.position
[0] = m
._41
;
891 aState
.sensorState
.pose
.position
[1] = m
._42
;
892 aState
.sensorState
.pose
.position
[2] = m
._43
;
893 aState
.sensorState
.pose
.linearVelocity
[0] = pose
.vVelocity
.v
[0];
894 aState
.sensorState
.pose
.linearVelocity
[1] = pose
.vVelocity
.v
[1];
895 aState
.sensorState
.pose
.linearVelocity
[2] = pose
.vVelocity
.v
[2];
899 void OpenVRSession::EnumerateControllers(VRSystemState
& aState
) {
900 MOZ_ASSERT(mVRSystem
);
902 MutexAutoLock
lock(mControllerHapticStateMutex
);
904 bool controllerPresent
[kVRControllerMaxCount
] = {false};
905 uint32_t stateIndex
= 0;
906 mActionsetFirefox
= vr::k_ulInvalidActionSetHandle
;
907 VRControllerType controllerType
= VRControllerType::_empty
;
909 if (vr::VRInput()->GetActionSetHandle(
910 "/actions/firefox", &mActionsetFirefox
) != vr::VRInputError_None
) {
914 for (int8_t handIndex
= 0; handIndex
< OpenVRHand::Total
; ++handIndex
) {
915 if (handIndex
== OpenVRHand::Left
) {
916 if (vr::VRInput()->GetInputSourceHandle(
917 "/user/hand/left", &mControllerHand
[OpenVRHand::Left
].mSource
) !=
918 vr::VRInputError_None
) {
921 } else if (handIndex
== OpenVRHand::Right
) {
922 if (vr::VRInput()->GetInputSourceHandle(
924 &mControllerHand
[OpenVRHand::Right
].mSource
) !=
925 vr::VRInputError_None
) {
929 MOZ_ASSERT(false, "Unknown OpenVR hand type.");
932 vr::InputOriginInfo_t originInfo
;
933 if (vr::VRInput()->GetOriginTrackedDeviceInfo(
934 mControllerHand
[handIndex
].mSource
, &originInfo
,
935 sizeof(originInfo
)) == vr::VRInputError_None
&&
936 originInfo
.trackedDeviceIndex
!= vr::k_unTrackedDeviceIndexInvalid
&&
937 mVRSystem
->IsTrackedDeviceConnected(originInfo
.trackedDeviceIndex
)) {
938 const ::vr::ETrackedDeviceClass deviceType
=
939 mVRSystem
->GetTrackedDeviceClass(originInfo
.trackedDeviceIndex
);
940 if (deviceType
!= ::vr::TrackedDeviceClass_Controller
&&
941 deviceType
!= ::vr::TrackedDeviceClass_GenericTracker
) {
945 if (mControllerDeviceIndex
[stateIndex
] != handIndex
) {
946 VRControllerState
& controllerState
= aState
.controllerState
[stateIndex
];
948 // Get controllers' action handles.
949 auto SetActionsToWriter
= [&](ControllerAction
& aAction
) {
950 vr::VRInput()->GetActionHandle(aAction
.name
.BeginReading(),
954 SetActionsToWriter(mControllerHand
[handIndex
].mActionPose
);
955 SetActionsToWriter(mControllerHand
[handIndex
].mActionHaptic
);
956 SetActionsToWriter(mControllerHand
[handIndex
].mActionTrackpad_Analog
);
957 SetActionsToWriter(mControllerHand
[handIndex
].mActionTrackpad_Pressed
);
958 SetActionsToWriter(mControllerHand
[handIndex
].mActionTrackpad_Touched
);
959 SetActionsToWriter(mControllerHand
[handIndex
].mActionTrigger_Value
);
960 SetActionsToWriter(mControllerHand
[handIndex
].mActionGrip_Pressed
);
961 SetActionsToWriter(mControllerHand
[handIndex
].mActionGrip_Touched
);
962 SetActionsToWriter(mControllerHand
[handIndex
].mActionMenu_Pressed
);
963 SetActionsToWriter(mControllerHand
[handIndex
].mActionMenu_Touched
);
964 SetActionsToWriter(mControllerHand
[handIndex
].mActionSystem_Pressed
);
965 SetActionsToWriter(mControllerHand
[handIndex
].mActionSystem_Touched
);
966 SetActionsToWriter(mControllerHand
[handIndex
].mActionA_Pressed
);
967 SetActionsToWriter(mControllerHand
[handIndex
].mActionA_Touched
);
968 SetActionsToWriter(mControllerHand
[handIndex
].mActionB_Pressed
);
969 SetActionsToWriter(mControllerHand
[handIndex
].mActionB_Touched
);
970 SetActionsToWriter(mControllerHand
[handIndex
].mActionThumbstick_Analog
);
972 mControllerHand
[handIndex
].mActionThumbstick_Pressed
);
974 mControllerHand
[handIndex
].mActionThumbstick_Touched
);
975 SetActionsToWriter(mControllerHand
[handIndex
].mActionFingerIndex_Value
);
977 mControllerHand
[handIndex
].mActionFingerMiddle_Value
);
978 SetActionsToWriter(mControllerHand
[handIndex
].mActionFingerRing_Value
);
979 SetActionsToWriter(mControllerHand
[handIndex
].mActionFingerPinky_Value
);
980 SetActionsToWriter(mControllerHand
[handIndex
].mActionBumper_Pressed
);
983 VRControllerType contrlType
= VRControllerType::_empty
;
984 GetControllerDeviceId(deviceType
, originInfo
.trackedDeviceIndex
,
985 deviceId
, contrlType
);
986 // Controllers should be the same type with one VR display.
987 MOZ_ASSERT(controllerType
== contrlType
||
988 controllerType
== VRControllerType::_empty
);
989 controllerType
= contrlType
;
990 strncpy(controllerState
.controllerName
, deviceId
.BeginReading(),
991 kVRControllerNameMaxLen
);
992 controllerState
.numHaptics
= kNumOpenVRHaptics
;
993 controllerState
.targetRayMode
= gfx::TargetRayMode::TrackedPointer
;
994 controllerState
.type
= controllerType
;
996 controllerPresent
[stateIndex
] = true;
997 mControllerDeviceIndex
[stateIndex
] = static_cast<OpenVRHand
>(handIndex
);
1002 // Clear out entries for disconnected controllers
1003 for (uint32_t stateIndex
= 0; stateIndex
< kVRControllerMaxCount
;
1005 if (!controllerPresent
[stateIndex
] &&
1006 mControllerDeviceIndex
[stateIndex
] != OpenVRHand::None
) {
1007 mControllerDeviceIndex
[stateIndex
] = OpenVRHand::None
;
1008 memset(&aState
.controllerState
[stateIndex
], 0, sizeof(VRControllerState
));
1012 // Create controller mapper
1013 if (controllerType
!= VRControllerType::_empty
) {
1014 switch (controllerType
) {
1015 case VRControllerType::HTCVive
:
1016 mControllerMapper
= MakeUnique
<OpenVRViveMapper
>();
1018 case VRControllerType::HTCViveCosmos
:
1019 mControllerMapper
= MakeUnique
<OpenVRCosmosMapper
>();
1022 case VRControllerType::MSMR
:
1023 mControllerMapper
= MakeUnique
<OpenVRWMRMapper
>();
1026 case VRControllerType::ValveIndex
:
1027 mControllerMapper
= MakeUnique
<OpenVRKnucklesMapper
>();
1030 mControllerMapper
= MakeUnique
<OpenVRDefaultMapper
>();
1031 NS_WARNING("Undefined controller type");
1037 void OpenVRSession::UpdateControllerButtons(VRSystemState
& aState
) {
1038 MOZ_ASSERT(mVRSystem
);
1040 for (uint32_t stateIndex
= 0; stateIndex
< kVRControllerMaxCount
;
1042 const OpenVRHand role
= mControllerDeviceIndex
[stateIndex
];
1043 if (role
== OpenVRHand::None
) {
1046 VRControllerState
& controllerState
= aState
.controllerState
[stateIndex
];
1047 controllerState
.hand
= GetControllerHandFromControllerRole(role
);
1048 mControllerMapper
->UpdateButtons(controllerState
, mControllerHand
[role
]);
1049 SetControllerSelectionAndSqueezeFrameId(
1050 controllerState
, aState
.displayState
.lastSubmittedFrameId
);
1054 void OpenVRSession::UpdateControllerPoses(VRSystemState
& aState
) {
1055 MOZ_ASSERT(mVRSystem
);
1057 for (uint32_t stateIndex
= 0; stateIndex
< kVRControllerMaxCount
;
1059 const OpenVRHand role
= mControllerDeviceIndex
[stateIndex
];
1060 if (role
== OpenVRHand::None
) {
1063 VRControllerState
& controllerState
= aState
.controllerState
[stateIndex
];
1064 vr::InputPoseActionData_t poseData
;
1065 if (vr::VRInput()->GetPoseActionDataRelativeToNow(
1066 mControllerHand
[role
].mActionPose
.handle
,
1067 vr::TrackingUniverseSeated
, 0, &poseData
, sizeof(poseData
),
1068 vr::k_ulInvalidInputValueHandle
) != vr::VRInputError_None
||
1069 !poseData
.bActive
|| !poseData
.pose
.bPoseIsValid
) {
1070 controllerState
.isOrientationValid
= false;
1071 controllerState
.isPositionValid
= false;
1073 const ::vr::TrackedDevicePose_t
& pose
= poseData
.pose
;
1074 if (pose
.bDeviceIsConnected
) {
1075 controllerState
.flags
=
1076 (dom::GamepadCapabilityFlags::Cap_Orientation
|
1077 dom::GamepadCapabilityFlags::Cap_Position
|
1078 dom::GamepadCapabilityFlags::Cap_GripSpacePosition
);
1080 controllerState
.flags
= dom::GamepadCapabilityFlags::Cap_None
;
1082 if (pose
.bPoseIsValid
&&
1083 pose
.eTrackingResult
== ::vr::TrackingResult_Running_OK
) {
1086 // NOTE! mDeviceToAbsoluteTracking is a 3x4 matrix, not 4x4. But
1087 // because of its arrangement, we can copy the 12 elements in and
1088 // then transpose them to the right place. We do this so we can
1089 // pull out a Quaternion.
1090 memcpy(&m
.components
, &pose
.mDeviceToAbsoluteTracking
,
1091 sizeof(pose
.mDeviceToAbsoluteTracking
));
1094 gfx::Quaternion rot
;
1095 rot
.SetFromRotationMatrix(m
);
1097 controllerState
.pose
.orientation
[0] = rot
.x
;
1098 controllerState
.pose
.orientation
[1] = rot
.y
;
1099 controllerState
.pose
.orientation
[2] = rot
.z
;
1100 controllerState
.pose
.orientation
[3] = rot
.w
;
1101 controllerState
.pose
.angularVelocity
[0] = pose
.vAngularVelocity
.v
[0];
1102 controllerState
.pose
.angularVelocity
[1] = pose
.vAngularVelocity
.v
[1];
1103 controllerState
.pose
.angularVelocity
[2] = pose
.vAngularVelocity
.v
[2];
1104 controllerState
.pose
.angularAcceleration
[0] = 0.0f
;
1105 controllerState
.pose
.angularAcceleration
[1] = 0.0f
;
1106 controllerState
.pose
.angularAcceleration
[2] = 0.0f
;
1107 controllerState
.isOrientationValid
= true;
1109 controllerState
.pose
.position
[0] = m
._41
;
1110 controllerState
.pose
.position
[1] = m
._42
;
1111 controllerState
.pose
.position
[2] = m
._43
;
1112 controllerState
.pose
.linearVelocity
[0] = pose
.vVelocity
.v
[0];
1113 controllerState
.pose
.linearVelocity
[1] = pose
.vVelocity
.v
[1];
1114 controllerState
.pose
.linearVelocity
[2] = pose
.vVelocity
.v
[2];
1115 controllerState
.pose
.linearAcceleration
[0] = 0.0f
;
1116 controllerState
.pose
.linearAcceleration
[1] = 0.0f
;
1117 controllerState
.pose
.linearAcceleration
[2] = 0.0f
;
1118 controllerState
.isPositionValid
= true;
1120 // Calculate its target ray space by shifting degrees in x-axis
1122 const float kPointerAngleDegrees
= -0.698; // 40 degrees.
1123 gfx::Matrix4x4
rayMtx(m
);
1124 rayMtx
.RotateX(kPointerAngleDegrees
);
1125 gfx::Quaternion rayRot
;
1126 rayRot
.SetFromRotationMatrix(rayMtx
);
1128 controllerState
.targetRayPose
= controllerState
.pose
;
1129 controllerState
.targetRayPose
.orientation
[0] = rayRot
.x
;
1130 controllerState
.targetRayPose
.orientation
[1] = rayRot
.y
;
1131 controllerState
.targetRayPose
.orientation
[2] = rayRot
.z
;
1132 controllerState
.targetRayPose
.orientation
[3] = rayRot
.w
;
1133 controllerState
.targetRayPose
.position
[0] = rayMtx
._41
;
1134 controllerState
.targetRayPose
.position
[1] = rayMtx
._42
;
1135 controllerState
.targetRayPose
.position
[2] = rayMtx
._43
;
1141 void OpenVRSession::GetControllerDeviceId(
1142 ::vr::ETrackedDeviceClass aDeviceType
,
1143 ::vr::TrackedDeviceIndex_t aDeviceIndex
, nsCString
& aId
,
1144 VRControllerType
& aControllerType
) {
1145 switch (aDeviceType
) {
1146 case ::vr::TrackedDeviceClass_Controller
: {
1147 ::vr::ETrackedPropertyError err
;
1148 uint32_t requiredBufferLen
;
1149 bool isFound
= false;
1151 requiredBufferLen
= mVRSystem
->GetStringTrackedDeviceProperty(
1152 aDeviceIndex
, ::vr::Prop_RenderModelName_String
, charBuf
, 128, &err
);
1153 if (requiredBufferLen
> 128) {
1154 MOZ_CRASH("Larger than the buffer size.");
1156 MOZ_ASSERT(requiredBufferLen
&& err
== ::vr::TrackedProp_Success
);
1157 nsCString
deviceId(charBuf
);
1158 if (deviceId
.Find("vr_controller_vive") != kNotFound
) {
1159 aId
.AssignLiteral("OpenVR Gamepad");
1161 aControllerType
= VRControllerType::HTCVive
;
1162 } else if (deviceId
.Find("knuckles") != kNotFound
||
1163 deviceId
.Find("valve_controller_knu") != kNotFound
) {
1164 aId
.AssignLiteral("OpenVR Knuckles");
1166 aControllerType
= VRControllerType::ValveIndex
;
1167 } else if (deviceId
.Find("vive_cosmos_controller") != kNotFound
) {
1168 aId
.AssignLiteral("OpenVR Cosmos");
1170 aControllerType
= VRControllerType::HTCViveCosmos
;
1173 requiredBufferLen
= mVRSystem
->GetStringTrackedDeviceProperty(
1174 aDeviceIndex
, ::vr::Prop_SerialNumber_String
, charBuf
, 128, &err
);
1175 if (requiredBufferLen
> 128) {
1176 MOZ_CRASH("Larger than the buffer size.");
1178 MOZ_ASSERT(requiredBufferLen
&& err
== ::vr::TrackedProp_Success
);
1179 deviceId
.Assign(charBuf
);
1180 if (deviceId
.Find("MRSOURCE") != kNotFound
) {
1181 aId
.AssignLiteral("Spatial Controller (Spatial Interaction Source) ");
1182 mIsWindowsMR
= true;
1184 aControllerType
= VRControllerType::MSMR
;
1188 aId
.AssignLiteral("OpenVR Undefined");
1189 aControllerType
= VRControllerType::_empty
;
1193 case ::vr::TrackedDeviceClass_GenericTracker
: {
1194 aId
.AssignLiteral("OpenVR Tracker");
1195 aControllerType
= VRControllerType::_empty
;
1204 void OpenVRSession::StartFrame(mozilla::gfx::VRSystemState
& aSystemState
) {
1205 UpdateHeadsetPose(aSystemState
);
1206 UpdateEyeParameters(aSystemState
);
1207 EnumerateControllers(aSystemState
);
1209 vr::VRActiveActionSet_t actionSet
= {0};
1210 actionSet
.ulActionSet
= mActionsetFirefox
;
1211 vr::VRInput()->UpdateActionState(&actionSet
, sizeof(actionSet
), 1);
1212 UpdateControllerButtons(aSystemState
);
1213 UpdateControllerPoses(aSystemState
);
1214 UpdateTelemetry(aSystemState
);
1217 void OpenVRSession::ProcessEvents(mozilla::gfx::VRSystemState
& aSystemState
) {
1218 bool isHmdPresent
= ::vr::VR_IsHmdPresent();
1219 if (!isHmdPresent
) {
1223 ::vr::VREvent_t event
;
1224 while (mVRSystem
&& mVRSystem
->PollNextEvent(&event
, sizeof(event
))) {
1225 switch (event
.eventType
) {
1226 case ::vr::VREvent_TrackedDeviceUserInteractionStarted
:
1227 if (event
.trackedDeviceIndex
== ::vr::k_unTrackedDeviceIndex_Hmd
) {
1228 aSystemState
.displayState
.isMounted
= true;
1231 case ::vr::VREvent_TrackedDeviceUserInteractionEnded
:
1232 if (event
.trackedDeviceIndex
== ::vr::k_unTrackedDeviceIndex_Hmd
) {
1233 aSystemState
.displayState
.isMounted
= false;
1236 case ::vr::EVREventType::VREvent_TrackedDeviceActivated
:
1237 if (event
.trackedDeviceIndex
== ::vr::k_unTrackedDeviceIndex_Hmd
) {
1238 aSystemState
.displayState
.isConnected
= true;
1241 case ::vr::EVREventType::VREvent_TrackedDeviceDeactivated
:
1242 if (event
.trackedDeviceIndex
== ::vr::k_unTrackedDeviceIndex_Hmd
) {
1243 aSystemState
.displayState
.isConnected
= false;
1246 case ::vr::EVREventType::VREvent_DriverRequestedQuit
:
1247 case ::vr::EVREventType::VREvent_Quit
:
1248 // When SteamVR runtime haven't been launched before viewing VR,
1249 // SteamVR will send a VREvent_ProcessQuit event. It will tell the parent
1250 // process to shutdown the VR process, and we need to avoid it.
1251 // case ::vr::EVREventType::VREvent_ProcessQuit:
1252 case ::vr::EVREventType::VREvent_QuitAcknowledged
:
1263 bool OpenVRSession::SubmitFrame(
1264 const mozilla::gfx::VRLayer_Stereo_Immersive
& aLayer
,
1265 ID3D11Texture2D
* aTexture
) {
1266 return SubmitFrame((void*)aTexture
, ::vr::ETextureType::TextureType_DirectX
,
1267 aLayer
.leftEyeRect
, aLayer
.rightEyeRect
);
1269 #elif defined(XP_MACOSX)
1270 bool OpenVRSession::SubmitFrame(
1271 const mozilla::gfx::VRLayer_Stereo_Immersive
& aLayer
,
1272 const VRLayerTextureHandle
& aTexture
) {
1273 return SubmitFrame(aTexture
, ::vr::ETextureType::TextureType_IOSurface
,
1274 aLayer
.leftEyeRect
, aLayer
.rightEyeRect
);
1278 bool OpenVRSession::SubmitFrame(const VRLayerTextureHandle
& aTextureHandle
,
1279 ::vr::ETextureType aTextureType
,
1280 const VRLayerEyeRect
& aLeftEyeRect
,
1281 const VRLayerEyeRect
& aRightEyeRect
) {
1282 ::vr::Texture_t tex
;
1283 #if defined(XP_MACOSX)
1284 // We get aTextureHandle from get_SurfaceDescriptorMacIOSurface() at
1285 // VRDisplayExternal. scaleFactor and opaque are skipped because they always
1286 // are 1.0 and false.
1287 RefPtr
<MacIOSurface
> surf
= MacIOSurface::LookupSurface(aTextureHandle
);
1289 NS_WARNING("OpenVRSession::SubmitFrame failed to get a MacIOSurface");
1293 CFTypeRefPtr
<IOSurfaceRef
> ioSurface
= surf
->GetIOSurfaceRef();
1294 tex
.handle
= (void*)ioSurface
.get();
1296 tex
.handle
= aTextureHandle
;
1298 tex
.eType
= aTextureType
;
1299 tex
.eColorSpace
= ::vr::EColorSpace::ColorSpace_Auto
;
1301 ::vr::VRTextureBounds_t bounds
;
1302 bounds
.uMin
= aLeftEyeRect
.x
;
1303 bounds
.vMin
= 1.0 - aLeftEyeRect
.y
;
1304 bounds
.uMax
= aLeftEyeRect
.x
+ aLeftEyeRect
.width
;
1305 bounds
.vMax
= 1.0 - (aLeftEyeRect
.y
+ aLeftEyeRect
.height
);
1307 ::vr::EVRCompositorError err
;
1308 err
= mVRCompositor
->Submit(::vr::EVREye::Eye_Left
, &tex
, &bounds
);
1309 if (err
!= ::vr::EVRCompositorError::VRCompositorError_None
) {
1310 printf_stderr("OpenVR Compositor Submit() failed.\n");
1313 bounds
.uMin
= aRightEyeRect
.x
;
1314 bounds
.vMin
= 1.0 - aRightEyeRect
.y
;
1315 bounds
.uMax
= aRightEyeRect
.x
+ aRightEyeRect
.width
;
1316 bounds
.vMax
= 1.0 - (aRightEyeRect
.y
+ aRightEyeRect
.height
);
1318 err
= mVRCompositor
->Submit(::vr::EVREye::Eye_Right
, &tex
, &bounds
);
1319 if (err
!= ::vr::EVRCompositorError::VRCompositorError_None
) {
1320 printf_stderr("OpenVR Compositor Submit() failed.\n");
1323 mVRCompositor
->PostPresentHandoff();
1327 void OpenVRSession::StopPresentation() {
1328 mVRCompositor
->ClearLastSubmittedFrame();
1330 ::vr::Compositor_CumulativeStats stats
;
1331 mVRCompositor
->GetCumulativeStats(&stats
,
1332 sizeof(::vr::Compositor_CumulativeStats
));
1335 bool OpenVRSession::StartPresentation() { return true; }
1337 void OpenVRSession::VibrateHaptic(uint32_t aControllerIdx
,
1338 uint32_t aHapticIndex
, float aIntensity
,
1340 MutexAutoLock
lock(mControllerHapticStateMutex
);
1342 // Initilize the haptic thread when the first time to do vibration.
1343 if (!mHapticThread
) {
1344 NS_DispatchToMainThread(NS_NewRunnableFunction(
1345 "OpenVRSession::StartHapticThread", [this]() { StartHapticThread(); }));
1347 if (aHapticIndex
>= kNumOpenVRHaptics
||
1348 aControllerIdx
>= kVRControllerMaxCount
) {
1352 const OpenVRHand role
= mControllerDeviceIndex
[aControllerIdx
];
1353 if (role
== OpenVRHand::None
) {
1356 mHapticPulseRemaining
[aControllerIdx
][aHapticIndex
] = aDuration
;
1357 mHapticPulseIntensity
[aControllerIdx
][aHapticIndex
] = aIntensity
;
1360 void OpenVRSession::StartHapticThread() {
1361 MOZ_ASSERT(NS_IsMainThread());
1362 if (!mHapticThread
) {
1363 mHapticThread
= new VRThread("VR_OpenVR_Haptics"_ns
);
1365 mHapticThread
->Start();
1369 void OpenVRSession::StopHapticThread() {
1370 if (mHapticThread
) {
1371 NS_DispatchToMainThread(NS_NewRunnableFunction(
1372 "mHapticThread::Shutdown",
1373 [thread
= mHapticThread
]() { thread
->Shutdown(); }));
1374 mHapticThread
= nullptr;
1378 void OpenVRSession::StartHapticTimer() {
1379 if (!mHapticTimer
&& mHapticThread
) {
1380 mLastHapticUpdate
= TimeStamp();
1381 mHapticTimer
= NS_NewTimer();
1382 nsCOMPtr
<nsIThread
> thread
= mHapticThread
->GetThread();
1383 mHapticTimer
->SetTarget(thread
);
1384 mHapticTimer
->InitWithNamedFuncCallback(
1385 HapticTimerCallback
, this, kVRHapticUpdateInterval
,
1386 nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP
,
1387 "OpenVRSession::HapticTimerCallback");
1391 void OpenVRSession::StopHapticTimer() {
1393 mHapticTimer
->Cancel();
1394 mHapticTimer
= nullptr;
1399 void OpenVRSession::HapticTimerCallback(nsITimer
* aTimer
, void* aClosure
) {
1401 * It is safe to use the pointer passed in aClosure to reference the
1402 * OpenVRSession object as the timer is canceled in OpenVRSession::Shutdown,
1403 * which is called by the OpenVRSession destructor, guaranteeing
1404 * that this function runs if and only if the VRManager object is valid.
1406 OpenVRSession
* self
= static_cast<OpenVRSession
*>(aClosure
);
1408 self
->UpdateHaptics();
1411 void OpenVRSession::UpdateHaptics() {
1412 MOZ_ASSERT(mHapticThread
->GetThread() == NS_GetCurrentThread());
1413 MOZ_ASSERT(mVRSystem
);
1415 MutexAutoLock
lock(mControllerHapticStateMutex
);
1417 TimeStamp now
= TimeStamp::Now();
1418 if (mLastHapticUpdate
.IsNull()) {
1419 mLastHapticUpdate
= now
;
1422 float deltaTime
= (float)(now
- mLastHapticUpdate
).ToSeconds();
1423 mLastHapticUpdate
= now
;
1425 for (uint32_t stateIndex
= 0; stateIndex
< kVRControllerMaxCount
;
1427 const OpenVRHand role
= mControllerDeviceIndex
[stateIndex
];
1428 if (role
== OpenVRHand::None
) {
1431 for (uint32_t hapticIdx
= 0; hapticIdx
< kNumOpenVRHaptics
; hapticIdx
++) {
1432 float intensity
= mHapticPulseIntensity
[stateIndex
][hapticIdx
];
1433 float duration
= mHapticPulseRemaining
[stateIndex
][hapticIdx
];
1434 if (duration
<= 0.0f
|| intensity
<= 0.0f
) {
1437 vr::VRInput()->TriggerHapticVibrationAction(
1438 mControllerHand
[role
].mActionHaptic
.handle
, 0.0f
, deltaTime
, 4.0f
,
1439 intensity
> 1.0f
? 1.0f
: intensity
, vr::k_ulInvalidInputValueHandle
);
1441 duration
-= deltaTime
;
1442 if (duration
< 0.0f
) {
1445 mHapticPulseRemaining
[stateIndex
][hapticIdx
] = duration
;
1450 void OpenVRSession::StopVibrateHaptic(uint32_t aControllerIdx
) {
1451 MutexAutoLock
lock(mControllerHapticStateMutex
);
1452 if (aControllerIdx
>= kVRControllerMaxCount
) {
1455 for (int iHaptic
= 0; iHaptic
< kNumOpenVRHaptics
; iHaptic
++) {
1456 mHapticPulseRemaining
[aControllerIdx
][iHaptic
] = 0.0f
;
1460 void OpenVRSession::StopAllHaptics() {
1461 MutexAutoLock
lock(mControllerHapticStateMutex
);
1462 for (auto& controller
: mHapticPulseRemaining
) {
1463 for (auto& haptic
: controller
) {
1469 void OpenVRSession::UpdateTelemetry(VRSystemState
& aSystemState
) {
1470 ::vr::Compositor_CumulativeStats stats
;
1471 mVRCompositor
->GetCumulativeStats(&stats
,
1472 sizeof(::vr::Compositor_CumulativeStats
));
1473 aSystemState
.displayState
.droppedFrameCount
= stats
.m_nNumReprojectedFrames
;
1476 } // namespace mozilla::gfx