Bug Summary

File:var/lib/jenkins/workspace/firefox-scan-build/gfx/vr/service/OpenVRSession.cpp
Warning:line 121, column 7
Value stored to 'hand' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name OpenVRSession.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -ffp-contract=off -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/gfx/vr/service -fcoverage-compilation-dir=/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/gfx/vr/service -resource-dir /usr/lib/llvm-18/lib/clang/18 -include /var/lib/jenkins/workspace/firefox-scan-build/config/gcc_hidden.h -include /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/mozilla-config.h -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/stl_wrappers -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D DEBUG=1 -D MOZ_HAS_MOZGLUE -D MOZILLA_INTERNAL_API -D IMPL_LIBXUL -D STATIC_EXPORTABLE_JS_API -I /var/lib/jenkins/workspace/firefox-scan-build/gfx/vr/service -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/gfx/vr/service -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/ipc/ipdl/_ipdlheaders -I /var/lib/jenkins/workspace/firefox-scan-build/ipc/chromium/src -I /var/lib/jenkins/workspace/firefox-scan-build/dom/base -I /var/lib/jenkins/workspace/firefox-scan-build/gfx/layers/d3d11 -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/nss -D MOZILLA_CLIENT -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/x86_64-linux-gnu/c++/13 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/backward -internal-isystem /usr/lib/llvm-18/lib/clang/18/include -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -Wno-error=tautological-type-limit-compare -Wno-invalid-offsetof -Wno-range-loop-analysis -Wno-deprecated-anon-enum-enum-conversion -Wno-deprecated-enum-enum-conversion -Wno-deprecated-this-capture -Wno-inline-new-delete -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wno-psabi -Wno-error=builtin-macro-redefined -Wno-vla-cxx-extension -Wno-unknown-warning-option -fdeprecated-macro -ferror-limit 19 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fno-rtti -fgnuc-version=4.2.1 -fno-aligned-allocation -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2024-05-16-034744-15991-1 -x c++ /var/lib/jenkins/workspace/firefox-scan-build/gfx/vr/service/OpenVRSession.cpp
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 <fstream>
8#include "mozilla/JSONStringWriteFuncs.h"
9#include "mozilla/ClearOnShutdown.h"
10#include "nsIThread.h"
11#include "nsString.h"
12
13#include "OpenVRSession.h"
14#include "mozilla/StaticPrefs_dom.h"
15
16#if defined(XP_WIN)
17# include <d3d11.h>
18# include "mozilla/gfx/DeviceManagerDx.h"
19#elif defined(XP_MACOSX)
20# include "mozilla/gfx/MacIOSurface.h"
21#endif
22
23#if !defined(XP_WIN)
24# include <sys/stat.h> // for umask()
25#endif
26
27#include "mozilla/dom/GamepadEventTypes.h"
28#include "mozilla/dom/GamepadBinding.h"
29#include "binding/OpenVRCosmosBinding.h"
30#include "binding/OpenVRKnucklesBinding.h"
31#include "binding/OpenVRViveBinding.h"
32#include "OpenVRCosmosMapper.h"
33#include "OpenVRDefaultMapper.h"
34#include "OpenVRKnucklesMapper.h"
35#include "OpenVRViveMapper.h"
36#if defined(XP_WIN) // Windows Mixed Reality is only available in Windows.
37# include "OpenVRWMRMapper.h"
38# include "binding/OpenVRWMRBinding.h"
39#endif
40
41#include "VRParent.h"
42#include "VRProcessChild.h"
43#include "VRThread.h"
44
45#if !defined(M_PI3.14159265358979323846)
46# define M_PI3.14159265358979323846 3.14159265358979323846264338327950288
47#endif
48
49#define BTN_MASK_FROM_ID(_id)::vr::ButtonMaskFromId(vr::EVRButtonId::_id) ::vr::ButtonMaskFromId(vr::EVRButtonId::_id)
50
51// Haptic feedback is updated every 5ms, as this is
52// the minimum period between new haptic pulse requests.
53// Effectively, this results in a pulse width modulation
54// with an interval of 5ms. Through experimentation, the
55// maximum duty cycle was found to be about 3.9ms
56const uint32_t kVRHapticUpdateInterval = 5;
57
58using namespace mozilla::gfx;
59
60namespace mozilla::gfx {
61
62namespace {
63
64class ControllerManifestFile {
65 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ControllerManifestFile)public: MozExternalRefCountType AddRef(void) { static_assert(
!std::is_destructible_v<ControllerManifestFile>, "Reference-counted class "
"ControllerManifestFile" " should not have a public destructor. "
"Make this class's destructor non-public"); do { static_assert
( mozilla::detail::AssertionConditionType<decltype(int32_t
(mRefCnt) >= 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(int32_t(mRefCnt) >= 0))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("int32_t(mRefCnt) >= 0"
" (" "illegal refcnt" ")", "/var/lib/jenkins/workspace/firefox-scan-build/gfx/vr/service/OpenVRSession.cpp"
, 65); AnnotateMozCrashReason("MOZ_ASSERT" "(" "int32_t(mRefCnt) >= 0"
") (" "illegal refcnt" ")"); do { *((volatile int*)__null) =
65; __attribute__((nomerge)) ::abort(); } while (false); } }
while (false); nsrefcnt count = ++mRefCnt; NS_LogAddRef((this
), (count), ("ControllerManifestFile"), (uint32_t)(sizeof(*this
))); return (nsrefcnt)count; } MozExternalRefCountType Release
(void) { do { static_assert( mozilla::detail::AssertionConditionType
<decltype(int32_t(mRefCnt) > 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(int32_t(mRefCnt) > 0))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("int32_t(mRefCnt) > 0"
" (" "dup release" ")", "/var/lib/jenkins/workspace/firefox-scan-build/gfx/vr/service/OpenVRSession.cpp"
, 65); AnnotateMozCrashReason("MOZ_ASSERT" "(" "int32_t(mRefCnt) > 0"
") (" "dup release" ")"); do { *((volatile int*)__null) = 65
; __attribute__((nomerge)) ::abort(); } while (false); } } while
(false); nsrefcnt count = --mRefCnt; NS_LogRelease((this), (
count), ("ControllerManifestFile")); if (count == 0) { delete
(this); return 0; } return count; } using HasThreadSafeRefCnt
= std::true_type; protected: ::mozilla::ThreadSafeAutoRefCnt
mRefCnt; public:
66
67 public:
68 static already_AddRefed<ControllerManifestFile> CreateManifest() {
69 RefPtr<ControllerManifestFile> manifest = new ControllerManifestFile();
70 return manifest.forget();
71 }
72
73 bool IsExisting() {
74 if (mFileName.IsEmpty() ||
75 !std::ifstream(mFileName.BeginReading()).good()) {
76 return false;
77 }
78 return true;
79 }
80
81 void SetFileName(const char* aName) { mFileName = aName; }
82
83 const char* GetFileName() const { return mFileName.BeginReading(); }
84
85 private:
86 ControllerManifestFile() = default;
87
88 ~ControllerManifestFile() {
89 if (!mFileName.IsEmpty() && remove(mFileName.BeginReading()) != 0) {
90 MOZ_ASSERT(false, "Delete controller manifest file failed.")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "Delete controller manifest file failed."
")", "/var/lib/jenkins/workspace/firefox-scan-build/gfx/vr/service/OpenVRSession.cpp"
, 90); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") (" "Delete controller manifest file failed."
")"); do { *((volatile int*)__null) = 90; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
91 }
92 mFileName = "";
93 }
94
95 nsCString mFileName;
96};
97
98// We wanna keep these temporary files existing
99// until Firefox is closed instead of following OpenVRSession's lifetime.
100StaticRefPtr<ControllerManifestFile> sCosmosBindingFile;
101StaticRefPtr<ControllerManifestFile> sKnucklesBindingFile;
102StaticRefPtr<ControllerManifestFile> sViveBindingFile;
103#if defined(XP_WIN)
104StaticRefPtr<ControllerManifestFile> sWMRBindingFile;
105#endif
106StaticRefPtr<ControllerManifestFile> sControllerActionFile;
107
108dom::GamepadHand GetControllerHandFromControllerRole(OpenVRHand aRole) {
109 dom::GamepadHand hand;
110 switch (aRole) {
111 case OpenVRHand::None:
112 hand = dom::GamepadHand::_empty;
113 break;
114 case OpenVRHand::Left:
115 hand = dom::GamepadHand::Left;
116 break;
117 case OpenVRHand::Right:
118 hand = dom::GamepadHand::Right;
119 break;
120 default:
121 hand = dom::GamepadHand::_empty;
Value stored to 'hand' is never read
122 MOZ_ASSERT(false)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false", "/var/lib/jenkins/workspace/firefox-scan-build/gfx/vr/service/OpenVRSession.cpp"
, 122); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ")");
do { *((volatile int*)__null) = 122; __attribute__((nomerge)
) ::abort(); } while (false); } } while (false)
;
123 break;
124 }
125
126 return hand;
127}
128
129bool FileIsExisting(const nsCString& aPath) {
130 if (aPath.IsEmpty() || !std::ifstream(aPath.BeginReading()).good()) {
131 return false;
132 }
133 return true;
134}
135
136}; // anonymous namespace
137
138#if defined(XP_WIN)
139bool GenerateTempFileName(nsCString& aPath) {
140 TCHAR tempPathBuffer[MAX_PATH];
141 TCHAR tempFileName[MAX_PATH];
142
143 // Gets the temp path env string (no guarantee it's a valid path).
144 DWORD dwRetVal = GetTempPath(MAX_PATH, tempPathBuffer);
145 if (dwRetVal > MAX_PATH || (dwRetVal == 0)) {
146 NS_WARNING("OpenVR - Creating temp path failed.")NS_DebugBreak(NS_DEBUG_WARNING, "OpenVR - Creating temp path failed."
, nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/gfx/vr/service/OpenVRSession.cpp"
, 146)
;
147 return false;
148 }
149
150 // Generates a temporary file name.
151 UINT uRetVal = GetTempFileName(tempPathBuffer, // directory for tmp files
152 TEXT("mozvr"), // temp file name prefix
153 0, // create unique name
154 tempFileName); // buffer for name
155 if (uRetVal == 0) {
156 NS_WARNING("OpenVR - Creating temp file failed.")NS_DebugBreak(NS_DEBUG_WARNING, "OpenVR - Creating temp file failed."
, nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/gfx/vr/service/OpenVRSession.cpp"
, 156)
;
157 return false;
158 }
159
160 aPath.Assign(NS_ConvertUTF16toUTF8(tempFileName));
161 return true;
162}
163#else
164bool GenerateTempFileName(nsCString& aPath) {
165 const char tmp[] = "/tmp/mozvrXXXXXX";
166 char fileName[PATH_MAX4096];
167
168 strcpy(fileName, tmp);
169 const mode_t prevMask = umask(S_IXUSR0100 | S_IRWXO(((0400|0200|0100) >> 3) >> 3) | S_IRWXG((0400|0200|0100) >> 3));
170 const int fd = mkstemp(fileName);
171 umask(prevMask);
172 if (fd == -1) {
173 NS_WARNING(nsPrintfCString("OpenVR - Creating temp file failed: %s",NS_DebugBreak(NS_DEBUG_WARNING, nsPrintfCString("OpenVR - Creating temp file failed: %s"
, strerror((*__errno_location ()))) .get(), nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/gfx/vr/service/OpenVRSession.cpp"
, 175)
174 strerror(errno))NS_DebugBreak(NS_DEBUG_WARNING, nsPrintfCString("OpenVR - Creating temp file failed: %s"
, strerror((*__errno_location ()))) .get(), nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/gfx/vr/service/OpenVRSession.cpp"
, 175)
175 .get())NS_DebugBreak(NS_DEBUG_WARNING, nsPrintfCString("OpenVR - Creating temp file failed: %s"
, strerror((*__errno_location ()))) .get(), nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/gfx/vr/service/OpenVRSession.cpp"
, 175)
;
176 return false;
177 }
178 close(fd);
179
180 aPath.Assign(fileName);
181 return true;
182}
183#endif // defined(XP_WIN)
184
185OpenVRSession::OpenVRSession()
186 : mVRSystem(nullptr),
187 mVRChaperone(nullptr),
188 mVRCompositor(nullptr),
189 mHapticPulseRemaining{},
190 mHapticPulseIntensity{},
191 mIsWindowsMR(false),
192 mControllerHapticStateMutex(
193 "OpenVRSession::mControllerHapticStateMutex") {
194 std::fill_n(mControllerDeviceIndex, kVRControllerMaxCount, OpenVRHand::None);
195}
196
197OpenVRSession::~OpenVRSession() {
198 mActionsetFirefox = ::vr::k_ulInvalidActionSetHandle;
199 Shutdown();
200}
201
202bool OpenVRSession::Initialize(mozilla::gfx::VRSystemState& aSystemState,
203 bool aDetectRuntimesOnly) {
204 if (StaticPrefs::dom_vr_puppet_enabled()) {
205 // Ensure that tests using the VR Puppet do not find real hardware
206 return false;
207 }
208 if (!StaticPrefs::dom_vr_enabled() || !StaticPrefs::dom_vr_openvr_enabled()) {
209 return false;
210 }
211 if (mVRSystem != nullptr) {
212 // Already initialized
213 return true;
214 }
215 if (!::vr::VR_IsRuntimeInstalled()) {
216 return false;
217 }
218 if (aDetectRuntimesOnly) {
219 aSystemState.displayState.capabilityFlags |=
220 VRDisplayCapabilityFlags::Cap_ImmersiveVR;
221 return false;
222 }
223 if (!::vr::VR_IsHmdPresent()) {
224 return false;
225 }
226
227 ::vr::HmdError err;
228
229 ::vr::VR_Init(&err, ::vr::EVRApplicationType::VRApplication_Scene);
230 if (err) {
231 return false;
232 }
233
234 mVRSystem = (::vr::IVRSystem*)::vr::VR_GetGenericInterface(
235 ::vr::IVRSystem_Version, &err);
236 if (err || !mVRSystem) {
237 Shutdown();
238 return false;
239 }
240 mVRChaperone = (::vr::IVRChaperone*)::vr::VR_GetGenericInterface(
241 ::vr::IVRChaperone_Version, &err);
242 if (err || !mVRChaperone) {
243 Shutdown();
244 return false;
245 }
246 mVRCompositor = (::vr::IVRCompositor*)::vr::VR_GetGenericInterface(
247 ::vr::IVRCompositor_Version, &err);
248 if (err || !mVRCompositor) {
249 Shutdown();
250 return false;
251 }
252
253#if defined(XP_WIN)
254 if (!CreateD3DObjects()) {
255 Shutdown();
256 return false;
257 }
258
259#endif
260
261 // Configure coordinate system
262 mVRCompositor->SetTrackingSpace(::vr::TrackingUniverseSeated);
263
264 if (!InitState(aSystemState)) {
265 Shutdown();
266 return false;
267 }
268 if (!SetupContollerActions()) {
269 return false;
270 }
271
272 // Succeeded
273 return true;
274}
275
276// "actions": [] Action paths must take the form: "/actions/<action
277// set>/in|out/<action>"
278#define CreateControllerAction(hand, name, type)ControllerAction("/actions/firefox/in/" "hand" "Hand_" "name"
, "type")
\
279 ControllerAction("/actions/firefox/in/" #hand "Hand_" #name, #type)
280#define CreateControllerOutAction(hand, name, type)ControllerAction("/actions/firefox/out/" "hand" "Hand_" "name"
, "type")
\
281 ControllerAction("/actions/firefox/out/" #hand "Hand_" #name, #type)
282
283bool OpenVRSession::SetupContollerActions() {
284 if (!vr::VRInput()) {
285 NS_WARNING("OpenVR - vr::VRInput() is null.")NS_DebugBreak(NS_DEBUG_WARNING, "OpenVR - vr::VRInput() is null."
, nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/gfx/vr/service/OpenVRSession.cpp"
, 285)
;
286 return false;
287 }
288
289 // Check if this device binding file has been created.
290 // If it didn't exist yet, create a new temp file.
291 nsCString controllerAction;
292 nsCString viveManifest;
293 nsCString WMRManifest;
294 nsCString knucklesManifest;
295 nsCString cosmosManifest;
296
297 // Getting / Generating manifest file paths.
298 if (StaticPrefs::dom_vr_process_enabled_AtStartup()) {
299 VRParent* vrParent = VRProcessChild::GetVRParent();
300 nsCString output;
301
302 if (vrParent->GetOpenVRControllerActionPath(&output)) {
303 controllerAction = output;
304 }
305
306 if (vrParent->GetOpenVRControllerManifestPath(VRControllerType::HTCVive,
307 &output)) {
308 viveManifest = output;
309 }
310 if (!viveManifest.Length() || !FileIsExisting(viveManifest)) {
311 if (!GenerateTempFileName(viveManifest)) {
312 return false;
313 }
314 OpenVRViveBinding viveBinding;
315 std::ofstream viveBindingFile(viveManifest.BeginReading());
316 if (viveBindingFile.is_open()) {
317 viveBindingFile << viveBinding.binding;
318 viveBindingFile.close();
319 }
320 }
321
322#if defined(XP_WIN)
323 if (vrParent->GetOpenVRControllerManifestPath(VRControllerType::MSMR,
324 &output)) {
325 WMRManifest = output;
326 }
327 if (!WMRManifest.Length() || !FileIsExisting(WMRManifest)) {
328 if (!GenerateTempFileName(WMRManifest)) {
329 return false;
330 }
331 OpenVRWMRBinding WMRBinding;
332 std::ofstream WMRBindingFile(WMRManifest.BeginReading());
333 if (WMRBindingFile.is_open()) {
334 WMRBindingFile << WMRBinding.binding;
335 WMRBindingFile.close();
336 }
337 }
338#endif
339 if (vrParent->GetOpenVRControllerManifestPath(VRControllerType::ValveIndex,
340 &output)) {
341 knucklesManifest = output;
342 }
343 if (!knucklesManifest.Length() || !FileIsExisting(knucklesManifest)) {
344 if (!GenerateTempFileName(knucklesManifest)) {
345 return false;
346 }
347 OpenVRKnucklesBinding knucklesBinding;
348 std::ofstream knucklesBindingFile(knucklesManifest.BeginReading());
349 if (knucklesBindingFile.is_open()) {
350 knucklesBindingFile << knucklesBinding.binding;
351 knucklesBindingFile.close();
352 }
353 }
354 if (vrParent->GetOpenVRControllerManifestPath(
355 VRControllerType::HTCViveCosmos, &output)) {
356 cosmosManifest = output;
357 }
358 if (!cosmosManifest.Length() || !FileIsExisting(cosmosManifest)) {
359 if (!GenerateTempFileName(cosmosManifest)) {
360 return false;
361 }
362 OpenVRCosmosBinding cosmosBinding;
363 std::ofstream cosmosBindingFile(cosmosManifest.BeginReading());
364 if (cosmosBindingFile.is_open()) {
365 cosmosBindingFile << cosmosBinding.binding;
366 cosmosBindingFile.close();
367 }
368 }
369 } else {
370 // Without using VR process
371 if (!sControllerActionFile) {
372 sControllerActionFile = ControllerManifestFile::CreateManifest();
373 NS_DispatchToMainThread(NS_NewRunnableFunction(
374 "ClearOnShutdown ControllerManifestFile",
375 []() { ClearOnShutdown(&sControllerActionFile); }));
376 }
377 controllerAction = sControllerActionFile->GetFileName();
378
379 if (!sViveBindingFile) {
380 sViveBindingFile = ControllerManifestFile::CreateManifest();
381 NS_DispatchToMainThread(
382 NS_NewRunnableFunction("ClearOnShutdown ControllerManifestFile",
383 []() { ClearOnShutdown(&sViveBindingFile); }));
384 }
385 if (!sViveBindingFile->IsExisting()) {
386 nsCString viveBindingPath;
387 if (!GenerateTempFileName(viveBindingPath)) {
388 return false;
389 }
390 sViveBindingFile->SetFileName(viveBindingPath.BeginReading());
391 OpenVRViveBinding viveBinding;
392 std::ofstream viveBindingFile(sViveBindingFile->GetFileName());
393 if (viveBindingFile.is_open()) {
394 viveBindingFile << viveBinding.binding;
395 viveBindingFile.close();
396 }
397 }
398 viveManifest = sViveBindingFile->GetFileName();
399
400 if (!sKnucklesBindingFile) {
401 sKnucklesBindingFile = ControllerManifestFile::CreateManifest();
402 NS_DispatchToMainThread(NS_NewRunnableFunction(
403 "ClearOnShutdown ControllerManifestFile",
404 []() { ClearOnShutdown(&sKnucklesBindingFile); }));
405 }
406 if (!sKnucklesBindingFile->IsExisting()) {
407 nsCString knucklesBindingPath;
408 if (!GenerateTempFileName(knucklesBindingPath)) {
409 return false;
410 }
411 sKnucklesBindingFile->SetFileName(knucklesBindingPath.BeginReading());
412 OpenVRKnucklesBinding knucklesBinding;
413 std::ofstream knucklesBindingFile(sKnucklesBindingFile->GetFileName());
414 if (knucklesBindingFile.is_open()) {
415 knucklesBindingFile << knucklesBinding.binding;
416 knucklesBindingFile.close();
417 }
418 }
419 knucklesManifest = sKnucklesBindingFile->GetFileName();
420
421 if (!sCosmosBindingFile) {
422 sCosmosBindingFile = ControllerManifestFile::CreateManifest();
423 NS_DispatchToMainThread(NS_NewRunnableFunction(
424 "ClearOnShutdown ControllerManifestFile",
425 []() { ClearOnShutdown(&sCosmosBindingFile); }));
426 }
427 if (!sCosmosBindingFile->IsExisting()) {
428 nsCString cosmosBindingPath;
429 if (!GenerateTempFileName(cosmosBindingPath)) {
430 return false;
431 }
432 sCosmosBindingFile->SetFileName(cosmosBindingPath.BeginReading());
433 OpenVRCosmosBinding cosmosBinding;
434 std::ofstream cosmosBindingFile(sCosmosBindingFile->GetFileName());
435 if (cosmosBindingFile.is_open()) {
436 cosmosBindingFile << cosmosBinding.binding;
437 cosmosBindingFile.close();
438 }
439 }
440 cosmosManifest = sCosmosBindingFile->GetFileName();
441#if defined(XP_WIN)
442 if (!sWMRBindingFile) {
443 sWMRBindingFile = ControllerManifestFile::CreateManifest();
444 NS_DispatchToMainThread(
445 NS_NewRunnableFunction("ClearOnShutdown ControllerManifestFile",
446 []() { ClearOnShutdown(&sWMRBindingFile); }));
447 }
448 if (!sWMRBindingFile->IsExisting()) {
449 nsCString WMRBindingPath;
450 if (!GenerateTempFileName(WMRBindingPath)) {
451 return false;
452 }
453 sWMRBindingFile->SetFileName(WMRBindingPath.BeginReading());
454 OpenVRWMRBinding WMRBinding;
455 std::ofstream WMRBindingFile(sWMRBindingFile->GetFileName());
456 if (WMRBindingFile.is_open()) {
457 WMRBindingFile << WMRBinding.binding;
458 WMRBindingFile.close();
459 }
460 }
461 WMRManifest = sWMRBindingFile->GetFileName();
462#endif
463 }
464 // End of Getting / Generating manifest file paths.
465
466 // Setup controller actions.
467 ControllerInfo leftContollerInfo;
468 leftContollerInfo.mActionPose = CreateControllerAction(L, pose, pose)ControllerAction("/actions/firefox/in/" "L" "Hand_" "pose", "pose"
)
;
469 leftContollerInfo.mActionTrackpad_Analog =
470 CreateControllerAction(L, trackpad_analog, vector2)ControllerAction("/actions/firefox/in/" "L" "Hand_" "trackpad_analog"
, "vector2")
;
471 leftContollerInfo.mActionTrackpad_Pressed =
472 CreateControllerAction(L, trackpad_pressed, boolean)ControllerAction("/actions/firefox/in/" "L" "Hand_" "trackpad_pressed"
, "boolean")
;
473 leftContollerInfo.mActionTrackpad_Touched =
474 CreateControllerAction(L, trackpad_touched, boolean)ControllerAction("/actions/firefox/in/" "L" "Hand_" "trackpad_touched"
, "boolean")
;
475 leftContollerInfo.mActionTrigger_Value =
476 CreateControllerAction(L, trigger_value, vector1)ControllerAction("/actions/firefox/in/" "L" "Hand_" "trigger_value"
, "vector1")
;
477 leftContollerInfo.mActionGrip_Pressed =
478 CreateControllerAction(L, grip_pressed, boolean)ControllerAction("/actions/firefox/in/" "L" "Hand_" "grip_pressed"
, "boolean")
;
479 leftContollerInfo.mActionGrip_Touched =
480 CreateControllerAction(L, grip_touched, boolean)ControllerAction("/actions/firefox/in/" "L" "Hand_" "grip_touched"
, "boolean")
;
481 leftContollerInfo.mActionMenu_Pressed =
482 CreateControllerAction(L, menu_pressed, boolean)ControllerAction("/actions/firefox/in/" "L" "Hand_" "menu_pressed"
, "boolean")
;
483 leftContollerInfo.mActionMenu_Touched =
484 CreateControllerAction(L, menu_touched, boolean)ControllerAction("/actions/firefox/in/" "L" "Hand_" "menu_touched"
, "boolean")
;
485 leftContollerInfo.mActionSystem_Pressed =
486 CreateControllerAction(L, system_pressed, boolean)ControllerAction("/actions/firefox/in/" "L" "Hand_" "system_pressed"
, "boolean")
;
487 leftContollerInfo.mActionSystem_Touched =
488 CreateControllerAction(L, system_touched, boolean)ControllerAction("/actions/firefox/in/" "L" "Hand_" "system_touched"
, "boolean")
;
489 leftContollerInfo.mActionA_Pressed =
490 CreateControllerAction(L, A_pressed, boolean)ControllerAction("/actions/firefox/in/" "L" "Hand_" "A_pressed"
, "boolean")
;
491 leftContollerInfo.mActionA_Touched =
492 CreateControllerAction(L, A_touched, boolean)ControllerAction("/actions/firefox/in/" "L" "Hand_" "A_touched"
, "boolean")
;
493 leftContollerInfo.mActionB_Pressed =
494 CreateControllerAction(L, B_pressed, boolean)ControllerAction("/actions/firefox/in/" "L" "Hand_" "B_pressed"
, "boolean")
;
495 leftContollerInfo.mActionB_Touched =
496 CreateControllerAction(L, B_touched, boolean)ControllerAction("/actions/firefox/in/" "L" "Hand_" "B_touched"
, "boolean")
;
497 leftContollerInfo.mActionThumbstick_Analog =
498 CreateControllerAction(L, thumbstick_analog, vector2)ControllerAction("/actions/firefox/in/" "L" "Hand_" "thumbstick_analog"
, "vector2")
;
499 leftContollerInfo.mActionThumbstick_Pressed =
500 CreateControllerAction(L, thumbstick_pressed, boolean)ControllerAction("/actions/firefox/in/" "L" "Hand_" "thumbstick_pressed"
, "boolean")
;
501 leftContollerInfo.mActionThumbstick_Touched =
502 CreateControllerAction(L, thumbstick_touched, boolean)ControllerAction("/actions/firefox/in/" "L" "Hand_" "thumbstick_touched"
, "boolean")
;
503 leftContollerInfo.mActionFingerIndex_Value =
504 CreateControllerAction(L, finger_index_value, vector1)ControllerAction("/actions/firefox/in/" "L" "Hand_" "finger_index_value"
, "vector1")
;
505 leftContollerInfo.mActionFingerMiddle_Value =
506 CreateControllerAction(L, finger_middle_value, vector1)ControllerAction("/actions/firefox/in/" "L" "Hand_" "finger_middle_value"
, "vector1")
;
507 leftContollerInfo.mActionFingerRing_Value =
508 CreateControllerAction(L, finger_ring_value, vector1)ControllerAction("/actions/firefox/in/" "L" "Hand_" "finger_ring_value"
, "vector1")
;
509 leftContollerInfo.mActionFingerPinky_Value =
510 CreateControllerAction(L, finger_pinky_value, vector1)ControllerAction("/actions/firefox/in/" "L" "Hand_" "finger_pinky_value"
, "vector1")
;
511 leftContollerInfo.mActionBumper_Pressed =
512 CreateControllerAction(L, bumper_pressed, boolean)ControllerAction("/actions/firefox/in/" "L" "Hand_" "bumper_pressed"
, "boolean")
;
513 leftContollerInfo.mActionHaptic =
514 CreateControllerOutAction(L, haptic, vibration)ControllerAction("/actions/firefox/out/" "L" "Hand_" "haptic"
, "vibration")
;
515
516 ControllerInfo rightContollerInfo;
517 rightContollerInfo.mActionPose = CreateControllerAction(R, pose, pose)ControllerAction("/actions/firefox/in/" "R" "Hand_" "pose", "pose"
)
;
518 rightContollerInfo.mActionTrackpad_Analog =
519 CreateControllerAction(R, trackpad_analog, vector2)ControllerAction("/actions/firefox/in/" "R" "Hand_" "trackpad_analog"
, "vector2")
;
520 rightContollerInfo.mActionTrackpad_Pressed =
521 CreateControllerAction(R, trackpad_pressed, boolean)ControllerAction("/actions/firefox/in/" "R" "Hand_" "trackpad_pressed"
, "boolean")
;
522 rightContollerInfo.mActionTrackpad_Touched =
523 CreateControllerAction(R, trackpad_touched, boolean)ControllerAction("/actions/firefox/in/" "R" "Hand_" "trackpad_touched"
, "boolean")
;
524 rightContollerInfo.mActionTrigger_Value =
525 CreateControllerAction(R, trigger_value, vector1)ControllerAction("/actions/firefox/in/" "R" "Hand_" "trigger_value"
, "vector1")
;
526 rightContollerInfo.mActionGrip_Pressed =
527 CreateControllerAction(R, grip_pressed, boolean)ControllerAction("/actions/firefox/in/" "R" "Hand_" "grip_pressed"
, "boolean")
;
528 rightContollerInfo.mActionGrip_Touched =
529 CreateControllerAction(R, grip_touched, boolean)ControllerAction("/actions/firefox/in/" "R" "Hand_" "grip_touched"
, "boolean")
;
530 rightContollerInfo.mActionMenu_Pressed =
531 CreateControllerAction(R, menu_pressed, boolean)ControllerAction("/actions/firefox/in/" "R" "Hand_" "menu_pressed"
, "boolean")
;
532 rightContollerInfo.mActionMenu_Touched =
533 CreateControllerAction(R, menu_touched, boolean)ControllerAction("/actions/firefox/in/" "R" "Hand_" "menu_touched"
, "boolean")
;
534 rightContollerInfo.mActionSystem_Pressed =
535 CreateControllerAction(R, system_pressed, boolean)ControllerAction("/actions/firefox/in/" "R" "Hand_" "system_pressed"
, "boolean")
;
536 rightContollerInfo.mActionSystem_Touched =
537 CreateControllerAction(R, system_touched, boolean)ControllerAction("/actions/firefox/in/" "R" "Hand_" "system_touched"
, "boolean")
;
538 rightContollerInfo.mActionA_Pressed =
539 CreateControllerAction(R, A_pressed, boolean)ControllerAction("/actions/firefox/in/" "R" "Hand_" "A_pressed"
, "boolean")
;
540 rightContollerInfo.mActionA_Touched =
541 CreateControllerAction(R, A_touched, boolean)ControllerAction("/actions/firefox/in/" "R" "Hand_" "A_touched"
, "boolean")
;
542 rightContollerInfo.mActionB_Pressed =
543 CreateControllerAction(R, B_pressed, boolean)ControllerAction("/actions/firefox/in/" "R" "Hand_" "B_pressed"
, "boolean")
;
544 rightContollerInfo.mActionB_Touched =
545 CreateControllerAction(R, B_touched, boolean)ControllerAction("/actions/firefox/in/" "R" "Hand_" "B_touched"
, "boolean")
;
546 rightContollerInfo.mActionThumbstick_Analog =
547 CreateControllerAction(R, thumbstick_analog, vector2)ControllerAction("/actions/firefox/in/" "R" "Hand_" "thumbstick_analog"
, "vector2")
;
548 rightContollerInfo.mActionThumbstick_Pressed =
549 CreateControllerAction(R, thumbstick_pressed, boolean)ControllerAction("/actions/firefox/in/" "R" "Hand_" "thumbstick_pressed"
, "boolean")
;
550 rightContollerInfo.mActionThumbstick_Touched =
551 CreateControllerAction(R, thumbstick_touched, boolean)ControllerAction("/actions/firefox/in/" "R" "Hand_" "thumbstick_touched"
, "boolean")
;
552 rightContollerInfo.mActionFingerIndex_Value =
553 CreateControllerAction(R, finger_index_value, vector1)ControllerAction("/actions/firefox/in/" "R" "Hand_" "finger_index_value"
, "vector1")
;
554 rightContollerInfo.mActionFingerMiddle_Value =
555 CreateControllerAction(R, finger_middle_value, vector1)ControllerAction("/actions/firefox/in/" "R" "Hand_" "finger_middle_value"
, "vector1")
;
556 rightContollerInfo.mActionFingerRing_Value =
557 CreateControllerAction(R, finger_ring_value, vector1)ControllerAction("/actions/firefox/in/" "R" "Hand_" "finger_ring_value"
, "vector1")
;
558 rightContollerInfo.mActionFingerPinky_Value =
559 CreateControllerAction(R, finger_pinky_value, vector1)ControllerAction("/actions/firefox/in/" "R" "Hand_" "finger_pinky_value"
, "vector1")
;
560 rightContollerInfo.mActionBumper_Pressed =
561 CreateControllerAction(R, bumper_pressed, boolean)ControllerAction("/actions/firefox/in/" "R" "Hand_" "bumper_pressed"
, "boolean")
;
562 rightContollerInfo.mActionHaptic =
563 CreateControllerOutAction(R, haptic, vibration)ControllerAction("/actions/firefox/out/" "R" "Hand_" "haptic"
, "vibration")
;
564
565 mControllerHand[OpenVRHand::Left] = leftContollerInfo;
566 mControllerHand[OpenVRHand::Right] = rightContollerInfo;
567
568 if (!controllerAction.Length() || !FileIsExisting(controllerAction)) {
569 if (!GenerateTempFileName(controllerAction)) {
570 return false;
571 }
572 JSONStringWriteFunc<nsCString> actionData;
573 JSONWriter actionWriter(actionData);
574 actionWriter.Start();
575
576 actionWriter.StringProperty("version",
577 "0.1.0"); // TODO: adding a version check.
578 // "default_bindings": []
579 actionWriter.StartArrayProperty("default_bindings");
580
581 auto SetupActionWriterByControllerType = [&](const char* aType,
582 const nsCString& aManifest) {
583 actionWriter.StartObjectElement();
584 actionWriter.StringProperty("controller_type", MakeStringSpan(aType));
585 actionWriter.StringProperty("binding_url", aManifest);
586 actionWriter.EndObject();
587 };
588 SetupActionWriterByControllerType("vive_controller", viveManifest);
589 SetupActionWriterByControllerType("knuckles", knucklesManifest);
590 SetupActionWriterByControllerType("vive_cosmos_controller", cosmosManifest);
591#if defined(XP_WIN)
592 SetupActionWriterByControllerType("holographic_controller", WMRManifest);
593#endif
594 actionWriter.EndArray(); // End "default_bindings": []
595
596 actionWriter.StartArrayProperty("actions");
597
598 for (auto& controller : mControllerHand) {
599 auto SetActionsToWriter = [&](const ControllerAction& aAction) {
600 actionWriter.StartObjectElement();
601 actionWriter.StringProperty("name", aAction.name);
602 actionWriter.StringProperty("type", aAction.type);
603 actionWriter.EndObject();
604 };
605
606 SetActionsToWriter(controller.mActionPose);
607 SetActionsToWriter(controller.mActionTrackpad_Analog);
608 SetActionsToWriter(controller.mActionTrackpad_Pressed);
609 SetActionsToWriter(controller.mActionTrackpad_Touched);
610 SetActionsToWriter(controller.mActionTrigger_Value);
611 SetActionsToWriter(controller.mActionGrip_Pressed);
612 SetActionsToWriter(controller.mActionGrip_Touched);
613 SetActionsToWriter(controller.mActionMenu_Pressed);
614 SetActionsToWriter(controller.mActionMenu_Touched);
615 SetActionsToWriter(controller.mActionSystem_Pressed);
616 SetActionsToWriter(controller.mActionSystem_Touched);
617 SetActionsToWriter(controller.mActionA_Pressed);
618 SetActionsToWriter(controller.mActionA_Touched);
619 SetActionsToWriter(controller.mActionB_Pressed);
620 SetActionsToWriter(controller.mActionB_Touched);
621 SetActionsToWriter(controller.mActionThumbstick_Analog);
622 SetActionsToWriter(controller.mActionThumbstick_Pressed);
623 SetActionsToWriter(controller.mActionThumbstick_Touched);
624 SetActionsToWriter(controller.mActionFingerIndex_Value);
625 SetActionsToWriter(controller.mActionFingerMiddle_Value);
626 SetActionsToWriter(controller.mActionFingerRing_Value);
627 SetActionsToWriter(controller.mActionFingerPinky_Value);
628 SetActionsToWriter(controller.mActionBumper_Pressed);
629 SetActionsToWriter(controller.mActionHaptic);
630 }
631 actionWriter.EndArray(); // End "actions": []
632 actionWriter.End();
633
634 std::ofstream actionfile(controllerAction.BeginReading());
635 if (actionfile.is_open()) {
636 actionfile << actionData.StringCRef().get();
637 actionfile.close();
638 }
639 }
640
641 vr::EVRInputError err =
642 vr::VRInput()->SetActionManifestPath(controllerAction.BeginReading());
643 if (err != vr::VRInputError_None) {
644 NS_WARNING("OpenVR - SetActionManifestPath failed.")NS_DebugBreak(NS_DEBUG_WARNING, "OpenVR - SetActionManifestPath failed."
, nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/gfx/vr/service/OpenVRSession.cpp"
, 644)
;
645 return false;
646 }
647 // End of setup controller actions.
648
649 // Notify the parent process these manifest files are already been recorded.
650 if (StaticPrefs::dom_vr_process_enabled_AtStartup()) {
651 NS_DispatchToMainThread(NS_NewRunnableFunction(
652 "SendOpenVRControllerActionPathToParent",
653 [controllerAction, viveManifest, WMRManifest, knucklesManifest,
654 cosmosManifest]() {
655 VRParent* vrParent = VRProcessChild::GetVRParent();
656 Unused << vrParent->SendOpenVRControllerActionPathToParent(
657 controllerAction);
658 Unused << vrParent->SendOpenVRControllerManifestPathToParent(
659 VRControllerType::HTCVive, viveManifest);
660 Unused << vrParent->SendOpenVRControllerManifestPathToParent(
661 VRControllerType::MSMR, WMRManifest);
662 Unused << vrParent->SendOpenVRControllerManifestPathToParent(
663 VRControllerType::ValveIndex, knucklesManifest);
664 Unused << vrParent->SendOpenVRControllerManifestPathToParent(
665 VRControllerType::HTCViveCosmos, cosmosManifest);
666 }));
667 } else {
668 sControllerActionFile->SetFileName(controllerAction.BeginReading());
669 }
670
671 return true;
672}
673
674#if defined(XP_WIN)
675bool OpenVRSession::CreateD3DObjects() {
676 RefPtr<ID3D11Device> device = gfx::DeviceManagerDx::Get()->GetVRDevice();
677 if (!device) {
678 return false;
679 }
680 if (!CreateD3DContext(device)) {
681 return false;
682 }
683 return true;
684}
685#endif
686
687void OpenVRSession::Shutdown() {
688 StopHapticTimer();
689 StopHapticThread();
690 if (mVRSystem || mVRCompositor || mVRChaperone) {
691 ::vr::VR_Shutdown();
692 mVRCompositor = nullptr;
693 mVRChaperone = nullptr;
694 mVRSystem = nullptr;
695 }
696}
697
698bool OpenVRSession::InitState(VRSystemState& aSystemState) {
699 VRDisplayState& state = aSystemState.displayState;
700 strncpy(state.displayName, "OpenVR HMD", kVRDisplayNameMaxLen);
701 state.eightCC = GFX_VR_EIGHTCC('O', 'p', 'e', 'n', 'V', 'R', ' ', ' ')((uint64_t)('O') << 56 | (uint64_t)('p') << 48 | (
uint64_t)('e') << 40 | (uint64_t)('n') << 32 | (uint64_t
)('V') << 24 | (uint64_t)('R') << 16 | (uint64_t)
(' ') << 8 | (uint64_t)(' '))
;
702 state.isConnected =
703 mVRSystem->IsTrackedDeviceConnected(::vr::k_unTrackedDeviceIndex_Hmd);
704 state.isMounted = false;
705 state.capabilityFlags =
706 (VRDisplayCapabilityFlags)((int)VRDisplayCapabilityFlags::Cap_None |
707 (int)
708 VRDisplayCapabilityFlags::Cap_Orientation |
709 (int)VRDisplayCapabilityFlags::Cap_Position |
710 (int)VRDisplayCapabilityFlags::Cap_External |
711 (int)VRDisplayCapabilityFlags::Cap_Present |
712 (int)VRDisplayCapabilityFlags::
713 Cap_StageParameters |
714 (int)
715 VRDisplayCapabilityFlags::Cap_ImmersiveVR);
716 state.blendMode = VRDisplayBlendMode::Opaque;
717 state.reportsDroppedFrames = true;
718
719 ::vr::ETrackedPropertyError err;
720 bool bHasProximitySensor = mVRSystem->GetBoolTrackedDeviceProperty(
721 ::vr::k_unTrackedDeviceIndex_Hmd, ::vr::Prop_ContainsProximitySensor_Bool,
722 &err);
723 if (err == ::vr::TrackedProp_Success && bHasProximitySensor) {
724 state.capabilityFlags =
725 (VRDisplayCapabilityFlags)((int)state.capabilityFlags |
726 (int)VRDisplayCapabilityFlags::
727 Cap_MountDetection);
728 }
729
730 uint32_t w, h;
731 mVRSystem->GetRecommendedRenderTargetSize(&w, &h);
732 state.eyeResolution.width = w;
733 state.eyeResolution.height = h;
734 state.nativeFramebufferScaleFactor = 1.0f;
735
736 // default to an identity quaternion
737 aSystemState.sensorState.pose.orientation[3] = 1.0f;
738
739 UpdateStageParameters(state);
740 UpdateEyeParameters(aSystemState);
741
742 VRHMDSensorState& sensorState = aSystemState.sensorState;
743 sensorState.flags =
744 (VRDisplayCapabilityFlags)((int)
745 VRDisplayCapabilityFlags::Cap_Orientation |
746 (int)VRDisplayCapabilityFlags::Cap_Position);
747 sensorState.pose.orientation[3] = 1.0f; // Default to an identity quaternion
748
749 return true;
750}
751
752void OpenVRSession::UpdateStageParameters(VRDisplayState& aState) {
753 float sizeX = 0.0f;
754 float sizeZ = 0.0f;
755 if (mVRChaperone->GetPlayAreaSize(&sizeX, &sizeZ)) {
756 ::vr::HmdMatrix34_t t =
757 mVRSystem->GetSeatedZeroPoseToStandingAbsoluteTrackingPose();
758 aState.stageSize.width = sizeX;
759 aState.stageSize.height = sizeZ;
760
761 aState.sittingToStandingTransform[0] = t.m[0][0];
762 aState.sittingToStandingTransform[1] = t.m[1][0];
763 aState.sittingToStandingTransform[2] = t.m[2][0];
764 aState.sittingToStandingTransform[3] = 0.0f;
765
766 aState.sittingToStandingTransform[4] = t.m[0][1];
767 aState.sittingToStandingTransform[5] = t.m[1][1];
768 aState.sittingToStandingTransform[6] = t.m[2][1];
769 aState.sittingToStandingTransform[7] = 0.0f;
770
771 aState.sittingToStandingTransform[8] = t.m[0][2];
772 aState.sittingToStandingTransform[9] = t.m[1][2];
773 aState.sittingToStandingTransform[10] = t.m[2][2];
774 aState.sittingToStandingTransform[11] = 0.0f;
775
776 aState.sittingToStandingTransform[12] = t.m[0][3];
777 aState.sittingToStandingTransform[13] = t.m[1][3];
778 aState.sittingToStandingTransform[14] = t.m[2][3];
779 aState.sittingToStandingTransform[15] = 1.0f;
780 } else {
781 // If we fail, fall back to reasonable defaults.
782 // 1m x 1m space, 0.75m high in seated position
783 aState.stageSize.width = 1.0f;
784 aState.stageSize.height = 1.0f;
785
786 aState.sittingToStandingTransform[0] = 1.0f;
787 aState.sittingToStandingTransform[1] = 0.0f;
788 aState.sittingToStandingTransform[2] = 0.0f;
789 aState.sittingToStandingTransform[3] = 0.0f;
790
791 aState.sittingToStandingTransform[4] = 0.0f;
792 aState.sittingToStandingTransform[5] = 1.0f;
793 aState.sittingToStandingTransform[6] = 0.0f;
794 aState.sittingToStandingTransform[7] = 0.0f;
795
796 aState.sittingToStandingTransform[8] = 0.0f;
797 aState.sittingToStandingTransform[9] = 0.0f;
798 aState.sittingToStandingTransform[10] = 1.0f;
799 aState.sittingToStandingTransform[11] = 0.0f;
800
801 aState.sittingToStandingTransform[12] = 0.0f;
802 aState.sittingToStandingTransform[13] = 0.75f;
803 aState.sittingToStandingTransform[14] = 0.0f;
804 aState.sittingToStandingTransform[15] = 1.0f;
805 }
806}
807
808void OpenVRSession::UpdateEyeParameters(VRSystemState& aState) {
809 // This must be called every frame in order to
810 // account for continuous adjustments to ipd.
811 gfx::Matrix4x4 headToEyeTransforms[2];
812
813 for (uint32_t eye = 0; eye < 2; ++eye) {
814 ::vr::HmdMatrix34_t eyeToHead =
815 mVRSystem->GetEyeToHeadTransform(static_cast<::vr::Hmd_Eye>(eye));
816 aState.displayState.eyeTranslation[eye].x = eyeToHead.m[0][3];
817 aState.displayState.eyeTranslation[eye].y = eyeToHead.m[1][3];
818 aState.displayState.eyeTranslation[eye].z = eyeToHead.m[2][3];
819
820 float left, right, up, down;
821 mVRSystem->GetProjectionRaw(static_cast<::vr::Hmd_Eye>(eye), &left, &right,
822 &up, &down);
823 aState.displayState.eyeFOV[eye].upDegrees = atan(-up) * 180.0 / M_PI3.14159265358979323846;
824 aState.displayState.eyeFOV[eye].rightDegrees = atan(right) * 180.0 / M_PI3.14159265358979323846;
825 aState.displayState.eyeFOV[eye].downDegrees = atan(down) * 180.0 / M_PI3.14159265358979323846;
826 aState.displayState.eyeFOV[eye].leftDegrees = atan(-left) * 180.0 / M_PI3.14159265358979323846;
827
828 Matrix4x4 pose;
829 // NOTE! eyeToHead.m is a 3x4 matrix, not 4x4. But
830 // because of its arrangement, we can copy the 12 elements in and
831 // then transpose them to the right place.
832 memcpy(&pose._11, &eyeToHead.m, sizeof(eyeToHead.m));
833 pose.Transpose();
834 pose.Invert();
835 headToEyeTransforms[eye] = pose;
836 }
837 aState.sensorState.CalcViewMatrices(headToEyeTransforms);
838}
839
840void OpenVRSession::UpdateHeadsetPose(VRSystemState& aState) {
841 const uint32_t posesSize = ::vr::k_unTrackedDeviceIndex_Hmd + 1;
842 ::vr::TrackedDevicePose_t poses[posesSize];
843 // Note: We *must* call WaitGetPoses in order for any rendering to happen at
844 // all.
845 mVRCompositor->WaitGetPoses(poses, posesSize, nullptr, 0);
846
847 ::vr::Compositor_FrameTiming timing;
848 timing.m_nSize = sizeof(::vr::Compositor_FrameTiming);
849 if (mVRCompositor->GetFrameTiming(&timing)) {
850 aState.sensorState.timestamp = timing.m_flSystemTimeInSeconds;
851 } else {
852 // This should not happen, but log it just in case
853 fprintf(stderrstderr, "OpenVR - IVRCompositor::GetFrameTiming failed");
854 }
855
856 if (poses[::vr::k_unTrackedDeviceIndex_Hmd].bDeviceIsConnected &&
857 poses[::vr::k_unTrackedDeviceIndex_Hmd].bPoseIsValid &&
858 poses[::vr::k_unTrackedDeviceIndex_Hmd].eTrackingResult ==
859 ::vr::TrackingResult_Running_OK) {
860 const ::vr::TrackedDevicePose_t& pose =
861 poses[::vr::k_unTrackedDeviceIndex_Hmd];
862
863 gfx::Matrix4x4 m;
864 // NOTE! mDeviceToAbsoluteTracking is a 3x4 matrix, not 4x4. But
865 // because of its arrangement, we can copy the 12 elements in and
866 // then transpose them to the right place. We do this so we can
867 // pull out a Quaternion.
868 memcpy(&m._11, &pose.mDeviceToAbsoluteTracking,
869 sizeof(pose.mDeviceToAbsoluteTracking));
870 m.Transpose();
871
872 gfx::Quaternion rot;
873 rot.SetFromRotationMatrix(m);
874
875 aState.sensorState.flags =
876 (VRDisplayCapabilityFlags)((int)aState.sensorState.flags |
877 (int)VRDisplayCapabilityFlags::
878 Cap_Orientation);
879 aState.sensorState.pose.orientation[0] = rot.x;
880 aState.sensorState.pose.orientation[1] = rot.y;
881 aState.sensorState.pose.orientation[2] = rot.z;
882 aState.sensorState.pose.orientation[3] = rot.w;
883 aState.sensorState.pose.angularVelocity[0] = pose.vAngularVelocity.v[0];
884 aState.sensorState.pose.angularVelocity[1] = pose.vAngularVelocity.v[1];
885 aState.sensorState.pose.angularVelocity[2] = pose.vAngularVelocity.v[2];
886
887 aState.sensorState.flags =
888 (VRDisplayCapabilityFlags)((int)aState.sensorState.flags |
889 (int)VRDisplayCapabilityFlags::Cap_Position);
890 aState.sensorState.pose.position[0] = m._41;
891 aState.sensorState.pose.position[1] = m._42;
892 aState.sensorState.pose.position[2] = m._43;
893 aState.sensorState.pose.linearVelocity[0] = pose.vVelocity.v[0];
894 aState.sensorState.pose.linearVelocity[1] = pose.vVelocity.v[1];
895 aState.sensorState.pose.linearVelocity[2] = pose.vVelocity.v[2];
896 }
897}
898
899void OpenVRSession::EnumerateControllers(VRSystemState& aState) {
900 MOZ_ASSERT(mVRSystem)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mVRSystem)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mVRSystem))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("mVRSystem", "/var/lib/jenkins/workspace/firefox-scan-build/gfx/vr/service/OpenVRSession.cpp"
, 900); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mVRSystem" ")"
); do { *((volatile int*)__null) = 900; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
901
902 MutexAutoLock lock(mControllerHapticStateMutex);
903
904 bool controllerPresent[kVRControllerMaxCount] = {false};
905 uint32_t stateIndex = 0;
906 mActionsetFirefox = vr::k_ulInvalidActionSetHandle;
907 VRControllerType controllerType = VRControllerType::_empty;
908
909 if (vr::VRInput()->GetActionSetHandle(
910 "/actions/firefox", &mActionsetFirefox) != vr::VRInputError_None) {
911 return;
912 }
913
914 for (int8_t handIndex = 0; handIndex < OpenVRHand::Total; ++handIndex) {
915 if (handIndex == OpenVRHand::Left) {
916 if (vr::VRInput()->GetInputSourceHandle(
917 "/user/hand/left", &mControllerHand[OpenVRHand::Left].mSource) !=
918 vr::VRInputError_None) {
919 continue;
920 }
921 } else if (handIndex == OpenVRHand::Right) {
922 if (vr::VRInput()->GetInputSourceHandle(
923 "/user/hand/right",
924 &mControllerHand[OpenVRHand::Right].mSource) !=
925 vr::VRInputError_None) {
926 continue;
927 }
928 } else {
929 MOZ_ASSERT(false, "Unknown OpenVR hand type.")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "Unknown OpenVR hand type."
")", "/var/lib/jenkins/workspace/firefox-scan-build/gfx/vr/service/OpenVRSession.cpp"
, 929); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"Unknown OpenVR hand type." ")"); do { *((volatile int*)__null
) = 929; __attribute__((nomerge)) ::abort(); } while (false);
} } while (false)
;
930 }
931
932 vr::InputOriginInfo_t originInfo;
933 if (vr::VRInput()->GetOriginTrackedDeviceInfo(
934 mControllerHand[handIndex].mSource, &originInfo,
935 sizeof(originInfo)) == vr::VRInputError_None &&
936 originInfo.trackedDeviceIndex != vr::k_unTrackedDeviceIndexInvalid &&
937 mVRSystem->IsTrackedDeviceConnected(originInfo.trackedDeviceIndex)) {
938 const ::vr::ETrackedDeviceClass deviceType =
939 mVRSystem->GetTrackedDeviceClass(originInfo.trackedDeviceIndex);
940 if (deviceType != ::vr::TrackedDeviceClass_Controller &&
941 deviceType != ::vr::TrackedDeviceClass_GenericTracker) {
942 continue;
943 }
944
945 if (mControllerDeviceIndex[stateIndex] != handIndex) {
946 VRControllerState& controllerState = aState.controllerState[stateIndex];
947
948 // Get controllers' action handles.
949 auto SetActionsToWriter = [&](ControllerAction& aAction) {
950 vr::VRInput()->GetActionHandle(aAction.name.BeginReading(),
951 &aAction.handle);
952 };
953
954 SetActionsToWriter(mControllerHand[handIndex].mActionPose);
955 SetActionsToWriter(mControllerHand[handIndex].mActionHaptic);
956 SetActionsToWriter(mControllerHand[handIndex].mActionTrackpad_Analog);
957 SetActionsToWriter(mControllerHand[handIndex].mActionTrackpad_Pressed);
958 SetActionsToWriter(mControllerHand[handIndex].mActionTrackpad_Touched);
959 SetActionsToWriter(mControllerHand[handIndex].mActionTrigger_Value);
960 SetActionsToWriter(mControllerHand[handIndex].mActionGrip_Pressed);
961 SetActionsToWriter(mControllerHand[handIndex].mActionGrip_Touched);
962 SetActionsToWriter(mControllerHand[handIndex].mActionMenu_Pressed);
963 SetActionsToWriter(mControllerHand[handIndex].mActionMenu_Touched);
964 SetActionsToWriter(mControllerHand[handIndex].mActionSystem_Pressed);
965 SetActionsToWriter(mControllerHand[handIndex].mActionSystem_Touched);
966 SetActionsToWriter(mControllerHand[handIndex].mActionA_Pressed);
967 SetActionsToWriter(mControllerHand[handIndex].mActionA_Touched);
968 SetActionsToWriter(mControllerHand[handIndex].mActionB_Pressed);
969 SetActionsToWriter(mControllerHand[handIndex].mActionB_Touched);
970 SetActionsToWriter(mControllerHand[handIndex].mActionThumbstick_Analog);
971 SetActionsToWriter(
972 mControllerHand[handIndex].mActionThumbstick_Pressed);
973 SetActionsToWriter(
974 mControllerHand[handIndex].mActionThumbstick_Touched);
975 SetActionsToWriter(mControllerHand[handIndex].mActionFingerIndex_Value);
976 SetActionsToWriter(
977 mControllerHand[handIndex].mActionFingerMiddle_Value);
978 SetActionsToWriter(mControllerHand[handIndex].mActionFingerRing_Value);
979 SetActionsToWriter(mControllerHand[handIndex].mActionFingerPinky_Value);
980 SetActionsToWriter(mControllerHand[handIndex].mActionBumper_Pressed);
981
982 nsCString deviceId;
983 VRControllerType contrlType = VRControllerType::_empty;
984 GetControllerDeviceId(deviceType, originInfo.trackedDeviceIndex,
985 deviceId, contrlType);
986 // Controllers should be the same type with one VR display.
987 MOZ_ASSERT(controllerType == contrlType ||do { static_assert( mozilla::detail::AssertionConditionType<
decltype(controllerType == contrlType || controllerType == VRControllerType
::_empty)>::isValid, "invalid assertion condition"); if ((
__builtin_expect(!!(!(!!(controllerType == contrlType || controllerType
== VRControllerType::_empty))), 0))) { do { } while (false);
MOZ_ReportAssertionFailure("controllerType == contrlType || controllerType == VRControllerType::_empty"
, "/var/lib/jenkins/workspace/firefox-scan-build/gfx/vr/service/OpenVRSession.cpp"
, 988); AnnotateMozCrashReason("MOZ_ASSERT" "(" "controllerType == contrlType || controllerType == VRControllerType::_empty"
")"); do { *((volatile int*)__null) = 988; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
988 controllerType == VRControllerType::_empty)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(controllerType == contrlType || controllerType == VRControllerType
::_empty)>::isValid, "invalid assertion condition"); if ((
__builtin_expect(!!(!(!!(controllerType == contrlType || controllerType
== VRControllerType::_empty))), 0))) { do { } while (false);
MOZ_ReportAssertionFailure("controllerType == contrlType || controllerType == VRControllerType::_empty"
, "/var/lib/jenkins/workspace/firefox-scan-build/gfx/vr/service/OpenVRSession.cpp"
, 988); AnnotateMozCrashReason("MOZ_ASSERT" "(" "controllerType == contrlType || controllerType == VRControllerType::_empty"
")"); do { *((volatile int*)__null) = 988; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
989 controllerType = contrlType;
990 strncpy(controllerState.controllerName, deviceId.BeginReading(),
991 kVRControllerNameMaxLen);
992 controllerState.numHaptics = kNumOpenVRHaptics;
993 controllerState.targetRayMode = gfx::TargetRayMode::TrackedPointer;
994 controllerState.type = controllerType;
995 }
996 controllerPresent[stateIndex] = true;
997 mControllerDeviceIndex[stateIndex] = static_cast<OpenVRHand>(handIndex);
998 ++stateIndex;
999 }
1000 }
1001
1002 // Clear out entries for disconnected controllers
1003 for (uint32_t stateIndex = 0; stateIndex < kVRControllerMaxCount;
1004 stateIndex++) {
1005 if (!controllerPresent[stateIndex] &&
1006 mControllerDeviceIndex[stateIndex] != OpenVRHand::None) {
1007 mControllerDeviceIndex[stateIndex] = OpenVRHand::None;
1008 memset(&aState.controllerState[stateIndex], 0, sizeof(VRControllerState));
1009 }
1010 }
1011
1012 // Create controller mapper
1013 if (controllerType != VRControllerType::_empty) {
1014 switch (controllerType) {
1015 case VRControllerType::HTCVive:
1016 mControllerMapper = MakeUnique<OpenVRViveMapper>();
1017 break;
1018 case VRControllerType::HTCViveCosmos:
1019 mControllerMapper = MakeUnique<OpenVRCosmosMapper>();
1020 break;
1021#if defined(XP_WIN)
1022 case VRControllerType::MSMR:
1023 mControllerMapper = MakeUnique<OpenVRWMRMapper>();
1024 break;
1025#endif
1026 case VRControllerType::ValveIndex:
1027 mControllerMapper = MakeUnique<OpenVRKnucklesMapper>();
1028 break;
1029 default:
1030 mControllerMapper = MakeUnique<OpenVRDefaultMapper>();
1031 NS_WARNING("Undefined controller type")NS_DebugBreak(NS_DEBUG_WARNING, "Undefined controller type", nullptr
, "/var/lib/jenkins/workspace/firefox-scan-build/gfx/vr/service/OpenVRSession.cpp"
, 1031)
;
1032 break;
1033 }
1034 }
1035}
1036
1037void OpenVRSession::UpdateControllerButtons(VRSystemState& aState) {
1038 MOZ_ASSERT(mVRSystem)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mVRSystem)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mVRSystem))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("mVRSystem", "/var/lib/jenkins/workspace/firefox-scan-build/gfx/vr/service/OpenVRSession.cpp"
, 1038); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mVRSystem" ")"
); do { *((volatile int*)__null) = 1038; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1039
1040 for (uint32_t stateIndex = 0; stateIndex < kVRControllerMaxCount;
1041 ++stateIndex) {
1042 const OpenVRHand role = mControllerDeviceIndex[stateIndex];
1043 if (role == OpenVRHand::None) {
1044 continue;
1045 }
1046 VRControllerState& controllerState = aState.controllerState[stateIndex];
1047 controllerState.hand = GetControllerHandFromControllerRole(role);
1048 mControllerMapper->UpdateButtons(controllerState, mControllerHand[role]);
1049 SetControllerSelectionAndSqueezeFrameId(
1050 controllerState, aState.displayState.lastSubmittedFrameId);
1051 }
1052}
1053
1054void OpenVRSession::UpdateControllerPoses(VRSystemState& aState) {
1055 MOZ_ASSERT(mVRSystem)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mVRSystem)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mVRSystem))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("mVRSystem", "/var/lib/jenkins/workspace/firefox-scan-build/gfx/vr/service/OpenVRSession.cpp"
, 1055); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mVRSystem" ")"
); do { *((volatile int*)__null) = 1055; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1056
1057 for (uint32_t stateIndex = 0; stateIndex < kVRControllerMaxCount;
1058 ++stateIndex) {
1059 const OpenVRHand role = mControllerDeviceIndex[stateIndex];
1060 if (role == OpenVRHand::None) {
1061 continue;
1062 }
1063 VRControllerState& controllerState = aState.controllerState[stateIndex];
1064 vr::InputPoseActionData_t poseData;
1065 if (vr::VRInput()->GetPoseActionDataRelativeToNow(
1066 mControllerHand[role].mActionPose.handle,
1067 vr::TrackingUniverseSeated, 0, &poseData, sizeof(poseData),
1068 vr::k_ulInvalidInputValueHandle) != vr::VRInputError_None ||
1069 !poseData.bActive || !poseData.pose.bPoseIsValid) {
1070 controllerState.isOrientationValid = false;
1071 controllerState.isPositionValid = false;
1072 } else {
1073 const ::vr::TrackedDevicePose_t& pose = poseData.pose;
1074 if (pose.bDeviceIsConnected) {
1075 controllerState.flags =
1076 (dom::GamepadCapabilityFlags::Cap_Orientation |
1077 dom::GamepadCapabilityFlags::Cap_Position |
1078 dom::GamepadCapabilityFlags::Cap_GripSpacePosition);
1079 } else {
1080 controllerState.flags = dom::GamepadCapabilityFlags::Cap_None;
1081 }
1082 if (pose.bPoseIsValid &&
1083 pose.eTrackingResult == ::vr::TrackingResult_Running_OK) {
1084 gfx::Matrix4x4 m;
1085
1086 // NOTE! mDeviceToAbsoluteTracking is a 3x4 matrix, not 4x4. But
1087 // because of its arrangement, we can copy the 12 elements in and
1088 // then transpose them to the right place. We do this so we can
1089 // pull out a Quaternion.
1090 memcpy(&m.components, &pose.mDeviceToAbsoluteTracking,
1091 sizeof(pose.mDeviceToAbsoluteTracking));
1092 m.Transpose();
1093
1094 gfx::Quaternion rot;
1095 rot.SetFromRotationMatrix(m);
1096
1097 controllerState.pose.orientation[0] = rot.x;
1098 controllerState.pose.orientation[1] = rot.y;
1099 controllerState.pose.orientation[2] = rot.z;
1100 controllerState.pose.orientation[3] = rot.w;
1101 controllerState.pose.angularVelocity[0] = pose.vAngularVelocity.v[0];
1102 controllerState.pose.angularVelocity[1] = pose.vAngularVelocity.v[1];
1103 controllerState.pose.angularVelocity[2] = pose.vAngularVelocity.v[2];
1104 controllerState.pose.angularAcceleration[0] = 0.0f;
1105 controllerState.pose.angularAcceleration[1] = 0.0f;
1106 controllerState.pose.angularAcceleration[2] = 0.0f;
1107 controllerState.isOrientationValid = true;
1108
1109 controllerState.pose.position[0] = m._41;
1110 controllerState.pose.position[1] = m._42;
1111 controllerState.pose.position[2] = m._43;
1112 controllerState.pose.linearVelocity[0] = pose.vVelocity.v[0];
1113 controllerState.pose.linearVelocity[1] = pose.vVelocity.v[1];
1114 controllerState.pose.linearVelocity[2] = pose.vVelocity.v[2];
1115 controllerState.pose.linearAcceleration[0] = 0.0f;
1116 controllerState.pose.linearAcceleration[1] = 0.0f;
1117 controllerState.pose.linearAcceleration[2] = 0.0f;
1118 controllerState.isPositionValid = true;
1119
1120 // Calculate its target ray space by shifting degrees in x-axis
1121 // for ergonomic.
1122 const float kPointerAngleDegrees = -0.698; // 40 degrees.
1123 gfx::Matrix4x4 rayMtx(m);
1124 rayMtx.RotateX(kPointerAngleDegrees);
1125 gfx::Quaternion rayRot;
1126 rayRot.SetFromRotationMatrix(rayMtx);
1127
1128 controllerState.targetRayPose = controllerState.pose;
1129 controllerState.targetRayPose.orientation[0] = rayRot.x;
1130 controllerState.targetRayPose.orientation[1] = rayRot.y;
1131 controllerState.targetRayPose.orientation[2] = rayRot.z;
1132 controllerState.targetRayPose.orientation[3] = rayRot.w;
1133 controllerState.targetRayPose.position[0] = rayMtx._41;
1134 controllerState.targetRayPose.position[1] = rayMtx._42;
1135 controllerState.targetRayPose.position[2] = rayMtx._43;
1136 }
1137 }
1138 }
1139}
1140
1141void OpenVRSession::GetControllerDeviceId(
1142 ::vr::ETrackedDeviceClass aDeviceType,
1143 ::vr::TrackedDeviceIndex_t aDeviceIndex, nsCString& aId,
1144 VRControllerType& aControllerType) {
1145 switch (aDeviceType) {
1146 case ::vr::TrackedDeviceClass_Controller: {
1147 ::vr::ETrackedPropertyError err;
1148 uint32_t requiredBufferLen;
1149 bool isFound = false;
1150 char charBuf[128];
1151 requiredBufferLen = mVRSystem->GetStringTrackedDeviceProperty(
1152 aDeviceIndex, ::vr::Prop_RenderModelName_String, charBuf, 128, &err);
1153 if (requiredBufferLen > 128) {
1154 MOZ_CRASH("Larger than the buffer size.")do { do { } while (false); MOZ_ReportCrash("" "Larger than the buffer size."
, "/var/lib/jenkins/workspace/firefox-scan-build/gfx/vr/service/OpenVRSession.cpp"
, 1154); AnnotateMozCrashReason("MOZ_CRASH(" "Larger than the buffer size."
")"); do { *((volatile int*)__null) = 1154; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
1155 }
1156 MOZ_ASSERT(requiredBufferLen && err == ::vr::TrackedProp_Success)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(requiredBufferLen && err == ::vr::TrackedProp_Success
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(requiredBufferLen && err == ::vr::TrackedProp_Success
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"requiredBufferLen && err == ::vr::TrackedProp_Success"
, "/var/lib/jenkins/workspace/firefox-scan-build/gfx/vr/service/OpenVRSession.cpp"
, 1156); AnnotateMozCrashReason("MOZ_ASSERT" "(" "requiredBufferLen && err == ::vr::TrackedProp_Success"
")"); do { *((volatile int*)__null) = 1156; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1157 nsCString deviceId(charBuf);
1158 if (deviceId.Find("vr_controller_vive") != kNotFound) {
1159 aId.AssignLiteral("OpenVR Gamepad");
1160 isFound = true;
1161 aControllerType = VRControllerType::HTCVive;
1162 } else if (deviceId.Find("knuckles") != kNotFound ||
1163 deviceId.Find("valve_controller_knu") != kNotFound) {
1164 aId.AssignLiteral("OpenVR Knuckles");
1165 isFound = true;
1166 aControllerType = VRControllerType::ValveIndex;
1167 } else if (deviceId.Find("vive_cosmos_controller") != kNotFound) {
1168 aId.AssignLiteral("OpenVR Cosmos");
1169 isFound = true;
1170 aControllerType = VRControllerType::HTCViveCosmos;
1171 }
1172 if (!isFound) {
1173 requiredBufferLen = mVRSystem->GetStringTrackedDeviceProperty(
1174 aDeviceIndex, ::vr::Prop_SerialNumber_String, charBuf, 128, &err);
1175 if (requiredBufferLen > 128) {
1176 MOZ_CRASH("Larger than the buffer size.")do { do { } while (false); MOZ_ReportCrash("" "Larger than the buffer size."
, "/var/lib/jenkins/workspace/firefox-scan-build/gfx/vr/service/OpenVRSession.cpp"
, 1176); AnnotateMozCrashReason("MOZ_CRASH(" "Larger than the buffer size."
")"); do { *((volatile int*)__null) = 1176; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
1177 }
1178 MOZ_ASSERT(requiredBufferLen && err == ::vr::TrackedProp_Success)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(requiredBufferLen && err == ::vr::TrackedProp_Success
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(requiredBufferLen && err == ::vr::TrackedProp_Success
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"requiredBufferLen && err == ::vr::TrackedProp_Success"
, "/var/lib/jenkins/workspace/firefox-scan-build/gfx/vr/service/OpenVRSession.cpp"
, 1178); AnnotateMozCrashReason("MOZ_ASSERT" "(" "requiredBufferLen && err == ::vr::TrackedProp_Success"
")"); do { *((volatile int*)__null) = 1178; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1179 deviceId.Assign(charBuf);
1180 if (deviceId.Find("MRSOURCE") != kNotFound) {
1181 aId.AssignLiteral("Spatial Controller (Spatial Interaction Source) ");
1182 mIsWindowsMR = true;
1183 isFound = true;
1184 aControllerType = VRControllerType::MSMR;
1185 }
1186 }
1187 if (!isFound) {
1188 aId.AssignLiteral("OpenVR Undefined");
1189 aControllerType = VRControllerType::_empty;
1190 }
1191 break;
1192 }
1193 case ::vr::TrackedDeviceClass_GenericTracker: {
1194 aId.AssignLiteral("OpenVR Tracker");
1195 aControllerType = VRControllerType::_empty;
1196 break;
1197 }
1198 default:
1199 MOZ_ASSERT(false)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false", "/var/lib/jenkins/workspace/firefox-scan-build/gfx/vr/service/OpenVRSession.cpp"
, 1199); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ")")
; do { *((volatile int*)__null) = 1199; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1200 break;
1201 }
1202}
1203
1204void OpenVRSession::StartFrame(mozilla::gfx::VRSystemState& aSystemState) {
1205 UpdateHeadsetPose(aSystemState);
1206 UpdateEyeParameters(aSystemState);
1207 EnumerateControllers(aSystemState);
1208
1209 vr::VRActiveActionSet_t actionSet = {0};
1210 actionSet.ulActionSet = mActionsetFirefox;
1211 vr::VRInput()->UpdateActionState(&actionSet, sizeof(actionSet), 1);
1212 UpdateControllerButtons(aSystemState);
1213 UpdateControllerPoses(aSystemState);
1214 UpdateTelemetry(aSystemState);
1215}
1216
1217void OpenVRSession::ProcessEvents(mozilla::gfx::VRSystemState& aSystemState) {
1218 bool isHmdPresent = ::vr::VR_IsHmdPresent();
1219 if (!isHmdPresent) {
1220 mShouldQuit = true;
1221 }
1222
1223 ::vr::VREvent_t event;
1224 while (mVRSystem && mVRSystem->PollNextEvent(&event, sizeof(event))) {
1225 switch (event.eventType) {
1226 case ::vr::VREvent_TrackedDeviceUserInteractionStarted:
1227 if (event.trackedDeviceIndex == ::vr::k_unTrackedDeviceIndex_Hmd) {
1228 aSystemState.displayState.isMounted = true;
1229 }
1230 break;
1231 case ::vr::VREvent_TrackedDeviceUserInteractionEnded:
1232 if (event.trackedDeviceIndex == ::vr::k_unTrackedDeviceIndex_Hmd) {
1233 aSystemState.displayState.isMounted = false;
1234 }
1235 break;
1236 case ::vr::EVREventType::VREvent_TrackedDeviceActivated:
1237 if (event.trackedDeviceIndex == ::vr::k_unTrackedDeviceIndex_Hmd) {
1238 aSystemState.displayState.isConnected = true;
1239 }
1240 break;
1241 case ::vr::EVREventType::VREvent_TrackedDeviceDeactivated:
1242 if (event.trackedDeviceIndex == ::vr::k_unTrackedDeviceIndex_Hmd) {
1243 aSystemState.displayState.isConnected = false;
1244 }
1245 break;
1246 case ::vr::EVREventType::VREvent_DriverRequestedQuit:
1247 case ::vr::EVREventType::VREvent_Quit:
1248 // When SteamVR runtime haven't been launched before viewing VR,
1249 // SteamVR will send a VREvent_ProcessQuit event. It will tell the parent
1250 // process to shutdown the VR process, and we need to avoid it.
1251 // case ::vr::EVREventType::VREvent_ProcessQuit:
1252 case ::vr::EVREventType::VREvent_QuitAcknowledged:
1253 mShouldQuit = true;
1254 break;
1255 default:
1256 // ignore
1257 break;
1258 }
1259 }
1260}
1261
1262#if defined(XP_WIN)
1263bool OpenVRSession::SubmitFrame(
1264 const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer,
1265 ID3D11Texture2D* aTexture) {
1266 return SubmitFrame((void*)aTexture, ::vr::ETextureType::TextureType_DirectX,
1267 aLayer.leftEyeRect, aLayer.rightEyeRect);
1268}
1269#elif defined(XP_MACOSX)
1270bool OpenVRSession::SubmitFrame(
1271 const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer,
1272 const VRLayerTextureHandle& aTexture) {
1273 return SubmitFrame(aTexture, ::vr::ETextureType::TextureType_IOSurface,
1274 aLayer.leftEyeRect, aLayer.rightEyeRect);
1275}
1276#endif
1277
1278bool OpenVRSession::SubmitFrame(const VRLayerTextureHandle& aTextureHandle,
1279 ::vr::ETextureType aTextureType,
1280 const VRLayerEyeRect& aLeftEyeRect,
1281 const VRLayerEyeRect& aRightEyeRect) {
1282 ::vr::Texture_t tex;
1283#if defined(XP_MACOSX)
1284 // We get aTextureHandle from get_SurfaceDescriptorMacIOSurface() at
1285 // VRDisplayExternal. scaleFactor and opaque are skipped because they always
1286 // are 1.0 and false.
1287 RefPtr<MacIOSurface> surf = MacIOSurface::LookupSurface(aTextureHandle);
1288 if (!surf) {
1289 NS_WARNING("OpenVRSession::SubmitFrame failed to get a MacIOSurface")NS_DebugBreak(NS_DEBUG_WARNING, "OpenVRSession::SubmitFrame failed to get a MacIOSurface"
, nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/gfx/vr/service/OpenVRSession.cpp"
, 1289)
;
1290 return false;
1291 }
1292
1293 CFTypeRefPtr<IOSurfaceRef> ioSurface = surf->GetIOSurfaceRef();
1294 tex.handle = (void*)ioSurface.get();
1295#else
1296 tex.handle = aTextureHandle;
1297#endif
1298 tex.eType = aTextureType;
1299 tex.eColorSpace = ::vr::EColorSpace::ColorSpace_Auto;
1300
1301 ::vr::VRTextureBounds_t bounds;
1302 bounds.uMin = aLeftEyeRect.x;
1303 bounds.vMin = 1.0 - aLeftEyeRect.y;
1304 bounds.uMax = aLeftEyeRect.x + aLeftEyeRect.width;
1305 bounds.vMax = 1.0 - (aLeftEyeRect.y + aLeftEyeRect.height);
1306
1307 ::vr::EVRCompositorError err;
1308 err = mVRCompositor->Submit(::vr::EVREye::Eye_Left, &tex, &bounds);
1309 if (err != ::vr::EVRCompositorError::VRCompositorError_None) {
1310 printf_stderr("OpenVR Compositor Submit() failed.\n");
1311 }
1312
1313 bounds.uMin = aRightEyeRect.x;
1314 bounds.vMin = 1.0 - aRightEyeRect.y;
1315 bounds.uMax = aRightEyeRect.x + aRightEyeRect.width;
1316 bounds.vMax = 1.0 - (aRightEyeRect.y + aRightEyeRect.height);
1317
1318 err = mVRCompositor->Submit(::vr::EVREye::Eye_Right, &tex, &bounds);
1319 if (err != ::vr::EVRCompositorError::VRCompositorError_None) {
1320 printf_stderr("OpenVR Compositor Submit() failed.\n");
1321 }
1322
1323 mVRCompositor->PostPresentHandoff();
1324 return true;
1325}
1326
1327void OpenVRSession::StopPresentation() {
1328 mVRCompositor->ClearLastSubmittedFrame();
1329
1330 ::vr::Compositor_CumulativeStats stats;
1331 mVRCompositor->GetCumulativeStats(&stats,
1332 sizeof(::vr::Compositor_CumulativeStats));
1333}
1334
1335bool OpenVRSession::StartPresentation() { return true; }
1336
1337void OpenVRSession::VibrateHaptic(uint32_t aControllerIdx,
1338 uint32_t aHapticIndex, float aIntensity,
1339 float aDuration) {
1340 MutexAutoLock lock(mControllerHapticStateMutex);
1341
1342 // Initilize the haptic thread when the first time to do vibration.
1343 if (!mHapticThread) {
1344 NS_DispatchToMainThread(NS_NewRunnableFunction(
1345 "OpenVRSession::StartHapticThread", [this]() { StartHapticThread(); }));
1346 }
1347 if (aHapticIndex >= kNumOpenVRHaptics ||
1348 aControllerIdx >= kVRControllerMaxCount) {
1349 return;
1350 }
1351
1352 const OpenVRHand role = mControllerDeviceIndex[aControllerIdx];
1353 if (role == OpenVRHand::None) {
1354 return;
1355 }
1356 mHapticPulseRemaining[aControllerIdx][aHapticIndex] = aDuration;
1357 mHapticPulseIntensity[aControllerIdx][aHapticIndex] = aIntensity;
1358}
1359
1360void OpenVRSession::StartHapticThread() {
1361 MOZ_ASSERT(NS_IsMainThread())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(NS_IsMainThread())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(NS_IsMainThread()))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("NS_IsMainThread()"
, "/var/lib/jenkins/workspace/firefox-scan-build/gfx/vr/service/OpenVRSession.cpp"
, 1361); AnnotateMozCrashReason("MOZ_ASSERT" "(" "NS_IsMainThread()"
")"); do { *((volatile int*)__null) = 1361; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1362 if (!mHapticThread) {
1363 mHapticThread = new VRThread("VR_OpenVR_Haptics"_ns);
1364 }
1365 mHapticThread->Start();
1366 StartHapticTimer();
1367}
1368
1369void OpenVRSession::StopHapticThread() {
1370 if (mHapticThread) {
1371 NS_DispatchToMainThread(NS_NewRunnableFunction(
1372 "mHapticThread::Shutdown",
1373 [thread = mHapticThread]() { thread->Shutdown(); }));
1374 mHapticThread = nullptr;
1375 }
1376}
1377
1378void OpenVRSession::StartHapticTimer() {
1379 if (!mHapticTimer && mHapticThread) {
1380 mLastHapticUpdate = TimeStamp();
1381 mHapticTimer = NS_NewTimer();
1382 nsCOMPtr<nsIThread> thread = mHapticThread->GetThread();
1383 mHapticTimer->SetTarget(thread);
1384 mHapticTimer->InitWithNamedFuncCallback(
1385 HapticTimerCallback, this, kVRHapticUpdateInterval,
1386 nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP,
1387 "OpenVRSession::HapticTimerCallback");
1388 }
1389}
1390
1391void OpenVRSession::StopHapticTimer() {
1392 if (mHapticTimer) {
1393 mHapticTimer->Cancel();
1394 mHapticTimer = nullptr;
1395 }
1396}
1397
1398/*static*/
1399void OpenVRSession::HapticTimerCallback(nsITimer* aTimer, void* aClosure) {
1400 /**
1401 * It is safe to use the pointer passed in aClosure to reference the
1402 * OpenVRSession object as the timer is canceled in OpenVRSession::Shutdown,
1403 * which is called by the OpenVRSession destructor, guaranteeing
1404 * that this function runs if and only if the VRManager object is valid.
1405 */
1406 OpenVRSession* self = static_cast<OpenVRSession*>(aClosure);
1407 MOZ_ASSERT(self)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(self)>::isValid, "invalid assertion condition"); if
((__builtin_expect(!!(!(!!(self))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("self", "/var/lib/jenkins/workspace/firefox-scan-build/gfx/vr/service/OpenVRSession.cpp"
, 1407); AnnotateMozCrashReason("MOZ_ASSERT" "(" "self" ")");
do { *((volatile int*)__null) = 1407; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1408 self->UpdateHaptics();
1409}
1410
1411void OpenVRSession::UpdateHaptics() {
1412 MOZ_ASSERT(mHapticThread->GetThread() == NS_GetCurrentThread())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mHapticThread->GetThread() == NS_GetCurrentThread
())>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(mHapticThread->GetThread() == NS_GetCurrentThread
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("mHapticThread->GetThread() == NS_GetCurrentThread()", "/var/lib/jenkins/workspace/firefox-scan-build/gfx/vr/service/OpenVRSession.cpp"
, 1412); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mHapticThread->GetThread() == NS_GetCurrentThread()"
")"); do { *((volatile int*)__null) = 1412; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1413 MOZ_ASSERT(mVRSystem)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mVRSystem)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mVRSystem))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("mVRSystem", "/var/lib/jenkins/workspace/firefox-scan-build/gfx/vr/service/OpenVRSession.cpp"
, 1413); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mVRSystem" ")"
); do { *((volatile int*)__null) = 1413; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1414
1415 MutexAutoLock lock(mControllerHapticStateMutex);
1416
1417 TimeStamp now = TimeStamp::Now();
1418 if (mLastHapticUpdate.IsNull()) {
1419 mLastHapticUpdate = now;
1420 return;
1421 }
1422 float deltaTime = (float)(now - mLastHapticUpdate).ToSeconds();
1423 mLastHapticUpdate = now;
1424
1425 for (uint32_t stateIndex = 0; stateIndex < kVRControllerMaxCount;
1426 ++stateIndex) {
1427 const OpenVRHand role = mControllerDeviceIndex[stateIndex];
1428 if (role == OpenVRHand::None) {
1429 continue;
1430 }
1431 for (uint32_t hapticIdx = 0; hapticIdx < kNumOpenVRHaptics; hapticIdx++) {
1432 float intensity = mHapticPulseIntensity[stateIndex][hapticIdx];
1433 float duration = mHapticPulseRemaining[stateIndex][hapticIdx];
1434 if (duration <= 0.0f || intensity <= 0.0f) {
1435 continue;
1436 }
1437 vr::VRInput()->TriggerHapticVibrationAction(
1438 mControllerHand[role].mActionHaptic.handle, 0.0f, deltaTime, 4.0f,
1439 intensity > 1.0f ? 1.0f : intensity, vr::k_ulInvalidInputValueHandle);
1440
1441 duration -= deltaTime;
1442 if (duration < 0.0f) {
1443 duration = 0.0f;
1444 }
1445 mHapticPulseRemaining[stateIndex][hapticIdx] = duration;
1446 }
1447 }
1448}
1449
1450void OpenVRSession::StopVibrateHaptic(uint32_t aControllerIdx) {
1451 MutexAutoLock lock(mControllerHapticStateMutex);
1452 if (aControllerIdx >= kVRControllerMaxCount) {
1453 return;
1454 }
1455 for (int iHaptic = 0; iHaptic < kNumOpenVRHaptics; iHaptic++) {
1456 mHapticPulseRemaining[aControllerIdx][iHaptic] = 0.0f;
1457 }
1458}
1459
1460void OpenVRSession::StopAllHaptics() {
1461 MutexAutoLock lock(mControllerHapticStateMutex);
1462 for (auto& controller : mHapticPulseRemaining) {
1463 for (auto& haptic : controller) {
1464 haptic = 0.0f;
1465 }
1466 }
1467}
1468
1469void OpenVRSession::UpdateTelemetry(VRSystemState& aSystemState) {
1470 ::vr::Compositor_CumulativeStats stats;
1471 mVRCompositor->GetCumulativeStats(&stats,
1472 sizeof(::vr::Compositor_CumulativeStats));
1473 aSystemState.displayState.droppedFrameCount = stats.m_nNumReprojectedFrames;
1474}
1475
1476} // namespace mozilla::gfx