Bug 1305892 - Support Oculus Touch Haptic Feedback; r=kip

MozReview-Commit-ID: 7V931IAcbbx

--HG--
extra : rebase_source : 3423aa7a99b79293783cb1572f8ad3d7a257c7c7
This commit is contained in:
Daosheng Mu 2017-03-29 17:58:11 +08:00
Родитель 3a4bd0aae6
Коммит 22ebee8da0
3 изменённых файлов: 180 добавлений и 4 удалений

Просмотреть файл

@ -29,6 +29,7 @@
#include "gfxVROculus.h"
#include "mozilla/layers/CompositorThread.h"
#include "mozilla/dom/GamepadEventTypes.h"
#include "mozilla/dom/GamepadBinding.h"
@ -145,7 +146,7 @@ enum class OculusRightControllerButtonType : uint16_t {
static const uint32_t kNumOculusButton = static_cast<uint32_t>
(OculusLeftControllerButtonType::
NumButtonType);
static const uint32_t kNumOculusHaptcs = 0; // TODO: Bug 1305892
static const uint32_t kNumOculusHaptcs = 1;
static bool
@ -886,6 +887,8 @@ VRControllerOculus::VRControllerOculus(dom::GamepadHand aHand)
: VRControllerHost(VRDeviceType::Oculus)
, mIndexTrigger(0.0f)
, mHandTrigger(0.0f)
, mVibrateThread(nullptr)
, mIsVibrateStopped(false)
{
MOZ_COUNT_CTOR_INHERITED(VRControllerOculus, VRControllerHost);
@ -957,6 +960,144 @@ VRControllerOculus::~VRControllerOculus()
MOZ_COUNT_DTOR_INHERITED(VRControllerOculus, VRControllerHost);
}
void
VRControllerOculus::UpdateVibrateHaptic(ovrSession aSession,
uint32_t aHapticIndex,
double aIntensity,
double aDuration,
uint64_t aVibrateIndex,
uint32_t aPromiseID)
{
// UpdateVibrateHaptic() only can be called by mVibrateThread
MOZ_ASSERT(mVibrateThread == NS_GetCurrentThread());
// It has been interrupted by loss focus.
if (mIsVibrateStopped) {
VibrateHapticComplete(aSession, aPromiseID, true);
return;
}
// Avoid the previous vibrate event to override the new one.
if (mVibrateIndex != aVibrateIndex) {
VibrateHapticComplete(aSession, aPromiseID, false);
return;
}
const double duration = (aIntensity == 0) ? 0 : aDuration;
// Vibration amplitude in the [0.0, 1.0] range.
const float amplitude = aIntensity > 1.0 ? 1.0 : aIntensity;
// Vibration is enabled by specifying the frequency.
// Specifying 0.0f will disable the vibration, 0.5f will vibrate at 160Hz,
// and 1.0f will vibrate at 320Hz.
const float frequency = (duration > 0) ? 1.0f : 0.0f;
ovrControllerType hand;
switch (GetHand()) {
case GamepadHand::Left:
hand = ovrControllerType::ovrControllerType_LTouch;
break;
case GamepadHand::Right:
hand = ovrControllerType::ovrControllerType_RTouch;
break;
default:
MOZ_ASSERT(false);
break;
}
// Oculus Touch only can get the response from ovr_SetControllerVibration()
// at the presenting mode.
ovrResult result = ovr_SetControllerVibration(aSession, hand, frequency,
(frequency == 0.0f) ? 0.0f : amplitude);
if (result != ovrSuccess) {
printf_stderr("%s hand ovr_SetControllerVibration skipped.\n",
GamepadHandValues::strings[uint32_t(GetHand())].value);
}
// In Oculus dev doc, it mentions vibration lasts for a maximum of 2.5 seconds
// at ovr_SetControllerVibration(), but we found 2.450 sec is more close to the
// real looping use case.
const double kVibrateRate = 2450.0;
const double remainingTime = (duration > kVibrateRate)
? (duration - kVibrateRate) : duration;
if (remainingTime) {
MOZ_ASSERT(mVibrateThread);
RefPtr<Runnable> runnable =
NewRunnableMethod<ovrSession, uint32_t, double, double, uint64_t, uint32_t>
(this, &VRControllerOculus::UpdateVibrateHaptic, aSession,
aHapticIndex, aIntensity, (duration > kVibrateRate) ? remainingTime : 0, aVibrateIndex, aPromiseID);
NS_DelayedDispatchToCurrentThread(runnable.forget(),
(duration > kVibrateRate) ? kVibrateRate : remainingTime);
} else {
VibrateHapticComplete(aSession, aPromiseID, true);
}
}
void
VRControllerOculus::VibrateHapticComplete(ovrSession aSession, uint32_t aPromiseID,
bool aStop)
{
if (aStop) {
ovrControllerType hand;
switch (GetHand()) {
case GamepadHand::Left:
hand = ovrControllerType::ovrControllerType_LTouch;
break;
case GamepadHand::Right:
hand = ovrControllerType::ovrControllerType_RTouch;
break;
default:
MOZ_ASSERT(false);
break;
}
ovrResult result = ovr_SetControllerVibration(aSession, hand, 0.0f, 0.0f);
if (result != ovrSuccess) {
printf_stderr("%s Haptics skipped.\n",
GamepadHandValues::strings[uint32_t(GetHand())].value);
}
}
VRManager *vm = VRManager::Get();
MOZ_ASSERT(vm);
CompositorThreadHolder::Loop()->PostTask(NewRunnableMethod<uint32_t>
(vm, &VRManager::NotifyVibrateHapticCompleted, aPromiseID));
}
void
VRControllerOculus::VibrateHaptic(ovrSession aSession,
uint32_t aHapticIndex,
double aIntensity,
double aDuration,
uint32_t aPromiseID)
{
// 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.");
}
}
++mVibrateIndex;
mIsVibrateStopped = false;
RefPtr<Runnable> runnable =
NewRunnableMethod<ovrSession, uint32_t, double, double, uint64_t, uint32_t>
(this, &VRControllerOculus::UpdateVibrateHaptic, aSession,
aHapticIndex, aIntensity, aDuration, mVibrateIndex, aPromiseID);
mVibrateThread->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
}
void
VRControllerOculus::StopVibrateHaptic()
{
mIsVibrateStopped = true;
}
/*static*/ already_AddRefed<VRSystemManagerOculus>
VRSystemManagerOculus::Create()
{
@ -1262,12 +1403,31 @@ VRSystemManagerOculus::VibrateHaptic(uint32_t aControllerIdx,
double aDuration,
uint32_t aPromiseID)
{
// TODO: Bug 1305892
// mSession is available after VRDisplay is created
// at GetHMDs().
if (!mSession) {
return;
}
RefPtr<impl::VRControllerOculus> controller = mOculusController[aControllerIdx];
MOZ_ASSERT(controller);
controller->VibrateHaptic(mSession, aHapticIndex, aIntensity, aDuration, aPromiseID);
}
void
VRSystemManagerOculus::StopVibrateHaptic(uint32_t aControllerIdx)
{
// mSession is available after VRDisplay is created
// at GetHMDs().
if (!mSession) {
return;
}
RefPtr<impl::VRControllerOculus> controller = mOculusController[aControllerIdx];
MOZ_ASSERT(controller);
controller->StopVibrateHaptic();
}
void

Просмотреть файл

@ -103,15 +103,31 @@ public:
void SetIndexTrigger(float aValue);
float GetHandTrigger();
void SetHandTrigger(float aValue);
void VibrateHaptic(ovrSession aSession,
uint32_t aHapticIndex,
double aIntensity,
double aDuration,
uint32_t aPromiseID);
void StopVibrateHaptic();
protected:
virtual ~VRControllerOculus();
private:
void UpdateVibrateHaptic(ovrSession aSession,
uint32_t aHapticIndex,
double aIntensity,
double aDuration,
uint64_t aVibrateIndex,
uint32_t aPromiseID);
void VibrateHapticComplete(ovrSession aSession, uint32_t aPromiseID, bool aStop);
float mAxisMove[static_cast<uint32_t>(
OculusControllerAxisType::NumVRControllerAxisType)];
float mIndexTrigger;
float mHandTrigger;
nsCOMPtr<nsIThread> mVibrateThread;
Atomic<bool> mIsVibrateStopped;
};
} // namespace impl

Просмотреть файл

@ -462,11 +462,11 @@ VRControllerOpenVR::UpdateVibrateHaptic(vr::IVRSystem* aVRSystem,
return;
}
double duration = (aIntensity == 0) ? 0 : aDuration;
const double duration = (aIntensity == 0) ? 0 : aDuration;
// We expect OpenVR to vibrate for 5 ms, but we found it only response the
// commend ~ 3.9 ms. For duration time longer than 3.9 ms, we separate them
// to a loop of 3.9 ms for make users feel that is a continuous events.
uint32_t microSec = (duration < 3.9 ? duration : 3.9) * 1000 * aIntensity;
const uint32_t microSec = (duration < 3.9 ? duration : 3.9) * 1000 * aIntensity;
aVRSystem->TriggerHapticPulse(GetTrackedIndex(),
aHapticIndex, microSec);