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