OculusSession.cpp (54993B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef XP_WIN 8 # error "Oculus support only available for Windows" 9 #endif 10 11 #include <math.h> 12 #include <d3d11.h> 13 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" 21 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. 28 */ 29 30 // See CompositorD3D11Shaders.h 31 namespace mozilla { 32 namespace layers { 33 struct ShaderBytes { 34 const void* mData; 35 size_t mLength; 36 }; 37 extern ShaderBytes sRGBShader; 38 extern ShaderBytes sLayerQuadVS; 39 } // namespace layers 40 } // namespace mozilla 41 42 using namespace mozilla; 43 using namespace mozilla::gfx; 44 using namespace mozilla::layers; 45 46 namespace { 47 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; 118 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 126 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; 132 133 #ifdef HAVE_64BIT_BUILD 134 # define BUILD_BITS 64 135 #else 136 # define BUILD_BITS 32 137 #endif 138 139 #define OVR_PRODUCT_VERSION 1 140 #define OVR_MAJOR_VERSION 1 141 #define OVR_MINOR_VERSION 19 142 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}; 152 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}, 158 }; 159 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}, 165 }; 166 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; 174 } 175 if (aInputState.Touches & OculusControllerTouches[aHandIdx][aButtonIdx]) { 176 aControllerState.buttonTouched |= ((uint64_t)1 << aButtonIdx); 177 } 178 } 179 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; 187 } 188 189 } // anonymous namespace 190 191 namespace mozilla { 192 namespace gfx { 193 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) {} 210 211 OculusSession::~OculusSession() { Shutdown(); } 212 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; 218 } 219 if (!StaticPrefs::dom_vr_enabled() || !StaticPrefs::dom_vr_oculus_enabled()) { 220 return false; 221 } 222 223 if (aDetectRuntimesOnly) { 224 if (LoadOvrLib()) { 225 aSystemState.displayState.capabilityFlags |= 226 VRDisplayCapabilityFlags::Cap_ImmersiveVR; 227 } 228 return false; 229 } 230 231 if (!CreateD3DObjects()) { 232 return false; 233 } 234 if (!CreateShaders()) { 235 return false; 236 } 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; 242 } 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; 247 } 248 if (!InitState(aSystemState)) { 249 return false; 250 } 251 252 mPresentationSize = IntSize(aSystemState.displayState.eyeResolution.width * 2, 253 aSystemState.displayState.eyeResolution.height); 254 return true; 255 } 256 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; 266 } 267 if (mInitFlags & ovrInit_Invisible) { 268 // We are already invisible 269 return; 270 } 271 if (mLastPresentationEnd.IsNull()) { 272 // There has been no presentation yet 273 return; 274 } 275 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"; 282 } 283 } 284 } 285 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; 296 } 297 298 if (mInitFlags & ovrInit_Invisible) { 299 // We are invisible, nothing to cover up 300 return; 301 } 302 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); 309 } 310 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); 316 } 317 if (mInitFlags == flags) { 318 // The new state is the same, nothing to do 319 return true; 320 } 321 322 // Tear everything down 323 StopRendering(); 324 StopSession(); 325 StopLib(); 326 327 // Start it back up 328 if (!StartLib(flags)) { 329 return false; 330 } 331 if (!StartSession()) { 332 return false; 333 } 334 return true; 335 } 336 337 void OculusSession::Shutdown() { 338 StopRendering(); 339 StopSession(); 340 StopLib(); 341 UnloadOvrLib(); 342 DestroyShaders(); 343 } 344 345 void OculusSession::ProcessEvents(mozilla::gfx::VRSystemState& aSystemState) { 346 if (!mSession) { 347 return; 348 } 349 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; 355 356 } else { 357 aSystemState.displayState.isConnected = false; 358 aSystemState.displayState.isMounted = false; 359 } 360 UpdateHaptics(); 361 UpdateVisibility(); 362 CoverTransitions(); 363 } 364 365 void OculusSession::StartFrame(mozilla::gfx::VRSystemState& aSystemState) { 366 UpdateHeadsetPose(aSystemState); 367 UpdateEyeParameters(aSystemState); 368 UpdateControllers(aSystemState); 369 UpdateTelemetry(aSystemState); 370 aSystemState.sensorState.inputFrameID++; 371 } 372 373 bool OculusSession::StartPresentation() { 374 /** 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. 381 **/ 382 if (!ChangeVisibility(true)) { 383 return false; 384 } 385 if (!StartRendering()) { 386 StopRendering(); 387 return false; 388 } 389 mIsPresenting = true; 390 return true; 391 } 392 393 void OculusSession::StopPresentation() { 394 mLastPresentationEnd = TimeStamp::Now(); 395 mIsPresenting = false; 396 } 397 398 bool OculusSession::SubmitFrame( 399 const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer, 400 ID3D11Texture2D* aTexture) { 401 if (!IsPresentationReady()) { 402 return false; 403 } 404 405 D3D11_TEXTURE2D_DESC textureDesc = {0}; 406 aTexture->GetDesc(&textureDesc); 407 408 int currentRenderTarget = 0; 409 ovrResult orv = ovr_GetTextureSwapChainCurrentIndex(mSession, mTextureSet, 410 ¤tRenderTarget); 411 if (orv != ovrSuccess) { 412 NS_WARNING("ovr_GetTextureSwapChainCurrentIndex failed."); 413 return false; 414 } 415 416 ID3D11RenderTargetView* view = mRTView[currentRenderTarget]; 417 418 float clear[] = {0.0f, 0.0f, 0.0f, 1.0f}; 419 mContext->ClearRenderTargetView(view, clear); 420 mContext->OMSetRenderTargets(1, &view, nullptr); 421 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; 428 429 Matrix transform2d; 430 gfx::Matrix4x4 transform = gfx::Matrix4x4::From2D(transform2d); 431 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; 439 440 D3D11_RECT scissor; 441 scissor.left = 0; 442 scissor.right = textureDesc.Width; 443 scissor.top = 0; 444 scissor.bottom = textureDesc.Height; 445 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); 455 456 mPSConstants.layerOpacity[0] = 1.0f; 457 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); 469 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; 477 } 478 ID3D11ShaderResourceView* viewPtr = srView.get(); 479 mContext->PSSetShaderResources(0 /* 0 == TexSlot::RGB */, 1, &viewPtr); 480 // XXX Use Constant from TexSlot in CompositorD3D11.cpp? 481 482 ID3D11SamplerState* sampler = mLinearSamplerState; 483 mContext->PSSetSamplers(0, 1, &sampler); 484 485 if (!UpdateConstantBuffers()) { 486 NS_WARNING("Failed to update constant buffers for Oculus"); 487 return false; 488 } 489 490 mContext->Draw(4, 0); 491 492 orv = ovr_CommitTextureSwapChain(mSession, mTextureSet); 493 if (orv != ovrSuccess) { 494 NS_WARNING("ovr_CommitTextureSwapChain failed."); 495 return false; 496 } 497 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; 514 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; 523 } 524 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. 529 530 if (!OVR_UNQUALIFIED_SUCCESS(orv)) { 531 /** 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. 538 */ 539 return false; 540 } 541 542 return true; 543 } 544 545 bool OculusSession::LoadOvrLib() { 546 if (mOvrLib) { 547 // Already loaded, early exit 548 return true; 549 } 550 #if defined(_WIN32) 551 nsTArray<nsString> libSearchPaths; 552 nsString libName; 553 nsString searchPath; 554 555 for (;;) { 556 UINT requiredLength = ::GetSystemDirectoryW( 557 char16ptr_t(searchPath.BeginWriting()), searchPath.Length()); 558 if (!requiredLength) { 559 break; 560 } 561 if (requiredLength < searchPath.Length()) { 562 searchPath.Truncate(requiredLength); 563 libSearchPaths.AppendElement(searchPath); 564 break; 565 } 566 searchPath.SetLength(requiredLength); 567 } 568 libName.AppendPrintf("LibOVRRT%d_%d.dll", BUILD_BITS, OVR_PRODUCT_VERSION); 569 570 // search the path/module dir 571 libSearchPaths.InsertElementsAt(0, 1, u""_ns); 572 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); 577 } 578 579 if (_wgetenv(L"OVR_LIB_NAME")) { 580 libName = _wgetenv(L"OVR_LIB_NAME"); 581 } 582 583 if (libName.IsEmpty()) { 584 return false; 585 } 586 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); 594 } 595 596 mOvrLib = LoadLibraryWithFlags(fullName.get()); 597 if (mOvrLib) { 598 break; 599 } 600 } 601 #else 602 # error "Unsupported platform!" 603 #endif 604 605 if (!mOvrLib) { 606 return false; 607 } 608 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; \ 615 } \ 616 } while (0) 617 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); 682 683 #ifdef XP_WIN 684 685 REQUIRE_FUNCTION(ovr_CreateTextureSwapChainDX); 686 REQUIRE_FUNCTION(ovr_GetTextureSwapChainBufferDX); 687 REQUIRE_FUNCTION(ovr_CreateMirrorTextureDX); 688 REQUIRE_FUNCTION(ovr_GetMirrorTextureBufferDX); 689 690 #endif 691 692 REQUIRE_FUNCTION(ovr_CreateTextureSwapChainGL); 693 REQUIRE_FUNCTION(ovr_GetTextureSwapChainBufferGL); 694 REQUIRE_FUNCTION(ovr_CreateMirrorTextureGL); 695 REQUIRE_FUNCTION(ovr_GetMirrorTextureBufferGL); 696 697 #undef REQUIRE_FUNCTION 698 699 return true; 700 701 fail: 702 ovr_Initialize = nullptr; 703 PR_UnloadLibrary(mOvrLib); 704 mOvrLib = nullptr; 705 return false; 706 } 707 708 void OculusSession::UnloadOvrLib() { 709 if (mOvrLib) { 710 PR_UnloadLibrary(mOvrLib); 711 mOvrLib = nullptr; 712 } 713 } 714 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; 723 724 ovrResult orv = ovr_Initialize(¶ms); 725 726 if (orv == ovrSuccess) { 727 mInitFlags = aFlags; 728 } else { 729 return false; 730 } 731 } 732 MOZ_ASSERT(mInitFlags == aFlags); 733 return true; 734 } 735 736 void OculusSession::StopLib() { 737 if (mInitFlags) { 738 ovr_Shutdown(); 739 mInitFlags = (ovrInitFlags)0; 740 } 741 } 742 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; 753 } 754 return false; 755 } 756 if (mSession != nullptr) { 757 // HMD Detected and we already have a session, let's keep using it. 758 return true; 759 } 760 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"); 770 } 771 mSession = session; 772 return true; 773 } 774 775 // Failed to create a session for the HMD 776 return false; 777 } 778 779 void OculusSession::StopSession() { 780 if (mSession) { 781 ovr_Destroy(mSession); 782 mSession = nullptr; 783 } 784 } 785 786 bool OculusSession::CreateD3DObjects() { 787 RefPtr<ID3D11Device> device = gfx::DeviceManagerDx::Get()->GetVRDevice(); 788 if (!device) { 789 return false; 790 } 791 if (!CreateD3DContext(device)) { 792 return false; 793 } 794 return true; 795 } 796 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; 803 } 804 } 805 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; 811 } 812 } 813 814 CD3D11_BUFFER_DESC cBufferDesc(sizeof(layers::VertexShaderConstants), 815 D3D11_BIND_CONSTANT_BUFFER, 816 D3D11_USAGE_DYNAMIC, D3D11_CPU_ACCESS_WRITE); 817 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; 823 } 824 } 825 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; 832 } 833 } 834 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; 841 } 842 } 843 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}, 848 }; 849 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; 856 } 857 } 858 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; 865 866 if (FAILED(mDevice->CreateBuffer(&bufferDesc, &data, 867 getter_AddRefs(mVertexBuffer)))) { 868 NS_WARNING("Failed to create vertex buffer for Oculus"); 869 return false; 870 } 871 } 872 873 memset(&mVSConstants, 0, sizeof(mVSConstants)); 874 memset(&mPSConstants, 0, sizeof(mPSConstants)); 875 return true; 876 } 877 878 void OculusSession::DestroyShaders() {} 879 880 bool OculusSession::UpdateConstantBuffers() { 881 HRESULT hr; 882 D3D11_MAPPED_SUBRESOURCE resource; 883 resource.pData = nullptr; 884 885 hr = mContext->Map(mVSConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, 886 &resource); 887 if (FAILED(hr) || !resource.pData) { 888 return false; 889 } 890 *(VertexShaderConstants*)resource.pData = mVSConstants; 891 mContext->Unmap(mVSConstantBuffer, 0); 892 resource.pData = nullptr; 893 894 hr = mContext->Map(mPSConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, 895 &resource); 896 if (FAILED(hr) || !resource.pData) { 897 return false; 898 } 899 *(PixelShaderConstants*)resource.pData = mPSConstants; 900 mContext->Unmap(mPSConstantBuffer, 0); 901 902 ID3D11Buffer* buffer = mVSConstantBuffer; 903 mContext->VSSetConstantBuffers(0, 1, &buffer); 904 buffer = mPSConstantBuffer; 905 mContext->PSSetConstantBuffers(0, 1, &buffer); 906 return true; 907 } 908 909 bool OculusSession::StartRendering() { 910 if (!mTextureSet) { 911 /** 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. 916 * 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) 921 */ 922 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; 935 936 ovrResult orv = 937 ovr_CreateTextureSwapChainDX(mSession, mDevice, &desc, &mTextureSet); 938 if (orv != ovrSuccess) { 939 NS_WARNING("ovr_CreateTextureSwapChainDX failed"); 940 return false; 941 } 942 943 int textureCount = 0; 944 orv = ovr_GetTextureSwapChainLength(mSession, mTextureSet, &textureCount); 945 if (orv != ovrSuccess) { 946 NS_WARNING("ovr_GetTextureSwapChainLength failed"); 947 return false; 948 } 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; 959 } 960 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; 971 } 972 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; 984 } 985 986 mTexture[i] = texture; 987 mRTView[i] = rtView; 988 mSRV[i] = srv; 989 texture->Release(); 990 } 991 } 992 return true; 993 } 994 995 bool OculusSession::IsPresentationReady() const { 996 return mTextureSet != nullptr; 997 } 998 999 void OculusSession::StopRendering() { 1000 mSRV.Clear(); 1001 mRTView.Clear(); 1002 mTexture.Clear(); 1003 1004 if (mTextureSet && mSession) { 1005 ovr_DestroyTextureSwapChain(mSession, mTextureSet); 1006 } 1007 mTextureSet = nullptr; 1008 mIsPresenting = false; 1009 } 1010 1011 bool OculusSession::InitState(VRSystemState& aSystemState) { 1012 VRDisplayState& state = aSystemState.displayState; 1013 strncpy(state.displayName.data(), "Oculus VR HMD", kVRDisplayNameMaxLen); 1014 state.isConnected = true; 1015 state.isMounted = false; 1016 1017 ovrHmdDesc desc = ovr_GetHmdDesc(mSession); 1018 1019 state.capabilityFlags = VRDisplayCapabilityFlags::Cap_None; 1020 if (desc.AvailableTrackingCaps & ovrTrackingCap_Orientation) { 1021 state.capabilityFlags |= VRDisplayCapabilityFlags::Cap_Orientation; 1022 state.capabilityFlags |= VRDisplayCapabilityFlags::Cap_AngularAcceleration; 1023 } 1024 if (desc.AvailableTrackingCaps & ovrTrackingCap_Position) { 1025 state.capabilityFlags |= VRDisplayCapabilityFlags::Cap_Position; 1026 state.capabilityFlags |= VRDisplayCapabilityFlags::Cap_LinearAcceleration; 1027 state.capabilityFlags |= VRDisplayCapabilityFlags::Cap_StageParameters; 1028 } 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; 1035 1036 mFOVPort[VRDisplayState::Eye_Left] = desc.DefaultEyeFov[ovrEye_Left]; 1037 mFOVPort[VRDisplayState::Eye_Right] = desc.DefaultEyeFov[ovrEye_Right]; 1038 1039 state.eyeFOV[VRDisplayState::Eye_Left] = 1040 FromFovPort(mFOVPort[VRDisplayState::Eye_Left]); 1041 state.eyeFOV[VRDisplayState::Eye_Right] = 1042 FromFovPort(mFOVPort[VRDisplayState::Eye_Right]); 1043 1044 float pixelsPerDisplayPixel = 1.0; 1045 ovrSizei texSize[2]; 1046 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); 1051 } 1052 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; 1059 1060 // default to an identity quaternion 1061 aSystemState.sensorState.pose.orientation[3] = 1.0f; 1062 1063 UpdateStageParameters(state); 1064 UpdateEyeParameters(aSystemState); 1065 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 1072 1073 return true; 1074 } 1075 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; 1088 } 1089 1090 float eyeHeight = 1091 ovr_GetFloat(mSession, OVR_KEY_EYE_HEIGHT, OVR_DEFAULT_EYE_HEIGHT); 1092 1093 aState.sittingToStandingTransform[0] = 1.0f; 1094 aState.sittingToStandingTransform[1] = 0.0f; 1095 aState.sittingToStandingTransform[2] = 0.0f; 1096 aState.sittingToStandingTransform[3] = 0.0f; 1097 1098 aState.sittingToStandingTransform[4] = 0.0f; 1099 aState.sittingToStandingTransform[5] = 1.0f; 1100 aState.sittingToStandingTransform[6] = 0.0f; 1101 aState.sittingToStandingTransform[7] = 0.0f; 1102 1103 aState.sittingToStandingTransform[8] = 0.0f; 1104 aState.sittingToStandingTransform[9] = 0.0f; 1105 aState.sittingToStandingTransform[10] = 1.0f; 1106 aState.sittingToStandingTransform[11] = 0.0f; 1107 1108 aState.sittingToStandingTransform[12] = 0.0f; 1109 aState.sittingToStandingTransform[13] = eyeHeight; 1110 aState.sittingToStandingTransform[14] = 0.0f; 1111 aState.sittingToStandingTransform[15] = 1.0f; 1112 } 1113 1114 void OculusSession::UpdateEyeParameters(VRSystemState& aState) { 1115 if (!mSession) { 1116 return; 1117 } 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; 1134 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; 1146 } 1147 aState.sensorState.CalcViewMatrices(headToEyeTransforms); 1148 1149 Matrix4x4 matView[2]; 1150 memcpy(matView[0].components, aState.sensorState.leftViewMatrix.data(), 1151 sizeof(float) * 16); 1152 memcpy(matView[1].components, aState.sensorState.rightViewMatrix.data(), 1153 sizeof(float) * 16); 1154 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"); 1161 } 1162 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; 1171 } 1172 } 1173 1174 void OculusSession::UpdateHeadsetPose(VRSystemState& aState) { 1175 if (!mSession) { 1176 return; 1177 } 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); 1184 } 1185 ovrTrackingState trackingState = 1186 ovr_GetTrackingState(mSession, predictedFrameTime, true); 1187 ovrPoseStatef& pose(trackingState.HeadPose); 1188 1189 aState.sensorState.timestamp = pose.TimeInSeconds; 1190 1191 if (trackingState.StatusFlags & ovrStatus_OrientationTracked) { 1192 aState.sensorState.flags |= VRDisplayCapabilityFlags::Cap_Orientation; 1193 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; 1198 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; 1202 1203 aState.sensorState.flags |= 1204 VRDisplayCapabilityFlags::Cap_AngularAcceleration; 1205 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; 1212 } 1213 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; 1218 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; 1222 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; 1226 1227 aState.sensorState.flags |= 1228 VRDisplayCapabilityFlags::Cap_LinearAcceleration; 1229 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; 1233 } 1234 aState.sensorState.flags |= VRDisplayCapabilityFlags::Cap_External; 1235 aState.sensorState.flags |= VRDisplayCapabilityFlags::Cap_MountDetection; 1236 aState.sensorState.flags |= VRDisplayCapabilityFlags::Cap_Present; 1237 } 1238 1239 void OculusSession::UpdateControllers(VRSystemState& aState) { 1240 if (!mSession) { 1241 return; 1242 } 1243 1244 ovrInputState inputState; 1245 bool hasInputState = ovr_GetInputState(mSession, ovrControllerType_Touch, 1246 &inputState) == ovrSuccess; 1247 1248 if (!hasInputState) { 1249 return; 1250 } 1251 1252 EnumerateControllers(aState, inputState); 1253 UpdateControllerInputs(aState, inputState); 1254 UpdateControllerPose(aState, inputState); 1255 } 1256 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; 1277 } 1278 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; 1297 } 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; 1309 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; 1316 } 1317 controllerState.targetRayPose = controllerState.pose; 1318 } 1319 } 1320 } 1321 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.data(), 1333 OculusControllerNames[handIdx], 1334 controllerState.controllerName.size()); 1335 controllerState.hand = OculusControllerHand[handIdx]; 1336 controllerState.targetRayMode = gfx::TargetRayMode::TrackedPointer; 1337 controllerState.numButtons = kNumOculusButtons; 1338 controllerState.numAxes = kNumOculusAxes; 1339 controllerState.numHaptics = kNumOculusHaptcs; 1340 controllerState.type = VRControllerType::OculusTouch; 1341 } 1342 } else { 1343 // Touch Controller not detected 1344 if (controllerState.controllerName[0] != '\0') { 1345 // Clear any newly disconnected ontrollers 1346 memset(&controllerState, 0, sizeof(VRControllerState)); 1347 } 1348 } 1349 } 1350 } 1351 1352 void OculusSession::UpdateControllerInputs(VRSystemState& aState, 1353 const ovrInputState& aInputState) { 1354 const float triggerThreshold = 1355 StaticPrefs::dom_vr_controller_trigger_threshold(); 1356 1357 for (uint32_t handIdx = 0; handIdx < 2; handIdx++) { 1358 // Left Touch Controller will always be at index 0 and 1359 // and Right Touch Controller will always be at index 1 1360 VRControllerState& controllerState = aState.controllerState[handIdx]; 1361 if (aInputState.ControllerType & OculusControllerTypes[handIdx]) { 1362 // Update Button States 1363 controllerState.buttonPressed = 0; 1364 controllerState.buttonTouched = 0; 1365 uint32_t buttonIdx = 0; 1366 1367 // Button 0: Trigger 1368 VRSession::UpdateTrigger(controllerState, buttonIdx, 1369 aInputState.IndexTrigger[handIdx], 1370 triggerThreshold); 1371 ++buttonIdx; 1372 // Button 1: Grip 1373 VRSession::UpdateTrigger(controllerState, buttonIdx, 1374 aInputState.HandTrigger[handIdx], 1375 triggerThreshold); 1376 ++buttonIdx; 1377 // Button 2: a placeholder button for trackpad. 1378 UpdateButton(aInputState, handIdx, buttonIdx, controllerState); 1379 ++buttonIdx; 1380 // Button 3: Thumbstick 1381 UpdateButton(aInputState, handIdx, buttonIdx, controllerState); 1382 ++buttonIdx; 1383 // Button 4: A 1384 UpdateButton(aInputState, handIdx, buttonIdx, controllerState); 1385 ++buttonIdx; 1386 // Button 5: B 1387 UpdateButton(aInputState, handIdx, buttonIdx, controllerState); 1388 ++buttonIdx; 1389 // Button 6: ThumbRest 1390 UpdateButton(aInputState, handIdx, buttonIdx, controllerState); 1391 ++buttonIdx; 1392 1393 MOZ_ASSERT(buttonIdx == kNumOculusButtons); 1394 1395 // Update Thumbstick axis 1396 uint32_t axisIdx = 0; 1397 // Axis 0, 1: placeholder axes for trackpad. 1398 axisIdx += 2; 1399 1400 // Axis 2, 3: placeholder axes for thumbstick. 1401 float axisValue = aInputState.Thumbstick[handIdx].x; 1402 if (abs(axisValue) < 0.0000009f) { 1403 axisValue = 0.0f; // Clear noise signal 1404 } 1405 controllerState.axisValue[axisIdx] = axisValue; 1406 axisIdx++; 1407 1408 // Note that y axis is intentionally inverted! 1409 axisValue = -aInputState.Thumbstick[handIdx].y; 1410 if (abs(axisValue) < 0.0000009f) { 1411 axisValue = 0.0f; // Clear noise signal 1412 } 1413 controllerState.axisValue[axisIdx] = axisValue; 1414 axisIdx++; 1415 1416 MOZ_ASSERT(axisIdx == kNumOculusAxes); 1417 } 1418 SetControllerSelectionAndSqueezeFrameId( 1419 controllerState, aState.displayState.lastSubmittedFrameId); 1420 } 1421 } 1422 1423 void OculusSession::UpdateTelemetry(VRSystemState& aSystemState) { 1424 if (!mSession) { 1425 return; 1426 } 1427 ovrPerfStats perfStats; 1428 if (ovr_GetPerfStats(mSession, &perfStats) == ovrSuccess) { 1429 if (perfStats.FrameStatsCount) { 1430 aSystemState.displayState.droppedFrameCount = 1431 perfStats.FrameStats[0].AppDroppedFrameCount; 1432 } 1433 } 1434 } 1435 1436 void OculusSession::VibrateHaptic(uint32_t aControllerIdx, 1437 uint32_t aHapticIndex, float aIntensity, 1438 float aDuration) { 1439 if (!mSession) { 1440 return; 1441 } 1442 1443 if (aDuration <= 0.0f) { 1444 StopVibrateHaptic(aControllerIdx); 1445 return; 1446 } 1447 1448 // Vibration amplitude in the [0.0, 1.0] range 1449 MOZ_ASSERT(aControllerIdx >= 0 && aControllerIdx <= 1); 1450 mHapticPulseIntensity[aControllerIdx] = aIntensity > 1.0 ? 1.0 : aIntensity; 1451 mRemainingVibrateTime[aControllerIdx] = aDuration; 1452 ovrControllerType hand = OculusControllerTypes[aControllerIdx]; 1453 1454 // The gamepad extensions API does not yet have independent control 1455 // of frequency and amplitude. We are always sending 0.0f (160hz) 1456 // to the frequency argument. 1457 ovrResult result = ovr_SetControllerVibration( 1458 mSession, hand, 0.0f, mHapticPulseIntensity[aControllerIdx]); 1459 if (result != ovrSuccess) { 1460 // This may happen if called when not presenting. 1461 gfxWarning() << "ovr_SetControllerVibration failed."; 1462 } 1463 } 1464 1465 void OculusSession::StopVibrateHaptic(uint32_t aControllerIdx) { 1466 if (!mSession) { 1467 return; 1468 } 1469 MOZ_ASSERT(aControllerIdx >= 0 && aControllerIdx <= 1); 1470 ovrControllerType hand = OculusControllerTypes[aControllerIdx]; 1471 mRemainingVibrateTime[aControllerIdx] = 0.0f; 1472 mHapticPulseIntensity[aControllerIdx] = 0.0f; 1473 1474 ovrResult result = ovr_SetControllerVibration(mSession, hand, 0.0f, 0.0f); 1475 if (result != ovrSuccess) { 1476 // This may happen if called when not presenting. 1477 gfxWarning() << "ovr_SetControllerVibration failed."; 1478 } 1479 } 1480 1481 void OculusSession::StopAllHaptics() { 1482 // Left Oculus Touch 1483 StopVibrateHaptic(0); 1484 // Right Oculus Touch 1485 StopVibrateHaptic(1); 1486 } 1487 1488 void OculusSession::UpdateHaptics() { 1489 if (!mSession) { 1490 return; 1491 } 1492 // The Oculus API and hardware takes at least 33ms to respond 1493 // to haptic state changes, so it is not beneficial to create 1494 // a dedicated haptic feedback thread and update multiple 1495 // times per frame. 1496 // If we wish to support more accurate effects with sub-frame timing, 1497 // we should use the buffered haptic feedback API's. 1498 1499 TimeStamp now = TimeStamp::Now(); 1500 if (mLastHapticUpdate.IsNull()) { 1501 mLastHapticUpdate = now; 1502 return; 1503 } 1504 float deltaTime = (float)(now - mLastHapticUpdate).ToSeconds(); 1505 mLastHapticUpdate = now; 1506 for (int i = 0; i < 2; i++) { 1507 if (mRemainingVibrateTime[i] <= 0.0f) { 1508 continue; 1509 } 1510 mRemainingVibrateTime[i] -= deltaTime; 1511 ovrControllerType hand = OculusControllerTypes[i]; 1512 if (mRemainingVibrateTime[i] > 0.0f) { 1513 ovrResult result = ovr_SetControllerVibration(mSession, hand, 0.0f, 1514 mHapticPulseIntensity[i]); 1515 if (result != ovrSuccess) { 1516 // This may happen if called when not presenting. 1517 gfxWarning() << "ovr_SetControllerVibration failed."; 1518 } 1519 } else { 1520 StopVibrateHaptic(i); 1521 } 1522 } 1523 } 1524 1525 } // namespace gfx 1526 } // namespace mozilla