OSVRSession.cpp (18948B)
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 "OSVRSession.h" 8 #include "prenv.h" 9 #include "nsString.h" 10 #include "mozilla/Preferences.h" 11 #include "mozilla/StaticPrefs_dom.h" 12 #include "mozilla/SharedLibrary.h" 13 #include "mozilla/gfx/Quaternion.h" 14 15 #if defined(XP_WIN) 16 # include <d3d11.h> 17 # include "mozilla/gfx/DeviceManagerDx.h" 18 #endif // defined(XP_WIN) 19 20 #ifndef M_PI 21 # define M_PI 3.14159265358979323846 22 #endif 23 24 using namespace mozilla; 25 using namespace mozilla::gfx; 26 27 namespace { 28 // need to typedef functions that will be used in the code below 29 extern "C" { 30 typedef OSVR_ClientContext (*pfn_osvrClientInit)( 31 const char applicationIdentifier[], uint32_t flags); 32 typedef OSVR_ReturnCode (*pfn_osvrClientShutdown)(OSVR_ClientContext ctx); 33 typedef OSVR_ReturnCode (*pfn_osvrClientUpdate)(OSVR_ClientContext ctx); 34 typedef OSVR_ReturnCode (*pfn_osvrClientCheckStatus)(OSVR_ClientContext ctx); 35 typedef OSVR_ReturnCode (*pfn_osvrClientGetInterface)( 36 OSVR_ClientContext ctx, const char path[], OSVR_ClientInterface* iface); 37 typedef OSVR_ReturnCode (*pfn_osvrClientFreeInterface)( 38 OSVR_ClientContext ctx, OSVR_ClientInterface iface); 39 typedef OSVR_ReturnCode (*pfn_osvrGetOrientationState)( 40 OSVR_ClientInterface iface, OSVR_TimeValue* timestamp, 41 OSVR_OrientationState* state); 42 typedef OSVR_ReturnCode (*pfn_osvrGetPositionState)(OSVR_ClientInterface iface, 43 OSVR_TimeValue* timestamp, 44 OSVR_PositionState* state); 45 typedef OSVR_ReturnCode (*pfn_osvrClientGetDisplay)(OSVR_ClientContext ctx, 46 OSVR_DisplayConfig* disp); 47 typedef OSVR_ReturnCode (*pfn_osvrClientFreeDisplay)(OSVR_DisplayConfig disp); 48 typedef OSVR_ReturnCode (*pfn_osvrClientGetNumEyesForViewer)( 49 OSVR_DisplayConfig disp, OSVR_ViewerCount viewer, OSVR_EyeCount* eyes); 50 typedef OSVR_ReturnCode (*pfn_osvrClientGetViewerEyePose)( 51 OSVR_DisplayConfig disp, OSVR_ViewerCount viewer, OSVR_EyeCount eye, 52 OSVR_Pose3* pose); 53 typedef OSVR_ReturnCode (*pfn_osvrClientGetDisplayDimensions)( 54 OSVR_DisplayConfig disp, OSVR_DisplayInputCount displayInputIndex, 55 OSVR_DisplayDimension* width, OSVR_DisplayDimension* height); 56 typedef OSVR_ReturnCode ( 57 *pfn_osvrClientGetViewerEyeSurfaceProjectionClippingPlanes)( 58 OSVR_DisplayConfig disp, OSVR_ViewerCount viewer, OSVR_EyeCount eye, 59 OSVR_SurfaceCount surface, double* left, double* right, double* bottom, 60 double* top); 61 typedef OSVR_ReturnCode (*pfn_osvrClientGetRelativeViewportForViewerEyeSurface)( 62 OSVR_DisplayConfig disp, OSVR_ViewerCount viewer, OSVR_EyeCount eye, 63 OSVR_SurfaceCount surface, OSVR_ViewportDimension* left, 64 OSVR_ViewportDimension* bottom, OSVR_ViewportDimension* width, 65 OSVR_ViewportDimension* height); 66 typedef OSVR_ReturnCode (*pfn_osvrClientGetViewerEyeSurfaceProjectionMatrixf)( 67 OSVR_DisplayConfig disp, OSVR_ViewerCount viewer, OSVR_EyeCount eye, 68 OSVR_SurfaceCount surface, float near, float far, 69 OSVR_MatrixConventions flags, float* matrix); 70 typedef OSVR_ReturnCode (*pfn_osvrClientCheckDisplayStartup)( 71 OSVR_DisplayConfig disp); 72 typedef OSVR_ReturnCode (*pfn_osvrClientSetRoomRotationUsingHead)( 73 OSVR_ClientContext ctx); 74 } 75 76 static pfn_osvrClientInit osvr_ClientInit = nullptr; 77 static pfn_osvrClientShutdown osvr_ClientShutdown = nullptr; 78 static pfn_osvrClientUpdate osvr_ClientUpdate = nullptr; 79 static pfn_osvrClientCheckStatus osvr_ClientCheckStatus = nullptr; 80 static pfn_osvrClientGetInterface osvr_ClientGetInterface = nullptr; 81 static pfn_osvrClientFreeInterface osvr_ClientFreeInterface = nullptr; 82 static pfn_osvrGetOrientationState osvr_GetOrientationState = nullptr; 83 static pfn_osvrGetPositionState osvr_GetPositionState = nullptr; 84 static pfn_osvrClientGetDisplay osvr_ClientGetDisplay = nullptr; 85 static pfn_osvrClientFreeDisplay osvr_ClientFreeDisplay = nullptr; 86 static pfn_osvrClientGetNumEyesForViewer osvr_ClientGetNumEyesForViewer = 87 nullptr; 88 static pfn_osvrClientGetViewerEyePose osvr_ClientGetViewerEyePose = nullptr; 89 static pfn_osvrClientGetDisplayDimensions osvr_ClientGetDisplayDimensions = 90 nullptr; 91 static pfn_osvrClientGetViewerEyeSurfaceProjectionClippingPlanes 92 osvr_ClientGetViewerEyeSurfaceProjectionClippingPlanes = nullptr; 93 static pfn_osvrClientGetRelativeViewportForViewerEyeSurface 94 osvr_ClientGetRelativeViewportForViewerEyeSurface = nullptr; 95 static pfn_osvrClientGetViewerEyeSurfaceProjectionMatrixf 96 osvr_ClientGetViewerEyeSurfaceProjectionMatrixf = nullptr; 97 static pfn_osvrClientCheckDisplayStartup osvr_ClientCheckDisplayStartup = 98 nullptr; 99 static pfn_osvrClientSetRoomRotationUsingHead 100 osvr_ClientSetRoomRotationUsingHead = nullptr; 101 102 bool LoadOSVRRuntime() { 103 static PRLibrary* osvrUtilLib = nullptr; 104 static PRLibrary* osvrCommonLib = nullptr; 105 static PRLibrary* osvrClientLib = nullptr; 106 static PRLibrary* osvrClientKitLib = nullptr; 107 // this looks up the path in the about:config setting, from greprefs.js or 108 // modules\libpref\init\all.js we need all the libs to be valid 109 #ifdef XP_WIN 110 constexpr static auto* pfnGetPathStringPref = mozilla::Preferences::GetString; 111 nsAutoString osvrUtilPath, osvrCommonPath, osvrClientPath, osvrClientKitPath; 112 #else 113 constexpr static auto* pfnGetPathStringPref = 114 mozilla::Preferences::GetCString; 115 nsAutoCString osvrUtilPath, osvrCommonPath, osvrClientPath, osvrClientKitPath; 116 #endif 117 if (NS_FAILED(pfnGetPathStringPref("gfx.vr.osvr.utilLibPath", osvrUtilPath, 118 PrefValueKind::User)) || 119 NS_FAILED(pfnGetPathStringPref("gfx.vr.osvr.commonLibPath", 120 osvrCommonPath, PrefValueKind::User)) || 121 NS_FAILED(pfnGetPathStringPref("gfx.vr.osvr.clientLibPath", 122 osvrClientPath, PrefValueKind::User)) || 123 NS_FAILED(pfnGetPathStringPref("gfx.vr.osvr.clientKitLibPath", 124 osvrClientKitPath, PrefValueKind::User))) { 125 return false; 126 } 127 128 osvrUtilLib = LoadLibraryWithFlags(osvrUtilPath.get()); 129 osvrCommonLib = LoadLibraryWithFlags(osvrCommonPath.get()); 130 osvrClientLib = LoadLibraryWithFlags(osvrClientPath.get()); 131 osvrClientKitLib = LoadLibraryWithFlags(osvrClientKitPath.get()); 132 133 if (!osvrUtilLib) { 134 printf_stderr("[OSVR] Failed to load OSVR Util library!\n"); 135 return false; 136 } 137 if (!osvrCommonLib) { 138 printf_stderr("[OSVR] Failed to load OSVR Common library!\n"); 139 return false; 140 } 141 if (!osvrClientLib) { 142 printf_stderr("[OSVR] Failed to load OSVR Client library!\n"); 143 return false; 144 } 145 if (!osvrClientKitLib) { 146 printf_stderr("[OSVR] Failed to load OSVR ClientKit library!\n"); 147 return false; 148 } 149 150 // make sure all functions that we'll be using are available 151 #define REQUIRE_FUNCTION(_x) \ 152 do { \ 153 *(void**)&osvr_##_x = (void*)PR_FindSymbol(osvrClientKitLib, "osvr" #_x); \ 154 if (!osvr_##_x) { \ 155 printf_stderr("osvr" #_x " symbol missing\n"); \ 156 goto fail; \ 157 } \ 158 } while (0) 159 160 REQUIRE_FUNCTION(ClientInit); 161 REQUIRE_FUNCTION(ClientShutdown); 162 REQUIRE_FUNCTION(ClientUpdate); 163 REQUIRE_FUNCTION(ClientCheckStatus); 164 REQUIRE_FUNCTION(ClientGetInterface); 165 REQUIRE_FUNCTION(ClientFreeInterface); 166 REQUIRE_FUNCTION(GetOrientationState); 167 REQUIRE_FUNCTION(GetPositionState); 168 REQUIRE_FUNCTION(ClientGetDisplay); 169 REQUIRE_FUNCTION(ClientFreeDisplay); 170 REQUIRE_FUNCTION(ClientGetNumEyesForViewer); 171 REQUIRE_FUNCTION(ClientGetViewerEyePose); 172 REQUIRE_FUNCTION(ClientGetDisplayDimensions); 173 REQUIRE_FUNCTION(ClientGetViewerEyeSurfaceProjectionClippingPlanes); 174 REQUIRE_FUNCTION(ClientGetRelativeViewportForViewerEyeSurface); 175 REQUIRE_FUNCTION(ClientGetViewerEyeSurfaceProjectionMatrixf); 176 REQUIRE_FUNCTION(ClientCheckDisplayStartup); 177 REQUIRE_FUNCTION(ClientSetRoomRotationUsingHead); 178 179 #undef REQUIRE_FUNCTION 180 181 return true; 182 183 fail: 184 return false; 185 } 186 187 } // namespace 188 189 mozilla::gfx::VRFieldOfView SetFromTanRadians(double left, double right, 190 double bottom, double top) { 191 mozilla::gfx::VRFieldOfView fovInfo; 192 fovInfo.leftDegrees = atan(left) * 180.0 / M_PI; 193 fovInfo.rightDegrees = atan(right) * 180.0 / M_PI; 194 fovInfo.upDegrees = atan(top) * 180.0 / M_PI; 195 fovInfo.downDegrees = atan(bottom) * 180.0 / M_PI; 196 return fovInfo; 197 } 198 199 OSVRSession::OSVRSession() 200 : mRuntimeLoaded(false), 201 mOSVRInitialized(false), 202 mClientContextInitialized(false), 203 mDisplayConfigInitialized(false), 204 mInterfaceInitialized(false), 205 m_ctx(nullptr), 206 m_iface(nullptr), 207 m_display(nullptr) {} 208 OSVRSession::~OSVRSession() { Shutdown(); } 209 210 bool OSVRSession::Initialize(mozilla::gfx::VRSystemState& aSystemState, 211 bool aDetectRuntimesOnly) { 212 if (StaticPrefs::dom_vr_puppet_enabled()) { 213 // Ensure that tests using the VR Puppet do not find real hardware 214 return false; 215 } 216 if (!StaticPrefs::dom_vr_enabled() || !StaticPrefs::dom_vr_osvr_enabled()) { 217 return false; 218 } 219 if (mOSVRInitialized) { 220 return true; 221 } 222 if (!LoadOSVRRuntime()) { 223 return false; 224 } 225 mRuntimeLoaded = true; 226 227 if (aDetectRuntimesOnly) { 228 aSystemState.displayState.capabilityFlags |= 229 VRDisplayCapabilityFlags::Cap_ImmersiveVR; 230 return false; 231 } 232 233 // initialize client context 234 InitializeClientContext(); 235 // try to initialize interface 236 InitializeInterface(); 237 // try to initialize display object 238 InitializeDisplay(); 239 // verify all components are initialized 240 CheckOSVRStatus(); 241 242 if (!mOSVRInitialized) { 243 return false; 244 } 245 246 if (!InitState(aSystemState)) { 247 return false; 248 } 249 250 return true; 251 } 252 253 void OSVRSession::CheckOSVRStatus() { 254 if (mOSVRInitialized) { 255 return; 256 } 257 258 // client context must be initialized first 259 InitializeClientContext(); 260 261 // update client context 262 osvr_ClientUpdate(m_ctx); 263 264 // initialize interface and display if they are not initialized yet 265 InitializeInterface(); 266 InitializeDisplay(); 267 268 // OSVR is fully initialized now 269 if (mClientContextInitialized && mDisplayConfigInitialized && 270 mInterfaceInitialized) { 271 mOSVRInitialized = true; 272 } 273 } 274 275 void OSVRSession::InitializeClientContext() { 276 // already initialized 277 if (mClientContextInitialized) { 278 return; 279 } 280 281 // first time creating 282 if (!m_ctx) { 283 // get client context 284 m_ctx = osvr_ClientInit("com.osvr.webvr", 0); 285 // update context 286 osvr_ClientUpdate(m_ctx); 287 // verify we are connected 288 if (OSVR_RETURN_SUCCESS == osvr_ClientCheckStatus(m_ctx)) { 289 mClientContextInitialized = true; 290 } 291 } 292 // client context exists but not up and running yet 293 else { 294 // update context 295 osvr_ClientUpdate(m_ctx); 296 if (OSVR_RETURN_SUCCESS == osvr_ClientCheckStatus(m_ctx)) { 297 mClientContextInitialized = true; 298 } 299 } 300 } 301 302 void OSVRSession::InitializeInterface() { 303 // already initialized 304 if (mInterfaceInitialized) { 305 return; 306 } 307 // Client context must be initialized before getting interface 308 if (mClientContextInitialized) { 309 // m_iface will remain nullptr if no interface is returned 310 if (OSVR_RETURN_SUCCESS == 311 osvr_ClientGetInterface(m_ctx, "/me/head", &m_iface)) { 312 mInterfaceInitialized = true; 313 } 314 } 315 } 316 317 void OSVRSession::InitializeDisplay() { 318 // display is fully configured 319 if (mDisplayConfigInitialized) { 320 return; 321 } 322 323 // Client context must be initialized before getting interface 324 if (mClientContextInitialized) { 325 // first time creating display object 326 if (m_display == nullptr) { 327 OSVR_ReturnCode ret = osvr_ClientGetDisplay(m_ctx, &m_display); 328 329 if (ret == OSVR_RETURN_SUCCESS) { 330 osvr_ClientUpdate(m_ctx); 331 // display object may have been created but not fully startup 332 if (OSVR_RETURN_SUCCESS == osvr_ClientCheckDisplayStartup(m_display)) { 333 mDisplayConfigInitialized = true; 334 } 335 } 336 337 // Typically once we get Display object, pose data is available after 338 // clientUpdate but sometimes it takes ~ 200 ms to get 339 // a succesfull connection, so we might have to run a few update cycles 340 } else { 341 if (OSVR_RETURN_SUCCESS == osvr_ClientCheckDisplayStartup(m_display)) { 342 mDisplayConfigInitialized = true; 343 } 344 } 345 } 346 } 347 348 bool OSVRSession::InitState(mozilla::gfx::VRSystemState& aSystemState) { 349 VRDisplayState& state = aSystemState.displayState; 350 strncpy(state.displayName.data(), "OSVR HMD", kVRDisplayNameMaxLen); 351 state.eightCC = GFX_VR_EIGHTCC('O', 'S', 'V', 'R', ' ', ' ', ' ', ' '); 352 state.isConnected = true; 353 state.isMounted = false; 354 state.capabilityFlags = 355 (VRDisplayCapabilityFlags)((int)VRDisplayCapabilityFlags::Cap_None | 356 (int) 357 VRDisplayCapabilityFlags::Cap_Orientation | 358 (int)VRDisplayCapabilityFlags::Cap_Position | 359 (int)VRDisplayCapabilityFlags::Cap_External | 360 (int)VRDisplayCapabilityFlags::Cap_Present | 361 (int) 362 VRDisplayCapabilityFlags::Cap_ImmersiveVR); 363 state.blendMode = VRDisplayBlendMode::Opaque; 364 state.reportsDroppedFrames = false; 365 366 // XXX OSVR display topology allows for more than one viewer 367 // will assume only one viewer for now (most likely stay that way) 368 369 OSVR_EyeCount numEyes; 370 osvr_ClientGetNumEyesForViewer(m_display, 0, &numEyes); 371 372 for (uint8_t eye = 0; eye < numEyes; eye++) { 373 double left, right, bottom, top; 374 // XXX for now there is only one surface per eye 375 osvr_ClientGetViewerEyeSurfaceProjectionClippingPlanes( 376 m_display, 0, eye, 0, &left, &right, &bottom, &top); 377 state.eyeFOV[eye] = SetFromTanRadians(-left, right, -bottom, top); 378 } 379 380 // XXX Assuming there is only one display input for now 381 // however, it's possible to have more than one (dSight with 2 HDMI inputs) 382 OSVR_DisplayDimension width, height; 383 osvr_ClientGetDisplayDimensions(m_display, 0, &width, &height); 384 385 for (uint8_t eye = 0; eye < numEyes; eye++) { 386 OSVR_ViewportDimension l, b, w, h; 387 osvr_ClientGetRelativeViewportForViewerEyeSurface(m_display, 0, eye, 0, &l, 388 &b, &w, &h); 389 state.eyeResolution.width = w; 390 state.eyeResolution.height = h; 391 OSVR_Pose3 eyePose; 392 // Viewer eye pose may not be immediately available, update client context 393 // until we get it 394 OSVR_ReturnCode ret = 395 osvr_ClientGetViewerEyePose(m_display, 0, eye, &eyePose); 396 while (ret != OSVR_RETURN_SUCCESS) { 397 osvr_ClientUpdate(m_ctx); 398 ret = osvr_ClientGetViewerEyePose(m_display, 0, eye, &eyePose); 399 } 400 state.eyeTranslation[eye].x = eyePose.translation.data[0]; 401 state.eyeTranslation[eye].y = eyePose.translation.data[1]; 402 state.eyeTranslation[eye].z = eyePose.translation.data[2]; 403 404 Matrix4x4 pose; 405 pose.SetRotationFromQuaternion(gfx::Quaternion( 406 osvrQuatGetX(&eyePose.rotation), osvrQuatGetY(&eyePose.rotation), 407 osvrQuatGetZ(&eyePose.rotation), osvrQuatGetW(&eyePose.rotation))); 408 pose.PreTranslate(eyePose.translation.data[0], eyePose.translation.data[1], 409 eyePose.translation.data[2]); 410 pose.Invert(); 411 mHeadToEye[eye] = pose; 412 } 413 414 // default to an identity quaternion 415 VRHMDSensorState& sensorState = aSystemState.sensorState; 416 sensorState.flags = 417 (VRDisplayCapabilityFlags)((int) 418 VRDisplayCapabilityFlags::Cap_Orientation | 419 (int)VRDisplayCapabilityFlags::Cap_Position); 420 sensorState.pose.orientation[3] = 1.0f; // Default to an identity quaternion 421 422 return true; 423 } 424 425 void OSVRSession::Shutdown() { 426 if (!mRuntimeLoaded) { 427 return; 428 } 429 mOSVRInitialized = false; 430 // client context may not have been initialized 431 if (m_ctx) { 432 osvr_ClientFreeDisplay(m_display); 433 } 434 // osvr checks that m_ctx or m_iface are not null 435 osvr_ClientFreeInterface(m_ctx, m_iface); 436 osvr_ClientShutdown(m_ctx); 437 } 438 439 void OSVRSession::ProcessEvents(mozilla::gfx::VRSystemState& aSystemState) {} 440 441 void OSVRSession::StartFrame(mozilla::gfx::VRSystemState& aSystemState) { 442 UpdateHeadsetPose(aSystemState); 443 } 444 445 void OSVRSession::UpdateHeadsetPose(mozilla::gfx::VRSystemState& aState) { 446 // Update client context before anything 447 // this usually goes into app's mainloop 448 osvr_ClientUpdate(m_ctx); 449 450 VRHMDSensorState result{}; 451 OSVR_TimeValue timestamp; 452 453 OSVR_OrientationState orientation; 454 455 OSVR_ReturnCode ret = 456 osvr_GetOrientationState(m_iface, ×tamp, &orientation); 457 458 aState.sensorState.timestamp = timestamp.seconds; 459 460 if (ret == OSVR_RETURN_SUCCESS) { 461 result.flags |= VRDisplayCapabilityFlags::Cap_Orientation; 462 result.pose.orientation[0] = orientation.data[1]; 463 result.pose.orientation[1] = orientation.data[2]; 464 result.pose.orientation[2] = orientation.data[3]; 465 result.pose.orientation[3] = orientation.data[0]; 466 } else { 467 // default to an identity quaternion 468 result.pose.orientation[3] = 1.0f; 469 } 470 471 OSVR_PositionState position; 472 ret = osvr_GetPositionState(m_iface, ×tamp, &position); 473 if (ret == OSVR_RETURN_SUCCESS) { 474 result.flags |= VRDisplayCapabilityFlags::Cap_Position; 475 result.pose.position[0] = position.data[0]; 476 result.pose.position[1] = position.data[1]; 477 result.pose.position[2] = position.data[2]; 478 } 479 480 result.CalcViewMatrices(mHeadToEye); 481 } 482 483 bool OSVRSession::StartPresentation() { 484 return false; 485 // TODO Implement 486 } 487 488 void OSVRSession::StopPresentation() { 489 // TODO Implement 490 } 491 492 #if defined(XP_WIN) 493 bool OSVRSession::SubmitFrame( 494 const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer, 495 ID3D11Texture2D* aTexture) { 496 return false; 497 // TODO Implement 498 } 499 #elif defined(XP_MACOSX) 500 bool OSVRSession::SubmitFrame( 501 const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer, 502 const VRLayerTextureHandle& aTexture) { 503 return false; 504 // TODO Implement 505 } 506 #endif 507 508 void OSVRSession::VibrateHaptic(uint32_t aControllerIdx, uint32_t aHapticIndex, 509 float aIntensity, float aDuration) {} 510 511 void OSVRSession::StopVibrateHaptic(uint32_t aControllerIdx) {} 512 513 void OSVRSession::StopAllHaptics() {}