OpenVRSession.cpp (56005B)
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/JSONStringWriteFuncs.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 class ControllerManifestFile { 65 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ControllerManifestFile) 66 67 public: 68 static already_AddRefed<ControllerManifestFile> CreateManifest() { 69 RefPtr<ControllerManifestFile> manifest = new ControllerManifestFile(); 70 return manifest.forget(); 71 } 72 73 bool IsExisting() { 74 if (mFileName.IsEmpty() || 75 !std::ifstream(mFileName.BeginReading()).good()) { 76 return false; 77 } 78 return true; 79 } 80 81 void SetFileName(const char* aName) { mFileName = aName; } 82 83 const char* GetFileName() const { return mFileName.BeginReading(); } 84 85 private: 86 ControllerManifestFile() = default; 87 88 ~ControllerManifestFile() { 89 if (!mFileName.IsEmpty() && remove(mFileName.BeginReading()) != 0) { 90 MOZ_ASSERT(false, "Delete controller manifest file failed."); 91 } 92 mFileName = ""; 93 } 94 95 nsCString mFileName; 96 }; 97 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; 103 #if defined(XP_WIN) 104 StaticRefPtr<ControllerManifestFile> sWMRBindingFile; 105 #endif 106 StaticRefPtr<ControllerManifestFile> sControllerActionFile; 107 108 dom::GamepadHand GetControllerHandFromControllerRole(OpenVRHand aRole) { 109 dom::GamepadHand hand; 110 switch (aRole) { 111 case OpenVRHand::None: 112 hand = dom::GamepadHand::_empty; 113 break; 114 case OpenVRHand::Left: 115 hand = dom::GamepadHand::Left; 116 break; 117 case OpenVRHand::Right: 118 hand = dom::GamepadHand::Right; 119 break; 120 default: 121 hand = dom::GamepadHand::_empty; 122 MOZ_ASSERT(false); 123 break; 124 } 125 126 return hand; 127 } 128 129 bool FileIsExisting(const nsCString& aPath) { 130 if (aPath.IsEmpty() || !std::ifstream(aPath.BeginReading()).good()) { 131 return false; 132 } 133 return true; 134 } 135 136 }; // anonymous namespace 137 138 #if defined(XP_WIN) 139 bool GenerateTempFileName(nsCString& aPath) { 140 TCHAR tempPathBuffer[MAX_PATH]; 141 TCHAR tempFileName[MAX_PATH]; 142 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."); 147 return false; 148 } 149 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 155 if (uRetVal == 0) { 156 NS_WARNING("OpenVR - Creating temp file failed."); 157 return false; 158 } 159 160 aPath.Assign(NS_ConvertUTF16toUTF8(tempFileName)); 161 return true; 162 } 163 #else 164 bool GenerateTempFileName(nsCString& aPath) { 165 const char tmp[] = "/tmp/mozvrXXXXXX"; 166 char fileName[PATH_MAX]; 167 168 strcpy(fileName, tmp); 169 const mode_t prevMask = umask(S_IXUSR | S_IRWXO | S_IRWXG); 170 const int fd = mkstemp(fileName); 171 umask(prevMask); 172 if (fd == -1) { 173 NS_WARNING(nsPrintfCString("OpenVR - Creating temp file failed: %s", 174 strerror(errno)) 175 .get()); 176 return false; 177 } 178 close(fd); 179 180 aPath.Assign(fileName); 181 return true; 182 } 183 #endif // defined(XP_WIN) 184 185 OpenVRSession::OpenVRSession() 186 : mVRSystem(nullptr), 187 mVRChaperone(nullptr), 188 mVRCompositor(nullptr), 189 mHapticPulseRemaining{}, 190 mHapticPulseIntensity{}, 191 mIsWindowsMR(false), 192 mControllerHapticStateMutex( 193 "OpenVRSession::mControllerHapticStateMutex") { 194 std::fill_n(mControllerDeviceIndex, kVRControllerMaxCount, OpenVRHand::None); 195 } 196 197 OpenVRSession::~OpenVRSession() { 198 mActionsetFirefox = ::vr::k_ulInvalidActionSetHandle; 199 Shutdown(); 200 } 201 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 206 return false; 207 } 208 if (!StaticPrefs::dom_vr_enabled() || !StaticPrefs::dom_vr_openvr_enabled()) { 209 return false; 210 } 211 if (mVRSystem != nullptr) { 212 // Already initialized 213 return true; 214 } 215 if (!::vr::VR_IsRuntimeInstalled()) { 216 return false; 217 } 218 if (aDetectRuntimesOnly) { 219 aSystemState.displayState.capabilityFlags |= 220 VRDisplayCapabilityFlags::Cap_ImmersiveVR; 221 return false; 222 } 223 if (!::vr::VR_IsHmdPresent()) { 224 return false; 225 } 226 227 ::vr::HmdError err; 228 229 ::vr::VR_Init(&err, ::vr::EVRApplicationType::VRApplication_Scene); 230 if (err) { 231 return false; 232 } 233 234 mVRSystem = (::vr::IVRSystem*)::vr::VR_GetGenericInterface( 235 ::vr::IVRSystem_Version, &err); 236 if (err || !mVRSystem) { 237 Shutdown(); 238 return false; 239 } 240 mVRChaperone = (::vr::IVRChaperone*)::vr::VR_GetGenericInterface( 241 ::vr::IVRChaperone_Version, &err); 242 if (err || !mVRChaperone) { 243 Shutdown(); 244 return false; 245 } 246 mVRCompositor = (::vr::IVRCompositor*)::vr::VR_GetGenericInterface( 247 ::vr::IVRCompositor_Version, &err); 248 if (err || !mVRCompositor) { 249 Shutdown(); 250 return false; 251 } 252 253 #if defined(XP_WIN) 254 if (!CreateD3DObjects()) { 255 Shutdown(); 256 return false; 257 } 258 259 #endif 260 261 // Configure coordinate system 262 mVRCompositor->SetTrackingSpace(::vr::TrackingUniverseSeated); 263 264 if (!InitState(aSystemState)) { 265 Shutdown(); 266 return false; 267 } 268 if (!SetupContollerActions()) { 269 return false; 270 } 271 272 // Succeeded 273 return true; 274 } 275 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) 282 283 bool OpenVRSession::SetupContollerActions() { 284 if (!vr::VRInput()) { 285 NS_WARNING("OpenVR - vr::VRInput() is null."); 286 return false; 287 } 288 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; 296 297 // Getting / Generating manifest file paths. 298 if (StaticPrefs::dom_vr_process_enabled_AtStartup()) { 299 VRParent* vrParent = VRProcessChild::GetVRParent(); 300 nsCString output; 301 302 if (vrParent->GetOpenVRControllerActionPath(&output)) { 303 controllerAction = output; 304 } 305 306 if (vrParent->GetOpenVRControllerManifestPath(VRControllerType::HTCVive, 307 &output)) { 308 viveManifest = output; 309 } 310 if (!viveManifest.Length() || !FileIsExisting(viveManifest)) { 311 if (!GenerateTempFileName(viveManifest)) { 312 return false; 313 } 314 OpenVRViveBinding viveBinding; 315 std::ofstream viveBindingFile(viveManifest.BeginReading()); 316 if (viveBindingFile.is_open()) { 317 viveBindingFile << viveBinding.binding; 318 viveBindingFile.close(); 319 } 320 } 321 322 #if defined(XP_WIN) 323 if (vrParent->GetOpenVRControllerManifestPath(VRControllerType::MSMR, 324 &output)) { 325 WMRManifest = output; 326 } 327 if (!WMRManifest.Length() || !FileIsExisting(WMRManifest)) { 328 if (!GenerateTempFileName(WMRManifest)) { 329 return false; 330 } 331 OpenVRWMRBinding WMRBinding; 332 std::ofstream WMRBindingFile(WMRManifest.BeginReading()); 333 if (WMRBindingFile.is_open()) { 334 WMRBindingFile << WMRBinding.binding; 335 WMRBindingFile.close(); 336 } 337 } 338 #endif 339 if (vrParent->GetOpenVRControllerManifestPath(VRControllerType::ValveIndex, 340 &output)) { 341 knucklesManifest = output; 342 } 343 if (!knucklesManifest.Length() || !FileIsExisting(knucklesManifest)) { 344 if (!GenerateTempFileName(knucklesManifest)) { 345 return false; 346 } 347 OpenVRKnucklesBinding knucklesBinding; 348 std::ofstream knucklesBindingFile(knucklesManifest.BeginReading()); 349 if (knucklesBindingFile.is_open()) { 350 knucklesBindingFile << knucklesBinding.binding; 351 knucklesBindingFile.close(); 352 } 353 } 354 if (vrParent->GetOpenVRControllerManifestPath( 355 VRControllerType::HTCViveCosmos, &output)) { 356 cosmosManifest = output; 357 } 358 if (!cosmosManifest.Length() || !FileIsExisting(cosmosManifest)) { 359 if (!GenerateTempFileName(cosmosManifest)) { 360 return false; 361 } 362 OpenVRCosmosBinding cosmosBinding; 363 std::ofstream cosmosBindingFile(cosmosManifest.BeginReading()); 364 if (cosmosBindingFile.is_open()) { 365 cosmosBindingFile << cosmosBinding.binding; 366 cosmosBindingFile.close(); 367 } 368 } 369 } else { 370 // Without using VR process 371 if (!sControllerActionFile) { 372 sControllerActionFile = ControllerManifestFile::CreateManifest(); 373 NS_DispatchToMainThread(NS_NewRunnableFunction( 374 "ClearOnShutdown ControllerManifestFile", 375 []() { ClearOnShutdown(&sControllerActionFile); })); 376 } 377 controllerAction = sControllerActionFile->GetFileName(); 378 379 if (!sViveBindingFile) { 380 sViveBindingFile = ControllerManifestFile::CreateManifest(); 381 NS_DispatchToMainThread( 382 NS_NewRunnableFunction("ClearOnShutdown ControllerManifestFile", 383 []() { ClearOnShutdown(&sViveBindingFile); })); 384 } 385 if (!sViveBindingFile->IsExisting()) { 386 nsCString viveBindingPath; 387 if (!GenerateTempFileName(viveBindingPath)) { 388 return false; 389 } 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(); 396 } 397 } 398 viveManifest = sViveBindingFile->GetFileName(); 399 400 if (!sKnucklesBindingFile) { 401 sKnucklesBindingFile = ControllerManifestFile::CreateManifest(); 402 NS_DispatchToMainThread(NS_NewRunnableFunction( 403 "ClearOnShutdown ControllerManifestFile", 404 []() { ClearOnShutdown(&sKnucklesBindingFile); })); 405 } 406 if (!sKnucklesBindingFile->IsExisting()) { 407 nsCString knucklesBindingPath; 408 if (!GenerateTempFileName(knucklesBindingPath)) { 409 return false; 410 } 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(); 417 } 418 } 419 knucklesManifest = sKnucklesBindingFile->GetFileName(); 420 421 if (!sCosmosBindingFile) { 422 sCosmosBindingFile = ControllerManifestFile::CreateManifest(); 423 NS_DispatchToMainThread(NS_NewRunnableFunction( 424 "ClearOnShutdown ControllerManifestFile", 425 []() { ClearOnShutdown(&sCosmosBindingFile); })); 426 } 427 if (!sCosmosBindingFile->IsExisting()) { 428 nsCString cosmosBindingPath; 429 if (!GenerateTempFileName(cosmosBindingPath)) { 430 return false; 431 } 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(); 438 } 439 } 440 cosmosManifest = sCosmosBindingFile->GetFileName(); 441 #if defined(XP_WIN) 442 if (!sWMRBindingFile) { 443 sWMRBindingFile = ControllerManifestFile::CreateManifest(); 444 NS_DispatchToMainThread( 445 NS_NewRunnableFunction("ClearOnShutdown ControllerManifestFile", 446 []() { ClearOnShutdown(&sWMRBindingFile); })); 447 } 448 if (!sWMRBindingFile->IsExisting()) { 449 nsCString WMRBindingPath; 450 if (!GenerateTempFileName(WMRBindingPath)) { 451 return false; 452 } 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(); 459 } 460 } 461 WMRManifest = sWMRBindingFile->GetFileName(); 462 #endif 463 } 464 // End of Getting / Generating manifest file paths. 465 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); 515 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); 564 565 mControllerHand[OpenVRHand::Left] = leftContollerInfo; 566 mControllerHand[OpenVRHand::Right] = rightContollerInfo; 567 568 if (!controllerAction.Length() || !FileIsExisting(controllerAction)) { 569 if (!GenerateTempFileName(controllerAction)) { 570 return false; 571 } 572 JSONStringWriteFunc<nsCString> actionData; 573 JSONWriter actionWriter(actionData); 574 actionWriter.Start(); 575 576 actionWriter.StringProperty("version", 577 "0.1.0"); // TODO: adding a version check. 578 // "default_bindings": [] 579 actionWriter.StartArrayProperty("default_bindings"); 580 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(); 587 }; 588 SetupActionWriterByControllerType("vive_controller", viveManifest); 589 SetupActionWriterByControllerType("knuckles", knucklesManifest); 590 SetupActionWriterByControllerType("vive_cosmos_controller", cosmosManifest); 591 #if defined(XP_WIN) 592 SetupActionWriterByControllerType("holographic_controller", WMRManifest); 593 #endif 594 actionWriter.EndArray(); // End "default_bindings": [] 595 596 actionWriter.StartArrayProperty("actions"); 597 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(); 604 }; 605 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); 630 } 631 actionWriter.EndArray(); // End "actions": [] 632 actionWriter.End(); 633 634 std::ofstream actionfile(controllerAction.BeginReading()); 635 if (actionfile.is_open()) { 636 actionfile << actionData.StringCRef().get(); 637 actionfile.close(); 638 } 639 } 640 641 vr::EVRInputError err = 642 vr::VRInput()->SetActionManifestPath(controllerAction.BeginReading()); 643 if (err != vr::VRInputError_None) { 644 NS_WARNING("OpenVR - SetActionManifestPath failed."); 645 return false; 646 } 647 // End of setup controller actions. 648 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, 654 cosmosManifest]() { 655 VRParent* vrParent = VRProcessChild::GetVRParent(); 656 (void)vrParent->SendOpenVRControllerActionPathToParent( 657 controllerAction); 658 (void)vrParent->SendOpenVRControllerManifestPathToParent( 659 VRControllerType::HTCVive, viveManifest); 660 (void)vrParent->SendOpenVRControllerManifestPathToParent( 661 VRControllerType::MSMR, WMRManifest); 662 (void)vrParent->SendOpenVRControllerManifestPathToParent( 663 VRControllerType::ValveIndex, knucklesManifest); 664 (void)vrParent->SendOpenVRControllerManifestPathToParent( 665 VRControllerType::HTCViveCosmos, cosmosManifest); 666 })); 667 } else { 668 sControllerActionFile->SetFileName(controllerAction.BeginReading()); 669 } 670 671 return true; 672 } 673 674 #if defined(XP_WIN) 675 bool OpenVRSession::CreateD3DObjects() { 676 RefPtr<ID3D11Device> device = gfx::DeviceManagerDx::Get()->GetVRDevice(); 677 if (!device) { 678 return false; 679 } 680 if (!CreateD3DContext(device)) { 681 return false; 682 } 683 return true; 684 } 685 #endif 686 687 void OpenVRSession::Shutdown() { 688 StopHapticTimer(); 689 StopHapticThread(); 690 if (mVRSystem || mVRCompositor || mVRChaperone) { 691 ::vr::VR_Shutdown(); 692 mVRCompositor = nullptr; 693 mVRChaperone = nullptr; 694 mVRSystem = nullptr; 695 } 696 } 697 698 bool OpenVRSession::InitState(VRSystemState& aSystemState) { 699 VRDisplayState& state = aSystemState.displayState; 700 strncpy(state.displayName.data(), "OpenVR HMD", kVRDisplayNameMaxLen); 701 state.eightCC = GFX_VR_EIGHTCC('O', 'p', 'e', 'n', 'V', 'R', ' ', ' '); 702 state.isConnected = 703 mVRSystem->IsTrackedDeviceConnected(::vr::k_unTrackedDeviceIndex_Hmd); 704 state.isMounted = false; 705 state.capabilityFlags = 706 (VRDisplayCapabilityFlags)((int)VRDisplayCapabilityFlags::Cap_None | 707 (int) 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 | 714 (int) 715 VRDisplayCapabilityFlags::Cap_ImmersiveVR); 716 state.blendMode = VRDisplayBlendMode::Opaque; 717 state.reportsDroppedFrames = true; 718 719 ::vr::ETrackedPropertyError err; 720 bool bHasProximitySensor = mVRSystem->GetBoolTrackedDeviceProperty( 721 ::vr::k_unTrackedDeviceIndex_Hmd, ::vr::Prop_ContainsProximitySensor_Bool, 722 &err); 723 if (err == ::vr::TrackedProp_Success && bHasProximitySensor) { 724 state.capabilityFlags = 725 (VRDisplayCapabilityFlags)((int)state.capabilityFlags | 726 (int)VRDisplayCapabilityFlags:: 727 Cap_MountDetection); 728 } 729 730 uint32_t w, h; 731 mVRSystem->GetRecommendedRenderTargetSize(&w, &h); 732 state.eyeResolution.width = w; 733 state.eyeResolution.height = h; 734 state.nativeFramebufferScaleFactor = 1.0f; 735 736 // default to an identity quaternion 737 aSystemState.sensorState.pose.orientation[3] = 1.0f; 738 739 UpdateStageParameters(state); 740 UpdateEyeParameters(aSystemState); 741 742 VRHMDSensorState& sensorState = aSystemState.sensorState; 743 sensorState.flags = 744 (VRDisplayCapabilityFlags)((int) 745 VRDisplayCapabilityFlags::Cap_Orientation | 746 (int)VRDisplayCapabilityFlags::Cap_Position); 747 sensorState.pose.orientation[3] = 1.0f; // Default to an identity quaternion 748 749 return true; 750 } 751 752 void OpenVRSession::UpdateStageParameters(VRDisplayState& aState) { 753 float sizeX = 0.0f; 754 float sizeZ = 0.0f; 755 if (mVRChaperone->GetPlayAreaSize(&sizeX, &sizeZ)) { 756 ::vr::HmdMatrix34_t t = 757 mVRSystem->GetSeatedZeroPoseToStandingAbsoluteTrackingPose(); 758 aState.stageSize.width = sizeX; 759 aState.stageSize.height = sizeZ; 760 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; 765 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; 770 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; 775 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; 780 } else { 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; 785 786 aState.sittingToStandingTransform[0] = 1.0f; 787 aState.sittingToStandingTransform[1] = 0.0f; 788 aState.sittingToStandingTransform[2] = 0.0f; 789 aState.sittingToStandingTransform[3] = 0.0f; 790 791 aState.sittingToStandingTransform[4] = 0.0f; 792 aState.sittingToStandingTransform[5] = 1.0f; 793 aState.sittingToStandingTransform[6] = 0.0f; 794 aState.sittingToStandingTransform[7] = 0.0f; 795 796 aState.sittingToStandingTransform[8] = 0.0f; 797 aState.sittingToStandingTransform[9] = 0.0f; 798 aState.sittingToStandingTransform[10] = 1.0f; 799 aState.sittingToStandingTransform[11] = 0.0f; 800 801 aState.sittingToStandingTransform[12] = 0.0f; 802 aState.sittingToStandingTransform[13] = 0.75f; 803 aState.sittingToStandingTransform[14] = 0.0f; 804 aState.sittingToStandingTransform[15] = 1.0f; 805 } 806 } 807 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]; 812 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]; 819 820 float left, right, up, down; 821 mVRSystem->GetProjectionRaw(static_cast<::vr::Hmd_Eye>(eye), &left, &right, 822 &up, &down); 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; 827 828 Matrix4x4 pose; 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)); 833 pose.Transpose(); 834 pose.Invert(); 835 headToEyeTransforms[eye] = pose; 836 } 837 aState.sensorState.CalcViewMatrices(headToEyeTransforms); 838 } 839 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 844 // all. 845 mVRCompositor->WaitGetPoses(poses, posesSize, nullptr, 0); 846 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; 851 } else { 852 // This should not happen, but log it just in case 853 fprintf(stderr, "OpenVR - IVRCompositor::GetFrameTiming failed"); 854 } 855 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]; 862 863 gfx::Matrix4x4 m; 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)); 870 m.Transpose(); 871 872 gfx::Quaternion rot; 873 rot.SetFromRotationMatrix(m); 874 875 aState.sensorState.flags = 876 (VRDisplayCapabilityFlags)((int)aState.sensorState.flags | 877 (int)VRDisplayCapabilityFlags:: 878 Cap_Orientation); 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]; 886 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]; 896 } 897 } 898 899 void OpenVRSession::EnumerateControllers(VRSystemState& aState) { 900 MOZ_ASSERT(mVRSystem); 901 902 MutexAutoLock lock(mControllerHapticStateMutex); 903 904 bool controllerPresent[kVRControllerMaxCount] = {false}; 905 uint32_t stateIndex = 0; 906 mActionsetFirefox = vr::k_ulInvalidActionSetHandle; 907 VRControllerType controllerType = VRControllerType::_empty; 908 909 if (vr::VRInput()->GetActionSetHandle( 910 "/actions/firefox", &mActionsetFirefox) != vr::VRInputError_None) { 911 return; 912 } 913 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) { 919 continue; 920 } 921 } else if (handIndex == OpenVRHand::Right) { 922 if (vr::VRInput()->GetInputSourceHandle( 923 "/user/hand/right", 924 &mControllerHand[OpenVRHand::Right].mSource) != 925 vr::VRInputError_None) { 926 continue; 927 } 928 } else { 929 MOZ_ASSERT(false, "Unknown OpenVR hand type."); 930 } 931 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) { 942 continue; 943 } 944 945 if (mControllerDeviceIndex[stateIndex] != handIndex) { 946 VRControllerState& controllerState = aState.controllerState[stateIndex]; 947 948 // Get controllers' action handles. 949 auto SetActionsToWriter = [&](ControllerAction& aAction) { 950 vr::VRInput()->GetActionHandle(aAction.name.BeginReading(), 951 &aAction.handle); 952 }; 953 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); 971 SetActionsToWriter( 972 mControllerHand[handIndex].mActionThumbstick_Pressed); 973 SetActionsToWriter( 974 mControllerHand[handIndex].mActionThumbstick_Touched); 975 SetActionsToWriter(mControllerHand[handIndex].mActionFingerIndex_Value); 976 SetActionsToWriter( 977 mControllerHand[handIndex].mActionFingerMiddle_Value); 978 SetActionsToWriter(mControllerHand[handIndex].mActionFingerRing_Value); 979 SetActionsToWriter(mControllerHand[handIndex].mActionFingerPinky_Value); 980 SetActionsToWriter(mControllerHand[handIndex].mActionBumper_Pressed); 981 982 nsCString deviceId; 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.data(), deviceId.BeginReading(), 991 controllerState.controllerName.size()); 992 controllerState.numHaptics = kNumOpenVRHaptics; 993 controllerState.targetRayMode = gfx::TargetRayMode::TrackedPointer; 994 controllerState.type = controllerType; 995 } 996 controllerPresent[stateIndex] = true; 997 mControllerDeviceIndex[stateIndex] = static_cast<OpenVRHand>(handIndex); 998 ++stateIndex; 999 } 1000 } 1001 1002 // Clear out entries for disconnected controllers 1003 for (uint32_t stateIndex = 0; stateIndex < kVRControllerMaxCount; 1004 stateIndex++) { 1005 if (!controllerPresent[stateIndex] && 1006 mControllerDeviceIndex[stateIndex] != OpenVRHand::None) { 1007 mControllerDeviceIndex[stateIndex] = OpenVRHand::None; 1008 memset(&aState.controllerState[stateIndex], 0, sizeof(VRControllerState)); 1009 } 1010 } 1011 1012 // Create controller mapper 1013 if (controllerType != VRControllerType::_empty) { 1014 switch (controllerType) { 1015 case VRControllerType::HTCVive: 1016 mControllerMapper = MakeUnique<OpenVRViveMapper>(); 1017 break; 1018 case VRControllerType::HTCViveCosmos: 1019 mControllerMapper = MakeUnique<OpenVRCosmosMapper>(); 1020 break; 1021 #if defined(XP_WIN) 1022 case VRControllerType::MSMR: 1023 mControllerMapper = MakeUnique<OpenVRWMRMapper>(); 1024 break; 1025 #endif 1026 case VRControllerType::ValveIndex: 1027 mControllerMapper = MakeUnique<OpenVRKnucklesMapper>(); 1028 break; 1029 default: 1030 mControllerMapper = MakeUnique<OpenVRDefaultMapper>(); 1031 NS_WARNING("Undefined controller type"); 1032 break; 1033 } 1034 } 1035 } 1036 1037 void OpenVRSession::UpdateControllerButtons(VRSystemState& aState) { 1038 MOZ_ASSERT(mVRSystem); 1039 1040 for (uint32_t stateIndex = 0; stateIndex < kVRControllerMaxCount; 1041 ++stateIndex) { 1042 const OpenVRHand role = mControllerDeviceIndex[stateIndex]; 1043 if (role == OpenVRHand::None) { 1044 continue; 1045 } 1046 VRControllerState& controllerState = aState.controllerState[stateIndex]; 1047 controllerState.hand = GetControllerHandFromControllerRole(role); 1048 mControllerMapper->UpdateButtons(controllerState, mControllerHand[role]); 1049 SetControllerSelectionAndSqueezeFrameId( 1050 controllerState, aState.displayState.lastSubmittedFrameId); 1051 } 1052 } 1053 1054 void OpenVRSession::UpdateControllerPoses(VRSystemState& aState) { 1055 MOZ_ASSERT(mVRSystem); 1056 1057 for (uint32_t stateIndex = 0; stateIndex < kVRControllerMaxCount; 1058 ++stateIndex) { 1059 const OpenVRHand role = mControllerDeviceIndex[stateIndex]; 1060 if (role == OpenVRHand::None) { 1061 continue; 1062 } 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; 1072 } else { 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); 1079 } else { 1080 controllerState.flags = dom::GamepadCapabilityFlags::Cap_None; 1081 } 1082 if (pose.bPoseIsValid && 1083 pose.eTrackingResult == ::vr::TrackingResult_Running_OK) { 1084 gfx::Matrix4x4 m; 1085 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)); 1092 m.Transpose(); 1093 1094 gfx::Quaternion rot; 1095 rot.SetFromRotationMatrix(m); 1096 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; 1108 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; 1119 1120 // Calculate its target ray space by shifting degrees in x-axis 1121 // for ergonomic. 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); 1127 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; 1136 } 1137 } 1138 } 1139 } 1140 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; 1150 char charBuf[128]; 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."); 1155 } 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"); 1160 isFound = true; 1161 aControllerType = VRControllerType::HTCVive; 1162 } else if (deviceId.Find("knuckles") != kNotFound || 1163 deviceId.Find("valve_controller_knu") != kNotFound) { 1164 aId.AssignLiteral("OpenVR Knuckles"); 1165 isFound = true; 1166 aControllerType = VRControllerType::ValveIndex; 1167 } else if (deviceId.Find("vive_cosmos_controller") != kNotFound) { 1168 aId.AssignLiteral("OpenVR Cosmos"); 1169 isFound = true; 1170 aControllerType = VRControllerType::HTCViveCosmos; 1171 } 1172 if (!isFound) { 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."); 1177 } 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; 1183 isFound = true; 1184 aControllerType = VRControllerType::MSMR; 1185 } 1186 } 1187 if (!isFound) { 1188 aId.AssignLiteral("OpenVR Undefined"); 1189 aControllerType = VRControllerType::_empty; 1190 } 1191 break; 1192 } 1193 case ::vr::TrackedDeviceClass_GenericTracker: { 1194 aId.AssignLiteral("OpenVR Tracker"); 1195 aControllerType = VRControllerType::_empty; 1196 break; 1197 } 1198 default: 1199 MOZ_ASSERT(false); 1200 break; 1201 } 1202 } 1203 1204 void OpenVRSession::StartFrame(mozilla::gfx::VRSystemState& aSystemState) { 1205 UpdateHeadsetPose(aSystemState); 1206 UpdateEyeParameters(aSystemState); 1207 EnumerateControllers(aSystemState); 1208 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); 1215 } 1216 1217 void OpenVRSession::ProcessEvents(mozilla::gfx::VRSystemState& aSystemState) { 1218 bool isHmdPresent = ::vr::VR_IsHmdPresent(); 1219 if (!isHmdPresent) { 1220 mShouldQuit = true; 1221 } 1222 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; 1229 } 1230 break; 1231 case ::vr::VREvent_TrackedDeviceUserInteractionEnded: 1232 if (event.trackedDeviceIndex == ::vr::k_unTrackedDeviceIndex_Hmd) { 1233 aSystemState.displayState.isMounted = false; 1234 } 1235 break; 1236 case ::vr::EVREventType::VREvent_TrackedDeviceActivated: 1237 if (event.trackedDeviceIndex == ::vr::k_unTrackedDeviceIndex_Hmd) { 1238 aSystemState.displayState.isConnected = true; 1239 } 1240 break; 1241 case ::vr::EVREventType::VREvent_TrackedDeviceDeactivated: 1242 if (event.trackedDeviceIndex == ::vr::k_unTrackedDeviceIndex_Hmd) { 1243 aSystemState.displayState.isConnected = false; 1244 } 1245 break; 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: 1253 mShouldQuit = true; 1254 break; 1255 default: 1256 // ignore 1257 break; 1258 } 1259 } 1260 } 1261 1262 #if defined(XP_WIN) 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); 1268 } 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); 1275 } 1276 #endif 1277 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); 1288 if (!surf) { 1289 NS_WARNING("OpenVRSession::SubmitFrame failed to get a MacIOSurface"); 1290 return false; 1291 } 1292 1293 CFTypeRefPtr<IOSurfaceRef> ioSurface = surf->GetIOSurfaceRef(); 1294 tex.handle = (void*)ioSurface.get(); 1295 #else 1296 tex.handle = aTextureHandle; 1297 #endif 1298 tex.eType = aTextureType; 1299 tex.eColorSpace = ::vr::EColorSpace::ColorSpace_Auto; 1300 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); 1306 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"); 1311 } 1312 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); 1317 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"); 1321 } 1322 1323 mVRCompositor->PostPresentHandoff(); 1324 return true; 1325 } 1326 1327 void OpenVRSession::StopPresentation() { 1328 mVRCompositor->ClearLastSubmittedFrame(); 1329 1330 ::vr::Compositor_CumulativeStats stats; 1331 mVRCompositor->GetCumulativeStats(&stats, 1332 sizeof(::vr::Compositor_CumulativeStats)); 1333 } 1334 1335 bool OpenVRSession::StartPresentation() { return true; } 1336 1337 void OpenVRSession::VibrateHaptic(uint32_t aControllerIdx, 1338 uint32_t aHapticIndex, float aIntensity, 1339 float aDuration) { 1340 MutexAutoLock lock(mControllerHapticStateMutex); 1341 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(); })); 1346 } 1347 if (aHapticIndex >= kNumOpenVRHaptics || 1348 aControllerIdx >= kVRControllerMaxCount) { 1349 return; 1350 } 1351 1352 const OpenVRHand role = mControllerDeviceIndex[aControllerIdx]; 1353 if (role == OpenVRHand::None) { 1354 return; 1355 } 1356 mHapticPulseRemaining[aControllerIdx][aHapticIndex] = aDuration; 1357 mHapticPulseIntensity[aControllerIdx][aHapticIndex] = aIntensity; 1358 } 1359 1360 void OpenVRSession::StartHapticThread() { 1361 MOZ_ASSERT(NS_IsMainThread()); 1362 if (!mHapticThread) { 1363 mHapticThread = new VRThread("VR_OpenVR_Haptics"_ns); 1364 } 1365 mHapticThread->Start(); 1366 StartHapticTimer(); 1367 } 1368 1369 void OpenVRSession::StopHapticThread() { 1370 if (mHapticThread) { 1371 NS_DispatchToMainThread(NS_NewRunnableFunction( 1372 "mHapticThread::Shutdown", 1373 [thread = mHapticThread]() { thread->Shutdown(); })); 1374 mHapticThread = nullptr; 1375 } 1376 } 1377 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"_ns); 1388 } 1389 } 1390 1391 void OpenVRSession::StopHapticTimer() { 1392 if (mHapticTimer) { 1393 mHapticTimer->Cancel(); 1394 mHapticTimer = nullptr; 1395 } 1396 } 1397 1398 /*static*/ 1399 void OpenVRSession::HapticTimerCallback(nsITimer* aTimer, void* aClosure) { 1400 /** 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. 1405 */ 1406 OpenVRSession* self = static_cast<OpenVRSession*>(aClosure); 1407 MOZ_ASSERT(self); 1408 self->UpdateHaptics(); 1409 } 1410 1411 void OpenVRSession::UpdateHaptics() { 1412 MOZ_ASSERT(mHapticThread->GetThread() == NS_GetCurrentThread()); 1413 MOZ_ASSERT(mVRSystem); 1414 1415 MutexAutoLock lock(mControllerHapticStateMutex); 1416 1417 TimeStamp now = TimeStamp::Now(); 1418 if (mLastHapticUpdate.IsNull()) { 1419 mLastHapticUpdate = now; 1420 return; 1421 } 1422 float deltaTime = (float)(now - mLastHapticUpdate).ToSeconds(); 1423 mLastHapticUpdate = now; 1424 1425 for (uint32_t stateIndex = 0; stateIndex < kVRControllerMaxCount; 1426 ++stateIndex) { 1427 const OpenVRHand role = mControllerDeviceIndex[stateIndex]; 1428 if (role == OpenVRHand::None) { 1429 continue; 1430 } 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) { 1435 continue; 1436 } 1437 vr::VRInput()->TriggerHapticVibrationAction( 1438 mControllerHand[role].mActionHaptic.handle, 0.0f, deltaTime, 4.0f, 1439 intensity > 1.0f ? 1.0f : intensity, vr::k_ulInvalidInputValueHandle); 1440 1441 duration -= deltaTime; 1442 if (duration < 0.0f) { 1443 duration = 0.0f; 1444 } 1445 mHapticPulseRemaining[stateIndex][hapticIdx] = duration; 1446 } 1447 } 1448 } 1449 1450 void OpenVRSession::StopVibrateHaptic(uint32_t aControllerIdx) { 1451 MutexAutoLock lock(mControllerHapticStateMutex); 1452 if (aControllerIdx >= kVRControllerMaxCount) { 1453 return; 1454 } 1455 for (int iHaptic = 0; iHaptic < kNumOpenVRHaptics; iHaptic++) { 1456 mHapticPulseRemaining[aControllerIdx][iHaptic] = 0.0f; 1457 } 1458 } 1459 1460 void OpenVRSession::StopAllHaptics() { 1461 MutexAutoLock lock(mControllerHapticStateMutex); 1462 for (auto& controller : mHapticPulseRemaining) { 1463 for (auto& haptic : controller) { 1464 haptic = 0.0f; 1465 } 1466 } 1467 } 1468 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; 1474 } 1475 1476 } // namespace mozilla::gfx