/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 | } |