Bug 1406327 - Part 2: When loading VR content, launching the VR listener thread; r?kip draft
authorDaosheng Mu <daoshengmu@gmail.com>
Thu, 26 Oct 2017 17:28:37 +0800
changeset 694065 eb8ce4aa9017d162567303a57b71d9c5a140b574
parent 694064 9177cd0de22fc3a7e69a5fa64108f2b625f97c0a
child 694066 6cfe3420d920c3dd5015e751acdeeadde29cefd9
push id88034
push userbmo:dmu@mozilla.com
push dateTue, 07 Nov 2017 10:40:18 +0000
reviewerskip
bugs1406327
milestone58.0a1
Bug 1406327 - Part 2: When loading VR content, launching the VR listener thread; r?kip MozReview-Commit-ID: IyBzJyDEVdv
gfx/vr/VRManager.cpp
gfx/vr/VRManager.h
gfx/vr/ipc/VRLayerParent.cpp
gfx/vr/ipc/VRManagerChild.cpp
gfx/vr/ipc/VRManagerParent.cpp
gfx/vr/ipc/VRManagerParent.h
--- a/gfx/vr/VRManager.cpp
+++ b/gfx/vr/VRManager.cpp
@@ -222,16 +222,17 @@ VRManager::NotifyVsync(const TimeStamp& 
       }
     }
   }
 
   // Shut down the VR devices when not in use
   if (bHaveEventListener || bHaveControllerListener) {
     // We are using a VR device, keep it alive
     mLastActiveTime = TimeStamp::Now();
+    mLastVRListenerThreadActiveTime = mLastActiveTime;
   } else if (mLastActiveTime.IsNull()) {
     Shutdown();
   } else {
     TimeDuration duration = TimeStamp::Now() - mLastActiveTime;
     if (duration.ToMilliseconds() > kVRDisplayInactiveMaxDuration) {
       Shutdown();
     }
   }
@@ -254,17 +255,17 @@ VRManager::NotifyVRVsync(const uint32_t&
 
   RefreshVRDisplays();
 }
 
 void
 VRManager::RefreshVRDisplays(bool aMustDispatch)
 {
   nsTArray<RefPtr<gfx::VRDisplayHost> > displays;
-
+  mLastVRListenerThreadActiveTime = TimeStamp::Now();
   /** We don't wish to enumerate the same display from multiple managers,
    * so stop as soon as we get a display.
    * It is still possible to get multiple displays from a single manager,
    * but do not wish to mix-and-match for risk of reporting a duplicate.
    *
    * XXX - Perhaps there will be a better way to detect duplicate displays
    *       in the future.
    */
@@ -303,32 +304,38 @@ VRManager::RefreshVRDisplays(bool aMustD
   if (displaySetChanged) {
     mVRDisplays.Clear();
     for (const auto& display: displays) {
       mVRDisplays.Put(display->GetDisplayInfo().GetDisplayID(), display);
     }
   }
 
   if (displayInfoChanged || displaySetChanged || aMustDispatch) {
-    DispatchVRDisplayInfoUpdate();
+    // Due to PVRManager is at Compositor thread. We have to post tasks
+    // to Compositor thread when sending to them the content processes.
+    MessageLoop* loop = CompositorThreadHolder::Loop();
+    loop->PostTask(
+      NewRunnableMethod("gfx::VRManager::DispatchVRDisplayInfoUpdate",
+                        this,
+                        &VRManager::DispatchVRDisplayInfoUpdate));
   }
 }
 
 void
 VRManager::DispatchVRDisplayInfoUpdate()
 {
+  MOZ_ASSERT(NS_IsInCompositorThread());
   nsTArray<VRDisplayInfo> update;
   GetVRDisplayInfo(update);
 
   for (auto iter = mVRManagerParents.Iter(); !iter.Done(); iter.Next()) {
     Unused << iter.Get()->GetKey()->SendUpdateDisplayInfo(update);
   }
 }
 
-
 /**
  * Get any VR displays that have already been enumerated without
  * activating any new devices.
  */
 void
 VRManager::GetVRDisplayInfo(nsTArray<VRDisplayInfo>& aDisplayInfo)
 {
   aDisplayInfo.Clear();
@@ -363,16 +370,22 @@ VRManager::GetVRControllerInfo(nsTArray<
 {
   aControllerInfo.Clear();
   for (auto iter = mVRControllers.Iter(); !iter.Done(); iter.Next()) {
     gfx::VRControllerHost* controller = iter.UserData();
     aControllerInfo.AppendElement(VRControllerInfo(controller->GetControllerInfo()));
   }
 }
 
+TimeStamp
+VRManager::GetLastVRListenerThreadActiveTime()
+{
+  return mLastVRListenerThreadActiveTime;
+}
+
 void
 VRManager::RefreshVRControllers()
 {
   nsTArray<RefPtr<gfx::VRControllerHost>> controllers;
 
   ScanForControllers();
 
   for (uint32_t i = 0; i < mManagers.Length()
@@ -437,18 +450,32 @@ VRManager::CreateVRTestSystem()
 
 template<class T>
 void
 VRManager::NotifyGamepadChange(uint32_t aIndex, const T& aInfo)
 {
   dom::GamepadChangeEventBody body(aInfo);
   dom::GamepadChangeEvent e(aIndex, dom::GamepadServiceType::VR, body);
 
+  // Due to PVRManager is at Compositor thread. We have to post
+  // tasks to Compositor thread.
+  MessageLoop* loop = CompositorThreadHolder::Loop();
+  loop->PostTask(
+    NewRunnableMethod<dom::GamepadChangeEvent>(
+                      "gfx::VRManager::NotifyGamepadChangeEventsToContent",
+                      this,
+                      &VRManager::NotifyGamepadChangeEventsToContent, e));
+}
+
+void
+VRManager::NotifyGamepadChangeEventsToContent(const dom::GamepadChangeEvent& aEvent)
+{
+  MOZ_ASSERT(NS_IsInCompositorThread());
   for (auto iter = mVRManagerParents.Iter(); !iter.Done(); iter.Next()) {
-    Unused << iter.Get()->GetKey()->SendGamepadUpdate(e);
+    Unused << iter.Get()->GetKey()->SendGamepadUpdate(aEvent);
   }
 }
 
 void
 VRManager::VibrateHaptic(uint32_t aControllerIdx, uint32_t aHapticIndex,
                          double aIntensity, double aDuration, uint32_t aPromiseID)
 
 {
@@ -464,23 +491,38 @@ VRManager::StopVibrateHaptic(uint32_t aC
   for (const auto& manager: mManagers) {
     manager->StopVibrateHaptic(aControllerIdx);
   }
 }
 
 void
 VRManager::NotifyVibrateHapticCompleted(uint32_t aPromiseID)
 {
+  // Due to PVRManager is at Compositor thread. We have to post
+  // tasks to Compositor thread.
+  MessageLoop* loop = CompositorThreadHolder::Loop();
+  loop->PostTask(
+    NewRunnableMethod<uint32_t>(
+                      "gfx::VRManager::NotifyVibrateHapticCompletedToContent",
+                      this,
+                      &VRManager::NotifyVibrateHapticCompletedToContent, aPromiseID));
+}
+
+void
+VRManager::NotifyVibrateHapticCompletedToContent(uint32_t aPromiseID)
+{
+  MOZ_ASSERT(NS_IsInCompositorThread());
   for (auto iter = mVRManagerParents.Iter(); !iter.Done(); iter.Next()) {
     Unused << iter.Get()->GetKey()->SendReplyGamepadVibrateHaptic(aPromiseID);
   }
 }
 
 void
 VRManager::DispatchSubmitFrameResult(uint32_t aDisplayID, const VRSubmitFrameResultInfo& aResult)
 {
+  MOZ_ASSERT(NS_IsInCompositorThread());
   for (auto iter = mVRManagerParents.Iter(); !iter.Done(); iter.Next()) {
     Unused << iter.Get()->GetKey()->SendDispatchSubmitFrameResult(aDisplayID, aResult);
   }
 }
 
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/vr/VRManager.h
+++ b/gfx/vr/VRManager.h
@@ -10,18 +10,18 @@
 #include "nsRefPtrHashtable.h"
 #include "nsTArray.h"
 #include "nsTHashtable.h"
 #include "nsDataHashtable.h"
 #include "mozilla/TimeStamp.h"
 #include "gfxVR.h"
 
 namespace mozilla {
-namespace layers {
-class TextureHost;
+namespace dom {
+class GamepadChangeEvent;
 }
 namespace gfx {
 
 class VRLayerParent;
 class VRManagerParent;
 class VRDisplayHost;
 
 class VRManager
@@ -47,28 +47,31 @@ public:
   RefPtr<gfx::VRControllerHost> GetController(const uint32_t& aControllerID);
   void GetVRControllerInfo(nsTArray<VRControllerInfo>& aControllerInfo);
   void CreateVRTestSystem();
   void VibrateHaptic(uint32_t aControllerIdx, uint32_t aHapticIndex,
                      double aIntensity, double aDuration, uint32_t aPromiseID);
   void StopVibrateHaptic(uint32_t aControllerIdx);
   void NotifyVibrateHapticCompleted(uint32_t aPromiseID);
   void DispatchSubmitFrameResult(uint32_t aDisplayID, const VRSubmitFrameResultInfo& aResult);
+  TimeStamp GetLastVRListenerThreadActiveTime();
 
 protected:
   VRManager();
   ~VRManager();
 
 private:
 
   void Init();
   void Destroy();
   void Shutdown();
 
   void DispatchVRDisplayInfoUpdate();
+  void NotifyGamepadChangeEventsToContent(const dom::GamepadChangeEvent& aEvent);
+  void NotifyVibrateHapticCompletedToContent(uint32_t aPromiseID);
 
   typedef nsTHashtable<nsRefPtrHashKey<VRManagerParent>> VRManagerParentSet;
   VRManagerParentSet mVRManagerParents;
 
   typedef nsTArray<RefPtr<VRSystemManager>> VRSystemManagerArray;
   VRSystemManagerArray mManagers;
 
   typedef nsRefPtrHashtable<nsUint32HashKey, gfx::VRDisplayHost> VRDisplayHostHashMap;
@@ -76,15 +79,16 @@ private:
 
   typedef nsRefPtrHashtable<nsUint32HashKey, gfx::VRControllerHost> VRControllerHostHashMap;
   VRControllerHostHashMap mVRControllers;
 
   Atomic<bool> mInitialized;
 
   TimeStamp mLastRefreshTime;
   TimeStamp mLastActiveTime;
+  TimeStamp mLastVRListenerThreadActiveTime;
   bool mVRTestSystemCreated;
 };
 
 } // namespace gfx
 } // namespace mozilla
 
 #endif // GFX_VR_MANAGER_H
--- a/gfx/vr/ipc/VRLayerParent.cpp
+++ b/gfx/vr/ipc/VRLayerParent.cpp
@@ -3,17 +3,16 @@
 /* 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 "VRLayerParent.h"
 #include "mozilla/Unused.h"
 #include "VRDisplayHost.h"
-#include "mozilla/layers/CompositorThread.h"
 
 namespace mozilla {
 using namespace layers;
 namespace gfx {
 
 VRLayerParent::VRLayerParent(uint32_t aVRDisplayID, const uint32_t aGroup)
   : mIPCOpen(true)
   , mVRDisplayID(aVRDisplayID)
@@ -60,28 +59,21 @@ VRLayerParent::Destroy()
 
 mozilla::ipc::IPCResult
 VRLayerParent::RecvSubmitFrame(const layers::SurfaceDescriptor &aTexture,
                                const uint64_t& aFrameId,
                                const gfx::Rect& aLeftEyeRect,
                                const gfx::Rect& aRightEyeRect)
 {
   if (mVRDisplayID) {
-    MessageLoop* loop = layers::CompositorThreadHolder::Loop();
     VRManager* vm = VRManager::Get();
     RefPtr<VRDisplayHost> display = vm->GetDisplay(mVRDisplayID);
     if (display) {
-      // Because VR compositor still shares the same graphics device with Compositor thread.
-      // We have to post sumbit frame tasks to Compositor thread.
-      // TODO: Move SubmitFrame to Bug 1392217.
-      loop->PostTask(NewRunnableMethod<VRDisplayHost*, const layers::SurfaceDescriptor, uint64_t,
-                                       const gfx::Rect&, const gfx::Rect&>(
-                     "gfx::VRLayerParent::SubmitFrame",
-                     this,
-                     &VRLayerParent::SubmitFrame, display, aTexture, aFrameId, aLeftEyeRect, aRightEyeRect));
+      // TODO: Move SubmitFrame to VRSubmitFrame thread in Bug 1392217.
+      SubmitFrame(display, aTexture, aFrameId, aLeftEyeRect, aRightEyeRect);
     }
   }
 
   return IPC_OK();
 }
 
 void
 VRLayerParent::SubmitFrame(VRDisplayHost* aDisplay, const layers::SurfaceDescriptor& aTexture,
--- a/gfx/vr/ipc/VRManagerChild.cpp
+++ b/gfx/vr/ipc/VRManagerChild.cpp
@@ -113,17 +113,17 @@ VRManagerChild::ReinitForContent(Endpoin
 VRManagerChild::InitSameProcess()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!sVRManagerChildSingleton);
 
   sVRManagerChildSingleton = new VRManagerChild();
   sVRManagerParentSingleton = VRManagerParent::CreateSameProcess();
   sVRManagerChildSingleton->Open(sVRManagerParentSingleton->GetIPCChannel(),
-                                 VRListenerThreadHolder::Loop(),
+                                 mozilla::layers::CompositorThreadHolder::Loop(),
                                  mozilla::ipc::ChildSide);
 }
 
 /* static */ void
 VRManagerChild::InitWithGPUProcess(Endpoint<PVRManagerChild>&& aEndpoint)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!sVRManagerChildSingleton);
--- a/gfx/vr/ipc/VRManagerParent.cpp
+++ b/gfx/vr/ipc/VRManagerParent.cpp
@@ -81,106 +81,123 @@ VRManagerParent::UnregisterFromManager()
   VRManager* vm = VRManager::Get();
   vm->RemoveVRManagerParent(this);
   mVRManagerHolder = nullptr;
 }
 
 /* static */ bool
 VRManagerParent::CreateForContent(Endpoint<PVRManagerParent>&& aEndpoint)
 {
-  MessageLoop* loop = VRListenerThreadHolder::Loop();
-
+  MessageLoop* loop = CompositorThreadHolder::Loop();
   RefPtr<VRManagerParent> vmp = new VRManagerParent(aEndpoint.OtherPid(), true);
   loop->PostTask(NewRunnableMethod<Endpoint<PVRManagerParent>&&>(
     "gfx::VRManagerParent::Bind",
     vmp,
     &VRManagerParent::Bind,
     Move(aEndpoint)));
-
   return true;
 }
 
 void
 VRManagerParent::Bind(Endpoint<PVRManagerParent>&& aEndpoint)
 {
   if (!aEndpoint.Bind(this)) {
     return;
   }
   mSelfRef = this;
 
   RegisterWithManager();
 }
 
 /*static*/ void
-VRManagerParent::RegisterVRManagerInVRListenerThread(VRManagerParent* aVRManager)
+VRManagerParent::RegisterVRManagerInCompositorThread(VRManagerParent* aVRManager)
 {
   aVRManager->RegisterWithManager();
 }
 
 /*static*/ VRManagerParent*
 VRManagerParent::CreateSameProcess()
 {
-  MessageLoop* loop = VRListenerThreadHolder::Loop();
+  MessageLoop* loop = CompositorThreadHolder::Loop();
   RefPtr<VRManagerParent> vmp = new VRManagerParent(base::GetCurrentProcId(), false);
-  vmp->mVRListenerThreadHolder = VRListenerThreadHolder::GetSingleton();
+  vmp->mCompositorThreadHolder = CompositorThreadHolder::GetSingleton();
   vmp->mSelfRef = vmp;
-  loop->PostTask(NewRunnableFunction(RegisterVRManagerInVRListenerThread, vmp.get()));
+  loop->PostTask(NewRunnableFunction(RegisterVRManagerInCompositorThread, vmp.get()));
   return vmp.get();
 }
 
 bool
 VRManagerParent::CreateForGPUProcess(Endpoint<PVRManagerParent>&& aEndpoint)
 {
-  MessageLoop* loop = VRListenerThreadHolder::Loop();
+  MessageLoop* loop = CompositorThreadHolder::Loop();
 
   RefPtr<VRManagerParent> vmp = new VRManagerParent(aEndpoint.OtherPid(), false);
-  vmp->mVRListenerThreadHolder = VRListenerThreadHolder::GetSingleton();
+  vmp->mCompositorThreadHolder = CompositorThreadHolder::GetSingleton();
   loop->PostTask(NewRunnableMethod<Endpoint<PVRManagerParent>&&>(
     "gfx::VRManagerParent::Bind",
     vmp,
     &VRManagerParent::Bind,
     Move(aEndpoint)));
   return true;
 }
 
 void
 VRManagerParent::DeferredDestroy()
 {
-  mVRListenerThreadHolder = nullptr;
+  mCompositorThreadHolder = nullptr;
   mSelfRef = nullptr;
 }
 
 void
+VRManagerParent::RefreshDisplays()
+{
+  // This is called to refresh the VR Displays for Navigator.GetVRDevices().
+  // We must pass "true" to VRManager::RefreshVRDisplays()
+  // to ensure that the promise returned by Navigator.GetVRDevices
+  // can resolve even if there are no changes to the VR Displays.
+  VRManager* vm = VRManager::Get();
+  MessageLoop* loop = VRListenerThreadHolder::Loop();
+  loop->PostTask(
+    NewRunnableMethod<bool>(
+      "gfx::VRManager::RefreshVRDisplays",
+      vm, &VRManager::RefreshVRDisplays, true));
+}
+
+void
 VRManagerParent::ActorDestroy(ActorDestroyReason why)
 {
   UnregisterFromManager();
   MessageLoop::current()->PostTask(
     NewRunnableMethod("gfx::VRManagerParent::DeferredDestroy",
                       this,
                       &VRManagerParent::DeferredDestroy));
 }
 
 void
 VRManagerParent::OnChannelConnected(int32_t aPid)
 {
-  mVRListenerThreadHolder = VRListenerThreadHolder::GetSingleton();
+  mCompositorThreadHolder = CompositorThreadHolder::GetSingleton();
 }
 
 mozilla::ipc::IPCResult
 VRManagerParent::RecvRefreshDisplays()
 {
-  // TODO: Bug 1406327, Launch VR listener thread here.
-  MOZ_ASSERT(VRListenerThreadHolder::IsInVRListenerThread());
+  MOZ_ASSERT(NS_IsInCompositorThread());
 
-  // This is called to refresh the VR Displays for Navigator.GetVRDevices().
-  // We must pass "true" to VRManager::RefreshVRDisplays()
-  // to ensure that the promise returned by Navigator.GetVRDevices
-  // can resolve even if there are no changes to the VR Displays.
-  VRManager* vm = VRManager::Get();
-  vm->RefreshVRDisplays(true);
+  // When receiving refresh display messages at the first time,
+  // it is time to start the VR listener thread.
+  // Spawning threads needs to be at the main thread.
+  if (!VRListenerThreadHolder::IsActive()) {
+    RefPtr<Runnable> runnable = NewRunnableMethod(
+      "gfx::VRManagerParent::StartVRListenerThread",
+      this, &VRManagerParent::StartVRListenerThread);
+    NS_DispatchToMainThread(runnable.forget());
+  } else {
+    RefreshDisplays();
+  }
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 VRManagerParent::RecvResetSensor(const uint32_t& aDisplayID)
 {
   VRManager* vm = VRManager::Get();
@@ -198,16 +215,23 @@ VRManagerParent::RecvSetGroupMask(const 
   VRManager* vm = VRManager::Get();
   RefPtr<gfx::VRDisplayHost> display = vm->GetDisplay(aDisplayID);
   if (display != nullptr) {
     display->SetGroupMask(aGroupMask);
   }
   return IPC_OK();
 }
 
+void
+VRManagerParent::StartVRListenerThread()
+{
+  VRListenerThreadHolder::Start();
+  RefreshDisplays();
+}
+
 bool
 VRManagerParent::HaveEventListener()
 {
   return mHaveEventListener;
 }
 
 bool
 VRManagerParent::HaveControllerListener()
--- a/gfx/vr/ipc/VRManagerParent.h
+++ b/gfx/vr/ipc/VRManagerParent.h
@@ -2,26 +2,25 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 MOZILLA_GFX_VR_VRMANAGERPARENT_H
 #define MOZILLA_GFX_VR_VRMANAGERPARENT_H
 
-#include "mozilla/layers/CompositableTransactionParent.h"  // need?
+#include "mozilla/layers/CompositorThread.h" // for CompositorThreadHolder
 #include "mozilla/gfx/PVRManagerParent.h" // for PVRManagerParent
 #include "mozilla/gfx/PVRLayerParent.h"   // for PVRLayerParent
 #include "mozilla/ipc/ProtocolUtils.h"    // for IToplevelProtocol
 #include "mozilla/TimeStamp.h"            // for TimeStamp
 #include "gfxVR.h"                        // for VRFieldOfView
 #include "VRThread.h"                     // for VRListenerThreadHolder
 
 namespace mozilla {
-using namespace layers;
 namespace gfx {
 
 class VRManager;
 
 namespace impl {
 class VRDisplayPuppet;
 class VRControllerPuppet;
 } // namespace impl
@@ -36,16 +35,17 @@ public:
   static bool CreateForGPUProcess(Endpoint<PVRManagerParent>&& aEndpoint);
   static bool CreateForContent(Endpoint<PVRManagerParent>&& aEndpoint);
 
   bool IsSameProcess() const;
   bool HaveEventListener();
   bool HaveControllerListener();
   bool SendGamepadUpdate(const GamepadChangeEvent& aGamepadEvent);
   bool SendReplyGamepadVibrateHaptic(const uint32_t& aPromiseID);
+  void StartVRListenerThread();
 
 protected:
   ~VRManagerParent();
 
   virtual PVRLayerParent* AllocPVRLayerParent(const uint32_t& aDisplayID,
                                               const uint32_t& aGroup) override;
   virtual bool DeallocPVRLayerParent(PVRLayerParent* actor) override;
 
@@ -75,24 +75,27 @@ protected:
   virtual mozilla::ipc::IPCResult RecvNewPoseMoveToMockController(const uint32_t& aDeviceID, const GamepadPoseState& pose) override;
 
 private:
   void RegisterWithManager();
   void UnregisterFromManager();
 
   void Bind(Endpoint<PVRManagerParent>&& aEndpoint);
 
-  static void RegisterVRManagerInVRListenerThread(VRManagerParent* aVRManager);
+  static void RegisterVRManagerInCompositorThread(VRManagerParent* aVRManager);
 
   void DeferredDestroy();
+  void RefreshDisplays();
 
   // This keeps us alive until ActorDestroy(), at which point we do a
   // deferred destruction of ourselves.
   RefPtr<VRManagerParent> mSelfRef;
-  RefPtr<VRListenerThreadHolder> mVRListenerThreadHolder;
+
+  // Keep the compositor thread alive, until we have destroyed ourselves.
+  RefPtr<layers::CompositorThreadHolder> mCompositorThreadHolder;
 
   // Keep the VRManager alive, until we have destroyed ourselves.
   RefPtr<VRManager> mVRManagerHolder;
   nsRefPtrHashtable<nsUint32HashKey, impl::VRDisplayPuppet> mVRDisplayTests;
   nsRefPtrHashtable<nsUint32HashKey, impl::VRControllerPuppet> mVRControllerTests;
   uint32_t mDisplayTestID;
   uint32_t mControllerTestID;
   bool mHaveEventListener;