Bug 1598491 - Implement VR process function to scan for XR device runtime installations r=daoshengmu
authorKearwood "Kip" Gilbert <kgilbert@mozilla.com>
Fri, 22 Nov 2019 01:13:28 +0000
changeset 566641 1e4ac843ccbb2fbbbd97f48f6c921d78da0a98aa
parent 566640 3b2676056584688eb47f8ad9b2b4070fe7ca00c8
child 566642 304b3bb77a10849177629cc19dfbe502c971ad1d
push id12351
push userffxbld-merge
push dateMon, 02 Dec 2019 11:32:26 +0000
treeherdermozilla-beta@dba4410526a2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdaoshengmu
bugs1598491
milestone72.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1598491 - Implement VR process function to scan for XR device runtime installations r=daoshengmu This is a dependency of the WebXR implementation. In order to support WebXR's navigator.xr.IsSessionSupported call without displaying any permission dialogue, it is necessary to have a safe way to detect the capability of running a VR or AR session without activating XR runtimes or powering on hardware. API's such as OpenVR make no guarantee that hardware and software won't be left activated after enumerating devices, so each backend in gfx/vr/service must allow for more granular detection of capabilities. By passing true to bDetectRuntimesOnly, the initialization exits early after reporting the presence of XR runtime software. The Initialize method will only enumerate hardware and possibly return true when aDetectRuntimesOnly is false. Differential Revision: https://phabricator.services.mozilla.com/D54234
gfx/vr/service/OSVRSession.cpp
gfx/vr/service/OSVRSession.h
gfx/vr/service/OculusSession.cpp
gfx/vr/service/OculusSession.h
gfx/vr/service/OpenVRSession.cpp
gfx/vr/service/OpenVRSession.h
gfx/vr/service/PuppetSession.cpp
gfx/vr/service/PuppetSession.h
gfx/vr/service/VRService.cpp
gfx/vr/service/VRSession.h
--- a/gfx/vr/service/OSVRSession.cpp
+++ b/gfx/vr/service/OSVRSession.cpp
@@ -203,28 +203,36 @@ OSVRSession::OSVRSession()
       mClientContextInitialized(false),
       mDisplayConfigInitialized(false),
       mInterfaceInitialized(false),
       m_ctx(nullptr),
       m_iface(nullptr),
       m_display(nullptr) {}
 OSVRSession::~OSVRSession() { Shutdown(); }
 
-bool OSVRSession::Initialize(mozilla::gfx::VRSystemState& aSystemState) {
+bool OSVRSession::Initialize(mozilla::gfx::VRSystemState& aSystemState,
+                             bool aDetectRuntimesOnly) {
   if (!StaticPrefs::dom_vr_enabled() ||
       !StaticPrefs::dom_vr_osvr_enabled_AtStartup()) {
     return false;
   }
   if (mOSVRInitialized) {
     return true;
   }
   if (!LoadOSVRRuntime()) {
     return false;
   }
   mRuntimeLoaded = true;
+
+  if (aDetectRuntimesOnly) {
+    aSystemState.displayState.capabilityFlags |=
+        VRDisplayCapabilityFlags::Cap_ImmersiveVR;
+    return false;
+  }
+
   // initialize client context
   InitializeClientContext();
   // try to initialize interface
   InitializeInterface();
   // try to initialize display object
   InitializeDisplay();
   // verify all components are initialized
   CheckOSVRStatus();
--- a/gfx/vr/service/OSVRSession.h
+++ b/gfx/vr/service/OSVRSession.h
@@ -23,17 +23,18 @@
 namespace mozilla {
 namespace gfx {
 
 class OSVRSession : public VRSession {
  public:
   OSVRSession();
   virtual ~OSVRSession();
 
-  bool Initialize(mozilla::gfx::VRSystemState& aSystemState) override;
+  bool Initialize(mozilla::gfx::VRSystemState& aSystemState,
+                  bool aDetectRuntimesOnly) override;
   void Shutdown() override;
   void ProcessEvents(mozilla::gfx::VRSystemState& aSystemState) override;
   void StartFrame(mozilla::gfx::VRSystemState& aSystemState) override;
   bool StartPresentation() override;
   void StopPresentation() override;
   void VibrateHaptic(uint32_t aControllerIdx, uint32_t aHapticIndex,
                      float aIntensity, float aDuration) override;
   void StopVibrateHaptic(uint32_t aControllerIdx) override;
--- a/gfx/vr/service/OculusSession.cpp
+++ b/gfx/vr/service/OculusSession.cpp
@@ -200,22 +200,31 @@ OculusSession::OculusSession()
       mVertexBuffer(nullptr),
       mInputLayout(nullptr),
       mRemainingVibrateTime{},
       mHapticPulseIntensity{},
       mIsPresenting(false) {}
 
 OculusSession::~OculusSession() { Shutdown(); }
 
-bool OculusSession::Initialize(mozilla::gfx::VRSystemState& aSystemState) {
+bool OculusSession::Initialize(mozilla::gfx::VRSystemState& aSystemState,
+                               bool aDetectRuntimesOnly) {
   if (!StaticPrefs::dom_vr_enabled() ||
       !StaticPrefs::dom_vr_oculus_enabled_AtStartup()) {
     return false;
   }
 
+  if (aDetectRuntimesOnly) {
+    if (LoadOvrLib()) {
+      aSystemState.displayState.capabilityFlags |=
+          VRDisplayCapabilityFlags::Cap_ImmersiveVR;
+    }
+    return false;
+  }
+
   if (!CreateD3DObjects()) {
     return false;
   }
   if (!CreateShaders()) {
     return false;
   }
   // Ideally, we should move LoadOvrLib() up to the first line to avoid
   // unnecessary D3D objects creation. But it will cause a WPT fail in Win 7
--- a/gfx/vr/service/OculusSession.h
+++ b/gfx/vr/service/OculusSession.h
@@ -25,17 +25,18 @@ struct PixelShaderConstants;
 }  // namespace layers
 namespace gfx {
 
 class OculusSession : public VRSession {
  public:
   OculusSession();
   virtual ~OculusSession();
 
-  bool Initialize(mozilla::gfx::VRSystemState& aSystemState) override;
+  bool Initialize(mozilla::gfx::VRSystemState& aSystemState,
+                  bool aDetectRuntimesOnly) override;
   void Shutdown() override;
   void ProcessEvents(mozilla::gfx::VRSystemState& aSystemState) override;
   void StartFrame(mozilla::gfx::VRSystemState& aSystemState) override;
   bool StartPresentation() override;
   void StopPresentation() override;
   bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer,
                    ID3D11Texture2D* aTexture) override;
   void VibrateHaptic(uint32_t aControllerIdx, uint32_t aHapticIndex,
--- a/gfx/vr/service/OpenVRSession.cpp
+++ b/gfx/vr/service/OpenVRSession.cpp
@@ -246,28 +246,34 @@ OpenVRSession::OpenVRSession()
   std::fill_n(mControllerDeviceIndex, kVRControllerMaxCount, OpenVRHand::None);
 }
 
 OpenVRSession::~OpenVRSession() {
   mActionsetFirefox = ::vr::k_ulInvalidActionSetHandle;
   Shutdown();
 }
 
-bool OpenVRSession::Initialize(mozilla::gfx::VRSystemState& aSystemState) {
+bool OpenVRSession::Initialize(mozilla::gfx::VRSystemState& aSystemState,
+                               bool aDetectRuntimesOnly) {
   if (!StaticPrefs::dom_vr_enabled() ||
       !StaticPrefs::dom_vr_openvr_enabled_AtStartup()) {
     return false;
   }
   if (mVRSystem != nullptr) {
     // Already initialized
     return true;
   }
   if (!::vr::VR_IsRuntimeInstalled()) {
     return false;
   }
+  if (aDetectRuntimesOnly) {
+    aSystemState.displayState.capabilityFlags |=
+        VRDisplayCapabilityFlags::Cap_ImmersiveVR;
+    return false;
+  }
   if (!::vr::VR_IsHmdPresent()) {
     return false;
   }
 
   ::vr::HmdError err;
 
   ::vr::VR_Init(&err, ::vr::EVRApplicationType::VRApplication_Scene);
   if (err) {
--- a/gfx/vr/service/OpenVRSession.h
+++ b/gfx/vr/service/OpenVRSession.h
@@ -83,17 +83,18 @@ struct ControllerInfo {
   ControllerAction mActionBumper_Pressed;
 };
 
 class OpenVRSession : public VRSession {
  public:
   OpenVRSession();
   virtual ~OpenVRSession();
 
-  bool Initialize(mozilla::gfx::VRSystemState& aSystemState) override;
+  bool Initialize(mozilla::gfx::VRSystemState& aSystemState,
+                  bool aDetectRuntimesOnly) override;
   void Shutdown() override;
   void ProcessEvents(mozilla::gfx::VRSystemState& aSystemState) override;
   void StartFrame(mozilla::gfx::VRSystemState& aSystemState) override;
   bool StartPresentation() override;
   void StopPresentation() override;
   void VibrateHaptic(uint32_t aControllerIdx, uint32_t aHapticIndex,
                      float aIntensity, float aDuration) override;
   void StopVibrateHaptic(uint32_t aControllerIdx) override;
--- a/gfx/vr/service/PuppetSession.cpp
+++ b/gfx/vr/service/PuppetSession.cpp
@@ -21,20 +21,26 @@ using namespace mozilla::gfx;
 
 namespace mozilla {
 namespace gfx {
 
 PuppetSession::PuppetSession() : VRSession() {}
 
 PuppetSession::~PuppetSession() { Shutdown(); }
 
-bool PuppetSession::Initialize(mozilla::gfx::VRSystemState& aSystemState) {
+bool PuppetSession::Initialize(mozilla::gfx::VRSystemState& aSystemState,
+                               bool aDetectRuntimesOnly) {
   if (!StaticPrefs::dom_vr_enabled() || !StaticPrefs::dom_vr_puppet_enabled()) {
     return false;
   }
+  if (aDetectRuntimesOnly) {
+    aSystemState.displayState.capabilityFlags |=
+        VRDisplayCapabilityFlags::Cap_ImmersiveVR;
+    return false;
+  }
   VRPuppetCommandBuffer::Get().Run(aSystemState);
   if (!aSystemState.displayState.isConnected) {
     return false;
   }
 #if defined(XP_WIN)
   if (!CreateD3DObjects()) {
     Shutdown();
     return false;
--- a/gfx/vr/service/PuppetSession.h
+++ b/gfx/vr/service/PuppetSession.h
@@ -20,17 +20,18 @@ class nsITimer;
 namespace mozilla {
 namespace gfx {
 
 class PuppetSession : public VRSession {
  public:
   PuppetSession();
   virtual ~PuppetSession();
 
-  bool Initialize(mozilla::gfx::VRSystemState& aSystemState) override;
+  bool Initialize(mozilla::gfx::VRSystemState& aSystemState,
+                  bool aDetectRuntimesOnly) override;
   void Shutdown() override;
   void ProcessEvents(mozilla::gfx::VRSystemState& aSystemState) override;
   void StartFrame(mozilla::gfx::VRSystemState& aSystemState) override;
   bool StartPresentation() override;
   void StopPresentation() override;
   void VibrateHaptic(uint32_t aControllerIdx, uint32_t aHapticIndex,
                      float aIntensity, float aDuration) override;
   void StopVibrateHaptic(uint32_t aControllerIdx) override;
--- a/gfx/vr/service/VRService.cpp
+++ b/gfx/vr/service/VRService.cpp
@@ -152,57 +152,61 @@ bool VRService::IsInServiceThread() {
 void VRService::ServiceInitialize() {
   MOZ_ASSERT(IsInServiceThread());
 
   if (!InitShmem()) {
     return;
   }
 
   mShutdownRequested = false;
-  memset(&mBrowserState, 0, sizeof(mBrowserState));
+  // Get initial state from the browser
+  PullState(mBrowserState);
 
   // Try to start a VRSession
   UniquePtr<VRSession> session;
 
   if (StaticPrefs::dom_vr_puppet_enabled()) {
     // When the VR Puppet is enabled, we don't want
     // to enumerate any real devices
     session = MakeUnique<PuppetSession>();
-    if (!session->Initialize(mSystemState)) {
+    if (!session->Initialize(mSystemState, mBrowserState.detectRuntimesOnly)) {
       session = nullptr;
     }
   } else {
     // We try Oculus first to ensure we use Oculus
     // devices trough the most native interface
     // when possible.
 #if defined(XP_WIN)
     // Try Oculus
     if (!session) {
       session = MakeUnique<OculusSession>();
-      if (!session->Initialize(mSystemState)) {
+      if (!session->Initialize(mSystemState,
+                               mBrowserState.detectRuntimesOnly)) {
         session = nullptr;
       }
     }
 #endif
 
 #if defined(XP_WIN) || defined(XP_MACOSX) || \
     (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
     // Try OpenVR
     if (!session) {
       session = MakeUnique<OpenVRSession>();
-      if (!session->Initialize(mSystemState)) {
+      if (!session->Initialize(mSystemState,
+                               mBrowserState.detectRuntimesOnly)) {
         session = nullptr;
       }
     }
 #endif
 #if !defined(MOZ_WIDGET_ANDROID)
     // Try OSVR
     if (!session) {
       session = MakeUnique<OSVRSession>();
-      if (!session->Initialize(mSystemState)) {
+      if (!session->Initialize(mSystemState,
+                               mBrowserState.detectRuntimesOnly)) {
         session = nullptr;
       }
     }
 #endif
 
   }  // if (staticPrefs:VRPuppetEnabled())
 
   if (session) {
@@ -217,20 +221,27 @@ void VRService::ServiceInitialize() {
         NewRunnableMethod("gfx::VRService::ServiceWaitForImmersive", this,
                           &VRService::ServiceWaitForImmersive));
   } else {
     // VR hardware was not detected.
     // We must inform the browser of the failure so it may try again
     // later and resolve WebVR promises.  A failure or shutdown is
     // indicated by enumerationCompleted being set to true, with all
     // other fields remaining zeroed out.
+    VRDisplayCapabilityFlags capFlags =
+        mSystemState.displayState.capabilityFlags;
     memset(&mSystemState, 0, sizeof(mSystemState));
     mSystemState.enumerationCompleted = true;
-    mSystemState.displayState.minRestartInterval =
-        StaticPrefs::dom_vr_external_notdetected_timeout();
+
+    if (mBrowserState.detectRuntimesOnly) {
+      mSystemState.displayState.capabilityFlags = capFlags;
+    } else {
+      mSystemState.displayState.minRestartInterval =
+          StaticPrefs::dom_vr_external_notdetected_timeout();
+    }
     mSystemState.displayState.shutdown = true;
     PushState(mSystemState);
   }
 }
 
 void VRService::ServiceShutdown() {
   MOZ_ASSERT(IsInServiceThread());
 
--- a/gfx/vr/service/VRSession.h
+++ b/gfx/vr/service/VRSession.h
@@ -29,17 +29,33 @@ class VRSession {
   VRSession& operator=(const VRSession&) = delete;
 
 #ifdef XP_WIN
   virtual ~VRSession();
 #else
   virtual ~VRSession() = default;
 #endif
 
-  virtual bool Initialize(mozilla::gfx::VRSystemState& aSystemState) = 0;
+  /**
+   * In order to support WebXR's navigator.xr.IsSessionSupported call without
+   * displaying any permission dialogue, it is necessary to have a safe way to
+   * detect the capability of running a VR or AR session without activating XR
+   * runtimes or powering on hardware.
+   *
+   * API's such as OpenVR make no guarantee that hardware and software won't be
+   * left activated after enumerating devices, so each backend in gfx/vr/service
+   * must allow for more granular detection of capabilities.
+   *
+   * By passing true to bDetectRuntimesOnly, the initialization exits early
+   * after reporting the presence of XR runtime software. The Initialize method
+   * will only enumerate hardware and possibly return true when
+   * aDetectRuntimesOnly is false.
+   */
+  virtual bool Initialize(mozilla::gfx::VRSystemState& aSystemState,
+                          bool aDetectRuntimesOnly) = 0;
   virtual void Shutdown() = 0;
   virtual void ProcessEvents(mozilla::gfx::VRSystemState& aSystemState) = 0;
   virtual void StartFrame(mozilla::gfx::VRSystemState& aSystemState) = 0;
   virtual bool StartPresentation() = 0;
   virtual void StopPresentation() = 0;
   virtual void VibrateHaptic(uint32_t aControllerIdx, uint32_t aHapticIndex,
                              float aIntensity, float aDuration) = 0;
   virtual void StopVibrateHaptic(uint32_t aControllerIdx) = 0;