Bug 1229480: Implement Puppet VR Device; r=kip
authorDaosheng Mu <daoshengmu@gmail.com>
Thu, 02 Mar 2017 00:04:12 +0800
changeset 394477 0f9b03faff358ed78b951b0f67401f8bd108bf34
parent 394476 0fb088fb7d66147ff48d99596dfd3f67960d6426
child 394478 36bb16774744cba10efdfa1d33f4a5dd027f32ce
push id1468
push userasasaki@mozilla.com
push dateMon, 05 Jun 2017 19:31:07 +0000
treeherdermozilla-release@0641fc6ee9d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskip
bugs1229480
milestone54.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 1229480: Implement Puppet VR Device; r=kip MozReview-Commit-ID: hslhADrGs5
gfx/thebes/gfxPrefs.h
gfx/vr/VRManager.cpp
gfx/vr/gfxVR.h
gfx/vr/gfxVRPuppet.cpp
gfx/vr/gfxVRPuppet.h
gfx/vr/moz.build
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -320,16 +320,17 @@ private:
 
   DECL_GFX_PREF(Live, "dom.ipc.plugins.asyncdrawing.enabled",  PluginAsyncDrawingEnabled, bool, false);
   DECL_GFX_PREF(Live, "dom.meta-viewport.enabled",             MetaViewportEnabled, bool, false);
   DECL_GFX_PREF(Once, "dom.vr.enabled",                        VREnabled, bool, false);
   DECL_GFX_PREF(Once, "dom.vr.oculus.enabled",                 VROculusEnabled, bool, true);
   DECL_GFX_PREF(Once, "dom.vr.openvr.enabled",                 VROpenVREnabled, bool, false);
   DECL_GFX_PREF(Once, "dom.vr.osvr.enabled",                   VROSVREnabled, bool, false);
   DECL_GFX_PREF(Live, "dom.vr.poseprediction.enabled",         VRPosePredictionEnabled, bool, false);
+  DECL_GFX_PREF(Once, "dom.vr.puppet.enabled",                 VRPuppetEnabled, bool, false);
   DECL_GFX_PREF(Live, "dom.w3c_pointer_events.enabled",        PointerEventsEnabled, bool, false);
   DECL_GFX_PREF(Live, "dom.w3c_touch_events.enabled",          TouchEventsEnabled, int32_t, 0);
 
   DECL_GFX_PREF(Live, "general.smoothScroll",                  SmoothScrollEnabled, bool, true);
   DECL_GFX_PREF(Live, "general.smoothScroll.currentVelocityWeighting",
                 SmoothScrollCurrentVelocityWeighting, float, 0.25);
   DECL_GFX_PREF(Live, "general.smoothScroll.durationToIntervalRatio",
                 SmoothScrollDurationToIntervalRatio, int32_t, 200);
--- a/gfx/vr/VRManager.cpp
+++ b/gfx/vr/VRManager.cpp
@@ -17,16 +17,17 @@
 #include "gfxPrefs.h"
 #include "gfxVR.h"
 #if defined(XP_WIN)
 #include "gfxVROculus.h"
 #endif
 #if defined(XP_WIN) || defined(XP_MACOSX) || defined(XP_LINUX)
 #include "gfxVROSVR.h"
 #endif
+#include "gfxVRPuppet.h"
 #include "ipc/VRLayerParent.h"
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 using namespace mozilla::layers;
 using namespace mozilla::gl;
 
 namespace mozilla {
@@ -83,16 +84,20 @@ VRManager::VRManager()
   }
 
   // OSVR is cross platform compatible
   mgr = VRSystemManagerOSVR::Create();
   if (mgr) {
       mManagers.AppendElement(mgr);
   }
 #endif
+  mgr = VRSystemManagerPuppet::Create();
+  if (mgr) {
+    mManagers.AppendElement(mgr);
+  }
   // Enable gamepad extensions while VR is enabled.
   // Preference only can be set at the Parent process.
   if (XRE_IsParentProcess() && gfxPrefs::VREnabled()) {
     Preferences::SetBool("dom.gamepad.extensions.enabled", true);
   }
 }
 
 VRManager::~VRManager()
--- a/gfx/vr/gfxVR.h
+++ b/gfx/vr/gfxVR.h
@@ -29,16 +29,17 @@ namespace gfx {
 class VRLayerParent;
 class VRDisplayHost;
 class VRControllerHost;
 
 enum class VRDeviceType : uint16_t {
   Oculus,
   OpenVR,
   OSVR,
+  Puppet,
   NumVRDeviceTypes
 };
 
 enum class VRDisplayCapabilityFlags : uint16_t {
   Cap_None = 0,
   /**
    * Cap_Position is set if the VRDisplay is capable of tracking its position.
    */
new file mode 100644
--- /dev/null
+++ b/gfx/vr/gfxVRPuppet.cpp
@@ -0,0 +1,459 @@
+/* -*- 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/. */
+
+#if defined(XP_WIN)
+#include "CompositorD3D11.h"
+#include "TextureD3D11.h"
+#endif // XP_WIN
+
+#include "gfxVRPuppet.h"
+
+#include "mozilla/dom/GamepadEventTypes.h"
+#include "mozilla/dom/GamepadBinding.h"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+using namespace mozilla::gfx::impl;
+using namespace mozilla::layers;
+
+
+// Reminder: changing the order of these buttons may break web content
+static const uint64_t kPuppetButtonMask[] = {
+  1,
+  2,
+  4,
+  8
+};
+
+static const uint32_t kNumPuppetButtonMask = sizeof(kPuppetButtonMask) /
+                                             sizeof(uint64_t);
+
+static const uint32_t kPuppetAxes[] = {
+  0,
+  1,
+  2
+};
+
+static const uint32_t kNumPuppetAxis = sizeof(kPuppetAxes) /
+                                       sizeof(uint32_t);
+
+VRDisplayPuppet::VRDisplayPuppet()
+ : VRDisplayHost(VRDeviceType::Puppet)
+ , mIsPresenting(false)
+{
+  MOZ_COUNT_CTOR_INHERITED(VRDisplayPuppet, VRDisplayHost);
+
+  mDisplayInfo.mDisplayName.AssignLiteral("Puppet HMD");
+  mDisplayInfo.mIsConnected = true;
+  mDisplayInfo.mIsMounted = false;
+  mDisplayInfo.mCapabilityFlags = VRDisplayCapabilityFlags::Cap_None |
+                                  VRDisplayCapabilityFlags::Cap_Orientation |
+                                  VRDisplayCapabilityFlags::Cap_Position |
+                                  VRDisplayCapabilityFlags::Cap_External |
+                                  VRDisplayCapabilityFlags::Cap_Present |
+                                  VRDisplayCapabilityFlags::Cap_StageParameters;
+  mDisplayInfo.mEyeResolution.width = 1836; // 1080 * 1.7
+  mDisplayInfo.mEyeResolution.height = 2040; // 1200 * 1.7
+
+  // SteamVR gives the application a single FOV to use; it's not configurable as with Oculus
+  for (uint32_t eye = 0; eye < 2; ++eye) {
+    mDisplayInfo.mEyeTranslation[eye].x = 0.0f;
+    mDisplayInfo.mEyeTranslation[eye].y = 0.0f;
+    mDisplayInfo.mEyeTranslation[eye].z = 0.0f;
+    mDisplayInfo.mEyeFOV[eye] = VRFieldOfView(45.0, 45.0, 45.0, 45.0);
+  }
+
+  // default: 1m x 1m space, 0.75m high in seated position
+  mDisplayInfo.mStageSize.width = 1.0f;
+  mDisplayInfo.mStageSize.height = 1.0f;
+
+  mDisplayInfo.mSittingToStandingTransform._11 = 1.0f;
+  mDisplayInfo.mSittingToStandingTransform._12 = 0.0f;
+  mDisplayInfo.mSittingToStandingTransform._13 = 0.0f;
+  mDisplayInfo.mSittingToStandingTransform._14 = 0.0f;
+
+  mDisplayInfo.mSittingToStandingTransform._21 = 0.0f;
+  mDisplayInfo.mSittingToStandingTransform._22 = 1.0f;
+  mDisplayInfo.mSittingToStandingTransform._23 = 0.0f;
+  mDisplayInfo.mSittingToStandingTransform._24 = 0.0f;
+
+  mDisplayInfo.mSittingToStandingTransform._31 = 0.0f;
+  mDisplayInfo.mSittingToStandingTransform._32 = 0.0f;
+  mDisplayInfo.mSittingToStandingTransform._33 = 1.0f;
+  mDisplayInfo.mSittingToStandingTransform._34 = 0.0f;
+
+  mDisplayInfo.mSittingToStandingTransform._41 = 0.0f;
+  mDisplayInfo.mSittingToStandingTransform._42 = 0.75f;
+  mDisplayInfo.mSittingToStandingTransform._43 = 0.0f;
+
+  mSensorState.Clear();
+  mSensorState.timestamp = PR_Now();
+
+  gfx::Quaternion rot;
+
+  mSensorState.flags |= VRDisplayCapabilityFlags::Cap_Orientation;
+  mSensorState.orientation[0] = rot.x;
+  mSensorState.orientation[1] = rot.y;
+  mSensorState.orientation[2] = rot.z;
+  mSensorState.orientation[3] = rot.w;
+  mSensorState.angularVelocity[0] = 0.0f;
+  mSensorState.angularVelocity[1] = 0.0f;
+  mSensorState.angularVelocity[2] = 0.0f;
+
+  mSensorState.flags |= VRDisplayCapabilityFlags::Cap_Position;
+  mSensorState.position[0] = 0.0f;
+  mSensorState.position[1] = 0.0f;
+  mSensorState.position[2] = 0.0f;
+  mSensorState.linearVelocity[0] = 0.0f;
+  mSensorState.linearVelocity[1] = 0.0f;
+  mSensorState.linearVelocity[2] = 0.0f;
+}
+
+VRDisplayPuppet::~VRDisplayPuppet()
+{
+  MOZ_COUNT_DTOR_INHERITED(VRDisplayPuppet, VRDisplayHost);
+}
+
+void
+VRDisplayPuppet::SetDisplayInfo(const VRDisplayInfo& aDisplayInfo)
+{
+  mDisplayInfo = aDisplayInfo;
+}
+
+void
+VRDisplayPuppet::Destroy()
+{
+  StopPresentation();
+}
+
+void
+VRDisplayPuppet::ZeroSensor()
+{
+}
+
+VRHMDSensorState
+VRDisplayPuppet::GetSensorState()
+{
+  return GetSensorState(0.0f);
+}
+
+VRHMDSensorState
+VRDisplayPuppet::GetImmediateSensorState()
+{
+  return GetSensorState(0.0f);
+}
+
+VRHMDSensorState
+VRDisplayPuppet::GetSensorState(double timeOffset)
+{
+  return mSensorState;
+}
+
+void
+VRDisplayPuppet::SetSensorState(const VRHMDSensorState& aSensorState)
+{
+  mSensorState = aSensorState;
+}
+
+void
+VRDisplayPuppet::StartPresentation()
+{
+  if (mIsPresenting) {
+    return;
+  }
+  mIsPresenting = true;
+}
+
+void
+VRDisplayPuppet::StopPresentation()
+{
+  if (!mIsPresenting) {
+    return;
+  }
+
+  mIsPresenting = false;
+}
+
+#if defined(XP_WIN)
+void
+VRDisplayPuppet::SubmitFrame(TextureSourceD3D11* aSource,
+                             const IntSize& aSize,
+                             const VRHMDSensorState& aSensorState,
+                             const gfx::Rect& aLeftEyeRect,
+                             const gfx::Rect& aRightEyeRect)
+{
+  if (!mIsPresenting) {
+    return;
+  }
+
+  ID3D11Texture2D* tex = aSource->GetD3D11Texture();
+  MOZ_ASSERT(tex);
+
+  // TODO: Bug 1343730, Need to block until the next simulated
+  // vblank interval and capture frames for use in reftests.
+
+  // Trigger the next VSync immediately
+  VRManager *vm = VRManager::Get();
+  MOZ_ASSERT(vm);
+  vm->NotifyVRVsync(mDisplayInfo.mDisplayID);
+}
+#else
+void
+VRDisplayPuppet::SubmitFrame(TextureSourceOGL* aSource,
+                             const IntSize& aSize,
+                             const VRHMDSensorState& aSensorState,
+                             const gfx::Rect& aLeftEyeRect,
+                             const gfx::Rect& aRightEyeRect)
+{
+  if (!mIsPresenting) {
+    return;
+  }
+
+  // TODO: Bug 1343730, Need to block until the next simulated
+  // vblank interval and capture frames for use in reftests.
+
+  // Trigger the next VSync immediately
+  VRManager *vm = VRManager::Get();
+  MOZ_ASSERT(vm);
+  vm->NotifyVRVsync(mDisplayInfo.mDisplayID);
+}
+#endif
+
+void
+VRDisplayPuppet::NotifyVSync()
+{
+  // We update mIsConneced once per frame.
+  mDisplayInfo.mIsConnected = true;
+}
+
+VRControllerPuppet::VRControllerPuppet(dom::GamepadHand aHand)
+  : VRControllerHost(VRDeviceType::Puppet)
+  , mButtonPressState(0)
+{
+  MOZ_COUNT_CTOR_INHERITED(VRControllerPuppet, VRControllerHost);
+  mControllerInfo.mControllerName.AssignLiteral("Puppet Gamepad");
+  mControllerInfo.mMappingType = GamepadMappingType::_empty;
+  mControllerInfo.mHand = aHand;
+  mControllerInfo.mNumButtons = kNumPuppetButtonMask;
+  mControllerInfo.mNumAxes = kNumPuppetAxis;
+}
+
+VRControllerPuppet::~VRControllerPuppet()
+{
+  MOZ_COUNT_DTOR_INHERITED(VRControllerPuppet, VRControllerHost);
+}
+
+void
+VRControllerPuppet::SetButtonPressState(uint32_t aButton, bool aPressed)
+{
+  const uint64_t buttonMask = kPuppetButtonMask[aButton];
+  uint64_t pressedBit = GetButtonPressed();
+
+  if (aPressed) {
+    pressedBit |= kPuppetButtonMask[aButton];
+  } else if (pressedBit & buttonMask) {
+    // this button was pressed but is released now.
+    uint64_t mask = 0xff ^ buttonMask;
+    pressedBit &= mask;
+  }
+
+  mButtonPressState = pressedBit;
+}
+
+uint64_t
+VRControllerPuppet::GetButtonPressState()
+{
+  return mButtonPressState;
+}
+
+void
+VRControllerPuppet::SetAxisMoveState(uint32_t aAxis, double aValue)
+{
+  MOZ_ASSERT((sizeof(mAxisMoveState) / sizeof(float)) == kNumPuppetAxis);
+  MOZ_ASSERT(aAxis <= kNumPuppetAxis);
+
+  mAxisMoveState[aAxis] = aValue;
+}
+
+double
+VRControllerPuppet::GetAxisMoveState(uint32_t aAxis)
+{
+  return mAxisMoveState[aAxis];
+}
+
+void
+VRControllerPuppet::SetPoseMoveState(const dom::GamepadPoseState& aPose)
+{
+  mPoseState = aPose;
+}
+
+const dom::GamepadPoseState&
+VRControllerPuppet::GetPoseMoveState()
+{
+  return mPoseState;
+}
+
+float
+VRControllerPuppet::GetAxisMove(uint32_t aAxis)
+{
+  return mAxisMove[aAxis];
+}
+
+void
+VRControllerPuppet::SetAxisMove(uint32_t aAxis, float aValue)
+{
+  mAxisMove[aAxis] = aValue;
+}
+
+VRSystemManagerPuppet::VRSystemManagerPuppet()
+{
+}
+
+/*static*/ already_AddRefed<VRSystemManagerPuppet>
+VRSystemManagerPuppet::Create()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!gfxPrefs::VREnabled() || !gfxPrefs::VRPuppetEnabled()) {
+    return nullptr;
+  }
+
+  RefPtr<VRSystemManagerPuppet> manager = new VRSystemManagerPuppet();
+  return manager.forget();
+}
+
+bool
+VRSystemManagerPuppet::Init()
+{
+  return true;
+}
+
+void
+VRSystemManagerPuppet::Destroy()
+{
+  mPuppetHMD = nullptr;
+}
+
+void
+VRSystemManagerPuppet::GetHMDs(nsTArray<RefPtr<VRDisplayHost>>& aHMDResult)
+{
+  if (mPuppetHMD == nullptr) {
+    mPuppetHMD = new VRDisplayPuppet();
+  }
+  aHMDResult.AppendElement(mPuppetHMD);
+}
+
+void
+VRSystemManagerPuppet::HandleInput()
+{
+  RefPtr<impl::VRControllerPuppet> controller;
+  for (uint32_t i = 0; i < mPuppetController.Length(); ++i) {
+    controller = mPuppetController[i];
+    HandleButtonPress(i, controller->GetButtonPressState());
+
+    for (uint32_t j = 0; j < kNumPuppetAxis; ++j) {
+      HandleAxisMove(i, j, controller->GetAxisMoveState(j));
+    }
+    HandlePoseTracking(i, controller->GetPoseMoveState(), controller);
+  }
+}
+
+void
+VRSystemManagerPuppet::HandleButtonPress(uint32_t aControllerIdx,
+                                         uint64_t aButtonPressed)
+{
+  uint64_t buttonMask = 0;
+  RefPtr<impl::VRControllerPuppet> controller(mPuppetController[aControllerIdx]);
+  MOZ_ASSERT(controller);
+  uint64_t diff = (controller->GetButtonPressed() ^ aButtonPressed);
+
+  if (!diff) {
+    return;
+  }
+
+  for (uint32_t i = 0; i < kNumPuppetButtonMask; ++i) {
+    buttonMask = kPuppetButtonMask[i];
+
+    if (diff & buttonMask) {
+      // diff & aButtonPressed would be true while a new button press
+      // event, otherwise it is an old press event and needs to notify
+      // the button has been released.
+      NewButtonEvent(aControllerIdx, i, diff & aButtonPressed);
+    }
+  }
+
+  controller->SetButtonPressed(aButtonPressed);
+}
+
+void
+VRSystemManagerPuppet::HandleAxisMove(uint32_t aControllerIdx, uint32_t aAxis,
+                                      float aValue)
+{
+  RefPtr<impl::VRControllerPuppet> controller(mPuppetController[aControllerIdx]);
+  MOZ_ASSERT(controller);
+
+  if (controller->GetAxisMove(aAxis) != aValue) {
+    NewAxisMove(aControllerIdx, aAxis, aValue);
+    controller->SetAxisMove(aAxis, aValue);
+  }
+}
+
+void
+VRSystemManagerPuppet::HandlePoseTracking(uint32_t aControllerIdx,
+                                          const GamepadPoseState& aPose,
+                                          VRControllerHost* aController)
+{
+  MOZ_ASSERT(aController);
+  if (aPose != aController->GetPose()) {
+    aController->SetPose(aPose);
+    NewPoseState(aControllerIdx, aPose);
+  }
+}
+
+void
+VRSystemManagerPuppet::GetControllers(nsTArray<RefPtr<VRControllerHost>>& aControllerResult)
+{
+  aControllerResult.Clear();
+  for (uint32_t i = 0; i < mPuppetController.Length(); ++i) {
+    aControllerResult.AppendElement(mPuppetController[i]);
+  }
+}
+
+void
+VRSystemManagerPuppet::ScanForControllers()
+{
+  // We make VRSystemManagerPuppet has two controllers always.
+  const uint32_t newControllerCount = 2;
+
+  if (newControllerCount != mControllerCount) {
+    // controller count is changed, removing the existing gamepads first.
+    for (uint32_t i = 0; i < mPuppetController.Length(); ++i) {
+      RemoveGamepad(i);
+    }
+
+    mControllerCount = 0;
+    mPuppetController.Clear();
+
+    // Re-adding controllers to VRControllerManager.
+    for (uint32_t i = 0; i < newControllerCount; ++i) {
+      dom::GamepadHand hand = (i % 2) ? dom::GamepadHand::Right :
+                                        dom::GamepadHand::Left;
+      RefPtr<VRControllerPuppet> puppetController = new VRControllerPuppet(hand);
+      puppetController->SetIndex(mControllerCount);
+      mPuppetController.AppendElement(puppetController);
+
+      // Not already present, add it.
+      AddGamepad(puppetController->GetControllerInfo());
+      ++mControllerCount;
+    }
+  }
+}
+
+void
+VRSystemManagerPuppet::RemoveControllers()
+{
+  mPuppetController.Clear();
+  mControllerCount = 0;
+}
new file mode 100644
--- /dev/null
+++ b/gfx/vr/gfxVRPuppet.h
@@ -0,0 +1,119 @@
+/* -*- 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/. */
+
+#ifndef GFX_VR_PUPPET_H
+#define GFX_VR_PUPPET_H
+
+#include "nsTArray.h"
+#include "mozilla/RefPtr.h"
+
+#include "gfxVR.h"
+
+namespace mozilla {
+namespace gfx {
+namespace impl {
+
+class VRDisplayPuppet : public VRDisplayHost
+{
+public:
+  void SetDisplayInfo(const VRDisplayInfo& aDisplayInfo);
+  virtual void NotifyVSync() override;
+  virtual VRHMDSensorState GetSensorState() override;
+  virtual VRHMDSensorState GetImmediateSensorState() override;
+  void SetSensorState(const VRHMDSensorState& aSensorState);
+  void ZeroSensor() override;
+
+protected:
+  virtual void StartPresentation() override;
+  virtual void StopPresentation() override;
+#if defined(XP_WIN)
+  virtual void SubmitFrame(mozilla::layers::TextureSourceD3D11* aSource,
+                           const IntSize& aSize,
+                           const VRHMDSensorState& aSensorState,
+                           const gfx::Rect& aLeftEyeRect,
+                           const gfx::Rect& aRightEyeRect) override;
+#else
+  virtual void SubmitFrame(mozilla::layers::TextureSourceOGL* aSource,
+                           const IntSize& aSize,
+                           const VRHMDSensorState& aSensorState,
+                           const gfx::Rect& aLeftEyeRect,
+                           const gfx::Rect& aRightEyeRect);
+#endif // XP_WIN
+
+public:
+  explicit VRDisplayPuppet();
+
+protected:
+  virtual ~VRDisplayPuppet();
+  void Destroy();
+
+  VRHMDSensorState GetSensorState(double timeOffset);
+
+  bool mIsPresenting;
+
+private:
+  VRHMDSensorState mSensorState;
+};
+
+class VRControllerPuppet : public VRControllerHost
+{
+public:
+  explicit VRControllerPuppet(dom::GamepadHand aHand);
+  void SetButtonPressState(uint32_t aButton, bool aPressed);
+  uint64_t GetButtonPressState();
+  void SetAxisMoveState(uint32_t aAxis, double aValue);
+  double GetAxisMoveState(uint32_t aAxis);
+  void SetPoseMoveState(const dom::GamepadPoseState& aPose);
+  const dom::GamepadPoseState& GetPoseMoveState();
+  float GetAxisMove(uint32_t aAxis);
+  void SetAxisMove(uint32_t aAxis, float aValue);
+
+protected:
+  virtual ~VRControllerPuppet();
+
+private:
+  uint64_t mButtonPressState;
+  float mAxisMoveState[3];
+  float mAxisMove[3];
+  dom::GamepadPoseState mPoseState;
+};
+
+} // namespace impl
+
+class VRSystemManagerPuppet : public VRSystemManager
+{
+public:
+  static already_AddRefed<VRSystemManagerPuppet> Create();
+
+  virtual bool Init() override;
+  virtual void Destroy() override;
+  virtual void GetHMDs(nsTArray<RefPtr<VRDisplayHost>>& aHMDResult) override;
+  virtual void HandleInput() override;
+  virtual void GetControllers(nsTArray<RefPtr<VRControllerHost>>&
+                              aControllerResult) override;
+  virtual void ScanForControllers() override;
+  virtual void RemoveControllers() override;
+
+protected:
+  VRSystemManagerPuppet();
+
+private:
+  virtual void HandleButtonPress(uint32_t aControllerIndex,
+                                 uint64_t aButtonPressed) override;
+  virtual void HandleAxisMove(uint32_t aControllerIndex, uint32_t aAxis,
+                              float aValue) override;
+  virtual void HandlePoseTracking(uint32_t aControllerIndex,
+                                  const dom::GamepadPoseState& aPose,
+                                  VRControllerHost* aController) override;
+
+  // there can only be one
+  RefPtr<impl::VRDisplayPuppet> mPuppetHMD;
+  nsTArray<RefPtr<impl::VRControllerPuppet>> mPuppetController;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif  /* GFX_VR_PUPPET_H*/
\ No newline at end of file
--- a/gfx/vr/moz.build
+++ b/gfx/vr/moz.build
@@ -20,16 +20,17 @@ LOCAL_INCLUDES += [
     '/gfx/layers/d3d11',
     '/gfx/thebes',
 ]
 
 UNIFIED_SOURCES += [
     'gfxVR.cpp',
     'gfxVROpenVR.cpp',
     'gfxVROSVR.cpp',
+    'gfxVRPuppet.cpp',
     'ipc/VRLayerChild.cpp',
     'ipc/VRLayerParent.cpp',
     'ipc/VRManagerChild.cpp',
     'ipc/VRManagerParent.cpp',
     'VRDisplayClient.cpp',
     'VRDisplayHost.cpp',
     'VRDisplayPresentation.cpp',
     'VRManager.cpp',