gecko-dev/gfx/vr/gfxVRExternal.cpp

899 строки
27 KiB
C++
Исходник Обычный вид История

/* -*- 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 <math.h>
#include "prlink.h"
#include "prenv.h"
#include "gfxPrefs.h"
#include "mozilla/Preferences.h"
#include "mozilla/gfx/Quaternion.h"
#ifdef XP_WIN
#include "CompositorD3D11.h"
#include "TextureD3D11.h"
static const char* kShmemName = "moz.gecko.vr_ext.0.0.1";
#elif defined(XP_MACOSX)
#include "mozilla/gfx/MacIOSurface.h"
#include <sys/mman.h>
#include <sys/stat.h> /* For mode constants */
#include <fcntl.h> /* For O_* constants */
#include <errno.h>
static const char* kShmemName = "/moz.gecko.vr_ext.0.0.1";
#elif defined(MOZ_WIDGET_ANDROID)
#include <string.h>
#include <pthread.h>
#include "GeckoVRManager.h"
#endif // defined(MOZ_WIDGET_ANDROID)
#include "gfxVRExternal.h"
#include "VRManagerParent.h"
#include "VRManager.h"
#include "VRThread.h"
#include "nsServiceManagerUtils.h"
#include "nsIScreenManager.h"
#include "mozilla/dom/GamepadEventTypes.h"
#include "mozilla/dom/GamepadBinding.h"
#include "mozilla/Telemetry.h"
#ifndef M_PI
# define M_PI 3.14159265358979323846
#endif
using namespace mozilla;
using namespace mozilla::gfx;
using namespace mozilla::gfx::impl;
using namespace mozilla::layers;
using namespace mozilla::dom;
VRDisplayExternal::VRDisplayExternal(const VRDisplayState& aDisplayState)
: VRDisplayHost(VRDeviceType::External)
, mHapticPulseRemaining{}
, mBrowserState{}
, mLastSensorState{}
{
MOZ_COUNT_CTOR_INHERITED(VRDisplayExternal, VRDisplayHost);
mDisplayInfo.mDisplayState = aDisplayState;
// default to an identity quaternion
mLastSensorState.pose.orientation[3] = 1.0f;
}
VRDisplayExternal::~VRDisplayExternal()
{
Destroy();
MOZ_COUNT_DTOR_INHERITED(VRDisplayExternal, VRDisplayHost);
}
void
VRDisplayExternal::Destroy()
{
StopAllHaptics();
StopPresentation();
}
void
VRDisplayExternal::ZeroSensor()
{
}
void
VRDisplayExternal::Run1msTasks(double aDeltaTime)
{
VRDisplayHost::Run1msTasks(aDeltaTime);
UpdateHaptics(aDeltaTime);
}
void
VRDisplayExternal::Run10msTasks()
{
VRDisplayHost::Run10msTasks();
ExpireNavigationTransition();
PullState();
PushState();
// 1ms tasks will always be run before
// the 10ms tasks, so no need to include
// them here as well.
}
void
VRDisplayExternal::ExpireNavigationTransition()
{
if (!mVRNavigationTransitionEnd.IsNull() &&
TimeStamp::Now() > mVRNavigationTransitionEnd) {
mBrowserState.navigationTransitionActive = false;
}
}
VRHMDSensorState
VRDisplayExternal::GetSensorState()
{
return mLastSensorState;
}
void
VRDisplayExternal::StartPresentation()
{
if (mBrowserState.presentationActive) {
return;
}
mTelemetry.Clear();
mTelemetry.mPresentationStart = TimeStamp::Now();
// Indicate that we are ready to start immersive mode
mBrowserState.presentationActive = true;
mBrowserState.layerState[0].type = VRLayerType::LayerType_Stereo_Immersive;
PushState();
mDisplayInfo.mDisplayState.mLastSubmittedFrameId = 0;
if (mDisplayInfo.mDisplayState.mReportsDroppedFrames) {
mTelemetry.mLastDroppedFrameCount = mDisplayInfo.mDisplayState.mDroppedFrameCount;
}
#if defined(MOZ_WIDGET_ANDROID)
mLastSubmittedFrameId = 0;
mLastStartedFrame = 0;
#endif
}
void
VRDisplayExternal::StopPresentation()
{
if (!mBrowserState.presentationActive) {
return;
}
// Indicate that we have stopped immersive mode
mBrowserState.presentationActive = false;
memset(mBrowserState.layerState, 0, sizeof(VRLayerState) * mozilla::ArrayLength(mBrowserState.layerState));
PushState(true);
Telemetry::HistogramID timeSpentID = Telemetry::HistogramCount;
Telemetry::HistogramID droppedFramesID = Telemetry::HistogramCount;
int viewIn = 0;
if (mDisplayInfo.mDisplayState.mEightCC == GFX_VR_EIGHTCC('O', 'c', 'u', 'l', 'u', 's', ' ', 'D')) {
// Oculus Desktop API
timeSpentID = Telemetry::WEBVR_TIME_SPENT_VIEWING_IN_OCULUS;
droppedFramesID = Telemetry::WEBVR_DROPPED_FRAMES_IN_OCULUS;
viewIn = 1;
} else if (mDisplayInfo.mDisplayState.mEightCC == GFX_VR_EIGHTCC('O', 'p', 'e', 'n', 'V', 'R', ' ', ' ')) {
// OpenVR API
timeSpentID = Telemetry::WEBVR_TIME_SPENT_VIEWING_IN_OPENVR;
droppedFramesID = Telemetry::WEBVR_DROPPED_FRAMES_IN_OPENVR;
viewIn = 2;
}
if (viewIn) {
const TimeDuration duration = TimeStamp::Now() - mTelemetry.mPresentationStart;
Telemetry::Accumulate(Telemetry::WEBVR_USERS_VIEW_IN, viewIn);
Telemetry::Accumulate(timeSpentID,
duration.ToMilliseconds());
const uint32_t droppedFramesPerSec = (mDisplayInfo.mDisplayState.mDroppedFrameCount -
mTelemetry.mLastDroppedFrameCount) / duration.ToSeconds();
Telemetry::Accumulate(droppedFramesID, droppedFramesPerSec);
}
}
void
VRDisplayExternal::StartVRNavigation()
{
mBrowserState.navigationTransitionActive = true;
mVRNavigationTransitionEnd = TimeStamp();
PushState();
}
void
VRDisplayExternal::StopVRNavigation(const TimeDuration& aTimeout)
{
if (aTimeout.ToMilliseconds() <= 0) {
mBrowserState.navigationTransitionActive = false;
mVRNavigationTransitionEnd = TimeStamp();
PushState();
}
mVRNavigationTransitionEnd = TimeStamp::Now() + aTimeout;
}
bool
VRDisplayExternal::PopulateLayerTexture(const layers::SurfaceDescriptor& aTexture,
VRLayerTextureType* aTextureType,
VRLayerTextureHandle* aTextureHandle)
{
switch (aTexture.type()) {
#if defined(XP_WIN)
case SurfaceDescriptor::TSurfaceDescriptorD3D10: {
const SurfaceDescriptorD3D10& surf = aTexture.get_SurfaceDescriptorD3D10();
*aTextureType = VRLayerTextureType::LayerTextureType_D3D10SurfaceDescriptor;
*aTextureHandle = (void *)surf.handle();
return true;
}
#elif defined(XP_MACOSX)
case SurfaceDescriptor::TSurfaceDescriptorMacIOSurface: {
const auto& desc = aTexture.get_SurfaceDescriptorMacIOSurface();
RefPtr<MacIOSurface> surf = MacIOSurface::LookupSurface(desc.surfaceId(),
desc.scaleFactor(),
!desc.isOpaque());
if (!surf) {
NS_WARNING("VRDisplayHost::SubmitFrame failed to get a MacIOSurface");
return false;
}
*aTextureType = VRLayerTextureType::LayerTextureType_MacIOSurface;
*aTextureHandle = (void *)surf->GetIOSurfacePtr();
return true;
}
#elif defined(MOZ_WIDGET_ANDROID)
case SurfaceDescriptor::TSurfaceTextureDescriptor: {
const SurfaceTextureDescriptor& desc = aTexture.get_SurfaceTextureDescriptor();
java::GeckoSurfaceTexture::LocalRef surfaceTexture = java::GeckoSurfaceTexture::Lookup(desc.handle());
if (!surfaceTexture) {
NS_WARNING("VRDisplayHost::SubmitFrame failed to get a SurfaceTexture");
return false;
}
*aTextureType = VRLayerTextureType::LayerTextureType_GeckoSurfaceTexture;
*aTextureHandle = desc.handle();
return true;
}
#endif
default: {
MOZ_ASSERT(false);
return false;
}
}
}
bool
VRDisplayExternal::SubmitFrame(const layers::SurfaceDescriptor& aTexture,
uint64_t aFrameId,
const gfx::Rect& aLeftEyeRect,
const gfx::Rect& aRightEyeRect)
{
MOZ_ASSERT(mBrowserState.layerState[0].type == VRLayerType::LayerType_Stereo_Immersive);
VRLayer_Stereo_Immersive& layer = mBrowserState.layerState[0].layer_stereo_immersive;
if (!PopulateLayerTexture(aTexture, &layer.mTextureType, &layer.mTextureHandle)) {
return false;
}
layer.mFrameId = aFrameId;
layer.mInputFrameId = mDisplayInfo.mLastSensorState[mDisplayInfo.mFrameId % kVRMaxLatencyFrames].inputFrameID;
layer.mLeftEyeRect.x = aLeftEyeRect.x;
layer.mLeftEyeRect.y = aLeftEyeRect.y;
layer.mLeftEyeRect.width = aLeftEyeRect.width;
layer.mLeftEyeRect.height = aLeftEyeRect.height;
layer.mRightEyeRect.x = aRightEyeRect.x;
layer.mRightEyeRect.y = aRightEyeRect.y;
layer.mRightEyeRect.width = aRightEyeRect.width;
layer.mRightEyeRect.height = aRightEyeRect.height;
PushState(true);
#if defined(MOZ_WIDGET_ANDROID)
PullState([&]() {
return (mDisplayInfo.mDisplayState.mLastSubmittedFrameId >= aFrameId) ||
mDisplayInfo.mDisplayState.mSuppressFrames ||
!mDisplayInfo.mDisplayState.mIsConnected;
});
if (mDisplayInfo.mDisplayState.mSuppressFrames || !mDisplayInfo.mDisplayState.mIsConnected) {
// External implementation wants to supress frames, service has shut down or hardware has been disconnected.
return false;
}
#else
while (mDisplayInfo.mDisplayState.mLastSubmittedFrameId < aFrameId) {
if (PullState()) {
if (mDisplayInfo.mDisplayState.mSuppressFrames || !mDisplayInfo.mDisplayState.mIsConnected) {
// External implementation wants to supress frames, service has shut down or hardware has been disconnected.
return false;
}
}
#ifdef XP_WIN
Sleep(0);
#else
sleep(0);
#endif
}
#endif // defined(MOZ_WIDGET_ANDROID)
return mDisplayInfo.mDisplayState.mLastSubmittedFrameSuccessful;
}
void
VRDisplayExternal::VibrateHaptic(uint32_t aControllerIdx,
uint32_t aHapticIndex,
double aIntensity,
double aDuration,
const VRManagerPromise& aPromise)
{
TimeStamp now = TimeStamp::Now();
size_t bestSlotIndex = 0;
// Default to an empty slot, or the slot holding the oldest haptic pulse
for (size_t i = 0; i < mozilla::ArrayLength(mBrowserState.hapticState); i++) {
const VRHapticState& state = mBrowserState.hapticState[i];
if (state.inputFrameID == 0) {
// Unused slot, use it
bestSlotIndex = i;
break;
}
if (mHapticPulseRemaining[i] < mHapticPulseRemaining[bestSlotIndex]) {
// If no empty slots are available, fall back to overriding
// the pulse which is ending soonest.
bestSlotIndex = i;
}
}
// Override the last pulse on the same actuator if present.
for (size_t i = 0; i < mozilla::ArrayLength(mBrowserState.hapticState); i++) {
const VRHapticState& state = mBrowserState.hapticState[i];
if (state.inputFrameID == 0) {
// This is an empty slot -- no match
continue;
}
if (state.controllerIndex == aControllerIdx &&
state.hapticIndex == aHapticIndex) {
// Found pulse on same actuator -- let's override it.
bestSlotIndex = i;
}
}
ClearHapticSlot(bestSlotIndex);
// Populate the selected slot with new haptic state
size_t bufferIndex = mDisplayInfo.mFrameId % kVRMaxLatencyFrames;
VRHapticState& bestSlot = mBrowserState.hapticState[bestSlotIndex];
bestSlot.inputFrameID =
mDisplayInfo.mLastSensorState[bufferIndex].inputFrameID;
bestSlot.controllerIndex = aControllerIdx;
bestSlot.hapticIndex = aHapticIndex;
bestSlot.pulseStart =
(now - mDisplayInfo.mLastFrameStart[bufferIndex]).ToSeconds();
bestSlot.pulseDuration = aDuration;
bestSlot.pulseIntensity = aIntensity;
// Convert from seconds to ms
mHapticPulseRemaining[bestSlotIndex] = aDuration * 1000.0f;
MOZ_ASSERT(bestSlotIndex <= mHapticPromises.Length());
if (bestSlotIndex == mHapticPromises.Length()) {
mHapticPromises.AppendElement(
UniquePtr<VRManagerPromise>(new VRManagerPromise(aPromise)));
} else {
mHapticPromises[bestSlotIndex] =
UniquePtr<VRManagerPromise>(new VRManagerPromise(aPromise));
}
PushState();
}
void
VRDisplayExternal::ClearHapticSlot(size_t aSlot)
{
MOZ_ASSERT(aSlot < mozilla::ArrayLength(mBrowserState.hapticState));
memset(&mBrowserState.hapticState[aSlot], 0, sizeof(VRHapticState));
mHapticPulseRemaining[aSlot] = 0.0f;
if (aSlot < mHapticPromises.Length() && mHapticPromises[aSlot]) {
VRManager* vm = VRManager::Get();
vm->NotifyVibrateHapticCompleted(*mHapticPromises[aSlot]);
mHapticPromises[aSlot] = nullptr;
}
}
void
VRDisplayExternal::UpdateHaptics(double aDeltaTime)
{
bool bNeedPush = false;
// Check for any haptic pulses that have ended and clear them
for (size_t i = 0; i < mozilla::ArrayLength(mBrowserState.hapticState); i++) {
const VRHapticState& state = mBrowserState.hapticState[i];
if (state.inputFrameID == 0) {
// Nothing in this slot
continue;
}
mHapticPulseRemaining[i] -= aDeltaTime;
if (mHapticPulseRemaining[i] <= 0.0f) {
// The pulse has finished
ClearHapticSlot(i);
bNeedPush = true;
}
}
if (bNeedPush) {
PushState();
}
}
void
VRDisplayExternal::StopVibrateHaptic(uint32_t aControllerIdx)
{
for (size_t i = 0; i < mozilla::ArrayLength(mBrowserState.hapticState); i++) {
VRHapticState& state = mBrowserState.hapticState[i];
if (state.controllerIndex == aControllerIdx) {
memset(&state, 0, sizeof(VRHapticState));
}
}
PushState();
}
void
VRDisplayExternal::StopAllHaptics()
{
for (size_t i = 0; i < mozilla::ArrayLength(mBrowserState.hapticState); i++) {
ClearHapticSlot(i);
}
PushState();
}
void
VRDisplayExternal::PushState(bool aNotifyCond)
{
VRManager *vm = VRManager::Get();
VRSystemManagerExternal* manager = vm->GetExternalManager();
manager->PushState(&mBrowserState, aNotifyCond);
}
#if defined(MOZ_WIDGET_ANDROID)
bool
VRDisplayExternal::PullState(const std::function<bool()>& aWaitCondition)
{
VRManager *vm = VRManager::Get();
VRSystemManagerExternal* manager = vm->GetExternalManager();
return manager->PullState(&mDisplayInfo.mDisplayState,
&mLastSensorState,
mDisplayInfo.mControllerState,
aWaitCondition);
}
#else
bool
VRDisplayExternal::PullState()
{
VRManager *vm = VRManager::Get();
VRSystemManagerExternal* manager = vm->GetExternalManager();
return manager->PullState(&mDisplayInfo.mDisplayState,
&mLastSensorState,
mDisplayInfo.mControllerState);
}
#endif
VRSystemManagerExternal::VRSystemManagerExternal(VRExternalShmem* aAPIShmem /* = nullptr*/)
: mExternalShmem(aAPIShmem)
#if !defined(MOZ_WIDGET_ANDROID)
, mSameProcess(aAPIShmem != nullptr)
#endif
{
#if defined(XP_MACOSX)
mShmemFD = 0;
#elif defined(XP_WIN)
mShmemFile = NULL;
#elif defined(MOZ_WIDGET_ANDROID)
mDoShutdown = false;
mExternalStructFailed = false;
mEnumerationCompleted = false;
#endif
if (!aAPIShmem) {
OpenShmem();
}
}
VRSystemManagerExternal::~VRSystemManagerExternal()
{
CloseShmem();
}
void
VRSystemManagerExternal::OpenShmem()
{
if (mExternalShmem) {
return;
#if defined(MOZ_WIDGET_ANDROID)
} else if (mExternalStructFailed) {
return;
#endif // defined(MOZ_WIDGET_ANDROID)
}
#if defined(XP_MACOSX)
if (mShmemFD == 0) {
mShmemFD = shm_open(kShmemName, O_RDWR, S_IRUSR | S_IWUSR | S_IROTH | S_IWOTH);
}
if (mShmemFD <= 0) {
mShmemFD = 0;
return;
}
struct stat sb;
fstat(mShmemFD, &sb);
off_t length = sb.st_size;
if (length < (off_t)sizeof(VRExternalShmem)) {
// TODO - Implement logging
CloseShmem();
return;
}
mExternalShmem = (VRExternalShmem*)mmap(NULL, length, PROT_READ|PROT_WRITE, MAP_SHARED, mShmemFD, 0);
if (mExternalShmem == MAP_FAILED) {
// TODO - Implement logging
mExternalShmem = NULL;
CloseShmem();
return;
}
#elif defined(XP_WIN)
if (mShmemFile == NULL) {
if (gfxPrefs::VRProcessEnabled()) {
mShmemFile = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0,
sizeof(VRExternalShmem), kShmemName);
MOZ_ASSERT(GetLastError() == 0);
} else {
mShmemFile = OpenFileMappingA(FILE_MAP_ALL_ACCESS, FALSE, kShmemName);
}
if (mShmemFile == NULL) {
// TODO - Implement logging
CloseShmem();
return;
}
}
LARGE_INTEGER length;
length.QuadPart = sizeof(VRExternalShmem);
mExternalShmem = (VRExternalShmem*)MapViewOfFile(mShmemFile, // handle to map object
FILE_MAP_ALL_ACCESS, // read/write permission
0,
0,
length.QuadPart);
if (mExternalShmem == NULL) {
// TODO - Implement logging
CloseShmem();
return;
}
#elif defined(MOZ_WIDGET_ANDROID)
mExternalShmem = (VRExternalShmem*)mozilla::GeckoVRManager::GetExternalContext();
if (!mExternalShmem) {
return;
}
int32_t version = -1;
int32_t size = 0;
if (pthread_mutex_lock((pthread_mutex_t*)&(mExternalShmem->systemMutex)) == 0) {
version = mExternalShmem->version;
size = mExternalShmem->size;
pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->systemMutex));
} else {
return;
}
if (version != kVRExternalVersion) {
mExternalShmem = nullptr;
mExternalStructFailed = true;
return;
}
if (size != sizeof(VRExternalShmem)) {
mExternalShmem = nullptr;
mExternalStructFailed = true;
return;
}
#endif
CheckForShutdown();
}
void
VRSystemManagerExternal::CheckForShutdown()
{
#if defined(MOZ_WIDGET_ANDROID)
if (mDoShutdown) {
Shutdown();
}
#else
if (mExternalShmem) {
if (mExternalShmem->generationA == -1 && mExternalShmem->generationB == -1) {
Shutdown();
}
}
#endif // defined(MOZ_WIDGET_ANDROID)
}
void
VRSystemManagerExternal::CloseShmem()
{
#if !defined(MOZ_WIDGET_ANDROID)
if (mSameProcess) {
return;
}
#endif
#if defined(XP_MACOSX)
if (mExternalShmem) {
munmap((void *)mExternalShmem, sizeof(VRExternalShmem));
mExternalShmem = NULL;
}
if (mShmemFD) {
close(mShmemFD);
}
mShmemFD = 0;
#elif defined(XP_WIN)
if (mExternalShmem) {
UnmapViewOfFile((void *)mExternalShmem);
mExternalShmem = NULL;
}
if (mShmemFile) {
CloseHandle(mShmemFile);
mShmemFile = NULL;
}
#elif defined(MOZ_WIDGET_ANDROID)
mExternalShmem = NULL;
#endif
}
/*static*/ already_AddRefed<VRSystemManagerExternal>
VRSystemManagerExternal::Create(VRExternalShmem* aAPIShmem /* = nullptr*/)
{
MOZ_ASSERT(NS_IsMainThread());
if (!gfxPrefs::VREnabled()) {
return nullptr;
}
if ((!gfxPrefs::VRExternalEnabled() && aAPIShmem == nullptr)
#if defined(XP_WIN)
|| !XRE_IsGPUProcess()
#endif
) {
return nullptr;
}
RefPtr<VRSystemManagerExternal> manager = new VRSystemManagerExternal(aAPIShmem);
return manager.forget();
}
void
VRSystemManagerExternal::Destroy()
{
Shutdown();
}
void
VRSystemManagerExternal::Shutdown()
{
if (mDisplay) {
mDisplay = nullptr;
}
CloseShmem();
#if defined(MOZ_WIDGET_ANDROID)
mDoShutdown = false;
#endif
}
void
VRSystemManagerExternal::Run100msTasks()
{
VRSystemManager::Run100msTasks();
// 1ms and 10ms tasks will always be run before
// the 100ms tasks, so no need to run them
// redundantly here.
CheckForShutdown();
}
void
VRSystemManagerExternal::Enumerate()
{
if (mDisplay == nullptr) {
OpenShmem();
if (mExternalShmem) {
VRDisplayState displayState;
memset(&displayState, 0, sizeof(VRDisplayState));
// We must block until enumeration has completed in order
// to signal that the WebVR promise should be resolved at the
// right time.
#if defined(MOZ_WIDGET_ANDROID)
PullState(&displayState, nullptr, nullptr, [&](){
return mEnumerationCompleted;
});
#else
while (!PullState(&displayState)) {
#ifdef XP_WIN
Sleep(0);
#else
sleep(0);
#endif // XP_WIN
}
#endif // defined(MOZ_WIDGET_ANDROID)
if (displayState.mIsConnected) {
mDisplay = new VRDisplayExternal(displayState);
}
}
}
}
bool
VRSystemManagerExternal::ShouldInhibitEnumeration()
{
if (VRSystemManager::ShouldInhibitEnumeration()) {
return true;
}
if (mDisplay) {
// When we find an a VR device, don't
// allow any further enumeration as it
// may get picked up redundantly by other
// API's.
return true;
}
return false;
}
void
VRSystemManagerExternal::GetHMDs(nsTArray<RefPtr<VRDisplayHost>>& aHMDResult)
{
if (mDisplay) {
aHMDResult.AppendElement(mDisplay);
}
}
bool
VRSystemManagerExternal::GetIsPresenting()
{
if (mDisplay) {
VRDisplayInfo displayInfo(mDisplay->GetDisplayInfo());
return displayInfo.GetPresentingGroups() != 0;
}
return false;
}
void
VRSystemManagerExternal::VibrateHaptic(uint32_t aControllerIdx,
uint32_t aHapticIndex,
double aIntensity,
double aDuration,
const VRManagerPromise& aPromise)
{
if (mDisplay) {
// VRDisplayClient::FireGamepadEvents() assigns a controller ID with ranges
// based on displayID. We must translate this to the indexes understood by
// VRDisplayExternal.
uint32_t controllerBaseIndex =
kVRControllerMaxCount * mDisplay->GetDisplayInfo().mDisplayID;
uint32_t controllerIndex = aControllerIdx - controllerBaseIndex;
double aDurationSeconds = aDuration * 0.001f;
mDisplay->VibrateHaptic(
controllerIndex, aHapticIndex, aIntensity, aDurationSeconds, aPromise);
}
}
void
VRSystemManagerExternal::StopVibrateHaptic(uint32_t aControllerIdx)
{
if (mDisplay) {
// VRDisplayClient::FireGamepadEvents() assigns a controller ID with ranges
// based on displayID. We must translate this to the indexes understood by
// VRDisplayExternal.
uint32_t controllerBaseIndex =
kVRControllerMaxCount * mDisplay->GetDisplayInfo().mDisplayID;
uint32_t controllerIndex = aControllerIdx - controllerBaseIndex;
mDisplay->StopVibrateHaptic(controllerIndex);
}
}
void
VRSystemManagerExternal::GetControllers(
nsTArray<RefPtr<VRControllerHost>>& aControllerResult)
{
// Controller updates are handled in VRDisplayClient for
// VRSystemManagerExternal
aControllerResult.Clear();
}
void
VRSystemManagerExternal::ScanForControllers()
{
// Controller updates are handled in VRDisplayClient for
// VRSystemManagerExternal
return;
}
void
VRSystemManagerExternal::HandleInput()
{
// Controller updates are handled in VRDisplayClient for
// VRSystemManagerExternal
return;
}
void
VRSystemManagerExternal::RemoveControllers()
{
if (mDisplay) {
mDisplay->StopAllHaptics();
}
// Controller updates are handled in VRDisplayClient for
// VRSystemManagerExternal
}
#if defined(MOZ_WIDGET_ANDROID)
bool
VRSystemManagerExternal::PullState(VRDisplayState* aDisplayState,
VRHMDSensorState* aSensorState /* = nullptr */,
VRControllerState* aControllerState /* = nullptr */,
const std::function<bool()>& aWaitCondition /* = nullptr */)
{
MOZ_ASSERT(mExternalShmem);
if (!mExternalShmem) {
return false;
}
bool done = false;
while(!done) {
if (pthread_mutex_lock((pthread_mutex_t*)&(mExternalShmem->systemMutex)) == 0) {
while (true) {
memcpy(aDisplayState, (void*)&(mExternalShmem->state.displayState), sizeof(VRDisplayState));
if (aSensorState) {
memcpy(aSensorState, (void*)&(mExternalShmem->state.sensorState), sizeof(VRHMDSensorState));
}
if (aControllerState) {
memcpy(aControllerState, (void*)&(mExternalShmem->state.controllerState), sizeof(VRControllerState) * kVRControllerMaxCount);
}
mEnumerationCompleted = mExternalShmem->state.enumerationCompleted;
mDoShutdown = aDisplayState->shutdown;
if (!aWaitCondition || aWaitCondition()) {
done = true;
break;
}
// Block current thead using the condition variable until data changes
pthread_cond_wait((pthread_cond_t*)&mExternalShmem->systemCond, (pthread_mutex_t*)&mExternalShmem->systemMutex);
}
pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->systemMutex));
} else if (!aWaitCondition) {
// pthread_mutex_lock failed and we are not waiting for a condition to exit from PullState call.
// return false to indicate that PullState call failed
return false;
}
}
return true;
}
#else
bool
VRSystemManagerExternal::PullState(VRDisplayState* aDisplayState,
VRHMDSensorState* aSensorState /* = nullptr */,
VRControllerState* aControllerState /* = nullptr */)
{
bool success = false;
MOZ_ASSERT(mExternalShmem);
if (mExternalShmem) {
VRExternalShmem tmp;
memcpy(&tmp, (void *)mExternalShmem, sizeof(VRExternalShmem));
if (tmp.generationA == tmp.generationB && tmp.generationA != 0 && tmp.generationA != -1 && tmp.state.enumerationCompleted) {
memcpy(aDisplayState, &tmp.state.displayState, sizeof(VRDisplayState));
if (aSensorState) {
memcpy(aSensorState, &tmp.state.sensorState, sizeof(VRHMDSensorState));
}
if (aControllerState) {
memcpy(aControllerState, (void*)&(mExternalShmem->state.controllerState), sizeof(VRControllerState) * kVRControllerMaxCount);
}
success = true;
}
}
return success;
}
#endif // defined(MOZ_WIDGET_ANDROID)
void
VRSystemManagerExternal::PushState(VRBrowserState* aBrowserState, bool aNotifyCond)
{
MOZ_ASSERT(aBrowserState);
MOZ_ASSERT(mExternalShmem);
if (mExternalShmem) {
#if defined(MOZ_WIDGET_ANDROID)
if (pthread_mutex_lock((pthread_mutex_t*)&(mExternalShmem->browserMutex)) == 0) {
memcpy((void *)&(mExternalShmem->browserState), aBrowserState, sizeof(VRBrowserState));
if (aNotifyCond) {
pthread_cond_signal((pthread_cond_t*)&(mExternalShmem->browserCond));
}
pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->browserMutex));
}
#else
mExternalShmem->browserGenerationA++;
memcpy((void *)&(mExternalShmem->browserState), (void *)aBrowserState, sizeof(VRBrowserState));
mExternalShmem->browserGenerationB++;
#endif // defined(MOZ_WIDGET_ANDROID)
}
}