Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/gfx/vr/gfxVROpenVR.cpp
Line
Count
Source (jump to first uncovered line)
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 <math.h>
8
9
#include "prlink.h"
10
#include "prenv.h"
11
#include "gfxPrefs.h"
12
#include "mozilla/Preferences.h"
13
14
#include "mozilla/gfx/Quaternion.h"
15
16
#ifdef XP_WIN
17
#include "CompositorD3D11.h"
18
#include "TextureD3D11.h"
19
#elif defined(XP_MACOSX)
20
#include "mozilla/gfx/MacIOSurface.h"
21
#endif
22
23
#include "gfxVROpenVR.h"
24
#include "VRManagerParent.h"
25
#include "VRManager.h"
26
#include "VRThread.h"
27
28
#include "nsServiceManagerUtils.h"
29
#include "nsIScreenManager.h"
30
31
#include "mozilla/dom/GamepadEventTypes.h"
32
#include "mozilla/dom/GamepadBinding.h"
33
#include "mozilla/Telemetry.h"
34
35
#ifndef M_PI
36
# define M_PI 3.14159265358979323846
37
#endif
38
39
using namespace mozilla;
40
using namespace mozilla::gfx;
41
using namespace mozilla::gfx::impl;
42
using namespace mozilla::layers;
43
using namespace mozilla::dom;
44
45
#define BTN_MASK_FROM_ID(_id) \
46
0
  ::vr::ButtonMaskFromId(vr::EVRButtonId::_id)
47
48
static const uint32_t kNumOpenVRHaptcs = 1;
49
50
VRDisplayOpenVR::VRDisplayOpenVR(::vr::IVRSystem *aVRSystem,
51
                                 ::vr::IVRChaperone *aVRChaperone,
52
                                 ::vr::IVRCompositor *aVRCompositor)
53
  : VRDisplayLocal(VRDeviceType::OpenVR)
54
  , mVRSystem(aVRSystem)
55
  , mVRChaperone(aVRChaperone)
56
  , mVRCompositor(aVRCompositor)
57
  , mIsPresenting(false)
58
0
{
59
0
  MOZ_COUNT_CTOR_INHERITED(VRDisplayOpenVR, VRDisplayLocal);
60
0
61
0
  VRDisplayState& state = mDisplayInfo.mDisplayState;
62
0
63
0
  strncpy(state.mDisplayName, "OpenVR HMD", kVRDisplayNameMaxLen);
64
0
  state.mIsConnected = mVRSystem->IsTrackedDeviceConnected(::vr::k_unTrackedDeviceIndex_Hmd);
65
0
  state.mIsMounted = false;
66
0
  state.mCapabilityFlags = VRDisplayCapabilityFlags::Cap_None |
67
0
                           VRDisplayCapabilityFlags::Cap_Orientation |
68
0
                           VRDisplayCapabilityFlags::Cap_Position |
69
0
                           VRDisplayCapabilityFlags::Cap_External |
70
0
                           VRDisplayCapabilityFlags::Cap_Present |
71
0
                           VRDisplayCapabilityFlags::Cap_StageParameters;
72
0
  mIsHmdPresent = ::vr::VR_IsHmdPresent();
73
0
74
0
  ::vr::ETrackedPropertyError err;
75
0
  bool bHasProximitySensor = mVRSystem->GetBoolTrackedDeviceProperty(::vr::k_unTrackedDeviceIndex_Hmd, ::vr::Prop_ContainsProximitySensor_Bool, &err);
76
0
  if (err == ::vr::TrackedProp_Success && bHasProximitySensor) {
77
0
    state.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_MountDetection;
78
0
  }
79
0
80
0
  mVRCompositor->SetTrackingSpace(::vr::TrackingUniverseSeated);
81
0
82
0
  uint32_t w, h;
83
0
  mVRSystem->GetRecommendedRenderTargetSize(&w, &h);
84
0
  state.mEyeResolution.width = w;
85
0
  state.mEyeResolution.height = h;
86
0
87
0
  // SteamVR gives the application a single FOV to use; it's not configurable as with Oculus
88
0
  for (uint32_t eye = 0; eye < 2; ++eye) {
89
0
    // get l/r/t/b clip plane coordinates
90
0
    float l, r, t, b;
91
0
    mVRSystem->GetProjectionRaw(static_cast<::vr::Hmd_Eye>(eye), &l, &r, &t, &b);
92
0
    state.mEyeFOV[eye].SetFromTanRadians(-t, r, b, -l);
93
0
  }
94
0
  UpdateEyeParameters();
95
0
  UpdateStageParameters();
96
0
}
97
98
VRDisplayOpenVR::~VRDisplayOpenVR()
99
0
{
100
0
  Destroy();
101
0
  MOZ_COUNT_DTOR_INHERITED(VRDisplayOpenVR, VRDisplayLocal);
102
0
}
103
104
void
105
VRDisplayOpenVR::Destroy()
106
0
{
107
0
  StopPresentation();
108
0
  ::vr::VR_Shutdown();
109
0
}
110
111
void
112
VRDisplayOpenVR::UpdateEyeParameters(gfx::Matrix4x4* aHeadToEyeTransforms /* = nullptr */)
113
0
{
114
0
  // Note this must be called every frame, as the IPD adjustment can be changed
115
0
  // by the user during a VR session.
116
0
  for (uint32_t eye = 0; eye < VRDisplayState::NumEyes; eye++) {
117
0
    ::vr::HmdMatrix34_t eyeToHead = mVRSystem->GetEyeToHeadTransform(static_cast<::vr::Hmd_Eye>(eye));
118
0
119
0
    mDisplayInfo.mDisplayState.mEyeTranslation[eye].x = eyeToHead.m[0][3];
120
0
    mDisplayInfo.mDisplayState.mEyeTranslation[eye].y = eyeToHead.m[1][3];
121
0
    mDisplayInfo.mDisplayState.mEyeTranslation[eye].z = eyeToHead.m[2][3];
122
0
123
0
    if (aHeadToEyeTransforms) {
124
0
      Matrix4x4 pose;
125
0
      // NOTE! eyeToHead.m is a 3x4 matrix, not 4x4.  But
126
0
      // because of its arrangement, we can copy the 12 elements in and
127
0
      // then transpose them to the right place.
128
0
      memcpy(&pose._11, &eyeToHead.m, sizeof(eyeToHead.m));
129
0
      pose.Transpose();
130
0
      pose.Invert();
131
0
      aHeadToEyeTransforms[eye] = pose;
132
0
    }
133
0
  }
134
0
}
135
136
void
137
VRDisplayOpenVR::UpdateStageParameters()
138
0
{
139
0
  VRDisplayState& state = mDisplayInfo.mDisplayState;
140
0
  float sizeX = 0.0f;
141
0
  float sizeZ = 0.0f;
142
0
  if (mVRChaperone->GetPlayAreaSize(&sizeX, &sizeZ)) {
143
0
    ::vr::HmdMatrix34_t t = mVRSystem->GetSeatedZeroPoseToStandingAbsoluteTrackingPose();
144
0
    state.mStageSize.width = sizeX;
145
0
    state.mStageSize.height = sizeZ;
146
0
147
0
    state.mSittingToStandingTransform[0] = t.m[0][0];
148
0
    state.mSittingToStandingTransform[1] = t.m[1][0];
149
0
    state.mSittingToStandingTransform[2] = t.m[2][0];
150
0
    state.mSittingToStandingTransform[3] = 0.0f;
151
0
152
0
    state.mSittingToStandingTransform[4] = t.m[0][1];
153
0
    state.mSittingToStandingTransform[5] = t.m[1][1];
154
0
    state.mSittingToStandingTransform[6] = t.m[2][1];
155
0
    state.mSittingToStandingTransform[7] = 0.0f;
156
0
157
0
    state.mSittingToStandingTransform[8] = t.m[0][2];
158
0
    state.mSittingToStandingTransform[9] = t.m[1][2];
159
0
    state.mSittingToStandingTransform[10] = t.m[2][2];
160
0
    state.mSittingToStandingTransform[11] = 0.0f;
161
0
162
0
    state.mSittingToStandingTransform[12] = t.m[0][3];
163
0
    state.mSittingToStandingTransform[13] = t.m[1][3];
164
0
    state.mSittingToStandingTransform[14] = t.m[2][3];
165
0
    state.mSittingToStandingTransform[15] = 1.0f;
166
0
  } else {
167
0
    // If we fail, fall back to reasonable defaults.
168
0
    // 1m x 1m space, 0.75m high in seated position
169
0
170
0
    state.mStageSize.width = 1.0f;
171
0
    state.mStageSize.height = 1.0f;
172
0
173
0
    state.mSittingToStandingTransform[0] = 1.0f;
174
0
    state.mSittingToStandingTransform[1] = 0.0f;
175
0
    state.mSittingToStandingTransform[2] = 0.0f;
176
0
    state.mSittingToStandingTransform[3] = 0.0f;
177
0
178
0
    state.mSittingToStandingTransform[4] = 0.0f;
179
0
    state.mSittingToStandingTransform[5] = 1.0f;
180
0
    state.mSittingToStandingTransform[6] = 0.0f;
181
0
    state.mSittingToStandingTransform[7] = 0.0f;
182
0
183
0
    state.mSittingToStandingTransform[8] = 0.0f;
184
0
    state.mSittingToStandingTransform[9] = 0.0f;
185
0
    state.mSittingToStandingTransform[10] = 1.0f;
186
0
    state.mSittingToStandingTransform[11] = 0.0f;
187
0
188
0
    state.mSittingToStandingTransform[12] = 0.0f;
189
0
    state.mSittingToStandingTransform[13] = 0.75f;
190
0
    state.mSittingToStandingTransform[14] = 0.0f;
191
0
    state.mSittingToStandingTransform[15] = 1.0f;
192
0
  }
193
0
}
194
195
void
196
VRDisplayOpenVR::ZeroSensor()
197
0
{
198
0
  mVRSystem->ResetSeatedZeroPose();
199
0
  UpdateStageParameters();
200
0
}
201
202
bool
203
VRDisplayOpenVR::GetIsHmdPresent()
204
0
{
205
0
  return mIsHmdPresent;
206
0
}
207
208
void
209
VRDisplayOpenVR::Refresh()
210
0
{
211
0
  mIsHmdPresent = ::vr::VR_IsHmdPresent();
212
0
213
0
  ::vr::VREvent_t event;
214
0
  while (mVRSystem && mVRSystem->PollNextEvent(&event, sizeof(event))) {
215
0
    switch (event.eventType) {
216
0
      case ::vr::VREvent_TrackedDeviceUserInteractionStarted:
217
0
        if (event.trackedDeviceIndex == ::vr::k_unTrackedDeviceIndex_Hmd) {
218
0
          mDisplayInfo.mDisplayState.mIsMounted = true;
219
0
        }
220
0
        break;
221
0
      case ::vr::VREvent_TrackedDeviceUserInteractionEnded:
222
0
        if (event.trackedDeviceIndex == ::vr::k_unTrackedDeviceIndex_Hmd) {
223
0
          mDisplayInfo.mDisplayState.mIsMounted = false;
224
0
        }
225
0
        break;
226
0
      case ::vr::EVREventType::VREvent_TrackedDeviceActivated:
227
0
        if (event.trackedDeviceIndex == ::vr::k_unTrackedDeviceIndex_Hmd) {
228
0
          mDisplayInfo.mDisplayState.mIsConnected = true;
229
0
        }
230
0
        break;
231
0
      case ::vr::EVREventType::VREvent_TrackedDeviceDeactivated:
232
0
        if (event.trackedDeviceIndex == ::vr::k_unTrackedDeviceIndex_Hmd) {
233
0
          mDisplayInfo.mDisplayState.mIsConnected = false;
234
0
        }
235
0
        break;
236
0
      case ::vr::EVREventType::VREvent_DriverRequestedQuit:
237
0
      case ::vr::EVREventType::VREvent_Quit:
238
0
      case ::vr::EVREventType::VREvent_ProcessQuit:
239
0
      case ::vr::EVREventType::VREvent_QuitAcknowledged:
240
0
      case ::vr::EVREventType::VREvent_QuitAborted_UserPrompt:
241
0
        mIsHmdPresent = false;
242
0
        break;
243
0
      default:
244
0
        // ignore
245
0
        break;
246
0
    }
247
0
  }
248
0
}
249
250
VRHMDSensorState
251
VRDisplayOpenVR::GetSensorState()
252
0
{
253
0
  const uint32_t posesSize = ::vr::k_unTrackedDeviceIndex_Hmd + 1;
254
0
  ::vr::TrackedDevicePose_t poses[posesSize];
255
0
  // Note: We *must* call WaitGetPoses in order for any rendering to happen at all.
256
0
  mVRCompositor->WaitGetPoses(poses, posesSize, nullptr, 0);
257
0
  gfx::Matrix4x4 headToEyeTransforms[2];
258
0
  UpdateEyeParameters(headToEyeTransforms);
259
0
260
0
  VRHMDSensorState result{};
261
0
262
0
  ::vr::Compositor_FrameTiming timing;
263
0
  timing.m_nSize = sizeof(::vr::Compositor_FrameTiming);
264
0
  if (mVRCompositor->GetFrameTiming(&timing)) {
265
0
    result.timestamp = timing.m_flSystemTimeInSeconds;
266
0
  } else {
267
0
    // This should not happen, but log it just in case
268
0
    NS_WARNING("OpenVR - IVRCompositor::GetFrameTiming failed");
269
0
  }
270
0
271
0
  if (poses[::vr::k_unTrackedDeviceIndex_Hmd].bDeviceIsConnected &&
272
0
      poses[::vr::k_unTrackedDeviceIndex_Hmd].bPoseIsValid &&
273
0
      poses[::vr::k_unTrackedDeviceIndex_Hmd].eTrackingResult == ::vr::TrackingResult_Running_OK)
274
0
  {
275
0
    const ::vr::TrackedDevicePose_t& pose = poses[::vr::k_unTrackedDeviceIndex_Hmd];
276
0
277
0
    gfx::Matrix4x4 m;
278
0
    // NOTE! mDeviceToAbsoluteTracking is a 3x4 matrix, not 4x4.  But
279
0
    // because of its arrangement, we can copy the 12 elements in and
280
0
    // then transpose them to the right place.  We do this so we can
281
0
    // pull out a Quaternion.
282
0
    memcpy(&m._11, &pose.mDeviceToAbsoluteTracking, sizeof(pose.mDeviceToAbsoluteTracking));
283
0
    m.Transpose();
284
0
285
0
    gfx::Quaternion rot;
286
0
    rot.SetFromRotationMatrix(m);
287
0
    rot.Invert();
288
0
289
0
    result.flags |= VRDisplayCapabilityFlags::Cap_Orientation;
290
0
    result.pose.orientation[0] = rot.x;
291
0
    result.pose.orientation[1] = rot.y;
292
0
    result.pose.orientation[2] = rot.z;
293
0
    result.pose.orientation[3] = rot.w;
294
0
    result.pose.angularVelocity[0] = pose.vAngularVelocity.v[0];
295
0
    result.pose.angularVelocity[1] = pose.vAngularVelocity.v[1];
296
0
    result.pose.angularVelocity[2] = pose.vAngularVelocity.v[2];
297
0
298
0
    result.flags |= VRDisplayCapabilityFlags::Cap_Position;
299
0
    result.pose.position[0] = m._41;
300
0
    result.pose.position[1] = m._42;
301
0
    result.pose.position[2] = m._43;
302
0
    result.pose.linearVelocity[0] = pose.vVelocity.v[0];
303
0
    result.pose.linearVelocity[1] = pose.vVelocity.v[1];
304
0
    result.pose.linearVelocity[2] = pose.vVelocity.v[2];
305
0
  } else {
306
0
    // default to an identity quaternion
307
0
    result.pose.orientation[3] = 1.0f;
308
0
  }
309
0
310
0
  result.CalcViewMatrices(headToEyeTransforms);
311
0
  result.inputFrameID = mDisplayInfo.mFrameId;
312
0
  return result;
313
0
}
314
315
void
316
VRDisplayOpenVR::StartPresentation()
317
0
{
318
0
  if (mIsPresenting) {
319
0
    return;
320
0
  }
321
0
  mIsPresenting = true;
322
0
  mTelemetry.Clear();
323
0
  mTelemetry.mPresentationStart = TimeStamp::Now();
324
0
325
0
  ::vr::Compositor_CumulativeStats stats;
326
0
  mVRCompositor->GetCumulativeStats(&stats, sizeof(::vr::Compositor_CumulativeStats));
327
0
  mTelemetry.mLastDroppedFrameCount = stats.m_nNumReprojectedFrames;
328
0
}
329
330
void
331
VRDisplayOpenVR::StopPresentation()
332
0
{
333
0
  if (!mIsPresenting) {
334
0
    return;
335
0
  }
336
0
337
0
  mVRCompositor->ClearLastSubmittedFrame();
338
0
339
0
  mIsPresenting = false;
340
0
  const TimeDuration duration = TimeStamp::Now() - mTelemetry.mPresentationStart;
341
0
  Telemetry::Accumulate(Telemetry::WEBVR_USERS_VIEW_IN, 2);
342
0
  Telemetry::Accumulate(Telemetry::WEBVR_TIME_SPENT_VIEWING_IN_OPENVR,
343
0
                        duration.ToMilliseconds());
344
0
345
0
  ::vr::Compositor_CumulativeStats stats;
346
0
  mVRCompositor->GetCumulativeStats(&stats, sizeof(::vr::Compositor_CumulativeStats));
347
0
  const uint32_t droppedFramesPerSec = (stats.m_nNumReprojectedFrames -
348
0
                                        mTelemetry.mLastDroppedFrameCount) / duration.ToSeconds();
349
0
  Telemetry::Accumulate(Telemetry::WEBVR_DROPPED_FRAMES_IN_OPENVR, droppedFramesPerSec);
350
0
}
351
352
bool
353
VRDisplayOpenVR::SubmitFrameOpenVRHandle(void* aTextureHandle,
354
                                         ::vr::ETextureType aTextureType,
355
                                         const IntSize& aSize,
356
                                         const gfx::Rect& aLeftEyeRect,
357
                                         const gfx::Rect& aRightEyeRect)
358
0
{
359
0
  MOZ_ASSERT(mSubmitThread->GetThread() == NS_GetCurrentThread());
360
0
  if (!mIsPresenting) {
361
0
    return false;
362
0
  }
363
0
364
0
  ::vr::Texture_t tex;
365
0
  tex.handle = aTextureHandle;
366
0
  tex.eType = aTextureType;
367
0
  tex.eColorSpace = ::vr::EColorSpace::ColorSpace_Auto;
368
0
369
0
  ::vr::VRTextureBounds_t bounds;
370
0
  bounds.uMin = aLeftEyeRect.X();
371
0
  bounds.vMin = 1.0 - aLeftEyeRect.Y();
372
0
  bounds.uMax = aLeftEyeRect.XMost();
373
0
  bounds.vMax = 1.0 - aLeftEyeRect.YMost();
374
0
375
0
  ::vr::EVRCompositorError err;
376
0
  err = mVRCompositor->Submit(::vr::EVREye::Eye_Left, &tex, &bounds);
377
0
  if (err != ::vr::EVRCompositorError::VRCompositorError_None) {
378
0
    printf_stderr("OpenVR Compositor Submit() failed.\n");
379
0
  }
380
0
381
0
  bounds.uMin = aRightEyeRect.X();
382
0
  bounds.vMin = 1.0 - aRightEyeRect.Y();
383
0
  bounds.uMax = aRightEyeRect.XMost();
384
0
  bounds.vMax = 1.0 - aRightEyeRect.YMost();
385
0
386
0
  err = mVRCompositor->Submit(::vr::EVREye::Eye_Right, &tex, &bounds);
387
0
  if (err != ::vr::EVRCompositorError::VRCompositorError_None) {
388
0
    printf_stderr("OpenVR Compositor Submit() failed.\n");
389
0
  }
390
0
391
0
  mVRCompositor->PostPresentHandoff();
392
0
  return true;
393
0
}
394
395
#if defined(XP_WIN)
396
397
bool
398
VRDisplayOpenVR::SubmitFrame(ID3D11Texture2D* aSource,
399
                             const IntSize& aSize,
400
                             const gfx::Rect& aLeftEyeRect,
401
                             const gfx::Rect& aRightEyeRect)
402
{
403
  return SubmitFrameOpenVRHandle((void *)aSource,
404
                                 ::vr::ETextureType::TextureType_DirectX,
405
                                 aSize, aLeftEyeRect, aRightEyeRect);
406
}
407
408
#elif defined(XP_MACOSX)
409
410
bool
411
VRDisplayOpenVR::SubmitFrame(MacIOSurface* aMacIOSurface,
412
                             const IntSize& aSize,
413
                             const gfx::Rect& aLeftEyeRect,
414
                             const gfx::Rect& aRightEyeRect)
415
{
416
  const void* ioSurface = aMacIOSurface->GetIOSurfacePtr();
417
  bool result = false;
418
  if (ioSurface == nullptr) {
419
    NS_WARNING("VRDisplayOpenVR::SubmitFrame() could not get an IOSurface");
420
  } else {
421
    result = SubmitFrameOpenVRHandle((void *)ioSurface,
422
                                     ::vr::ETextureType::TextureType_IOSurface,
423
                                     aSize, aLeftEyeRect, aRightEyeRect);
424
  }
425
  return result;
426
}
427
428
#endif
429
430
VRControllerOpenVR::VRControllerOpenVR(dom::GamepadHand aHand, uint32_t aDisplayID,
431
                                       uint32_t aNumButtons, uint32_t aNumTriggers,
432
                                       uint32_t aNumAxes, const nsCString& aId)
433
  : VRControllerHost(VRDeviceType::OpenVR, aHand, aDisplayID)
434
  , mTrackedIndex(0)
435
  , mVibrateThread(nullptr)
436
  , mIsVibrateStopped(false)
437
0
{
438
0
  MOZ_COUNT_CTOR_INHERITED(VRControllerOpenVR, VRControllerHost);
439
0
440
0
  VRControllerState& state = mControllerInfo.mControllerState;
441
0
  strncpy(state.controllerName, aId.BeginReading(), kVRControllerNameMaxLen);
442
0
  state.numButtons = aNumButtons;
443
0
  state.numAxes = aNumAxes;
444
0
  state.numHaptics = kNumOpenVRHaptcs;
445
0
}
446
447
VRControllerOpenVR::~VRControllerOpenVR()
448
0
{
449
0
  ShutdownVibrateHapticThread();
450
0
  MOZ_COUNT_DTOR_INHERITED(VRControllerOpenVR, VRControllerHost);
451
0
}
452
453
void
454
VRControllerOpenVR::SetTrackedIndex(uint32_t aTrackedIndex)
455
0
{
456
0
  mTrackedIndex = aTrackedIndex;
457
0
}
458
459
uint32_t
460
VRControllerOpenVR::GetTrackedIndex()
461
0
{
462
0
  return mTrackedIndex;
463
0
}
464
465
float
466
VRControllerOpenVR::GetAxisMove(uint32_t aAxis)
467
0
{
468
0
  return mControllerInfo.mControllerState.axisValue[aAxis];
469
0
}
470
471
void
472
VRControllerOpenVR::SetAxisMove(uint32_t aAxis, float aValue)
473
0
{
474
0
  mControllerInfo.mControllerState.axisValue[aAxis] = aValue;
475
0
}
476
477
void
478
VRControllerOpenVR::SetTrigger(uint32_t aButton, float aValue)
479
0
{
480
0
  mControllerInfo.mControllerState.triggerValue[aButton] = aValue;
481
0
}
482
483
float
484
VRControllerOpenVR::GetTrigger(uint32_t aButton)
485
0
{
486
0
  return mControllerInfo.mControllerState.triggerValue[aButton];
487
0
}
488
489
void
490
VRControllerOpenVR::SetHand(dom::GamepadHand aHand)
491
0
{
492
0
  mControllerInfo.mControllerState.hand = aHand;
493
0
}
494
495
void
496
VRControllerOpenVR::UpdateVibrateHaptic(::vr::IVRSystem* aVRSystem,
497
                                        uint32_t aHapticIndex,
498
                                        double aIntensity,
499
                                        double aDuration,
500
                                        uint64_t aVibrateIndex,
501
                                        const VRManagerPromise& aPromise)
502
0
{
503
0
  // UpdateVibrateHaptic() only can be called by mVibrateThread
504
0
  MOZ_ASSERT(mVibrateThread->GetThread() == NS_GetCurrentThread());
505
0
506
0
  // It has been interrupted by loss focus.
507
0
  if (mIsVibrateStopped) {
508
0
    VibrateHapticComplete(aPromise);
509
0
    return;
510
0
  }
511
0
  // Avoid the previous vibrate event to override the new one.
512
0
  if (mVibrateIndex != aVibrateIndex) {
513
0
    VibrateHapticComplete(aPromise);
514
0
    return;
515
0
  }
516
0
517
0
  const double duration = (aIntensity == 0) ? 0 : aDuration;
518
0
  // We expect OpenVR to vibrate for 5 ms, but we found it only response the
519
0
  // commend ~ 3.9 ms. For duration time longer than 3.9 ms, we separate them
520
0
  // to a loop of 3.9 ms for make users feel that is a continuous events.
521
0
  const uint32_t microSec = (duration < 3.9 ? duration : 3.9) * 1000 * aIntensity;
522
0
  aVRSystem->TriggerHapticPulse(GetTrackedIndex(),
523
0
                                aHapticIndex, microSec);
524
0
525
0
  // In OpenVR spec, it mentions TriggerHapticPulse() may not trigger another haptic pulse
526
0
  // on this controller and axis combination for 5ms.
527
0
  const double kVibrateRate = 5.0;
528
0
  if (duration >= kVibrateRate) {
529
0
    MOZ_ASSERT(mVibrateThread);
530
0
    MOZ_ASSERT(mVibrateThread->IsActive());
531
0
532
0
    RefPtr<Runnable> runnable =
533
0
      NewRunnableMethod<::vr::IVRSystem*, uint32_t, double, double, uint64_t,
534
0
        StoreCopyPassByConstLRef<VRManagerPromise>>(
535
0
          "VRControllerOpenVR::UpdateVibrateHaptic",
536
0
          this, &VRControllerOpenVR::UpdateVibrateHaptic, aVRSystem,
537
0
          aHapticIndex, aIntensity, duration - kVibrateRate, aVibrateIndex, aPromise);
538
0
    mVibrateThread->PostDelayedTask(runnable.forget(), kVibrateRate);
539
0
  } else {
540
0
    // The pulse has completed
541
0
    VibrateHapticComplete(aPromise);
542
0
  }
543
0
}
544
545
void
546
VRControllerOpenVR::VibrateHapticComplete(const VRManagerPromise& aPromise)
547
0
{
548
0
  VRManager *vm = VRManager::Get();
549
0
  VRListenerThreadHolder::Loop()->PostTask(
550
0
    NewRunnableMethod<StoreCopyPassByConstLRef<VRManagerPromise>>(
551
0
      "VRManager::NotifyVibrateHapticCompleted",
552
0
      vm, &VRManager::NotifyVibrateHapticCompleted, aPromise));
553
0
}
554
555
void
556
VRControllerOpenVR::VibrateHaptic(::vr::IVRSystem* aVRSystem,
557
                                  uint32_t aHapticIndex,
558
                                  double aIntensity,
559
                                  double aDuration,
560
                                  const VRManagerPromise& aPromise)
561
0
{
562
0
  // Spinning up the haptics thread at the first haptics call.
563
0
  if (!mVibrateThread) {
564
0
    mVibrateThread = new VRThread(NS_LITERAL_CSTRING("OpenVR_Vibration"));
565
0
  }
566
0
  mVibrateThread->Start();
567
0
  ++mVibrateIndex;
568
0
  mIsVibrateStopped = false;
569
0
570
0
  RefPtr<Runnable> runnable =
571
0
    NewRunnableMethod<::vr::IVRSystem*, uint32_t, double, double, uint64_t,
572
0
      StoreCopyPassByConstLRef<VRManagerPromise>>(
573
0
        "VRControllerOpenVR::UpdateVibrateHaptic",
574
0
        this, &VRControllerOpenVR::UpdateVibrateHaptic, aVRSystem,
575
0
        aHapticIndex, aIntensity, aDuration, mVibrateIndex, aPromise);
576
0
  mVibrateThread->PostTask(runnable.forget());
577
0
}
578
579
void
580
VRControllerOpenVR::StopVibrateHaptic()
581
0
{
582
0
  mIsVibrateStopped = true;
583
0
}
584
585
void
586
VRControllerOpenVR::ShutdownVibrateHapticThread()
587
0
{
588
0
  StopVibrateHaptic();
589
0
  if (mVibrateThread) {
590
0
    mVibrateThread->Shutdown();
591
0
    mVibrateThread = nullptr;
592
0
  }
593
0
}
594
595
VRSystemManagerOpenVR::VRSystemManagerOpenVR()
596
  : mVRSystem(nullptr)
597
  , mRuntimeCheckFailed(false)
598
  , mIsWindowsMR(false)
599
0
{
600
0
}
601
602
/*static*/ already_AddRefed<VRSystemManagerOpenVR>
603
VRSystemManagerOpenVR::Create()
604
0
{
605
0
  MOZ_ASSERT(NS_IsMainThread());
606
0
607
0
  if (!gfxPrefs::VREnabled() || !gfxPrefs::VROpenVREnabled()) {
608
0
    return nullptr;
609
0
  }
610
0
611
0
  RefPtr<VRSystemManagerOpenVR> manager = new VRSystemManagerOpenVR();
612
0
  return manager.forget();
613
0
}
614
615
void
616
VRSystemManagerOpenVR::Destroy()
617
0
{
618
0
  Shutdown();
619
0
}
620
621
void
622
VRSystemManagerOpenVR::Shutdown()
623
0
{
624
0
  if (mOpenVRHMD) {
625
0
    mOpenVRHMD = nullptr;
626
0
  }
627
0
  RemoveControllers();
628
0
  mVRSystem = nullptr;
629
0
}
630
631
void
632
VRSystemManagerOpenVR::NotifyVSync()
633
0
{
634
0
  VRSystemManager::NotifyVSync();
635
0
636
0
  // Avoid doing anything unless we have already
637
0
  // successfully enumerated and loaded the OpenVR
638
0
  // runtime.
639
0
  if (mVRSystem == nullptr) {
640
0
    return;
641
0
  }
642
0
643
0
  if (mOpenVRHMD) {
644
0
    mOpenVRHMD->Refresh();
645
0
    if (!mOpenVRHMD->GetIsHmdPresent()) {
646
0
      // OpenVR runtime could be quit accidentally
647
0
      // or a device could be disconnected.
648
0
      // We free up resources and must re-initialize
649
0
      // if a device is detected again later.
650
0
      mOpenVRHMD = nullptr;
651
0
      mVRSystem = nullptr;
652
0
    }
653
0
  }
654
0
}
655
656
void
657
VRSystemManagerOpenVR::Enumerate()
658
0
{
659
0
  if (mOpenVRHMD) {
660
0
    // Already enumerated, nothing more to do
661
0
    return;
662
0
  }
663
0
  if (mRuntimeCheckFailed) {
664
0
    // We have already checked for a runtime and
665
0
    // know that its not installed.
666
0
    return;
667
0
  }
668
0
  if (!::vr::VR_IsRuntimeInstalled()) {
669
0
    // Runtime is not installed, remember so we don't
670
0
    // continue to scan for the files
671
0
    mRuntimeCheckFailed = true;
672
0
    return;
673
0
  }
674
0
  if (!::vr::VR_IsHmdPresent()) {
675
0
    // Avoid initializing if no headset is connected
676
0
    return;
677
0
  }
678
0
679
0
  ::vr::HmdError err;
680
0
681
0
  ::vr::VR_Init(&err, ::vr::EVRApplicationType::VRApplication_Scene);
682
0
  if (err) {
683
0
    return;
684
0
  }
685
0
686
0
  ::vr::IVRSystem *system = (::vr::IVRSystem *)::vr::VR_GetGenericInterface(::vr::IVRSystem_Version, &err);
687
0
  if (err || !system) {
688
0
    ::vr::VR_Shutdown();
689
0
    return;
690
0
  }
691
0
  ::vr::IVRChaperone *chaperone = (::vr::IVRChaperone *)::vr::VR_GetGenericInterface(::vr::IVRChaperone_Version, &err);
692
0
  if (err || !chaperone) {
693
0
    ::vr::VR_Shutdown();
694
0
    return;
695
0
  }
696
0
  ::vr::IVRCompositor *compositor = (::vr::IVRCompositor*)::vr::VR_GetGenericInterface(::vr::IVRCompositor_Version, &err);
697
0
  if (err || !compositor) {
698
0
    ::vr::VR_Shutdown();
699
0
    return;
700
0
  }
701
0
702
0
  mVRSystem = system;
703
0
  mOpenVRHMD = new VRDisplayOpenVR(system, chaperone, compositor);
704
0
}
705
706
bool
707
VRSystemManagerOpenVR::ShouldInhibitEnumeration()
708
0
{
709
0
  if (VRSystemManager::ShouldInhibitEnumeration()) {
710
0
    return true;
711
0
  }
712
0
  if (mOpenVRHMD) {
713
0
    // When we find an a VR device, don't
714
0
    // allow any further enumeration as it
715
0
    // may get picked up redundantly by other
716
0
    // API's.
717
0
    return true;
718
0
  }
719
0
  return false;
720
0
}
721
722
void
723
VRSystemManagerOpenVR::GetHMDs(nsTArray<RefPtr<VRDisplayHost>>& aHMDResult)
724
0
{
725
0
  if (mOpenVRHMD) {
726
0
    aHMDResult.AppendElement(mOpenVRHMD);
727
0
  }
728
0
}
729
730
bool
731
VRSystemManagerOpenVR::GetIsPresenting()
732
0
{
733
0
  if (mOpenVRHMD) {
734
0
    VRDisplayInfo displayInfo(mOpenVRHMD->GetDisplayInfo());
735
0
    return displayInfo.GetPresentingGroups() != kVRGroupNone;
736
0
  }
737
0
738
0
  return false;
739
0
}
740
741
void
742
VRSystemManagerOpenVR::HandleInput()
743
0
{
744
0
  // mVRSystem is available after VRDisplay is created
745
0
  // at GetHMDs().
746
0
  if (!mVRSystem) {
747
0
    return;
748
0
  }
749
0
750
0
  RefPtr<impl::VRControllerOpenVR> controller;
751
0
  // Compare with Edge, we have a wrong implementation for the vertical axis value.
752
0
  // In order to not affect the current VR content, we add a workaround for yAxis.
753
0
  const float yAxisInvert = (mIsWindowsMR) ? -1.0f : 1.0f;
754
0
  ::vr::VRControllerState_t state;
755
0
  ::vr::TrackedDevicePose_t poses[::vr::k_unMaxTrackedDeviceCount];
756
0
  mVRSystem->GetDeviceToAbsoluteTrackingPose(::vr::TrackingUniverseSeated, 0.0f,
757
0
                                             poses, ::vr::k_unMaxTrackedDeviceCount);
758
0
  // Process OpenVR controller state
759
0
  for (uint32_t i = 0; i < mOpenVRController.Length(); ++i) {
760
0
    uint32_t axisIdx = 0;
761
0
    uint32_t buttonIdx = 0;
762
0
    uint32_t triggerIdx = 0;
763
0
    controller = mOpenVRController[i];
764
0
    const uint32_t trackedIndex = controller->GetTrackedIndex();
765
0
766
0
    MOZ_ASSERT(mVRSystem->GetTrackedDeviceClass(trackedIndex)
767
0
               == ::vr::TrackedDeviceClass_Controller ||
768
0
               mVRSystem->GetTrackedDeviceClass(trackedIndex)
769
0
               == ::vr::TrackedDeviceClass_GenericTracker);
770
0
771
0
    // Sometimes, OpenVR controllers are not located by HMD at the initial time.
772
0
    // That makes us have to update the hand info at runtime although switching controllers
773
0
    // to the other hand does not have new changes at the current OpenVR SDK. But, it makes sense
774
0
    // to detect hand changing at runtime.
775
0
    const ::vr::ETrackedControllerRole role = mVRSystem->
776
0
                                                GetControllerRoleForTrackedDeviceIndex(
777
0
                                                trackedIndex);
778
0
    const dom::GamepadHand hand = GetGamepadHandFromControllerRole(role);
779
0
    if (hand != controller->GetHand()) {
780
0
      controller->SetHand(hand);
781
0
      NewHandChangeEvent(i, hand);
782
0
    }
783
0
784
0
    if (mVRSystem->GetControllerState(trackedIndex, &state, sizeof(state))) {
785
0
      for (uint32_t j = 0; j < ::vr::k_unControllerStateAxisCount; ++j) {
786
0
        const uint32_t axisType = mVRSystem->GetInt32TrackedDeviceProperty(
787
0
                                   trackedIndex,
788
0
                                   static_cast<::vr::TrackedDeviceProperty>(
789
0
                                   ::vr::Prop_Axis0Type_Int32 + j));
790
0
        switch (axisType) {
791
0
          case ::vr::EVRControllerAxisType::k_eControllerAxis_Joystick:
792
0
          case ::vr::EVRControllerAxisType::k_eControllerAxis_TrackPad:
793
0
            if (mIsWindowsMR) {
794
0
              // Adjust the input mapping for Windows MR which has
795
0
              // different order.
796
0
              axisIdx = (axisIdx == 0) ? 2 : 0;
797
0
              buttonIdx = (buttonIdx == 0) ? 4 : 0;
798
0
            }
799
0
800
0
            HandleAxisMove(i, axisIdx,
801
0
                           state.rAxis[j].x);
802
0
            ++axisIdx;
803
0
            HandleAxisMove(i, axisIdx,
804
0
                           state.rAxis[j].y * yAxisInvert);
805
0
            ++axisIdx;
806
0
            HandleButtonPress(i, buttonIdx,
807
0
                              ::vr::ButtonMaskFromId(
808
0
                                 static_cast<::vr::EVRButtonId>(::vr::k_EButton_Axis0 + j)),
809
0
                                 state.ulButtonPressed, state.ulButtonTouched);
810
0
            ++buttonIdx;
811
0
812
0
            if (mIsWindowsMR) {
813
0
              axisIdx = (axisIdx == 4) ? 2 : 4;
814
0
              buttonIdx = (buttonIdx == 5) ? 1 : 2;
815
0
            }
816
0
            break;
817
0
          case vr::EVRControllerAxisType::k_eControllerAxis_Trigger:
818
0
            if (j <= 2) {
819
0
              HandleTriggerPress(i, buttonIdx, triggerIdx, state.rAxis[j].x);
820
0
              ++buttonIdx;
821
0
              ++triggerIdx;
822
0
            } else {
823
0
              // For SteamVR Knuckles.
824
0
              HandleTriggerPress(i, buttonIdx, triggerIdx, state.rAxis[j].x);
825
0
              ++buttonIdx;
826
0
              ++triggerIdx;
827
0
              HandleTriggerPress(i, buttonIdx, triggerIdx, state.rAxis[j].y);
828
0
              ++buttonIdx;
829
0
              ++triggerIdx;
830
0
            }
831
0
            break;
832
0
        }
833
0
      }
834
0
      MOZ_ASSERT(axisIdx ==
835
0
                 controller->GetControllerInfo().GetNumAxes());
836
0
837
0
      const uint64_t supportedButtons = mVRSystem->GetUint64TrackedDeviceProperty(
838
0
                                         trackedIndex, ::vr::Prop_SupportedButtons_Uint64);
839
0
      if (supportedButtons &
840
0
          BTN_MASK_FROM_ID(k_EButton_A)) {
841
0
        HandleButtonPress(i, buttonIdx,
842
0
                          BTN_MASK_FROM_ID(k_EButton_A),
843
0
                          state.ulButtonPressed, state.ulButtonTouched);
844
0
        ++buttonIdx;
845
0
      }
846
0
      if (supportedButtons &
847
0
          BTN_MASK_FROM_ID(k_EButton_Grip)) {
848
0
        HandleButtonPress(i, buttonIdx,
849
0
                          BTN_MASK_FROM_ID(k_EButton_Grip),
850
0
                          state.ulButtonPressed, state.ulButtonTouched);
851
0
        ++buttonIdx;
852
0
      }
853
0
      if (supportedButtons &
854
0
          BTN_MASK_FROM_ID(k_EButton_ApplicationMenu)) {
855
0
        HandleButtonPress(i, buttonIdx,
856
0
                          BTN_MASK_FROM_ID(k_EButton_ApplicationMenu),
857
0
                          state.ulButtonPressed, state.ulButtonTouched);
858
0
        ++buttonIdx;
859
0
      }
860
0
      if (mIsWindowsMR) {
861
0
        // button 4 in Windows MR has already been assigned
862
0
        // to k_eControllerAxis_TrackPad.
863
0
        ++buttonIdx;
864
0
      }
865
0
      if (supportedButtons &
866
0
          BTN_MASK_FROM_ID(k_EButton_DPad_Left)) {
867
0
        HandleButtonPress(i, buttonIdx,
868
0
                          BTN_MASK_FROM_ID(k_EButton_DPad_Left),
869
0
                          state.ulButtonPressed, state.ulButtonTouched);
870
0
        ++buttonIdx;
871
0
      }
872
0
      if (supportedButtons &
873
0
          BTN_MASK_FROM_ID(k_EButton_DPad_Up)) {
874
0
        HandleButtonPress(i, buttonIdx,
875
0
                          BTN_MASK_FROM_ID(k_EButton_DPad_Up),
876
0
                          state.ulButtonPressed, state.ulButtonTouched);
877
0
        ++buttonIdx;
878
0
      }
879
0
      if (supportedButtons &
880
0
          BTN_MASK_FROM_ID(k_EButton_DPad_Right)) {
881
0
        HandleButtonPress(i, buttonIdx,
882
0
                          BTN_MASK_FROM_ID(k_EButton_DPad_Right),
883
0
                          state.ulButtonPressed, state.ulButtonTouched);
884
0
        ++buttonIdx;
885
0
      }
886
0
      if (supportedButtons &
887
0
          BTN_MASK_FROM_ID(k_EButton_DPad_Down)) {
888
0
        HandleButtonPress(i, buttonIdx,
889
0
                          BTN_MASK_FROM_ID(k_EButton_DPad_Down),
890
0
                          state.ulButtonPressed, state.ulButtonTouched);
891
0
        ++buttonIdx;
892
0
      }
893
0
      MOZ_ASSERT(buttonIdx ==
894
0
                 controller->GetControllerInfo().GetNumButtons());
895
0
      controller->SetButtonPressed(state.ulButtonPressed);
896
0
      controller->SetButtonTouched(state.ulButtonTouched);
897
0
898
0
      // Start to process pose
899
0
      const ::vr::TrackedDevicePose_t& pose = poses[trackedIndex];
900
0
      GamepadPoseState poseState;
901
0
902
0
      if (pose.bDeviceIsConnected) {
903
0
        poseState.flags |= (GamepadCapabilityFlags::Cap_Orientation |
904
0
                            GamepadCapabilityFlags::Cap_Position);
905
0
      }
906
0
907
0
      if (pose.bPoseIsValid &&
908
0
          pose.eTrackingResult == ::vr::TrackingResult_Running_OK) {
909
0
        gfx::Matrix4x4 m;
910
0
911
0
        // NOTE! mDeviceToAbsoluteTracking is a 3x4 matrix, not 4x4.  But
912
0
        // because of its arrangement, we can copy the 12 elements in and
913
0
        // then transpose them to the right place.  We do this so we can
914
0
        // pull out a Quaternion.
915
0
        memcpy(&m.components, &pose.mDeviceToAbsoluteTracking, sizeof(pose.mDeviceToAbsoluteTracking));
916
0
        m.Transpose();
917
0
918
0
        gfx::Quaternion rot;
919
0
        rot.SetFromRotationMatrix(m);
920
0
        rot.Invert();
921
0
922
0
        poseState.orientation[0] = rot.x;
923
0
        poseState.orientation[1] = rot.y;
924
0
        poseState.orientation[2] = rot.z;
925
0
        poseState.orientation[3] = rot.w;
926
0
        poseState.angularVelocity[0] = pose.vAngularVelocity.v[0];
927
0
        poseState.angularVelocity[1] = pose.vAngularVelocity.v[1];
928
0
        poseState.angularVelocity[2] = pose.vAngularVelocity.v[2];
929
0
        poseState.isOrientationValid = true;
930
0
931
0
        poseState.position[0] = m._41;
932
0
        poseState.position[1] = m._42;
933
0
        poseState.position[2] = m._43;
934
0
        poseState.linearVelocity[0] = pose.vVelocity.v[0];
935
0
        poseState.linearVelocity[1] = pose.vVelocity.v[1];
936
0
        poseState.linearVelocity[2] = pose.vVelocity.v[2];
937
0
        poseState.isPositionValid = true;
938
0
      }
939
0
      HandlePoseTracking(i, poseState, controller);
940
0
    }
941
0
  }
942
0
}
943
944
void
945
VRSystemManagerOpenVR::HandleButtonPress(uint32_t aControllerIdx,
946
                                         uint32_t aButton,
947
                                         uint64_t aButtonMask,
948
                                         uint64_t aButtonPressed,
949
                                         uint64_t aButtonTouched)
950
0
{
951
0
  RefPtr<impl::VRControllerOpenVR> controller(mOpenVRController[aControllerIdx]);
952
0
  MOZ_ASSERT(controller);
953
0
  const uint64_t pressedDiff = (controller->GetButtonPressed() ^ aButtonPressed);
954
0
  const uint64_t touchedDiff = (controller->GetButtonTouched() ^ aButtonTouched);
955
0
956
0
  if (!pressedDiff && !touchedDiff) {
957
0
    return;
958
0
  }
959
0
960
0
  if (pressedDiff & aButtonMask ||
961
0
      touchedDiff & aButtonMask) {
962
0
    // diff & (aButtonPressed, aButtonTouched) would be true while a new button pressed or
963
0
    // touched event, otherwise it is an old event and needs to notify
964
0
    // the button has been released.
965
0
    NewButtonEvent(aControllerIdx, aButton, aButtonMask & aButtonPressed,
966
0
                   aButtonMask & aButtonTouched,
967
0
                   (aButtonMask & aButtonPressed) ? 1.0L : 0.0L);
968
0
  }
969
0
}
970
971
void
972
VRSystemManagerOpenVR::HandleTriggerPress(uint32_t aControllerIdx,
973
                                          uint32_t aButton,
974
                                          uint32_t aTrigger,
975
                                          float aValue)
976
0
{
977
0
  RefPtr<impl::VRControllerOpenVR> controller(mOpenVRController[aControllerIdx]);
978
0
  MOZ_ASSERT(controller);
979
0
  const float oldValue = controller->GetTrigger(aTrigger);
980
0
  // For OpenVR, the threshold value of ButtonPressed and ButtonTouched is 0.55.
981
0
  // We prefer to let developers to set their own threshold for the adjustment.
982
0
  // Therefore, we don't check ButtonPressed and ButtonTouched with ButtonMask here.
983
0
  // we just check the button value is larger than the threshold value or not.
984
0
  const float threshold = gfxPrefs::VRControllerTriggerThreshold();
985
0
986
0
  // Avoid sending duplicated events in IPC channels.
987
0
  if (oldValue != aValue) {
988
0
    NewButtonEvent(aControllerIdx, aButton, aValue > threshold,
989
0
                   aValue > threshold, aValue);
990
0
    controller->SetTrigger(aTrigger, aValue);
991
0
  }
992
0
}
993
994
void
995
VRSystemManagerOpenVR::HandleAxisMove(uint32_t aControllerIdx, uint32_t aAxis,
996
                                      float aValue)
997
0
{
998
0
  RefPtr<impl::VRControllerOpenVR> controller(mOpenVRController[aControllerIdx]);
999
0
  MOZ_ASSERT(controller);
1000
0
1001
0
  if (controller->GetAxisMove(aAxis) != aValue) {
1002
0
    NewAxisMove(aControllerIdx, aAxis, aValue);
1003
0
    controller->SetAxisMove(aAxis, aValue);
1004
0
  }
1005
0
}
1006
1007
void
1008
VRSystemManagerOpenVR::HandlePoseTracking(uint32_t aControllerIdx,
1009
                                          const GamepadPoseState& aPose,
1010
                                          VRControllerHost* aController)
1011
0
{
1012
0
  MOZ_ASSERT(aController);
1013
0
  if (aPose != aController->GetPose()) {
1014
0
    aController->SetPose(aPose);
1015
0
    NewPoseState(aControllerIdx, aPose);
1016
0
  }
1017
0
}
1018
1019
dom::GamepadHand
1020
VRSystemManagerOpenVR::GetGamepadHandFromControllerRole(
1021
                                          ::vr::ETrackedControllerRole aRole)
1022
0
{
1023
0
  dom::GamepadHand hand;
1024
0
1025
0
  switch(aRole) {
1026
0
    case ::vr::ETrackedControllerRole::TrackedControllerRole_Invalid:
1027
0
    case ::vr::ETrackedControllerRole::TrackedControllerRole_OptOut:
1028
0
      hand = dom::GamepadHand::_empty;
1029
0
      break;
1030
0
    case ::vr::ETrackedControllerRole::TrackedControllerRole_LeftHand:
1031
0
      hand = dom::GamepadHand::Left;
1032
0
      break;
1033
0
    case ::vr::ETrackedControllerRole::TrackedControllerRole_RightHand:
1034
0
      hand = dom::GamepadHand::Right;
1035
0
      break;
1036
0
    default:
1037
0
      hand = dom::GamepadHand::_empty;
1038
0
      MOZ_ASSERT(false);
1039
0
      break;
1040
0
  }
1041
0
1042
0
  return hand;
1043
0
}
1044
1045
void
1046
VRSystemManagerOpenVR::VibrateHaptic(uint32_t aControllerIdx,
1047
                                     uint32_t aHapticIndex,
1048
                                     double aIntensity,
1049
                                     double aDuration,
1050
                                     const VRManagerPromise& aPromise)
1051
0
{
1052
0
  // mVRSystem is available after VRDisplay is created
1053
0
  // at GetHMDs().
1054
0
  if (!mVRSystem || (aControllerIdx >= mOpenVRController.Length())) {
1055
0
    return;
1056
0
  }
1057
0
1058
0
  RefPtr<impl::VRControllerOpenVR> controller = mOpenVRController[aControllerIdx];
1059
0
  MOZ_ASSERT(controller);
1060
0
1061
0
  controller->VibrateHaptic(mVRSystem, aHapticIndex, aIntensity, aDuration, aPromise);
1062
0
}
1063
1064
void
1065
VRSystemManagerOpenVR::StopVibrateHaptic(uint32_t aControllerIdx)
1066
0
{
1067
0
  // mVRSystem is available after VRDisplay is created
1068
0
  // at GetHMDs().
1069
0
  if (!mVRSystem || (aControllerIdx >= mOpenVRController.Length())) {
1070
0
    return;
1071
0
  }
1072
0
1073
0
  RefPtr<impl::VRControllerOpenVR> controller = mOpenVRController[aControllerIdx];
1074
0
  MOZ_ASSERT(controller);
1075
0
1076
0
  controller->StopVibrateHaptic();
1077
0
}
1078
1079
void
1080
VRSystemManagerOpenVR::GetControllers(nsTArray<RefPtr<VRControllerHost>>& aControllerResult)
1081
0
{
1082
0
  aControllerResult.Clear();
1083
0
  for (uint32_t i = 0; i < mOpenVRController.Length(); ++i) {
1084
0
    aControllerResult.AppendElement(mOpenVRController[i]);
1085
0
  }
1086
0
}
1087
1088
void
1089
VRSystemManagerOpenVR::ScanForControllers()
1090
0
{
1091
0
  // mVRSystem is available after VRDisplay is created
1092
0
  // at GetHMDs().
1093
0
  if (!mVRSystem) {
1094
0
    return;
1095
0
  }
1096
0
1097
0
  ::vr::TrackedDeviceIndex_t trackedIndexArray[::vr::k_unMaxTrackedDeviceCount];
1098
0
  uint32_t newControllerCount = 0;
1099
0
  // Basically, we would have HMDs in the tracked devices,
1100
0
  // but we are just interested in the controllers.
1101
0
  for (::vr::TrackedDeviceIndex_t trackedDevice = ::vr::k_unTrackedDeviceIndex_Hmd + 1;
1102
0
       trackedDevice < ::vr::k_unMaxTrackedDeviceCount; ++trackedDevice) {
1103
0
1104
0
    if (!mVRSystem->IsTrackedDeviceConnected(trackedDevice)) {
1105
0
      continue;
1106
0
    }
1107
0
1108
0
    const ::vr::ETrackedDeviceClass deviceType = mVRSystem->
1109
0
                                                 GetTrackedDeviceClass(trackedDevice);
1110
0
    if (deviceType != ::vr::TrackedDeviceClass_Controller
1111
0
        && deviceType != ::vr::TrackedDeviceClass_GenericTracker) {
1112
0
      continue;
1113
0
    }
1114
0
1115
0
    trackedIndexArray[newControllerCount] = trackedDevice;
1116
0
    ++newControllerCount;
1117
0
  }
1118
0
1119
0
  if (newControllerCount != mControllerCount) {
1120
0
    RemoveControllers();
1121
0
1122
0
    // Re-adding controllers to VRControllerManager.
1123
0
    for (::vr::TrackedDeviceIndex_t i = 0; i < newControllerCount; ++i) {
1124
0
      const ::vr::TrackedDeviceIndex_t trackedDevice = trackedIndexArray[i];
1125
0
      const ::vr::ETrackedDeviceClass deviceType = mVRSystem->
1126
0
                                                   GetTrackedDeviceClass(trackedDevice);
1127
0
      const ::vr::ETrackedControllerRole role = mVRSystem->
1128
0
                                                GetControllerRoleForTrackedDeviceIndex(
1129
0
                                                trackedDevice);
1130
0
      const GamepadHand hand = GetGamepadHandFromControllerRole(role);
1131
0
      uint32_t numButtons = 0;
1132
0
      uint32_t numTriggers = 0;
1133
0
      uint32_t numAxes = 0;
1134
0
1135
0
      // Scan the axes that the controllers support
1136
0
      for (uint32_t j = 0; j < ::vr::k_unControllerStateAxisCount; ++j) {
1137
0
        const uint32_t supportAxis = mVRSystem->GetInt32TrackedDeviceProperty(trackedDevice,
1138
0
                                      static_cast<vr::TrackedDeviceProperty>(
1139
0
                                      ::vr::Prop_Axis0Type_Int32 + j));
1140
0
        switch (supportAxis) {
1141
0
          case ::vr::EVRControllerAxisType::k_eControllerAxis_Joystick:
1142
0
          case ::vr::EVRControllerAxisType::k_eControllerAxis_TrackPad:
1143
0
            numAxes += 2; // It has x and y axes.
1144
0
            ++numButtons;
1145
0
            break;
1146
0
          case ::vr::k_eControllerAxis_Trigger:
1147
0
            if (j <= 2) {
1148
0
              ++numButtons;
1149
0
              ++numTriggers;
1150
0
            } else {
1151
          #ifdef DEBUG
1152
              // SteamVR Knuckles is the only special case for using 2D axis values on triggers.
1153
              ::vr::ETrackedPropertyError err;
1154
              uint32_t requiredBufferLen;
1155
              char charBuf[128];
1156
              requiredBufferLen = mVRSystem->GetStringTrackedDeviceProperty(trackedDevice,
1157
                                  ::vr::Prop_RenderModelName_String, charBuf, 128, &err);
1158
              MOZ_ASSERT(requiredBufferLen && err == ::vr::TrackedProp_Success);
1159
              nsCString deviceId(charBuf);
1160
              MOZ_ASSERT(deviceId.Find("knuckles") != kNotFound);
1161
          #endif // #ifdef DEBUG
1162
              numButtons += 2;
1163
0
              numTriggers += 2;
1164
0
            }
1165
0
            break;
1166
0
        }
1167
0
      }
1168
0
1169
0
      // Scan the buttons that the controllers support
1170
0
      const uint64_t supportButtons = mVRSystem->GetUint64TrackedDeviceProperty(
1171
0
                                       trackedDevice, ::vr::Prop_SupportedButtons_Uint64);
1172
0
      if (supportButtons &
1173
0
          BTN_MASK_FROM_ID(k_EButton_A)) {
1174
0
        ++numButtons;
1175
0
      }
1176
0
      if (supportButtons &
1177
0
          BTN_MASK_FROM_ID(k_EButton_Grip)) {
1178
0
        ++numButtons;
1179
0
      }
1180
0
      if (supportButtons &
1181
0
          BTN_MASK_FROM_ID(k_EButton_ApplicationMenu)) {
1182
0
        ++numButtons;
1183
0
      }
1184
0
      if (supportButtons &
1185
0
          BTN_MASK_FROM_ID(k_EButton_DPad_Left)) {
1186
0
        ++numButtons;
1187
0
      }
1188
0
      if (supportButtons &
1189
0
          BTN_MASK_FROM_ID(k_EButton_DPad_Up)) {
1190
0
        ++numButtons;
1191
0
      }
1192
0
      if (supportButtons &
1193
0
          BTN_MASK_FROM_ID(k_EButton_DPad_Right)) {
1194
0
        ++numButtons;
1195
0
      }
1196
0
      if (supportButtons &
1197
0
          BTN_MASK_FROM_ID(k_EButton_DPad_Down)) {
1198
0
        ++numButtons;
1199
0
      }
1200
0
1201
0
      nsCString deviceId;
1202
0
      GetControllerDeviceId(deviceType, trackedDevice, deviceId);
1203
0
      RefPtr<VRControllerOpenVR> openVRController =
1204
0
        new VRControllerOpenVR(hand, mOpenVRHMD->GetDisplayInfo().GetDisplayID(),
1205
0
                               numButtons, numTriggers, numAxes, deviceId);
1206
0
      openVRController->SetTrackedIndex(trackedDevice);
1207
0
      mOpenVRController.AppendElement(openVRController);
1208
0
1209
0
      // If the Windows MR controller doesn't has the amount
1210
0
      // of buttons or axes as our expectation, switching off
1211
0
      // the workaround for Windows MR.
1212
0
      if (mIsWindowsMR && (numAxes < 4 || numButtons < 5)) {
1213
0
        mIsWindowsMR = false;
1214
0
        NS_WARNING("OpenVR - Switching off Windows MR mode.");
1215
0
      }
1216
0
      // Not already present, add it.
1217
0
      AddGamepad(openVRController->GetControllerInfo());
1218
0
      ++mControllerCount;
1219
0
    }
1220
0
  }
1221
0
}
1222
1223
void
1224
VRSystemManagerOpenVR::RemoveControllers()
1225
0
{
1226
0
  // The controller count is changed, removing the existing gamepads first.
1227
0
  for (uint32_t i = 0; i < mOpenVRController.Length(); ++i) {
1228
0
    mOpenVRController[i]->ShutdownVibrateHapticThread();
1229
0
    RemoveGamepad(i);
1230
0
  }
1231
0
  mOpenVRController.Clear();
1232
0
  mControllerCount = 0;
1233
0
}
1234
1235
void
1236
VRSystemManagerOpenVR::GetControllerDeviceId(::vr::ETrackedDeviceClass aDeviceType,
1237
                                             ::vr::TrackedDeviceIndex_t aDeviceIndex, nsCString& aId)
1238
0
{
1239
0
  switch (aDeviceType) {
1240
0
    case ::vr::TrackedDeviceClass_Controller:
1241
0
    {
1242
0
      ::vr::ETrackedPropertyError err;
1243
0
      uint32_t requiredBufferLen;
1244
0
      bool isFound = false;
1245
0
      char charBuf[128];
1246
0
      requiredBufferLen = mVRSystem->GetStringTrackedDeviceProperty(aDeviceIndex,
1247
0
                          ::vr::Prop_RenderModelName_String, charBuf, 128, &err);
1248
0
      if (requiredBufferLen > 128) {
1249
0
        MOZ_CRASH("Larger than the buffer size.");
1250
0
      }
1251
0
      MOZ_ASSERT(requiredBufferLen && err == ::vr::TrackedProp_Success);
1252
0
      nsCString deviceId(charBuf);
1253
0
      if (deviceId.Find("knuckles") != kNotFound) {
1254
0
        aId.AssignLiteral("OpenVR Knuckles");
1255
0
        isFound = true;
1256
0
      }
1257
0
      requiredBufferLen = mVRSystem->GetStringTrackedDeviceProperty(aDeviceIndex,
1258
0
        ::vr::Prop_SerialNumber_String, charBuf, 128, &err);
1259
0
      if (requiredBufferLen > 128) {
1260
0
        MOZ_CRASH("Larger than the buffer size.");
1261
0
      }
1262
0
      MOZ_ASSERT(requiredBufferLen && err == ::vr::TrackedProp_Success);
1263
0
      deviceId.Assign(charBuf);
1264
0
      if (deviceId.Find("MRSOURCE") != kNotFound) {
1265
0
        aId.AssignLiteral("Spatial Controller (Spatial Interaction Source) ");
1266
0
        mIsWindowsMR = true;
1267
0
        isFound = true;
1268
0
      }
1269
0
      if (!isFound) {
1270
0
        aId.AssignLiteral("OpenVR Gamepad");
1271
0
      }
1272
0
      break;
1273
0
    }
1274
0
    case ::vr::TrackedDeviceClass_GenericTracker:
1275
0
    {
1276
0
      aId.AssignLiteral("OpenVR Tracker");
1277
0
      break;
1278
0
    }
1279
0
    default:
1280
0
      MOZ_ASSERT(false);
1281
0
      break;
1282
0
  }
1283
0
}