Bug 1406327 - Part 2: When loading VR content, launching the VR listener thread; r?kip
MozReview-Commit-ID: IyBzJyDEVdv
--- 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;