Bug Summary

File:root/firefox-clang/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 -O2 -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=/root/firefox-clang/obj-x86_64-pc-linux-gnu/gfx/vr/service -fcoverage-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/gfx/vr/service -resource-dir /usr/lib/llvm-23/lib/clang/23 -include /root/firefox-clang/config/gcc_hidden.h -include /root/firefox-clang/obj-x86_64-pc-linux-gnu/mozilla-config.h -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/stl_wrappers -D _GLIBCXX_ASSERTIONS=1 -I /root/firefox-clang/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 MOZ_SUPPORT_LEAKCHECKING -D STATIC_EXPORTABLE_JS_API -I /root/firefox-clang/gfx/vr/service -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/gfx/vr/service -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/ipc/ipdl/_ipdlheaders -I /root/firefox-clang/ipc/chromium/src -I /root/firefox-clang/third_party/abseil-cpp -I /root/firefox-clang/toolkit/components/telemetry -I /root/firefox-clang/xpcom/base -I /root/firefox-clang/dom/base -I /root/firefox-clang/gfx/layers/d3d11 -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nss -D MOZILLA_CLIENT -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/x86_64-linux-gnu/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16/backward -internal-isystem /usr/lib/llvm-23/lib/clang/23/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-error=pessimizing-move -Wno-error=large-by-value-copy=128 -Wno-error=implicit-int-float-conversion -Wno-error=thread-safety-analysis -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-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 -Wno-character-conversion -std=gnu++20 -fdeprecated-macro -ferror-limit 19 -fstrict-flex-arrays=1 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fno-rtti -fgnuc-version=4.2.1 -fno-implicit-modules -fskip-odr-check-in-gmf -fno-sized-deallocation -fno-aligned-allocation -fdiagnostics-absolute-paths -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -mllvm -dwarf-linkage-names=Abstract -faddrsig -fdwarf2-cfi-asm -o /tmp/scan-build-2026-09-01-224014-2642839-1 -x c++ /root/firefox-clang/gfx/vr/service/OpenVRSession.cpp
1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5#include "OpenVRSession.h"
6
7#include <fstream>
8#include <numbers>
9
10#include "mozilla/ClearOnShutdown.h"
11#include "mozilla/JSONStringWriteFuncs.h"
12#include "mozilla/StaticPrefs_dom.h"
13#include "nsIThread.h"
14#include "nsString.h"
15
16#if defined(XP_WIN)
17# include <d3d11.h>
18
19# include "mozilla/gfx/DeviceManagerDx.h"
20#elif defined(XP_MACOSX)
21# include "mozilla/gfx/MacIOSurface.h"
22#endif
23
24#if !defined(XP_WIN)
25# include <sys/stat.h> // for umask()
26#endif
27
28#include "OpenVRCosmosMapper.h"
29#include "OpenVRDefaultMapper.h"
30#include "OpenVRKnucklesMapper.h"
31#include "OpenVRViveMapper.h"
32#include "binding/OpenVRCosmosBinding.h"
33#include "binding/OpenVRKnucklesBinding.h"
34#include "binding/OpenVRViveBinding.h"
35#include "mozilla/dom/GamepadBinding.h"
36#include "mozilla/dom/GamepadEventTypes.h"
37#if defined(XP_WIN) // Windows Mixed Reality is only available in Windows.
38# include "OpenVRWMRMapper.h"
39# include "binding/OpenVRWMRBinding.h"
40#endif
41
42#include "VRParent.h"
43#include "VRProcessChild.h"
44#include "VRThread.h"
45
46#if !defined(M_PI3.14159265358979323846)
47# define M_PI3.14159265358979323846 3.14159265358979323846264338327950288
48#endif
49
50#define BTN_MASK_FROM_ID(_id)::vr::ButtonMaskFromId(vr::EVRButtonId::_id) ::vr::ButtonMaskFromId(vr::EVRButtonId::_id)
51
52// Haptic feedback is updated every 5ms, as this is
53// the minimum period between new haptic pulse requests.
54// Effectively, this results in a pulse width modulation
55// with an interval of 5ms. Through experimentation, the
56// maximum duty cycle was found to be about 3.9ms
57const uint32_t kVRHapticUpdateInterval = 5;
58
59using namespace mozilla::gfx;
60
61namespace mozilla::gfx {
62
63namespace {
64
65class ControllerManifestFile {
66 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" ")", "/root/firefox-clang/gfx/vr/service/OpenVRSession.cpp"
, 66); AnnotateMozCrashReason("MOZ_ASSERT" "(" "int32_t(mRefCnt) >= 0"
") (" "illegal refcnt" ")"); do { MOZ_CrashSequence(__null, 66
); __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" ")", "/root/firefox-clang/gfx/vr/service/OpenVRSession.cpp"
, 66); AnnotateMozCrashReason("MOZ_ASSERT" "(" "int32_t(mRefCnt) > 0"
") (" "dup release" ")"); do { MOZ_CrashSequence(__null, 66)
; __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:
67
68 public:
69 static already_AddRefed<ControllerManifestFile> CreateManifest() {
70 RefPtr<ControllerManifestFile> manifest = new ControllerManifestFile();
71 return manifest.forget();
72 }
73
74 bool IsExisting() {
75 if (mFileName.IsEmpty() || !std::ifstream(mFileName.get()).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.get(); }
84
85 private:
86 ControllerManifestFile() = default;
87
88 ~ControllerManifestFile() {
89 if (!mFileName.IsEmpty() && remove(mFileName.get()) != 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."
")", "/root/firefox-clang/gfx/vr/service/OpenVRSession.cpp",
90); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") (" "Delete controller manifest file failed."
")"); do { MOZ_CrashSequence(__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", "/root/firefox-clang/gfx/vr/service/OpenVRSession.cpp"
, 122); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ")");
do { MOZ_CrashSequence(__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.get()).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, "/root/firefox-clang/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, "/root/firefox-clang/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, "/root/firefox-clang/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, "/root/firefox-clang/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, "/root/firefox-clang/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, "/root/firefox-clang/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.get());
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.get());
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.get());
349 if (knucklesBindingFile.is_open()) {
350 knucklesBindingFile << knucklesBinding.binding;
351 knucklesBindingFile.close();
352 }
353 }
354 if (vrParent->GetOpenVRControllerManifestPath(
355 VRControllerType::HTCViveCosmos, &output)) {
356 cosmosManifest = std::move(output);
357 }
358 if (!cosmosManifest.Length() || !FileIsExisting(cosmosManifest)) {
359 if (!GenerateTempFileName(cosmosManifest)) {
360 return false;
361 }
362 OpenVRCosmosBinding cosmosBinding;
363 std::ofstream cosmosBindingFile(cosmosManifest.get());
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.get());
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.get());
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.get());
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.get());
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] = std::move(leftContollerInfo);
566 mControllerHand[OpenVRHand::Right] = std::move(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.get());
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.get());
643 if (err != vr::VRInputError_None) {
644 NS_WARNING("OpenVR - SetActionManifestPath failed.")NS_DebugBreak(NS_DEBUG_WARNING, "OpenVR - SetActionManifestPath failed."
, nullptr, "/root/firefox-clang/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 = std::move(controllerAction),
654 viveManifest = std::move(viveManifest),
655 WMRManifest = std::move(WMRManifest),
656 knucklesManifest = std::move(knucklesManifest),
657 cosmosManifest = std::move(cosmosManifest)]() {
658 VRParent* vrParent = VRProcessChild::GetVRParent();
659 (void)vrParent->SendOpenVRControllerActionPathToParent(
660 controllerAction);
661 (void)vrParent->SendOpenVRControllerManifestPathToParent(
662 VRControllerType::HTCVive, viveManifest);
663 (void)vrParent->SendOpenVRControllerManifestPathToParent(
664 VRControllerType::MSMR, WMRManifest);
665 (void)vrParent->SendOpenVRControllerManifestPathToParent(
666 VRControllerType::ValveIndex, knucklesManifest);
667 (void)vrParent->SendOpenVRControllerManifestPathToParent(
668 VRControllerType::HTCViveCosmos, cosmosManifest);
669 }));
670 } else {
671 sControllerActionFile->SetFileName(controllerAction.get());
672 }
673
674 return true;
675}
676
677#if defined(XP_WIN)
678bool OpenVRSession::CreateD3DObjects() {
679 RefPtr<ID3D11Device> device = gfx::DeviceManagerDx::Get()->GetVRDevice();
680 if (!device) {
681 return false;
682 }
683 if (!CreateD3DContext(device)) {
684 return false;
685 }
686 return true;
687}
688#endif
689
690void OpenVRSession::Shutdown() {
691 StopHapticTimer();
692 StopHapticThread();
693 if (mVRSystem || mVRCompositor || mVRChaperone) {
694 ::vr::VR_Shutdown();
695 mVRCompositor = nullptr;
696 mVRChaperone = nullptr;
697 mVRSystem = nullptr;
698 }
699}
700
701bool OpenVRSession::InitState(VRSystemState& aSystemState) {
702 VRDisplayState& state = aSystemState.displayState;
703 strncpy(state.displayName.data(), "OpenVR HMD", kVRDisplayNameMaxLen);
704 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)(' '))
;
705 state.isConnected =
706 mVRSystem->IsTrackedDeviceConnected(::vr::k_unTrackedDeviceIndex_Hmd);
707 state.isMounted = false;
708 state.capabilityFlags =
709 (VRDisplayCapabilityFlags)((int)VRDisplayCapabilityFlags::Cap_None |
710 (int)
711 VRDisplayCapabilityFlags::Cap_Orientation |
712 (int)VRDisplayCapabilityFlags::Cap_Position |
713 (int)VRDisplayCapabilityFlags::Cap_External |
714 (int)VRDisplayCapabilityFlags::Cap_Present |
715 (int)VRDisplayCapabilityFlags::
716 Cap_StageParameters |
717 (int)
718 VRDisplayCapabilityFlags::Cap_ImmersiveVR);
719 state.blendMode = VRDisplayBlendMode::Opaque;
720 state.reportsDroppedFrames = true;
721
722 ::vr::ETrackedPropertyError err;
723 bool bHasProximitySensor = mVRSystem->GetBoolTrackedDeviceProperty(
724 ::vr::k_unTrackedDeviceIndex_Hmd, ::vr::Prop_ContainsProximitySensor_Bool,
725 &err);
726 if (err == ::vr::TrackedProp_Success && bHasProximitySensor) {
727 state.capabilityFlags =
728 (VRDisplayCapabilityFlags)((int)state.capabilityFlags |
729 (int)VRDisplayCapabilityFlags::
730 Cap_MountDetection);
731 }
732
733 uint32_t w, h;
734 mVRSystem->GetRecommendedRenderTargetSize(&w, &h);
735 state.eyeResolution.width = w;
736 state.eyeResolution.height = h;
737 state.nativeFramebufferScaleFactor = 1.0f;
738
739 // default to an identity quaternion
740 aSystemState.sensorState.pose.orientation[3] = 1.0f;
741
742 UpdateStageParameters(state);
743 UpdateEyeParameters(aSystemState);
744
745 VRHMDSensorState& sensorState = aSystemState.sensorState;
746 sensorState.flags =
747 (VRDisplayCapabilityFlags)((int)
748 VRDisplayCapabilityFlags::Cap_Orientation |
749 (int)VRDisplayCapabilityFlags::Cap_Position);
750 sensorState.pose.orientation[3] = 1.0f; // Default to an identity quaternion
751
752 return true;
753}
754
755void OpenVRSession::UpdateStageParameters(VRDisplayState& aState) {
756 float sizeX = 0.0f;
757 float sizeZ = 0.0f;
758 if (mVRChaperone->GetPlayAreaSize(&sizeX, &sizeZ)) {
759 ::vr::HmdMatrix34_t t =
760 mVRSystem->GetSeatedZeroPoseToStandingAbsoluteTrackingPose();
761 aState.stageSize.width = sizeX;
762 aState.stageSize.height = sizeZ;
763
764 aState.sittingToStandingTransform[0] = t.m[0][0];
765 aState.sittingToStandingTransform[1] = t.m[1][0];
766 aState.sittingToStandingTransform[2] = t.m[2][0];
767 aState.sittingToStandingTransform[3] = 0.0f;
768
769 aState.sittingToStandingTransform[4] = t.m[0][1];
770 aState.sittingToStandingTransform[5] = t.m[1][1];
771 aState.sittingToStandingTransform[6] = t.m[2][1];
772 aState.sittingToStandingTransform[7] = 0.0f;
773
774 aState.sittingToStandingTransform[8] = t.m[0][2];
775 aState.sittingToStandingTransform[9] = t.m[1][2];
776 aState.sittingToStandingTransform[10] = t.m[2][2];
777 aState.sittingToStandingTransform[11] = 0.0f;
778
779 aState.sittingToStandingTransform[12] = t.m[0][3];
780 aState.sittingToStandingTransform[13] = t.m[1][3];
781 aState.sittingToStandingTransform[14] = t.m[2][3];
782 aState.sittingToStandingTransform[15] = 1.0f;
783 } else {
784 // If we fail, fall back to reasonable defaults.
785 // 1m x 1m space, 0.75m high in seated position
786 aState.stageSize.width = 1.0f;
787 aState.stageSize.height = 1.0f;
788
789 aState.sittingToStandingTransform[0] = 1.0f;
790 aState.sittingToStandingTransform[1] = 0.0f;
791 aState.sittingToStandingTransform[2] = 0.0f;
792 aState.sittingToStandingTransform[3] = 0.0f;
793
794 aState.sittingToStandingTransform[4] = 0.0f;
795 aState.sittingToStandingTransform[5] = 1.0f;
796 aState.sittingToStandingTransform[6] = 0.0f;
797 aState.sittingToStandingTransform[7] = 0.0f;
798
799 aState.sittingToStandingTransform[8] = 0.0f;
800 aState.sittingToStandingTransform[9] = 0.0f;
801 aState.sittingToStandingTransform[10] = 1.0f;
802 aState.sittingToStandingTransform[11] = 0.0f;
803
804 aState.sittingToStandingTransform[12] = 0.0f;
805 aState.sittingToStandingTransform[13] = 0.75f;
806 aState.sittingToStandingTransform[14] = 0.0f;
807 aState.sittingToStandingTransform[15] = 1.0f;
808 }
809}
810
811void OpenVRSession::UpdateEyeParameters(VRSystemState& aState) {
812 // This must be called every frame in order to
813 // account for continuous adjustments to ipd.
814 gfx::Matrix4x4 headToEyeTransforms[2];
815
816 for (uint32_t eye = 0; eye < 2; ++eye) {
817 ::vr::HmdMatrix34_t eyeToHead =
818 mVRSystem->GetEyeToHeadTransform(static_cast<::vr::Hmd_Eye>(eye));
819 aState.displayState.eyeTranslation[eye].x = eyeToHead.m[0][3];
820 aState.displayState.eyeTranslation[eye].y = eyeToHead.m[1][3];
821 aState.displayState.eyeTranslation[eye].z = eyeToHead.m[2][3];
822
823 float left, right, up, down;
824 mVRSystem->GetProjectionRaw(static_cast<::vr::Hmd_Eye>(eye), &left, &right,
825 &up, &down);
826 aState.displayState.eyeFOV[eye].upDegrees =
827 atan(-up) * 180.0 / std::numbers::pi;
828 aState.displayState.eyeFOV[eye].rightDegrees =
829 atan(right) * 180.0 / std::numbers::pi;
830 aState.displayState.eyeFOV[eye].downDegrees =
831 atan(down) * 180.0 / std::numbers::pi;
832 aState.displayState.eyeFOV[eye].leftDegrees =
833 atan(-left) * 180.0 / std::numbers::pi;
834
835 Matrix4x4 pose;
836 // NOTE! eyeToHead.m is a 3x4 matrix, not 4x4. But
837 // because of its arrangement, we can copy the 12 elements in and
838 // then transpose them to the right place.
839 memcpy(&pose._11, &eyeToHead.m, sizeof(eyeToHead.m));
840 pose.Transpose();
841 pose.Invert();
842 headToEyeTransforms[eye] = pose;
843 }
844 aState.sensorState.CalcViewMatrices(headToEyeTransforms);
845}
846
847void OpenVRSession::UpdateHeadsetPose(VRSystemState& aState) {
848 const uint32_t posesSize = ::vr::k_unTrackedDeviceIndex_Hmd + 1;
849 ::vr::TrackedDevicePose_t poses[posesSize];
850 // Note: We *must* call WaitGetPoses in order for any rendering to happen at
851 // all.
852 mVRCompositor->WaitGetPoses(poses, posesSize, nullptr, 0);
853
854 ::vr::Compositor_FrameTiming timing;
855 timing.m_nSize = sizeof(::vr::Compositor_FrameTiming);
856 if (mVRCompositor->GetFrameTiming(&timing)) {
857 aState.sensorState.timestamp = timing.m_flSystemTimeInSeconds;
858 } else {
859 // This should not happen, but log it just in case
860 fprintf(stderrstderr, "OpenVR - IVRCompositor::GetFrameTiming failed");
861 }
862
863 if (poses[::vr::k_unTrackedDeviceIndex_Hmd].bDeviceIsConnected &&
864 poses[::vr::k_unTrackedDeviceIndex_Hmd].bPoseIsValid &&
865 poses[::vr::k_unTrackedDeviceIndex_Hmd].eTrackingResult ==
866 ::vr::TrackingResult_Running_OK) {
867 const ::vr::TrackedDevicePose_t& pose =
868 poses[::vr::k_unTrackedDeviceIndex_Hmd];
869
870 gfx::Matrix4x4 m;
871 // NOTE! mDeviceToAbsoluteTracking is a 3x4 matrix, not 4x4. But
872 // because of its arrangement, we can copy the 12 elements in and
873 // then transpose them to the right place. We do this so we can
874 // pull out a Quaternion.
875 memcpy(&m._11, &pose.mDeviceToAbsoluteTracking,
876 sizeof(pose.mDeviceToAbsoluteTracking));
877 m.Transpose();
878
879 gfx::Quaternion rot;
880 rot.SetFromRotationMatrix(m);
881
882 aState.sensorState.flags =
883 (VRDisplayCapabilityFlags)((int)aState.sensorState.flags |
884 (int)VRDisplayCapabilityFlags::
885 Cap_Orientation);
886 aState.sensorState.pose.orientation[0] = rot.x;
887 aState.sensorState.pose.orientation[1] = rot.y;
888 aState.sensorState.pose.orientation[2] = rot.z;
889 aState.sensorState.pose.orientation[3] = rot.w;
890 aState.sensorState.pose.angularVelocity[0] = pose.vAngularVelocity.v[0];
891 aState.sensorState.pose.angularVelocity[1] = pose.vAngularVelocity.v[1];
892 aState.sensorState.pose.angularVelocity[2] = pose.vAngularVelocity.v[2];
893
894 aState.sensorState.flags =
895 (VRDisplayCapabilityFlags)((int)aState.sensorState.flags |
896 (int)VRDisplayCapabilityFlags::Cap_Position);
897 aState.sensorState.pose.position[0] = m._41;
898 aState.sensorState.pose.position[1] = m._42;
899 aState.sensorState.pose.position[2] = m._43;
900 aState.sensorState.pose.linearVelocity[0] = pose.vVelocity.v[0];
901 aState.sensorState.pose.linearVelocity[1] = pose.vVelocity.v[1];
902 aState.sensorState.pose.linearVelocity[2] = pose.vVelocity.v[2];
903 }
904}
905
906void OpenVRSession::EnumerateControllers(VRSystemState& aState) {
907 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", "/root/firefox-clang/gfx/vr/service/OpenVRSession.cpp"
, 907); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mVRSystem" ")"
); do { MOZ_CrashSequence(__null, 907); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
908
909 MutexAutoLock lock(mControllerHapticStateMutex);
910
911 bool controllerPresent[kVRControllerMaxCount] = {false};
912 uint32_t stateIndex = 0;
913 mActionsetFirefox = vr::k_ulInvalidActionSetHandle;
914 VRControllerType controllerType = VRControllerType::_empty;
915
916 if (vr::VRInput()->GetActionSetHandle(
917 "/actions/firefox", &mActionsetFirefox) != vr::VRInputError_None) {
918 return;
919 }
920
921 for (int8_t handIndex = 0; handIndex < OpenVRHand::Total; ++handIndex) {
922 if (handIndex == OpenVRHand::Left) {
923 if (vr::VRInput()->GetInputSourceHandle(
924 "/user/hand/left", &mControllerHand[OpenVRHand::Left].mSource) !=
925 vr::VRInputError_None) {
926 continue;
927 }
928 } else if (handIndex == OpenVRHand::Right) {
929 if (vr::VRInput()->GetInputSourceHandle(
930 "/user/hand/right",
931 &mControllerHand[OpenVRHand::Right].mSource) !=
932 vr::VRInputError_None) {
933 continue;
934 }
935 } else {
936 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."
")", "/root/firefox-clang/gfx/vr/service/OpenVRSession.cpp",
936); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") (" "Unknown OpenVR hand type."
")"); do { MOZ_CrashSequence(__null, 936); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
937 }
938
939 vr::InputOriginInfo_t originInfo;
940 if (vr::VRInput()->GetOriginTrackedDeviceInfo(
941 mControllerHand[handIndex].mSource, &originInfo,
942 sizeof(originInfo)) == vr::VRInputError_None &&
943 originInfo.trackedDeviceIndex != vr::k_unTrackedDeviceIndexInvalid &&
944 mVRSystem->IsTrackedDeviceConnected(originInfo.trackedDeviceIndex)) {
945 const ::vr::ETrackedDeviceClass deviceType =
946 mVRSystem->GetTrackedDeviceClass(originInfo.trackedDeviceIndex);
947 if (deviceType != ::vr::TrackedDeviceClass_Controller &&
948 deviceType != ::vr::TrackedDeviceClass_GenericTracker) {
949 continue;
950 }
951
952 if (mControllerDeviceIndex[stateIndex] != handIndex) {
953 VRControllerState& controllerState = aState.controllerState[stateIndex];
954
955 // Get controllers' action handles.
956 auto SetActionsToWriter = [&](ControllerAction& aAction) {
957 vr::VRInput()->GetActionHandle(aAction.name.get(), &aAction.handle);
958 };
959
960 SetActionsToWriter(mControllerHand[handIndex].mActionPose);
961 SetActionsToWriter(mControllerHand[handIndex].mActionHaptic);
962 SetActionsToWriter(mControllerHand[handIndex].mActionTrackpad_Analog);
963 SetActionsToWriter(mControllerHand[handIndex].mActionTrackpad_Pressed);
964 SetActionsToWriter(mControllerHand[handIndex].mActionTrackpad_Touched);
965 SetActionsToWriter(mControllerHand[handIndex].mActionTrigger_Value);
966 SetActionsToWriter(mControllerHand[handIndex].mActionGrip_Pressed);
967 SetActionsToWriter(mControllerHand[handIndex].mActionGrip_Touched);
968 SetActionsToWriter(mControllerHand[handIndex].mActionMenu_Pressed);
969 SetActionsToWriter(mControllerHand[handIndex].mActionMenu_Touched);
970 SetActionsToWriter(mControllerHand[handIndex].mActionSystem_Pressed);
971 SetActionsToWriter(mControllerHand[handIndex].mActionSystem_Touched);
972 SetActionsToWriter(mControllerHand[handIndex].mActionA_Pressed);
973 SetActionsToWriter(mControllerHand[handIndex].mActionA_Touched);
974 SetActionsToWriter(mControllerHand[handIndex].mActionB_Pressed);
975 SetActionsToWriter(mControllerHand[handIndex].mActionB_Touched);
976 SetActionsToWriter(mControllerHand[handIndex].mActionThumbstick_Analog);
977 SetActionsToWriter(
978 mControllerHand[handIndex].mActionThumbstick_Pressed);
979 SetActionsToWriter(
980 mControllerHand[handIndex].mActionThumbstick_Touched);
981 SetActionsToWriter(mControllerHand[handIndex].mActionFingerIndex_Value);
982 SetActionsToWriter(
983 mControllerHand[handIndex].mActionFingerMiddle_Value);
984 SetActionsToWriter(mControllerHand[handIndex].mActionFingerRing_Value);
985 SetActionsToWriter(mControllerHand[handIndex].mActionFingerPinky_Value);
986 SetActionsToWriter(mControllerHand[handIndex].mActionBumper_Pressed);
987
988 nsCString deviceId;
989 VRControllerType contrlType = VRControllerType::_empty;
990 GetControllerDeviceId(deviceType, originInfo.trackedDeviceIndex,
991 deviceId, contrlType);
992 // Controllers should be the same type with one VR display.
993 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"
, "/root/firefox-clang/gfx/vr/service/OpenVRSession.cpp", 994
); AnnotateMozCrashReason("MOZ_ASSERT" "(" "controllerType == contrlType || controllerType == VRControllerType::_empty"
")"); do { MOZ_CrashSequence(__null, 994); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
994 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"
, "/root/firefox-clang/gfx/vr/service/OpenVRSession.cpp", 994
); AnnotateMozCrashReason("MOZ_ASSERT" "(" "controllerType == contrlType || controllerType == VRControllerType::_empty"
")"); do { MOZ_CrashSequence(__null, 994); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
995 controllerType = contrlType;
996 strncpy(controllerState.controllerName.data(), deviceId.BeginReading(),
997 controllerState.controllerName.size());
998 controllerState.numHaptics = kNumOpenVRHaptics;
999 controllerState.targetRayMode = gfx::TargetRayMode::TrackedPointer;
1000 controllerState.type = controllerType;
1001 }
1002 controllerPresent[stateIndex] = true;
1003 mControllerDeviceIndex[stateIndex] = static_cast<OpenVRHand>(handIndex);
1004 ++stateIndex;
1005 }
1006 }
1007
1008 // Clear out entries for disconnected controllers
1009 for (uint32_t stateIndex = 0; stateIndex < kVRControllerMaxCount;
1010 stateIndex++) {
1011 if (!controllerPresent[stateIndex] &&
1012 mControllerDeviceIndex[stateIndex] != OpenVRHand::None) {
1013 mControllerDeviceIndex[stateIndex] = OpenVRHand::None;
1014 memset(&aState.controllerState[stateIndex], 0, sizeof(VRControllerState));
1015 }
1016 }
1017
1018 // Create controller mapper
1019 if (controllerType != VRControllerType::_empty) {
1020 switch (controllerType) {
1021 case VRControllerType::HTCVive:
1022 mControllerMapper = MakeUnique<OpenVRViveMapper>();
1023 break;
1024 case VRControllerType::HTCViveCosmos:
1025 mControllerMapper = MakeUnique<OpenVRCosmosMapper>();
1026 break;
1027#if defined(XP_WIN)
1028 case VRControllerType::MSMR:
1029 mControllerMapper = MakeUnique<OpenVRWMRMapper>();
1030 break;
1031#endif
1032 case VRControllerType::ValveIndex:
1033 mControllerMapper = MakeUnique<OpenVRKnucklesMapper>();
1034 break;
1035 default:
1036 mControllerMapper = MakeUnique<OpenVRDefaultMapper>();
1037 NS_WARNING("Undefined controller type")NS_DebugBreak(NS_DEBUG_WARNING, "Undefined controller type", nullptr
, "/root/firefox-clang/gfx/vr/service/OpenVRSession.cpp", 1037
)
;
1038 break;
1039 }
1040 }
1041}
1042
1043void OpenVRSession::UpdateControllerButtons(VRSystemState& aState) {
1044 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", "/root/firefox-clang/gfx/vr/service/OpenVRSession.cpp"
, 1044); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mVRSystem" ")"
); do { MOZ_CrashSequence(__null, 1044); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1045
1046 for (uint32_t stateIndex = 0; stateIndex < kVRControllerMaxCount;
1047 ++stateIndex) {
1048 const OpenVRHand role = mControllerDeviceIndex[stateIndex];
1049 if (role == OpenVRHand::None) {
1050 continue;
1051 }
1052 VRControllerState& controllerState = aState.controllerState[stateIndex];
1053 controllerState.hand = GetControllerHandFromControllerRole(role);
1054 mControllerMapper->UpdateButtons(controllerState, mControllerHand[role]);
1055 SetControllerSelectionAndSqueezeFrameId(
1056 controllerState, aState.displayState.lastSubmittedFrameId);
1057 }
1058}
1059
1060void OpenVRSession::UpdateControllerPoses(VRSystemState& aState) {
1061 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", "/root/firefox-clang/gfx/vr/service/OpenVRSession.cpp"
, 1061); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mVRSystem" ")"
); do { MOZ_CrashSequence(__null, 1061); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1062
1063 for (uint32_t stateIndex = 0; stateIndex < kVRControllerMaxCount;
1064 ++stateIndex) {
1065 const OpenVRHand role = mControllerDeviceIndex[stateIndex];
1066 if (role == OpenVRHand::None) {
1067 continue;
1068 }
1069 VRControllerState& controllerState = aState.controllerState[stateIndex];
1070 vr::InputPoseActionData_t poseData;
1071 if (vr::VRInput()->GetPoseActionDataRelativeToNow(
1072 mControllerHand[role].mActionPose.handle,
1073 vr::TrackingUniverseSeated, 0, &poseData, sizeof(poseData),
1074 vr::k_ulInvalidInputValueHandle) != vr::VRInputError_None ||
1075 !poseData.bActive || !poseData.pose.bPoseIsValid) {
1076 controllerState.isOrientationValid = false;
1077 controllerState.isPositionValid = false;
1078 } else {
1079 const ::vr::TrackedDevicePose_t& pose = poseData.pose;
1080 if (pose.bDeviceIsConnected) {
1081 controllerState.flags =
1082 (dom::GamepadCapabilityFlags::Cap_Orientation |
1083 dom::GamepadCapabilityFlags::Cap_Position |
1084 dom::GamepadCapabilityFlags::Cap_GripSpacePosition);
1085 } else {
1086 controllerState.flags = dom::GamepadCapabilityFlags::Cap_None;
1087 }
1088 if (pose.bPoseIsValid &&
1089 pose.eTrackingResult == ::vr::TrackingResult_Running_OK) {
1090 gfx::Matrix4x4 m;
1091
1092 // NOTE! mDeviceToAbsoluteTracking is a 3x4 matrix, not 4x4. But
1093 // because of its arrangement, we can copy the 12 elements in and
1094 // then transpose them to the right place. We do this so we can
1095 // pull out a Quaternion.
1096 memcpy(&m.components, &pose.mDeviceToAbsoluteTracking,
1097 sizeof(pose.mDeviceToAbsoluteTracking));
1098 m.Transpose();
1099
1100 gfx::Quaternion rot;
1101 rot.SetFromRotationMatrix(m);
1102
1103 controllerState.pose.orientation[0] = rot.x;
1104 controllerState.pose.orientation[1] = rot.y;
1105 controllerState.pose.orientation[2] = rot.z;
1106 controllerState.pose.orientation[3] = rot.w;
1107 controllerState.pose.angularVelocity[0] = pose.vAngularVelocity.v[0];
1108 controllerState.pose.angularVelocity[1] = pose.vAngularVelocity.v[1];
1109 controllerState.pose.angularVelocity[2] = pose.vAngularVelocity.v[2];
1110 controllerState.pose.angularAcceleration[0] = 0.0f;
1111 controllerState.pose.angularAcceleration[1] = 0.0f;
1112 controllerState.pose.angularAcceleration[2] = 0.0f;
1113 controllerState.isOrientationValid = true;
1114
1115 controllerState.pose.position[0] = m._41;
1116 controllerState.pose.position[1] = m._42;
1117 controllerState.pose.position[2] = m._43;
1118 controllerState.pose.linearVelocity[0] = pose.vVelocity.v[0];
1119 controllerState.pose.linearVelocity[1] = pose.vVelocity.v[1];
1120 controllerState.pose.linearVelocity[2] = pose.vVelocity.v[2];
1121 controllerState.pose.linearAcceleration[0] = 0.0f;
1122 controllerState.pose.linearAcceleration[1] = 0.0f;
1123 controllerState.pose.linearAcceleration[2] = 0.0f;
1124 controllerState.isPositionValid = true;
1125
1126 // Calculate its target ray space by shifting degrees in x-axis
1127 // for ergonomic.
1128 const float kPointerAngleDegrees = -0.698; // 40 degrees.
1129 gfx::Matrix4x4 rayMtx(m);
1130 rayMtx.RotateX(kPointerAngleDegrees);
1131 gfx::Quaternion rayRot;
1132 rayRot.SetFromRotationMatrix(rayMtx);
1133
1134 controllerState.targetRayPose = controllerState.pose;
1135 controllerState.targetRayPose.orientation[0] = rayRot.x;
1136 controllerState.targetRayPose.orientation[1] = rayRot.y;
1137 controllerState.targetRayPose.orientation[2] = rayRot.z;
1138 controllerState.targetRayPose.orientation[3] = rayRot.w;
1139 controllerState.targetRayPose.position[0] = rayMtx._41;
1140 controllerState.targetRayPose.position[1] = rayMtx._42;
1141 controllerState.targetRayPose.position[2] = rayMtx._43;
1142 }
1143 }
1144 }
1145}
1146
1147void OpenVRSession::GetControllerDeviceId(
1148 ::vr::ETrackedDeviceClass aDeviceType,
1149 ::vr::TrackedDeviceIndex_t aDeviceIndex, nsCString& aId,
1150 VRControllerType& aControllerType) {
1151 switch (aDeviceType) {
1152 case ::vr::TrackedDeviceClass_Controller: {
1153 ::vr::ETrackedPropertyError err;
1154 uint32_t requiredBufferLen;
1155 bool isFound = false;
1156 char charBuf[128];
1157 requiredBufferLen = mVRSystem->GetStringTrackedDeviceProperty(
1158 aDeviceIndex, ::vr::Prop_RenderModelName_String, charBuf, 128, &err);
1159 if (requiredBufferLen > 128) {
1160 MOZ_CRASH("Larger than the buffer size.")do { do { } while (false); MOZ_ReportCrash("" "Larger than the buffer size."
, "/root/firefox-clang/gfx/vr/service/OpenVRSession.cpp", 1160
); AnnotateMozCrashReason("MOZ_CRASH(" "Larger than the buffer size."
")"); do { MOZ_CrashSequence(__null, 1160); __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
1161 }
1162 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"
, "/root/firefox-clang/gfx/vr/service/OpenVRSession.cpp", 1162
); AnnotateMozCrashReason("MOZ_ASSERT" "(" "requiredBufferLen && err == ::vr::TrackedProp_Success"
")"); do { MOZ_CrashSequence(__null, 1162); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1163 nsCString deviceId(charBuf);
1164 if (deviceId.Find("vr_controller_vive") != kNotFound) {
1165 aId.AssignLiteral("OpenVR Gamepad");
1166 isFound = true;
1167 aControllerType = VRControllerType::HTCVive;
1168 } else if (deviceId.Find("knuckles") != kNotFound ||
1169 deviceId.Find("valve_controller_knu") != kNotFound) {
1170 aId.AssignLiteral("OpenVR Knuckles");
1171 isFound = true;
1172 aControllerType = VRControllerType::ValveIndex;
1173 } else if (deviceId.Find("vive_cosmos_controller") != kNotFound) {
1174 aId.AssignLiteral("OpenVR Cosmos");
1175 isFound = true;
1176 aControllerType = VRControllerType::HTCViveCosmos;
1177 }
1178 if (!isFound) {
1179 requiredBufferLen = mVRSystem->GetStringTrackedDeviceProperty(
1180 aDeviceIndex, ::vr::Prop_SerialNumber_String, charBuf, 128, &err);
1181 if (requiredBufferLen > 128) {
1182 MOZ_CRASH("Larger than the buffer size.")do { do { } while (false); MOZ_ReportCrash("" "Larger than the buffer size."
, "/root/firefox-clang/gfx/vr/service/OpenVRSession.cpp", 1182
); AnnotateMozCrashReason("MOZ_CRASH(" "Larger than the buffer size."
")"); do { MOZ_CrashSequence(__null, 1182); __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
1183 }
1184 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"
, "/root/firefox-clang/gfx/vr/service/OpenVRSession.cpp", 1184
); AnnotateMozCrashReason("MOZ_ASSERT" "(" "requiredBufferLen && err == ::vr::TrackedProp_Success"
")"); do { MOZ_CrashSequence(__null, 1184); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1185 deviceId.Assign(charBuf);
1186 if (deviceId.Find("MRSOURCE") != kNotFound) {
1187 aId.AssignLiteral("Spatial Controller (Spatial Interaction Source) ");
1188 mIsWindowsMR = true;
1189 isFound = true;
1190 aControllerType = VRControllerType::MSMR;
1191 }
1192 }
1193 if (!isFound) {
1194 aId.AssignLiteral("OpenVR Undefined");
1195 aControllerType = VRControllerType::_empty;
1196 }
1197 break;
1198 }
1199 case ::vr::TrackedDeviceClass_GenericTracker: {
1200 aId.AssignLiteral("OpenVR Tracker");
1201 aControllerType = VRControllerType::_empty;
1202 break;
1203 }
1204 default:
1205 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", "/root/firefox-clang/gfx/vr/service/OpenVRSession.cpp"
, 1205); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ")")
; do { MOZ_CrashSequence(__null, 1205); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1206 break;
1207 }
1208}
1209
1210void OpenVRSession::StartFrame(mozilla::gfx::VRSystemState& aSystemState) {
1211 UpdateHeadsetPose(aSystemState);
1212 UpdateEyeParameters(aSystemState);
1213 EnumerateControllers(aSystemState);
1214
1215 vr::VRActiveActionSet_t actionSet = {0};
1216 actionSet.ulActionSet = mActionsetFirefox;
1217 vr::VRInput()->UpdateActionState(&actionSet, sizeof(actionSet), 1);
1218 UpdateControllerButtons(aSystemState);
1219 UpdateControllerPoses(aSystemState);
1220 UpdateTelemetry(aSystemState);
1221}
1222
1223void OpenVRSession::ProcessEvents(mozilla::gfx::VRSystemState& aSystemState) {
1224 bool isHmdPresent = ::vr::VR_IsHmdPresent();
1225 if (!isHmdPresent) {
1226 mShouldQuit = true;
1227 }
1228
1229 ::vr::VREvent_t event;
1230 while (mVRSystem && mVRSystem->PollNextEvent(&event, sizeof(event))) {
1231 switch (event.eventType) {
1232 case ::vr::VREvent_TrackedDeviceUserInteractionStarted:
1233 if (event.trackedDeviceIndex == ::vr::k_unTrackedDeviceIndex_Hmd) {
1234 aSystemState.displayState.isMounted = true;
1235 }
1236 break;
1237 case ::vr::VREvent_TrackedDeviceUserInteractionEnded:
1238 if (event.trackedDeviceIndex == ::vr::k_unTrackedDeviceIndex_Hmd) {
1239 aSystemState.displayState.isMounted = false;
1240 }
1241 break;
1242 case ::vr::EVREventType::VREvent_TrackedDeviceActivated:
1243 if (event.trackedDeviceIndex == ::vr::k_unTrackedDeviceIndex_Hmd) {
1244 aSystemState.displayState.isConnected = true;
1245 }
1246 break;
1247 case ::vr::EVREventType::VREvent_TrackedDeviceDeactivated:
1248 if (event.trackedDeviceIndex == ::vr::k_unTrackedDeviceIndex_Hmd) {
1249 aSystemState.displayState.isConnected = false;
1250 }
1251 break;
1252 case ::vr::EVREventType::VREvent_DriverRequestedQuit:
1253 case ::vr::EVREventType::VREvent_Quit:
1254 // When SteamVR runtime haven't been launched before viewing VR,
1255 // SteamVR will send a VREvent_ProcessQuit event. It will tell the parent
1256 // process to shutdown the VR process, and we need to avoid it.
1257 // case ::vr::EVREventType::VREvent_ProcessQuit:
1258 case ::vr::EVREventType::VREvent_QuitAcknowledged:
1259 mShouldQuit = true;
1260 break;
1261 default:
1262 // ignore
1263 break;
1264 }
1265 }
1266}
1267
1268#if defined(XP_WIN)
1269bool OpenVRSession::SubmitFrame(
1270 const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer,
1271 ID3D11Texture2D* aTexture) {
1272 return SubmitFrame((void*)aTexture, ::vr::ETextureType::TextureType_DirectX,
1273 aLayer.leftEyeRect, aLayer.rightEyeRect);
1274}
1275#elif defined(XP_MACOSX)
1276bool OpenVRSession::SubmitFrame(
1277 const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer,
1278 const VRLayerTextureHandle& aTexture) {
1279 return SubmitFrame(aTexture, ::vr::ETextureType::TextureType_IOSurface,
1280 aLayer.leftEyeRect, aLayer.rightEyeRect);
1281}
1282#endif
1283
1284bool OpenVRSession::SubmitFrame(const VRLayerTextureHandle& aTextureHandle,
1285 ::vr::ETextureType aTextureType,
1286 const VRLayerEyeRect& aLeftEyeRect,
1287 const VRLayerEyeRect& aRightEyeRect) {
1288 ::vr::Texture_t tex;
1289#if defined(XP_MACOSX)
1290 // We get aTextureHandle from get_SurfaceDescriptorMacIOSurface() at
1291 // VRDisplayExternal. scaleFactor and opaque are skipped because they always
1292 // are 1.0 and false.
1293 RefPtr<MacIOSurface> surf = MacIOSurface::LookupSurface(
1294 aTextureHandle, gfx::YUVColorSpace::Identity, gfx::TransferFunction::SRGB,
1295 MacIOSurface::AllowAlpha::No);
1296 if (!surf) {
1297 NS_WARNING("OpenVRSession::SubmitFrame failed to get a MacIOSurface")NS_DebugBreak(NS_DEBUG_WARNING, "OpenVRSession::SubmitFrame failed to get a MacIOSurface"
, nullptr, "/root/firefox-clang/gfx/vr/service/OpenVRSession.cpp"
, 1297)
;
1298 return false;
1299 }
1300
1301 CFTypeRefPtr<IOSurfaceRef> ioSurface = surf->GetIOSurfaceRef();
1302 tex.handle = (void*)ioSurface.get();
1303#else
1304 tex.handle = aTextureHandle;
1305#endif
1306 tex.eType = aTextureType;
1307 tex.eColorSpace = ::vr::EColorSpace::ColorSpace_Auto;
1308
1309 ::vr::VRTextureBounds_t bounds;
1310 bounds.uMin = aLeftEyeRect.x;
1311 bounds.vMin = 1.0 - aLeftEyeRect.y;
1312 bounds.uMax = aLeftEyeRect.x + aLeftEyeRect.width;
1313 bounds.vMax = 1.0 - (aLeftEyeRect.y + aLeftEyeRect.height);
1314
1315 ::vr::EVRCompositorError err;
1316 err = mVRCompositor->Submit(::vr::EVREye::Eye_Left, &tex, &bounds);
1317 if (err != ::vr::EVRCompositorError::VRCompositorError_None) {
1318 printf_stderr("OpenVR Compositor Submit() failed.\n");
1319 }
1320
1321 bounds.uMin = aRightEyeRect.x;
1322 bounds.vMin = 1.0 - aRightEyeRect.y;
1323 bounds.uMax = aRightEyeRect.x + aRightEyeRect.width;
1324 bounds.vMax = 1.0 - (aRightEyeRect.y + aRightEyeRect.height);
1325
1326 err = mVRCompositor->Submit(::vr::EVREye::Eye_Right, &tex, &bounds);
1327 if (err != ::vr::EVRCompositorError::VRCompositorError_None) {
1328 printf_stderr("OpenVR Compositor Submit() failed.\n");
1329 }
1330
1331 mVRCompositor->PostPresentHandoff();
1332 return true;
1333}
1334
1335void OpenVRSession::StopPresentation() {
1336 mVRCompositor->ClearLastSubmittedFrame();
1337
1338 ::vr::Compositor_CumulativeStats stats;
1339 mVRCompositor->GetCumulativeStats(&stats,
1340 sizeof(::vr::Compositor_CumulativeStats));
1341}
1342
1343bool OpenVRSession::StartPresentation() { return true; }
1344
1345void OpenVRSession::VibrateHaptic(uint32_t aControllerIdx,
1346 uint32_t aHapticIndex, float aIntensity,
1347 float aDuration) {
1348 MutexAutoLock lock(mControllerHapticStateMutex);
1349
1350 // Initilize the haptic thread when the first time to do vibration.
1351 if (!mHapticThread) {
1352 NS_DispatchToMainThread(NS_NewRunnableFunction(
1353 "OpenVRSession::StartHapticThread", [this]() { StartHapticThread(); }));
1354 }
1355 if (aHapticIndex >= kNumOpenVRHaptics ||
1356 aControllerIdx >= kVRControllerMaxCount) {
1357 return;
1358 }
1359
1360 const OpenVRHand role = mControllerDeviceIndex[aControllerIdx];
1361 if (role == OpenVRHand::None) {
1362 return;
1363 }
1364 mHapticPulseRemaining[aControllerIdx][aHapticIndex] = aDuration;
1365 mHapticPulseIntensity[aControllerIdx][aHapticIndex] = aIntensity;
1366}
1367
1368void OpenVRSession::StartHapticThread() {
1369 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()"
, "/root/firefox-clang/gfx/vr/service/OpenVRSession.cpp", 1369
); AnnotateMozCrashReason("MOZ_ASSERT" "(" "NS_IsMainThread()"
")"); do { MOZ_CrashSequence(__null, 1369); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1370 if (!mHapticThread) {
1371 mHapticThread = new VRThread("VR_OpenVR_Haptics"_ns);
1372 }
1373 mHapticThread->Start();
1374 StartHapticTimer();
1375}
1376
1377void OpenVRSession::StopHapticThread() {
1378 if (mHapticThread) {
1379 NS_DispatchToMainThread(NS_NewRunnableFunction(
1380 "mHapticThread::Shutdown",
1381 [thread = mHapticThread]() { thread->Shutdown(); }));
1382 mHapticThread = nullptr;
1383 }
1384}
1385
1386void OpenVRSession::StartHapticTimer() {
1387 if (!mHapticTimer && mHapticThread) {
1388 mLastHapticUpdate = TimeStamp();
1389 mHapticTimer = NS_NewTimer();
1390 nsCOMPtr<nsIThread> thread = mHapticThread->GetThread();
1391 mHapticTimer->SetTarget(thread);
1392 mHapticTimer->InitWithNamedFuncCallback(
1393 HapticTimerCallback, this, kVRHapticUpdateInterval,
1394 nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP,
1395 "OpenVRSession::HapticTimerCallback"_ns);
1396 }
1397}
1398
1399void OpenVRSession::StopHapticTimer() {
1400 if (mHapticTimer) {
1401 mHapticTimer->Cancel();
1402 mHapticTimer = nullptr;
1403 }
1404}
1405
1406/*static*/
1407void OpenVRSession::HapticTimerCallback(nsITimer* aTimer, void* aClosure) {
1408 /**
1409 * It is safe to use the pointer passed in aClosure to reference the
1410 * OpenVRSession object as the timer is canceled in OpenVRSession::Shutdown,
1411 * which is called by the OpenVRSession destructor, guaranteeing
1412 * that this function runs if and only if the VRManager object is valid.
1413 */
1414 OpenVRSession* self = static_cast<OpenVRSession*>(aClosure);
1415 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", "/root/firefox-clang/gfx/vr/service/OpenVRSession.cpp"
, 1415); AnnotateMozCrashReason("MOZ_ASSERT" "(" "self" ")");
do { MOZ_CrashSequence(__null, 1415); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1416 self->UpdateHaptics();
1417}
1418
1419void OpenVRSession::UpdateHaptics() {
1420 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()", "/root/firefox-clang/gfx/vr/service/OpenVRSession.cpp"
, 1420); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mHapticThread->GetThread() == NS_GetCurrentThread()"
")"); do { MOZ_CrashSequence(__null, 1420); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1421 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", "/root/firefox-clang/gfx/vr/service/OpenVRSession.cpp"
, 1421); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mVRSystem" ")"
); do { MOZ_CrashSequence(__null, 1421); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1422
1423 MutexAutoLock lock(mControllerHapticStateMutex);
1424
1425 TimeStamp now = TimeStamp::Now();
1426 if (mLastHapticUpdate.IsNull()) {
1427 mLastHapticUpdate = now;
1428 return;
1429 }
1430 float deltaTime = (float)(now - mLastHapticUpdate).ToSeconds();
1431 mLastHapticUpdate = now;
1432
1433 for (uint32_t stateIndex = 0; stateIndex < kVRControllerMaxCount;
1434 ++stateIndex) {
1435 const OpenVRHand role = mControllerDeviceIndex[stateIndex];
1436 if (role == OpenVRHand::None) {
1437 continue;
1438 }
1439 for (uint32_t hapticIdx = 0; hapticIdx < kNumOpenVRHaptics; hapticIdx++) {
1440 float intensity = mHapticPulseIntensity[stateIndex][hapticIdx];
1441 float duration = mHapticPulseRemaining[stateIndex][hapticIdx];
1442 if (duration <= 0.0f || intensity <= 0.0f) {
1443 continue;
1444 }
1445 vr::VRInput()->TriggerHapticVibrationAction(
1446 mControllerHand[role].mActionHaptic.handle, 0.0f, deltaTime, 4.0f,
1447 intensity > 1.0f ? 1.0f : intensity, vr::k_ulInvalidInputValueHandle);
1448
1449 duration -= deltaTime;
1450 if (duration < 0.0f) {
1451 duration = 0.0f;
1452 }
1453 mHapticPulseRemaining[stateIndex][hapticIdx] = duration;
1454 }
1455 }
1456}
1457
1458void OpenVRSession::StopVibrateHaptic(uint32_t aControllerIdx) {
1459 MutexAutoLock lock(mControllerHapticStateMutex);
1460 if (aControllerIdx >= kVRControllerMaxCount) {
1461 return;
1462 }
1463 for (int iHaptic = 0; iHaptic < kNumOpenVRHaptics; iHaptic++) {
1464 mHapticPulseRemaining[aControllerIdx][iHaptic] = 0.0f;
1465 }
1466}
1467
1468void OpenVRSession::StopAllHaptics() {
1469 MutexAutoLock lock(mControllerHapticStateMutex);
1470 for (auto& controller : mHapticPulseRemaining) {
1471 for (auto& haptic : controller) {
1472 haptic = 0.0f;
1473 }
1474 }
1475}
1476
1477void OpenVRSession::UpdateTelemetry(VRSystemState& aSystemState) {
1478 ::vr::Compositor_CumulativeStats stats;
1479 mVRCompositor->GetCumulativeStats(&stats,
1480 sizeof(::vr::Compositor_CumulativeStats));
1481 aSystemState.displayState.droppedFrameCount = stats.m_nNumReprojectedFrames;
1482}
1483
1484} // namespace mozilla::gfx