Bug 1299928 - Part 4: Implement VRControllerManager; r?kip
MozReview-Commit-ID: 3bItvKV2HB
--- 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);