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 ¤tRenderTarget);
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(¶ms, 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(¶ms);
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