Merge mozilla-central to autoland. CLOSED TREE
[gecko.git] / gfx / vr / service / OculusSession.cpp
blob07b0f208e327a182e1e3c0ba54a5114590ac8eb1
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/. */
7 #ifndef XP_WIN
8 # error "Oculus support only available for Windows"
9 #endif
11 #include <math.h>
12 #include <d3d11.h>
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\
25 * drivers.
26 * Alternately, we could remove the extra blit for
27 * Oculus as well with some more refactoring.
30 // See CompositorD3D11Shaders.h
31 namespace mozilla {
32 namespace layers {
33 struct ShaderBytes {
34 const void* mData;
35 size_t mLength;
37 extern ShaderBytes sRGBShader;
38 extern ShaderBytes sLayerQuadVS;
39 } // namespace layers
40 } // namespace mozilla
42 using namespace mozilla;
43 using namespace mozilla::gfx;
44 using namespace mozilla::layers;
46 namespace {
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 =
72 nullptr;
73 static pfn_ovr_GetTouchHapticsDesc ovr_GetTouchHapticsDesc = nullptr;
74 static pfn_ovr_SetControllerVibration ovr_SetControllerVibration = nullptr;
75 static pfn_ovr_SubmitControllerVibration ovr_SubmitControllerVibration =
76 nullptr;
77 static pfn_ovr_GetControllerVibrationState ovr_GetControllerVibrationState =
78 nullptr;
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 =
88 nullptr;
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 =
117 nullptr;
119 #ifdef XP_WIN
120 static pfn_ovr_CreateTextureSwapChainDX ovr_CreateTextureSwapChainDX = nullptr;
121 static pfn_ovr_GetTextureSwapChainBufferDX ovr_GetTextureSwapChainBufferDX =
122 nullptr;
123 static pfn_ovr_CreateMirrorTextureDX ovr_CreateMirrorTextureDX = nullptr;
124 static pfn_ovr_GetMirrorTextureBufferDX ovr_GetMirrorTextureBufferDX = nullptr;
125 #endif
127 static pfn_ovr_CreateTextureSwapChainGL ovr_CreateTextureSwapChainGL = nullptr;
128 static pfn_ovr_GetTextureSwapChainBufferGL ovr_GetTextureSwapChainBufferGL =
129 nullptr;
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
135 #else
136 # define BUILD_BITS 32
137 #endif
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;
172 } else {
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;
186 return fovInfo;
189 } // anonymous namespace
191 namespace mozilla {
192 namespace gfx {
194 OculusSession::OculusSession()
195 : VRSession(),
196 mOvrLib(nullptr),
197 mSession(nullptr),
198 mInitFlags((ovrInitFlags)0),
199 mTextureSet(nullptr),
200 mQuadVS(nullptr),
201 mQuadPS(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
217 return false;
219 if (!StaticPrefs::dom_vr_enabled() || !StaticPrefs::dom_vr_oculus_enabled()) {
220 return false;
223 if (aDetectRuntimesOnly) {
224 if (LoadOvrLib()) {
225 aSystemState.displayState.capabilityFlags |=
226 VRDisplayCapabilityFlags::Cap_ImmersiveVR;
228 return false;
231 if (!CreateD3DObjects()) {
232 return false;
234 if (!CreateShaders()) {
235 return false;
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
239 // debug.
240 if (!LoadOvrLib()) {
241 return false;
243 // We start off with an invisible session, then re-initialize
244 // with visible session once WebVR content starts rendering.
245 if (!ChangeVisibility(false)) {
246 return false;
248 if (!InitState(aSystemState)) {
249 return false;
252 mPresentationSize = IntSize(aSystemState.displayState.eyeResolution.width * 2,
253 aSystemState.displayState.eyeResolution.height);
254 return true;
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
261 // traversal.
262 if (mIsPresenting) {
263 // We are currently rendering immersive content.
264 // Avoid interrupting the session
265 return;
267 if (mInitFlags & ovrInit_Invisible) {
268 // We are already invisible
269 return;
271 if (mLastPresentationEnd.IsNull()) {
272 // There has been no presentation yet
273 return;
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);
292 if (mIsPresenting) {
293 // We are currently rendering immersive content.
294 // Avoid interrupting the session
295 return;
298 if (mInitFlags & ovrInit_Invisible) {
299 // We are invisible, nothing to cover up
300 return;
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) {
312 ovrInitFlags flags =
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
319 return true;
322 // Tear everything down
323 StopRendering();
324 StopSession();
325 StopLib();
327 // Start it back up
328 if (!StartLib(flags)) {
329 return false;
331 if (!StartSession()) {
332 return false;
334 return true;
337 void OculusSession::Shutdown() {
338 StopRendering();
339 StopSession();
340 StopLib();
341 UnloadOvrLib();
342 DestroyShaders();
345 void OculusSession::ProcessEvents(mozilla::gfx::VRSystemState& aSystemState) {
346 if (!mSession) {
347 return;
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;
356 } else {
357 aSystemState.displayState.isConnected = false;
358 aSystemState.displayState.isMounted = false;
360 UpdateHaptics();
361 UpdateVisibility();
362 CoverTransitions();
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.
380 * See Bug 1299309.
382 if (!ChangeVisibility(true)) {
383 return false;
385 if (!StartRendering()) {
386 StopRendering();
387 return false;
389 mIsPresenting = true;
390 return 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()) {
402 return false;
405 D3D11_TEXTURE2D_DESC textureDesc = {0};
406 aTexture->GetDesc(&textureDesc);
408 int currentRenderTarget = 0;
409 ovrResult orv = ovr_GetTextureSwapChainCurrentIndex(mSession, mTextureSet,
410 &currentRenderTarget);
411 if (orv != ovrSuccess) {
412 NS_WARNING("ovr_GetTextureSwapChainCurrentIndex failed.");
413 return false;
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;
429 Matrix transform2d;
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;
440 D3D11_RECT scissor;
441 scissor.left = 0;
442 scissor.right = textureDesc.Width;
443 scissor.top = 0;
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);
460 UINT voffset = 0;
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));
473 if (FAILED(hr)) {
474 gfxWarning() << "Could not create shader resource view for Oculus: "
475 << hexa(hr);
476 return false;
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");
487 return false;
490 mContext->Draw(4, 0);
492 orv = ovr_CommitTextureSwapChain(mSession, mTextureSet);
493 if (orv != ovrSuccess) {
494 NS_WARNING("ovr_CommitTextureSwapChain failed.");
495 return false;
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.
539 return false;
542 return true;
545 bool OculusSession::LoadOvrLib() {
546 if (mOvrLib) {
547 // Already loaded, early exit
548 return true;
550 #if defined(_WIN32)
551 nsTArray<nsString> libSearchPaths;
552 nsString libName;
553 nsString searchPath;
555 for (;;) {
556 UINT requiredLength = ::GetSystemDirectoryW(
557 char16ptr_t(searchPath.BeginWriting()), searchPath.Length());
558 if (!requiredLength) {
559 break;
561 if (requiredLength < searchPath.Length()) {
562 searchPath.Truncate(requiredLength);
563 libSearchPaths.AppendElement(searchPath);
564 break;
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()) {
584 return false;
587 for (uint32_t i = 0; i < libSearchPaths.Length(); ++i) {
588 nsString& libPath = libSearchPaths[i];
589 nsString fullName;
590 if (libPath.Length() == 0) {
591 fullName.Assign(libName);
592 } else {
593 fullName.Assign(libPath + u"\\"_ns + libName);
596 mOvrLib = LoadLibraryWithFlags(fullName.get());
597 if (mOvrLib) {
598 break;
601 #else
602 # error "Unsupported platform!"
603 #endif
605 if (!mOvrLib) {
606 return false;
609 #define REQUIRE_FUNCTION(_x) \
610 do { \
611 *(void**)&_x = (void*)PR_FindSymbol(mOvrLib, #_x); \
612 if (!_x) { \
613 printf_stderr(#_x " symbol missing\n"); \
614 goto fail; \
616 } while (0)
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);
683 #ifdef XP_WIN
685 REQUIRE_FUNCTION(ovr_CreateTextureSwapChainDX);
686 REQUIRE_FUNCTION(ovr_GetTextureSwapChainBufferDX);
687 REQUIRE_FUNCTION(ovr_CreateMirrorTextureDX);
688 REQUIRE_FUNCTION(ovr_GetMirrorTextureBufferDX);
690 #endif
692 REQUIRE_FUNCTION(ovr_CreateTextureSwapChainGL);
693 REQUIRE_FUNCTION(ovr_GetTextureSwapChainBufferGL);
694 REQUIRE_FUNCTION(ovr_CreateMirrorTextureGL);
695 REQUIRE_FUNCTION(ovr_GetMirrorTextureBufferGL);
697 #undef REQUIRE_FUNCTION
699 return true;
701 fail:
702 ovr_Initialize = nullptr;
703 PR_UnloadLibrary(mOvrLib);
704 mOvrLib = nullptr;
705 return false;
708 void OculusSession::UnloadOvrLib() {
709 if (mOvrLib) {
710 PR_UnloadLibrary(mOvrLib);
711 mOvrLib = nullptr;
715 bool OculusSession::StartLib(ovrInitFlags aFlags) {
716 if (mInitFlags == 0) {
717 ovrInitParams params;
718 memset(&params, 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(&params);
726 if (orv == ovrSuccess) {
727 mInitFlags = aFlags;
728 } else {
729 return false;
732 MOZ_ASSERT(mInitFlags == aFlags);
733 return true;
736 void OculusSession::StopLib() {
737 if (mInitFlags) {
738 ovr_Shutdown();
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
750 if (mSession) {
751 ovr_Destroy(mSession);
752 mSession = nullptr;
754 return false;
756 if (mSession != nullptr) {
757 // HMD Detected and we already have a session, let's keep using it.
758 return true;
761 // HMD Detected and we don't have a session yet,
762 // try to create a new session
763 ovrSession 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");
771 mSession = session;
772 return true;
775 // Failed to create a session for the HMD
776 return false;
779 void OculusSession::StopSession() {
780 if (mSession) {
781 ovr_Destroy(mSession);
782 mSession = nullptr;
786 bool OculusSession::CreateD3DObjects() {
787 RefPtr<ID3D11Device> device = gfx::DeviceManagerDx::Get()->GetVRDevice();
788 if (!device) {
789 return false;
791 if (!CreateD3DContext(device)) {
792 return false;
794 return true;
797 bool OculusSession::CreateShaders() {
798 if (!mQuadVS) {
799 if (FAILED(mDevice->CreateVertexShader(
800 sLayerQuadVS.mData, sLayerQuadVS.mLength, nullptr, &mQuadVS))) {
801 NS_WARNING("Failed to create vertex shader for Oculus");
802 return false;
806 if (!mQuadPS) {
807 if (FAILED(mDevice->CreatePixelShader(sRGBShader.mData, sRGBShader.mLength,
808 nullptr, &mQuadPS))) {
809 NS_WARNING("Failed to create pixel shader for Oculus");
810 return false;
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");
822 return false;
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");
831 return false;
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");
840 return false;
844 if (!mInputLayout) {
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");
855 return false;
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");
869 return false;
873 memset(&mVSConstants, 0, sizeof(mVSConstants));
874 memset(&mPSConstants, 0, sizeof(mPSConstants));
875 return true;
878 void OculusSession::DestroyShaders() {}
880 bool OculusSession::UpdateConstantBuffers() {
881 HRESULT hr;
882 D3D11_MAPPED_SUBRESOURCE resource;
883 resource.pData = nullptr;
885 hr = mContext->Map(mVSConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0,
886 &resource);
887 if (FAILED(hr) || !resource.pData) {
888 return false;
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,
895 &resource);
896 if (FAILED(hr) || !resource.pData) {
897 return false;
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);
906 return true;
909 bool OculusSession::StartRendering() {
910 if (!mTextureSet) {
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;
926 desc.ArraySize = 1;
927 desc.Format = OVR_FORMAT_B8G8R8A8_UNORM_SRGB;
928 desc.Width = mPresentationSize.width;
929 desc.Height = mPresentationSize.height;
930 desc.MipLevels = 1;
931 desc.SampleCount = 1;
932 desc.StaticImage = false;
933 desc.MiscFlags = ovrTextureMisc_DX_Typeless;
934 desc.BindFlags = ovrTextureBind_DX_RenderTarget;
936 ovrResult orv =
937 ovr_CreateTextureSwapChainDX(mSession, mDevice, &desc, &mTextureSet);
938 if (orv != ovrSuccess) {
939 NS_WARNING("ovr_CreateTextureSwapChainDX failed");
940 return false;
943 int textureCount = 0;
944 orv = ovr_GetTextureSwapChainLength(mSession, mTextureSet, &textureCount);
945 if (orv != ovrSuccess) {
946 NS_WARNING("ovr_GetTextureSwapChainLength failed");
947 return false;
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.");
958 return false;
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));
966 if (FAILED(hr)) {
967 NS_WARNING(
968 "Failed to create RenderTargetView for Oculus texture swap chain.");
969 texture->Release();
970 return false;
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));
978 if (FAILED(hr)) {
979 NS_WARNING(
980 "Failed to create ShaderResourceView for Oculus texture swap "
981 "chain.");
982 texture->Release();
983 return false;
986 mTexture[i] = texture;
987 mRTView[i] = rtView;
988 mSRV[i] = srv;
989 texture->Release();
992 return true;
995 bool OculusSession::IsPresentationReady() const {
996 return mTextureSet != nullptr;
999 void OculusSession::StopRendering() {
1000 mSRV.Clear();
1001 mRTView.Clear();
1002 mTexture.Clear();
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;
1067 sensorState.flags =
1068 (VRDisplayCapabilityFlags)((int)
1069 VRDisplayCapabilityFlags::Cap_Orientation |
1070 (int)VRDisplayCapabilityFlags::Cap_Position);
1071 sensorState.pose.orientation[3] = 1.0f; // Default to an identity quaternion
1073 return true;
1076 void OculusSession::UpdateStageParameters(VRDisplayState& aState) {
1077 ovrVector3f playArea;
1078 ovrResult res =
1079 ovr_GetBoundaryDimensions(mSession, ovrBoundary_PlayArea, &playArea);
1080 if (res == ovrSuccess) {
1081 aState.stageSize.width = playArea.x;
1082 aState.stageSize.height = playArea.z;
1083 } else {
1084 // If we fail, fall back to reasonable defaults.
1085 // 1m x 1m space
1086 aState.stageSize.width = 1.0f;
1087 aState.stageSize.height = 1.0f;
1090 float eyeHeight =
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) {
1115 if (!mSession) {
1116 return;
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;
1135 Matrix4x4 pose;
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);
1144 pose.Invert();
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;
1158 Point3D eyeScale;
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) {
1175 if (!mSession) {
1176 return;
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
1182 // warnings...
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;
1209 } else {
1210 // default to an identity quaternion
1211 aState.sensorState.pose.orientation[3] = 1.0f;
1214 if (trackingState.StatusFlags & ovrStatus_PositionTracked) {
1215 float eyeHeight =
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) {
1240 if (!mSession) {
1241 return;
1244 ovrInputState inputState;
1245 bool hasInputState = ovr_GetInputState(mSession, ovrControllerType_Touch,
1246 &inputState) == ovrSuccess;
1248 if (!hasInputState) {
1249 return;
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;
1295 } else {
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;
1310 float eyeHeight =
1311 ovr_GetFloat(mSession, OVR_KEY_EYE_HEIGHT, OVR_DEFAULT_EYE_HEIGHT);
1312 controllerState.pose.position[1] -= eyeHeight;
1313 controllerState.isPositionValid = true;
1314 } else {
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;
1341 } else {
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],
1369 triggerThreshold);
1370 ++buttonIdx;
1371 // Button 1: Grip
1372 VRSession::UpdateTrigger(controllerState, buttonIdx,
1373 aInputState.HandTrigger[handIdx],
1374 triggerThreshold);
1375 ++buttonIdx;
1376 // Button 2: a placeholder button for trackpad.
1377 UpdateButton(aInputState, handIdx, buttonIdx, controllerState);
1378 ++buttonIdx;
1379 // Button 3: Thumbstick
1380 UpdateButton(aInputState, handIdx, buttonIdx, controllerState);
1381 ++buttonIdx;
1382 // Button 4: A
1383 UpdateButton(aInputState, handIdx, buttonIdx, controllerState);
1384 ++buttonIdx;
1385 // Button 5: B
1386 UpdateButton(aInputState, handIdx, buttonIdx, controllerState);
1387 ++buttonIdx;
1388 // Button 6: ThumbRest
1389 UpdateButton(aInputState, handIdx, buttonIdx, controllerState);
1390 ++buttonIdx;
1392 MOZ_ASSERT(buttonIdx == kNumOculusButtons);
1394 // Update Thumbstick axis
1395 uint32_t axisIdx = 0;
1396 // Axis 0, 1: placeholder axes for trackpad.
1397 axisIdx += 2;
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;
1405 axisIdx++;
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;
1413 axisIdx++;
1415 MOZ_ASSERT(axisIdx == kNumOculusAxes);
1417 SetControllerSelectionAndSqueezeFrameId(
1418 controllerState, aState.displayState.lastSubmittedFrameId);
1422 void OculusSession::UpdateTelemetry(VRSystemState& aSystemState) {
1423 if (!mSession) {
1424 return;
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,
1437 float aDuration) {
1438 if (!mSession) {
1439 return;
1442 if (aDuration <= 0.0f) {
1443 StopVibrateHaptic(aControllerIdx);
1444 return;
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) {
1465 if (!mSession) {
1466 return;
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() {
1488 if (!mSession) {
1489 return;
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
1494 // times per frame.
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;
1501 return;
1503 float deltaTime = (float)(now - mLastHapticUpdate).ToSeconds();
1504 mLastHapticUpdate = now;
1505 for (int i = 0; i < 2; i++) {
1506 if (mRemainingVibrateTime[i] <= 0.0f) {
1507 continue;
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.";
1518 } else {
1519 StopVibrateHaptic(i);
1524 } // namespace gfx
1525 } // namespace mozilla