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/. */
8 # error "Oculus support only available for Windows"
14 #include "mozilla/StaticPrefs_dom.h"
15 #include "mozilla/dom/GamepadEventTypes.h"
16 #include "mozilla/dom/GamepadBinding.h"
17 #include "mozilla/gfx/DeviceManagerDx.h"
18 #include "mozilla/gfx/Logging.h"
19 #include "mozilla/SharedLibrary.h"
20 #include "OculusSession.h"
22 /** XXX The DX11 objects and quad blitting could be encapsulated
23 * into a separate object if either Oculus starts supporting
24 * non-Windows platforms or the blit is needed by other HMD\
26 * Alternately, we could remove the extra blit for
27 * Oculus as well with some more refactoring.
30 // See CompositorD3D11Shaders.h
37 extern ShaderBytes sRGBShader
;
38 extern ShaderBytes sLayerQuadVS
;
40 } // namespace mozilla
42 using namespace mozilla
;
43 using namespace mozilla::gfx
;
44 using namespace mozilla::layers
;
48 static pfn_ovr_Initialize ovr_Initialize
= nullptr;
49 static pfn_ovr_Shutdown ovr_Shutdown
= nullptr;
50 static pfn_ovr_GetLastErrorInfo ovr_GetLastErrorInfo
= nullptr;
51 static pfn_ovr_GetVersionString ovr_GetVersionString
= nullptr;
52 static pfn_ovr_TraceMessage ovr_TraceMessage
= nullptr;
53 static pfn_ovr_IdentifyClient ovr_IdentifyClient
= nullptr;
54 static pfn_ovr_GetHmdDesc ovr_GetHmdDesc
= nullptr;
55 static pfn_ovr_GetTrackerCount ovr_GetTrackerCount
= nullptr;
56 static pfn_ovr_GetTrackerDesc ovr_GetTrackerDesc
= nullptr;
57 static pfn_ovr_Create ovr_Create
= nullptr;
58 static pfn_ovr_Destroy ovr_Destroy
= nullptr;
59 static pfn_ovr_GetSessionStatus ovr_GetSessionStatus
= nullptr;
60 static pfn_ovr_IsExtensionSupported ovr_IsExtensionSupported
= nullptr;
61 static pfn_ovr_EnableExtension ovr_EnableExtension
= nullptr;
62 static pfn_ovr_SetTrackingOriginType ovr_SetTrackingOriginType
= nullptr;
63 static pfn_ovr_GetTrackingOriginType ovr_GetTrackingOriginType
= nullptr;
64 static pfn_ovr_RecenterTrackingOrigin ovr_RecenterTrackingOrigin
= nullptr;
65 static pfn_ovr_SpecifyTrackingOrigin ovr_SpecifyTrackingOrigin
= nullptr;
66 static pfn_ovr_ClearShouldRecenterFlag ovr_ClearShouldRecenterFlag
= nullptr;
67 static pfn_ovr_GetTrackingState ovr_GetTrackingState
= nullptr;
68 static pfn_ovr_GetDevicePoses ovr_GetDevicePoses
= nullptr;
69 static pfn_ovr_GetTrackerPose ovr_GetTrackerPose
= nullptr;
70 static pfn_ovr_GetInputState ovr_GetInputState
= nullptr;
71 static pfn_ovr_GetConnectedControllerTypes ovr_GetConnectedControllerTypes
=
73 static pfn_ovr_GetTouchHapticsDesc ovr_GetTouchHapticsDesc
= nullptr;
74 static pfn_ovr_SetControllerVibration ovr_SetControllerVibration
= nullptr;
75 static pfn_ovr_SubmitControllerVibration ovr_SubmitControllerVibration
=
77 static pfn_ovr_GetControllerVibrationState ovr_GetControllerVibrationState
=
79 static pfn_ovr_TestBoundary ovr_TestBoundary
= nullptr;
80 static pfn_ovr_TestBoundaryPoint ovr_TestBoundaryPoint
= nullptr;
81 static pfn_ovr_SetBoundaryLookAndFeel ovr_SetBoundaryLookAndFeel
= nullptr;
82 static pfn_ovr_ResetBoundaryLookAndFeel ovr_ResetBoundaryLookAndFeel
= nullptr;
83 static pfn_ovr_GetBoundaryGeometry ovr_GetBoundaryGeometry
= nullptr;
84 static pfn_ovr_GetBoundaryDimensions ovr_GetBoundaryDimensions
= nullptr;
85 static pfn_ovr_GetBoundaryVisible ovr_GetBoundaryVisible
= nullptr;
86 static pfn_ovr_RequestBoundaryVisible ovr_RequestBoundaryVisible
= nullptr;
87 static pfn_ovr_GetTextureSwapChainLength ovr_GetTextureSwapChainLength
=
89 static pfn_ovr_GetTextureSwapChainCurrentIndex
90 ovr_GetTextureSwapChainCurrentIndex
= nullptr;
91 static pfn_ovr_GetTextureSwapChainDesc ovr_GetTextureSwapChainDesc
= nullptr;
92 static pfn_ovr_CommitTextureSwapChain ovr_CommitTextureSwapChain
= nullptr;
93 static pfn_ovr_DestroyTextureSwapChain ovr_DestroyTextureSwapChain
= nullptr;
94 static pfn_ovr_DestroyMirrorTexture ovr_DestroyMirrorTexture
= nullptr;
95 static pfn_ovr_GetFovTextureSize ovr_GetFovTextureSize
= nullptr;
96 static pfn_ovr_GetRenderDesc2 ovr_GetRenderDesc2
= nullptr;
97 static pfn_ovr_WaitToBeginFrame ovr_WaitToBeginFrame
= nullptr;
98 static pfn_ovr_BeginFrame ovr_BeginFrame
= nullptr;
99 static pfn_ovr_EndFrame ovr_EndFrame
= nullptr;
100 static pfn_ovr_SubmitFrame ovr_SubmitFrame
= nullptr;
101 static pfn_ovr_GetPerfStats ovr_GetPerfStats
= nullptr;
102 static pfn_ovr_ResetPerfStats ovr_ResetPerfStats
= nullptr;
103 static pfn_ovr_GetPredictedDisplayTime ovr_GetPredictedDisplayTime
= nullptr;
104 static pfn_ovr_GetTimeInSeconds ovr_GetTimeInSeconds
= nullptr;
105 static pfn_ovr_GetBool ovr_GetBool
= nullptr;
106 static pfn_ovr_SetBool ovr_SetBool
= nullptr;
107 static pfn_ovr_GetInt ovr_GetInt
= nullptr;
108 static pfn_ovr_SetInt ovr_SetInt
= nullptr;
109 static pfn_ovr_GetFloat ovr_GetFloat
= nullptr;
110 static pfn_ovr_SetFloat ovr_SetFloat
= nullptr;
111 static pfn_ovr_GetFloatArray ovr_GetFloatArray
= nullptr;
112 static pfn_ovr_SetFloatArray ovr_SetFloatArray
= nullptr;
113 static pfn_ovr_GetString ovr_GetString
= nullptr;
114 static pfn_ovr_SetString ovr_SetString
= nullptr;
115 static pfn_ovr_GetExternalCameras ovr_GetExternalCameras
= nullptr;
116 static pfn_ovr_SetExternalCameraProperties ovr_SetExternalCameraProperties
=
120 static pfn_ovr_CreateTextureSwapChainDX ovr_CreateTextureSwapChainDX
= nullptr;
121 static pfn_ovr_GetTextureSwapChainBufferDX ovr_GetTextureSwapChainBufferDX
=
123 static pfn_ovr_CreateMirrorTextureDX ovr_CreateMirrorTextureDX
= nullptr;
124 static pfn_ovr_GetMirrorTextureBufferDX ovr_GetMirrorTextureBufferDX
= nullptr;
127 static pfn_ovr_CreateTextureSwapChainGL ovr_CreateTextureSwapChainGL
= nullptr;
128 static pfn_ovr_GetTextureSwapChainBufferGL ovr_GetTextureSwapChainBufferGL
=
130 static pfn_ovr_CreateMirrorTextureGL ovr_CreateMirrorTextureGL
= nullptr;
131 static pfn_ovr_GetMirrorTextureBufferGL ovr_GetMirrorTextureBufferGL
= nullptr;
133 #ifdef HAVE_64BIT_BUILD
134 # define BUILD_BITS 64
136 # define BUILD_BITS 32
139 #define OVR_PRODUCT_VERSION 1
140 #define OVR_MAJOR_VERSION 1
141 #define OVR_MINOR_VERSION 19
143 static const uint32_t kNumOculusButtons
= 7;
144 static const uint32_t kNumOculusHaptcs
= 1;
145 static const uint32_t kNumOculusAxes
= 4;
146 ovrControllerType OculusControllerTypes
[2] = {ovrControllerType_LTouch
,
147 ovrControllerType_RTouch
};
148 const char* OculusControllerNames
[2] = {"Oculus Touch (Left)",
149 "Oculus Touch (Right)"};
150 dom::GamepadHand OculusControllerHand
[2] = {dom::GamepadHand::Left
,
151 dom::GamepadHand::Right
};
153 ovrButton OculusControllerButtons
[2][kNumOculusButtons
] = {
154 {(ovrButton
)0, (ovrButton
)0, (ovrButton
)0, ovrButton_LThumb
, ovrButton_X
,
155 ovrButton_Y
, (ovrButton
)0},
156 {(ovrButton
)0, (ovrButton
)0, (ovrButton
)0, ovrButton_RThumb
, ovrButton_A
,
157 ovrButton_B
, (ovrButton
)0},
160 ovrTouch OculusControllerTouches
[2][kNumOculusButtons
] = {
161 {ovrTouch_LIndexTrigger
, (ovrTouch
)0, (ovrTouch
)0, ovrTouch_LThumb
,
162 ovrTouch_X
, ovrTouch_Y
, ovrTouch_LThumbRest
},
163 {ovrTouch_RIndexTrigger
, (ovrTouch
)0, (ovrTouch
)0, ovrTouch_RThumb
,
164 ovrTouch_A
, ovrTouch_B
, ovrTouch_RThumbRest
},
167 void UpdateButton(const ovrInputState
& aInputState
, uint32_t aHandIdx
,
168 uint32_t aButtonIdx
, VRControllerState
& aControllerState
) {
169 if (aInputState
.Buttons
& OculusControllerButtons
[aHandIdx
][aButtonIdx
]) {
170 aControllerState
.buttonPressed
|= ((uint64_t)1 << aButtonIdx
);
171 aControllerState
.triggerValue
[aButtonIdx
] = 1.0f
;
173 aControllerState
.triggerValue
[aButtonIdx
] = 0.0f
;
175 if (aInputState
.Touches
& OculusControllerTouches
[aHandIdx
][aButtonIdx
]) {
176 aControllerState
.buttonTouched
|= ((uint64_t)1 << aButtonIdx
);
180 VRFieldOfView
FromFovPort(const ovrFovPort
& aFOV
) {
181 VRFieldOfView fovInfo
;
182 fovInfo
.leftDegrees
= atan(aFOV
.LeftTan
) * 180.0 / M_PI
;
183 fovInfo
.rightDegrees
= atan(aFOV
.RightTan
) * 180.0 / M_PI
;
184 fovInfo
.upDegrees
= atan(aFOV
.UpTan
) * 180.0 / M_PI
;
185 fovInfo
.downDegrees
= atan(aFOV
.DownTan
) * 180.0 / M_PI
;
189 } // anonymous namespace
194 OculusSession::OculusSession()
198 mInitFlags((ovrInitFlags
)0),
199 mTextureSet(nullptr),
202 mLinearSamplerState(nullptr),
203 mVSConstantBuffer(nullptr),
204 mPSConstantBuffer(nullptr),
205 mVertexBuffer(nullptr),
206 mInputLayout(nullptr),
207 mRemainingVibrateTime
{},
208 mHapticPulseIntensity
{},
209 mIsPresenting(false) {}
211 OculusSession::~OculusSession() { Shutdown(); }
213 bool OculusSession::Initialize(mozilla::gfx::VRSystemState
& aSystemState
,
214 bool aDetectRuntimesOnly
) {
215 if (StaticPrefs::dom_vr_puppet_enabled()) {
216 // Ensure that tests using the VR Puppet do not find real hardware
219 if (!StaticPrefs::dom_vr_enabled() || !StaticPrefs::dom_vr_oculus_enabled()) {
223 if (aDetectRuntimesOnly
) {
225 aSystemState
.displayState
.capabilityFlags
|=
226 VRDisplayCapabilityFlags::Cap_ImmersiveVR
;
231 if (!CreateD3DObjects()) {
234 if (!CreateShaders()) {
237 // Ideally, we should move LoadOvrLib() up to the first line to avoid
238 // unnecessary D3D objects creation. But it will cause a WPT fail in Win 7
243 // We start off with an invisible session, then re-initialize
244 // with visible session once WebVR content starts rendering.
245 if (!ChangeVisibility(false)) {
248 if (!InitState(aSystemState
)) {
252 mPresentationSize
= IntSize(aSystemState
.displayState
.eyeResolution
.width
* 2,
253 aSystemState
.displayState
.eyeResolution
.height
);
257 void OculusSession::UpdateVisibility() {
258 // Do not immediately re-initialize with an invisible session after
259 // the end of a VR presentation. Waiting for the configured duraction
260 // ensures that the user will not drop to Oculus Home during VR link
263 // We are currently rendering immersive content.
264 // Avoid interrupting the session
267 if (mInitFlags
& ovrInit_Invisible
) {
268 // We are already invisible
271 if (mLastPresentationEnd
.IsNull()) {
272 // There has been no presentation yet
276 TimeDuration duration
= TimeStamp::Now() - mLastPresentationEnd
;
277 TimeDuration timeout
= TimeDuration::FromMilliseconds(
278 StaticPrefs::dom_vr_oculus_present_timeout());
279 if (timeout
<= TimeDuration(0) || duration
>= timeout
) {
280 if (!ChangeVisibility(false)) {
281 gfxWarning() << "OculusSession::ChangeVisibility(false) failed";
286 void OculusSession::CoverTransitions() {
287 // While content is loading or during immersive-mode link
288 // traversal, we need to prevent the user from seeing the
289 // last rendered frame.
290 // We render black frames to cover up the transition.
291 MOZ_ASSERT(mSession
);
293 // We are currently rendering immersive content.
294 // Avoid interrupting the session
298 if (mInitFlags
& ovrInit_Invisible
) {
299 // We are invisible, nothing to cover up
303 // Render a black frame
304 ovrLayerEyeFov layer
;
305 memset(&layer
, 0, sizeof(layer
));
306 layer
.Header
.Type
= ovrLayerType_Disabled
;
307 ovrLayerHeader
* layers
= &layer
.Header
;
308 ovr_SubmitFrame(mSession
, 0, nullptr, &layers
, 1);
311 bool OculusSession::ChangeVisibility(bool bVisible
) {
313 (ovrInitFlags
)(ovrInit_RequestVersion
| ovrInit_MixedRendering
);
314 if (StaticPrefs::dom_vr_oculus_invisible_enabled() && !bVisible
) {
315 flags
= (ovrInitFlags
)(flags
| ovrInit_Invisible
);
317 if (mInitFlags
== flags
) {
318 // The new state is the same, nothing to do
322 // Tear everything down
328 if (!StartLib(flags
)) {
331 if (!StartSession()) {
337 void OculusSession::Shutdown() {
345 void OculusSession::ProcessEvents(mozilla::gfx::VRSystemState
& aSystemState
) {
350 ovrSessionStatus status
;
351 if (OVR_SUCCESS(ovr_GetSessionStatus(mSession
, &status
))) {
352 aSystemState
.displayState
.isConnected
= status
.HmdPresent
;
353 aSystemState
.displayState
.isMounted
= status
.HmdMounted
;
354 mShouldQuit
= status
.ShouldQuit
;
357 aSystemState
.displayState
.isConnected
= false;
358 aSystemState
.displayState
.isMounted
= false;
365 void OculusSession::StartFrame(mozilla::gfx::VRSystemState
& aSystemState
) {
366 UpdateHeadsetPose(aSystemState
);
367 UpdateEyeParameters(aSystemState
);
368 UpdateControllers(aSystemState
);
369 UpdateTelemetry(aSystemState
);
370 aSystemState
.sensorState
.inputFrameID
++;
373 bool OculusSession::StartPresentation() {
375 * XXX - We should resolve fail the promise returned by
376 * VRDisplay.requestPresent() when the DX11 resources fail allocation
377 * in VRDisplayOculus::StartPresentation().
378 * Bailing out here prevents the crash but content should be aware
379 * that frames are not being presented.
382 if (!ChangeVisibility(true)) {
385 if (!StartRendering()) {
389 mIsPresenting
= true;
393 void OculusSession::StopPresentation() {
394 mLastPresentationEnd
= TimeStamp::Now();
395 mIsPresenting
= false;
398 bool OculusSession::SubmitFrame(
399 const mozilla::gfx::VRLayer_Stereo_Immersive
& aLayer
,
400 ID3D11Texture2D
* aTexture
) {
401 if (!IsPresentationReady()) {
405 D3D11_TEXTURE2D_DESC textureDesc
= {0};
406 aTexture
->GetDesc(&textureDesc
);
408 int currentRenderTarget
= 0;
409 ovrResult orv
= ovr_GetTextureSwapChainCurrentIndex(mSession
, mTextureSet
,
410 ¤tRenderTarget
);
411 if (orv
!= ovrSuccess
) {
412 NS_WARNING("ovr_GetTextureSwapChainCurrentIndex failed.");
416 ID3D11RenderTargetView
* view
= mRTView
[currentRenderTarget
];
418 float clear
[] = {0.0f
, 0.0f
, 0.0f
, 1.0f
};
419 mContext
->ClearRenderTargetView(view
, clear
);
420 mContext
->OMSetRenderTargets(1, &view
, nullptr);
422 Matrix viewMatrix
= Matrix::Translation(-1.0, 1.0);
423 viewMatrix
.PreScale(2.0f
/ float(textureDesc
.Width
),
424 2.0f
/ float(textureDesc
.Height
));
425 viewMatrix
.PreScale(1.0f
, -1.0f
);
426 Matrix4x4 projection
= Matrix4x4::From2D(viewMatrix
);
427 projection
._33
= 0.0f
;
430 gfx::Matrix4x4 transform
= gfx::Matrix4x4::From2D(transform2d
);
432 D3D11_VIEWPORT viewport
;
433 viewport
.MinDepth
= 0.0f
;
434 viewport
.MaxDepth
= 1.0f
;
435 viewport
.Width
= textureDesc
.Width
;
436 viewport
.Height
= textureDesc
.Height
;
437 viewport
.TopLeftX
= 0;
438 viewport
.TopLeftY
= 0;
442 scissor
.right
= textureDesc
.Width
;
444 scissor
.bottom
= textureDesc
.Height
;
446 memcpy(&mVSConstants
.layerTransform
, &transform
._11
,
447 sizeof(mVSConstants
.layerTransform
));
448 memcpy(&mVSConstants
.projection
, &projection
._11
,
449 sizeof(mVSConstants
.projection
));
450 mVSConstants
.renderTargetOffset
[0] = 0.0f
;
451 mVSConstants
.renderTargetOffset
[1] = 0.0f
;
452 mVSConstants
.layerQuad
=
453 Rect(0.0f
, 0.0f
, textureDesc
.Width
, textureDesc
.Height
);
454 mVSConstants
.textureCoords
= Rect(0.0f
, 1.0f
, 1.0f
, -1.0f
);
456 mPSConstants
.layerOpacity
[0] = 1.0f
;
458 ID3D11Buffer
* vbuffer
= mVertexBuffer
;
459 UINT vsize
= sizeof(Vertex
);
461 mContext
->IASetVertexBuffers(0, 1, &vbuffer
, &vsize
, &voffset
);
462 mContext
->IASetIndexBuffer(nullptr, DXGI_FORMAT_R16_UINT
, 0);
463 mContext
->IASetInputLayout(mInputLayout
);
464 mContext
->RSSetViewports(1, &viewport
);
465 mContext
->RSSetScissorRects(1, &scissor
);
466 mContext
->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP
);
467 mContext
->VSSetShader(mQuadVS
, nullptr, 0);
468 mContext
->PSSetShader(mQuadPS
, nullptr, 0);
470 RefPtr
<ID3D11ShaderResourceView
> srView
;
471 HRESULT hr
= mDevice
->CreateShaderResourceView(aTexture
, nullptr,
472 getter_AddRefs(srView
));
474 gfxWarning() << "Could not create shader resource view for Oculus: "
478 ID3D11ShaderResourceView
* viewPtr
= srView
.get();
479 mContext
->PSSetShaderResources(0 /* 0 == TexSlot::RGB */, 1, &viewPtr
);
480 // XXX Use Constant from TexSlot in CompositorD3D11.cpp?
482 ID3D11SamplerState
* sampler
= mLinearSamplerState
;
483 mContext
->PSSetSamplers(0, 1, &sampler
);
485 if (!UpdateConstantBuffers()) {
486 NS_WARNING("Failed to update constant buffers for Oculus");
490 mContext
->Draw(4, 0);
492 orv
= ovr_CommitTextureSwapChain(mSession
, mTextureSet
);
493 if (orv
!= ovrSuccess
) {
494 NS_WARNING("ovr_CommitTextureSwapChain failed.");
498 ovrLayerEyeFov layer
;
499 memset(&layer
, 0, sizeof(layer
));
500 layer
.Header
.Type
= ovrLayerType_EyeFov
;
501 layer
.Header
.Flags
= 0;
502 layer
.ColorTexture
[0] = mTextureSet
;
503 layer
.ColorTexture
[1] = nullptr;
504 layer
.Fov
[0] = mFOVPort
[0];
505 layer
.Fov
[1] = mFOVPort
[1];
506 layer
.Viewport
[0].Pos
.x
= textureDesc
.Width
* aLayer
.leftEyeRect
.x
;
507 layer
.Viewport
[0].Pos
.y
= textureDesc
.Height
* aLayer
.leftEyeRect
.y
;
508 layer
.Viewport
[0].Size
.w
= textureDesc
.Width
* aLayer
.leftEyeRect
.width
;
509 layer
.Viewport
[0].Size
.h
= textureDesc
.Height
* aLayer
.leftEyeRect
.height
;
510 layer
.Viewport
[1].Pos
.x
= textureDesc
.Width
* aLayer
.rightEyeRect
.x
;
511 layer
.Viewport
[1].Pos
.y
= textureDesc
.Height
* aLayer
.rightEyeRect
.y
;
512 layer
.Viewport
[1].Size
.w
= textureDesc
.Width
* aLayer
.rightEyeRect
.width
;
513 layer
.Viewport
[1].Size
.h
= textureDesc
.Height
* aLayer
.rightEyeRect
.height
;
515 for (uint32_t i
= 0; i
< 2; ++i
) {
516 layer
.RenderPose
[i
].Orientation
.x
= mFrameStartPose
[i
].Orientation
.x
;
517 layer
.RenderPose
[i
].Orientation
.y
= mFrameStartPose
[i
].Orientation
.y
;
518 layer
.RenderPose
[i
].Orientation
.z
= mFrameStartPose
[i
].Orientation
.z
;
519 layer
.RenderPose
[i
].Orientation
.w
= mFrameStartPose
[i
].Orientation
.w
;
520 layer
.RenderPose
[i
].Position
.x
= mFrameStartPose
[i
].Position
.x
;
521 layer
.RenderPose
[i
].Position
.y
= mFrameStartPose
[i
].Position
.y
;
522 layer
.RenderPose
[i
].Position
.z
= mFrameStartPose
[i
].Position
.z
;
525 ovrLayerHeader
* layers
= &layer
.Header
;
526 orv
= ovr_SubmitFrame(mSession
, 0, nullptr, &layers
, 1);
527 // ovr_SubmitFrame will fail during the Oculus health and safety warning.
528 // and will start succeeding once the warning has been dismissed by the user.
530 if (!OVR_UNQUALIFIED_SUCCESS(orv
)) {
532 * We wish to throttle the framerate for any case that the rendered
533 * result is not visible. In some cases, such as during the Oculus
534 * "health and safety warning", orv will be > 0 (OVR_SUCCESS but not
535 * OVR_UNQUALIFIED_SUCCESS) and ovr_SubmitFrame will not block.
536 * In this case, returning true would have resulted in an unthrottled
537 * render loop hiting excessive frame rates and consuming resources.
545 bool OculusSession::LoadOvrLib() {
547 // Already loaded, early exit
551 nsTArray
<nsString
> libSearchPaths
;
556 UINT requiredLength
= ::GetSystemDirectoryW(
557 char16ptr_t(searchPath
.BeginWriting()), searchPath
.Length());
558 if (!requiredLength
) {
561 if (requiredLength
< searchPath
.Length()) {
562 searchPath
.Truncate(requiredLength
);
563 libSearchPaths
.AppendElement(searchPath
);
566 searchPath
.SetLength(requiredLength
);
568 libName
.AppendPrintf("LibOVRRT%d_%d.dll", BUILD_BITS
, OVR_PRODUCT_VERSION
);
570 // search the path/module dir
571 libSearchPaths
.InsertElementsAt(0, 1, u
""_ns
);
573 // If the env var is present, we override libName
574 if (_wgetenv(L
"OVR_LIB_PATH")) {
575 searchPath
= _wgetenv(L
"OVR_LIB_PATH");
576 libSearchPaths
.InsertElementsAt(0, 1, searchPath
);
579 if (_wgetenv(L
"OVR_LIB_NAME")) {
580 libName
= _wgetenv(L
"OVR_LIB_NAME");
583 if (libName
.IsEmpty()) {
587 for (uint32_t i
= 0; i
< libSearchPaths
.Length(); ++i
) {
588 nsString
& libPath
= libSearchPaths
[i
];
590 if (libPath
.Length() == 0) {
591 fullName
.Assign(libName
);
593 fullName
.Assign(libPath
+ u
"\\"_ns
+ libName
);
596 mOvrLib
= LoadLibraryWithFlags(fullName
.get());
602 # error "Unsupported platform!"
609 #define REQUIRE_FUNCTION(_x) \
611 *(void**)&_x = (void*)PR_FindSymbol(mOvrLib, #_x); \
613 printf_stderr(#_x " symbol missing\n"); \
618 REQUIRE_FUNCTION(ovr_Initialize
);
619 REQUIRE_FUNCTION(ovr_Shutdown
);
620 REQUIRE_FUNCTION(ovr_GetLastErrorInfo
);
621 REQUIRE_FUNCTION(ovr_GetVersionString
);
622 REQUIRE_FUNCTION(ovr_TraceMessage
);
623 REQUIRE_FUNCTION(ovr_IdentifyClient
);
624 REQUIRE_FUNCTION(ovr_GetHmdDesc
);
625 REQUIRE_FUNCTION(ovr_GetTrackerCount
);
626 REQUIRE_FUNCTION(ovr_GetTrackerDesc
);
627 REQUIRE_FUNCTION(ovr_Create
);
628 REQUIRE_FUNCTION(ovr_Destroy
);
629 REQUIRE_FUNCTION(ovr_GetSessionStatus
);
630 REQUIRE_FUNCTION(ovr_IsExtensionSupported
);
631 REQUIRE_FUNCTION(ovr_EnableExtension
);
632 REQUIRE_FUNCTION(ovr_SetTrackingOriginType
);
633 REQUIRE_FUNCTION(ovr_GetTrackingOriginType
);
634 REQUIRE_FUNCTION(ovr_RecenterTrackingOrigin
);
635 REQUIRE_FUNCTION(ovr_SpecifyTrackingOrigin
);
636 REQUIRE_FUNCTION(ovr_ClearShouldRecenterFlag
);
637 REQUIRE_FUNCTION(ovr_GetTrackingState
);
638 REQUIRE_FUNCTION(ovr_GetDevicePoses
);
639 REQUIRE_FUNCTION(ovr_GetTrackerPose
);
640 REQUIRE_FUNCTION(ovr_GetInputState
);
641 REQUIRE_FUNCTION(ovr_GetConnectedControllerTypes
);
642 REQUIRE_FUNCTION(ovr_GetTouchHapticsDesc
);
643 REQUIRE_FUNCTION(ovr_SetControllerVibration
);
644 REQUIRE_FUNCTION(ovr_SubmitControllerVibration
);
645 REQUIRE_FUNCTION(ovr_GetControllerVibrationState
);
646 REQUIRE_FUNCTION(ovr_TestBoundary
);
647 REQUIRE_FUNCTION(ovr_TestBoundaryPoint
);
648 REQUIRE_FUNCTION(ovr_SetBoundaryLookAndFeel
);
649 REQUIRE_FUNCTION(ovr_ResetBoundaryLookAndFeel
);
650 REQUIRE_FUNCTION(ovr_GetBoundaryGeometry
);
651 REQUIRE_FUNCTION(ovr_GetBoundaryDimensions
);
652 REQUIRE_FUNCTION(ovr_GetBoundaryVisible
);
653 REQUIRE_FUNCTION(ovr_RequestBoundaryVisible
);
654 REQUIRE_FUNCTION(ovr_GetTextureSwapChainLength
);
655 REQUIRE_FUNCTION(ovr_GetTextureSwapChainCurrentIndex
);
656 REQUIRE_FUNCTION(ovr_GetTextureSwapChainDesc
);
657 REQUIRE_FUNCTION(ovr_CommitTextureSwapChain
);
658 REQUIRE_FUNCTION(ovr_DestroyTextureSwapChain
);
659 REQUIRE_FUNCTION(ovr_DestroyMirrorTexture
);
660 REQUIRE_FUNCTION(ovr_GetFovTextureSize
);
661 REQUIRE_FUNCTION(ovr_GetRenderDesc2
);
662 REQUIRE_FUNCTION(ovr_WaitToBeginFrame
);
663 REQUIRE_FUNCTION(ovr_BeginFrame
);
664 REQUIRE_FUNCTION(ovr_EndFrame
);
665 REQUIRE_FUNCTION(ovr_SubmitFrame
);
666 REQUIRE_FUNCTION(ovr_GetPerfStats
);
667 REQUIRE_FUNCTION(ovr_ResetPerfStats
);
668 REQUIRE_FUNCTION(ovr_GetPredictedDisplayTime
);
669 REQUIRE_FUNCTION(ovr_GetTimeInSeconds
);
670 REQUIRE_FUNCTION(ovr_GetBool
);
671 REQUIRE_FUNCTION(ovr_SetBool
);
672 REQUIRE_FUNCTION(ovr_GetInt
);
673 REQUIRE_FUNCTION(ovr_SetInt
);
674 REQUIRE_FUNCTION(ovr_GetFloat
);
675 REQUIRE_FUNCTION(ovr_SetFloat
);
676 REQUIRE_FUNCTION(ovr_GetFloatArray
);
677 REQUIRE_FUNCTION(ovr_SetFloatArray
);
678 REQUIRE_FUNCTION(ovr_GetString
);
679 REQUIRE_FUNCTION(ovr_SetString
);
680 REQUIRE_FUNCTION(ovr_GetExternalCameras
);
681 REQUIRE_FUNCTION(ovr_SetExternalCameraProperties
);
685 REQUIRE_FUNCTION(ovr_CreateTextureSwapChainDX
);
686 REQUIRE_FUNCTION(ovr_GetTextureSwapChainBufferDX
);
687 REQUIRE_FUNCTION(ovr_CreateMirrorTextureDX
);
688 REQUIRE_FUNCTION(ovr_GetMirrorTextureBufferDX
);
692 REQUIRE_FUNCTION(ovr_CreateTextureSwapChainGL
);
693 REQUIRE_FUNCTION(ovr_GetTextureSwapChainBufferGL
);
694 REQUIRE_FUNCTION(ovr_CreateMirrorTextureGL
);
695 REQUIRE_FUNCTION(ovr_GetMirrorTextureBufferGL
);
697 #undef REQUIRE_FUNCTION
702 ovr_Initialize
= nullptr;
703 PR_UnloadLibrary(mOvrLib
);
708 void OculusSession::UnloadOvrLib() {
710 PR_UnloadLibrary(mOvrLib
);
715 bool OculusSession::StartLib(ovrInitFlags aFlags
) {
716 if (mInitFlags
== 0) {
717 ovrInitParams params
;
718 memset(¶ms
, 0, sizeof(params
));
719 params
.Flags
= aFlags
;
720 params
.RequestedMinorVersion
= OVR_MINOR_VERSION
;
721 params
.LogCallback
= nullptr;
722 params
.ConnectionTimeoutMS
= 0;
724 ovrResult orv
= ovr_Initialize(¶ms
);
726 if (orv
== ovrSuccess
) {
732 MOZ_ASSERT(mInitFlags
== aFlags
);
736 void OculusSession::StopLib() {
739 mInitFlags
= (ovrInitFlags
)0;
743 bool OculusSession::StartSession() {
744 // ovr_Create can be slow when no HMD is present and we wish
745 // to keep the same oculus session when possible, so we detect
746 // presence of an HMD with ovr_GetHmdDesc before calling ovr_Create
747 ovrHmdDesc desc
= ovr_GetHmdDesc(NULL
);
748 if (desc
.Type
== ovrHmd_None
) {
749 // No HMD connected, destroy any existing session
751 ovr_Destroy(mSession
);
756 if (mSession
!= nullptr) {
757 // HMD Detected and we already have a session, let's keep using it.
761 // HMD Detected and we don't have a session yet,
762 // try to create a new session
764 ovrGraphicsLuid luid
;
765 ovrResult orv
= ovr_Create(&session
, &luid
);
766 if (orv
== ovrSuccess
) {
767 orv
= ovr_SetTrackingOriginType(session
, ovrTrackingOrigin_FloorLevel
);
768 if (orv
!= ovrSuccess
) {
769 NS_WARNING("ovr_SetTrackingOriginType failed.\n");
775 // Failed to create a session for the HMD
779 void OculusSession::StopSession() {
781 ovr_Destroy(mSession
);
786 bool OculusSession::CreateD3DObjects() {
787 RefPtr
<ID3D11Device
> device
= gfx::DeviceManagerDx::Get()->GetVRDevice();
791 if (!CreateD3DContext(device
)) {
797 bool OculusSession::CreateShaders() {
799 if (FAILED(mDevice
->CreateVertexShader(
800 sLayerQuadVS
.mData
, sLayerQuadVS
.mLength
, nullptr, &mQuadVS
))) {
801 NS_WARNING("Failed to create vertex shader for Oculus");
807 if (FAILED(mDevice
->CreatePixelShader(sRGBShader
.mData
, sRGBShader
.mLength
,
808 nullptr, &mQuadPS
))) {
809 NS_WARNING("Failed to create pixel shader for Oculus");
814 CD3D11_BUFFER_DESC
cBufferDesc(sizeof(layers::VertexShaderConstants
),
815 D3D11_BIND_CONSTANT_BUFFER
,
816 D3D11_USAGE_DYNAMIC
, D3D11_CPU_ACCESS_WRITE
);
818 if (!mVSConstantBuffer
) {
819 if (FAILED(mDevice
->CreateBuffer(&cBufferDesc
, nullptr,
820 getter_AddRefs(mVSConstantBuffer
)))) {
821 NS_WARNING("Failed to vertex shader constant buffer for Oculus");
826 if (!mPSConstantBuffer
) {
827 cBufferDesc
.ByteWidth
= sizeof(layers::PixelShaderConstants
);
828 if (FAILED(mDevice
->CreateBuffer(&cBufferDesc
, nullptr,
829 getter_AddRefs(mPSConstantBuffer
)))) {
830 NS_WARNING("Failed to pixel shader constant buffer for Oculus");
835 if (!mLinearSamplerState
) {
836 CD3D11_SAMPLER_DESC
samplerDesc(D3D11_DEFAULT
);
837 if (FAILED(mDevice
->CreateSamplerState(
838 &samplerDesc
, getter_AddRefs(mLinearSamplerState
)))) {
839 NS_WARNING("Failed to create sampler state for Oculus");
845 D3D11_INPUT_ELEMENT_DESC layout
[] = {
846 {"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT
, 0, 0,
847 D3D11_INPUT_PER_VERTEX_DATA
, 0},
850 if (FAILED(mDevice
->CreateInputLayout(
851 layout
, sizeof(layout
) / sizeof(D3D11_INPUT_ELEMENT_DESC
),
852 sLayerQuadVS
.mData
, sLayerQuadVS
.mLength
,
853 getter_AddRefs(mInputLayout
)))) {
854 NS_WARNING("Failed to create input layout for Oculus");
859 if (!mVertexBuffer
) {
860 Vertex vertices
[] = {
861 {{0.0, 0.0}}, {{1.0, 0.0}}, {{0.0, 1.0}}, {{1.0, 1.0}}};
862 CD3D11_BUFFER_DESC
bufferDesc(sizeof(vertices
), D3D11_BIND_VERTEX_BUFFER
);
863 D3D11_SUBRESOURCE_DATA data
;
864 data
.pSysMem
= (void*)vertices
;
866 if (FAILED(mDevice
->CreateBuffer(&bufferDesc
, &data
,
867 getter_AddRefs(mVertexBuffer
)))) {
868 NS_WARNING("Failed to create vertex buffer for Oculus");
873 memset(&mVSConstants
, 0, sizeof(mVSConstants
));
874 memset(&mPSConstants
, 0, sizeof(mPSConstants
));
878 void OculusSession::DestroyShaders() {}
880 bool OculusSession::UpdateConstantBuffers() {
882 D3D11_MAPPED_SUBRESOURCE resource
;
883 resource
.pData
= nullptr;
885 hr
= mContext
->Map(mVSConstantBuffer
, 0, D3D11_MAP_WRITE_DISCARD
, 0,
887 if (FAILED(hr
) || !resource
.pData
) {
890 *(VertexShaderConstants
*)resource
.pData
= mVSConstants
;
891 mContext
->Unmap(mVSConstantBuffer
, 0);
892 resource
.pData
= nullptr;
894 hr
= mContext
->Map(mPSConstantBuffer
, 0, D3D11_MAP_WRITE_DISCARD
, 0,
896 if (FAILED(hr
) || !resource
.pData
) {
899 *(PixelShaderConstants
*)resource
.pData
= mPSConstants
;
900 mContext
->Unmap(mPSConstantBuffer
, 0);
902 ID3D11Buffer
* buffer
= mVSConstantBuffer
;
903 mContext
->VSSetConstantBuffers(0, 1, &buffer
);
904 buffer
= mPSConstantBuffer
;
905 mContext
->PSSetConstantBuffers(0, 1, &buffer
);
909 bool OculusSession::StartRendering() {
912 * The presentation format is determined by content, which describes the
913 * left and right eye rectangles in the VRLayer. The default, if no
914 * coordinates are passed is to place the left and right eye textures
915 * side-by-side within the buffer.
917 * XXX - An optimization would be to dynamically resize this buffer
918 * to accomodate sites that are choosing to render in a lower
919 * resolution or are using space outside of the left and right
920 * eye textures for other purposes. (Bug 1291443)
923 ovrTextureSwapChainDesc desc
;
924 memset(&desc
, 0, sizeof(desc
));
925 desc
.Type
= ovrTexture_2D
;
927 desc
.Format
= OVR_FORMAT_B8G8R8A8_UNORM_SRGB
;
928 desc
.Width
= mPresentationSize
.width
;
929 desc
.Height
= mPresentationSize
.height
;
931 desc
.SampleCount
= 1;
932 desc
.StaticImage
= false;
933 desc
.MiscFlags
= ovrTextureMisc_DX_Typeless
;
934 desc
.BindFlags
= ovrTextureBind_DX_RenderTarget
;
937 ovr_CreateTextureSwapChainDX(mSession
, mDevice
, &desc
, &mTextureSet
);
938 if (orv
!= ovrSuccess
) {
939 NS_WARNING("ovr_CreateTextureSwapChainDX failed");
943 int textureCount
= 0;
944 orv
= ovr_GetTextureSwapChainLength(mSession
, mTextureSet
, &textureCount
);
945 if (orv
!= ovrSuccess
) {
946 NS_WARNING("ovr_GetTextureSwapChainLength failed");
949 mTexture
.SetLength(textureCount
);
950 mRTView
.SetLength(textureCount
);
951 mSRV
.SetLength(textureCount
);
952 for (int i
= 0; i
< textureCount
; ++i
) {
953 ID3D11Texture2D
* texture
= nullptr;
954 orv
= ovr_GetTextureSwapChainBufferDX(mSession
, mTextureSet
, i
,
955 IID_PPV_ARGS(&texture
));
956 if (orv
!= ovrSuccess
) {
957 NS_WARNING("Failed to create Oculus texture swap chain.");
961 RefPtr
<ID3D11RenderTargetView
> rtView
;
962 CD3D11_RENDER_TARGET_VIEW_DESC
rtvDesc(D3D11_RTV_DIMENSION_TEXTURE2D
,
963 DXGI_FORMAT_B8G8R8A8_UNORM
);
964 HRESULT hr
= mDevice
->CreateRenderTargetView(texture
, &rtvDesc
,
965 getter_AddRefs(rtView
));
968 "Failed to create RenderTargetView for Oculus texture swap chain.");
973 RefPtr
<ID3D11ShaderResourceView
> srv
;
974 CD3D11_SHADER_RESOURCE_VIEW_DESC
srvDesc(D3D11_SRV_DIMENSION_TEXTURE2D
,
975 DXGI_FORMAT_B8G8R8A8_UNORM
);
976 hr
= mDevice
->CreateShaderResourceView(texture
, &srvDesc
,
977 getter_AddRefs(srv
));
980 "Failed to create ShaderResourceView for Oculus texture swap "
986 mTexture
[i
] = texture
;
995 bool OculusSession::IsPresentationReady() const {
996 return mTextureSet
!= nullptr;
999 void OculusSession::StopRendering() {
1004 if (mTextureSet
&& mSession
) {
1005 ovr_DestroyTextureSwapChain(mSession
, mTextureSet
);
1007 mTextureSet
= nullptr;
1008 mIsPresenting
= false;
1011 bool OculusSession::InitState(VRSystemState
& aSystemState
) {
1012 VRDisplayState
& state
= aSystemState
.displayState
;
1013 strncpy(state
.displayName
, "Oculus VR HMD", kVRDisplayNameMaxLen
);
1014 state
.isConnected
= true;
1015 state
.isMounted
= false;
1017 ovrHmdDesc desc
= ovr_GetHmdDesc(mSession
);
1019 state
.capabilityFlags
= VRDisplayCapabilityFlags::Cap_None
;
1020 if (desc
.AvailableTrackingCaps
& ovrTrackingCap_Orientation
) {
1021 state
.capabilityFlags
|= VRDisplayCapabilityFlags::Cap_Orientation
;
1022 state
.capabilityFlags
|= VRDisplayCapabilityFlags::Cap_AngularAcceleration
;
1024 if (desc
.AvailableTrackingCaps
& ovrTrackingCap_Position
) {
1025 state
.capabilityFlags
|= VRDisplayCapabilityFlags::Cap_Position
;
1026 state
.capabilityFlags
|= VRDisplayCapabilityFlags::Cap_LinearAcceleration
;
1027 state
.capabilityFlags
|= VRDisplayCapabilityFlags::Cap_StageParameters
;
1029 state
.capabilityFlags
|= VRDisplayCapabilityFlags::Cap_External
;
1030 state
.capabilityFlags
|= VRDisplayCapabilityFlags::Cap_MountDetection
;
1031 state
.capabilityFlags
|= VRDisplayCapabilityFlags::Cap_Present
;
1032 state
.capabilityFlags
|= VRDisplayCapabilityFlags::Cap_ImmersiveVR
;
1033 state
.blendMode
= VRDisplayBlendMode::Opaque
;
1034 state
.reportsDroppedFrames
= true;
1036 mFOVPort
[VRDisplayState::Eye_Left
] = desc
.DefaultEyeFov
[ovrEye_Left
];
1037 mFOVPort
[VRDisplayState::Eye_Right
] = desc
.DefaultEyeFov
[ovrEye_Right
];
1039 state
.eyeFOV
[VRDisplayState::Eye_Left
] =
1040 FromFovPort(mFOVPort
[VRDisplayState::Eye_Left
]);
1041 state
.eyeFOV
[VRDisplayState::Eye_Right
] =
1042 FromFovPort(mFOVPort
[VRDisplayState::Eye_Right
]);
1044 float pixelsPerDisplayPixel
= 1.0;
1045 ovrSizei texSize
[2];
1047 // get eye texture sizes
1048 for (uint32_t eye
= 0; eye
< VRDisplayState::NumEyes
; eye
++) {
1049 texSize
[eye
] = ovr_GetFovTextureSize(mSession
, (ovrEyeType
)eye
,
1050 mFOVPort
[eye
], pixelsPerDisplayPixel
);
1053 // take the max of both for eye resolution
1054 state
.eyeResolution
.width
= std::max(texSize
[VRDisplayState::Eye_Left
].w
,
1055 texSize
[VRDisplayState::Eye_Right
].w
);
1056 state
.eyeResolution
.height
= std::max(texSize
[VRDisplayState::Eye_Left
].h
,
1057 texSize
[VRDisplayState::Eye_Right
].h
);
1058 state
.nativeFramebufferScaleFactor
= 1.0f
;
1060 // default to an identity quaternion
1061 aSystemState
.sensorState
.pose
.orientation
[3] = 1.0f
;
1063 UpdateStageParameters(state
);
1064 UpdateEyeParameters(aSystemState
);
1066 VRHMDSensorState
& sensorState
= aSystemState
.sensorState
;
1068 (VRDisplayCapabilityFlags
)((int)
1069 VRDisplayCapabilityFlags::Cap_Orientation
|
1070 (int)VRDisplayCapabilityFlags::Cap_Position
);
1071 sensorState
.pose
.orientation
[3] = 1.0f
; // Default to an identity quaternion
1076 void OculusSession::UpdateStageParameters(VRDisplayState
& aState
) {
1077 ovrVector3f playArea
;
1079 ovr_GetBoundaryDimensions(mSession
, ovrBoundary_PlayArea
, &playArea
);
1080 if (res
== ovrSuccess
) {
1081 aState
.stageSize
.width
= playArea
.x
;
1082 aState
.stageSize
.height
= playArea
.z
;
1084 // If we fail, fall back to reasonable defaults.
1086 aState
.stageSize
.width
= 1.0f
;
1087 aState
.stageSize
.height
= 1.0f
;
1091 ovr_GetFloat(mSession
, OVR_KEY_EYE_HEIGHT
, OVR_DEFAULT_EYE_HEIGHT
);
1093 aState
.sittingToStandingTransform
[0] = 1.0f
;
1094 aState
.sittingToStandingTransform
[1] = 0.0f
;
1095 aState
.sittingToStandingTransform
[2] = 0.0f
;
1096 aState
.sittingToStandingTransform
[3] = 0.0f
;
1098 aState
.sittingToStandingTransform
[4] = 0.0f
;
1099 aState
.sittingToStandingTransform
[5] = 1.0f
;
1100 aState
.sittingToStandingTransform
[6] = 0.0f
;
1101 aState
.sittingToStandingTransform
[7] = 0.0f
;
1103 aState
.sittingToStandingTransform
[8] = 0.0f
;
1104 aState
.sittingToStandingTransform
[9] = 0.0f
;
1105 aState
.sittingToStandingTransform
[10] = 1.0f
;
1106 aState
.sittingToStandingTransform
[11] = 0.0f
;
1108 aState
.sittingToStandingTransform
[12] = 0.0f
;
1109 aState
.sittingToStandingTransform
[13] = eyeHeight
;
1110 aState
.sittingToStandingTransform
[14] = 0.0f
;
1111 aState
.sittingToStandingTransform
[15] = 1.0f
;
1114 void OculusSession::UpdateEyeParameters(VRSystemState
& aState
) {
1118 // This must be called every frame in order to
1119 // account for continuous adjustments to ipd.
1120 gfx::Matrix4x4 headToEyeTransforms
[2];
1121 for (uint32_t eye
= 0; eye
< VRDisplayState::NumEyes
; eye
++) {
1122 // As of Oculus 1.17 SDK, we must use the ovr_GetRenderDesc2 function to
1123 // return the updated version of ovrEyeRenderDesc. This is normally done by
1124 // the Oculus static lib shim, but we need to do this explicitly as we are
1125 // loading the Oculus runtime dll directly.
1126 ovrEyeRenderDesc renderDesc
=
1127 ovr_GetRenderDesc2(mSession
, (ovrEyeType
)eye
, mFOVPort
[eye
]);
1128 aState
.displayState
.eyeTranslation
[eye
].x
=
1129 renderDesc
.HmdToEyePose
.Position
.x
;
1130 aState
.displayState
.eyeTranslation
[eye
].y
=
1131 renderDesc
.HmdToEyePose
.Position
.y
;
1132 aState
.displayState
.eyeTranslation
[eye
].z
=
1133 renderDesc
.HmdToEyePose
.Position
.z
;
1136 pose
.SetRotationFromQuaternion(
1137 gfx::Quaternion(-renderDesc
.HmdToEyePose
.Orientation
.x
,
1138 -renderDesc
.HmdToEyePose
.Orientation
.y
,
1139 -renderDesc
.HmdToEyePose
.Orientation
.z
,
1140 renderDesc
.HmdToEyePose
.Orientation
.w
));
1141 pose
.PreTranslate(renderDesc
.HmdToEyePose
.Position
.x
,
1142 renderDesc
.HmdToEyePose
.Position
.y
,
1143 renderDesc
.HmdToEyePose
.Position
.z
);
1145 headToEyeTransforms
[eye
] = pose
;
1147 aState
.sensorState
.CalcViewMatrices(headToEyeTransforms
);
1149 Matrix4x4 matView
[2];
1150 memcpy(matView
[0].components
, aState
.sensorState
.leftViewMatrix
,
1151 sizeof(float) * 16);
1152 memcpy(matView
[1].components
, aState
.sensorState
.rightViewMatrix
,
1153 sizeof(float) * 16);
1155 for (uint32_t eye
= 0; eye
< VRDisplayState::NumEyes
; eye
++) {
1156 Point3D eyeTranslation
;
1157 Quaternion eyeRotation
;
1159 if (!matView
[eye
].Decompose(eyeTranslation
, eyeRotation
, eyeScale
)) {
1160 NS_WARNING("Failed to decompose eye pose matrix for Oculus");
1163 eyeRotation
.Invert();
1164 mFrameStartPose
[eye
].Orientation
.x
= eyeRotation
.x
;
1165 mFrameStartPose
[eye
].Orientation
.y
= eyeRotation
.y
;
1166 mFrameStartPose
[eye
].Orientation
.z
= eyeRotation
.z
;
1167 mFrameStartPose
[eye
].Orientation
.w
= eyeRotation
.w
;
1168 mFrameStartPose
[eye
].Position
.x
= eyeTranslation
.x
;
1169 mFrameStartPose
[eye
].Position
.y
= eyeTranslation
.y
;
1170 mFrameStartPose
[eye
].Position
.z
= eyeTranslation
.z
;
1174 void OculusSession::UpdateHeadsetPose(VRSystemState
& aState
) {
1178 double predictedFrameTime
= 0.0f
;
1179 if (StaticPrefs::dom_vr_poseprediction_enabled()) {
1180 // XXX We might need to call ovr_GetPredictedDisplayTime even if we don't
1181 // use the result. If we don't call it, the Oculus driver will spew out many
1183 predictedFrameTime
= ovr_GetPredictedDisplayTime(mSession
, 0);
1185 ovrTrackingState trackingState
=
1186 ovr_GetTrackingState(mSession
, predictedFrameTime
, true);
1187 ovrPoseStatef
& pose(trackingState
.HeadPose
);
1189 aState
.sensorState
.timestamp
= pose
.TimeInSeconds
;
1191 if (trackingState
.StatusFlags
& ovrStatus_OrientationTracked
) {
1192 aState
.sensorState
.flags
|= VRDisplayCapabilityFlags::Cap_Orientation
;
1194 aState
.sensorState
.pose
.orientation
[0] = pose
.ThePose
.Orientation
.x
;
1195 aState
.sensorState
.pose
.orientation
[1] = pose
.ThePose
.Orientation
.y
;
1196 aState
.sensorState
.pose
.orientation
[2] = pose
.ThePose
.Orientation
.z
;
1197 aState
.sensorState
.pose
.orientation
[3] = pose
.ThePose
.Orientation
.w
;
1199 aState
.sensorState
.pose
.angularVelocity
[0] = pose
.AngularVelocity
.x
;
1200 aState
.sensorState
.pose
.angularVelocity
[1] = pose
.AngularVelocity
.y
;
1201 aState
.sensorState
.pose
.angularVelocity
[2] = pose
.AngularVelocity
.z
;
1203 aState
.sensorState
.flags
|=
1204 VRDisplayCapabilityFlags::Cap_AngularAcceleration
;
1206 aState
.sensorState
.pose
.angularAcceleration
[0] = pose
.AngularAcceleration
.x
;
1207 aState
.sensorState
.pose
.angularAcceleration
[1] = pose
.AngularAcceleration
.y
;
1208 aState
.sensorState
.pose
.angularAcceleration
[2] = pose
.AngularAcceleration
.z
;
1210 // default to an identity quaternion
1211 aState
.sensorState
.pose
.orientation
[3] = 1.0f
;
1214 if (trackingState
.StatusFlags
& ovrStatus_PositionTracked
) {
1216 ovr_GetFloat(mSession
, OVR_KEY_EYE_HEIGHT
, OVR_DEFAULT_EYE_HEIGHT
);
1217 aState
.sensorState
.flags
|= VRDisplayCapabilityFlags::Cap_Position
;
1219 aState
.sensorState
.pose
.position
[0] = pose
.ThePose
.Position
.x
;
1220 aState
.sensorState
.pose
.position
[1] = pose
.ThePose
.Position
.y
- eyeHeight
;
1221 aState
.sensorState
.pose
.position
[2] = pose
.ThePose
.Position
.z
;
1223 aState
.sensorState
.pose
.linearVelocity
[0] = pose
.LinearVelocity
.x
;
1224 aState
.sensorState
.pose
.linearVelocity
[1] = pose
.LinearVelocity
.y
;
1225 aState
.sensorState
.pose
.linearVelocity
[2] = pose
.LinearVelocity
.z
;
1227 aState
.sensorState
.flags
|=
1228 VRDisplayCapabilityFlags::Cap_LinearAcceleration
;
1230 aState
.sensorState
.pose
.linearAcceleration
[0] = pose
.LinearAcceleration
.x
;
1231 aState
.sensorState
.pose
.linearAcceleration
[1] = pose
.LinearAcceleration
.y
;
1232 aState
.sensorState
.pose
.linearAcceleration
[2] = pose
.LinearAcceleration
.z
;
1234 aState
.sensorState
.flags
|= VRDisplayCapabilityFlags::Cap_External
;
1235 aState
.sensorState
.flags
|= VRDisplayCapabilityFlags::Cap_MountDetection
;
1236 aState
.sensorState
.flags
|= VRDisplayCapabilityFlags::Cap_Present
;
1239 void OculusSession::UpdateControllers(VRSystemState
& aState
) {
1244 ovrInputState inputState
;
1245 bool hasInputState
= ovr_GetInputState(mSession
, ovrControllerType_Touch
,
1246 &inputState
) == ovrSuccess
;
1248 if (!hasInputState
) {
1252 EnumerateControllers(aState
, inputState
);
1253 UpdateControllerInputs(aState
, inputState
);
1254 UpdateControllerPose(aState
, inputState
);
1257 void OculusSession::UpdateControllerPose(VRSystemState
& aState
,
1258 const ovrInputState
& aInputState
) {
1259 ovrTrackingState trackingState
= ovr_GetTrackingState(mSession
, 0.0, false);
1260 for (uint32_t handIdx
= 0; handIdx
< 2; handIdx
++) {
1261 // Left Touch Controller will always be at index 0 and
1262 // and Right Touch Controller will always be at index 1
1263 VRControllerState
& controllerState
= aState
.controllerState
[handIdx
];
1264 if (aInputState
.ControllerType
& OculusControllerTypes
[handIdx
]) {
1265 ovrPoseStatef
& pose
= trackingState
.HandPoses
[handIdx
];
1266 bool bNewController
= !(controllerState
.flags
&
1267 dom::GamepadCapabilityFlags::Cap_Orientation
);
1268 if (bNewController
) {
1269 controllerState
.flags
|= dom::GamepadCapabilityFlags::Cap_Orientation
;
1270 controllerState
.flags
|= dom::GamepadCapabilityFlags::Cap_Position
;
1271 controllerState
.flags
|=
1272 dom::GamepadCapabilityFlags::Cap_AngularAcceleration
;
1273 controllerState
.flags
|=
1274 dom::GamepadCapabilityFlags::Cap_LinearAcceleration
;
1275 controllerState
.flags
|=
1276 dom::GamepadCapabilityFlags::Cap_GripSpacePosition
;
1279 if (bNewController
|| trackingState
.HandStatusFlags
[handIdx
] &
1280 ovrStatus_OrientationTracked
) {
1281 controllerState
.pose
.orientation
[0] = pose
.ThePose
.Orientation
.x
;
1282 controllerState
.pose
.orientation
[1] = pose
.ThePose
.Orientation
.y
;
1283 controllerState
.pose
.orientation
[2] = pose
.ThePose
.Orientation
.z
;
1284 controllerState
.pose
.orientation
[3] = pose
.ThePose
.Orientation
.w
;
1285 controllerState
.pose
.angularVelocity
[0] = pose
.AngularVelocity
.x
;
1286 controllerState
.pose
.angularVelocity
[1] = pose
.AngularVelocity
.y
;
1287 controllerState
.pose
.angularVelocity
[2] = pose
.AngularVelocity
.z
;
1288 controllerState
.pose
.angularAcceleration
[0] =
1289 pose
.AngularAcceleration
.x
;
1290 controllerState
.pose
.angularAcceleration
[1] =
1291 pose
.AngularAcceleration
.y
;
1292 controllerState
.pose
.angularAcceleration
[2] =
1293 pose
.AngularAcceleration
.z
;
1294 controllerState
.isOrientationValid
= true;
1296 controllerState
.isOrientationValid
= false;
1298 if (bNewController
||
1299 trackingState
.HandStatusFlags
[handIdx
] & ovrStatus_PositionTracked
) {
1300 controllerState
.pose
.position
[0] = pose
.ThePose
.Position
.x
;
1301 controllerState
.pose
.position
[1] = pose
.ThePose
.Position
.y
;
1302 controllerState
.pose
.position
[2] = pose
.ThePose
.Position
.z
;
1303 controllerState
.pose
.linearVelocity
[0] = pose
.LinearVelocity
.x
;
1304 controllerState
.pose
.linearVelocity
[1] = pose
.LinearVelocity
.y
;
1305 controllerState
.pose
.linearVelocity
[2] = pose
.LinearVelocity
.z
;
1306 controllerState
.pose
.linearAcceleration
[0] = pose
.LinearAcceleration
.x
;
1307 controllerState
.pose
.linearAcceleration
[1] = pose
.LinearAcceleration
.y
;
1308 controllerState
.pose
.linearAcceleration
[2] = pose
.LinearAcceleration
.z
;
1311 ovr_GetFloat(mSession
, OVR_KEY_EYE_HEIGHT
, OVR_DEFAULT_EYE_HEIGHT
);
1312 controllerState
.pose
.position
[1] -= eyeHeight
;
1313 controllerState
.isPositionValid
= true;
1315 controllerState
.isPositionValid
= false;
1317 controllerState
.targetRayPose
= controllerState
.pose
;
1322 void OculusSession::EnumerateControllers(VRSystemState
& aState
,
1323 const ovrInputState
& aInputState
) {
1324 for (uint32_t handIdx
= 0; handIdx
< 2; handIdx
++) {
1325 // Left Touch Controller will always be at index 0 and
1326 // and Right Touch Controller will always be at index 1
1327 VRControllerState
& controllerState
= aState
.controllerState
[handIdx
];
1328 if (aInputState
.ControllerType
& OculusControllerTypes
[handIdx
]) {
1329 // Touch Controller detected
1330 if (controllerState
.controllerName
[0] == '\0') {
1331 // Controller has been just enumerated
1332 strncpy(controllerState
.controllerName
, OculusControllerNames
[handIdx
],
1333 kVRControllerNameMaxLen
);
1334 controllerState
.hand
= OculusControllerHand
[handIdx
];
1335 controllerState
.targetRayMode
= gfx::TargetRayMode::TrackedPointer
;
1336 controllerState
.numButtons
= kNumOculusButtons
;
1337 controllerState
.numAxes
= kNumOculusAxes
;
1338 controllerState
.numHaptics
= kNumOculusHaptcs
;
1339 controllerState
.type
= VRControllerType::OculusTouch
;
1342 // Touch Controller not detected
1343 if (controllerState
.controllerName
[0] != '\0') {
1344 // Clear any newly disconnected ontrollers
1345 memset(&controllerState
, 0, sizeof(VRControllerState
));
1351 void OculusSession::UpdateControllerInputs(VRSystemState
& aState
,
1352 const ovrInputState
& aInputState
) {
1353 const float triggerThreshold
=
1354 StaticPrefs::dom_vr_controller_trigger_threshold();
1356 for (uint32_t handIdx
= 0; handIdx
< 2; handIdx
++) {
1357 // Left Touch Controller will always be at index 0 and
1358 // and Right Touch Controller will always be at index 1
1359 VRControllerState
& controllerState
= aState
.controllerState
[handIdx
];
1360 if (aInputState
.ControllerType
& OculusControllerTypes
[handIdx
]) {
1361 // Update Button States
1362 controllerState
.buttonPressed
= 0;
1363 controllerState
.buttonTouched
= 0;
1364 uint32_t buttonIdx
= 0;
1366 // Button 0: Trigger
1367 VRSession::UpdateTrigger(controllerState
, buttonIdx
,
1368 aInputState
.IndexTrigger
[handIdx
],
1372 VRSession::UpdateTrigger(controllerState
, buttonIdx
,
1373 aInputState
.HandTrigger
[handIdx
],
1376 // Button 2: a placeholder button for trackpad.
1377 UpdateButton(aInputState
, handIdx
, buttonIdx
, controllerState
);
1379 // Button 3: Thumbstick
1380 UpdateButton(aInputState
, handIdx
, buttonIdx
, controllerState
);
1383 UpdateButton(aInputState
, handIdx
, buttonIdx
, controllerState
);
1386 UpdateButton(aInputState
, handIdx
, buttonIdx
, controllerState
);
1388 // Button 6: ThumbRest
1389 UpdateButton(aInputState
, handIdx
, buttonIdx
, controllerState
);
1392 MOZ_ASSERT(buttonIdx
== kNumOculusButtons
);
1394 // Update Thumbstick axis
1395 uint32_t axisIdx
= 0;
1396 // Axis 0, 1: placeholder axes for trackpad.
1399 // Axis 2, 3: placeholder axes for thumbstick.
1400 float axisValue
= aInputState
.Thumbstick
[handIdx
].x
;
1401 if (abs(axisValue
) < 0.0000009f
) {
1402 axisValue
= 0.0f
; // Clear noise signal
1404 controllerState
.axisValue
[axisIdx
] = axisValue
;
1407 // Note that y axis is intentionally inverted!
1408 axisValue
= -aInputState
.Thumbstick
[handIdx
].y
;
1409 if (abs(axisValue
) < 0.0000009f
) {
1410 axisValue
= 0.0f
; // Clear noise signal
1412 controllerState
.axisValue
[axisIdx
] = axisValue
;
1415 MOZ_ASSERT(axisIdx
== kNumOculusAxes
);
1417 SetControllerSelectionAndSqueezeFrameId(
1418 controllerState
, aState
.displayState
.lastSubmittedFrameId
);
1422 void OculusSession::UpdateTelemetry(VRSystemState
& aSystemState
) {
1426 ovrPerfStats perfStats
;
1427 if (ovr_GetPerfStats(mSession
, &perfStats
) == ovrSuccess
) {
1428 if (perfStats
.FrameStatsCount
) {
1429 aSystemState
.displayState
.droppedFrameCount
=
1430 perfStats
.FrameStats
[0].AppDroppedFrameCount
;
1435 void OculusSession::VibrateHaptic(uint32_t aControllerIdx
,
1436 uint32_t aHapticIndex
, float aIntensity
,
1442 if (aDuration
<= 0.0f
) {
1443 StopVibrateHaptic(aControllerIdx
);
1447 // Vibration amplitude in the [0.0, 1.0] range
1448 MOZ_ASSERT(aControllerIdx
>= 0 && aControllerIdx
<= 1);
1449 mHapticPulseIntensity
[aControllerIdx
] = aIntensity
> 1.0 ? 1.0 : aIntensity
;
1450 mRemainingVibrateTime
[aControllerIdx
] = aDuration
;
1451 ovrControllerType hand
= OculusControllerTypes
[aControllerIdx
];
1453 // The gamepad extensions API does not yet have independent control
1454 // of frequency and amplitude. We are always sending 0.0f (160hz)
1455 // to the frequency argument.
1456 ovrResult result
= ovr_SetControllerVibration(
1457 mSession
, hand
, 0.0f
, mHapticPulseIntensity
[aControllerIdx
]);
1458 if (result
!= ovrSuccess
) {
1459 // This may happen if called when not presenting.
1460 gfxWarning() << "ovr_SetControllerVibration failed.";
1464 void OculusSession::StopVibrateHaptic(uint32_t aControllerIdx
) {
1468 MOZ_ASSERT(aControllerIdx
>= 0 && aControllerIdx
<= 1);
1469 ovrControllerType hand
= OculusControllerTypes
[aControllerIdx
];
1470 mRemainingVibrateTime
[aControllerIdx
] = 0.0f
;
1471 mHapticPulseIntensity
[aControllerIdx
] = 0.0f
;
1473 ovrResult result
= ovr_SetControllerVibration(mSession
, hand
, 0.0f
, 0.0f
);
1474 if (result
!= ovrSuccess
) {
1475 // This may happen if called when not presenting.
1476 gfxWarning() << "ovr_SetControllerVibration failed.";
1480 void OculusSession::StopAllHaptics() {
1481 // Left Oculus Touch
1482 StopVibrateHaptic(0);
1483 // Right Oculus Touch
1484 StopVibrateHaptic(1);
1487 void OculusSession::UpdateHaptics() {
1491 // The Oculus API and hardware takes at least 33ms to respond
1492 // to haptic state changes, so it is not beneficial to create
1493 // a dedicated haptic feedback thread and update multiple
1495 // If we wish to support more accurate effects with sub-frame timing,
1496 // we should use the buffered haptic feedback API's.
1498 TimeStamp now
= TimeStamp::Now();
1499 if (mLastHapticUpdate
.IsNull()) {
1500 mLastHapticUpdate
= now
;
1503 float deltaTime
= (float)(now
- mLastHapticUpdate
).ToSeconds();
1504 mLastHapticUpdate
= now
;
1505 for (int i
= 0; i
< 2; i
++) {
1506 if (mRemainingVibrateTime
[i
] <= 0.0f
) {
1509 mRemainingVibrateTime
[i
] -= deltaTime
;
1510 ovrControllerType hand
= OculusControllerTypes
[i
];
1511 if (mRemainingVibrateTime
[i
] > 0.0f
) {
1512 ovrResult result
= ovr_SetControllerVibration(mSession
, hand
, 0.0f
,
1513 mHapticPulseIntensity
[i
]);
1514 if (result
!= ovrSuccess
) {
1515 // This may happen if called when not presenting.
1516 gfxWarning() << "ovr_SetControllerVibration failed.";
1519 StopVibrateHaptic(i
);
1525 } // namespace mozilla