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 #include "VRSession.h"
8 
9 #include "moz_external_vr.h"
10 
11 #if defined(XP_WIN)
12 #  include <d3d11.h>
13 #endif  // defined(XP_WIN)
14 
15 #ifndef MOZILLA_INTERNAL_API
16 #  define NS_WARNING(s)
17 #endif
18 
19 using namespace mozilla::gfx;
20 
VRSession()21 VRSession::VRSession()
22     : mShouldQuit(false)
23 #ifdef XP_WIN
24       ,
25       mDevice(nullptr),
26       mContext(nullptr),
27       mDeviceContextState(nullptr)
28 #endif
29 {
30 }
31 
32 #ifdef XP_WIN
~VRSession()33 VRSession::~VRSession() {
34   if (mDevice != nullptr) {
35     mDevice->Release();
36     mDevice = nullptr;
37   }
38 
39   if (mContext != nullptr) {
40     mContext->Release();
41     mContext = nullptr;
42   }
43 
44   if (mDeviceContextState != nullptr) {
45     mDeviceContextState->Release();
46     mDeviceContextState = nullptr;
47   }
48 }
49 #endif
50 
51 #if defined(XP_WIN)
CreateD3DContext(ID3D11Device * aDevice)52 bool VRSession::CreateD3DContext(ID3D11Device* aDevice) {
53   if (!mDevice) {
54     if (!aDevice) {
55       NS_WARNING("VRSession::CreateD3DObjects failed to get a D3D11Device");
56       return false;
57     }
58     if (FAILED(aDevice->QueryInterface(IID_PPV_ARGS(&mDevice)))) {
59       NS_WARNING("VRSession::CreateD3DObjects failed to get a D3D11Device1");
60       return false;
61     }
62   }
63   if (!mContext) {
64     mDevice->GetImmediateContext1(&mContext);
65     if (!mContext) {
66       NS_WARNING(
67           "VRSession::CreateD3DObjects failed to get an immediate context");
68       return false;
69     }
70   }
71   if (!mDeviceContextState) {
72     D3D_FEATURE_LEVEL featureLevels[]{D3D_FEATURE_LEVEL_11_1,
73                                       D3D_FEATURE_LEVEL_11_0};
74     mDevice->CreateDeviceContextState(0, featureLevels, 2, D3D11_SDK_VERSION,
75                                       __uuidof(ID3D11Device1), nullptr,
76                                       &mDeviceContextState);
77   }
78   if (!mDeviceContextState) {
79     NS_WARNING(
80         "VRSession::CreateD3DObjects failed to get a D3D11DeviceContextState");
81     return false;
82   }
83   return true;
84 }
85 
GetD3DDevice()86 ID3D11Device1* VRSession::GetD3DDevice() { return mDevice; }
87 
GetD3DDeviceContext()88 ID3D11DeviceContext1* VRSession::GetD3DDeviceContext() { return mContext; }
89 
GetD3DDeviceContextState()90 ID3DDeviceContextState* VRSession::GetD3DDeviceContextState() {
91   return mDeviceContextState;
92 }
93 
94 #endif  // defined(XP_WIN)
95 
SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive & aLayer)96 bool VRSession::SubmitFrame(
97     const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer) {
98 #if defined(XP_WIN)
99   bool success = false;
100   if (aLayer.textureType ==
101       VRLayerTextureType::LayerTextureType_D3D10SurfaceDescriptor) {
102     ID3D11Texture2D* dxTexture = nullptr;
103     HRESULT hr = mDevice->OpenSharedResource((HANDLE)aLayer.textureHandle,
104                                              IID_PPV_ARGS(&dxTexture));
105     if (SUCCEEDED(hr) && dxTexture != nullptr) {
106       // Similar to LockD3DTexture in TextureD3D11.cpp
107       IDXGIKeyedMutex* mutex = nullptr;
108       hr = dxTexture->QueryInterface(IID_PPV_ARGS(&mutex));
109       if (SUCCEEDED(hr)) {
110         hr = mutex->AcquireSync(0, 1000);
111 #  ifdef MOZILLA_INTERNAL_API
112         if (hr == WAIT_TIMEOUT) {
113           gfxDevCrash(LogReason::D3DLockTimeout) << "D3D lock mutex timeout";
114         } else if (hr == WAIT_ABANDONED) {
115           gfxCriticalNote << "GFX: D3D11 lock mutex abandoned";
116         }
117 #  endif
118         if (SUCCEEDED(hr)) {
119           success = SubmitFrame(aLayer, dxTexture);
120           hr = mutex->ReleaseSync(0);
121           if (FAILED(hr)) {
122             NS_WARNING("Failed to unlock the texture");
123           }
124         } else {
125           NS_WARNING("Failed to lock the texture");
126         }
127 
128         mutex->Release();
129         mutex = nullptr;
130       }
131 
132       dxTexture->Release();
133       dxTexture = nullptr;
134     } else {
135       NS_WARNING("Failed to open shared texture");
136     }
137 
138     return SUCCEEDED(hr) && success;
139   }
140 
141 #elif defined(XP_MACOSX)
142 
143   if (aLayer.textureType == VRLayerTextureType::LayerTextureType_MacIOSurface) {
144     return SubmitFrame(aLayer, aLayer.textureHandle);
145   }
146 
147 #endif
148 
149   return false;
150 }
151 
UpdateTrigger(VRControllerState & aState,uint32_t aButtonIndex,float aValue,float aThreshold)152 void VRSession::UpdateTrigger(VRControllerState& aState, uint32_t aButtonIndex,
153                               float aValue, float aThreshold) {
154   // For OpenVR, the threshold value of ButtonPressed and ButtonTouched is 0.55.
155   // We prefer to let developers to set their own threshold for the adjustment.
156   // Therefore, we don't check ButtonPressed and ButtonTouched with ButtonMask
157   // here. we just check the button value is larger than the threshold value or
158   // not.
159   uint64_t mask = (1ULL << aButtonIndex);
160   aState.triggerValue[aButtonIndex] = aValue;
161   if (aValue > aThreshold) {
162     aState.buttonPressed |= mask;
163     aState.buttonTouched |= mask;
164   } else {
165     aState.buttonPressed &= ~mask;
166     aState.buttonTouched &= ~mask;
167   }
168 }
169 
SetControllerSelectionAndSqueezeFrameId(VRControllerState & controllerState,uint64_t aFrameId)170 void VRSession::SetControllerSelectionAndSqueezeFrameId(
171     VRControllerState& controllerState, uint64_t aFrameId) {
172   // The 1st button, trigger, is its selection action.
173   const bool selectionPressed = controllerState.buttonPressed & 1ULL;
174   if (selectionPressed && controllerState.selectActionStopFrameId >=
175                               controllerState.selectActionStartFrameId) {
176     controllerState.selectActionStartFrameId = aFrameId;
177   } else if (!selectionPressed && controllerState.selectActionStartFrameId >
178                                       controllerState.selectActionStopFrameId) {
179     controllerState.selectActionStopFrameId = aFrameId;
180   }
181   // The 2nd button, squeeze, is its squeeze action.
182   const bool squeezePressed = controllerState.buttonPressed & (1ULL << 1);
183   if (squeezePressed && controllerState.squeezeActionStopFrameId >=
184                             controllerState.squeezeActionStartFrameId) {
185     controllerState.squeezeActionStartFrameId = aFrameId;
186   } else if (!squeezePressed && controllerState.squeezeActionStartFrameId >
187                                     controllerState.squeezeActionStopFrameId) {
188     controllerState.squeezeActionStopFrameId = aFrameId;
189   }
190 }
191 
ShouldQuit() const192 bool VRSession::ShouldQuit() const { return mShouldQuit; }
193