Bug 1299928 - Part 4: Implement VRControllerManager; r=kip
authorDaosheng Mu <daoshengmu@gmail.com>
Fri, 07 Oct 2016 16:58:01 +0800
changeset 363311 c56ec883e2f395ecdfc63437dd89d07c7b846ccd
parent 363310 16414aeacf43c81fbccf15578bbf03712a9c3431
child 363312 298aafb4c28cce1a45f7b0736215281b3678f58d
push id1369
push userjlorenzo@mozilla.com
push dateMon, 27 Feb 2017 14:59:41 +0000
treeherdermozilla-release@d75a1dba431f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskip
bugs1299928
milestone52.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 1299928 - Part 4: Implement VRControllerManager; r=kip MozReview-Commit-ID: 3bItvKV2HB
gfx/vr/VRDisplayHost.cpp
gfx/vr/VRDisplayHost.h
gfx/vr/VRManager.cpp
gfx/vr/VRManager.h
gfx/vr/gfxVR.cpp
gfx/vr/gfxVR.h
gfx/vr/gfxVROpenVR.cpp
gfx/vr/gfxVROpenVR.h
gfx/vr/ipc/VRManagerParent.cpp
gfx/vr/ipc/VRManagerParent.h
--- a/gfx/vr/VRDisplayHost.cpp
+++ b/gfx/vr/VRDisplayHost.cpp
@@ -139,8 +139,20 @@ bool
 VRDisplayHost::CheckClearDisplayInfoDirty()
 {
   if (mDisplayInfo == mLastUpdateDisplayInfo) {
     return false;
   }
   mLastUpdateDisplayInfo = mDisplayInfo;
   return true;
 }
+
+VRControllerHost::VRControllerHost(VRDeviceType aType)
+{
+  MOZ_COUNT_CTOR(VRControllerHost);
+  mControllerInfo.mType = aType;
+  mControllerInfo.mControllerID = VRDisplayManager::AllocateDisplayID();
+}
+
+VRControllerHost::~VRControllerHost()
+{
+  MOZ_COUNT_DTOR(VRControllerHost);
+}
\ No newline at end of file
--- a/gfx/vr/VRDisplayHost.h
+++ b/gfx/vr/VRDisplayHost.h
@@ -78,12 +78,25 @@ protected:
   static const int kMaxLatencyFrames = 100;
   VRHMDSensorState mLastSensorState[kMaxLatencyFrames];
   int32_t mInputFrameID;
 
 private:
   VRDisplayInfo mLastUpdateDisplayInfo;
 };
 
+class VRControllerHost {
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VRControllerHost)
+
+  const VRControllerInfo& GetControllerInfo() const { return mControllerInfo; }
+
+protected:
+  explicit VRControllerHost(VRDeviceType aType);
+  virtual ~VRControllerHost();
+
+  VRControllerInfo mControllerInfo;
+};
+
 } // namespace gfx
 } // namespace mozilla
 
 #endif /* GFX_VR_DISPLAY_HOST_H */
--- a/gfx/vr/VRManager.cpp
+++ b/gfx/vr/VRManager.cpp
@@ -5,16 +5,17 @@
 
 
 #include "VRManager.h"
 #include "VRManagerParent.h"
 #include "gfxVR.h"
 #include "gfxVROpenVR.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/dom/VRDisplay.h"
+#include "mozilla/dom/GamepadEventTypes.h"
 #include "mozilla/layers/TextureHost.h"
 #include "mozilla/Unused.h"
 
 #include "gfxPrefs.h"
 #include "gfxVR.h"
 #if defined(XP_WIN)
 #include "gfxVROculus.h"
 #endif
@@ -46,16 +47,17 @@ VRManager::ManagerInit()
 
 VRManager::VRManager()
   : mInitialized(false)
 {
   MOZ_COUNT_CTOR(VRManager);
   MOZ_ASSERT(sVRManagerSingleton == nullptr);
 
   RefPtr<VRDisplayManager> mgr;
+  RefPtr<VRControllerManager> controllerMgr;
 
   /**
    * We must add the VRDisplayManager's to mManagers in a careful order to
    * ensure that we don't detect the same VRDisplay from multiple API's.
    *
    * Oculus comes first, as it will only enumerate Oculus HMD's and is the
    * native interface for Oculus HMD's.
    *
@@ -76,16 +78,21 @@ VRManager::VRManager()
 
 #if defined(XP_WIN) || defined(XP_MACOSX) || defined(XP_LINUX)
   // OpenVR is cross platform compatible
   mgr = VRDisplayManagerOpenVR::Create();
   if (mgr) {
     mManagers.AppendElement(mgr);
   }
 
+  controllerMgr = VRControllerManagerOpenVR::Create();
+  if (mgr) {
+    mControllerManagers.AppendElement(controllerMgr);
+  }
+
   // OSVR is cross platform compatible
   mgr = VRDisplayManagerOSVR::Create();
   if (mgr) {
       mManagers.AppendElement(mgr);
   }
 #endif
 }
 
@@ -98,25 +105,34 @@ VRManager::~VRManager()
 
 void
 VRManager::Destroy()
 {
   mVRDisplays.Clear();
   for (uint32_t i = 0; i < mManagers.Length(); ++i) {
     mManagers[i]->Destroy();
   }
+
+  mVRControllers.Clear();
+  for (uint32_t i = 0; i < mControllerManagers.Length(); ++i) {
+    mControllerManagers[i]->Destroy();
+  }
   mInitialized = false;
 }
 
 void
 VRManager::Init()
 {
   for (uint32_t i = 0; i < mManagers.Length(); ++i) {
     mManagers[i]->Init();
   }
+
+  for (uint32_t i = 0; i < mControllerManagers.Length(); ++i) {
+    mControllerManagers[i]->Init();
+  }
   mInitialized = true;
 }
 
 /* static */VRManager*
 VRManager::Get()
 {
   MOZ_ASSERT(sVRManagerSingleton != nullptr);
 
@@ -284,10 +300,49 @@ VRManager::SubmitFrame(VRLayerParent* aL
   TextureHost* th = TextureHost::AsTextureHost(aTexture);
   mLastFrame = th;
   RefPtr<VRDisplayHost> display = GetDisplay(aLayer->GetDisplayID());
   if (display) {
     display->SubmitFrame(aLayer, 0, aTexture, aLeftEyeRect, aRightEyeRect);
   }
 }
 
+RefPtr<gfx::VRControllerHost>
+VRManager::GetController(const uint32_t& aControllerID)
+{
+  RefPtr<gfx::VRControllerHost> controller;
+  if (mVRControllers.Get(aControllerID, getter_AddRefs(controller))) {
+    return controller;
+  }
+  return nullptr;
+}
+
+void
+VRManager::GetVRControllerInfo(nsTArray<VRControllerInfo>& aControllerInfo)
+{
+  aControllerInfo.Clear();
+  for (auto iter = mVRControllers.Iter(); !iter.Done(); iter.Next()) {
+    gfx::VRControllerHost* controller = iter.UserData();
+    aControllerInfo.AppendElement(VRControllerInfo(controller->GetControllerInfo()));
+  }
+}
+
+void
+VRManager::ScanForDevices()
+{
+  for (uint32_t i = 0; i < mControllerManagers.Length(); ++i) {
+    mControllerManagers[i]->ScanForDevices();
+  }
+}
+
+template<class T>
+void
+VRManager::NotifyGamepadChange(const T& aInfo)
+{
+  dom::GamepadChangeEvent e(aInfo);
+
+  for (auto iter = mVRManagerParents.Iter(); !iter.Done(); iter.Next()) {
+    Unused << iter.Get()->GetKey()->SendGamepadUpdate(e);
+  }
+}
+
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/vr/VRManager.h
+++ b/gfx/vr/VRManager.h
@@ -17,37 +17,42 @@ namespace mozilla {
 namespace layers {
 class TextureHost;
 }
 namespace gfx {
 
 class VRLayerParent;
 class VRManagerParent;
 class VRDisplayHost;
+class VRControllerManager;
 
 class VRManager
 {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::gfx::VRManager)
 
 public:
   static void ManagerInit();
   static VRManager* Get();
 
   void AddVRManagerParent(VRManagerParent* aVRManagerParent);
   void RemoveVRManagerParent(VRManagerParent* aVRManagerParent);
 
   void NotifyVsync(const TimeStamp& aVsyncTimestamp);
   void NotifyVRVsync(const uint32_t& aDisplayID);
   void RefreshVRDisplays(bool aMustDispatch = false);
+  void ScanForDevices();
+  template<class T> void NotifyGamepadChange(const T& aInfo);
   RefPtr<gfx::VRDisplayHost> GetDisplay(const uint32_t& aDisplayID);
   void GetVRDisplayInfo(nsTArray<VRDisplayInfo>& aDisplayInfo);
 
   void SubmitFrame(VRLayerParent* aLayer, layers::PTextureParent* aTexture,
                    const gfx::Rect& aLeftEyeRect,
                    const gfx::Rect& aRightEyeRect);
+  RefPtr<gfx::VRControllerHost> GetController(const uint32_t& aControllerID);
+  void GetVRControllerInfo(nsTArray<VRControllerInfo>& aControllerInfo);
 
 protected:
   VRManager();
   ~VRManager();
 
 private:
   RefPtr<layers::TextureHost> mLastFrame;
 
@@ -57,19 +62,25 @@ private:
   void DispatchVRDisplayInfoUpdate();
 
   typedef nsTHashtable<nsRefPtrHashKey<VRManagerParent>> VRManagerParentSet;
   VRManagerParentSet mVRManagerParents;
 
   typedef nsTArray<RefPtr<VRDisplayManager>> VRDisplayManagerArray;
   VRDisplayManagerArray mManagers;
 
+  typedef nsTArray<RefPtr<VRControllerManager>> VRControllerManagerArray;
+  VRControllerManagerArray mControllerManagers;
+
   typedef nsRefPtrHashtable<nsUint32HashKey, gfx::VRDisplayHost> VRDisplayHostHashMap;
   VRDisplayHostHashMap mVRDisplays;
 
+  typedef nsRefPtrHashtable<nsUint32HashKey, gfx::VRControllerHost> VRControllerHostHashMap;
+  VRControllerHostHashMap mVRControllers;
+
   Atomic<bool> mInitialized;
 
   TimeStamp mLastRefreshTime;
 };
 
 } // namespace gfx
 } // namespace mozilla
 
--- a/gfx/vr/gfxVR.cpp
+++ b/gfx/vr/gfxVR.cpp
@@ -1,25 +1,27 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include <math.h>
 
 #include "gfxVR.h"
+#include "mozilla/dom/Gamepad.h"
 
 #ifndef M_PI
 # define M_PI 3.14159265358979323846
 #endif
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 
 Atomic<uint32_t> VRDisplayManager::sDisplayBase(0);
+Atomic<uint32_t> VRControllerManager::sControllerBase(0);
 
 /* static */ uint32_t
 VRDisplayManager::AllocateDisplayID()
 {
   return ++sDisplayBase;
 }
 
 Matrix4x4
@@ -50,8 +52,39 @@ VRFieldOfView::ConstructProjectionMatrix
   m[2*4+2] = zFar / (zNear - zFar) * -handednessScale;
   m[3*4+2] = (zFar * zNear) / (zNear - zFar);
 
   m[2*4+3] = handednessScale;
   m[3*4+3] = 0.0f;
 
   return mobj;
 }
+
+/* static */ uint32_t
+VRControllerManager::AllocateControllerID()
+{
+  return ++sControllerBase;
+}
+
+void
+VRControllerManager::AddGamepad(const char* aID,
+                                dom::GamepadMappingType aMapping,
+                                uint32_t aNumButtons, uint32_t aNumAxes)
+{
+  dom::GamepadAdded a(NS_ConvertUTF8toUTF16(nsDependentCString(aID)), mControllerCount,
+                     aMapping, dom::GamepadServiceType::VR, aNumButtons,
+                     aNumAxes);
+
+  VRManager* vm = VRManager::Get();
+  MOZ_ASSERT(vm);
+  vm->NotifyGamepadChange<dom::GamepadAdded>(a);
+}
+
+void
+VRControllerManager::NewButtonEvent(uint32_t aIndex, uint32_t aButton,
+                                    bool aPressed, double aValue)
+{
+  dom::GamepadButtonInformation a(aIndex, aButton, aPressed, aValue);
+
+  VRManager* vm = VRManager::Get();
+  MOZ_ASSERT(vm);
+  vm->NotifyGamepadChange<dom::GamepadButtonInformation>(a);
+}
--- a/gfx/vr/gfxVR.h
+++ b/gfx/vr/gfxVR.h
@@ -19,16 +19,17 @@
 
 namespace mozilla {
 namespace layers {
 class PTextureParent;
 }
 namespace gfx {
 class VRLayerParent;
 class VRDisplayHost;
+class VRControllerHost;
 
 enum class VRDeviceType : uint16_t {
   Oculus,
   OpenVR,
   OSVR,
   NumVRDeviceTypes
 };
 
@@ -235,12 +236,36 @@ struct VRControllerInfo
          mNumAxes == other.mNumAxes;
   }
 
   bool operator!=(const VRControllerInfo& other) const {
     return !(*this == other);
   }
 };
 
+class VRControllerManager {
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VRControllerManager)
+
+  static uint32_t AllocateControllerID();
+  virtual bool Init() = 0;
+  virtual void Destroy() = 0;
+  virtual void HandleInput() = 0;
+  virtual void GetControllers(nsTArray<RefPtr<VRControllerHost>>& aControllerResult) = 0;
+  virtual void ScanForDevices() = 0;
+  void NewButtonEvent(uint32_t aIndex, uint32_t aButton,
+                      bool aPressed, double aValue);
+  void AddGamepad(const char* aID, dom::GamepadMappingType aMapping,
+                  uint32_t aNumButtons, uint32_t aNumAxes);
+
+protected:
+  VRControllerManager() : mInstalled(false), mControllerCount(0) {}
+  virtual ~VRControllerManager() {}
+
+  bool mInstalled;
+  uint32_t mControllerCount;
+  static Atomic<uint32_t> sControllerBase;
+};
+
 } // namespace gfx
 } // namespace mozilla
 
 #endif /* GFX_VR_H */
--- a/gfx/vr/gfxVROpenVR.cpp
+++ b/gfx/vr/gfxVROpenVR.cpp
@@ -19,25 +19,27 @@
 #include "TextureD3D11.h"
 #endif // XP_WIN
 
 #include "gfxVROpenVR.h"
 
 #include "nsServiceManagerUtils.h"
 #include "nsIScreenManager.h"
 #include "openvr/openvr.h"
+#include "mozilla/dom/Gamepad.h"
 
 #ifndef M_PI
 # define M_PI 3.14159265358979323846
 #endif
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 using namespace mozilla::gfx::impl;
 using namespace mozilla::layers;
+using namespace mozilla::dom;
 
 namespace {
 extern "C" {
 typedef uint32_t (VR_CALLTYPE * pfn_VR_InitInternal)(::vr::HmdError *peError, ::vr::EVRApplicationType eApplicationType);
 typedef void (VR_CALLTYPE * pfn_VR_ShutdownInternal)();
 typedef bool (VR_CALLTYPE * pfn_VR_IsHmdPresent)();
 typedef bool (VR_CALLTYPE * pfn_VR_IsRuntimeInstalled)();
 typedef const char * (VR_CALLTYPE * pfn_VR_GetStringForHmdError)(::vr::HmdError error);
@@ -435,8 +437,118 @@ VRDisplayManagerOpenVR::GetHMDs(nsTArray
 
     mOpenVRHMD = new VRDisplayOpenVR(system, chaperone, compositor);
   }
 
   if (mOpenVRHMD) {
     aHMDResult.AppendElement(mOpenVRHMD);
   }
 }
+
+VRControllerOpenVR::VRControllerOpenVR()
+  : VRControllerHost(VRDeviceType::OpenVR)
+{
+  MOZ_COUNT_CTOR_INHERITED(VRControllerOpenVR, VRControllerHost);
+  mControllerInfo.mControllerName.AssignLiteral("OpenVR HMD");
+}
+
+VRControllerOpenVR::~VRControllerOpenVR()
+{
+  MOZ_COUNT_DTOR_INHERITED(VRControllerOpenVR, VRControllerHost);
+}
+
+VRControllerManagerOpenVR::VRControllerManagerOpenVR()
+  : mOpenVRInstalled(false), mVRSystem(nullptr)
+{
+}
+
+VRControllerManagerOpenVR::~VRControllerManagerOpenVR()
+{
+  Destroy();
+}
+
+/*static*/ already_AddRefed<VRControllerManagerOpenVR>
+VRControllerManagerOpenVR::Create()
+{
+  if (!gfxPrefs::VREnabled() || !gfxPrefs::VROpenVREnabled()) {
+    return nullptr;
+  }
+
+  RefPtr<VRControllerManagerOpenVR> manager = new VRControllerManagerOpenVR();
+  return manager.forget();
+}
+
+bool
+VRControllerManagerOpenVR::Init()
+{
+  if (mOpenVRInstalled)
+    return true;
+
+  if (!vr_IsRuntimeInstalled())
+    return false;
+
+  // Loading the OpenVR Runtime
+  vr::EVRInitError err = vr::VRInitError_None;
+
+  vr_InitInternal(&err, vr::VRApplication_Scene);
+  if (err != vr::VRInitError_None) {
+    return false;
+  }
+
+  mVRSystem = (vr::IVRSystem *)vr_GetGenericInterface(vr::IVRSystem_Version, &err);
+  if ((err != vr::VRInitError_None) || !mVRSystem) {
+    vr_ShutdownInternal();
+    return false;
+  }
+
+  mOpenVRInstalled = true;
+  return true;
+}
+
+void
+VRControllerManagerOpenVR::Destroy()
+{
+  mOpenVRController.Clear();
+  mOpenVRInstalled = false;
+}
+
+void
+VRControllerManagerOpenVR::GetControllers(nsTArray<RefPtr<VRControllerHost>>& aControllerResult)
+{
+  if (!mOpenVRInstalled) {
+    return;
+  }
+
+  aControllerResult.Clear();
+  for (uint32_t i = 0; i < mOpenVRController.Length(); ++i) {
+    aControllerResult.AppendElement(mOpenVRController[i]);
+  }
+}
+
+void
+VRControllerManagerOpenVR::ScanForDevices()
+{
+  mControllerCount = 0;
+  mOpenVRController.Clear();
+
+  if (!mVRSystem)
+    return;
+
+  // Basically, we would have HMDs in the tracked devices, but we are just interested in the controllers.
+  for ( vr::TrackedDeviceIndex_t trackedDevice = vr::k_unTrackedDeviceIndex_Hmd + 1;
+        trackedDevice < vr::k_unMaxTrackedDeviceCount; ++trackedDevice ) {
+    if (!mVRSystem->IsTrackedDeviceConnected(trackedDevice)) {
+      continue;
+    }
+
+    if (mVRSystem->GetTrackedDeviceClass(trackedDevice) != vr::TrackedDeviceClass_Controller) {
+      continue;
+    }
+
+    RefPtr<VRControllerOpenVR> openVRController = new VRControllerOpenVR();
+    mOpenVRController.AppendElement(openVRController);
+
+    // Not already present, add it.
+    AddGamepad("OpenVR Gamepad", GamepadMappingType::_empty,
+               kOpenVRControllerAxes, kOpenVRControllerButtons);
+    ++mControllerCount;
+  }
+}
\ No newline at end of file
--- a/gfx/vr/gfxVROpenVR.h
+++ b/gfx/vr/gfxVROpenVR.h
@@ -81,13 +81,50 @@ public:
 protected:
   VRDisplayManagerOpenVR();
 
   // there can only be one
   RefPtr<impl::VRDisplayOpenVR> mOpenVRHMD;
   bool mOpenVRInstalled;
 };
 
+namespace impl {
+
+class VRControllerOpenVR : public VRControllerHost
+{
+public:
+  explicit VRControllerOpenVR();
+
+protected:
+  virtual ~VRControllerOpenVR();
+};
+
+} // namespace impl
+
+class VRControllerManagerOpenVR : public VRControllerManager
+{
+public:
+  static already_AddRefed<VRControllerManagerOpenVR> Create();
+
+  virtual bool Init() override;
+  virtual void Destroy() override;
+  virtual void HandleInput() override;
+  virtual void GetControllers(nsTArray<RefPtr<VRControllerHost>>&
+                              aControllerResult) override;
+  virtual void ScanForDevices() override;
+
+private:
+  VRControllerManagerOpenVR();
+  ~VRControllerManagerOpenVR();
+
+  bool mOpenVRInstalled;
+  nsTArray<RefPtr<impl::VRControllerOpenVR>> mOpenVRController;
+  vr::IVRSystem *mVRSystem;
+
+  const uint32_t kOpenVRControllerButtons = 8;
+  const uint32_t kOpenVRControllerAxes = 5;
+};
+
 } // namespace gfx
 } // namespace mozilla
 
 
 #endif /* GFX_VR_OPENVR_H */
--- a/gfx/vr/ipc/VRManagerParent.cpp
+++ b/gfx/vr/ipc/VRManagerParent.cpp
@@ -286,10 +286,34 @@ VRManagerParent::HaveEventListener()
 
 bool
 VRManagerParent::RecvSetHaveEventListener(const bool& aHaveEventListener)
 {
   mHaveEventListener = aHaveEventListener;
   return true;
 }
 
+bool
+VRManagerParent::RecvControllerListenerAdded()
+{
+  VRManager* vm = VRManager::Get();
+  // Ask the connected gamepads to be added to GamepadManager
+  vm->ScanForDevices();
+
+  return true;
+}
+
+bool
+VRManagerParent::RecvControllerListenerRemoved()
+{
+  return true;
+}
+
+bool
+VRManagerParent::RecvGetControllers(nsTArray<VRControllerInfo> *aControllers)
+{
+  VRManager* vm = VRManager::Get();
+  vm->GetVRControllerInfo(*aControllers);
+  return true;
+}
+
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/vr/ipc/VRManagerParent.h
+++ b/gfx/vr/ipc/VRManagerParent.h
@@ -79,16 +79,19 @@ protected:
   void OnChannelConnected(int32_t pid) override;
 
   virtual bool RecvRefreshDisplays() override;
   virtual bool RecvGetDisplays(nsTArray<VRDisplayInfo> *aDisplays) override;
   virtual bool RecvResetSensor(const uint32_t& aDisplayID) override;
   virtual bool RecvGetSensorState(const uint32_t& aDisplayID, VRHMDSensorState* aState) override;
   virtual bool RecvGetImmediateSensorState(const uint32_t& aDisplayID, VRHMDSensorState* aState) override;
   virtual bool RecvSetHaveEventListener(const bool& aHaveEventListener) override;
+  virtual bool RecvControllerListenerAdded() override;
+  virtual bool RecvControllerListenerRemoved() override;
+  virtual bool RecvGetControllers(nsTArray<VRControllerInfo> *aControllers) override;
 
 private:
   void RegisterWithManager();
   void UnregisterFromManager();
 
   void Bind(Endpoint<PVRManagerParent>&& aEndpoint);
 
   static void RegisterVRManagerInCompositorThread(VRManagerParent* aVRManager);