gecko-dev/gfx/vr/gfxVROpenVR.cpp
Kearwood Gilbert 7a3eb1a6a2 Bug 1494556 - Remove VRListenerThread r=daoshengmu
We are refactoring much of the code in gfx/vr, moving
most of the code that runs in the VRListenerThread into
it's own process.  The remaining code will be non-blocking
once this refactoring is complete.

In order to resolve some shutdown crashes, it is simpler
to remove the VRListenerThread and the related code
starting and stopping this thread.  If this is done
prior to completion of the refactoring for Bug 1473399
(Enable VRService thread by default), there would be a
regression in responsiveness during detection of VR
hardware due to blocking API calls moving off the thread.

Differential Revision: https://phabricator.services.mozilla.com/D7227

--HG--
extra : moz-landing-system : lando
2018-10-02 21:17:05 +00:00

1350 lines
45 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <math.h>
#include "prlink.h"
#include "prenv.h"
#include "gfxPrefs.h"
#include "mozilla/Preferences.h"
#include "mozilla/gfx/Quaternion.h"
#ifdef XP_WIN
#include "CompositorD3D11.h"
#include "TextureD3D11.h"
#elif defined(XP_MACOSX)
#include "mozilla/gfx/MacIOSurface.h"
#endif
#include "gfxVROpenVR.h"
#include "VRManagerParent.h"
#include "VRManager.h"
#include "VRThread.h"
#include "nsServiceManagerUtils.h"
#include "nsIScreenManager.h"
#include "mozilla/dom/GamepadEventTypes.h"
#include "mozilla/dom/GamepadBinding.h"
#include "mozilla/Telemetry.h"
#ifndef M_PI
# define M_PI 3.14159265358979323846
#endif
using namespace mozilla;
using namespace mozilla::gfx;
using namespace mozilla::gfx::impl;
using namespace mozilla::layers;
using namespace mozilla::dom;
#define BTN_MASK_FROM_ID(_id) \
::vr::ButtonMaskFromId(vr::EVRButtonId::_id)
static const uint32_t kNumOpenVRHaptcs = 1;
VRDisplayOpenVR::VRDisplayOpenVR(::vr::IVRSystem *aVRSystem,
::vr::IVRChaperone *aVRChaperone,
::vr::IVRCompositor *aVRCompositor)
: VRDisplayLocal(VRDeviceType::OpenVR)
, mVRSystem(aVRSystem)
, mVRChaperone(aVRChaperone)
, mVRCompositor(aVRCompositor)
, mIsPresenting(false)
{
MOZ_COUNT_CTOR_INHERITED(VRDisplayOpenVR, VRDisplayLocal);
VRDisplayState& state = mDisplayInfo.mDisplayState;
strncpy(state.mDisplayName, "OpenVR HMD", kVRDisplayNameMaxLen);
state.mIsConnected = mVRSystem->IsTrackedDeviceConnected(::vr::k_unTrackedDeviceIndex_Hmd);
state.mIsMounted = false;
state.mCapabilityFlags = VRDisplayCapabilityFlags::Cap_None |
VRDisplayCapabilityFlags::Cap_Orientation |
VRDisplayCapabilityFlags::Cap_Position |
VRDisplayCapabilityFlags::Cap_External |
VRDisplayCapabilityFlags::Cap_Present |
VRDisplayCapabilityFlags::Cap_StageParameters;
mIsHmdPresent = ::vr::VR_IsHmdPresent();
::vr::ETrackedPropertyError err;
bool bHasProximitySensor = mVRSystem->GetBoolTrackedDeviceProperty(::vr::k_unTrackedDeviceIndex_Hmd, ::vr::Prop_ContainsProximitySensor_Bool, &err);
if (err == ::vr::TrackedProp_Success && bHasProximitySensor) {
state.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_MountDetection;
}
mVRCompositor->SetTrackingSpace(::vr::TrackingUniverseSeated);
uint32_t w, h;
mVRSystem->GetRecommendedRenderTargetSize(&w, &h);
state.mEyeResolution.width = w;
state.mEyeResolution.height = h;
// SteamVR gives the application a single FOV to use; it's not configurable as with Oculus
for (uint32_t eye = 0; eye < 2; ++eye) {
// get l/r/t/b clip plane coordinates
float l, r, t, b;
mVRSystem->GetProjectionRaw(static_cast<::vr::Hmd_Eye>(eye), &l, &r, &t, &b);
state.mEyeFOV[eye].SetFromTanRadians(-t, r, b, -l);
}
UpdateEyeParameters();
UpdateStageParameters();
}
VRDisplayOpenVR::~VRDisplayOpenVR()
{
Destroy();
MOZ_COUNT_DTOR_INHERITED(VRDisplayOpenVR, VRDisplayLocal);
}
void
VRDisplayOpenVR::Destroy()
{
StopPresentation();
::vr::VR_Shutdown();
}
void
VRDisplayOpenVR::UpdateEyeParameters(gfx::Matrix4x4* aHeadToEyeTransforms /* = nullptr */)
{
// Note this must be called every frame, as the IPD adjustment can be changed
// by the user during a VR session.
for (uint32_t eye = 0; eye < VRDisplayState::NumEyes; eye++) {
::vr::HmdMatrix34_t eyeToHead = mVRSystem->GetEyeToHeadTransform(static_cast<::vr::Hmd_Eye>(eye));
mDisplayInfo.mDisplayState.mEyeTranslation[eye].x = eyeToHead.m[0][3];
mDisplayInfo.mDisplayState.mEyeTranslation[eye].y = eyeToHead.m[1][3];
mDisplayInfo.mDisplayState.mEyeTranslation[eye].z = eyeToHead.m[2][3];
if (aHeadToEyeTransforms) {
Matrix4x4 pose;
// NOTE! eyeToHead.m is a 3x4 matrix, not 4x4. But
// because of its arrangement, we can copy the 12 elements in and
// then transpose them to the right place.
memcpy(&pose._11, &eyeToHead.m, sizeof(eyeToHead.m));
pose.Transpose();
pose.Invert();
aHeadToEyeTransforms[eye] = pose;
}
}
}
void
VRDisplayOpenVR::UpdateStageParameters()
{
VRDisplayState& state = mDisplayInfo.mDisplayState;
float sizeX = 0.0f;
float sizeZ = 0.0f;
if (mVRChaperone->GetPlayAreaSize(&sizeX, &sizeZ)) {
::vr::HmdMatrix34_t t = mVRSystem->GetSeatedZeroPoseToStandingAbsoluteTrackingPose();
state.mStageSize.width = sizeX;
state.mStageSize.height = sizeZ;
state.mSittingToStandingTransform[0] = t.m[0][0];
state.mSittingToStandingTransform[1] = t.m[1][0];
state.mSittingToStandingTransform[2] = t.m[2][0];
state.mSittingToStandingTransform[3] = 0.0f;
state.mSittingToStandingTransform[4] = t.m[0][1];
state.mSittingToStandingTransform[5] = t.m[1][1];
state.mSittingToStandingTransform[6] = t.m[2][1];
state.mSittingToStandingTransform[7] = 0.0f;
state.mSittingToStandingTransform[8] = t.m[0][2];
state.mSittingToStandingTransform[9] = t.m[1][2];
state.mSittingToStandingTransform[10] = t.m[2][2];
state.mSittingToStandingTransform[11] = 0.0f;
state.mSittingToStandingTransform[12] = t.m[0][3];
state.mSittingToStandingTransform[13] = t.m[1][3];
state.mSittingToStandingTransform[14] = t.m[2][3];
state.mSittingToStandingTransform[15] = 1.0f;
} else {
// If we fail, fall back to reasonable defaults.
// 1m x 1m space, 0.75m high in seated position
state.mStageSize.width = 1.0f;
state.mStageSize.height = 1.0f;
state.mSittingToStandingTransform[0] = 1.0f;
state.mSittingToStandingTransform[1] = 0.0f;
state.mSittingToStandingTransform[2] = 0.0f;
state.mSittingToStandingTransform[3] = 0.0f;
state.mSittingToStandingTransform[4] = 0.0f;
state.mSittingToStandingTransform[5] = 1.0f;
state.mSittingToStandingTransform[6] = 0.0f;
state.mSittingToStandingTransform[7] = 0.0f;
state.mSittingToStandingTransform[8] = 0.0f;
state.mSittingToStandingTransform[9] = 0.0f;
state.mSittingToStandingTransform[10] = 1.0f;
state.mSittingToStandingTransform[11] = 0.0f;
state.mSittingToStandingTransform[12] = 0.0f;
state.mSittingToStandingTransform[13] = 0.75f;
state.mSittingToStandingTransform[14] = 0.0f;
state.mSittingToStandingTransform[15] = 1.0f;
}
}
void
VRDisplayOpenVR::ZeroSensor()
{
mVRSystem->ResetSeatedZeroPose();
UpdateStageParameters();
}
bool
VRDisplayOpenVR::GetIsHmdPresent()
{
return mIsHmdPresent;
}
void
VRDisplayOpenVR::Refresh()
{
mIsHmdPresent = ::vr::VR_IsHmdPresent();
::vr::VREvent_t event;
while (mVRSystem && mVRSystem->PollNextEvent(&event, sizeof(event))) {
switch (event.eventType) {
case ::vr::VREvent_TrackedDeviceUserInteractionStarted:
if (event.trackedDeviceIndex == ::vr::k_unTrackedDeviceIndex_Hmd) {
mDisplayInfo.mDisplayState.mIsMounted = true;
}
break;
case ::vr::VREvent_TrackedDeviceUserInteractionEnded:
if (event.trackedDeviceIndex == ::vr::k_unTrackedDeviceIndex_Hmd) {
mDisplayInfo.mDisplayState.mIsMounted = false;
}
break;
case ::vr::EVREventType::VREvent_TrackedDeviceActivated:
if (event.trackedDeviceIndex == ::vr::k_unTrackedDeviceIndex_Hmd) {
mDisplayInfo.mDisplayState.mIsConnected = true;
}
break;
case ::vr::EVREventType::VREvent_TrackedDeviceDeactivated:
if (event.trackedDeviceIndex == ::vr::k_unTrackedDeviceIndex_Hmd) {
mDisplayInfo.mDisplayState.mIsConnected = false;
}
break;
case ::vr::EVREventType::VREvent_DriverRequestedQuit:
case ::vr::EVREventType::VREvent_Quit:
case ::vr::EVREventType::VREvent_ProcessQuit:
case ::vr::EVREventType::VREvent_QuitAcknowledged:
case ::vr::EVREventType::VREvent_QuitAborted_UserPrompt:
mIsHmdPresent = false;
break;
default:
// ignore
break;
}
}
}
VRHMDSensorState
VRDisplayOpenVR::GetSensorState()
{
const uint32_t posesSize = ::vr::k_unTrackedDeviceIndex_Hmd + 1;
::vr::TrackedDevicePose_t poses[posesSize];
// Note: We *must* call WaitGetPoses in order for any rendering to happen at all.
mVRCompositor->WaitGetPoses(poses, posesSize, nullptr, 0);
gfx::Matrix4x4 headToEyeTransforms[2];
UpdateEyeParameters(headToEyeTransforms);
VRHMDSensorState result{};
::vr::Compositor_FrameTiming timing;
timing.m_nSize = sizeof(::vr::Compositor_FrameTiming);
if (mVRCompositor->GetFrameTiming(&timing)) {
result.timestamp = timing.m_flSystemTimeInSeconds;
} else {
// This should not happen, but log it just in case
NS_WARNING("OpenVR - IVRCompositor::GetFrameTiming failed");
}
if (poses[::vr::k_unTrackedDeviceIndex_Hmd].bDeviceIsConnected &&
poses[::vr::k_unTrackedDeviceIndex_Hmd].bPoseIsValid &&
poses[::vr::k_unTrackedDeviceIndex_Hmd].eTrackingResult == ::vr::TrackingResult_Running_OK)
{
const ::vr::TrackedDevicePose_t& pose = poses[::vr::k_unTrackedDeviceIndex_Hmd];
gfx::Matrix4x4 m;
// NOTE! mDeviceToAbsoluteTracking is a 3x4 matrix, not 4x4. But
// because of its arrangement, we can copy the 12 elements in and
// then transpose them to the right place. We do this so we can
// pull out a Quaternion.
memcpy(&m._11, &pose.mDeviceToAbsoluteTracking, sizeof(pose.mDeviceToAbsoluteTracking));
m.Transpose();
gfx::Quaternion rot;
rot.SetFromRotationMatrix(m);
rot.Invert();
result.flags |= VRDisplayCapabilityFlags::Cap_Orientation;
result.pose.orientation[0] = rot.x;
result.pose.orientation[1] = rot.y;
result.pose.orientation[2] = rot.z;
result.pose.orientation[3] = rot.w;
result.pose.angularVelocity[0] = pose.vAngularVelocity.v[0];
result.pose.angularVelocity[1] = pose.vAngularVelocity.v[1];
result.pose.angularVelocity[2] = pose.vAngularVelocity.v[2];
result.flags |= VRDisplayCapabilityFlags::Cap_Position;
result.pose.position[0] = m._41;
result.pose.position[1] = m._42;
result.pose.position[2] = m._43;
result.pose.linearVelocity[0] = pose.vVelocity.v[0];
result.pose.linearVelocity[1] = pose.vVelocity.v[1];
result.pose.linearVelocity[2] = pose.vVelocity.v[2];
} else {
// default to an identity quaternion
result.pose.orientation[3] = 1.0f;
}
result.CalcViewMatrices(headToEyeTransforms);
result.inputFrameID = mDisplayInfo.mFrameId;
return result;
}
void
VRDisplayOpenVR::StartPresentation()
{
if (mIsPresenting) {
return;
}
mIsPresenting = true;
mTelemetry.Clear();
mTelemetry.mPresentationStart = TimeStamp::Now();
::vr::Compositor_CumulativeStats stats;
mVRCompositor->GetCumulativeStats(&stats, sizeof(::vr::Compositor_CumulativeStats));
mTelemetry.mLastDroppedFrameCount = stats.m_nNumReprojectedFrames;
}
void
VRDisplayOpenVR::StopPresentation()
{
if (!mIsPresenting) {
return;
}
mVRCompositor->ClearLastSubmittedFrame();
mIsPresenting = false;
const TimeDuration duration = TimeStamp::Now() - mTelemetry.mPresentationStart;
Telemetry::Accumulate(Telemetry::WEBVR_USERS_VIEW_IN, 2);
Telemetry::Accumulate(Telemetry::WEBVR_TIME_SPENT_VIEWING_IN_OPENVR,
duration.ToMilliseconds());
::vr::Compositor_CumulativeStats stats;
mVRCompositor->GetCumulativeStats(&stats, sizeof(::vr::Compositor_CumulativeStats));
const uint32_t droppedFramesPerSec = (stats.m_nNumReprojectedFrames -
mTelemetry.mLastDroppedFrameCount) / duration.ToSeconds();
Telemetry::Accumulate(Telemetry::WEBVR_DROPPED_FRAMES_IN_OPENVR, droppedFramesPerSec);
}
bool
VRDisplayOpenVR::SubmitFrameOpenVRHandle(void* aTextureHandle,
::vr::ETextureType aTextureType,
const IntSize& aSize,
const gfx::Rect& aLeftEyeRect,
const gfx::Rect& aRightEyeRect)
{
MOZ_ASSERT(mSubmitThread->GetThread() == NS_GetCurrentThread());
if (!mIsPresenting) {
return false;
}
::vr::Texture_t tex;
tex.handle = aTextureHandle;
tex.eType = aTextureType;
tex.eColorSpace = ::vr::EColorSpace::ColorSpace_Auto;
::vr::VRTextureBounds_t bounds;
bounds.uMin = aLeftEyeRect.X();
bounds.vMin = 1.0 - aLeftEyeRect.Y();
bounds.uMax = aLeftEyeRect.XMost();
bounds.vMax = 1.0 - aLeftEyeRect.YMost();
::vr::EVRCompositorError err;
err = mVRCompositor->Submit(::vr::EVREye::Eye_Left, &tex, &bounds);
if (err != ::vr::EVRCompositorError::VRCompositorError_None) {
printf_stderr("OpenVR Compositor Submit() failed.\n");
}
bounds.uMin = aRightEyeRect.X();
bounds.vMin = 1.0 - aRightEyeRect.Y();
bounds.uMax = aRightEyeRect.XMost();
bounds.vMax = 1.0 - aRightEyeRect.YMost();
err = mVRCompositor->Submit(::vr::EVREye::Eye_Right, &tex, &bounds);
if (err != ::vr::EVRCompositorError::VRCompositorError_None) {
printf_stderr("OpenVR Compositor Submit() failed.\n");
}
mVRCompositor->PostPresentHandoff();
return true;
}
#if defined(XP_WIN)
bool
VRDisplayOpenVR::SubmitFrame(ID3D11Texture2D* aSource,
const IntSize& aSize,
const gfx::Rect& aLeftEyeRect,
const gfx::Rect& aRightEyeRect)
{
return SubmitFrameOpenVRHandle((void *)aSource,
::vr::ETextureType::TextureType_DirectX,
aSize, aLeftEyeRect, aRightEyeRect);
}
#elif defined(XP_MACOSX)
bool
VRDisplayOpenVR::SubmitFrame(MacIOSurface* aMacIOSurface,
const IntSize& aSize,
const gfx::Rect& aLeftEyeRect,
const gfx::Rect& aRightEyeRect)
{
const void* ioSurface = aMacIOSurface->GetIOSurfacePtr();
bool result = false;
if (ioSurface == nullptr) {
NS_WARNING("VRDisplayOpenVR::SubmitFrame() could not get an IOSurface");
} else {
result = SubmitFrameOpenVRHandle((void *)ioSurface,
::vr::ETextureType::TextureType_IOSurface,
aSize, aLeftEyeRect, aRightEyeRect);
}
return result;
}
#endif
VRControllerOpenVR::VRControllerOpenVR(dom::GamepadHand aHand, uint32_t aDisplayID,
uint32_t aNumButtons, uint32_t aNumTriggers,
uint32_t aNumAxes, const nsCString& aId)
: VRControllerHost(VRDeviceType::OpenVR, aHand, aDisplayID)
, mTrackedIndex(0)
, mVibrateThread(nullptr)
, mIsVibrateStopped(false)
{
MOZ_COUNT_CTOR_INHERITED(VRControllerOpenVR, VRControllerHost);
VRControllerState& state = mControllerInfo.mControllerState;
strncpy(state.controllerName, aId.BeginReading(), kVRControllerNameMaxLen);
state.numButtons = aNumButtons;
state.numAxes = aNumAxes;
state.numHaptics = kNumOpenVRHaptcs;
}
VRControllerOpenVR::~VRControllerOpenVR()
{
ShutdownVibrateHapticThread();
MOZ_COUNT_DTOR_INHERITED(VRControllerOpenVR, VRControllerHost);
}
void
VRControllerOpenVR::SetTrackedIndex(uint32_t aTrackedIndex)
{
mTrackedIndex = aTrackedIndex;
}
uint32_t
VRControllerOpenVR::GetTrackedIndex()
{
return mTrackedIndex;
}
float
VRControllerOpenVR::GetAxisMove(uint32_t aAxis)
{
return mControllerInfo.mControllerState.axisValue[aAxis];
}
void
VRControllerOpenVR::SetAxisMove(uint32_t aAxis, float aValue)
{
mControllerInfo.mControllerState.axisValue[aAxis] = aValue;
}
void
VRControllerOpenVR::SetTrigger(uint32_t aButton, float aValue)
{
mControllerInfo.mControllerState.triggerValue[aButton] = aValue;
}
float
VRControllerOpenVR::GetTrigger(uint32_t aButton)
{
return mControllerInfo.mControllerState.triggerValue[aButton];
}
void
VRControllerOpenVR::SetHand(dom::GamepadHand aHand)
{
mControllerInfo.mControllerState.hand = aHand;
}
void
VRControllerOpenVR::UpdateVibrateHaptic(::vr::IVRSystem* aVRSystem,
uint32_t aHapticIndex,
double aIntensity,
double aDuration,
uint64_t aVibrateIndex,
const VRManagerPromise& aPromise)
{
// UpdateVibrateHaptic() only can be called by mVibrateThread
MOZ_ASSERT(mVibrateThread->GetThread() == NS_GetCurrentThread());
// It has been interrupted by loss focus.
if (mIsVibrateStopped) {
VibrateHapticComplete(aPromise);
return;
}
// Avoid the previous vibrate event to override the new one.
if (mVibrateIndex != aVibrateIndex) {
VibrateHapticComplete(aPromise);
return;
}
const double duration = (aIntensity == 0) ? 0 : aDuration;
// We expect OpenVR to vibrate for 5 ms, but we found it only response the
// commend ~ 3.9 ms. For duration time longer than 3.9 ms, we separate them
// to a loop of 3.9 ms for make users feel that is a continuous events.
const uint32_t microSec = (duration < 3.9 ? duration : 3.9) * 1000 * aIntensity;
aVRSystem->TriggerHapticPulse(GetTrackedIndex(),
aHapticIndex, microSec);
// In OpenVR spec, it mentions TriggerHapticPulse() may not trigger another haptic pulse
// on this controller and axis combination for 5ms.
const double kVibrateRate = 5.0;
if (duration >= kVibrateRate) {
MOZ_ASSERT(mVibrateThread);
MOZ_ASSERT(mVibrateThread->IsActive());
RefPtr<Runnable> runnable =
NewRunnableMethod<::vr::IVRSystem*, uint32_t, double, double, uint64_t,
StoreCopyPassByConstLRef<VRManagerPromise>>(
"VRControllerOpenVR::UpdateVibrateHaptic",
this, &VRControllerOpenVR::UpdateVibrateHaptic, aVRSystem,
aHapticIndex, aIntensity, duration - kVibrateRate, aVibrateIndex, aPromise);
mVibrateThread->PostDelayedTask(runnable.forget(), kVibrateRate);
} else {
// The pulse has completed
VibrateHapticComplete(aPromise);
}
}
void
VRControllerOpenVR::VibrateHapticComplete(const VRManagerPromise& aPromise)
{
VRManager *vm = VRManager::Get();
CompositorThreadHolder::Loop()->PostTask(
NewRunnableMethod<StoreCopyPassByConstLRef<VRManagerPromise>>(
"VRManager::NotifyVibrateHapticCompleted",
vm, &VRManager::NotifyVibrateHapticCompleted, aPromise));
}
void
VRControllerOpenVR::VibrateHaptic(::vr::IVRSystem* aVRSystem,
uint32_t aHapticIndex,
double aIntensity,
double aDuration,
const VRManagerPromise& aPromise)
{
// Spinning up the haptics thread at the first haptics call.
if (!mVibrateThread) {
mVibrateThread = new VRThread(NS_LITERAL_CSTRING("OpenVR_Vibration"));
}
mVibrateThread->Start();
++mVibrateIndex;
mIsVibrateStopped = false;
RefPtr<Runnable> runnable =
NewRunnableMethod<::vr::IVRSystem*, uint32_t, double, double, uint64_t,
StoreCopyPassByConstLRef<VRManagerPromise>>(
"VRControllerOpenVR::UpdateVibrateHaptic",
this, &VRControllerOpenVR::UpdateVibrateHaptic, aVRSystem,
aHapticIndex, aIntensity, aDuration, mVibrateIndex, aPromise);
mVibrateThread->PostTask(runnable.forget());
}
void
VRControllerOpenVR::StopVibrateHaptic()
{
mIsVibrateStopped = true;
}
void
VRControllerOpenVR::ShutdownVibrateHapticThread()
{
StopVibrateHaptic();
if (mVibrateThread) {
mVibrateThread->Shutdown();
mVibrateThread = nullptr;
}
}
VRSystemManagerOpenVR::VRSystemManagerOpenVR()
: mVRSystem(nullptr)
, mRuntimeCheckFailed(false)
, mIsWindowsMR(false)
{
}
/*static*/ already_AddRefed<VRSystemManagerOpenVR>
VRSystemManagerOpenVR::Create()
{
MOZ_ASSERT(NS_IsMainThread());
if (!gfxPrefs::VREnabled() || !gfxPrefs::VROpenVREnabled()) {
return nullptr;
}
RefPtr<VRSystemManagerOpenVR> manager = new VRSystemManagerOpenVR();
return manager.forget();
}
void
VRSystemManagerOpenVR::Destroy()
{
Shutdown();
}
void
VRSystemManagerOpenVR::Shutdown()
{
if (mOpenVRHMD) {
mOpenVRHMD = nullptr;
}
RemoveControllers();
mVRSystem = nullptr;
}
void
VRSystemManagerOpenVR::NotifyVSync()
{
VRSystemManager::NotifyVSync();
// Avoid doing anything unless we have already
// successfully enumerated and loaded the OpenVR
// runtime.
if (mVRSystem == nullptr) {
return;
}
if (mOpenVRHMD) {
mOpenVRHMD->Refresh();
if (!mOpenVRHMD->GetIsHmdPresent()) {
// OpenVR runtime could be quit accidentally
// or a device could be disconnected.
// We free up resources and must re-initialize
// if a device is detected again later.
mOpenVRHMD = nullptr;
mVRSystem = nullptr;
}
}
}
void
VRSystemManagerOpenVR::Enumerate()
{
if (mOpenVRHMD) {
// Already enumerated, nothing more to do
return;
}
if (mRuntimeCheckFailed) {
// We have already checked for a runtime and
// know that its not installed.
return;
}
if (!::vr::VR_IsRuntimeInstalled()) {
// Runtime is not installed, remember so we don't
// continue to scan for the files
mRuntimeCheckFailed = true;
return;
}
if (!::vr::VR_IsHmdPresent()) {
// Avoid initializing if no headset is connected
return;
}
::vr::HmdError err;
::vr::VR_Init(&err, ::vr::EVRApplicationType::VRApplication_Scene);
if (err) {
return;
}
::vr::IVRSystem *system = (::vr::IVRSystem *)::vr::VR_GetGenericInterface(::vr::IVRSystem_Version, &err);
if (err || !system) {
::vr::VR_Shutdown();
return;
}
::vr::IVRChaperone *chaperone = (::vr::IVRChaperone *)::vr::VR_GetGenericInterface(::vr::IVRChaperone_Version, &err);
if (err || !chaperone) {
::vr::VR_Shutdown();
return;
}
::vr::IVRCompositor *compositor = (::vr::IVRCompositor*)::vr::VR_GetGenericInterface(::vr::IVRCompositor_Version, &err);
if (err || !compositor) {
::vr::VR_Shutdown();
return;
}
mVRSystem = system;
mOpenVRHMD = new VRDisplayOpenVR(system, chaperone, compositor);
}
bool
VRSystemManagerOpenVR::ShouldInhibitEnumeration()
{
if (VRSystemManager::ShouldInhibitEnumeration()) {
return true;
}
if (mOpenVRHMD) {
// When we find an a VR device, don't
// allow any further enumeration as it
// may get picked up redundantly by other
// API's.
return true;
}
return false;
}
void
VRSystemManagerOpenVR::GetHMDs(nsTArray<RefPtr<VRDisplayHost>>& aHMDResult)
{
if (mOpenVRHMD) {
aHMDResult.AppendElement(mOpenVRHMD);
}
}
bool
VRSystemManagerOpenVR::GetIsPresenting()
{
if (mOpenVRHMD) {
VRDisplayInfo displayInfo(mOpenVRHMD->GetDisplayInfo());
return displayInfo.GetPresentingGroups() != kVRGroupNone;
}
return false;
}
void
VRSystemManagerOpenVR::HandleInput()
{
// mVRSystem is available after VRDisplay is created
// at GetHMDs().
if (!mVRSystem) {
return;
}
RefPtr<impl::VRControllerOpenVR> controller;
// Compare with Edge, we have a wrong implementation for the vertical axis value.
// In order to not affect the current VR content, we add a workaround for yAxis.
const float yAxisInvert = (mIsWindowsMR) ? -1.0f : 1.0f;
::vr::VRControllerState_t state;
::vr::TrackedDevicePose_t poses[::vr::k_unMaxTrackedDeviceCount];
mVRSystem->GetDeviceToAbsoluteTrackingPose(::vr::TrackingUniverseSeated, 0.0f,
poses, ::vr::k_unMaxTrackedDeviceCount);
// Process OpenVR controller state
for (uint32_t i = 0; i < mOpenVRController.Length(); ++i) {
uint32_t axisIdx = 0;
uint32_t buttonIdx = 0;
uint32_t triggerIdx = 0;
controller = mOpenVRController[i];
const uint32_t trackedIndex = controller->GetTrackedIndex();
MOZ_ASSERT(mVRSystem->GetTrackedDeviceClass(trackedIndex)
== ::vr::TrackedDeviceClass_Controller ||
mVRSystem->GetTrackedDeviceClass(trackedIndex)
== ::vr::TrackedDeviceClass_GenericTracker);
// Sometimes, OpenVR controllers are not located by HMD at the initial time.
// That makes us have to update the hand info at runtime although switching controllers
// to the other hand does not have new changes at the current OpenVR SDK. But, it makes sense
// to detect hand changing at runtime.
const ::vr::ETrackedControllerRole role = mVRSystem->
GetControllerRoleForTrackedDeviceIndex(
trackedIndex);
const dom::GamepadHand hand = GetGamepadHandFromControllerRole(role);
if (hand != controller->GetHand()) {
controller->SetHand(hand);
NewHandChangeEvent(i, hand);
}
if (mVRSystem->GetControllerState(trackedIndex, &state, sizeof(state))) {
for (uint32_t j = 0; j < ::vr::k_unControllerStateAxisCount; ++j) {
const uint32_t axisType = mVRSystem->GetInt32TrackedDeviceProperty(
trackedIndex,
static_cast<::vr::TrackedDeviceProperty>(
::vr::Prop_Axis0Type_Int32 + j));
switch (axisType) {
case ::vr::EVRControllerAxisType::k_eControllerAxis_Joystick:
case ::vr::EVRControllerAxisType::k_eControllerAxis_TrackPad:
if (mIsWindowsMR) {
// Adjust the input mapping for Windows MR which has
// different order.
axisIdx = (axisIdx == 0) ? 2 : 0;
buttonIdx = (buttonIdx == 0) ? 4 : 0;
}
if (!HandleAxisMove(i, axisIdx, state.rAxis[j].x)) {
RemoveControllers();
return;
}
++axisIdx;
if (!HandleAxisMove(i, axisIdx, state.rAxis[j].y * yAxisInvert)) {
RemoveControllers();
return;
}
++axisIdx;
if (!HandleButtonPress(i, buttonIdx,
::vr::ButtonMaskFromId(
static_cast<::vr::EVRButtonId>(::vr::k_EButton_Axis0 + j)),
state.ulButtonPressed, state.ulButtonTouched)) {
RemoveControllers();
return;
}
++buttonIdx;
if (mIsWindowsMR) {
axisIdx = (axisIdx == 4) ? 2 : 4;
buttonIdx = (buttonIdx == 5) ? 1 : 2;
}
break;
case vr::EVRControllerAxisType::k_eControllerAxis_Trigger:
if (j <= 2) {
if (!HandleTriggerPress(i, buttonIdx, triggerIdx, state.rAxis[j].x)) {
RemoveControllers();
return;
}
++buttonIdx;
++triggerIdx;
} else {
// For SteamVR Knuckles.
if (!HandleTriggerPress(i, buttonIdx, triggerIdx, state.rAxis[j].x)) {
RemoveControllers();
return;
}
++buttonIdx;
++triggerIdx;
if (!HandleTriggerPress(i, buttonIdx, triggerIdx, state.rAxis[j].y)) {
RemoveControllers();
return;
}
++buttonIdx;
++triggerIdx;
}
break;
}
}
MOZ_ASSERT(axisIdx ==
controller->GetControllerInfo().GetNumAxes());
const uint64_t supportedButtons = mVRSystem->GetUint64TrackedDeviceProperty(
trackedIndex, ::vr::Prop_SupportedButtons_Uint64);
if (supportedButtons &
BTN_MASK_FROM_ID(k_EButton_A)) {
if (!HandleButtonPress(i, buttonIdx,
BTN_MASK_FROM_ID(k_EButton_A),
state.ulButtonPressed, state.ulButtonTouched)) {
RemoveControllers();
return;
}
++buttonIdx;
}
if (supportedButtons &
BTN_MASK_FROM_ID(k_EButton_Grip)) {
if (!HandleButtonPress(i, buttonIdx,
BTN_MASK_FROM_ID(k_EButton_Grip),
state.ulButtonPressed, state.ulButtonTouched)) {
RemoveControllers();
return;
}
++buttonIdx;
}
if (supportedButtons &
BTN_MASK_FROM_ID(k_EButton_ApplicationMenu)) {
if (!HandleButtonPress(i, buttonIdx,
BTN_MASK_FROM_ID(k_EButton_ApplicationMenu),
state.ulButtonPressed, state.ulButtonTouched)) {
RemoveControllers();
return;
}
++buttonIdx;
}
if (mIsWindowsMR) {
// button 4 in Windows MR has already been assigned
// to k_eControllerAxis_TrackPad.
++buttonIdx;
}
if (supportedButtons &
BTN_MASK_FROM_ID(k_EButton_DPad_Left)) {
if (!HandleButtonPress(i, buttonIdx,
BTN_MASK_FROM_ID(k_EButton_DPad_Left),
state.ulButtonPressed, state.ulButtonTouched)) {
RemoveControllers();
return;
}
++buttonIdx;
}
if (supportedButtons &
BTN_MASK_FROM_ID(k_EButton_DPad_Up)) {
if (!HandleButtonPress(i, buttonIdx,
BTN_MASK_FROM_ID(k_EButton_DPad_Up),
state.ulButtonPressed, state.ulButtonTouched)) {
RemoveControllers();
return;
}
++buttonIdx;
}
if (supportedButtons &
BTN_MASK_FROM_ID(k_EButton_DPad_Right)) {
if (!HandleButtonPress(i, buttonIdx,
BTN_MASK_FROM_ID(k_EButton_DPad_Right),
state.ulButtonPressed, state.ulButtonTouched)) {
RemoveControllers();
return;
}
++buttonIdx;
}
if (supportedButtons &
BTN_MASK_FROM_ID(k_EButton_DPad_Down)) {
if (!HandleButtonPress(i, buttonIdx,
BTN_MASK_FROM_ID(k_EButton_DPad_Down),
state.ulButtonPressed, state.ulButtonTouched)) {
RemoveControllers();
return;
}
++buttonIdx;
}
MOZ_ASSERT(buttonIdx ==
controller->GetControllerInfo().GetNumButtons());
controller->SetButtonPressed(state.ulButtonPressed);
controller->SetButtonTouched(state.ulButtonTouched);
// Start to process pose
const ::vr::TrackedDevicePose_t& pose = poses[trackedIndex];
GamepadPoseState poseState;
if (pose.bDeviceIsConnected) {
poseState.flags |= (GamepadCapabilityFlags::Cap_Orientation |
GamepadCapabilityFlags::Cap_Position);
}
if (pose.bPoseIsValid &&
pose.eTrackingResult == ::vr::TrackingResult_Running_OK) {
gfx::Matrix4x4 m;
// NOTE! mDeviceToAbsoluteTracking is a 3x4 matrix, not 4x4. But
// because of its arrangement, we can copy the 12 elements in and
// then transpose them to the right place. We do this so we can
// pull out a Quaternion.
memcpy(&m.components, &pose.mDeviceToAbsoluteTracking, sizeof(pose.mDeviceToAbsoluteTracking));
m.Transpose();
gfx::Quaternion rot;
rot.SetFromRotationMatrix(m);
rot.Invert();
poseState.orientation[0] = rot.x;
poseState.orientation[1] = rot.y;
poseState.orientation[2] = rot.z;
poseState.orientation[3] = rot.w;
poseState.angularVelocity[0] = pose.vAngularVelocity.v[0];
poseState.angularVelocity[1] = pose.vAngularVelocity.v[1];
poseState.angularVelocity[2] = pose.vAngularVelocity.v[2];
poseState.isOrientationValid = true;
poseState.position[0] = m._41;
poseState.position[1] = m._42;
poseState.position[2] = m._43;
poseState.linearVelocity[0] = pose.vVelocity.v[0];
poseState.linearVelocity[1] = pose.vVelocity.v[1];
poseState.linearVelocity[2] = pose.vVelocity.v[2];
poseState.isPositionValid = true;
}
HandlePoseTracking(i, poseState, controller);
}
}
}
bool
VRSystemManagerOpenVR::HandleButtonPress(uint32_t aControllerIdx,
uint32_t aButton,
uint64_t aButtonMask,
uint64_t aButtonPressed,
uint64_t aButtonTouched)
{
RefPtr<impl::VRControllerOpenVR> controller(mOpenVRController[aControllerIdx]);
MOZ_ASSERT(controller);
const uint64_t pressedDiff = (controller->GetButtonPressed() ^ aButtonPressed);
const uint64_t touchedDiff = (controller->GetButtonTouched() ^ aButtonTouched);
if (!pressedDiff && !touchedDiff) {
return true;
}
if (pressedDiff & aButtonMask ||
touchedDiff & aButtonMask) {
// diff & (aButtonPressed, aButtonTouched) would be true while a new button pressed or
// touched event, otherwise it is an old event and needs to notify
// the button has been released.
if (MOZ_UNLIKELY(aButton >= controller->GetControllerInfo().GetNumButtons())) {
// FIXME: Removing crash log for Bug 1488573 to investigate unmatched count.
MOZ_CRASH_UNSAFE_PRINTF("OpenVR handleButton(aButton = %d, length = %d, controller: %s.)",
aButton,
controller->GetControllerInfo().GetNumButtons(),
controller->GetControllerInfo().GetControllerName());
return false;
}
NewButtonEvent(aControllerIdx, aButton, aButtonMask & aButtonPressed,
aButtonMask & aButtonTouched,
(aButtonMask & aButtonPressed) ? 1.0L : 0.0L);
}
return true;
}
bool
VRSystemManagerOpenVR::HandleTriggerPress(uint32_t aControllerIdx,
uint32_t aButton,
uint32_t aTrigger,
float aValue)
{
RefPtr<impl::VRControllerOpenVR> controller(mOpenVRController[aControllerIdx]);
MOZ_ASSERT(controller);
const float oldValue = controller->GetTrigger(aTrigger);
// For OpenVR, the threshold value of ButtonPressed and ButtonTouched is 0.55.
// We prefer to let developers to set their own threshold for the adjustment.
// Therefore, we don't check ButtonPressed and ButtonTouched with ButtonMask here.
// we just check the button value is larger than the threshold value or not.
const float threshold = gfxPrefs::VRControllerTriggerThreshold();
// Avoid sending duplicated events in IPC channels.
if (oldValue != aValue) {
if (MOZ_UNLIKELY(aButton >= controller->GetControllerInfo().GetNumButtons())) {
// FIXME: Removing crash log for Bug 1488573 to investigate unmatched count.
MOZ_CRASH_UNSAFE_PRINTF("OpenVR handleTrigger(aButton = %d, length = %d, controller: %s.)",
aButton,
controller->GetControllerInfo().GetNumButtons(),
controller->GetControllerInfo().GetControllerName());
return false;
}
NewButtonEvent(aControllerIdx, aButton, aValue > threshold,
aValue > threshold, aValue);
controller->SetTrigger(aTrigger, aValue);
}
return true;
}
bool
VRSystemManagerOpenVR::HandleAxisMove(uint32_t aControllerIdx, uint32_t aAxis,
float aValue)
{
RefPtr<impl::VRControllerOpenVR> controller(mOpenVRController[aControllerIdx]);
MOZ_ASSERT(controller);
if (controller->GetAxisMove(aAxis) != aValue) {
if (MOZ_UNLIKELY(aAxis >= controller->GetControllerInfo().GetNumAxes())) {
// FIXME: Removing crash log for Bug 1488573 to investigate unmatched count.
MOZ_CRASH_UNSAFE_PRINTF("OpenVR handleAxis(aAxis = %d, length = %d, controller: %s.)",
aAxis,
controller->GetControllerInfo().GetNumAxes(),
controller->GetControllerInfo().GetControllerName());
return false;
}
NewAxisMove(aControllerIdx, aAxis, aValue);
controller->SetAxisMove(aAxis, aValue);
}
return true;
}
void
VRSystemManagerOpenVR::HandlePoseTracking(uint32_t aControllerIdx,
const GamepadPoseState& aPose,
VRControllerHost* aController)
{
MOZ_ASSERT(aController);
if (aPose != aController->GetPose()) {
aController->SetPose(aPose);
NewPoseState(aControllerIdx, aPose);
}
}
dom::GamepadHand
VRSystemManagerOpenVR::GetGamepadHandFromControllerRole(
::vr::ETrackedControllerRole aRole)
{
dom::GamepadHand hand;
switch(aRole) {
case ::vr::ETrackedControllerRole::TrackedControllerRole_Invalid:
case ::vr::ETrackedControllerRole::TrackedControllerRole_OptOut:
hand = dom::GamepadHand::_empty;
break;
case ::vr::ETrackedControllerRole::TrackedControllerRole_LeftHand:
hand = dom::GamepadHand::Left;
break;
case ::vr::ETrackedControllerRole::TrackedControllerRole_RightHand:
hand = dom::GamepadHand::Right;
break;
default:
hand = dom::GamepadHand::_empty;
MOZ_ASSERT(false);
break;
}
return hand;
}
void
VRSystemManagerOpenVR::VibrateHaptic(uint32_t aControllerIdx,
uint32_t aHapticIndex,
double aIntensity,
double aDuration,
const VRManagerPromise& aPromise)
{
// mVRSystem is available after VRDisplay is created
// at GetHMDs().
if (!mVRSystem || (aControllerIdx >= mOpenVRController.Length())) {
return;
}
RefPtr<impl::VRControllerOpenVR> controller = mOpenVRController[aControllerIdx];
MOZ_ASSERT(controller);
controller->VibrateHaptic(mVRSystem, aHapticIndex, aIntensity, aDuration, aPromise);
}
void
VRSystemManagerOpenVR::StopVibrateHaptic(uint32_t aControllerIdx)
{
// mVRSystem is available after VRDisplay is created
// at GetHMDs().
if (!mVRSystem || (aControllerIdx >= mOpenVRController.Length())) {
return;
}
RefPtr<impl::VRControllerOpenVR> controller = mOpenVRController[aControllerIdx];
MOZ_ASSERT(controller);
controller->StopVibrateHaptic();
}
void
VRSystemManagerOpenVR::GetControllers(nsTArray<RefPtr<VRControllerHost>>& aControllerResult)
{
aControllerResult.Clear();
for (uint32_t i = 0; i < mOpenVRController.Length(); ++i) {
aControllerResult.AppendElement(mOpenVRController[i]);
}
}
void
VRSystemManagerOpenVR::ScanForControllers()
{
// mVRSystem is available after VRDisplay is created
// at GetHMDs().
if (!mVRSystem) {
return;
}
::vr::TrackedDeviceIndex_t trackedIndexArray[::vr::k_unMaxTrackedDeviceCount];
uint32_t newControllerCount = 0;
// Basically, we would have HMDs in the tracked devices,
// but we are just interested in the controllers.
for (::vr::TrackedDeviceIndex_t trackedDevice = ::vr::k_unTrackedDeviceIndex_Hmd + 1;
trackedDevice < ::vr::k_unMaxTrackedDeviceCount; ++trackedDevice) {
if (!mVRSystem->IsTrackedDeviceConnected(trackedDevice)) {
continue;
}
const ::vr::ETrackedDeviceClass deviceType = mVRSystem->
GetTrackedDeviceClass(trackedDevice);
if (deviceType != ::vr::TrackedDeviceClass_Controller
&& deviceType != ::vr::TrackedDeviceClass_GenericTracker) {
continue;
}
trackedIndexArray[newControllerCount] = trackedDevice;
++newControllerCount;
}
if (newControllerCount != mControllerCount) {
RemoveControllers();
// Re-adding controllers to VRControllerManager.
for (::vr::TrackedDeviceIndex_t i = 0; i < newControllerCount; ++i) {
const ::vr::TrackedDeviceIndex_t trackedDevice = trackedIndexArray[i];
const ::vr::ETrackedDeviceClass deviceType = mVRSystem->
GetTrackedDeviceClass(trackedDevice);
const ::vr::ETrackedControllerRole role = mVRSystem->
GetControllerRoleForTrackedDeviceIndex(
trackedDevice);
const GamepadHand hand = GetGamepadHandFromControllerRole(role);
uint32_t numButtons = 0;
uint32_t numTriggers = 0;
uint32_t numAxes = 0;
// Scan the axes that the controllers support
for (uint32_t j = 0; j < ::vr::k_unControllerStateAxisCount; ++j) {
const uint32_t supportAxis = mVRSystem->GetInt32TrackedDeviceProperty(trackedDevice,
static_cast<vr::TrackedDeviceProperty>(
::vr::Prop_Axis0Type_Int32 + j));
switch (supportAxis) {
case ::vr::EVRControllerAxisType::k_eControllerAxis_Joystick:
case ::vr::EVRControllerAxisType::k_eControllerAxis_TrackPad:
numAxes += 2; // It has x and y axes.
++numButtons;
break;
case ::vr::k_eControllerAxis_Trigger:
if (j <= 2) {
++numButtons;
++numTriggers;
} else {
#ifdef DEBUG
// SteamVR Knuckles is the only special case for using 2D axis values on triggers.
::vr::ETrackedPropertyError err;
uint32_t requiredBufferLen;
char charBuf[128];
requiredBufferLen = mVRSystem->GetStringTrackedDeviceProperty(trackedDevice,
::vr::Prop_RenderModelName_String, charBuf, 128, &err);
MOZ_ASSERT(requiredBufferLen && err == ::vr::TrackedProp_Success);
nsCString deviceId(charBuf);
MOZ_ASSERT(deviceId.Find("knuckles") != kNotFound);
#endif // #ifdef DEBUG
numButtons += 2;
numTriggers += 2;
}
break;
}
}
// Scan the buttons that the controllers support
const uint64_t supportButtons = mVRSystem->GetUint64TrackedDeviceProperty(
trackedDevice, ::vr::Prop_SupportedButtons_Uint64);
if (supportButtons &
BTN_MASK_FROM_ID(k_EButton_A)) {
++numButtons;
}
if (supportButtons &
BTN_MASK_FROM_ID(k_EButton_Grip)) {
++numButtons;
}
if (supportButtons &
BTN_MASK_FROM_ID(k_EButton_ApplicationMenu)) {
++numButtons;
}
if (supportButtons &
BTN_MASK_FROM_ID(k_EButton_DPad_Left)) {
++numButtons;
}
if (supportButtons &
BTN_MASK_FROM_ID(k_EButton_DPad_Up)) {
++numButtons;
}
if (supportButtons &
BTN_MASK_FROM_ID(k_EButton_DPad_Right)) {
++numButtons;
}
if (supportButtons &
BTN_MASK_FROM_ID(k_EButton_DPad_Down)) {
++numButtons;
}
nsCString deviceId;
GetControllerDeviceId(deviceType, trackedDevice, deviceId);
RefPtr<VRControllerOpenVR> openVRController =
new VRControllerOpenVR(hand, mOpenVRHMD->GetDisplayInfo().GetDisplayID(),
numButtons, numTriggers, numAxes, deviceId);
openVRController->SetTrackedIndex(trackedDevice);
mOpenVRController.AppendElement(openVRController);
// If the Windows MR controller doesn't has the amount
// of buttons or axes as our expectation, switching off
// the workaround for Windows MR.
if (mIsWindowsMR && (numAxes < 4 || numButtons < 5)) {
mIsWindowsMR = false;
NS_WARNING("OpenVR - Switching off Windows MR mode.");
}
// Not already present, add it.
AddGamepad(openVRController->GetControllerInfo());
++mControllerCount;
}
}
}
void
VRSystemManagerOpenVR::RemoveControllers()
{
// The controller count is changed, removing the existing gamepads first.
for (uint32_t i = 0; i < mOpenVRController.Length(); ++i) {
mOpenVRController[i]->ShutdownVibrateHapticThread();
RemoveGamepad(i);
}
mOpenVRController.Clear();
mControllerCount = 0;
}
void
VRSystemManagerOpenVR::GetControllerDeviceId(::vr::ETrackedDeviceClass aDeviceType,
::vr::TrackedDeviceIndex_t aDeviceIndex, nsCString& aId)
{
switch (aDeviceType) {
case ::vr::TrackedDeviceClass_Controller:
{
::vr::ETrackedPropertyError err;
uint32_t requiredBufferLen;
bool isFound = false;
char charBuf[128];
requiredBufferLen = mVRSystem->GetStringTrackedDeviceProperty(aDeviceIndex,
::vr::Prop_RenderModelName_String, charBuf, 128, &err);
if (requiredBufferLen > 128) {
MOZ_CRASH("Larger than the buffer size.");
}
MOZ_ASSERT(requiredBufferLen && err == ::vr::TrackedProp_Success);
nsCString deviceId(charBuf);
if (deviceId.Find("knuckles") != kNotFound) {
aId.AssignLiteral("OpenVR Knuckles");
isFound = true;
}
requiredBufferLen = mVRSystem->GetStringTrackedDeviceProperty(aDeviceIndex,
::vr::Prop_SerialNumber_String, charBuf, 128, &err);
if (requiredBufferLen > 128) {
MOZ_CRASH("Larger than the buffer size.");
}
MOZ_ASSERT(requiredBufferLen && err == ::vr::TrackedProp_Success);
deviceId.Assign(charBuf);
if (deviceId.Find("MRSOURCE") != kNotFound) {
aId.AssignLiteral("Spatial Controller (Spatial Interaction Source) ");
mIsWindowsMR = true;
isFound = true;
}
if (!isFound) {
aId.AssignLiteral("OpenVR Gamepad");
}
break;
}
case ::vr::TrackedDeviceClass_GenericTracker:
{
aId.AssignLiteral("OpenVR Tracker");
break;
}
default:
MOZ_ASSERT(false);
break;
}
}