Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/gfx/vr/service/OpenVRSession.cpp
Line
Count
Source (jump to first uncovered line)
1
#include "OpenVRSession.h"
2
#include "gfxPrefs.h"
3
4
#if defined(XP_WIN)
5
#include <d3d11.h>
6
#include "mozilla/gfx/DeviceManagerDx.h"
7
#endif // defined(XP_WIN)
8
9
#include "mozilla/dom/GamepadEventTypes.h"
10
#include "mozilla/dom/GamepadBinding.h"
11
12
#if !defined(M_PI)
13
#define M_PI 3.14159265358979323846264338327950288
14
#endif
15
16
#define BTN_MASK_FROM_ID(_id) \
17
0
  ::vr::ButtonMaskFromId(vr::EVRButtonId::_id)
18
19
static const uint32_t kNumOpenVRHaptcs = 1;
20
21
using namespace mozilla::gfx;
22
23
namespace mozilla {
24
namespace gfx {
25
26
namespace {
27
28
dom::GamepadHand
29
GetControllerHandFromControllerRole(::vr::ETrackedControllerRole aRole)
30
0
{
31
0
  dom::GamepadHand hand;
32
0
33
0
  switch(aRole) {
34
0
    case ::vr::ETrackedControllerRole::TrackedControllerRole_Invalid:
35
0
    case ::vr::ETrackedControllerRole::TrackedControllerRole_OptOut:
36
0
      hand = dom::GamepadHand::_empty;
37
0
      break;
38
0
    case ::vr::ETrackedControllerRole::TrackedControllerRole_LeftHand:
39
0
      hand = dom::GamepadHand::Left;
40
0
      break;
41
0
    case ::vr::ETrackedControllerRole::TrackedControllerRole_RightHand:
42
0
      hand = dom::GamepadHand::Right;
43
0
      break;
44
0
    default:
45
0
      hand = dom::GamepadHand::_empty;
46
0
      MOZ_ASSERT(false);
47
0
      break;
48
0
  }
49
0
50
0
  return hand;
51
0
}
52
53
54
void
55
UpdateButton(VRControllerState& aState, const ::vr::VRControllerState_t& aControllerState, uint32_t aButtonIndex, uint64_t aButtonMask)
56
0
{
57
0
  uint64_t mask = (1ULL << aButtonIndex);
58
0
  if ((aControllerState.ulButtonPressed & aButtonMask) == 0) {
59
0
    // not pressed
60
0
    aState.buttonPressed &= ~mask;
61
0
    aState.triggerValue[aButtonIndex] = 0.0f;
62
0
  } else {
63
0
    // pressed
64
0
    aState.buttonPressed |= mask;
65
0
    aState.triggerValue[aButtonIndex] = 1.0f;
66
0
  }
67
0
  if ((aControllerState.ulButtonTouched & aButtonMask) == 0) {
68
0
    // not touched
69
0
    aState.buttonTouched &= ~mask;
70
0
  } else {
71
0
    // touched
72
0
    aState.buttonTouched |= mask;
73
0
  }
74
0
}
75
76
void
77
UpdateTrigger(VRControllerState& aState, uint32_t aButtonIndex, float aValue, float aThreshold)
78
0
{
79
0
  // For OpenVR, the threshold value of ButtonPressed and ButtonTouched is 0.55.
80
0
  // We prefer to let developers to set their own threshold for the adjustment.
81
0
  // Therefore, we don't check ButtonPressed and ButtonTouched with ButtonMask here.
82
0
  // we just check the button value is larger than the threshold value or not.
83
0
  uint64_t mask = (1ULL << aButtonIndex);
84
0
  aState.triggerValue[aButtonIndex] = aValue;
85
0
  if (aValue > aThreshold) {
86
0
    aState.buttonPressed |= mask;
87
0
    aState.buttonTouched |= mask;
88
0
  } else {
89
0
    aState.buttonPressed &= ~mask;
90
0
    aState.buttonTouched &= ~mask;
91
0
  }
92
0
}
93
94
}; // anonymous namespace
95
96
OpenVRSession::OpenVRSession()
97
  : VRSession()
98
  , mVRSystem(nullptr)
99
  , mVRChaperone(nullptr)
100
  , mVRCompositor(nullptr)
101
  , mControllerDeviceIndex{0}
102
  , mShouldQuit(false)
103
  , mIsWindowsMR(false)
104
0
{
105
0
}
106
107
OpenVRSession::~OpenVRSession()
108
0
{
109
0
  Shutdown();
110
0
}
111
112
bool
113
OpenVRSession::Initialize(mozilla::gfx::VRSystemState& aSystemState)
114
0
{
115
0
  if (mVRSystem != nullptr) {
116
0
    // Already initialized
117
0
    return true;
118
0
  }
119
0
  if (!::vr::VR_IsRuntimeInstalled()) {
120
0
    return false;
121
0
  }
122
0
  if (!::vr::VR_IsHmdPresent()) {
123
0
    return false;
124
0
  }
125
0
126
0
  ::vr::HmdError err;
127
0
128
0
  ::vr::VR_Init(&err, ::vr::EVRApplicationType::VRApplication_Scene);
129
0
  if (err) {
130
0
    return false;
131
0
  }
132
0
133
0
  mVRSystem = (::vr::IVRSystem *)::vr::VR_GetGenericInterface(::vr::IVRSystem_Version, &err);
134
0
  if (err || !mVRSystem) {
135
0
    Shutdown();
136
0
    return false;
137
0
  }
138
0
  mVRChaperone = (::vr::IVRChaperone *)::vr::VR_GetGenericInterface(::vr::IVRChaperone_Version, &err);
139
0
  if (err || !mVRChaperone) {
140
0
    Shutdown();
141
0
    return false;
142
0
  }
143
0
  mVRCompositor = (::vr::IVRCompositor*)::vr::VR_GetGenericInterface(::vr::IVRCompositor_Version, &err);
144
0
  if (err || !mVRCompositor) {
145
0
    Shutdown();
146
0
    return false;
147
0
  }
148
0
149
#if defined(XP_WIN)
150
  if (!CreateD3DObjects()) {
151
    Shutdown();
152
    return false;
153
  }
154
155
#endif
156
157
0
  // Configure coordinate system
158
0
  mVRCompositor->SetTrackingSpace(::vr::TrackingUniverseSeated);
159
0
160
0
  if (!InitState(aSystemState)) {
161
0
    Shutdown();
162
0
    return false;
163
0
  }
164
0
165
0
  // Succeeded
166
0
  return true;
167
0
}
168
169
#if defined(XP_WIN)
170
bool
171
OpenVRSession::CreateD3DObjects()
172
{
173
  RefPtr<ID3D11Device> device = gfx::DeviceManagerDx::Get()->GetVRDevice();
174
  if (!device) {
175
    return false;
176
  }
177
  if (!CreateD3DContext(device)) {
178
    return false;
179
  }
180
  return true;
181
}
182
#endif
183
184
void
185
OpenVRSession::Shutdown()
186
0
{
187
0
  if (mVRSystem || mVRCompositor || mVRSystem) {
188
0
    ::vr::VR_Shutdown();
189
0
    mVRCompositor = nullptr;
190
0
    mVRChaperone = nullptr;
191
0
    mVRSystem = nullptr;
192
0
  }
193
0
}
194
195
bool
196
OpenVRSession::InitState(VRSystemState& aSystemState)
197
0
{
198
0
  VRDisplayState& state = aSystemState.displayState;
199
0
  strncpy(state.mDisplayName, "OpenVR HMD", kVRDisplayNameMaxLen);
200
0
  state.mIsConnected = mVRSystem->IsTrackedDeviceConnected(::vr::k_unTrackedDeviceIndex_Hmd);
201
0
  state.mIsMounted = false;
202
0
  state.mCapabilityFlags = (VRDisplayCapabilityFlags)((int)VRDisplayCapabilityFlags::Cap_None |
203
0
    (int)VRDisplayCapabilityFlags::Cap_Orientation |
204
0
    (int)VRDisplayCapabilityFlags::Cap_Position |
205
0
    (int)VRDisplayCapabilityFlags::Cap_External |
206
0
    (int)VRDisplayCapabilityFlags::Cap_Present |
207
0
    (int)VRDisplayCapabilityFlags::Cap_StageParameters);
208
0
209
0
  ::vr::ETrackedPropertyError err;
210
0
  bool bHasProximitySensor = mVRSystem->GetBoolTrackedDeviceProperty(::vr::k_unTrackedDeviceIndex_Hmd, ::vr::Prop_ContainsProximitySensor_Bool, &err);
211
0
  if (err == ::vr::TrackedProp_Success && bHasProximitySensor) {
212
0
    state.mCapabilityFlags = (VRDisplayCapabilityFlags)((int)state.mCapabilityFlags | (int)VRDisplayCapabilityFlags::Cap_MountDetection);
213
0
  }
214
0
215
0
  uint32_t w, h;
216
0
  mVRSystem->GetRecommendedRenderTargetSize(&w, &h);
217
0
  state.mEyeResolution.width = w;
218
0
  state.mEyeResolution.height = h;
219
0
220
0
  // default to an identity quaternion
221
0
  aSystemState.sensorState.pose.orientation[3] = 1.0f;
222
0
223
0
  UpdateStageParameters(state);
224
0
  UpdateEyeParameters(aSystemState);
225
0
226
0
  VRHMDSensorState& sensorState = aSystemState.sensorState;
227
0
  sensorState.flags = (VRDisplayCapabilityFlags)(
228
0
    (int)VRDisplayCapabilityFlags::Cap_Orientation |
229
0
    (int)VRDisplayCapabilityFlags::Cap_Position);
230
0
  sensorState.pose.orientation[3] = 1.0f; // Default to an identity quaternion
231
0
232
0
  return true;
233
0
}
234
235
void
236
OpenVRSession::UpdateStageParameters(VRDisplayState& aState)
237
0
{
238
0
  float sizeX = 0.0f;
239
0
  float sizeZ = 0.0f;
240
0
  if (mVRChaperone->GetPlayAreaSize(&sizeX, &sizeZ)) {
241
0
    ::vr::HmdMatrix34_t t = mVRSystem->GetSeatedZeroPoseToStandingAbsoluteTrackingPose();
242
0
    aState.mStageSize.width = sizeX;
243
0
    aState.mStageSize.height = sizeZ;
244
0
245
0
    aState.mSittingToStandingTransform[0] = t.m[0][0];
246
0
    aState.mSittingToStandingTransform[1] = t.m[1][0];
247
0
    aState.mSittingToStandingTransform[2] = t.m[2][0];
248
0
    aState.mSittingToStandingTransform[3] = 0.0f;
249
0
250
0
    aState.mSittingToStandingTransform[4] = t.m[0][1];
251
0
    aState.mSittingToStandingTransform[5] = t.m[1][1];
252
0
    aState.mSittingToStandingTransform[6] = t.m[2][1];
253
0
    aState.mSittingToStandingTransform[7] = 0.0f;
254
0
255
0
    aState.mSittingToStandingTransform[8] = t.m[0][2];
256
0
    aState.mSittingToStandingTransform[9] = t.m[1][2];
257
0
    aState.mSittingToStandingTransform[10] = t.m[2][2];
258
0
    aState.mSittingToStandingTransform[11] = 0.0f;
259
0
260
0
    aState.mSittingToStandingTransform[12] = t.m[0][3];
261
0
    aState.mSittingToStandingTransform[13] = t.m[1][3];
262
0
    aState.mSittingToStandingTransform[14] = t.m[2][3];
263
0
    aState.mSittingToStandingTransform[15] = 1.0f;
264
0
  } else {
265
0
    // If we fail, fall back to reasonable defaults.
266
0
    // 1m x 1m space, 0.75m high in seated position
267
0
    aState.mStageSize.width = 1.0f;
268
0
    aState.mStageSize.height = 1.0f;
269
0
270
0
    aState.mSittingToStandingTransform[0] = 1.0f;
271
0
    aState.mSittingToStandingTransform[1] = 0.0f;
272
0
    aState.mSittingToStandingTransform[2] = 0.0f;
273
0
    aState.mSittingToStandingTransform[3] = 0.0f;
274
0
275
0
    aState.mSittingToStandingTransform[4] = 0.0f;
276
0
    aState.mSittingToStandingTransform[5] = 1.0f;
277
0
    aState.mSittingToStandingTransform[6] = 0.0f;
278
0
    aState.mSittingToStandingTransform[7] = 0.0f;
279
0
280
0
    aState.mSittingToStandingTransform[8] = 0.0f;
281
0
    aState.mSittingToStandingTransform[9] = 0.0f;
282
0
    aState.mSittingToStandingTransform[10] = 1.0f;
283
0
    aState.mSittingToStandingTransform[11] = 0.0f;
284
0
285
0
    aState.mSittingToStandingTransform[12] = 0.0f;
286
0
    aState.mSittingToStandingTransform[13] = 0.75f;
287
0
    aState.mSittingToStandingTransform[14] = 0.0f;
288
0
    aState.mSittingToStandingTransform[15] = 1.0f;
289
0
  }
290
0
}
291
292
void
293
OpenVRSession::UpdateEyeParameters(VRSystemState& aState)
294
0
{
295
0
  // This must be called every frame in order to
296
0
  // account for continuous adjustments to ipd.
297
0
  gfx::Matrix4x4 headToEyeTransforms[2];
298
0
299
0
  for (uint32_t eye = 0; eye < 2; ++eye) {
300
0
    ::vr::HmdMatrix34_t eyeToHead = mVRSystem->GetEyeToHeadTransform(static_cast<::vr::Hmd_Eye>(eye));
301
0
    aState.displayState.mEyeTranslation[eye].x = eyeToHead.m[0][3];
302
0
    aState.displayState.mEyeTranslation[eye].y = eyeToHead.m[1][3];
303
0
    aState.displayState.mEyeTranslation[eye].z = eyeToHead.m[2][3];
304
0
305
0
    float left, right, up, down;
306
0
    mVRSystem->GetProjectionRaw(static_cast<::vr::Hmd_Eye>(eye), &left, &right, &up, &down);
307
0
    aState.displayState.mEyeFOV[eye].upDegrees = atan(-up) * 180.0 / M_PI;
308
0
    aState.displayState.mEyeFOV[eye].rightDegrees = atan(right) * 180.0 / M_PI;
309
0
    aState.displayState.mEyeFOV[eye].downDegrees = atan(down) * 180.0 / M_PI;
310
0
    aState.displayState.mEyeFOV[eye].leftDegrees = atan(-left) * 180.0 / M_PI;
311
0
312
0
    Matrix4x4 pose;
313
0
    // NOTE! eyeToHead.m is a 3x4 matrix, not 4x4.  But
314
0
    // because of its arrangement, we can copy the 12 elements in and
315
0
    // then transpose them to the right place.
316
0
    memcpy(&pose._11, &eyeToHead.m, sizeof(eyeToHead.m));
317
0
    pose.Transpose();
318
0
    pose.Invert();
319
0
    headToEyeTransforms[eye] = pose;
320
0
  }
321
0
  aState.sensorState.CalcViewMatrices(headToEyeTransforms);
322
0
}
323
324
void
325
OpenVRSession::UpdateHeadsetPose(VRSystemState& aState)
326
0
{
327
0
  const uint32_t posesSize = ::vr::k_unTrackedDeviceIndex_Hmd + 1;
328
0
  ::vr::TrackedDevicePose_t poses[posesSize];
329
0
  // Note: We *must* call WaitGetPoses in order for any rendering to happen at all.
330
0
  mVRCompositor->WaitGetPoses(poses, posesSize, nullptr, 0);
331
0
332
0
  ::vr::Compositor_FrameTiming timing;
333
0
  timing.m_nSize = sizeof(::vr::Compositor_FrameTiming);
334
0
  if (mVRCompositor->GetFrameTiming(&timing)) {
335
0
    aState.sensorState.timestamp = timing.m_flSystemTimeInSeconds;
336
0
  } else {
337
0
    // This should not happen, but log it just in case
338
0
    fprintf(stderr, "OpenVR - IVRCompositor::GetFrameTiming failed");
339
0
  }
340
0
341
0
  if (poses[::vr::k_unTrackedDeviceIndex_Hmd].bDeviceIsConnected &&
342
0
    poses[::vr::k_unTrackedDeviceIndex_Hmd].bPoseIsValid &&
343
0
    poses[::vr::k_unTrackedDeviceIndex_Hmd].eTrackingResult == ::vr::TrackingResult_Running_OK)
344
0
  {
345
0
    const ::vr::TrackedDevicePose_t& pose = poses[::vr::k_unTrackedDeviceIndex_Hmd];
346
0
347
0
    gfx::Matrix4x4 m;
348
0
    // NOTE! mDeviceToAbsoluteTracking is a 3x4 matrix, not 4x4.  But
349
0
    // because of its arrangement, we can copy the 12 elements in and
350
0
    // then transpose them to the right place.  We do this so we can
351
0
    // pull out a Quaternion.
352
0
    memcpy(&m._11, &pose.mDeviceToAbsoluteTracking, sizeof(pose.mDeviceToAbsoluteTracking));
353
0
    m.Transpose();
354
0
355
0
    gfx::Quaternion rot;
356
0
    rot.SetFromRotationMatrix(m);
357
0
    rot.Invert();
358
0
359
0
    aState.sensorState.flags = (VRDisplayCapabilityFlags)((int)aState.sensorState.flags | (int)VRDisplayCapabilityFlags::Cap_Orientation);
360
0
    aState.sensorState.pose.orientation[0] = rot.x;
361
0
    aState.sensorState.pose.orientation[1] = rot.y;
362
0
    aState.sensorState.pose.orientation[2] = rot.z;
363
0
    aState.sensorState.pose.orientation[3] = rot.w;
364
0
    aState.sensorState.pose.angularVelocity[0] = pose.vAngularVelocity.v[0];
365
0
    aState.sensorState.pose.angularVelocity[1] = pose.vAngularVelocity.v[1];
366
0
    aState.sensorState.pose.angularVelocity[2] = pose.vAngularVelocity.v[2];
367
0
368
0
    aState.sensorState.flags =(VRDisplayCapabilityFlags)((int)aState.sensorState.flags | (int)VRDisplayCapabilityFlags::Cap_Position);
369
0
    aState.sensorState.pose.position[0] = m._41;
370
0
    aState.sensorState.pose.position[1] = m._42;
371
0
    aState.sensorState.pose.position[2] = m._43;
372
0
    aState.sensorState.pose.linearVelocity[0] = pose.vVelocity.v[0];
373
0
    aState.sensorState.pose.linearVelocity[1] = pose.vVelocity.v[1];
374
0
    aState.sensorState.pose.linearVelocity[2] = pose.vVelocity.v[2];
375
0
  }
376
0
}
377
378
void
379
OpenVRSession::EnumerateControllers(VRSystemState& aState)
380
0
{
381
0
  MOZ_ASSERT(mVRSystem);
382
0
383
0
  bool controllerPresent[kVRControllerMaxCount] = { false };
384
0
385
0
  // Basically, we would have HMDs in the tracked devices,
386
0
  // but we are just interested in the controllers.
387
0
  for (::vr::TrackedDeviceIndex_t trackedDevice = ::vr::k_unTrackedDeviceIndex_Hmd + 1;
388
0
       trackedDevice < ::vr::k_unMaxTrackedDeviceCount; ++trackedDevice) {
389
0
390
0
    if (!mVRSystem->IsTrackedDeviceConnected(trackedDevice)) {
391
0
      continue;
392
0
    }
393
0
394
0
    const ::vr::ETrackedDeviceClass deviceType = mVRSystem->
395
0
                                                 GetTrackedDeviceClass(trackedDevice);
396
0
    if (deviceType != ::vr::TrackedDeviceClass_Controller
397
0
        && deviceType != ::vr::TrackedDeviceClass_GenericTracker) {
398
0
      continue;
399
0
    }
400
0
401
0
    uint32_t stateIndex = 0;
402
0
    uint32_t firstEmptyIndex = kVRControllerMaxCount;
403
0
404
0
    // Find the existing controller
405
0
    for (stateIndex = 0; stateIndex < kVRControllerMaxCount; stateIndex++) {
406
0
      if (mControllerDeviceIndex[stateIndex] == 0 && firstEmptyIndex == kVRControllerMaxCount) {
407
0
        firstEmptyIndex = stateIndex;
408
0
      }
409
0
      if (mControllerDeviceIndex[stateIndex] == trackedDevice) {
410
0
        break;
411
0
      }
412
0
    }
413
0
    if (stateIndex == kVRControllerMaxCount) {
414
0
      // This is a new controller, let's add it
415
0
      if (firstEmptyIndex == kVRControllerMaxCount) {
416
0
        NS_WARNING("OpenVR - Too many controllers, need to increase kVRControllerMaxCount.");
417
0
        continue;
418
0
      }
419
0
      stateIndex = firstEmptyIndex;
420
0
      mControllerDeviceIndex[stateIndex] = trackedDevice;
421
0
      VRControllerState& controllerState = aState.controllerState[stateIndex];
422
0
      uint32_t numButtons = 0;
423
0
      uint32_t numAxes = 0;
424
0
425
0
      // Scan the axes that the controllers support
426
0
      for (uint32_t j = 0; j < ::vr::k_unControllerStateAxisCount; ++j) {
427
0
        const uint32_t supportAxis = mVRSystem->GetInt32TrackedDeviceProperty(trackedDevice,
428
0
                                      static_cast<vr::TrackedDeviceProperty>(
429
0
                                      ::vr::Prop_Axis0Type_Int32 + j));
430
0
        switch (supportAxis) {
431
0
          case ::vr::EVRControllerAxisType::k_eControllerAxis_Joystick:
432
0
          case ::vr::EVRControllerAxisType::k_eControllerAxis_TrackPad:
433
0
            numAxes += 2; // It has x and y axes.
434
0
            ++numButtons;
435
0
            break;
436
0
          case ::vr::k_eControllerAxis_Trigger:
437
0
            if (j <= 2) {
438
0
              ++numButtons;
439
0
            } else {
440
          #ifdef DEBUG
441
              // SteamVR Knuckles is the only special case for using 2D axis values on triggers.
442
              ::vr::ETrackedPropertyError err;
443
              uint32_t requiredBufferLen;
444
              char charBuf[128];
445
              requiredBufferLen = mVRSystem->GetStringTrackedDeviceProperty(trackedDevice,
446
                                  ::vr::Prop_RenderModelName_String, charBuf, 128, &err);
447
              MOZ_ASSERT(requiredBufferLen && err == ::vr::TrackedProp_Success);
448
              nsCString deviceId(charBuf);
449
              MOZ_ASSERT(deviceId.Find("knuckles") != kNotFound);
450
          #endif // #ifdef DEBUG
451
              numButtons += 2;
452
0
            }
453
0
            break;
454
0
        }
455
0
      }
456
0
457
0
      // Scan the buttons that the controllers support
458
0
      const uint64_t supportButtons = mVRSystem->GetUint64TrackedDeviceProperty(
459
0
                                       trackedDevice, ::vr::Prop_SupportedButtons_Uint64);
460
0
      if (supportButtons &
461
0
          BTN_MASK_FROM_ID(k_EButton_A)) {
462
0
        ++numButtons;
463
0
      }
464
0
      if (supportButtons &
465
0
          BTN_MASK_FROM_ID(k_EButton_Grip)) {
466
0
        ++numButtons;
467
0
      }
468
0
      if (supportButtons &
469
0
          BTN_MASK_FROM_ID(k_EButton_ApplicationMenu)) {
470
0
        ++numButtons;
471
0
      }
472
0
      if (supportButtons &
473
0
          BTN_MASK_FROM_ID(k_EButton_DPad_Left)) {
474
0
        ++numButtons;
475
0
      }
476
0
      if (supportButtons &
477
0
          BTN_MASK_FROM_ID(k_EButton_DPad_Up)) {
478
0
        ++numButtons;
479
0
      }
480
0
      if (supportButtons &
481
0
          BTN_MASK_FROM_ID(k_EButton_DPad_Right)) {
482
0
        ++numButtons;
483
0
      }
484
0
      if (supportButtons &
485
0
          BTN_MASK_FROM_ID(k_EButton_DPad_Down)) {
486
0
        ++numButtons;
487
0
      }
488
0
489
0
      nsCString deviceId;
490
0
      GetControllerDeviceId(deviceType, trackedDevice, deviceId);
491
0
492
0
      strncpy(controllerState.controllerName, deviceId.BeginReading(), kVRControllerNameMaxLen);
493
0
      controllerState.numButtons = numButtons;
494
0
      controllerState.numAxes = numAxes;
495
0
      controllerState.numHaptics = kNumOpenVRHaptcs;
496
0
497
0
      // If the Windows MR controller doesn't has the amount
498
0
      // of buttons or axes as our expectation, switching off
499
0
      // the workaround for Windows MR.
500
0
      if (mIsWindowsMR && (numAxes < 4 || numButtons < 5)) {
501
0
        mIsWindowsMR = false;
502
0
        NS_WARNING("OpenVR - Switching off Windows MR mode.");
503
0
      }
504
0
    }
505
0
    controllerPresent[stateIndex] = true;
506
0
  }
507
0
  // Clear out entries for disconnected controllers
508
0
  for (uint32_t stateIndex = 0; stateIndex < kVRControllerMaxCount; stateIndex++) {
509
0
    if (!controllerPresent[stateIndex] && mControllerDeviceIndex[stateIndex] != 0) {
510
0
      mControllerDeviceIndex[stateIndex] = 0;
511
0
      memset(&aState.controllerState[stateIndex], 0, sizeof(VRControllerState));
512
0
    }
513
0
  }
514
0
}
515
516
void
517
OpenVRSession::UpdateControllerButtons(VRSystemState& aState)
518
0
{
519
0
  MOZ_ASSERT(mVRSystem);
520
0
521
0
  // Compared to Edge, we have a wrong implementation for the vertical axis value.
522
0
  // In order to not affect the current VR content, we add a workaround for yAxis.
523
0
  const float yAxisInvert = (mIsWindowsMR) ? -1.0f : 1.0f;
524
0
  const float triggerThreshold = gfxPrefs::VRControllerTriggerThreshold();
525
0
526
0
  for (uint32_t stateIndex = 0; stateIndex < kVRControllerMaxCount; stateIndex++) {
527
0
    ::vr::TrackedDeviceIndex_t trackedDevice = mControllerDeviceIndex[stateIndex];
528
0
    if (trackedDevice == 0) {
529
0
      continue;
530
0
    }
531
0
    VRControllerState& controllerState = aState.controllerState[stateIndex];
532
0
    const ::vr::ETrackedControllerRole role = mVRSystem->
533
0
                                          GetControllerRoleForTrackedDeviceIndex(
534
0
                                          trackedDevice);
535
0
    dom::GamepadHand hand = GetControllerHandFromControllerRole(role);
536
0
    controllerState.hand = hand;
537
0
538
0
    ::vr::VRControllerState_t vrControllerState;
539
0
    if (mVRSystem->GetControllerState(trackedDevice, &vrControllerState, sizeof(vrControllerState))) {
540
0
      uint32_t axisIdx = 0;
541
0
      uint32_t buttonIdx = 0;
542
0
      for (uint32_t j = 0; j < ::vr::k_unControllerStateAxisCount; ++j) {
543
0
        const uint32_t axisType = mVRSystem->GetInt32TrackedDeviceProperty(
544
0
                                   trackedDevice,
545
0
                                   static_cast<::vr::TrackedDeviceProperty>(
546
0
                                   ::vr::Prop_Axis0Type_Int32 + j));
547
0
        switch (axisType) {
548
0
          case ::vr::EVRControllerAxisType::k_eControllerAxis_Joystick:
549
0
          case ::vr::EVRControllerAxisType::k_eControllerAxis_TrackPad:
550
0
          {
551
0
            if (mIsWindowsMR) {
552
0
              // Adjust the input mapping for Windows MR which has
553
0
              // different order.
554
0
              axisIdx = (axisIdx == 0) ? 2 : 0;
555
0
              buttonIdx = (buttonIdx == 0) ? 4 : 0;
556
0
            }
557
0
558
0
            controllerState.axisValue[axisIdx] = vrControllerState.rAxis[j].x;
559
0
            ++axisIdx;
560
0
            controllerState.axisValue[axisIdx] = vrControllerState.rAxis[j].y * yAxisInvert;
561
0
            ++axisIdx;
562
0
            uint64_t buttonMask = ::vr::ButtonMaskFromId(
563
0
                                 static_cast<::vr::EVRButtonId>(::vr::k_EButton_Axis0 + j));
564
0
565
0
            UpdateButton(controllerState, vrControllerState, buttonIdx, buttonMask);
566
0
            ++buttonIdx;
567
0
568
0
            if (mIsWindowsMR) {
569
0
              axisIdx = (axisIdx == 4) ? 2 : 4;
570
0
              buttonIdx = (buttonIdx == 5) ? 1 : 2;
571
0
            }
572
0
            break;
573
0
          }
574
0
          case vr::EVRControllerAxisType::k_eControllerAxis_Trigger:
575
0
          {
576
0
            if (j <= 2) {
577
0
              UpdateTrigger(controllerState, buttonIdx, vrControllerState.rAxis[j].x, triggerThreshold);
578
0
              ++buttonIdx;
579
0
            } else {
580
0
              // For SteamVR Knuckles.
581
0
              UpdateTrigger(controllerState, buttonIdx, vrControllerState.rAxis[j].x, triggerThreshold);
582
0
              ++buttonIdx;
583
0
              UpdateTrigger(controllerState, buttonIdx, vrControllerState.rAxis[j].y, triggerThreshold);
584
0
              ++buttonIdx;
585
0
            }
586
0
            break;
587
0
          }
588
0
        }
589
0
      }
590
0
591
0
      const uint64_t supportedButtons = mVRSystem->GetUint64TrackedDeviceProperty(
592
0
                                         trackedDevice, ::vr::Prop_SupportedButtons_Uint64);
593
0
      if (supportedButtons &
594
0
          BTN_MASK_FROM_ID(k_EButton_A)) {
595
0
        UpdateButton(controllerState, vrControllerState, buttonIdx, BTN_MASK_FROM_ID(k_EButton_A));
596
0
        ++buttonIdx;
597
0
      }
598
0
      if (supportedButtons &
599
0
          BTN_MASK_FROM_ID(k_EButton_Grip)) {
600
0
        UpdateButton(controllerState, vrControllerState, buttonIdx, BTN_MASK_FROM_ID(k_EButton_Grip));
601
0
        ++buttonIdx;
602
0
      }
603
0
      if (supportedButtons &
604
0
          BTN_MASK_FROM_ID(k_EButton_ApplicationMenu)) {
605
0
        UpdateButton(controllerState, vrControllerState, buttonIdx, BTN_MASK_FROM_ID(k_EButton_ApplicationMenu));
606
0
        ++buttonIdx;
607
0
      }
608
0
      if (mIsWindowsMR) {
609
0
        // button 4 in Windows MR has already been assigned
610
0
        // to k_eControllerAxis_TrackPad.
611
0
        ++buttonIdx;
612
0
      }
613
0
      if (supportedButtons &
614
0
          BTN_MASK_FROM_ID(k_EButton_DPad_Left)) {
615
0
        UpdateButton(controllerState, vrControllerState, buttonIdx, BTN_MASK_FROM_ID(k_EButton_DPad_Left));
616
0
        ++buttonIdx;
617
0
      }
618
0
      if (supportedButtons &
619
0
          BTN_MASK_FROM_ID(k_EButton_DPad_Up)) {
620
0
        UpdateButton(controllerState, vrControllerState, buttonIdx, BTN_MASK_FROM_ID(k_EButton_DPad_Up));
621
0
        ++buttonIdx;
622
0
      }
623
0
      if (supportedButtons &
624
0
          BTN_MASK_FROM_ID(k_EButton_DPad_Right)) {
625
0
        UpdateButton(controllerState, vrControllerState, buttonIdx, BTN_MASK_FROM_ID(k_EButton_DPad_Right));
626
0
        ++buttonIdx;
627
0
      }
628
0
      if (supportedButtons &
629
0
          BTN_MASK_FROM_ID(k_EButton_DPad_Down)) {
630
0
        UpdateButton(controllerState, vrControllerState, buttonIdx, BTN_MASK_FROM_ID(k_EButton_DPad_Down));
631
0
        ++buttonIdx;
632
0
      }
633
0
    }
634
0
  }
635
0
}
636
637
void
638
OpenVRSession::UpdateControllerPoses(VRSystemState& aState)
639
0
{
640
0
  MOZ_ASSERT(mVRSystem);
641
0
642
0
  ::vr::TrackedDevicePose_t poses[::vr::k_unMaxTrackedDeviceCount];
643
0
  mVRSystem->GetDeviceToAbsoluteTrackingPose(::vr::TrackingUniverseSeated, 0.0f,
644
0
                                             poses, ::vr::k_unMaxTrackedDeviceCount);
645
0
646
0
  for (uint32_t stateIndex = 0; stateIndex < kVRControllerMaxCount; stateIndex++) {
647
0
    ::vr::TrackedDeviceIndex_t trackedDevice = mControllerDeviceIndex[stateIndex];
648
0
    if (trackedDevice == 0) {
649
0
      continue;
650
0
    }
651
0
    VRControllerState& controllerState = aState.controllerState[stateIndex];
652
0
    const ::vr::TrackedDevicePose_t& pose = poses[trackedDevice];
653
0
654
0
    if (pose.bDeviceIsConnected) {
655
0
      controllerState.flags = (dom::GamepadCapabilityFlags::Cap_Orientation |
656
0
                               dom::GamepadCapabilityFlags::Cap_Position);
657
0
    } else {
658
0
      controllerState.flags =  dom::GamepadCapabilityFlags::Cap_None;
659
0
    }
660
0
    if (pose.bPoseIsValid &&
661
0
        pose.eTrackingResult == ::vr::TrackingResult_Running_OK) {
662
0
      gfx::Matrix4x4 m;
663
0
664
0
      // NOTE! mDeviceToAbsoluteTracking is a 3x4 matrix, not 4x4.  But
665
0
      // because of its arrangement, we can copy the 12 elements in and
666
0
      // then transpose them to the right place.  We do this so we can
667
0
      // pull out a Quaternion.
668
0
      memcpy(&m.components, &pose.mDeviceToAbsoluteTracking, sizeof(pose.mDeviceToAbsoluteTracking));
669
0
      m.Transpose();
670
0
671
0
      gfx::Quaternion rot;
672
0
      rot.SetFromRotationMatrix(m);
673
0
      rot.Invert();
674
0
675
0
      controllerState.pose.orientation[0] = rot.x;
676
0
      controllerState.pose.orientation[1] = rot.y;
677
0
      controllerState.pose.orientation[2] = rot.z;
678
0
      controllerState.pose.orientation[3] = rot.w;
679
0
      controllerState.pose.angularVelocity[0] = pose.vAngularVelocity.v[0];
680
0
      controllerState.pose.angularVelocity[1] = pose.vAngularVelocity.v[1];
681
0
      controllerState.pose.angularVelocity[2] = pose.vAngularVelocity.v[2];
682
0
      controllerState.pose.angularAcceleration[0] = 0.0f;
683
0
      controllerState.pose.angularAcceleration[1] = 0.0f;
684
0
      controllerState.pose.angularAcceleration[2] = 0.0f;
685
0
      controllerState.isOrientationValid = true;
686
0
687
0
      controllerState.pose.position[0] = m._41;
688
0
      controllerState.pose.position[1] = m._42;
689
0
      controllerState.pose.position[2] = m._43;
690
0
      controllerState.pose.linearVelocity[0] = pose.vVelocity.v[0];
691
0
      controllerState.pose.linearVelocity[1] = pose.vVelocity.v[1];
692
0
      controllerState.pose.linearVelocity[2] = pose.vVelocity.v[2];
693
0
      controllerState.pose.linearAcceleration[0] = 0.0f;
694
0
      controllerState.pose.linearAcceleration[1] = 0.0f;
695
0
      controllerState.pose.linearAcceleration[2] = 0.0f;
696
0
      controllerState.isPositionValid = true;
697
0
    } else {
698
0
      controllerState.isOrientationValid = false;
699
0
      controllerState.isPositionValid = false;
700
0
    }
701
0
  }
702
0
}
703
704
void
705
OpenVRSession::GetControllerDeviceId(::vr::ETrackedDeviceClass aDeviceType,
706
                                     ::vr::TrackedDeviceIndex_t aDeviceIndex,
707
                                     nsCString& aId)
708
0
{
709
0
  switch (aDeviceType) {
710
0
    case ::vr::TrackedDeviceClass_Controller:
711
0
    {
712
0
      ::vr::ETrackedPropertyError err;
713
0
      uint32_t requiredBufferLen;
714
0
      bool isFound = false;
715
0
      char charBuf[128];
716
0
      requiredBufferLen = mVRSystem->GetStringTrackedDeviceProperty(aDeviceIndex,
717
0
                          ::vr::Prop_RenderModelName_String, charBuf, 128, &err);
718
0
      if (requiredBufferLen > 128) {
719
0
        MOZ_CRASH("Larger than the buffer size.");
720
0
      }
721
0
      MOZ_ASSERT(requiredBufferLen && err == ::vr::TrackedProp_Success);
722
0
      nsCString deviceId(charBuf);
723
0
      if (deviceId.Find("knuckles") != kNotFound) {
724
0
        aId.AssignLiteral("OpenVR Knuckles");
725
0
        isFound = true;
726
0
      }
727
0
      requiredBufferLen = mVRSystem->GetStringTrackedDeviceProperty(aDeviceIndex,
728
0
        ::vr::Prop_SerialNumber_String, charBuf, 128, &err);
729
0
      if (requiredBufferLen > 128) {
730
0
        MOZ_CRASH("Larger than the buffer size.");
731
0
      }
732
0
      MOZ_ASSERT(requiredBufferLen && err == ::vr::TrackedProp_Success);
733
0
      deviceId.Assign(charBuf);
734
0
      if (deviceId.Find("MRSOURCE") != kNotFound) {
735
0
        aId.AssignLiteral("Spatial Controller (Spatial Interaction Source) ");
736
0
        mIsWindowsMR = true;
737
0
        isFound = true;
738
0
      }
739
0
      if (!isFound) {
740
0
        aId.AssignLiteral("OpenVR Gamepad");
741
0
      }
742
0
      break;
743
0
    }
744
0
    case ::vr::TrackedDeviceClass_GenericTracker:
745
0
    {
746
0
      aId.AssignLiteral("OpenVR Tracker");
747
0
      break;
748
0
    }
749
0
    default:
750
0
      MOZ_ASSERT(false);
751
0
      break;
752
0
  }
753
0
}
754
755
void
756
OpenVRSession::StartFrame(mozilla::gfx::VRSystemState& aSystemState)
757
0
{
758
0
  UpdateHeadsetPose(aSystemState);
759
0
  UpdateEyeParameters(aSystemState);
760
0
  EnumerateControllers(aSystemState);
761
0
  UpdateControllerButtons(aSystemState);
762
0
  UpdateControllerPoses(aSystemState);
763
0
  aSystemState.sensorState.inputFrameID++;
764
0
}
765
766
bool
767
OpenVRSession::ShouldQuit() const
768
0
{
769
0
  return mShouldQuit;
770
0
}
771
772
void
773
OpenVRSession::ProcessEvents(mozilla::gfx::VRSystemState& aSystemState)
774
0
{
775
0
  bool isHmdPresent = ::vr::VR_IsHmdPresent();
776
0
  if (!isHmdPresent) {
777
0
    mShouldQuit = true;
778
0
  }
779
0
780
0
  ::vr::VREvent_t event;
781
0
  while (mVRSystem && mVRSystem->PollNextEvent(&event, sizeof(event))) {
782
0
    switch (event.eventType) {
783
0
      case ::vr::VREvent_TrackedDeviceUserInteractionStarted:
784
0
        if (event.trackedDeviceIndex == ::vr::k_unTrackedDeviceIndex_Hmd) {
785
0
          aSystemState.displayState.mIsMounted = true;
786
0
        }
787
0
        break;
788
0
      case ::vr::VREvent_TrackedDeviceUserInteractionEnded:
789
0
        if (event.trackedDeviceIndex == ::vr::k_unTrackedDeviceIndex_Hmd) {
790
0
          aSystemState.displayState.mIsMounted = false;
791
0
        }
792
0
        break;
793
0
      case ::vr::EVREventType::VREvent_TrackedDeviceActivated:
794
0
        if (event.trackedDeviceIndex == ::vr::k_unTrackedDeviceIndex_Hmd) {
795
0
          aSystemState.displayState.mIsConnected = true;
796
0
        }
797
0
        break;
798
0
      case ::vr::EVREventType::VREvent_TrackedDeviceDeactivated:
799
0
        if (event.trackedDeviceIndex == ::vr::k_unTrackedDeviceIndex_Hmd) {
800
0
          aSystemState.displayState.mIsConnected = false;
801
0
        }
802
0
        break;
803
0
      case ::vr::EVREventType::VREvent_DriverRequestedQuit:
804
0
      case ::vr::EVREventType::VREvent_Quit:
805
0
      case ::vr::EVREventType::VREvent_ProcessQuit:
806
0
      case ::vr::EVREventType::VREvent_QuitAcknowledged:
807
0
      case ::vr::EVREventType::VREvent_QuitAborted_UserPrompt:
808
0
        mShouldQuit = true;
809
0
        break;
810
0
      default:
811
0
        // ignore
812
0
        break;
813
0
    }
814
0
  }
815
0
}
816
817
bool
818
OpenVRSession::SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer)
819
0
{
820
#if defined(XP_WIN)
821
822
  if (aLayer.mTextureType == VRLayerTextureType::LayerTextureType_D3D10SurfaceDescriptor) {
823
      RefPtr<ID3D11Texture2D> dxTexture;
824
      HRESULT hr = mDevice->OpenSharedResource((HANDLE)aLayer.mTextureHandle,
825
        __uuidof(ID3D11Texture2D),
826
        (void**)(ID3D11Texture2D**)getter_AddRefs(dxTexture));
827
      if (FAILED(hr) || !dxTexture) {
828
        NS_WARNING("Failed to open shared texture");
829
        return false;
830
      }
831
832
      // Similar to LockD3DTexture in TextureD3D11.cpp
833
      RefPtr<IDXGIKeyedMutex> mutex;
834
      dxTexture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mutex));
835
      if (mutex) {
836
        HRESULT hr = mutex->AcquireSync(0, 1000);
837
        if (hr == WAIT_TIMEOUT) {
838
          gfxDevCrash(LogReason::D3DLockTimeout) << "D3D lock mutex timeout";
839
        }
840
        else if (hr == WAIT_ABANDONED) {
841
          gfxCriticalNote << "GFX: D3D11 lock mutex abandoned";
842
        }
843
        if (FAILED(hr)) {
844
          NS_WARNING("Failed to lock the texture");
845
          return false;
846
        }
847
      }
848
      bool success = SubmitFrame((void *)dxTexture,
849
                     ::vr::ETextureType::TextureType_DirectX,
850
                     aLayer.mLeftEyeRect, aLayer.mRightEyeRect);
851
      if (mutex) {
852
        HRESULT hr = mutex->ReleaseSync(0);
853
        if (FAILED(hr)) {
854
          NS_WARNING("Failed to unlock the texture");
855
        }
856
      }
857
      if (!success) {
858
        return false;
859
      }
860
      return true;
861
  }
862
863
#elif defined(XP_MACOSX)
864
865
  if (aLayer.mTextureType == VRLayerTextureType::LayerTextureType_MacIOSurface) {
866
    return SubmitFrame(aLayer.mTextureHandle,
867
                       ::vr::ETextureType::TextureType_IOSurface,
868
                       aLayer.mLeftEyeRect, aLayer.mRightEyeRect);
869
  }
870
871
#endif
872
873
0
  return false;
874
0
}
875
876
bool
877
OpenVRSession::SubmitFrame(void* aTextureHandle,
878
                           ::vr::ETextureType aTextureType,
879
                           const VRLayerEyeRect& aLeftEyeRect,
880
                           const VRLayerEyeRect& aRightEyeRect)
881
0
{
882
0
  ::vr::Texture_t tex;
883
0
  tex.handle = aTextureHandle;
884
0
  tex.eType = aTextureType;
885
0
  tex.eColorSpace = ::vr::EColorSpace::ColorSpace_Auto;
886
0
887
0
  ::vr::VRTextureBounds_t bounds;
888
0
  bounds.uMin = aLeftEyeRect.x;
889
0
  bounds.vMin = 1.0 - aLeftEyeRect.y;
890
0
  bounds.uMax = aLeftEyeRect.x + aLeftEyeRect.width;
891
0
  bounds.vMax = 1.0 - (aLeftEyeRect.y + aLeftEyeRect.height);
892
0
893
0
  ::vr::EVRCompositorError err;
894
0
  err = mVRCompositor->Submit(::vr::EVREye::Eye_Left, &tex, &bounds);
895
0
  if (err != ::vr::EVRCompositorError::VRCompositorError_None) {
896
0
    printf_stderr("OpenVR Compositor Submit() failed.\n");
897
0
  }
898
0
899
0
  bounds.uMin = aRightEyeRect.x;
900
0
  bounds.vMin = 1.0 - aRightEyeRect.y;
901
0
  bounds.uMax = aRightEyeRect.x + aRightEyeRect.width;
902
0
  bounds.vMax = 1.0 - (aRightEyeRect.y + aRightEyeRect.height);
903
0
904
0
  err = mVRCompositor->Submit(::vr::EVREye::Eye_Right, &tex, &bounds);
905
0
  if (err != ::vr::EVRCompositorError::VRCompositorError_None) {
906
0
    printf_stderr("OpenVR Compositor Submit() failed.\n");
907
0
  }
908
0
909
0
  mVRCompositor->PostPresentHandoff();
910
0
  return true;
911
0
}
912
913
void
914
OpenVRSession::StopPresentation()
915
0
{
916
0
  mVRCompositor->ClearLastSubmittedFrame();
917
0
918
0
  ::vr::Compositor_CumulativeStats stats;
919
0
  mVRCompositor->GetCumulativeStats(&stats, sizeof(::vr::Compositor_CumulativeStats));
920
0
  // TODO - Need to send telemetry back to browser.
921
0
  // Bug 1473398 will refactor this original gfxVROpenVR code:
922
0
  //   const uint32_t droppedFramesPerSec = (stats.m_nNumReprojectedFrames -
923
0
  //                                      mTelemetry.mLastDroppedFrameCount) / duration.ToSeconds();
924
0
  // Telemetry::Accumulate(Telemetry::WEBVR_DROPPED_FRAMES_IN_OPENVR, droppedFramesPerSec);
925
0
}
926
927
bool
928
OpenVRSession::StartPresentation()
929
0
{
930
0
  return true;
931
0
}
932
933
} // namespace mozilla
934
} // namespace gfx