Bug 1392217 - Part 1: Refactor the VR vibrate thread to be as a VRThread and manage its lifetime; r?kip
MozReview-Commit-ID: 7svQQGxDT6j
--- a/gfx/vr/VRThread.cpp
+++ b/gfx/vr/VRThread.cpp
@@ -1,24 +1,25 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
-
#include "VRThread.h"
#include "nsThreadUtils.h"
namespace mozilla {
namespace gfx {
static StaticRefPtr<VRListenerThreadHolder> sVRListenerThreadHolder;
static bool sFinishedVRListenerShutDown = false;
+static const uint32_t kDefaultThreadLifeTime = 60; // in 60 seconds.
+static const uint32_t kDelayPostTaskTime = 20000; // in 20000 ms.
VRListenerThreadHolder* GetVRListenerThreadHolder()
{
return sVRListenerThreadHolder;
}
base::Thread*
VRListenerThread()
@@ -109,16 +110,133 @@ VRListenerThreadHolder::Shutdown()
/* static */ bool
VRListenerThreadHolder::IsInVRListenerThread()
{
return VRListenerThread() &&
VRListenerThread()->thread_id() == PlatformThread::CurrentId();
}
-} // namespace gfx
-} // namespace mozilla
+VRThread::VRThread(const nsCString& aName)
+ : mThread(nullptr)
+ , mLifeTime(kDefaultThreadLifeTime)
+ , mStarted(false)
+{
+ mName = aName;
+}
+
+VRThread::~VRThread()
+{
+ Shutdown();
+}
+
+void
+VRThread::Start()
+{
+ MOZ_ASSERT(VRListenerThreadHolder::IsInVRListenerThread());
+
+ if (!mThread) {
+ nsresult rv = NS_NewNamedThread(mName, getter_AddRefs(mThread));
+ MOZ_ASSERT(mThread);
+
+ if (NS_FAILED(rv)) {
+ MOZ_ASSERT(false, "Failed to create a vr thread.");
+ }
+ RefPtr<Runnable> runnable =
+ NewRunnableMethod<TimeStamp>(
+ "gfx::VRThread::CheckLife", this, &VRThread::CheckLife, TimeStamp::Now());
+ // Post it to the main thread for tracking the lifetime.
+ nsCOMPtr<nsIThread> mainThread;
+ rv = NS_GetMainThread(getter_AddRefs(mainThread));
+ if (NS_FAILED(rv)) {
+ NS_WARNING("VRThread::Start() could not get Main thread");
+ return;
+ }
+ mainThread->DelayedDispatch(runnable.forget(), kDelayPostTaskTime);
+ }
+ mStarted = true;
+ mLastActiveTime = TimeStamp::Now();
+}
+
+void
+VRThread::Shutdown()
+{
+ if (mThread) {
+ mThread->Shutdown();
+ mThread = nullptr;
+ }
+ mStarted = false;
+}
+
+const nsCOMPtr<nsIThread>
+VRThread::GetThread() const
+{
+ return mThread;
+}
+
+void
+VRThread::PostTask(already_AddRefed<Runnable> aTask)
+{
+ PostDelayedTask(Move(aTask), 0);
+}
+
+void
+VRThread::PostDelayedTask(already_AddRefed<Runnable> aTask,
+ uint32_t aTime)
+{
+ MOZ_ASSERT(mStarted, "Must call Start() before posting tasks.");
+ MOZ_ASSERT(mThread);
+ mLastActiveTime = TimeStamp::Now();
+
+ if (!aTime) {
+ mThread->Dispatch(Move(aTask), NS_DISPATCH_NORMAL);
+ } else {
+ mThread->DelayedDispatch(Move(aTask), aTime);
+ }
+}
+
+void
+VRThread::CheckLife(TimeStamp aCheckTimestamp)
+{
+ // VR system is going to shutdown.
+ if (!mStarted) {
+ Shutdown();
+ return;
+ }
+
+ const TimeDuration timeout = TimeDuration::FromSeconds(mLifeTime);
+ if ((aCheckTimestamp - mLastActiveTime) > timeout) {
+ Shutdown();
+ } else {
+ RefPtr<Runnable> runnable =
+ NewRunnableMethod<TimeStamp>(
+ "gfx::VRThread::CheckLife", this, &VRThread::CheckLife, TimeStamp::Now());
+ // Post it to the main thread for tracking the lifetime.
+ nsCOMPtr<nsIThread> mainThread;
+ nsresult rv = NS_GetMainThread(getter_AddRefs(mainThread));
+ if (NS_FAILED(rv)) {
+ NS_WARNING("VRThread::CheckLife() could not get Main thread");
+ return;
+ }
+ mainThread->DelayedDispatch(runnable.forget(), kDelayPostTaskTime);
+ }
+}
+
+void
+VRThread::SetLifeTime(uint32_t aLifeTime)
+{
+ mLifeTime = aLifeTime;
+}
+
+uint32_t
+VRThread::GetLifeTime()
+{
+ return mLifeTime;
+}
bool
-NS_IsInVRListenerThread()
+VRThread::IsActive()
{
- return mozilla::gfx::VRListenerThreadHolder::IsInVRListenerThread();
-}
\ No newline at end of file
+ return !!mThread;
+}
+
+} // namespace gfx
+} // namespace mozilla
--- a/gfx/vr/VRThread.h
+++ b/gfx/vr/VRThread.h
@@ -41,12 +41,40 @@ private:
base::Thread* const mThread;
static base::Thread* CreateThread();
static void DestroyThread(base::Thread* aThread);
};
base::Thread* VRListenerThread();
+class VRThread final
+{
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VRThread)
+
+public:
+ explicit VRThread(const nsCString& aName);
+
+ void Start();
+ void Shutdown();
+ void SetLifeTime(uint32_t aLifeTime);
+ uint32_t GetLifeTime();
+ void CheckLife(TimeStamp aCheckTimestamp);
+ void PostTask(already_AddRefed<Runnable> aTask);
+ void PostDelayedTask(already_AddRefed<Runnable> aTask, uint32_t aTime);
+ const nsCOMPtr<nsIThread> GetThread() const;
+ bool IsActive();
+
+protected:
+ ~VRThread();
+
+private:
+ nsCOMPtr<nsIThread> mThread;
+ TimeStamp mLastActiveTime;
+ nsCString mName;
+ uint32_t mLifeTime;
+ Atomic<bool> mStarted;
+};
+
} // namespace gfx
} // namespace mozilla
#endif // GFX_VR_THREAD_H
\ No newline at end of file
--- a/gfx/vr/gfxVROculus.cpp
+++ b/gfx/vr/gfxVROculus.cpp
@@ -1342,17 +1342,17 @@ void
VRControllerOculus::UpdateVibrateHaptic(ovrSession aSession,
uint32_t aHapticIndex,
double aIntensity,
double aDuration,
uint64_t aVibrateIndex,
const VRManagerPromise& aPromise)
{
// UpdateVibrateHaptic() only can be called by mVibrateThread
- MOZ_ASSERT(mVibrateThread == NS_GetCurrentThread());
+ MOZ_ASSERT(mVibrateThread->GetThread() == NS_GetCurrentThread());
// It has been interrupted by loss focus.
if (mIsVibrateStopped) {
VibrateHapticComplete(aSession, aPromise, true);
return;
}
// Avoid the previous vibrate event to override the new one.
if (mVibrateIndex != aVibrateIndex) {
@@ -1401,18 +1401,17 @@ VRControllerOculus::UpdateVibrateHaptic(
MOZ_ASSERT(mVibrateThread);
RefPtr<Runnable> runnable =
NewRunnableMethod<ovrSession, uint32_t, double, double, uint64_t,
StoreCopyPassByConstLRef<VRManagerPromise>>(
"VRControllerOculus::UpdateVibrateHaptic",
this, &VRControllerOculus::UpdateVibrateHaptic, aSession,
aHapticIndex, aIntensity, (duration > kVibrateRate) ? remainingTime : 0, aVibrateIndex, aPromise);
- NS_DelayedDispatchToCurrentThread(runnable.forget(),
- (duration > kVibrateRate) ? kVibrateRate : remainingTime);
+ mVibrateThread->PostDelayedTask(runnable.forget(), (duration > kVibrateRate) ? kVibrateRate : remainingTime);
} else {
VibrateHapticComplete(aSession, aPromise, true);
}
}
void
VRControllerOculus::VibrateHapticComplete(ovrSession aSession, const VRManagerPromise& aPromise,
bool aStop)
@@ -1452,33 +1451,29 @@ void
VRControllerOculus::VibrateHaptic(ovrSession aSession,
uint32_t aHapticIndex,
double aIntensity,
double aDuration,
const VRManagerPromise& aPromise)
{
// Spinning up the haptics thread at the first haptics call.
if (!mVibrateThread) {
- nsresult rv = NS_NewThread(getter_AddRefs(mVibrateThread));
- MOZ_ASSERT(mVibrateThread);
-
- if (NS_FAILED(rv)) {
- MOZ_ASSERT(false, "Failed to create async thread.");
- }
+ mVibrateThread = new VRThread(NS_LITERAL_CSTRING("Oculus_Vibration"));
}
+ mVibrateThread->Start();
++mVibrateIndex;
mIsVibrateStopped = false;
RefPtr<Runnable> runnable =
- NewRunnableMethod<ovrSession, uint32_t, double, double, uint64_t,
- StoreCopyPassByConstLRef<VRManagerPromise>>(
- "VRControllerOculus::UpdateVibrateHaptic",
- this, &VRControllerOculus::UpdateVibrateHaptic, aSession,
- aHapticIndex, aIntensity, aDuration, mVibrateIndex, aPromise);
- mVibrateThread->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
+ NewRunnableMethod<ovrSession, uint32_t, double, double, uint64_t,
+ StoreCopyPassByConstLRef<VRManagerPromise>>(
+ "VRControllerOculus::UpdateVibrateHaptic",
+ this, &VRControllerOculus::UpdateVibrateHaptic, aSession,
+ aHapticIndex, aIntensity, aDuration, mVibrateIndex, aPromise);
+ mVibrateThread->PostTask(runnable.forget());
}
void
VRControllerOculus::StopVibrateHaptic()
{
mIsVibrateStopped = true;
}
--- a/gfx/vr/gfxVROculus.h
+++ b/gfx/vr/gfxVROculus.h
@@ -22,16 +22,18 @@ struct ID3D11Device;
namespace mozilla {
namespace layers {
class CompositingRenderTargetD3D11;
struct VertexShaderConstants;
struct PixelShaderConstants;
}
namespace gfx {
+class VRThread;
+
namespace impl {
enum class OculusControllerAxisType : uint16_t {
ThumbstickXAxis,
ThumbstickYAxis,
NumVRControllerAxisType
};
@@ -159,17 +161,17 @@ private:
uint64_t aVibrateIndex,
const VRManagerPromise& aPromise);
void VibrateHapticComplete(ovrSession aSession, const VRManagerPromise& aPromise, bool aStop);
float mAxisMove[static_cast<uint32_t>(
OculusControllerAxisType::NumVRControllerAxisType)];
float mIndexTrigger;
float mHandTrigger;
- nsCOMPtr<nsIThread> mVibrateThread;
+ RefPtr<VRThread> mVibrateThread;
Atomic<bool> mIsVibrateStopped;
};
} // namespace impl
class VRSystemManagerOculus : public VRSystemManager
{
public:
--- a/gfx/vr/gfxVROpenVR.cpp
+++ b/gfx/vr/gfxVROpenVR.cpp
@@ -509,17 +509,17 @@ void
VRControllerOpenVR::UpdateVibrateHaptic(::vr::IVRSystem* aVRSystem,
uint32_t aHapticIndex,
double aIntensity,
double aDuration,
uint64_t aVibrateIndex,
const VRManagerPromise& aPromise)
{
// UpdateVibrateHaptic() only can be called by mVibrateThread
- MOZ_ASSERT(mVibrateThread == NS_GetCurrentThread());
+ MOZ_ASSERT(mVibrateThread->GetThread() == NS_GetCurrentThread());
// It has been interrupted by loss focus.
if (mIsVibrateStopped) {
VibrateHapticComplete(aPromise);
return;
}
// Avoid the previous vibrate event to override the new one.
if (mVibrateIndex != aVibrateIndex) {
@@ -535,24 +535,25 @@ VRControllerOpenVR::UpdateVibrateHaptic(
aVRSystem->TriggerHapticPulse(GetTrackedIndex(),
aHapticIndex, microSec);
// In OpenVR spec, it mentions TriggerHapticPulse() may not trigger another haptic pulse
// on this controller and axis combination for 5ms.
const double kVibrateRate = 5.0;
if (duration >= kVibrateRate) {
MOZ_ASSERT(mVibrateThread);
+ MOZ_ASSERT(mVibrateThread->IsActive());
RefPtr<Runnable> runnable =
NewRunnableMethod<::vr::IVRSystem*, uint32_t, double, double, uint64_t,
StoreCopyPassByConstLRef<VRManagerPromise>>(
"VRControllerOpenVR::UpdateVibrateHaptic",
this, &VRControllerOpenVR::UpdateVibrateHaptic, aVRSystem,
aHapticIndex, aIntensity, duration - kVibrateRate, aVibrateIndex, aPromise);
- NS_DelayedDispatchToCurrentThread(runnable.forget(), kVibrateRate);
+ mVibrateThread->PostDelayedTask(runnable.forget(), kVibrateRate);
} else {
// The pulse has completed
VibrateHapticComplete(aPromise);
}
}
void
VRControllerOpenVR::VibrateHapticComplete(const VRManagerPromise& aPromise)
@@ -568,33 +569,29 @@ void
VRControllerOpenVR::VibrateHaptic(::vr::IVRSystem* aVRSystem,
uint32_t aHapticIndex,
double aIntensity,
double aDuration,
const VRManagerPromise& aPromise)
{
// Spinning up the haptics thread at the first haptics call.
if (!mVibrateThread) {
- nsresult rv = NS_NewThread(getter_AddRefs(mVibrateThread));
- MOZ_ASSERT(mVibrateThread);
-
- if (NS_FAILED(rv)) {
- MOZ_ASSERT(false, "Failed to create async thread.");
- }
+ mVibrateThread = new VRThread(NS_LITERAL_CSTRING("OpenVR_Vibration"));
}
+ mVibrateThread->Start();
++mVibrateIndex;
mIsVibrateStopped = false;
RefPtr<Runnable> runnable =
- NewRunnableMethod<::vr::IVRSystem*, uint32_t, double, double, uint64_t,
- StoreCopyPassByConstLRef<VRManagerPromise>>(
- "VRControllerOpenVR::UpdateVibrateHaptic",
- this, &VRControllerOpenVR::UpdateVibrateHaptic, aVRSystem,
- aHapticIndex, aIntensity, aDuration, mVibrateIndex, aPromise);
- mVibrateThread->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
+ NewRunnableMethod<::vr::IVRSystem*, uint32_t, double, double, uint64_t,
+ StoreCopyPassByConstLRef<VRManagerPromise>>(
+ "VRControllerOpenVR::UpdateVibrateHaptic",
+ this, &VRControllerOpenVR::UpdateVibrateHaptic, aVRSystem,
+ aHapticIndex, aIntensity, aDuration, mVibrateIndex, aPromise);
+ mVibrateThread->PostTask(runnable.forget());
}
void
VRControllerOpenVR::StopVibrateHaptic()
{
mIsVibrateStopped = true;
}
--- a/gfx/vr/gfxVROpenVR.h
+++ b/gfx/vr/gfxVROpenVR.h
@@ -4,32 +4,33 @@
* 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_OPENVR_H
#define GFX_VR_OPENVR_H
#include "nsTArray.h"
#include "nsIScreen.h"
-#include "nsIThread.h"
#include "nsCOMPtr.h"
#include "mozilla/RefPtr.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/EnumeratedArray.h"
#include "openvr.h"
#include "gfxVR.h"
#include "VRDisplayHost.h"
#if defined(XP_MACOSX)
class MacIOSurface;
#endif
namespace mozilla {
namespace gfx {
+class VRThread;
+
namespace impl {
class VRDisplayOpenVR : public VRDisplayHost
{
public:
virtual void NotifyVSync() override;
void ZeroSensor() override;
bool GetIsHmdPresent();
@@ -109,17 +110,17 @@ private:
uint64_t aVibrateIndex,
const VRManagerPromise& aPromise);
void VibrateHapticComplete(const VRManagerPromise& aPromise);
// The index of tracked devices from ::vr::IVRSystem.
uint32_t mTrackedIndex;
nsTArray<float> mTrigger;
nsTArray<float> mAxisMove;
- nsCOMPtr<nsIThread> mVibrateThread;
+ RefPtr<VRThread> mVibrateThread;
Atomic<bool> mIsVibrateStopped;
};
} // namespace impl
class VRSystemManagerOpenVR : public VRSystemManager
{
public: