Bug 1299928 - Part 4: Implement VRControllerManager; r?kip draft
authorDaosheng Mu <daoshengmu@gmail.com>
Fri, 07 Oct 2016 16:58:01 +0800
changeset 425941 6a21b94110bc0a6debc3bb13d88461611e2b74a4
parent 425940 86c6b60dc10ad11d7a9a5e126ccafa856770d51f
child 425942 9f7ce0e6804a02aeef9b6f7147c701af499cc652
push id32537
push userbmo:dmu@mozilla.com
push dateMon, 17 Oct 2016 10:06:25 +0000
reviewerskip
bugs1299928
milestone52.0a1
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
@@ -133,8 +133,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, aInputFrameID, 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, const int32_t& aInputFrameID,
                    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);