gecko-dev/gfx/vr/gfxVRPuppet.cpp
Kearwood Gilbert eb3becc0b0 Bug 1362213 - Implement chrome-only API to manage VR sessions r=daoshengmu,kanru,smaug
- Added new chrome-only webidl methods to be used by browser UI and WebExtensions
- Implemented bitmasked group visibility for VR sessions to enable switching
  between chrome and regular content presentations.
- Implemented throttling mechanism to avoid runaway, unthrottled render loops
  for VR sessions that are hidden by group visibility bitmasks or due to
  lower level platform VR events, such as during the Oculus
  "Health and Safety Warning".
- Simplified the PVRManager IPC protocol while extending it to support
  VR session groups and later WebVR content performance profiling API's.
- Removed the last WebVR related sync IPC call.


MozReview-Commit-ID: BMEIPyYeEbq

--HG--
extra : rebase_source : 47d3682cad3d913504175b7d4c3e9d992236f097
2017-05-08 16:01:36 -07:00

764 lines
23 KiB
C++

/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* 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/. */
#if defined(XP_WIN)
#include "CompositorD3D11.h"
#include "TextureD3D11.h"
#include "mozilla/gfx/DeviceManagerDx.h"
#endif // XP_WIN
#include "mozilla/Base64.h"
#include "mozilla/gfx/DataSurfaceHelpers.h"
#include "gfxUtils.h"
#include "gfxVRPuppet.h"
#include "mozilla/dom/GamepadEventTypes.h"
#include "mozilla/dom/GamepadBinding.h"
// See CompositorD3D11Shaders.h
namespace mozilla {
namespace layers {
struct ShaderBytes { const void* mData; size_t mLength; };
extern ShaderBytes sRGBShader;
extern ShaderBytes sLayerQuadVS;
} // namespace layers
} // namespace mozilla
using namespace mozilla;
using namespace mozilla::gfx;
using namespace mozilla::gfx::impl;
using namespace mozilla::layers;
// Reminder: changing the order of these buttons may break web content
static const uint64_t kPuppetButtonMask[] = {
1,
2,
4,
8
};
static const uint32_t kNumPuppetButtonMask = sizeof(kPuppetButtonMask) /
sizeof(uint64_t);
static const uint32_t kPuppetAxes[] = {
0,
1,
2
};
static const uint32_t kNumPuppetAxis = sizeof(kPuppetAxes) /
sizeof(uint32_t);
static const uint32_t kNumPuppetHaptcs = 1;
VRDisplayPuppet::VRDisplayPuppet()
: VRDisplayHost(VRDeviceType::Puppet)
, mIsPresenting(false)
{
MOZ_COUNT_CTOR_INHERITED(VRDisplayPuppet, VRDisplayHost);
mDisplayInfo.mDisplayName.AssignLiteral("Puppet HMD");
mDisplayInfo.mIsConnected = true;
mDisplayInfo.mIsMounted = false;
mDisplayInfo.mCapabilityFlags = VRDisplayCapabilityFlags::Cap_None |
VRDisplayCapabilityFlags::Cap_Orientation |
VRDisplayCapabilityFlags::Cap_Position |
VRDisplayCapabilityFlags::Cap_External |
VRDisplayCapabilityFlags::Cap_Present |
VRDisplayCapabilityFlags::Cap_StageParameters;
mDisplayInfo.mEyeResolution.width = 1836; // 1080 * 1.7
mDisplayInfo.mEyeResolution.height = 2040; // 1200 * 1.7
// SteamVR gives the application a single FOV to use; it's not configurable as with Oculus
for (uint32_t eye = 0; eye < 2; ++eye) {
mDisplayInfo.mEyeTranslation[eye].x = 0.0f;
mDisplayInfo.mEyeTranslation[eye].y = 0.0f;
mDisplayInfo.mEyeTranslation[eye].z = 0.0f;
mDisplayInfo.mEyeFOV[eye] = VRFieldOfView(45.0, 45.0, 45.0, 45.0);
}
// default: 1m x 1m space, 0.75m high in seated position
mDisplayInfo.mStageSize.width = 1.0f;
mDisplayInfo.mStageSize.height = 1.0f;
mDisplayInfo.mSittingToStandingTransform._11 = 1.0f;
mDisplayInfo.mSittingToStandingTransform._12 = 0.0f;
mDisplayInfo.mSittingToStandingTransform._13 = 0.0f;
mDisplayInfo.mSittingToStandingTransform._14 = 0.0f;
mDisplayInfo.mSittingToStandingTransform._21 = 0.0f;
mDisplayInfo.mSittingToStandingTransform._22 = 1.0f;
mDisplayInfo.mSittingToStandingTransform._23 = 0.0f;
mDisplayInfo.mSittingToStandingTransform._24 = 0.0f;
mDisplayInfo.mSittingToStandingTransform._31 = 0.0f;
mDisplayInfo.mSittingToStandingTransform._32 = 0.0f;
mDisplayInfo.mSittingToStandingTransform._33 = 1.0f;
mDisplayInfo.mSittingToStandingTransform._34 = 0.0f;
mDisplayInfo.mSittingToStandingTransform._41 = 0.0f;
mDisplayInfo.mSittingToStandingTransform._42 = 0.75f;
mDisplayInfo.mSittingToStandingTransform._43 = 0.0f;
gfx::Quaternion rot;
mSensorState.flags |= VRDisplayCapabilityFlags::Cap_Orientation;
mSensorState.orientation[0] = rot.x;
mSensorState.orientation[1] = rot.y;
mSensorState.orientation[2] = rot.z;
mSensorState.orientation[3] = rot.w;
mSensorState.angularVelocity[0] = 0.0f;
mSensorState.angularVelocity[1] = 0.0f;
mSensorState.angularVelocity[2] = 0.0f;
mSensorState.flags |= VRDisplayCapabilityFlags::Cap_Position;
mSensorState.position[0] = 0.0f;
mSensorState.position[1] = 0.0f;
mSensorState.position[2] = 0.0f;
mSensorState.linearVelocity[0] = 0.0f;
mSensorState.linearVelocity[1] = 0.0f;
mSensorState.linearVelocity[2] = 0.0f;
}
VRDisplayPuppet::~VRDisplayPuppet()
{
MOZ_COUNT_DTOR_INHERITED(VRDisplayPuppet, VRDisplayHost);
}
void
VRDisplayPuppet::SetDisplayInfo(const VRDisplayInfo& aDisplayInfo)
{
// We are only interested in the eye and mount info of the display info.
mDisplayInfo.mEyeResolution = aDisplayInfo.mEyeResolution;
mDisplayInfo.mIsMounted = aDisplayInfo.mIsMounted;
memcpy(&mDisplayInfo.mEyeFOV, &aDisplayInfo.mEyeFOV,
sizeof(mDisplayInfo.mEyeFOV[0]) * VRDisplayInfo::NumEyes);
memcpy(&mDisplayInfo.mEyeTranslation, &aDisplayInfo.mEyeTranslation,
sizeof(mDisplayInfo.mEyeTranslation[0]) * VRDisplayInfo::NumEyes);
}
void
VRDisplayPuppet::Destroy()
{
StopPresentation();
}
void
VRDisplayPuppet::ZeroSensor()
{
}
VRHMDSensorState
VRDisplayPuppet::GetSensorState()
{
mSensorState.inputFrameID = mDisplayInfo.mFrameId;
return mSensorState;
}
void
VRDisplayPuppet::SetSensorState(const VRHMDSensorState& aSensorState)
{
memcpy(&mSensorState, &aSensorState, sizeof(mSensorState));
}
void
VRDisplayPuppet::StartPresentation()
{
if (mIsPresenting) {
return;
}
mIsPresenting = true;
#if defined(XP_WIN)
if (!mDevice) {
mDevice = gfx::DeviceManagerDx::Get()->GetCompositorDevice();
if (!mDevice) {
NS_WARNING("Failed to get a D3D11Device for Puppet");
return;
}
}
mDevice->GetImmediateContext(getter_AddRefs(mContext));
if (!mContext) {
NS_WARNING("Failed to get immediate context for Puppet");
return;
}
if (FAILED(mDevice->CreateVertexShader(sLayerQuadVS.mData,
sLayerQuadVS.mLength, nullptr, &mQuadVS))) {
NS_WARNING("Failed to create vertex shader for Puppet");
return;
}
if (FAILED(mDevice->CreatePixelShader(sRGBShader.mData,
sRGBShader.mLength, nullptr, &mQuadPS))) {
NS_WARNING("Failed to create pixel shader for Puppet");
return;
}
CD3D11_BUFFER_DESC cBufferDesc(sizeof(layers::VertexShaderConstants),
D3D11_BIND_CONSTANT_BUFFER,
D3D11_USAGE_DYNAMIC,
D3D11_CPU_ACCESS_WRITE);
if (FAILED(mDevice->CreateBuffer(&cBufferDesc, nullptr, getter_AddRefs(mVSConstantBuffer)))) {
NS_WARNING("Failed to vertex shader constant buffer for Puppet");
return;
}
cBufferDesc.ByteWidth = sizeof(layers::PixelShaderConstants);
if (FAILED(mDevice->CreateBuffer(&cBufferDesc, nullptr, getter_AddRefs(mPSConstantBuffer)))) {
NS_WARNING("Failed to pixel shader constant buffer for Puppet");
return;
}
CD3D11_SAMPLER_DESC samplerDesc(D3D11_DEFAULT);
if (FAILED(mDevice->CreateSamplerState(&samplerDesc, getter_AddRefs(mLinearSamplerState)))) {
NS_WARNING("Failed to create sampler state for Puppet");
return;
}
D3D11_INPUT_ELEMENT_DESC layout[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
};
if (FAILED(mDevice->CreateInputLayout(layout,
sizeof(layout) / sizeof(D3D11_INPUT_ELEMENT_DESC),
sLayerQuadVS.mData,
sLayerQuadVS.mLength,
getter_AddRefs(mInputLayout)))) {
NS_WARNING("Failed to create input layout for Puppet");
return;
}
Vertex vertices[] = { { { 0.0, 0.0 } },{ { 1.0, 0.0 } },{ { 0.0, 1.0 } },{ { 1.0, 1.0 } } };
CD3D11_BUFFER_DESC bufferDesc(sizeof(vertices), D3D11_BIND_VERTEX_BUFFER);
D3D11_SUBRESOURCE_DATA data;
data.pSysMem = (void*)vertices;
if (FAILED(mDevice->CreateBuffer(&bufferDesc, &data, getter_AddRefs(mVertexBuffer)))) {
NS_WARNING("Failed to create vertex buffer for Puppet");
return;
}
memset(&mVSConstants, 0, sizeof(mVSConstants));
memset(&mPSConstants, 0, sizeof(mPSConstants));
#endif // XP_WIN
}
void
VRDisplayPuppet::StopPresentation()
{
if (!mIsPresenting) {
return;
}
mIsPresenting = false;
}
#if defined(XP_WIN)
bool
VRDisplayPuppet::UpdateConstantBuffers()
{
HRESULT hr;
D3D11_MAPPED_SUBRESOURCE resource;
resource.pData = nullptr;
hr = mContext->Map(mVSConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &resource);
if (FAILED(hr) || !resource.pData) {
return false;
}
*(VertexShaderConstants*)resource.pData = mVSConstants;
mContext->Unmap(mVSConstantBuffer, 0);
resource.pData = nullptr;
hr = mContext->Map(mPSConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &resource);
if (FAILED(hr) || !resource.pData) {
return false;
}
*(PixelShaderConstants*)resource.pData = mPSConstants;
mContext->Unmap(mPSConstantBuffer, 0);
ID3D11Buffer *buffer = mVSConstantBuffer;
mContext->VSSetConstantBuffers(0, 1, &buffer);
buffer = mPSConstantBuffer;
mContext->PSSetConstantBuffers(0, 1, &buffer);
return true;
}
bool
VRDisplayPuppet::SubmitFrame(TextureSourceD3D11* aSource,
const IntSize& aSize,
const gfx::Rect& aLeftEyeRect,
const gfx::Rect& aRightEyeRect)
{
if (!mIsPresenting) {
return false;
}
VRManager *vm = VRManager::Get();
MOZ_ASSERT(vm);
switch (gfxPrefs::VRPuppetSubmitFrame()) {
case 0:
// The VR frame is not displayed.
break;
case 1:
{
// The frames are submitted to VR compositor are decoded
// into a base64Image and dispatched to the DOM side.
D3D11_TEXTURE2D_DESC desc;
ID3D11Texture2D* texture = aSource->GetD3D11Texture();
texture->GetDesc(&desc);
DXGI_FORMAT format = desc.Format;
// Map the staging resource
ID3D11Texture2D* mappedTexture = nullptr;
D3D11_MAPPED_SUBRESOURCE mapInfo;
HRESULT hr = mContext->Map(texture,
0, // Subsource
D3D11_MAP_READ,
0, // MapFlags
&mapInfo);
if (FAILED(hr)) {
// If we can't map this texture, copy it to a staging resource.
if (hr == E_INVALIDARG) {
D3D11_TEXTURE2D_DESC desc2;
desc2.Width = desc.Width;
desc2.Height = desc.Height;
desc2.MipLevels = desc.MipLevels;
desc2.ArraySize = desc.ArraySize;
desc2.Format = desc.Format;
desc2.SampleDesc = desc.SampleDesc;
desc2.Usage = D3D11_USAGE_STAGING;
desc2.BindFlags = 0;
desc2.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
desc2.MiscFlags = 0;
ID3D11Texture2D* stagingTexture = nullptr;
hr = mDevice->CreateTexture2D(&desc2, nullptr, &stagingTexture);
if (FAILED(hr)) {
MOZ_ASSERT(false, "Failed to create a staging texture");
return false;
}
// Copy the texture to a staging resource
mContext->CopyResource(stagingTexture, texture);
// Map the staging resource
hr = mContext->Map(stagingTexture,
0, // Subsource
D3D11_MAP_READ,
0, // MapFlags
&mapInfo);
if (FAILED(hr)) {
MOZ_ASSERT(false, "Failed to map staging texture");
}
mappedTexture = stagingTexture;
} else {
MOZ_ASSERT(false, "Failed to map staging texture");
return false;
}
} else {
mappedTexture = texture;
}
// Ideally, we should convert the srcData to a PNG image and decode it
// to a Base64 string here, but the GPU process does not have the privilege to
// access the image library. So, we have to convert the RAW image data
// to a base64 string and forward it to let the content process to
// do the image conversion.
char* srcData = static_cast<char*>(mapInfo.pData);
VRSubmitFrameResultInfo result;
result.mFormat = SurfaceFormat::B8G8R8A8;
result.mWidth = desc.Width;
result.mHeight = desc.Height;
result.mFrameNum = mDisplayInfo.mFrameId;
nsCString rawString(Substring((char*)srcData, mapInfo.RowPitch * desc.Height));
if (Base64Encode(rawString, result.mBase64Image) != NS_OK) {
MOZ_ASSERT(false, "Failed to encode base64 images.");
}
mContext->Unmap(mappedTexture, 0);
// Dispatch the base64 encoded string to the DOM side, and it will be decoded
// and convert to a PNG image there.
vm->DispatchSubmitFrameResult(mDisplayInfo.mDisplayID, result);
break;
}
case 2:
{
// The VR compositor sumbmit frame to the screen window,
// the current coordinate is at (0, 0, width, height).
Matrix viewMatrix = Matrix::Translation(-1.0, 1.0);
viewMatrix.PreScale(2.0f / float(aSize.width), 2.0f / float(aSize.height));
viewMatrix.PreScale(1.0f, -1.0f);
Matrix4x4 projection = Matrix4x4::From2D(viewMatrix);
projection._33 = 0.0f;
Matrix transform2d;
gfx::Matrix4x4 transform = gfx::Matrix4x4::From2D(transform2d);
const float posX = 0.0f, posY = 0.0f;
D3D11_VIEWPORT viewport;
viewport.MinDepth = 0.0f;
viewport.MaxDepth = 1.0f;
viewport.Width = aSize.width;
viewport.Height = aSize.height;
viewport.TopLeftX = posX;
viewport.TopLeftY = posY;
D3D11_RECT scissor;
scissor.left = posX;
scissor.right = aSize.width + posX;
scissor.top = posY;
scissor.bottom = aSize.height + posY;
memcpy(&mVSConstants.layerTransform, &transform._11, sizeof(mVSConstants.layerTransform));
memcpy(&mVSConstants.projection, &projection._11, sizeof(mVSConstants.projection));
mVSConstants.renderTargetOffset[0] = 0.0f;
mVSConstants.renderTargetOffset[1] = 0.0f;
mVSConstants.layerQuad = Rect(0.0f, 0.0f, aSize.width, aSize.height);
mVSConstants.textureCoords = Rect(0.0f, 1.0f, 1.0f, -1.0f);
mPSConstants.layerOpacity[0] = 1.0f;
ID3D11Buffer* vbuffer = mVertexBuffer;
UINT vsize = sizeof(Vertex);
UINT voffset = 0;
mContext->IASetVertexBuffers(0, 1, &vbuffer, &vsize, &voffset);
mContext->IASetIndexBuffer(nullptr, DXGI_FORMAT_R16_UINT, 0);
mContext->IASetInputLayout(mInputLayout);
mContext->RSSetViewports(1, &viewport);
mContext->RSSetScissorRects(1, &scissor);
mContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
mContext->VSSetShader(mQuadVS, nullptr, 0);
mContext->PSSetShader(mQuadPS, nullptr, 0);
ID3D11ShaderResourceView* srView = aSource->GetShaderResourceView();
mContext->PSSetShaderResources(0 /* 0 == TexSlot::RGB */, 1, &srView);
// XXX Use Constant from TexSlot in CompositorD3D11.cpp?
ID3D11SamplerState *sampler = mLinearSamplerState;
mContext->PSSetSamplers(0, 1, &sampler);
if (!UpdateConstantBuffers()) {
NS_WARNING("Failed to update constant buffers for Puppet");
return false;
}
mContext->Draw(4, 0);
break;
}
}
// We will always return false for gfxVRPuppet to ensure that the fallback "watchdog"
// code in VRDisplayHost::NotifyVSync() throttles the render loop. This "watchdog" will
// result in a refresh rate that is quite low compared to real hardware, but should be
// sufficient for non-performance oriented tests. If we wish to simulate realistic frame
// rates with VRDisplayPuppet, we should block here for the appropriate amount of time and
// return true to indicate that we have blocked.
return false;
}
#else
bool
VRDisplayPuppet::SubmitFrame(TextureSourceOGL* aSource,
const IntSize& aSize,
const gfx::Rect& aLeftEyeRect,
const gfx::Rect& aRightEyeRect)
{
if (!mIsPresenting) {
return false;
}
// TODO: Bug 1343730, Need to block until the next simulated
// vblank interval and capture frames for use in reftests.
return false;
}
#endif
void
VRDisplayPuppet::NotifyVSync()
{
// We update mIsConneced once per frame.
mDisplayInfo.mIsConnected = true;
VRDisplayHost::NotifyVSync();
}
VRControllerPuppet::VRControllerPuppet(dom::GamepadHand aHand)
: VRControllerHost(VRDeviceType::Puppet)
, mButtonPressState(0)
{
MOZ_COUNT_CTOR_INHERITED(VRControllerPuppet, VRControllerHost);
mControllerInfo.mControllerName.AssignLiteral("Puppet Gamepad");
mControllerInfo.mMappingType = GamepadMappingType::_empty;
mControllerInfo.mHand = aHand;
mControllerInfo.mNumButtons = kNumPuppetButtonMask;
mControllerInfo.mNumAxes = kNumPuppetAxis;
mControllerInfo.mNumHaptics = kNumPuppetHaptcs;
}
VRControllerPuppet::~VRControllerPuppet()
{
MOZ_COUNT_DTOR_INHERITED(VRControllerPuppet, VRControllerHost);
}
void
VRControllerPuppet::SetButtonPressState(uint32_t aButton, bool aPressed)
{
const uint64_t buttonMask = kPuppetButtonMask[aButton];
uint64_t pressedBit = GetButtonPressed();
if (aPressed) {
pressedBit |= kPuppetButtonMask[aButton];
} else if (pressedBit & buttonMask) {
// this button was pressed but is released now.
uint64_t mask = 0xff ^ buttonMask;
pressedBit &= mask;
}
mButtonPressState = pressedBit;
}
uint64_t
VRControllerPuppet::GetButtonPressState()
{
return mButtonPressState;
}
void
VRControllerPuppet::SetButtonTouchState(uint32_t aButton, bool aTouched)
{
const uint64_t buttonMask = kPuppetButtonMask[aButton];
uint64_t touchedBit = GetButtonTouched();
if (aTouched) {
touchedBit |= kPuppetButtonMask[aButton];
} else if (touchedBit & buttonMask) {
// this button was touched but is released now.
uint64_t mask = 0xff ^ buttonMask;
touchedBit &= mask;
}
mButtonTouchState = touchedBit;
}
uint64_t
VRControllerPuppet::GetButtonTouchState()
{
return mButtonTouchState;
}
void
VRControllerPuppet::SetAxisMoveState(uint32_t aAxis, double aValue)
{
MOZ_ASSERT((sizeof(mAxisMoveState) / sizeof(float)) == kNumPuppetAxis);
MOZ_ASSERT(aAxis <= kNumPuppetAxis);
mAxisMoveState[aAxis] = aValue;
}
double
VRControllerPuppet::GetAxisMoveState(uint32_t aAxis)
{
return mAxisMoveState[aAxis];
}
void
VRControllerPuppet::SetPoseMoveState(const dom::GamepadPoseState& aPose)
{
mPoseState = aPose;
}
const dom::GamepadPoseState&
VRControllerPuppet::GetPoseMoveState()
{
return mPoseState;
}
float
VRControllerPuppet::GetAxisMove(uint32_t aAxis)
{
return mAxisMove[aAxis];
}
void
VRControllerPuppet::SetAxisMove(uint32_t aAxis, float aValue)
{
mAxisMove[aAxis] = aValue;
}
VRSystemManagerPuppet::VRSystemManagerPuppet()
{
}
/*static*/ already_AddRefed<VRSystemManagerPuppet>
VRSystemManagerPuppet::Create()
{
if (!gfxPrefs::VREnabled() || !gfxPrefs::VRPuppetEnabled()) {
return nullptr;
}
RefPtr<VRSystemManagerPuppet> manager = new VRSystemManagerPuppet();
return manager.forget();
}
void
VRSystemManagerPuppet::Destroy()
{
Shutdown();
}
void
VRSystemManagerPuppet::Shutdown()
{
mPuppetHMD = nullptr;
}
void
VRSystemManagerPuppet::GetHMDs(nsTArray<RefPtr<VRDisplayHost>>& aHMDResult)
{
if (mPuppetHMD == nullptr) {
mPuppetHMD = new VRDisplayPuppet();
}
aHMDResult.AppendElement(mPuppetHMD);
}
bool
VRSystemManagerPuppet::GetIsPresenting()
{
if (mPuppetHMD) {
VRDisplayInfo displayInfo(mPuppetHMD->GetDisplayInfo());
return displayInfo.GetPresentingGroups() != kVRGroupNone;
}
return false;
}
void
VRSystemManagerPuppet::HandleInput()
{
RefPtr<impl::VRControllerPuppet> controller;
for (uint32_t i = 0; i < mPuppetController.Length(); ++i) {
controller = mPuppetController[i];
for (uint32_t j = 0; j < kNumPuppetButtonMask; ++j) {
HandleButtonPress(i, j, kPuppetButtonMask[i], controller->GetButtonPressState(),
controller->GetButtonTouchState());
}
controller->SetButtonPressed(controller->GetButtonPressState());
for (uint32_t j = 0; j < kNumPuppetAxis; ++j) {
HandleAxisMove(i, j, controller->GetAxisMoveState(j));
}
HandlePoseTracking(i, controller->GetPoseMoveState(), controller);
}
}
void
VRSystemManagerPuppet::HandleButtonPress(uint32_t aControllerIdx,
uint32_t aButton,
uint64_t aButtonMask,
uint64_t aButtonPressed,
uint64_t aButtonTouched)
{
RefPtr<impl::VRControllerPuppet> controller(mPuppetController[aControllerIdx]);
MOZ_ASSERT(controller);
const uint64_t pressedDiff = (controller->GetButtonPressed() ^ aButtonPressed);
const uint64_t touchedDiff = (controller->GetButtonTouched() ^ aButtonTouched);
if (!pressedDiff && !touchedDiff) {
return;
}
if (pressedDiff & aButtonMask
|| touchedDiff & aButtonMask) {
// diff & (aButtonPressed, aButtonTouched) would be true while a new button pressed or
// touched event, otherwise it is an old event and needs to notify
// the button has been released.
NewButtonEvent(aControllerIdx, aButton, aButtonMask & aButtonPressed,
aButtonMask & aButtonPressed,
(aButtonMask & aButtonPressed) ? 1.0L : 0.0L);
}
}
void
VRSystemManagerPuppet::HandleAxisMove(uint32_t aControllerIdx, uint32_t aAxis,
float aValue)
{
RefPtr<impl::VRControllerPuppet> controller(mPuppetController[aControllerIdx]);
MOZ_ASSERT(controller);
if (controller->GetAxisMove(aAxis) != aValue) {
NewAxisMove(aControllerIdx, aAxis, aValue);
controller->SetAxisMove(aAxis, aValue);
}
}
void
VRSystemManagerPuppet::HandlePoseTracking(uint32_t aControllerIdx,
const GamepadPoseState& aPose,
VRControllerHost* aController)
{
MOZ_ASSERT(aController);
if (aPose != aController->GetPose()) {
aController->SetPose(aPose);
NewPoseState(aControllerIdx, aPose);
}
}
void
VRSystemManagerPuppet::VibrateHaptic(uint32_t aControllerIdx,
uint32_t aHapticIndex,
double aIntensity,
double aDuration,
uint32_t aPromiseID)
{
}
void
VRSystemManagerPuppet::StopVibrateHaptic(uint32_t aControllerIdx)
{
}
void
VRSystemManagerPuppet::GetControllers(nsTArray<RefPtr<VRControllerHost>>& aControllerResult)
{
aControllerResult.Clear();
for (uint32_t i = 0; i < mPuppetController.Length(); ++i) {
aControllerResult.AppendElement(mPuppetController[i]);
}
}
void
VRSystemManagerPuppet::ScanForControllers()
{
// We make VRSystemManagerPuppet has two controllers always.
const uint32_t newControllerCount = 2;
if (newControllerCount != mControllerCount) {
RemoveControllers();
// Re-adding controllers to VRControllerManager.
for (uint32_t i = 0; i < newControllerCount; ++i) {
dom::GamepadHand hand = (i % 2) ? dom::GamepadHand::Right :
dom::GamepadHand::Left;
RefPtr<VRControllerPuppet> puppetController = new VRControllerPuppet(hand);
mPuppetController.AppendElement(puppetController);
// Not already present, add it.
AddGamepad(puppetController->GetControllerInfo());
++mControllerCount;
}
}
}
void
VRSystemManagerPuppet::RemoveControllers()
{
// controller count is changed, removing the existing gamepads first.
for (uint32_t i = 0; i < mPuppetController.Length(); ++i) {
RemoveGamepad(i);
}
mPuppetController.Clear();
mControllerCount = 0;
}