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/SharedLibrary.h"
19 #include "OculusSession.h"
20 
21 /** XXX The DX11 objects and quad blitting could be encapsulated
22  *    into a separate object if either Oculus starts supporting
23  *     non-Windows platforms or the blit is needed by other HMD\
24  *     drivers.
25  *     Alternately, we could remove the extra blit for
26  *     Oculus as well with some more refactoring.
27  */
28 
29 // See CompositorD3D11Shaders.h
30 namespace mozilla {
31 namespace layers {
32 struct ShaderBytes {
33   const void* mData;
34   size_t mLength;
35 };
36 extern ShaderBytes sRGBShader;
37 extern ShaderBytes sLayerQuadVS;
38 }  // namespace layers
39 }  // namespace mozilla
40 
41 using namespace mozilla;
42 using namespace mozilla::gfx;
43 using namespace mozilla::layers;
44 
45 namespace {
46 
47 static pfn_ovr_Initialize ovr_Initialize = nullptr;
48 static pfn_ovr_Shutdown ovr_Shutdown = nullptr;
49 static pfn_ovr_GetLastErrorInfo ovr_GetLastErrorInfo = nullptr;
50 static pfn_ovr_GetVersionString ovr_GetVersionString = nullptr;
51 static pfn_ovr_TraceMessage ovr_TraceMessage = nullptr;
52 static pfn_ovr_IdentifyClient ovr_IdentifyClient = nullptr;
53 static pfn_ovr_GetHmdDesc ovr_GetHmdDesc = nullptr;
54 static pfn_ovr_GetTrackerCount ovr_GetTrackerCount = nullptr;
55 static pfn_ovr_GetTrackerDesc ovr_GetTrackerDesc = nullptr;
56 static pfn_ovr_Create ovr_Create = nullptr;
57 static pfn_ovr_Destroy ovr_Destroy = nullptr;
58 static pfn_ovr_GetSessionStatus ovr_GetSessionStatus = nullptr;
59 static pfn_ovr_IsExtensionSupported ovr_IsExtensionSupported = nullptr;
60 static pfn_ovr_EnableExtension ovr_EnableExtension = nullptr;
61 static pfn_ovr_SetTrackingOriginType ovr_SetTrackingOriginType = nullptr;
62 static pfn_ovr_GetTrackingOriginType ovr_GetTrackingOriginType = nullptr;
63 static pfn_ovr_RecenterTrackingOrigin ovr_RecenterTrackingOrigin = nullptr;
64 static pfn_ovr_SpecifyTrackingOrigin ovr_SpecifyTrackingOrigin = nullptr;
65 static pfn_ovr_ClearShouldRecenterFlag ovr_ClearShouldRecenterFlag = nullptr;
66 static pfn_ovr_GetTrackingState ovr_GetTrackingState = nullptr;
67 static pfn_ovr_GetDevicePoses ovr_GetDevicePoses = nullptr;
68 static pfn_ovr_GetTrackerPose ovr_GetTrackerPose = nullptr;
69 static pfn_ovr_GetInputState ovr_GetInputState = nullptr;
70 static pfn_ovr_GetConnectedControllerTypes ovr_GetConnectedControllerTypes =
71     nullptr;
72 static pfn_ovr_GetTouchHapticsDesc ovr_GetTouchHapticsDesc = nullptr;
73 static pfn_ovr_SetControllerVibration ovr_SetControllerVibration = nullptr;
74 static pfn_ovr_SubmitControllerVibration ovr_SubmitControllerVibration =
75     nullptr;
76 static pfn_ovr_GetControllerVibrationState ovr_GetControllerVibrationState =
77     nullptr;
78 static pfn_ovr_TestBoundary ovr_TestBoundary = nullptr;
79 static pfn_ovr_TestBoundaryPoint ovr_TestBoundaryPoint = nullptr;
80 static pfn_ovr_SetBoundaryLookAndFeel ovr_SetBoundaryLookAndFeel = nullptr;
81 static pfn_ovr_ResetBoundaryLookAndFeel ovr_ResetBoundaryLookAndFeel = nullptr;
82 static pfn_ovr_GetBoundaryGeometry ovr_GetBoundaryGeometry = nullptr;
83 static pfn_ovr_GetBoundaryDimensions ovr_GetBoundaryDimensions = nullptr;
84 static pfn_ovr_GetBoundaryVisible ovr_GetBoundaryVisible = nullptr;
85 static pfn_ovr_RequestBoundaryVisible ovr_RequestBoundaryVisible = nullptr;
86 static pfn_ovr_GetTextureSwapChainLength ovr_GetTextureSwapChainLength =
87     nullptr;
88 static pfn_ovr_GetTextureSwapChainCurrentIndex
89     ovr_GetTextureSwapChainCurrentIndex = nullptr;
90 static pfn_ovr_GetTextureSwapChainDesc ovr_GetTextureSwapChainDesc = nullptr;
91 static pfn_ovr_CommitTextureSwapChain ovr_CommitTextureSwapChain = nullptr;
92 static pfn_ovr_DestroyTextureSwapChain ovr_DestroyTextureSwapChain = nullptr;
93 static pfn_ovr_DestroyMirrorTexture ovr_DestroyMirrorTexture = nullptr;
94 static pfn_ovr_GetFovTextureSize ovr_GetFovTextureSize = nullptr;
95 static pfn_ovr_GetRenderDesc2 ovr_GetRenderDesc2 = nullptr;
96 static pfn_ovr_WaitToBeginFrame ovr_WaitToBeginFrame = nullptr;
97 static pfn_ovr_BeginFrame ovr_BeginFrame = nullptr;
98 static pfn_ovr_EndFrame ovr_EndFrame = nullptr;
99 static pfn_ovr_SubmitFrame ovr_SubmitFrame = nullptr;
100 static pfn_ovr_GetPerfStats ovr_GetPerfStats = nullptr;
101 static pfn_ovr_ResetPerfStats ovr_ResetPerfStats = nullptr;
102 static pfn_ovr_GetPredictedDisplayTime ovr_GetPredictedDisplayTime = nullptr;
103 static pfn_ovr_GetTimeInSeconds ovr_GetTimeInSeconds = nullptr;
104 static pfn_ovr_GetBool ovr_GetBool = nullptr;
105 static pfn_ovr_SetBool ovr_SetBool = nullptr;
106 static pfn_ovr_GetInt ovr_GetInt = nullptr;
107 static pfn_ovr_SetInt ovr_SetInt = nullptr;
108 static pfn_ovr_GetFloat ovr_GetFloat = nullptr;
109 static pfn_ovr_SetFloat ovr_SetFloat = nullptr;
110 static pfn_ovr_GetFloatArray ovr_GetFloatArray = nullptr;
111 static pfn_ovr_SetFloatArray ovr_SetFloatArray = nullptr;
112 static pfn_ovr_GetString ovr_GetString = nullptr;
113 static pfn_ovr_SetString ovr_SetString = nullptr;
114 static pfn_ovr_GetExternalCameras ovr_GetExternalCameras = nullptr;
115 static pfn_ovr_SetExternalCameraProperties ovr_SetExternalCameraProperties =
116     nullptr;
117 
118 #ifdef XP_WIN
119 static pfn_ovr_CreateTextureSwapChainDX ovr_CreateTextureSwapChainDX = nullptr;
120 static pfn_ovr_GetTextureSwapChainBufferDX ovr_GetTextureSwapChainBufferDX =
121     nullptr;
122 static pfn_ovr_CreateMirrorTextureDX ovr_CreateMirrorTextureDX = nullptr;
123 static pfn_ovr_GetMirrorTextureBufferDX ovr_GetMirrorTextureBufferDX = nullptr;
124 #endif
125 
126 static pfn_ovr_CreateTextureSwapChainGL ovr_CreateTextureSwapChainGL = nullptr;
127 static pfn_ovr_GetTextureSwapChainBufferGL ovr_GetTextureSwapChainBufferGL =
128     nullptr;
129 static pfn_ovr_CreateMirrorTextureGL ovr_CreateMirrorTextureGL = nullptr;
130 static pfn_ovr_GetMirrorTextureBufferGL ovr_GetMirrorTextureBufferGL = nullptr;
131 
132 #ifdef HAVE_64BIT_BUILD
133 #  define BUILD_BITS 64
134 #else
135 #  define BUILD_BITS 32
136 #endif
137 
138 #define OVR_PRODUCT_VERSION 1
139 #define OVR_MAJOR_VERSION 1
140 #define OVR_MINOR_VERSION 19
141 
142 static const uint32_t kNumOculusButtons = 7;
143 static const uint32_t kNumOculusHaptcs = 1;
144 static const uint32_t kNumOculusAxes = 4;
145 ovrControllerType OculusControllerTypes[2] = {ovrControllerType_LTouch,
146                                               ovrControllerType_RTouch};
147 const char* OculusControllerNames[2] = {"Oculus Touch (Left)",
148                                         "Oculus Touch (Right)"};
149 dom::GamepadHand OculusControllerHand[2] = {dom::GamepadHand::Left,
150                                             dom::GamepadHand::Right};
151 
152 ovrButton OculusControllerButtons[2][kNumOculusButtons] = {
153     {(ovrButton)0, (ovrButton)0, (ovrButton)0, ovrButton_LThumb, ovrButton_X,
154      ovrButton_Y, (ovrButton)0},
155     {(ovrButton)0, (ovrButton)0, (ovrButton)0, ovrButton_RThumb, ovrButton_A,
156      ovrButton_B, (ovrButton)0},
157 };
158 
159 ovrTouch OculusControllerTouches[2][kNumOculusButtons] = {
160     {ovrTouch_LIndexTrigger, (ovrTouch)0, (ovrTouch)0, ovrTouch_LThumb,
161      ovrTouch_X, ovrTouch_Y, ovrTouch_LThumbRest},
162     {ovrTouch_RIndexTrigger, (ovrTouch)0, (ovrTouch)0, ovrTouch_RThumb,
163      ovrTouch_A, ovrTouch_B, ovrTouch_RThumbRest},
164 };
165 
UpdateButton(const ovrInputState & aInputState,uint32_t aHandIdx,uint32_t aButtonIdx,VRControllerState & aControllerState)166 void UpdateButton(const ovrInputState& aInputState, uint32_t aHandIdx,
167                   uint32_t aButtonIdx, VRControllerState& aControllerState) {
168   if (aInputState.Buttons & OculusControllerButtons[aHandIdx][aButtonIdx]) {
169     aControllerState.buttonPressed |= ((uint64_t)1 << aButtonIdx);
170     aControllerState.triggerValue[aButtonIdx] = 1.0f;
171   } else {
172     aControllerState.triggerValue[aButtonIdx] = 0.0f;
173   }
174   if (aInputState.Touches & OculusControllerTouches[aHandIdx][aButtonIdx]) {
175     aControllerState.buttonTouched |= ((uint64_t)1 << aButtonIdx);
176   }
177 }
178 
FromFovPort(const ovrFovPort & aFOV)179 VRFieldOfView FromFovPort(const ovrFovPort& aFOV) {
180   VRFieldOfView fovInfo;
181   fovInfo.leftDegrees = atan(aFOV.LeftTan) * 180.0 / M_PI;
182   fovInfo.rightDegrees = atan(aFOV.RightTan) * 180.0 / M_PI;
183   fovInfo.upDegrees = atan(aFOV.UpTan) * 180.0 / M_PI;
184   fovInfo.downDegrees = atan(aFOV.DownTan) * 180.0 / M_PI;
185   return fovInfo;
186 }
187 
188 }  // anonymous namespace
189 
190 namespace mozilla {
191 namespace gfx {
192 
OculusSession()193 OculusSession::OculusSession()
194     : VRSession(),
195       mOvrLib(nullptr),
196       mSession(nullptr),
197       mInitFlags((ovrInitFlags)0),
198       mTextureSet(nullptr),
199       mQuadVS(nullptr),
200       mQuadPS(nullptr),
201       mLinearSamplerState(nullptr),
202       mVSConstantBuffer(nullptr),
203       mPSConstantBuffer(nullptr),
204       mVertexBuffer(nullptr),
205       mInputLayout(nullptr),
206       mRemainingVibrateTime{},
207       mHapticPulseIntensity{},
208       mIsPresenting(false) {}
209 
~OculusSession()210 OculusSession::~OculusSession() { Shutdown(); }
211 
Initialize(mozilla::gfx::VRSystemState & aSystemState,bool aDetectRuntimesOnly)212 bool OculusSession::Initialize(mozilla::gfx::VRSystemState& aSystemState,
213                                bool aDetectRuntimesOnly) {
214   if (!StaticPrefs::dom_vr_enabled() ||
215       !StaticPrefs::dom_vr_oculus_enabled_AtStartup()) {
216     return false;
217   }
218 
219   if (aDetectRuntimesOnly) {
220     if (LoadOvrLib()) {
221       aSystemState.displayState.capabilityFlags |=
222           VRDisplayCapabilityFlags::Cap_ImmersiveVR;
223     }
224     return false;
225   }
226 
227   if (!CreateD3DObjects()) {
228     return false;
229   }
230   if (!CreateShaders()) {
231     return false;
232   }
233   // Ideally, we should move LoadOvrLib() up to the first line to avoid
234   // unnecessary D3D objects creation. But it will cause a WPT fail in Win 7
235   // debug.
236   if (!LoadOvrLib()) {
237     return false;
238   }
239   // We start off with an invisible session, then re-initialize
240   // with visible session once WebVR content starts rendering.
241   if (!ChangeVisibility(false)) {
242     return false;
243   }
244   if (!InitState(aSystemState)) {
245     return false;
246   }
247 
248   mPresentationSize = IntSize(aSystemState.displayState.eyeResolution.width * 2,
249                               aSystemState.displayState.eyeResolution.height);
250   return true;
251 }
252 
UpdateVisibility()253 void OculusSession::UpdateVisibility() {
254   // Do not immediately re-initialize with an invisible session after
255   // the end of a VR presentation.  Waiting for the configured duraction
256   // ensures that the user will not drop to Oculus Home during VR link
257   // traversal.
258   if (mIsPresenting) {
259     // We are currently rendering immersive content.
260     // Avoid interrupting the session
261     return;
262   }
263   if (mInitFlags & ovrInit_Invisible) {
264     // We are already invisible
265     return;
266   }
267   if (mLastPresentationEnd.IsNull()) {
268     // There has been no presentation yet
269     return;
270   }
271 
272   TimeDuration duration = TimeStamp::Now() - mLastPresentationEnd;
273   TimeDuration timeout = TimeDuration::FromMilliseconds(
274       StaticPrefs::dom_vr_oculus_present_timeout());
275   if (timeout <= TimeDuration(0) || duration >= timeout) {
276     if (!ChangeVisibility(false)) {
277       gfxWarning() << "OculusSession::ChangeVisibility(false) failed";
278     }
279   }
280 }
281 
CoverTransitions()282 void OculusSession::CoverTransitions() {
283   // While content is loading or during immersive-mode link
284   // traversal, we need to prevent the user from seeing the
285   // last rendered frame.
286   // We render black frames to cover up the transition.
287   MOZ_ASSERT(mSession);
288   if (mIsPresenting) {
289     // We are currently rendering immersive content.
290     // Avoid interrupting the session
291     return;
292   }
293 
294   if (mInitFlags & ovrInit_Invisible) {
295     // We are invisible, nothing to cover up
296     return;
297   }
298 
299   // Render a black frame
300   ovrLayerEyeFov layer;
301   memset(&layer, 0, sizeof(layer));
302   layer.Header.Type = ovrLayerType_Disabled;
303   ovrLayerHeader* layers = &layer.Header;
304   ovr_SubmitFrame(mSession, 0, nullptr, &layers, 1);
305 }
306 
ChangeVisibility(bool bVisible)307 bool OculusSession::ChangeVisibility(bool bVisible) {
308   ovrInitFlags flags =
309       (ovrInitFlags)(ovrInit_RequestVersion | ovrInit_MixedRendering);
310   if (StaticPrefs::dom_vr_oculus_invisible_enabled() && !bVisible) {
311     flags = (ovrInitFlags)(flags | ovrInit_Invisible);
312   }
313   if (mInitFlags == flags) {
314     // The new state is the same, nothing to do
315     return true;
316   }
317 
318   // Tear everything down
319   StopRendering();
320   StopSession();
321   StopLib();
322 
323   // Start it back up
324   if (!StartLib(flags)) {
325     return false;
326   }
327   if (!StartSession()) {
328     return false;
329   }
330   return true;
331 }
332 
Shutdown()333 void OculusSession::Shutdown() {
334   StopRendering();
335   StopSession();
336   StopLib();
337   UnloadOvrLib();
338   DestroyShaders();
339 }
340 
ProcessEvents(mozilla::gfx::VRSystemState & aSystemState)341 void OculusSession::ProcessEvents(mozilla::gfx::VRSystemState& aSystemState) {
342   if (!mSession) {
343     return;
344   }
345 
346   ovrSessionStatus status;
347   if (OVR_SUCCESS(ovr_GetSessionStatus(mSession, &status))) {
348     aSystemState.displayState.isConnected = status.HmdPresent;
349     aSystemState.displayState.isMounted = status.HmdMounted;
350     mShouldQuit = status.ShouldQuit;
351 
352   } else {
353     aSystemState.displayState.isConnected = false;
354     aSystemState.displayState.isMounted = false;
355   }
356   UpdateHaptics();
357   UpdateVisibility();
358   CoverTransitions();
359 }
360 
StartFrame(mozilla::gfx::VRSystemState & aSystemState)361 void OculusSession::StartFrame(mozilla::gfx::VRSystemState& aSystemState) {
362   UpdateHeadsetPose(aSystemState);
363   UpdateEyeParameters(aSystemState);
364   UpdateControllers(aSystemState);
365   UpdateTelemetry(aSystemState);
366   aSystemState.sensorState.inputFrameID++;
367 }
368 
StartPresentation()369 bool OculusSession::StartPresentation() {
370   /**
371    * XXX - We should resolve fail the promise returned by
372    *       VRDisplay.requestPresent() when the DX11 resources fail allocation
373    *       in VRDisplayOculus::StartPresentation().
374    *       Bailing out here prevents the crash but content should be aware
375    *       that frames are not being presented.
376    *       See Bug 1299309.
377    **/
378   if (!ChangeVisibility(true)) {
379     return false;
380   }
381   if (!StartRendering()) {
382     StopRendering();
383     return false;
384   }
385   mIsPresenting = true;
386   return true;
387 }
388 
StopPresentation()389 void OculusSession::StopPresentation() {
390   mLastPresentationEnd = TimeStamp::Now();
391   mIsPresenting = false;
392 }
393 
SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive & aLayer,ID3D11Texture2D * aTexture)394 bool OculusSession::SubmitFrame(
395     const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer,
396     ID3D11Texture2D* aTexture) {
397   if (!IsPresentationReady()) {
398     return false;
399   }
400 
401   D3D11_TEXTURE2D_DESC textureDesc = {0};
402   aTexture->GetDesc(&textureDesc);
403 
404   int currentRenderTarget = 0;
405   ovrResult orv = ovr_GetTextureSwapChainCurrentIndex(mSession, mTextureSet,
406                                                       &currentRenderTarget);
407   if (orv != ovrSuccess) {
408     NS_WARNING("ovr_GetTextureSwapChainCurrentIndex failed.");
409     return false;
410   }
411 
412   ID3D11RenderTargetView* view = mRTView[currentRenderTarget];
413 
414   float clear[] = {0.0f, 0.0f, 0.0f, 1.0f};
415   mContext->ClearRenderTargetView(view, clear);
416   mContext->OMSetRenderTargets(1, &view, nullptr);
417 
418   Matrix viewMatrix = Matrix::Translation(-1.0, 1.0);
419   viewMatrix.PreScale(2.0f / float(textureDesc.Width),
420                       2.0f / float(textureDesc.Height));
421   viewMatrix.PreScale(1.0f, -1.0f);
422   Matrix4x4 projection = Matrix4x4::From2D(viewMatrix);
423   projection._33 = 0.0f;
424 
425   Matrix transform2d;
426   gfx::Matrix4x4 transform = gfx::Matrix4x4::From2D(transform2d);
427 
428   D3D11_VIEWPORT viewport;
429   viewport.MinDepth = 0.0f;
430   viewport.MaxDepth = 1.0f;
431   viewport.Width = textureDesc.Width;
432   viewport.Height = textureDesc.Height;
433   viewport.TopLeftX = 0;
434   viewport.TopLeftY = 0;
435 
436   D3D11_RECT scissor;
437   scissor.left = 0;
438   scissor.right = textureDesc.Width;
439   scissor.top = 0;
440   scissor.bottom = textureDesc.Height;
441 
442   memcpy(&mVSConstants.layerTransform, &transform._11,
443          sizeof(mVSConstants.layerTransform));
444   memcpy(&mVSConstants.projection, &projection._11,
445          sizeof(mVSConstants.projection));
446   mVSConstants.renderTargetOffset[0] = 0.0f;
447   mVSConstants.renderTargetOffset[1] = 0.0f;
448   mVSConstants.layerQuad =
449       Rect(0.0f, 0.0f, textureDesc.Width, textureDesc.Height);
450   mVSConstants.textureCoords = Rect(0.0f, 1.0f, 1.0f, -1.0f);
451 
452   mPSConstants.layerOpacity[0] = 1.0f;
453 
454   ID3D11Buffer* vbuffer = mVertexBuffer;
455   UINT vsize = sizeof(Vertex);
456   UINT voffset = 0;
457   mContext->IASetVertexBuffers(0, 1, &vbuffer, &vsize, &voffset);
458   mContext->IASetIndexBuffer(nullptr, DXGI_FORMAT_R16_UINT, 0);
459   mContext->IASetInputLayout(mInputLayout);
460   mContext->RSSetViewports(1, &viewport);
461   mContext->RSSetScissorRects(1, &scissor);
462   mContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
463   mContext->VSSetShader(mQuadVS, nullptr, 0);
464   mContext->PSSetShader(mQuadPS, nullptr, 0);
465 
466   RefPtr<ID3D11ShaderResourceView> srView;
467   HRESULT hr = mDevice->CreateShaderResourceView(aTexture, nullptr,
468                                                  getter_AddRefs(srView));
469   if (FAILED(hr)) {
470     gfxWarning() << "Could not create shader resource view for Oculus: "
471                  << hexa(hr);
472     return false;
473   }
474   ID3D11ShaderResourceView* viewPtr = srView.get();
475   mContext->PSSetShaderResources(0 /* 0 == TexSlot::RGB */, 1, &viewPtr);
476   // XXX Use Constant from TexSlot in CompositorD3D11.cpp?
477 
478   ID3D11SamplerState* sampler = mLinearSamplerState;
479   mContext->PSSetSamplers(0, 1, &sampler);
480 
481   if (!UpdateConstantBuffers()) {
482     NS_WARNING("Failed to update constant buffers for Oculus");
483     return false;
484   }
485 
486   mContext->Draw(4, 0);
487 
488   orv = ovr_CommitTextureSwapChain(mSession, mTextureSet);
489   if (orv != ovrSuccess) {
490     NS_WARNING("ovr_CommitTextureSwapChain failed.");
491     return false;
492   }
493 
494   ovrLayerEyeFov layer;
495   memset(&layer, 0, sizeof(layer));
496   layer.Header.Type = ovrLayerType_EyeFov;
497   layer.Header.Flags = 0;
498   layer.ColorTexture[0] = mTextureSet;
499   layer.ColorTexture[1] = nullptr;
500   layer.Fov[0] = mFOVPort[0];
501   layer.Fov[1] = mFOVPort[1];
502   layer.Viewport[0].Pos.x = textureDesc.Width * aLayer.leftEyeRect.x;
503   layer.Viewport[0].Pos.y = textureDesc.Height * aLayer.leftEyeRect.y;
504   layer.Viewport[0].Size.w = textureDesc.Width * aLayer.leftEyeRect.width;
505   layer.Viewport[0].Size.h = textureDesc.Height * aLayer.leftEyeRect.height;
506   layer.Viewport[1].Pos.x = textureDesc.Width * aLayer.rightEyeRect.x;
507   layer.Viewport[1].Pos.y = textureDesc.Height * aLayer.rightEyeRect.y;
508   layer.Viewport[1].Size.w = textureDesc.Width * aLayer.rightEyeRect.width;
509   layer.Viewport[1].Size.h = textureDesc.Height * aLayer.rightEyeRect.height;
510 
511   for (uint32_t i = 0; i < 2; ++i) {
512     layer.RenderPose[i].Orientation.x = mFrameStartPose[i].Orientation.x;
513     layer.RenderPose[i].Orientation.y = mFrameStartPose[i].Orientation.y;
514     layer.RenderPose[i].Orientation.z = mFrameStartPose[i].Orientation.z;
515     layer.RenderPose[i].Orientation.w = mFrameStartPose[i].Orientation.w;
516     layer.RenderPose[i].Position.x = mFrameStartPose[i].Position.x;
517     layer.RenderPose[i].Position.y = mFrameStartPose[i].Position.y;
518     layer.RenderPose[i].Position.z = mFrameStartPose[i].Position.z;
519   }
520 
521   ovrLayerHeader* layers = &layer.Header;
522   orv = ovr_SubmitFrame(mSession, 0, nullptr, &layers, 1);
523   // ovr_SubmitFrame will fail during the Oculus health and safety warning.
524   // and will start succeeding once the warning has been dismissed by the user.
525 
526   if (!OVR_UNQUALIFIED_SUCCESS(orv)) {
527     /**
528      * We wish to throttle the framerate for any case that the rendered
529      * result is not visible.  In some cases, such as during the Oculus
530      * "health and safety warning", orv will be > 0 (OVR_SUCCESS but not
531      * OVR_UNQUALIFIED_SUCCESS) and ovr_SubmitFrame will not block.
532      * In this case, returning true would have resulted in an unthrottled
533      * render loop hiting excessive frame rates and consuming resources.
534      */
535     return false;
536   }
537 
538   return true;
539 }
540 
LoadOvrLib()541 bool OculusSession::LoadOvrLib() {
542   if (mOvrLib) {
543     // Already loaded, early exit
544     return true;
545   }
546 #if defined(_WIN32)
547   nsTArray<nsString> libSearchPaths;
548   nsString libName;
549   nsString searchPath;
550 
551   for (;;) {
552     UINT requiredLength = ::GetSystemDirectoryW(
553         char16ptr_t(searchPath.BeginWriting()), searchPath.Length());
554     if (!requiredLength) {
555       break;
556     }
557     if (requiredLength < searchPath.Length()) {
558       searchPath.Truncate(requiredLength);
559       libSearchPaths.AppendElement(searchPath);
560       break;
561     }
562     searchPath.SetLength(requiredLength);
563   }
564   libName.AppendPrintf("LibOVRRT%d_%d.dll", BUILD_BITS, OVR_PRODUCT_VERSION);
565 
566   // search the path/module dir
567   libSearchPaths.InsertElementsAt(0, 1, EmptyString());
568 
569   // If the env var is present, we override libName
570   if (_wgetenv(L"OVR_LIB_PATH")) {
571     searchPath = _wgetenv(L"OVR_LIB_PATH");
572     libSearchPaths.InsertElementsAt(0, 1, searchPath);
573   }
574 
575   if (_wgetenv(L"OVR_LIB_NAME")) {
576     libName = _wgetenv(L"OVR_LIB_NAME");
577   }
578 
579   if (libName.IsEmpty()) {
580     return false;
581   }
582 
583   for (uint32_t i = 0; i < libSearchPaths.Length(); ++i) {
584     nsString& libPath = libSearchPaths[i];
585     nsString fullName;
586     if (libPath.Length() == 0) {
587       fullName.Assign(libName);
588     } else {
589       fullName.Assign(libPath + NS_LITERAL_STRING(u"\\") + libName);
590     }
591 
592     mOvrLib = LoadLibraryWithFlags(fullName.get());
593     if (mOvrLib) {
594       break;
595     }
596   }
597 #else
598 #  error "Unsupported platform!"
599 #endif
600 
601   if (!mOvrLib) {
602     return false;
603   }
604 
605 #define REQUIRE_FUNCTION(_x)                           \
606   do {                                                 \
607     *(void**)&_x = (void*)PR_FindSymbol(mOvrLib, #_x); \
608     if (!_x) {                                         \
609       printf_stderr(#_x " symbol missing\n");          \
610       goto fail;                                       \
611     }                                                  \
612   } while (0)
613 
614   REQUIRE_FUNCTION(ovr_Initialize);
615   REQUIRE_FUNCTION(ovr_Shutdown);
616   REQUIRE_FUNCTION(ovr_GetLastErrorInfo);
617   REQUIRE_FUNCTION(ovr_GetVersionString);
618   REQUIRE_FUNCTION(ovr_TraceMessage);
619   REQUIRE_FUNCTION(ovr_IdentifyClient);
620   REQUIRE_FUNCTION(ovr_GetHmdDesc);
621   REQUIRE_FUNCTION(ovr_GetTrackerCount);
622   REQUIRE_FUNCTION(ovr_GetTrackerDesc);
623   REQUIRE_FUNCTION(ovr_Create);
624   REQUIRE_FUNCTION(ovr_Destroy);
625   REQUIRE_FUNCTION(ovr_GetSessionStatus);
626   REQUIRE_FUNCTION(ovr_IsExtensionSupported);
627   REQUIRE_FUNCTION(ovr_EnableExtension);
628   REQUIRE_FUNCTION(ovr_SetTrackingOriginType);
629   REQUIRE_FUNCTION(ovr_GetTrackingOriginType);
630   REQUIRE_FUNCTION(ovr_RecenterTrackingOrigin);
631   REQUIRE_FUNCTION(ovr_SpecifyTrackingOrigin);
632   REQUIRE_FUNCTION(ovr_ClearShouldRecenterFlag);
633   REQUIRE_FUNCTION(ovr_GetTrackingState);
634   REQUIRE_FUNCTION(ovr_GetDevicePoses);
635   REQUIRE_FUNCTION(ovr_GetTrackerPose);
636   REQUIRE_FUNCTION(ovr_GetInputState);
637   REQUIRE_FUNCTION(ovr_GetConnectedControllerTypes);
638   REQUIRE_FUNCTION(ovr_GetTouchHapticsDesc);
639   REQUIRE_FUNCTION(ovr_SetControllerVibration);
640   REQUIRE_FUNCTION(ovr_SubmitControllerVibration);
641   REQUIRE_FUNCTION(ovr_GetControllerVibrationState);
642   REQUIRE_FUNCTION(ovr_TestBoundary);
643   REQUIRE_FUNCTION(ovr_TestBoundaryPoint);
644   REQUIRE_FUNCTION(ovr_SetBoundaryLookAndFeel);
645   REQUIRE_FUNCTION(ovr_ResetBoundaryLookAndFeel);
646   REQUIRE_FUNCTION(ovr_GetBoundaryGeometry);
647   REQUIRE_FUNCTION(ovr_GetBoundaryDimensions);
648   REQUIRE_FUNCTION(ovr_GetBoundaryVisible);
649   REQUIRE_FUNCTION(ovr_RequestBoundaryVisible);
650   REQUIRE_FUNCTION(ovr_GetTextureSwapChainLength);
651   REQUIRE_FUNCTION(ovr_GetTextureSwapChainCurrentIndex);
652   REQUIRE_FUNCTION(ovr_GetTextureSwapChainDesc);
653   REQUIRE_FUNCTION(ovr_CommitTextureSwapChain);
654   REQUIRE_FUNCTION(ovr_DestroyTextureSwapChain);
655   REQUIRE_FUNCTION(ovr_DestroyMirrorTexture);
656   REQUIRE_FUNCTION(ovr_GetFovTextureSize);
657   REQUIRE_FUNCTION(ovr_GetRenderDesc2);
658   REQUIRE_FUNCTION(ovr_WaitToBeginFrame);
659   REQUIRE_FUNCTION(ovr_BeginFrame);
660   REQUIRE_FUNCTION(ovr_EndFrame);
661   REQUIRE_FUNCTION(ovr_SubmitFrame);
662   REQUIRE_FUNCTION(ovr_GetPerfStats);
663   REQUIRE_FUNCTION(ovr_ResetPerfStats);
664   REQUIRE_FUNCTION(ovr_GetPredictedDisplayTime);
665   REQUIRE_FUNCTION(ovr_GetTimeInSeconds);
666   REQUIRE_FUNCTION(ovr_GetBool);
667   REQUIRE_FUNCTION(ovr_SetBool);
668   REQUIRE_FUNCTION(ovr_GetInt);
669   REQUIRE_FUNCTION(ovr_SetInt);
670   REQUIRE_FUNCTION(ovr_GetFloat);
671   REQUIRE_FUNCTION(ovr_SetFloat);
672   REQUIRE_FUNCTION(ovr_GetFloatArray);
673   REQUIRE_FUNCTION(ovr_SetFloatArray);
674   REQUIRE_FUNCTION(ovr_GetString);
675   REQUIRE_FUNCTION(ovr_SetString);
676   REQUIRE_FUNCTION(ovr_GetExternalCameras);
677   REQUIRE_FUNCTION(ovr_SetExternalCameraProperties);
678 
679 #ifdef XP_WIN
680 
681   REQUIRE_FUNCTION(ovr_CreateTextureSwapChainDX);
682   REQUIRE_FUNCTION(ovr_GetTextureSwapChainBufferDX);
683   REQUIRE_FUNCTION(ovr_CreateMirrorTextureDX);
684   REQUIRE_FUNCTION(ovr_GetMirrorTextureBufferDX);
685 
686 #endif
687 
688   REQUIRE_FUNCTION(ovr_CreateTextureSwapChainGL);
689   REQUIRE_FUNCTION(ovr_GetTextureSwapChainBufferGL);
690   REQUIRE_FUNCTION(ovr_CreateMirrorTextureGL);
691   REQUIRE_FUNCTION(ovr_GetMirrorTextureBufferGL);
692 
693 #undef REQUIRE_FUNCTION
694 
695   return true;
696 
697 fail:
698   ovr_Initialize = nullptr;
699   PR_UnloadLibrary(mOvrLib);
700   mOvrLib = nullptr;
701   return false;
702 }
703 
UnloadOvrLib()704 void OculusSession::UnloadOvrLib() {
705   if (mOvrLib) {
706     PR_UnloadLibrary(mOvrLib);
707     mOvrLib = nullptr;
708   }
709 }
710 
StartLib(ovrInitFlags aFlags)711 bool OculusSession::StartLib(ovrInitFlags aFlags) {
712   if (mInitFlags == 0) {
713     ovrInitParams params;
714     memset(&params, 0, sizeof(params));
715     params.Flags = aFlags;
716     params.RequestedMinorVersion = OVR_MINOR_VERSION;
717     params.LogCallback = nullptr;
718     params.ConnectionTimeoutMS = 0;
719 
720     ovrResult orv = ovr_Initialize(&params);
721 
722     if (orv == ovrSuccess) {
723       mInitFlags = aFlags;
724     } else {
725       return false;
726     }
727   }
728   MOZ_ASSERT(mInitFlags == aFlags);
729   return true;
730 }
731 
StopLib()732 void OculusSession::StopLib() {
733   if (mInitFlags) {
734     ovr_Shutdown();
735     mInitFlags = (ovrInitFlags)0;
736   }
737 }
738 
StartSession()739 bool OculusSession::StartSession() {
740   // ovr_Create can be slow when no HMD is present and we wish
741   // to keep the same oculus session when possible, so we detect
742   // presence of an HMD with ovr_GetHmdDesc before calling ovr_Create
743   ovrHmdDesc desc = ovr_GetHmdDesc(NULL);
744   if (desc.Type == ovrHmd_None) {
745     // No HMD connected, destroy any existing session
746     if (mSession) {
747       ovr_Destroy(mSession);
748       mSession = nullptr;
749     }
750     return false;
751   }
752   if (mSession != nullptr) {
753     // HMD Detected and we already have a session, let's keep using it.
754     return true;
755   }
756 
757   // HMD Detected and we don't have a session yet,
758   // try to create a new session
759   ovrSession session;
760   ovrGraphicsLuid luid;
761   ovrResult orv = ovr_Create(&session, &luid);
762   if (orv == ovrSuccess) {
763     orv = ovr_SetTrackingOriginType(session, ovrTrackingOrigin_FloorLevel);
764     if (orv != ovrSuccess) {
765       NS_WARNING("ovr_SetTrackingOriginType failed.\n");
766     }
767     mSession = session;
768     return true;
769   }
770 
771   // Failed to create a session for the HMD
772   return false;
773 }
774 
StopSession()775 void OculusSession::StopSession() {
776   if (mSession) {
777     ovr_Destroy(mSession);
778     mSession = nullptr;
779   }
780 }
781 
CreateD3DObjects()782 bool OculusSession::CreateD3DObjects() {
783   RefPtr<ID3D11Device> device = gfx::DeviceManagerDx::Get()->GetVRDevice();
784   if (!device) {
785     return false;
786   }
787   if (!CreateD3DContext(device)) {
788     return false;
789   }
790   return true;
791 }
792 
CreateShaders()793 bool OculusSession::CreateShaders() {
794   if (!mQuadVS) {
795     if (FAILED(mDevice->CreateVertexShader(
796             sLayerQuadVS.mData, sLayerQuadVS.mLength, nullptr, &mQuadVS))) {
797       NS_WARNING("Failed to create vertex shader for Oculus");
798       return false;
799     }
800   }
801 
802   if (!mQuadPS) {
803     if (FAILED(mDevice->CreatePixelShader(sRGBShader.mData, sRGBShader.mLength,
804                                           nullptr, &mQuadPS))) {
805       NS_WARNING("Failed to create pixel shader for Oculus");
806       return false;
807     }
808   }
809 
810   CD3D11_BUFFER_DESC cBufferDesc(sizeof(layers::VertexShaderConstants),
811                                  D3D11_BIND_CONSTANT_BUFFER,
812                                  D3D11_USAGE_DYNAMIC, D3D11_CPU_ACCESS_WRITE);
813 
814   if (!mVSConstantBuffer) {
815     if (FAILED(mDevice->CreateBuffer(&cBufferDesc, nullptr,
816                                      getter_AddRefs(mVSConstantBuffer)))) {
817       NS_WARNING("Failed to vertex shader constant buffer for Oculus");
818       return false;
819     }
820   }
821 
822   if (!mPSConstantBuffer) {
823     cBufferDesc.ByteWidth = sizeof(layers::PixelShaderConstants);
824     if (FAILED(mDevice->CreateBuffer(&cBufferDesc, nullptr,
825                                      getter_AddRefs(mPSConstantBuffer)))) {
826       NS_WARNING("Failed to pixel shader constant buffer for Oculus");
827       return false;
828     }
829   }
830 
831   if (!mLinearSamplerState) {
832     CD3D11_SAMPLER_DESC samplerDesc(D3D11_DEFAULT);
833     if (FAILED(mDevice->CreateSamplerState(
834             &samplerDesc, getter_AddRefs(mLinearSamplerState)))) {
835       NS_WARNING("Failed to create sampler state for Oculus");
836       return false;
837     }
838   }
839 
840   if (!mInputLayout) {
841     D3D11_INPUT_ELEMENT_DESC layout[] = {
842         {"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0,
843          D3D11_INPUT_PER_VERTEX_DATA, 0},
844     };
845 
846     if (FAILED(mDevice->CreateInputLayout(
847             layout, sizeof(layout) / sizeof(D3D11_INPUT_ELEMENT_DESC),
848             sLayerQuadVS.mData, sLayerQuadVS.mLength,
849             getter_AddRefs(mInputLayout)))) {
850       NS_WARNING("Failed to create input layout for Oculus");
851       return false;
852     }
853   }
854 
855   if (!mVertexBuffer) {
856     Vertex vertices[] = {
857         {{0.0, 0.0}}, {{1.0, 0.0}}, {{0.0, 1.0}}, {{1.0, 1.0}}};
858     CD3D11_BUFFER_DESC bufferDesc(sizeof(vertices), D3D11_BIND_VERTEX_BUFFER);
859     D3D11_SUBRESOURCE_DATA data;
860     data.pSysMem = (void*)vertices;
861 
862     if (FAILED(mDevice->CreateBuffer(&bufferDesc, &data,
863                                      getter_AddRefs(mVertexBuffer)))) {
864       NS_WARNING("Failed to create vertex buffer for Oculus");
865       return false;
866     }
867   }
868 
869   memset(&mVSConstants, 0, sizeof(mVSConstants));
870   memset(&mPSConstants, 0, sizeof(mPSConstants));
871   return true;
872 }
873 
DestroyShaders()874 void OculusSession::DestroyShaders() {}
875 
UpdateConstantBuffers()876 bool OculusSession::UpdateConstantBuffers() {
877   HRESULT hr;
878   D3D11_MAPPED_SUBRESOURCE resource;
879   resource.pData = nullptr;
880 
881   hr = mContext->Map(mVSConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0,
882                      &resource);
883   if (FAILED(hr) || !resource.pData) {
884     return false;
885   }
886   *(VertexShaderConstants*)resource.pData = mVSConstants;
887   mContext->Unmap(mVSConstantBuffer, 0);
888   resource.pData = nullptr;
889 
890   hr = mContext->Map(mPSConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0,
891                      &resource);
892   if (FAILED(hr) || !resource.pData) {
893     return false;
894   }
895   *(PixelShaderConstants*)resource.pData = mPSConstants;
896   mContext->Unmap(mPSConstantBuffer, 0);
897 
898   ID3D11Buffer* buffer = mVSConstantBuffer;
899   mContext->VSSetConstantBuffers(0, 1, &buffer);
900   buffer = mPSConstantBuffer;
901   mContext->PSSetConstantBuffers(0, 1, &buffer);
902   return true;
903 }
904 
StartRendering()905 bool OculusSession::StartRendering() {
906   if (!mTextureSet) {
907     /**
908      * The presentation format is determined by content, which describes the
909      * left and right eye rectangles in the VRLayer.  The default, if no
910      * coordinates are passed is to place the left and right eye textures
911      * side-by-side within the buffer.
912      *
913      * XXX - An optimization would be to dynamically resize this buffer
914      *       to accomodate sites that are choosing to render in a lower
915      *       resolution or are using space outside of the left and right
916      *       eye textures for other purposes.  (Bug 1291443)
917      */
918 
919     ovrTextureSwapChainDesc desc;
920     memset(&desc, 0, sizeof(desc));
921     desc.Type = ovrTexture_2D;
922     desc.ArraySize = 1;
923     desc.Format = OVR_FORMAT_B8G8R8A8_UNORM_SRGB;
924     desc.Width = mPresentationSize.width;
925     desc.Height = mPresentationSize.height;
926     desc.MipLevels = 1;
927     desc.SampleCount = 1;
928     desc.StaticImage = false;
929     desc.MiscFlags = ovrTextureMisc_DX_Typeless;
930     desc.BindFlags = ovrTextureBind_DX_RenderTarget;
931 
932     ovrResult orv =
933         ovr_CreateTextureSwapChainDX(mSession, mDevice, &desc, &mTextureSet);
934     if (orv != ovrSuccess) {
935       NS_WARNING("ovr_CreateTextureSwapChainDX failed");
936       return false;
937     }
938 
939     int textureCount = 0;
940     orv = ovr_GetTextureSwapChainLength(mSession, mTextureSet, &textureCount);
941     if (orv != ovrSuccess) {
942       NS_WARNING("ovr_GetTextureSwapChainLength failed");
943       return false;
944     }
945     mTexture.SetLength(textureCount);
946     mRTView.SetLength(textureCount);
947     mSRV.SetLength(textureCount);
948     for (int i = 0; i < textureCount; ++i) {
949       ID3D11Texture2D* texture = nullptr;
950       orv = ovr_GetTextureSwapChainBufferDX(mSession, mTextureSet, i,
951                                             IID_PPV_ARGS(&texture));
952       if (orv != ovrSuccess) {
953         NS_WARNING("Failed to create Oculus texture swap chain.");
954         return false;
955       }
956 
957       RefPtr<ID3D11RenderTargetView> rtView;
958       CD3D11_RENDER_TARGET_VIEW_DESC rtvDesc(D3D11_RTV_DIMENSION_TEXTURE2D,
959                                              DXGI_FORMAT_B8G8R8A8_UNORM);
960       HRESULT hr = mDevice->CreateRenderTargetView(texture, &rtvDesc,
961                                                    getter_AddRefs(rtView));
962       if (FAILED(hr)) {
963         NS_WARNING(
964             "Failed to create RenderTargetView for Oculus texture swap chain.");
965         texture->Release();
966         return false;
967       }
968 
969       RefPtr<ID3D11ShaderResourceView> srv;
970       CD3D11_SHADER_RESOURCE_VIEW_DESC srvDesc(D3D11_SRV_DIMENSION_TEXTURE2D,
971                                                DXGI_FORMAT_B8G8R8A8_UNORM);
972       hr = mDevice->CreateShaderResourceView(texture, &srvDesc,
973                                              getter_AddRefs(srv));
974       if (FAILED(hr)) {
975         NS_WARNING(
976             "Failed to create ShaderResourceView for Oculus texture swap "
977             "chain.");
978         texture->Release();
979         return false;
980       }
981 
982       mTexture[i] = texture;
983       mRTView[i] = rtView;
984       mSRV[i] = srv;
985       texture->Release();
986     }
987   }
988   return true;
989 }
990 
IsPresentationReady() const991 bool OculusSession::IsPresentationReady() const {
992   return mTextureSet != nullptr;
993 }
994 
StopRendering()995 void OculusSession::StopRendering() {
996   mSRV.Clear();
997   mRTView.Clear();
998   mTexture.Clear();
999 
1000   if (mTextureSet && mSession) {
1001     ovr_DestroyTextureSwapChain(mSession, mTextureSet);
1002   }
1003   mTextureSet = nullptr;
1004   mIsPresenting = false;
1005 }
1006 
InitState(VRSystemState & aSystemState)1007 bool OculusSession::InitState(VRSystemState& aSystemState) {
1008   VRDisplayState& state = aSystemState.displayState;
1009   strncpy(state.displayName, "Oculus VR HMD", kVRDisplayNameMaxLen);
1010   state.isConnected = true;
1011   state.isMounted = false;
1012 
1013   ovrHmdDesc desc = ovr_GetHmdDesc(mSession);
1014 
1015   state.capabilityFlags = VRDisplayCapabilityFlags::Cap_None;
1016   if (desc.AvailableTrackingCaps & ovrTrackingCap_Orientation) {
1017     state.capabilityFlags |= VRDisplayCapabilityFlags::Cap_Orientation;
1018     state.capabilityFlags |= VRDisplayCapabilityFlags::Cap_AngularAcceleration;
1019   }
1020   if (desc.AvailableTrackingCaps & ovrTrackingCap_Position) {
1021     state.capabilityFlags |= VRDisplayCapabilityFlags::Cap_Position;
1022     state.capabilityFlags |= VRDisplayCapabilityFlags::Cap_LinearAcceleration;
1023     state.capabilityFlags |= VRDisplayCapabilityFlags::Cap_StageParameters;
1024   }
1025   state.capabilityFlags |= VRDisplayCapabilityFlags::Cap_External;
1026   state.capabilityFlags |= VRDisplayCapabilityFlags::Cap_MountDetection;
1027   state.capabilityFlags |= VRDisplayCapabilityFlags::Cap_Present;
1028   state.capabilityFlags |= VRDisplayCapabilityFlags::Cap_ImmersiveVR;
1029   state.blendMode = VRDisplayBlendMode::Opaque;
1030   state.reportsDroppedFrames = true;
1031 
1032   mFOVPort[VRDisplayState::Eye_Left] = desc.DefaultEyeFov[ovrEye_Left];
1033   mFOVPort[VRDisplayState::Eye_Right] = desc.DefaultEyeFov[ovrEye_Right];
1034 
1035   state.eyeFOV[VRDisplayState::Eye_Left] =
1036       FromFovPort(mFOVPort[VRDisplayState::Eye_Left]);
1037   state.eyeFOV[VRDisplayState::Eye_Right] =
1038       FromFovPort(mFOVPort[VRDisplayState::Eye_Right]);
1039 
1040   float pixelsPerDisplayPixel = 1.0;
1041   ovrSizei texSize[2];
1042 
1043   // get eye texture sizes
1044   for (uint32_t eye = 0; eye < VRDisplayState::NumEyes; eye++) {
1045     texSize[eye] = ovr_GetFovTextureSize(mSession, (ovrEyeType)eye,
1046                                          mFOVPort[eye], pixelsPerDisplayPixel);
1047   }
1048 
1049   // take the max of both for eye resolution
1050   state.eyeResolution.width = std::max(texSize[VRDisplayState::Eye_Left].w,
1051                                        texSize[VRDisplayState::Eye_Right].w);
1052   state.eyeResolution.height = std::max(texSize[VRDisplayState::Eye_Left].h,
1053                                         texSize[VRDisplayState::Eye_Right].h);
1054 
1055   // default to an identity quaternion
1056   aSystemState.sensorState.pose.orientation[3] = 1.0f;
1057 
1058   UpdateStageParameters(state);
1059   UpdateEyeParameters(aSystemState);
1060 
1061   VRHMDSensorState& sensorState = aSystemState.sensorState;
1062   sensorState.flags = (VRDisplayCapabilityFlags)(
1063       (int)VRDisplayCapabilityFlags::Cap_Orientation |
1064       (int)VRDisplayCapabilityFlags::Cap_Position);
1065   sensorState.pose.orientation[3] = 1.0f;  // Default to an identity quaternion
1066 
1067   return true;
1068 }
1069 
UpdateStageParameters(VRDisplayState & aState)1070 void OculusSession::UpdateStageParameters(VRDisplayState& aState) {
1071   ovrVector3f playArea;
1072   ovrResult res =
1073       ovr_GetBoundaryDimensions(mSession, ovrBoundary_PlayArea, &playArea);
1074   if (res == ovrSuccess) {
1075     aState.stageSize.width = playArea.x;
1076     aState.stageSize.height = playArea.z;
1077   } else {
1078     // If we fail, fall back to reasonable defaults.
1079     // 1m x 1m space
1080     aState.stageSize.width = 1.0f;
1081     aState.stageSize.height = 1.0f;
1082   }
1083 
1084   float eyeHeight =
1085       ovr_GetFloat(mSession, OVR_KEY_EYE_HEIGHT, OVR_DEFAULT_EYE_HEIGHT);
1086 
1087   aState.sittingToStandingTransform[0] = 1.0f;
1088   aState.sittingToStandingTransform[1] = 0.0f;
1089   aState.sittingToStandingTransform[2] = 0.0f;
1090   aState.sittingToStandingTransform[3] = 0.0f;
1091 
1092   aState.sittingToStandingTransform[4] = 0.0f;
1093   aState.sittingToStandingTransform[5] = 1.0f;
1094   aState.sittingToStandingTransform[6] = 0.0f;
1095   aState.sittingToStandingTransform[7] = 0.0f;
1096 
1097   aState.sittingToStandingTransform[8] = 0.0f;
1098   aState.sittingToStandingTransform[9] = 0.0f;
1099   aState.sittingToStandingTransform[10] = 1.0f;
1100   aState.sittingToStandingTransform[11] = 0.0f;
1101 
1102   aState.sittingToStandingTransform[12] = 0.0f;
1103   aState.sittingToStandingTransform[13] = eyeHeight;
1104   aState.sittingToStandingTransform[14] = 0.0f;
1105   aState.sittingToStandingTransform[15] = 1.0f;
1106 }
1107 
UpdateEyeParameters(VRSystemState & aState)1108 void OculusSession::UpdateEyeParameters(VRSystemState& aState) {
1109   if (!mSession) {
1110     return;
1111   }
1112   // This must be called every frame in order to
1113   // account for continuous adjustments to ipd.
1114   gfx::Matrix4x4 headToEyeTransforms[2];
1115   for (uint32_t eye = 0; eye < VRDisplayState::NumEyes; eye++) {
1116     // As of Oculus 1.17 SDK, we must use the ovr_GetRenderDesc2 function to
1117     // return the updated version of ovrEyeRenderDesc.  This is normally done by
1118     // the Oculus static lib shim, but we need to do this explicitly as we are
1119     // loading the Oculus runtime dll directly.
1120     ovrEyeRenderDesc renderDesc =
1121         ovr_GetRenderDesc2(mSession, (ovrEyeType)eye, mFOVPort[eye]);
1122     aState.displayState.eyeTranslation[eye].x =
1123         renderDesc.HmdToEyePose.Position.x;
1124     aState.displayState.eyeTranslation[eye].y =
1125         renderDesc.HmdToEyePose.Position.y;
1126     aState.displayState.eyeTranslation[eye].z =
1127         renderDesc.HmdToEyePose.Position.z;
1128 
1129     Matrix4x4 pose;
1130     pose.SetRotationFromQuaternion(
1131         gfx::Quaternion(renderDesc.HmdToEyePose.Orientation.x,
1132                         renderDesc.HmdToEyePose.Orientation.y,
1133                         renderDesc.HmdToEyePose.Orientation.z,
1134                         renderDesc.HmdToEyePose.Orientation.w));
1135     pose.PreTranslate(renderDesc.HmdToEyePose.Position.x,
1136                       renderDesc.HmdToEyePose.Position.y,
1137                       renderDesc.HmdToEyePose.Position.z);
1138     pose.Invert();
1139     headToEyeTransforms[eye] = pose;
1140   }
1141   aState.sensorState.CalcViewMatrices(headToEyeTransforms);
1142 
1143   Matrix4x4 matView[2];
1144   memcpy(matView[0].components, aState.sensorState.leftViewMatrix,
1145          sizeof(float) * 16);
1146   memcpy(matView[1].components, aState.sensorState.rightViewMatrix,
1147          sizeof(float) * 16);
1148 
1149   for (uint32_t eye = 0; eye < VRDisplayState::NumEyes; eye++) {
1150     Point3D eyeTranslation;
1151     Quaternion eyeRotation;
1152     Point3D eyeScale;
1153     if (!matView[eye].Decompose(eyeTranslation, eyeRotation, eyeScale)) {
1154       NS_WARNING("Failed to decompose eye pose matrix for Oculus");
1155     }
1156 
1157     mFrameStartPose[eye].Orientation.x = eyeRotation.x;
1158     mFrameStartPose[eye].Orientation.y = eyeRotation.y;
1159     mFrameStartPose[eye].Orientation.z = eyeRotation.z;
1160     mFrameStartPose[eye].Orientation.w = eyeRotation.w;
1161     mFrameStartPose[eye].Position.x = eyeTranslation.x;
1162     mFrameStartPose[eye].Position.y = eyeTranslation.y;
1163     mFrameStartPose[eye].Position.z = eyeTranslation.z;
1164   }
1165 }
1166 
UpdateHeadsetPose(VRSystemState & aState)1167 void OculusSession::UpdateHeadsetPose(VRSystemState& aState) {
1168   if (!mSession) {
1169     return;
1170   }
1171   double predictedFrameTime = 0.0f;
1172   if (StaticPrefs::dom_vr_poseprediction_enabled()) {
1173     // XXX We might need to call ovr_GetPredictedDisplayTime even if we don't
1174     // use the result. If we don't call it, the Oculus driver will spew out many
1175     // warnings...
1176     predictedFrameTime = ovr_GetPredictedDisplayTime(mSession, 0);
1177   }
1178   ovrTrackingState trackingState =
1179       ovr_GetTrackingState(mSession, predictedFrameTime, true);
1180   ovrPoseStatef& pose(trackingState.HeadPose);
1181 
1182   aState.sensorState.timestamp = pose.TimeInSeconds;
1183 
1184   if (trackingState.StatusFlags & ovrStatus_OrientationTracked) {
1185     aState.sensorState.flags |= VRDisplayCapabilityFlags::Cap_Orientation;
1186 
1187     aState.sensorState.pose.orientation[0] = pose.ThePose.Orientation.x;
1188     aState.sensorState.pose.orientation[1] = pose.ThePose.Orientation.y;
1189     aState.sensorState.pose.orientation[2] = pose.ThePose.Orientation.z;
1190     aState.sensorState.pose.orientation[3] = pose.ThePose.Orientation.w;
1191 
1192     aState.sensorState.pose.angularVelocity[0] = pose.AngularVelocity.x;
1193     aState.sensorState.pose.angularVelocity[1] = pose.AngularVelocity.y;
1194     aState.sensorState.pose.angularVelocity[2] = pose.AngularVelocity.z;
1195 
1196     aState.sensorState.flags |=
1197         VRDisplayCapabilityFlags::Cap_AngularAcceleration;
1198 
1199     aState.sensorState.pose.angularAcceleration[0] = pose.AngularAcceleration.x;
1200     aState.sensorState.pose.angularAcceleration[1] = pose.AngularAcceleration.y;
1201     aState.sensorState.pose.angularAcceleration[2] = pose.AngularAcceleration.z;
1202   } else {
1203     // default to an identity quaternion
1204     aState.sensorState.pose.orientation[3] = 1.0f;
1205   }
1206 
1207   if (trackingState.StatusFlags & ovrStatus_PositionTracked) {
1208     float eyeHeight =
1209         ovr_GetFloat(mSession, OVR_KEY_EYE_HEIGHT, OVR_DEFAULT_EYE_HEIGHT);
1210     aState.sensorState.flags |= VRDisplayCapabilityFlags::Cap_Position;
1211 
1212     aState.sensorState.pose.position[0] = pose.ThePose.Position.x;
1213     aState.sensorState.pose.position[1] = pose.ThePose.Position.y - eyeHeight;
1214     aState.sensorState.pose.position[2] = pose.ThePose.Position.z;
1215 
1216     aState.sensorState.pose.linearVelocity[0] = pose.LinearVelocity.x;
1217     aState.sensorState.pose.linearVelocity[1] = pose.LinearVelocity.y;
1218     aState.sensorState.pose.linearVelocity[2] = pose.LinearVelocity.z;
1219 
1220     aState.sensorState.flags |=
1221         VRDisplayCapabilityFlags::Cap_LinearAcceleration;
1222 
1223     aState.sensorState.pose.linearAcceleration[0] = pose.LinearAcceleration.x;
1224     aState.sensorState.pose.linearAcceleration[1] = pose.LinearAcceleration.y;
1225     aState.sensorState.pose.linearAcceleration[2] = pose.LinearAcceleration.z;
1226   }
1227   aState.sensorState.flags |= VRDisplayCapabilityFlags::Cap_External;
1228   aState.sensorState.flags |= VRDisplayCapabilityFlags::Cap_MountDetection;
1229   aState.sensorState.flags |= VRDisplayCapabilityFlags::Cap_Present;
1230 }
1231 
UpdateControllers(VRSystemState & aState)1232 void OculusSession::UpdateControllers(VRSystemState& aState) {
1233   if (!mSession) {
1234     return;
1235   }
1236 
1237   ovrInputState inputState;
1238   bool hasInputState = ovr_GetInputState(mSession, ovrControllerType_Touch,
1239                                          &inputState) == ovrSuccess;
1240 
1241   if (!hasInputState) {
1242     return;
1243   }
1244 
1245   EnumerateControllers(aState, inputState);
1246   UpdateControllerInputs(aState, inputState);
1247   UpdateControllerPose(aState, inputState);
1248 }
1249 
UpdateControllerPose(VRSystemState & aState,const ovrInputState & aInputState)1250 void OculusSession::UpdateControllerPose(VRSystemState& aState,
1251                                          const ovrInputState& aInputState) {
1252   ovrTrackingState trackingState = ovr_GetTrackingState(mSession, 0.0, false);
1253   for (uint32_t handIdx = 0; handIdx < 2; handIdx++) {
1254     // Left Touch Controller will always be at index 0 and
1255     // and Right Touch Controller will always be at index 1
1256     VRControllerState& controllerState = aState.controllerState[handIdx];
1257     if (aInputState.ControllerType & OculusControllerTypes[handIdx]) {
1258       ovrPoseStatef& pose = trackingState.HandPoses[handIdx];
1259       bool bNewController = !(controllerState.flags &
1260                               dom::GamepadCapabilityFlags::Cap_Orientation);
1261       if (bNewController) {
1262         controllerState.flags |= dom::GamepadCapabilityFlags::Cap_Orientation;
1263         controllerState.flags |= dom::GamepadCapabilityFlags::Cap_Position;
1264         controllerState.flags |=
1265             dom::GamepadCapabilityFlags::Cap_AngularAcceleration;
1266         controllerState.flags |=
1267             dom::GamepadCapabilityFlags::Cap_LinearAcceleration;
1268         controllerState.flags |=
1269             dom::GamepadCapabilityFlags::Cap_GripSpacePosition;
1270       }
1271 
1272       if (bNewController || trackingState.HandStatusFlags[handIdx] &
1273                                 ovrStatus_OrientationTracked) {
1274         controllerState.pose.orientation[0] = pose.ThePose.Orientation.x;
1275         controllerState.pose.orientation[1] = pose.ThePose.Orientation.y;
1276         controllerState.pose.orientation[2] = pose.ThePose.Orientation.z;
1277         controllerState.pose.orientation[3] = pose.ThePose.Orientation.w;
1278         controllerState.pose.angularVelocity[0] = pose.AngularVelocity.x;
1279         controllerState.pose.angularVelocity[1] = pose.AngularVelocity.y;
1280         controllerState.pose.angularVelocity[2] = pose.AngularVelocity.z;
1281         controllerState.pose.angularAcceleration[0] =
1282             pose.AngularAcceleration.x;
1283         controllerState.pose.angularAcceleration[1] =
1284             pose.AngularAcceleration.y;
1285         controllerState.pose.angularAcceleration[2] =
1286             pose.AngularAcceleration.z;
1287         controllerState.isOrientationValid = true;
1288       } else {
1289         controllerState.isOrientationValid = false;
1290       }
1291       if (bNewController ||
1292           trackingState.HandStatusFlags[handIdx] & ovrStatus_PositionTracked) {
1293         controllerState.pose.position[0] = pose.ThePose.Position.x;
1294         controllerState.pose.position[1] = pose.ThePose.Position.y;
1295         controllerState.pose.position[2] = pose.ThePose.Position.z;
1296         controllerState.pose.linearVelocity[0] = pose.LinearVelocity.x;
1297         controllerState.pose.linearVelocity[1] = pose.LinearVelocity.y;
1298         controllerState.pose.linearVelocity[2] = pose.LinearVelocity.z;
1299         controllerState.pose.linearAcceleration[0] = pose.LinearAcceleration.x;
1300         controllerState.pose.linearAcceleration[1] = pose.LinearAcceleration.y;
1301         controllerState.pose.linearAcceleration[2] = pose.LinearAcceleration.z;
1302 
1303         float eyeHeight =
1304             ovr_GetFloat(mSession, OVR_KEY_EYE_HEIGHT, OVR_DEFAULT_EYE_HEIGHT);
1305         controllerState.pose.position[1] -= eyeHeight;
1306         controllerState.isPositionValid = true;
1307       } else {
1308         controllerState.isPositionValid = false;
1309       }
1310       controllerState.targetRayPose = controllerState.pose;
1311     }
1312   }
1313 }
1314 
EnumerateControllers(VRSystemState & aState,const ovrInputState & aInputState)1315 void OculusSession::EnumerateControllers(VRSystemState& aState,
1316                                          const ovrInputState& aInputState) {
1317   for (uint32_t handIdx = 0; handIdx < 2; handIdx++) {
1318     // Left Touch Controller will always be at index 0 and
1319     // and Right Touch Controller will always be at index 1
1320     VRControllerState& controllerState = aState.controllerState[handIdx];
1321     if (aInputState.ControllerType & OculusControllerTypes[handIdx]) {
1322       bool bNewController = false;
1323       // Touch Controller detected
1324       if (controllerState.controllerName[0] == '\0') {
1325         // Controller has been just enumerated
1326         strncpy(controllerState.controllerName, OculusControllerNames[handIdx],
1327                 kVRControllerNameMaxLen);
1328         controllerState.hand = OculusControllerHand[handIdx];
1329         controllerState.targetRayMode = gfx::TargetRayMode::TrackedPointer;
1330         controllerState.numButtons = kNumOculusButtons;
1331         controllerState.numAxes = kNumOculusAxes;
1332         controllerState.numHaptics = kNumOculusHaptcs;
1333         controllerState.type = VRControllerType::OculusTouch;
1334         bNewController = true;
1335       }
1336     } else {
1337       // Touch Controller not detected
1338       if (controllerState.controllerName[0] != '\0') {
1339         // Clear any newly disconnected ontrollers
1340         memset(&controllerState, 0, sizeof(VRControllerState));
1341       }
1342     }
1343   }
1344 }
1345 
UpdateControllerInputs(VRSystemState & aState,const ovrInputState & aInputState)1346 void OculusSession::UpdateControllerInputs(VRSystemState& aState,
1347                                            const ovrInputState& aInputState) {
1348   const float triggerThreshold =
1349       StaticPrefs::dom_vr_controller_trigger_threshold();
1350 
1351   for (uint32_t handIdx = 0; handIdx < 2; handIdx++) {
1352     // Left Touch Controller will always be at index 0 and
1353     // and Right Touch Controller will always be at index 1
1354     VRControllerState& controllerState = aState.controllerState[handIdx];
1355     if (aInputState.ControllerType & OculusControllerTypes[handIdx]) {
1356       // Update Button States
1357       controllerState.buttonPressed = 0;
1358       controllerState.buttonTouched = 0;
1359       uint32_t buttonIdx = 0;
1360 
1361       // Button 0: Trigger
1362       VRSession::UpdateTrigger(controllerState, buttonIdx,
1363                                aInputState.IndexTrigger[handIdx],
1364                                triggerThreshold);
1365       ++buttonIdx;
1366       // Button 1: Grip
1367       VRSession::UpdateTrigger(controllerState, buttonIdx,
1368                                aInputState.HandTrigger[handIdx],
1369                                triggerThreshold);
1370       ++buttonIdx;
1371       // Button 2: a placeholder button for trackpad.
1372       UpdateButton(aInputState, handIdx, buttonIdx, controllerState);
1373       ++buttonIdx;
1374       // Button 3: Thumbstick
1375       UpdateButton(aInputState, handIdx, buttonIdx, controllerState);
1376       ++buttonIdx;
1377       // Button 4: A
1378       UpdateButton(aInputState, handIdx, buttonIdx, controllerState);
1379       ++buttonIdx;
1380       // Button 5: B
1381       UpdateButton(aInputState, handIdx, buttonIdx, controllerState);
1382       ++buttonIdx;
1383       // Button 6: ThumbRest
1384       UpdateButton(aInputState, handIdx, buttonIdx, controllerState);
1385       ++buttonIdx;
1386 
1387       MOZ_ASSERT(buttonIdx == kNumOculusButtons);
1388 
1389       // Update Thumbstick axis
1390       uint32_t axisIdx = 0;
1391       // Axis 0, 1: placeholder axes for trackpad.
1392       axisIdx += 2;
1393 
1394       // Axis 2, 3: placeholder axes for thumbstick.
1395       float axisValue = aInputState.Thumbstick[handIdx].x;
1396       if (abs(axisValue) < 0.0000009f) {
1397         axisValue = 0.0f;  // Clear noise signal
1398       }
1399       controllerState.axisValue[axisIdx] = axisValue;
1400       axisIdx++;
1401 
1402       // Note that y axis is intentionally inverted!
1403       axisValue = -aInputState.Thumbstick[handIdx].y;
1404       if (abs(axisValue) < 0.0000009f) {
1405         axisValue = 0.0f;  // Clear noise signal
1406       }
1407       controllerState.axisValue[axisIdx] = axisValue;
1408       axisIdx++;
1409 
1410       MOZ_ASSERT(axisIdx == kNumOculusAxes);
1411     }
1412     SetControllerSelectionAndSqueezeFrameId(
1413         controllerState, aState.displayState.lastSubmittedFrameId);
1414   }
1415 }
1416 
UpdateTelemetry(VRSystemState & aSystemState)1417 void OculusSession::UpdateTelemetry(VRSystemState& aSystemState) {
1418   if (!mSession) {
1419     return;
1420   }
1421   ovrPerfStats perfStats;
1422   if (ovr_GetPerfStats(mSession, &perfStats) == ovrSuccess) {
1423     if (perfStats.FrameStatsCount) {
1424       aSystemState.displayState.droppedFrameCount =
1425           perfStats.FrameStats[0].AppDroppedFrameCount;
1426     }
1427   }
1428 }
1429 
VibrateHaptic(uint32_t aControllerIdx,uint32_t aHapticIndex,float aIntensity,float aDuration)1430 void OculusSession::VibrateHaptic(uint32_t aControllerIdx,
1431                                   uint32_t aHapticIndex, float aIntensity,
1432                                   float aDuration) {
1433   if (!mSession) {
1434     return;
1435   }
1436 
1437   if (aDuration <= 0.0f) {
1438     StopVibrateHaptic(aControllerIdx);
1439     return;
1440   }
1441 
1442   // Vibration amplitude in the [0.0, 1.0] range
1443   MOZ_ASSERT(aControllerIdx >= 0 && aControllerIdx <= 1);
1444   mHapticPulseIntensity[aControllerIdx] = aIntensity > 1.0 ? 1.0 : aIntensity;
1445   mRemainingVibrateTime[aControllerIdx] = aDuration;
1446   ovrControllerType hand = OculusControllerTypes[aControllerIdx];
1447 
1448   // The gamepad extensions API does not yet have independent control
1449   // of frequency and amplitude.  We are always sending 0.0f (160hz)
1450   // to the frequency argument.
1451   ovrResult result = ovr_SetControllerVibration(
1452       mSession, hand, 0.0f, mHapticPulseIntensity[aControllerIdx]);
1453   if (result != ovrSuccess) {
1454     // This may happen if called when not presenting.
1455     gfxWarning() << "ovr_SetControllerVibration failed.";
1456   }
1457 }
1458 
StopVibrateHaptic(uint32_t aControllerIdx)1459 void OculusSession::StopVibrateHaptic(uint32_t aControllerIdx) {
1460   if (!mSession) {
1461     return;
1462   }
1463   MOZ_ASSERT(aControllerIdx >= 0 && aControllerIdx <= 1);
1464   ovrControllerType hand = OculusControllerTypes[aControllerIdx];
1465   mRemainingVibrateTime[aControllerIdx] = 0.0f;
1466   mHapticPulseIntensity[aControllerIdx] = 0.0f;
1467 
1468   ovrResult result = ovr_SetControllerVibration(mSession, hand, 0.0f, 0.0f);
1469   if (result != ovrSuccess) {
1470     // This may happen if called when not presenting.
1471     gfxWarning() << "ovr_SetControllerVibration failed.";
1472   }
1473 }
1474 
StopAllHaptics()1475 void OculusSession::StopAllHaptics() {
1476   // Left Oculus Touch
1477   StopVibrateHaptic(0);
1478   // Right Oculus Touch
1479   StopVibrateHaptic(1);
1480 }
1481 
UpdateHaptics()1482 void OculusSession::UpdateHaptics() {
1483   if (!mSession) {
1484     return;
1485   }
1486   // The Oculus API and hardware takes at least 33ms to respond
1487   // to haptic state changes, so it is not beneficial to create
1488   // a dedicated haptic feedback thread and update multiple
1489   // times per frame.
1490   // If we wish to support more accurate effects with sub-frame timing,
1491   // we should use the buffered haptic feedback API's.
1492 
1493   TimeStamp now = TimeStamp::Now();
1494   if (mLastHapticUpdate.IsNull()) {
1495     mLastHapticUpdate = now;
1496     return;
1497   }
1498   float deltaTime = (float)(now - mLastHapticUpdate).ToSeconds();
1499   mLastHapticUpdate = now;
1500   for (int i = 0; i < 2; i++) {
1501     if (mRemainingVibrateTime[i] <= 0.0f) {
1502       continue;
1503     }
1504     mRemainingVibrateTime[i] -= deltaTime;
1505     ovrControllerType hand = OculusControllerTypes[i];
1506     if (mRemainingVibrateTime[i] > 0.0f) {
1507       ovrResult result = ovr_SetControllerVibration(mSession, hand, 0.0f,
1508                                                     mHapticPulseIntensity[i]);
1509       if (result != ovrSuccess) {
1510         // This may happen if called when not presenting.
1511         gfxWarning() << "ovr_SetControllerVibration failed.";
1512       }
1513     } else {
1514       StopVibrateHaptic(i);
1515     }
1516   }
1517 }
1518 
1519 }  // namespace gfx
1520 }  // namespace mozilla
1521