Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  OculusSession.cpp

  Sprache: 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/. */


#ifndef XP_WIN
#  error "Oculus support only available for Windows"
#endif

#include <math.h>
#include <d3d11.h>

#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/dom/GamepadEventTypes.h"
#include "mozilla/dom/GamepadBinding.h"
#include "mozilla/gfx/DeviceManagerDx.h"
#include "mozilla/gfx/Logging.h"
#include "mozilla/SharedLibrary.h"
#include "OculusSession.h"

/** XXX The DX11 objects and quad blitting could be encapsulated
 *    into a separate object if either Oculus starts supporting
 *     non-Windows platforms or the blit is needed by other HMD\
 *     drivers.
 *     Alternately, we could remove the extra blit for
 *     Oculus as well with some more refactoring.
 */


// 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::layers;

namespace {

static pfn_ovr_Initialize ovr_Initialize = nullptr;
static pfn_ovr_Shutdown ovr_Shutdown = nullptr;
static pfn_ovr_GetLastErrorInfo ovr_GetLastErrorInfo = nullptr;
static pfn_ovr_GetVersionString ovr_GetVersionString = nullptr;
static pfn_ovr_TraceMessage ovr_TraceMessage = nullptr;
static pfn_ovr_IdentifyClient ovr_IdentifyClient = nullptr;
static pfn_ovr_GetHmdDesc ovr_GetHmdDesc = nullptr;
static pfn_ovr_GetTrackerCount ovr_GetTrackerCount = nullptr;
static pfn_ovr_GetTrackerDesc ovr_GetTrackerDesc = nullptr;
static pfn_ovr_Create ovr_Create = nullptr;
static pfn_ovr_Destroy ovr_Destroy = nullptr;
static pfn_ovr_GetSessionStatus ovr_GetSessionStatus = nullptr;
static pfn_ovr_IsExtensionSupported ovr_IsExtensionSupported = nullptr;
static pfn_ovr_EnableExtension ovr_EnableExtension = nullptr;
static pfn_ovr_SetTrackingOriginType ovr_SetTrackingOriginType = nullptr;
static pfn_ovr_GetTrackingOriginType ovr_GetTrackingOriginType = nullptr;
static pfn_ovr_RecenterTrackingOrigin ovr_RecenterTrackingOrigin = nullptr;
static pfn_ovr_SpecifyTrackingOrigin ovr_SpecifyTrackingOrigin = nullptr;
static pfn_ovr_ClearShouldRecenterFlag ovr_ClearShouldRecenterFlag = nullptr;
static pfn_ovr_GetTrackingState ovr_GetTrackingState = nullptr;
static pfn_ovr_GetDevicePoses ovr_GetDevicePoses = nullptr;
static pfn_ovr_GetTrackerPose ovr_GetTrackerPose = nullptr;
static pfn_ovr_GetInputState ovr_GetInputState = nullptr;
static pfn_ovr_GetConnectedControllerTypes ovr_GetConnectedControllerTypes =
    nullptr;
static pfn_ovr_GetTouchHapticsDesc ovr_GetTouchHapticsDesc = nullptr;
static pfn_ovr_SetControllerVibration ovr_SetControllerVibration = nullptr;
static pfn_ovr_SubmitControllerVibration ovr_SubmitControllerVibration =
    nullptr;
static pfn_ovr_GetControllerVibrationState ovr_GetControllerVibrationState =
    nullptr;
static pfn_ovr_TestBoundary ovr_TestBoundary = nullptr;
static pfn_ovr_TestBoundaryPoint ovr_TestBoundaryPoint = nullptr;
static pfn_ovr_SetBoundaryLookAndFeel ovr_SetBoundaryLookAndFeel = nullptr;
static pfn_ovr_ResetBoundaryLookAndFeel ovr_ResetBoundaryLookAndFeel = nullptr;
static pfn_ovr_GetBoundaryGeometry ovr_GetBoundaryGeometry = nullptr;
static pfn_ovr_GetBoundaryDimensions ovr_GetBoundaryDimensions = nullptr;
static pfn_ovr_GetBoundaryVisible ovr_GetBoundaryVisible = nullptr;
static pfn_ovr_RequestBoundaryVisible ovr_RequestBoundaryVisible = nullptr;
static pfn_ovr_GetTextureSwapChainLength ovr_GetTextureSwapChainLength =
    nullptr;
static pfn_ovr_GetTextureSwapChainCurrentIndex
    ovr_GetTextureSwapChainCurrentIndex = nullptr;
static pfn_ovr_GetTextureSwapChainDesc ovr_GetTextureSwapChainDesc = nullptr;
static pfn_ovr_CommitTextureSwapChain ovr_CommitTextureSwapChain = nullptr;
static pfn_ovr_DestroyTextureSwapChain ovr_DestroyTextureSwapChain = nullptr;
static pfn_ovr_DestroyMirrorTexture ovr_DestroyMirrorTexture = nullptr;
static pfn_ovr_GetFovTextureSize ovr_GetFovTextureSize = nullptr;
static pfn_ovr_GetRenderDesc2 ovr_GetRenderDesc2 = nullptr;
static pfn_ovr_WaitToBeginFrame ovr_WaitToBeginFrame = nullptr;
static pfn_ovr_BeginFrame ovr_BeginFrame = nullptr;
static pfn_ovr_EndFrame ovr_EndFrame = nullptr;
static pfn_ovr_SubmitFrame ovr_SubmitFrame = nullptr;
static pfn_ovr_GetPerfStats ovr_GetPerfStats = nullptr;
static pfn_ovr_ResetPerfStats ovr_ResetPerfStats = nullptr;
static pfn_ovr_GetPredictedDisplayTime ovr_GetPredictedDisplayTime = nullptr;
static pfn_ovr_GetTimeInSeconds ovr_GetTimeInSeconds = nullptr;
static pfn_ovr_GetBool ovr_GetBool = nullptr;
static pfn_ovr_SetBool ovr_SetBool = nullptr;
static pfn_ovr_GetInt ovr_GetInt = nullptr;
static pfn_ovr_SetInt ovr_SetInt = nullptr;
static pfn_ovr_GetFloat ovr_GetFloat = nullptr;
static pfn_ovr_SetFloat ovr_SetFloat = nullptr;
static pfn_ovr_GetFloatArray ovr_GetFloatArray = nullptr;
static pfn_ovr_SetFloatArray ovr_SetFloatArray = nullptr;
static pfn_ovr_GetString ovr_GetString = nullptr;
static pfn_ovr_SetString ovr_SetString = nullptr;
static pfn_ovr_GetExternalCameras ovr_GetExternalCameras = nullptr;
static pfn_ovr_SetExternalCameraProperties ovr_SetExternalCameraProperties =
    nullptr;

#ifdef XP_WIN
static pfn_ovr_CreateTextureSwapChainDX ovr_CreateTextureSwapChainDX = nullptr;
static pfn_ovr_GetTextureSwapChainBufferDX ovr_GetTextureSwapChainBufferDX =
    nullptr;
static pfn_ovr_CreateMirrorTextureDX ovr_CreateMirrorTextureDX = nullptr;
static pfn_ovr_GetMirrorTextureBufferDX ovr_GetMirrorTextureBufferDX = nullptr;
#endif

static pfn_ovr_CreateTextureSwapChainGL ovr_CreateTextureSwapChainGL = nullptr;
static pfn_ovr_GetTextureSwapChainBufferGL ovr_GetTextureSwapChainBufferGL =
    nullptr;
static pfn_ovr_CreateMirrorTextureGL ovr_CreateMirrorTextureGL = nullptr;
static pfn_ovr_GetMirrorTextureBufferGL ovr_GetMirrorTextureBufferGL = nullptr;

#ifdef HAVE_64BIT_BUILD
#  define BUILD_BITS 64
#else
#  define BUILD_BITS 32
#endif

#define OVR_PRODUCT_VERSION 1
#define OVR_MAJOR_VERSION 1
#define OVR_MINOR_VERSION 19

static const uint32_t kNumOculusButtons = 7;
static const uint32_t kNumOculusHaptcs = 1;
static const uint32_t kNumOculusAxes = 4;
ovrControllerType OculusControllerTypes[2] = {ovrControllerType_LTouch,
                                              ovrControllerType_RTouch};
const char* OculusControllerNames[2] = {"Oculus Touch (Left)",
                                        "Oculus Touch (Right)"};
dom::GamepadHand OculusControllerHand[2] = {dom::GamepadHand::Left,
                                            dom::GamepadHand::Right};

ovrButton OculusControllerButtons[2][kNumOculusButtons] = {
    {(ovrButton)0, (ovrButton)0, (ovrButton)0, ovrButton_LThumb, ovrButton_X,
     ovrButton_Y, (ovrButton)0},
    {(ovrButton)0, (ovrButton)0, (ovrButton)0, ovrButton_RThumb, ovrButton_A,
     ovrButton_B, (ovrButton)0},
};

ovrTouch OculusControllerTouches[2][kNumOculusButtons] = {
    {ovrTouch_LIndexTrigger, (ovrTouch)0, (ovrTouch)0, ovrTouch_LThumb,
     ovrTouch_X, ovrTouch_Y, ovrTouch_LThumbRest},
    {ovrTouch_RIndexTrigger, (ovrTouch)0, (ovrTouch)0, ovrTouch_RThumb,
     ovrTouch_A, ovrTouch_B, ovrTouch_RThumbRest},
};

void UpdateButton(const ovrInputState& aInputState, uint32_t aHandIdx,
                  uint32_t aButtonIdx, VRControllerState& aControllerState) {
  if (aInputState.Buttons & OculusControllerButtons[aHandIdx][aButtonIdx]) {
    aControllerState.buttonPressed |= ((uint64_t)1 << aButtonIdx);
    aControllerState.triggerValue[aButtonIdx] = 1.0f;
  } else {
    aControllerState.triggerValue[aButtonIdx] = 0.0f;
  }
  if (aInputState.Touches & OculusControllerTouches[aHandIdx][aButtonIdx]) {
    aControllerState.buttonTouched |= ((uint64_t)1 << aButtonIdx);
  }
}

VRFieldOfView FromFovPort(const ovrFovPort& aFOV) {
  VRFieldOfView fovInfo;
  fovInfo.leftDegrees = atan(aFOV.LeftTan) * 180.0 / M_PI;
  fovInfo.rightDegrees = atan(aFOV.RightTan) * 180.0 / M_PI;
  fovInfo.upDegrees = atan(aFOV.UpTan) * 180.0 / M_PI;
  fovInfo.downDegrees = atan(aFOV.DownTan) * 180.0 / M_PI;
  return fovInfo;
}

}  // anonymous namespace

namespace mozilla {
namespace gfx {

OculusSession::OculusSession()
    : VRSession(),
      mOvrLib(nullptr),
      mSession(nullptr),
      mInitFlags((ovrInitFlags)0),
      mTextureSet(nullptr),
      mQuadVS(nullptr),
      mQuadPS(nullptr),
      mLinearSamplerState(nullptr),
      mVSConstantBuffer(nullptr),
      mPSConstantBuffer(nullptr),
      mVertexBuffer(nullptr),
      mInputLayout(nullptr),
      mRemainingVibrateTime{},
      mHapticPulseIntensity{},
      mIsPresenting(false) {}

OculusSession::~OculusSession() { Shutdown(); }

bool OculusSession::Initialize(mozilla::gfx::VRSystemState& aSystemState,
                               bool aDetectRuntimesOnly) {
  if (StaticPrefs::dom_vr_puppet_enabled()) {
    // Ensure that tests using the VR Puppet do not find real hardware
    return false;
  }
  if (!StaticPrefs::dom_vr_enabled() || !StaticPrefs::dom_vr_oculus_enabled()) {
    return false;
  }

  if (aDetectRuntimesOnly) {
    if (LoadOvrLib()) {
      aSystemState.displayState.capabilityFlags |=
          VRDisplayCapabilityFlags::Cap_ImmersiveVR;
    }
    return false;
  }

  if (!CreateD3DObjects()) {
    return false;
  }
  if (!CreateShaders()) {
    return false;
  }
  // Ideally, we should move LoadOvrLib() up to the first line to avoid
  // unnecessary D3D objects creation. But it will cause a WPT fail in Win 7
  // debug.
  if (!LoadOvrLib()) {
    return false;
  }
  // We start off with an invisible session, then re-initialize
  // with visible session once WebVR content starts rendering.
  if (!ChangeVisibility(false)) {
    return false;
  }
  if (!InitState(aSystemState)) {
    return false;
  }

  mPresentationSize = IntSize(aSystemState.displayState.eyeResolution.width * 2,
                              aSystemState.displayState.eyeResolution.height);
  return true;
}

void OculusSession::UpdateVisibility() {
  // Do not immediately re-initialize with an invisible session after
  // the end of a VR presentation.  Waiting for the configured duraction
  // ensures that the user will not drop to Oculus Home during VR link
  // traversal.
  if (mIsPresenting) {
    // We are currently rendering immersive content.
    // Avoid interrupting the session
    return;
  }
  if (mInitFlags & ovrInit_Invisible) {
    // We are already invisible
    return;
  }
  if (mLastPresentationEnd.IsNull()) {
    // There has been no presentation yet
    return;
  }

  TimeDuration duration = TimeStamp::Now() - mLastPresentationEnd;
  TimeDuration timeout = TimeDuration::FromMilliseconds(
      StaticPrefs::dom_vr_oculus_present_timeout());
  if (timeout <= TimeDuration(0) || duration >= timeout) {
    if (!ChangeVisibility(false)) {
      gfxWarning() << "OculusSession::ChangeVisibility(false) failed";
    }
  }
}

void OculusSession::CoverTransitions() {
  // While content is loading or during immersive-mode link
  // traversal, we need to prevent the user from seeing the
  // last rendered frame.
  // We render black frames to cover up the transition.
  MOZ_ASSERT(mSession);
  if (mIsPresenting) {
    // We are currently rendering immersive content.
    // Avoid interrupting the session
    return;
  }

  if (mInitFlags & ovrInit_Invisible) {
    // We are invisible, nothing to cover up
    return;
  }

  // Render a black frame
  ovrLayerEyeFov layer;
  memset(&layer, 0, sizeof(layer));
  layer.Header.Type = ovrLayerType_Disabled;
  ovrLayerHeader* layers = &layer.Header;
  ovr_SubmitFrame(mSession, 0, nullptr, &layers, 1);
}

bool OculusSession::ChangeVisibility(bool bVisible) {
  ovrInitFlags flags =
      (ovrInitFlags)(ovrInit_RequestVersion | ovrInit_MixedRendering);
  if (StaticPrefs::dom_vr_oculus_invisible_enabled() && !bVisible) {
    flags = (ovrInitFlags)(flags | ovrInit_Invisible);
  }
  if (mInitFlags == flags) {
    // The new state is the same, nothing to do
    return true;
  }

  // Tear everything down
  StopRendering();
  StopSession();
  StopLib();

  // Start it back up
  if (!StartLib(flags)) {
    return false;
  }
  if (!StartSession()) {
    return false;
  }
  return true;
}

void OculusSession::Shutdown() {
  StopRendering();
  StopSession();
  StopLib();
  UnloadOvrLib();
  DestroyShaders();
}

void OculusSession::ProcessEvents(mozilla::gfx::VRSystemState& aSystemState) {
  if (!mSession) {
    return;
  }

  ovrSessionStatus status;
  if (OVR_SUCCESS(ovr_GetSessionStatus(mSession, &status))) {
    aSystemState.displayState.isConnected = status.HmdPresent;
    aSystemState.displayState.isMounted = status.HmdMounted;
    mShouldQuit = status.ShouldQuit;

  } else {
    aSystemState.displayState.isConnected = false;
    aSystemState.displayState.isMounted = false;
  }
  UpdateHaptics();
  UpdateVisibility();
  CoverTransitions();
}

void OculusSession::StartFrame(mozilla::gfx::VRSystemState& aSystemState) {
  UpdateHeadsetPose(aSystemState);
  UpdateEyeParameters(aSystemState);
  UpdateControllers(aSystemState);
  UpdateTelemetry(aSystemState);
  aSystemState.sensorState.inputFrameID++;
}

bool OculusSession::StartPresentation() {
  /**
   * XXX - We should resolve fail the promise returned by
   *       VRDisplay.requestPresent() when the DX11 resources fail allocation
   *       in VRDisplayOculus::StartPresentation().
   *       Bailing out here prevents the crash but content should be aware
   *       that frames are not being presented.
   *       See Bug 1299309.
   **/

  if (!ChangeVisibility(true)) {
    return false;
  }
  if (!StartRendering()) {
    StopRendering();
    return false;
  }
  mIsPresenting = true;
  return true;
}

void OculusSession::StopPresentation() {
  mLastPresentationEnd = TimeStamp::Now();
  mIsPresenting = false;
}

bool OculusSession::SubmitFrame(
    const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer,
    ID3D11Texture2D* aTexture) {
  if (!IsPresentationReady()) {
    return false;
  }

  D3D11_TEXTURE2D_DESC textureDesc = {0};
  aTexture->GetDesc(&textureDesc);

  int currentRenderTarget = 0;
  ovrResult orv = ovr_GetTextureSwapChainCurrentIndex(mSession, mTextureSet,
                                                      ¤tRenderTarget);
  if (orv != ovrSuccess) {
    NS_WARNING("ovr_GetTextureSwapChainCurrentIndex failed.");
    return false;
  }

  ID3D11RenderTargetView* view = mRTView[currentRenderTarget];

  float clear[] = {0.0f, 0.0f, 0.0f, 1.0f};
  mContext->ClearRenderTargetView(view, clear);
  mContext->OMSetRenderTargets(1, &view, nullptr);

  Matrix viewMatrix = Matrix::Translation(-1.0, 1.0);
  viewMatrix.PreScale(2.0f / float(textureDesc.Width),
                      2.0f / float(textureDesc.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);

  D3D11_VIEWPORT viewport;
  viewport.MinDepth = 0.0f;
  viewport.MaxDepth = 1.0f;
  viewport.Width = textureDesc.Width;
  viewport.Height = textureDesc.Height;
  viewport.TopLeftX = 0;
  viewport.TopLeftY = 0;

  D3D11_RECT scissor;
  scissor.left = 0;
  scissor.right = textureDesc.Width;
  scissor.top = 0;
  scissor.bottom = textureDesc.Height;

  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, textureDesc.Width, textureDesc.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);

  RefPtr<ID3D11ShaderResourceView> srView;
  HRESULT hr = mDevice->CreateShaderResourceView(aTexture, nullptr,
                                                 getter_AddRefs(srView));
  if (FAILED(hr)) {
    gfxWarning() << "Could not create shader resource view for Oculus: "
                 << hexa(hr);
    return false;
  }
  ID3D11ShaderResourceView* viewPtr = srView.get();
  mContext->PSSetShaderResources(0 /* 0 == TexSlot::RGB */, 1, &viewPtr);
  // 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 Oculus");
    return false;
  }

  mContext->Draw(4, 0);

  orv = ovr_CommitTextureSwapChain(mSession, mTextureSet);
  if (orv != ovrSuccess) {
    NS_WARNING("ovr_CommitTextureSwapChain failed.");
    return false;
  }

  ovrLayerEyeFov layer;
  memset(&layer, 0, sizeof(layer));
  layer.Header.Type = ovrLayerType_EyeFov;
  layer.Header.Flags = 0;
  layer.ColorTexture[0] = mTextureSet;
  layer.ColorTexture[1] = nullptr;
  layer.Fov[0] = mFOVPort[0];
  layer.Fov[1] = mFOVPort[1];
  layer.Viewport[0].Pos.x = textureDesc.Width * aLayer.leftEyeRect.x;
  layer.Viewport[0].Pos.y = textureDesc.Height * aLayer.leftEyeRect.y;
  layer.Viewport[0].Size.w = textureDesc.Width * aLayer.leftEyeRect.width;
  layer.Viewport[0].Size.h = textureDesc.Height * aLayer.leftEyeRect.height;
  layer.Viewport[1].Pos.x = textureDesc.Width * aLayer.rightEyeRect.x;
  layer.Viewport[1].Pos.y = textureDesc.Height * aLayer.rightEyeRect.y;
  layer.Viewport[1].Size.w = textureDesc.Width * aLayer.rightEyeRect.width;
  layer.Viewport[1].Size.h = textureDesc.Height * aLayer.rightEyeRect.height;

  for (uint32_t i = 0; i < 2; ++i) {
    layer.RenderPose[i].Orientation.x = mFrameStartPose[i].Orientation.x;
    layer.RenderPose[i].Orientation.y = mFrameStartPose[i].Orientation.y;
    layer.RenderPose[i].Orientation.z = mFrameStartPose[i].Orientation.z;
    layer.RenderPose[i].Orientation.w = mFrameStartPose[i].Orientation.w;
    layer.RenderPose[i].Position.x = mFrameStartPose[i].Position.x;
    layer.RenderPose[i].Position.y = mFrameStartPose[i].Position.y;
    layer.RenderPose[i].Position.z = mFrameStartPose[i].Position.z;
  }

  ovrLayerHeader* layers = &layer.Header;
  orv = ovr_SubmitFrame(mSession, 0, nullptr, &layers, 1);
  // ovr_SubmitFrame will fail during the Oculus health and safety warning.
  // and will start succeeding once the warning has been dismissed by the user.

  if (!OVR_UNQUALIFIED_SUCCESS(orv)) {
    /**
     * We wish to throttle the framerate for any case that the rendered
     * result is not visible.  In some cases, such as during the Oculus
     * "health and safety warning", orv will be > 0 (OVR_SUCCESS but not
     * OVR_UNQUALIFIED_SUCCESS) and ovr_SubmitFrame will not block.
     * In this case, returning true would have resulted in an unthrottled
     * render loop hiting excessive frame rates and consuming resources.
     */

    return false;
  }

  return true;
}

bool OculusSession::LoadOvrLib() {
  if (mOvrLib) {
    // Already loaded, early exit
    return true;
  }
#if defined(_WIN32)
  nsTArray<nsString> libSearchPaths;
  nsString libName;
  nsString searchPath;

  for (;;) {
    UINT requiredLength = ::GetSystemDirectoryW(
        char16ptr_t(searchPath.BeginWriting()), searchPath.Length());
    if (!requiredLength) {
      break;
    }
    if (requiredLength < searchPath.Length()) {
      searchPath.Truncate(requiredLength);
      libSearchPaths.AppendElement(searchPath);
      break;
    }
    searchPath.SetLength(requiredLength);
  }
  libName.AppendPrintf("LibOVRRT%d_%d.dll", BUILD_BITS, OVR_PRODUCT_VERSION);

  // search the path/module dir
  libSearchPaths.InsertElementsAt(0, 1, u""_ns);

  // If the env var is present, we override libName
  if (_wgetenv(L"OVR_LIB_PATH")) {
    searchPath = _wgetenv(L"OVR_LIB_PATH");
    libSearchPaths.InsertElementsAt(0, 1, searchPath);
  }

  if (_wgetenv(L"OVR_LIB_NAME")) {
    libName = _wgetenv(L"OVR_LIB_NAME");
  }

  if (libName.IsEmpty()) {
    return false;
  }

  for (uint32_t i = 0; i < libSearchPaths.Length(); ++i) {
    nsString& libPath = libSearchPaths[i];
    nsString fullName;
    if (libPath.Length() == 0) {
      fullName.Assign(libName);
    } else {
      fullName.Assign(libPath + u"\\"_ns + libName);
    }

    mOvrLib = LoadLibraryWithFlags(fullName.get());
    if (mOvrLib) {
      break;
    }
  }
#else
#  error "Unsupported platform!"
#endif

  if (!mOvrLib) {
    return false;
  }

#define REQUIRE_FUNCTION(_x)                           \
  do {                                                 \
    *(void**)&_x = (void*)PR_FindSymbol(mOvrLib, #_x); \
    if (!_x) {                                         \
      printf_stderr(#_" symbol missing\n");          \
      goto fail;                                       \
    }                                                  \
  } while (0)

  REQUIRE_FUNCTION(ovr_Initialize);
  REQUIRE_FUNCTION(ovr_Shutdown);
  REQUIRE_FUNCTION(ovr_GetLastErrorInfo);
  REQUIRE_FUNCTION(ovr_GetVersionString);
  REQUIRE_FUNCTION(ovr_TraceMessage);
  REQUIRE_FUNCTION(ovr_IdentifyClient);
  REQUIRE_FUNCTION(ovr_GetHmdDesc);
  REQUIRE_FUNCTION(ovr_GetTrackerCount);
  REQUIRE_FUNCTION(ovr_GetTrackerDesc);
  REQUIRE_FUNCTION(ovr_Create);
  REQUIRE_FUNCTION(ovr_Destroy);
  REQUIRE_FUNCTION(ovr_GetSessionStatus);
  REQUIRE_FUNCTION(ovr_IsExtensionSupported);
  REQUIRE_FUNCTION(ovr_EnableExtension);
  REQUIRE_FUNCTION(ovr_SetTrackingOriginType);
  REQUIRE_FUNCTION(ovr_GetTrackingOriginType);
  REQUIRE_FUNCTION(ovr_RecenterTrackingOrigin);
  REQUIRE_FUNCTION(ovr_SpecifyTrackingOrigin);
  REQUIRE_FUNCTION(ovr_ClearShouldRecenterFlag);
  REQUIRE_FUNCTION(ovr_GetTrackingState);
  REQUIRE_FUNCTION(ovr_GetDevicePoses);
  REQUIRE_FUNCTION(ovr_GetTrackerPose);
  REQUIRE_FUNCTION(ovr_GetInputState);
  REQUIRE_FUNCTION(ovr_GetConnectedControllerTypes);
  REQUIRE_FUNCTION(ovr_GetTouchHapticsDesc);
  REQUIRE_FUNCTION(ovr_SetControllerVibration);
  REQUIRE_FUNCTION(ovr_SubmitControllerVibration);
  REQUIRE_FUNCTION(ovr_GetControllerVibrationState);
  REQUIRE_FUNCTION(ovr_TestBoundary);
  REQUIRE_FUNCTION(ovr_TestBoundaryPoint);
  REQUIRE_FUNCTION(ovr_SetBoundaryLookAndFeel);
  REQUIRE_FUNCTION(ovr_ResetBoundaryLookAndFeel);
  REQUIRE_FUNCTION(ovr_GetBoundaryGeometry);
  REQUIRE_FUNCTION(ovr_GetBoundaryDimensions);
  REQUIRE_FUNCTION(ovr_GetBoundaryVisible);
  REQUIRE_FUNCTION(ovr_RequestBoundaryVisible);
  REQUIRE_FUNCTION(ovr_GetTextureSwapChainLength);
  REQUIRE_FUNCTION(ovr_GetTextureSwapChainCurrentIndex);
  REQUIRE_FUNCTION(ovr_GetTextureSwapChainDesc);
  REQUIRE_FUNCTION(ovr_CommitTextureSwapChain);
  REQUIRE_FUNCTION(ovr_DestroyTextureSwapChain);
  REQUIRE_FUNCTION(ovr_DestroyMirrorTexture);
  REQUIRE_FUNCTION(ovr_GetFovTextureSize);
  REQUIRE_FUNCTION(ovr_GetRenderDesc2);
  REQUIRE_FUNCTION(ovr_WaitToBeginFrame);
  REQUIRE_FUNCTION(ovr_BeginFrame);
  REQUIRE_FUNCTION(ovr_EndFrame);
  REQUIRE_FUNCTION(ovr_SubmitFrame);
  REQUIRE_FUNCTION(ovr_GetPerfStats);
  REQUIRE_FUNCTION(ovr_ResetPerfStats);
  REQUIRE_FUNCTION(ovr_GetPredictedDisplayTime);
  REQUIRE_FUNCTION(ovr_GetTimeInSeconds);
  REQUIRE_FUNCTION(ovr_GetBool);
  REQUIRE_FUNCTION(ovr_SetBool);
  REQUIRE_FUNCTION(ovr_GetInt);
  REQUIRE_FUNCTION(ovr_SetInt);
  REQUIRE_FUNCTION(ovr_GetFloat);
  REQUIRE_FUNCTION(ovr_SetFloat);
  REQUIRE_FUNCTION(ovr_GetFloatArray);
  REQUIRE_FUNCTION(ovr_SetFloatArray);
  REQUIRE_FUNCTION(ovr_GetString);
  REQUIRE_FUNCTION(ovr_SetString);
  REQUIRE_FUNCTION(ovr_GetExternalCameras);
  REQUIRE_FUNCTION(ovr_SetExternalCameraProperties);

#ifdef XP_WIN

  REQUIRE_FUNCTION(ovr_CreateTextureSwapChainDX);
  REQUIRE_FUNCTION(ovr_GetTextureSwapChainBufferDX);
  REQUIRE_FUNCTION(ovr_CreateMirrorTextureDX);
  REQUIRE_FUNCTION(ovr_GetMirrorTextureBufferDX);

#endif

  REQUIRE_FUNCTION(ovr_CreateTextureSwapChainGL);
  REQUIRE_FUNCTION(ovr_GetTextureSwapChainBufferGL);
  REQUIRE_FUNCTION(ovr_CreateMirrorTextureGL);
  REQUIRE_FUNCTION(ovr_GetMirrorTextureBufferGL);

#undef REQUIRE_FUNCTION

  return true;

fail:
  ovr_Initialize = nullptr;
  PR_UnloadLibrary(mOvrLib);
  mOvrLib = nullptr;
  return false;
}

void OculusSession::UnloadOvrLib() {
  if (mOvrLib) {
    PR_UnloadLibrary(mOvrLib);
    mOvrLib = nullptr;
  }
}

bool OculusSession::StartLib(ovrInitFlags aFlags) {
  if (mInitFlags == 0) {
    ovrInitParams params;
    memset(¶ms, 0, sizeof(params));
    params.Flags = aFlags;
    params.RequestedMinorVersion = OVR_MINOR_VERSION;
    params.LogCallback = nullptr;
    params.ConnectionTimeoutMS = 0;

    ovrResult orv = ovr_Initialize(¶ms);

    if (orv == ovrSuccess) {
      mInitFlags = aFlags;
    } else {
      return false;
    }
  }
  MOZ_ASSERT(mInitFlags == aFlags);
  return true;
}

void OculusSession::StopLib() {
  if (mInitFlags) {
    ovr_Shutdown();
    mInitFlags = (ovrInitFlags)0;
  }
}

bool OculusSession::StartSession() {
  // ovr_Create can be slow when no HMD is present and we wish
  // to keep the same oculus session when possible, so we detect
  // presence of an HMD with ovr_GetHmdDesc before calling ovr_Create
  ovrHmdDesc desc = ovr_GetHmdDesc(NULL);
  if (desc.Type == ovrHmd_None) {
    // No HMD connected, destroy any existing session
    if (mSession) {
      ovr_Destroy(mSession);
      mSession = nullptr;
    }
    return false;
  }
  if (mSession != nullptr) {
    // HMD Detected and we already have a session, let's keep using it.
    return true;
  }

  // HMD Detected and we don't have a session yet,
  // try to create a new session
  ovrSession session;
  ovrGraphicsLuid luid;
  ovrResult orv = ovr_Create(&session, &luid);
  if (orv == ovrSuccess) {
    orv = ovr_SetTrackingOriginType(session, ovrTrackingOrigin_FloorLevel);
    if (orv != ovrSuccess) {
      NS_WARNING("ovr_SetTrackingOriginType failed.\n");
    }
    mSession = session;
    return true;
  }

  // Failed to create a session for the HMD
  return false;
}

void OculusSession::StopSession() {
  if (mSession) {
    ovr_Destroy(mSession);
    mSession = nullptr;
  }
}

bool OculusSession::CreateD3DObjects() {
  RefPtr<ID3D11Device> device = gfx::DeviceManagerDx::Get()->GetVRDevice();
  if (!device) {
    return false;
  }
  if (!CreateD3DContext(device)) {
    return false;
  }
  return true;
}

bool OculusSession::CreateShaders() {
  if (!mQuadVS) {
    if (FAILED(mDevice->CreateVertexShader(
            sLayerQuadVS.mData, sLayerQuadVS.mLength, nullptr, &mQuadVS))) {
      NS_WARNING("Failed to create vertex shader for Oculus");
      return false;
    }
  }

  if (!mQuadPS) {
    if (FAILED(mDevice->CreatePixelShader(sRGBShader.mData, sRGBShader.mLength,
                                          nullptr, &mQuadPS))) {
      NS_WARNING("Failed to create pixel shader for Oculus");
      return false;
    }
  }

  CD3D11_BUFFER_DESC cBufferDesc(sizeof(layers::VertexShaderConstants),
                                 D3D11_BIND_CONSTANT_BUFFER,
                                 D3D11_USAGE_DYNAMIC, D3D11_CPU_ACCESS_WRITE);

  if (!mVSConstantBuffer) {
    if (FAILED(mDevice->CreateBuffer(&cBufferDesc, nullptr,
                                     getter_AddRefs(mVSConstantBuffer)))) {
      NS_WARNING("Failed to vertex shader constant buffer for Oculus");
      return false;
    }
  }

  if (!mPSConstantBuffer) {
    cBufferDesc.ByteWidth = sizeof(layers::PixelShaderConstants);
    if (FAILED(mDevice->CreateBuffer(&cBufferDesc, nullptr,
                                     getter_AddRefs(mPSConstantBuffer)))) {
      NS_WARNING("Failed to pixel shader constant buffer for Oculus");
      return false;
    }
  }

  if (!mLinearSamplerState) {
    CD3D11_SAMPLER_DESC samplerDesc(D3D11_DEFAULT);
    if (FAILED(mDevice->CreateSamplerState(
            &samplerDesc, getter_AddRefs(mLinearSamplerState)))) {
      NS_WARNING("Failed to create sampler state for Oculus");
      return false;
    }
  }

  if (!mInputLayout) {
    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 Oculus");
      return false;
    }
  }

  if (!mVertexBuffer) {
    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 Oculus");
      return false;
    }
  }

  memset(&mVSConstants, 0, sizeof(mVSConstants));
  memset(&mPSConstants, 0, sizeof(mPSConstants));
  return true;
}

void OculusSession::DestroyShaders() {}

bool OculusSession::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 OculusSession::StartRendering() {
  if (!mTextureSet) {
    /**
     * The presentation format is determined by content, which describes the
     * left and right eye rectangles in the VRLayer.  The default, if no
     * coordinates are passed is to place the left and right eye textures
     * side-by-side within the buffer.
     *
     * XXX - An optimization would be to dynamically resize this buffer
     *       to accomodate sites that are choosing to render in a lower
     *       resolution or are using space outside of the left and right
     *       eye textures for other purposes.  (Bug 1291443)
     */


    ovrTextureSwapChainDesc desc;
    memset(&desc, 0, sizeof(desc));
    desc.Type = ovrTexture_2D;
    desc.ArraySize = 1;
    desc.Format = OVR_FORMAT_B8G8R8A8_UNORM_SRGB;
    desc.Width = mPresentationSize.width;
    desc.Height = mPresentationSize.height;
    desc.MipLevels = 1;
    desc.SampleCount = 1;
    desc.StaticImage = false;
    desc.MiscFlags = ovrTextureMisc_DX_Typeless;
    desc.BindFlags = ovrTextureBind_DX_RenderTarget;

    ovrResult orv =
        ovr_CreateTextureSwapChainDX(mSession, mDevice, &desc, &mTextureSet);
    if (orv != ovrSuccess) {
      NS_WARNING("ovr_CreateTextureSwapChainDX failed");
      return false;
    }

    int textureCount = 0;
    orv = ovr_GetTextureSwapChainLength(mSession, mTextureSet, &textureCount);
    if (orv != ovrSuccess) {
      NS_WARNING("ovr_GetTextureSwapChainLength failed");
      return false;
    }
    mTexture.SetLength(textureCount);
    mRTView.SetLength(textureCount);
    mSRV.SetLength(textureCount);
    for (int i = 0; i < textureCount; ++i) {
      ID3D11Texture2D* texture = nullptr;
      orv = ovr_GetTextureSwapChainBufferDX(mSession, mTextureSet, i,
                                            IID_PPV_ARGS(&texture));
      if (orv != ovrSuccess) {
        NS_WARNING("Failed to create Oculus texture swap chain.");
        return false;
      }

      RefPtr<ID3D11RenderTargetView> rtView;
      CD3D11_RENDER_TARGET_VIEW_DESC rtvDesc(D3D11_RTV_DIMENSION_TEXTURE2D,
                                             DXGI_FORMAT_B8G8R8A8_UNORM);
      HRESULT hr = mDevice->CreateRenderTargetView(texture, &rtvDesc,
                                                   getter_AddRefs(rtView));
      if (FAILED(hr)) {
        NS_WARNING(
            "Failed to create RenderTargetView for Oculus texture swap chain.");
        texture->Release();
        return false;
      }

      RefPtr<ID3D11ShaderResourceView> srv;
      CD3D11_SHADER_RESOURCE_VIEW_DESC srvDesc(D3D11_SRV_DIMENSION_TEXTURE2D,
                                               DXGI_FORMAT_B8G8R8A8_UNORM);
      hr = mDevice->CreateShaderResourceView(texture, &srvDesc,
                                             getter_AddRefs(srv));
      if (FAILED(hr)) {
        NS_WARNING(
            "Failed to create ShaderResourceView for Oculus texture swap "
            "chain.");
        texture->Release();
        return false;
      }

      mTexture[i] = texture;
      mRTView[i] = rtView;
      mSRV[i] = srv;
      texture->Release();
    }
  }
  return true;
}

bool OculusSession::IsPresentationReady() const {
  return mTextureSet != nullptr;
}

void OculusSession::StopRendering() {
  mSRV.Clear();
  mRTView.Clear();
  mTexture.Clear();

  if (mTextureSet && mSession) {
    ovr_DestroyTextureSwapChain(mSession, mTextureSet);
  }
  mTextureSet = nullptr;
  mIsPresenting = false;
}

bool OculusSession::InitState(VRSystemState& aSystemState) {
  VRDisplayState& state = aSystemState.displayState;
  strncpy(state.displayName.data(), "Oculus VR HMD", kVRDisplayNameMaxLen);
  state.isConnected = true;
  state.isMounted = false;

  ovrHmdDesc desc = ovr_GetHmdDesc(mSession);

  state.capabilityFlags = VRDisplayCapabilityFlags::Cap_None;
  if (desc.AvailableTrackingCaps & ovrTrackingCap_Orientation) {
    state.capabilityFlags |= VRDisplayCapabilityFlags::Cap_Orientation;
    state.capabilityFlags |= VRDisplayCapabilityFlags::Cap_AngularAcceleration;
  }
  if (desc.AvailableTrackingCaps & ovrTrackingCap_Position) {
    state.capabilityFlags |= VRDisplayCapabilityFlags::Cap_Position;
    state.capabilityFlags |= VRDisplayCapabilityFlags::Cap_LinearAcceleration;
    state.capabilityFlags |= VRDisplayCapabilityFlags::Cap_StageParameters;
  }
  state.capabilityFlags |= VRDisplayCapabilityFlags::Cap_External;
  state.capabilityFlags |= VRDisplayCapabilityFlags::Cap_MountDetection;
  state.capabilityFlags |= VRDisplayCapabilityFlags::Cap_Present;
  state.capabilityFlags |= VRDisplayCapabilityFlags::Cap_ImmersiveVR;
  state.blendMode = VRDisplayBlendMode::Opaque;
  state.reportsDroppedFrames = true;

  mFOVPort[VRDisplayState::Eye_Left] = desc.DefaultEyeFov[ovrEye_Left];
  mFOVPort[VRDisplayState::Eye_Right] = desc.DefaultEyeFov[ovrEye_Right];

  state.eyeFOV[VRDisplayState::Eye_Left] =
      FromFovPort(mFOVPort[VRDisplayState::Eye_Left]);
  state.eyeFOV[VRDisplayState::Eye_Right] =
      FromFovPort(mFOVPort[VRDisplayState::Eye_Right]);

  float pixelsPerDisplayPixel = 1.0;
  ovrSizei texSize[2];

  // get eye texture sizes
  for (uint32_t eye = 0; eye < VRDisplayState::NumEyes; eye++) {
    texSize[eye] = ovr_GetFovTextureSize(mSession, (ovrEyeType)eye,
                                         mFOVPort[eye], pixelsPerDisplayPixel);
  }

  // take the max of both for eye resolution
  state.eyeResolution.width = std::max(texSize[VRDisplayState::Eye_Left].w,
                                       texSize[VRDisplayState::Eye_Right].w);
  state.eyeResolution.height = std::max(texSize[VRDisplayState::Eye_Left].h,
                                        texSize[VRDisplayState::Eye_Right].h);
  state.nativeFramebufferScaleFactor = 1.0f;

  // default to an identity quaternion
  aSystemState.sensorState.pose.orientation[3] = 1.0f;

  UpdateStageParameters(state);
  UpdateEyeParameters(aSystemState);

  VRHMDSensorState& sensorState = aSystemState.sensorState;
  sensorState.flags =
      (VRDisplayCapabilityFlags)((int)
                                     VRDisplayCapabilityFlags::Cap_Orientation |
                                 (int)VRDisplayCapabilityFlags::Cap_Position);
  sensorState.pose.orientation[3] = 1.0f;  // Default to an identity quaternion

  return true;
}

void OculusSession::UpdateStageParameters(VRDisplayState& aState) {
  ovrVector3f playArea;
  ovrResult res =
      ovr_GetBoundaryDimensions(mSession, ovrBoundary_PlayArea, &playArea);
  if (res == ovrSuccess) {
    aState.stageSize.width = playArea.x;
    aState.stageSize.height = playArea.z;
  } else {
    // If we fail, fall back to reasonable defaults.
    // 1m x 1m space
    aState.stageSize.width = 1.0f;
    aState.stageSize.height = 1.0f;
  }

  float eyeHeight =
      ovr_GetFloat(mSession, OVR_KEY_EYE_HEIGHT, OVR_DEFAULT_EYE_HEIGHT);

  aState.sittingToStandingTransform[0] = 1.0f;
  aState.sittingToStandingTransform[1] = 0.0f;
  aState.sittingToStandingTransform[2] = 0.0f;
  aState.sittingToStandingTransform[3] = 0.0f;

  aState.sittingToStandingTransform[4] = 0.0f;
  aState.sittingToStandingTransform[5] = 1.0f;
  aState.sittingToStandingTransform[6] = 0.0f;
  aState.sittingToStandingTransform[7] = 0.0f;

  aState.sittingToStandingTransform[8] = 0.0f;
  aState.sittingToStandingTransform[9] = 0.0f;
  aState.sittingToStandingTransform[10] = 1.0f;
  aState.sittingToStandingTransform[11] = 0.0f;

  aState.sittingToStandingTransform[12] = 0.0f;
  aState.sittingToStandingTransform[13] = eyeHeight;
  aState.sittingToStandingTransform[14] = 0.0f;
  aState.sittingToStandingTransform[15] = 1.0f;
}

void OculusSession::UpdateEyeParameters(VRSystemState& aState) {
  if (!mSession) {
    return;
  }
  // This must be called every frame in order to
  // account for continuous adjustments to ipd.
  gfx::Matrix4x4 headToEyeTransforms[2];
  for (uint32_t eye = 0; eye < VRDisplayState::NumEyes; eye++) {
    // As of Oculus 1.17 SDK, we must use the ovr_GetRenderDesc2 function to
    // return the updated version of ovrEyeRenderDesc.  This is normally done by
    // the Oculus static lib shim, but we need to do this explicitly as we are
    // loading the Oculus runtime dll directly.
    ovrEyeRenderDesc renderDesc =
        ovr_GetRenderDesc2(mSession, (ovrEyeType)eye, mFOVPort[eye]);
    aState.displayState.eyeTranslation[eye].x =
        renderDesc.HmdToEyePose.Position.x;
    aState.displayState.eyeTranslation[eye].y =
        renderDesc.HmdToEyePose.Position.y;
    aState.displayState.eyeTranslation[eye].z =
        renderDesc.HmdToEyePose.Position.z;

    Matrix4x4 pose;
    pose.SetRotationFromQuaternion(
        gfx::Quaternion(-renderDesc.HmdToEyePose.Orientation.x,
                        -renderDesc.HmdToEyePose.Orientation.y,
                        -renderDesc.HmdToEyePose.Orientation.z,
                        renderDesc.HmdToEyePose.Orientation.w));
    pose.PreTranslate(renderDesc.HmdToEyePose.Position.x,
                      renderDesc.HmdToEyePose.Position.y,
                      renderDesc.HmdToEyePose.Position.z);
    pose.Invert();
    headToEyeTransforms[eye] = pose;
  }
  aState.sensorState.CalcViewMatrices(headToEyeTransforms);

  Matrix4x4 matView[2];
  memcpy(matView[0].components, aState.sensorState.leftViewMatrix.data(),
         sizeof(float) * 16);
  memcpy(matView[1].components, aState.sensorState.rightViewMatrix.data(),
         sizeof(float) * 16);

  for (uint32_t eye = 0; eye < VRDisplayState::NumEyes; eye++) {
    Point3D eyeTranslation;
    Quaternion eyeRotation;
    Point3D eyeScale;
    if (!matView[eye].Decompose(eyeTranslation, eyeRotation, eyeScale)) {
      NS_WARNING("Failed to decompose eye pose matrix for Oculus");
    }

    eyeRotation.Invert();
    mFrameStartPose[eye].Orientation.x = eyeRotation.x;
    mFrameStartPose[eye].Orientation.y = eyeRotation.y;
    mFrameStartPose[eye].Orientation.z = eyeRotation.z;
    mFrameStartPose[eye].Orientation.w = eyeRotation.w;
    mFrameStartPose[eye].Position.x = eyeTranslation.x;
    mFrameStartPose[eye].Position.y = eyeTranslation.y;
    mFrameStartPose[eye].Position.z = eyeTranslation.z;
  }
}

void OculusSession::UpdateHeadsetPose(VRSystemState& aState) {
  if (!mSession) {
    return;
  }
  double predictedFrameTime = 0.0f;
  if (StaticPrefs::dom_vr_poseprediction_enabled()) {
    // XXX We might need to call ovr_GetPredictedDisplayTime even if we don't
    // use the result. If we don't call it, the Oculus driver will spew out many
    // warnings...
    predictedFrameTime = ovr_GetPredictedDisplayTime(mSession, 0);
  }
  ovrTrackingState trackingState =
      ovr_GetTrackingState(mSession, predictedFrameTime, true);
  ovrPoseStatef& pose(trackingState.HeadPose);

  aState.sensorState.timestamp = pose.TimeInSeconds;

  if (trackingState.StatusFlags & ovrStatus_OrientationTracked) {
    aState.sensorState.flags |= VRDisplayCapabilityFlags::Cap_Orientation;

    aState.sensorState.pose.orientation[0] = pose.ThePose.Orientation.x;
    aState.sensorState.pose.orientation[1] = pose.ThePose.Orientation.y;
    aState.sensorState.pose.orientation[2] = pose.ThePose.Orientation.z;
    aState.sensorState.pose.orientation[3] = pose.ThePose.Orientation.w;

    aState.sensorState.pose.angularVelocity[0] = pose.AngularVelocity.x;
    aState.sensorState.pose.angularVelocity[1] = pose.AngularVelocity.y;
    aState.sensorState.pose.angularVelocity[2] = pose.AngularVelocity.z;

    aState.sensorState.flags |=
        VRDisplayCapabilityFlags::Cap_AngularAcceleration;

    aState.sensorState.pose.angularAcceleration[0] = pose.AngularAcceleration.x;
    aState.sensorState.pose.angularAcceleration[1] = pose.AngularAcceleration.y;
    aState.sensorState.pose.angularAcceleration[2] = pose.AngularAcceleration.z;
  } else {
    // default to an identity quaternion
    aState.sensorState.pose.orientation[3] = 1.0f;
  }

  if (trackingState.StatusFlags & ovrStatus_PositionTracked) {
    float eyeHeight =
        ovr_GetFloat(mSession, OVR_KEY_EYE_HEIGHT, OVR_DEFAULT_EYE_HEIGHT);
    aState.sensorState.flags |= VRDisplayCapabilityFlags::Cap_Position;

    aState.sensorState.pose.position[0] = pose.ThePose.Position.x;
    aState.sensorState.pose.position[1] = pose.ThePose.Position.y - eyeHeight;
    aState.sensorState.pose.position[2] = pose.ThePose.Position.z;

    aState.sensorState.pose.linearVelocity[0] = pose.LinearVelocity.x;
    aState.sensorState.pose.linearVelocity[1] = pose.LinearVelocity.y;
    aState.sensorState.pose.linearVelocity[2] = pose.LinearVelocity.z;

    aState.sensorState.flags |=
        VRDisplayCapabilityFlags::Cap_LinearAcceleration;

    aState.sensorState.pose.linearAcceleration[0] = pose.LinearAcceleration.x;
    aState.sensorState.pose.linearAcceleration[1] = pose.LinearAcceleration.y;
    aState.sensorState.pose.linearAcceleration[2] = pose.LinearAcceleration.z;
  }
  aState.sensorState.flags |= VRDisplayCapabilityFlags::Cap_External;
  aState.sensorState.flags |= VRDisplayCapabilityFlags::Cap_MountDetection;
  aState.sensorState.flags |= VRDisplayCapabilityFlags::Cap_Present;
}

void OculusSession::UpdateControllers(VRSystemState& aState) {
  if (!mSession) {
    return;
  }

  ovrInputState inputState;
  bool hasInputState = ovr_GetInputState(mSession, ovrControllerType_Touch,
                                         &inputState) == ovrSuccess;

  if (!hasInputState) {
    return;
  }

  EnumerateControllers(aState, inputState);
  UpdateControllerInputs(aState, inputState);
  UpdateControllerPose(aState, inputState);
}

void OculusSession::UpdateControllerPose(VRSystemState& aState,
                                         const ovrInputState& aInputState) {
  ovrTrackingState trackingState = ovr_GetTrackingState(mSession, 0.0, false);
  for (uint32_t handIdx = 0; handIdx < 2; handIdx++) {
    // Left Touch Controller will always be at index 0 and
    // and Right Touch Controller will always be at index 1
    VRControllerState& controllerState = aState.controllerState[handIdx];
    if (aInputState.ControllerType & OculusControllerTypes[handIdx]) {
      ovrPoseStatef& pose = trackingState.HandPoses[handIdx];
      bool bNewController = !(controllerState.flags &
                              dom::GamepadCapabilityFlags::Cap_Orientation);
      if (bNewController) {
        controllerState.flags |= dom::GamepadCapabilityFlags::Cap_Orientation;
        controllerState.flags |= dom::GamepadCapabilityFlags::Cap_Position;
        controllerState.flags |=
            dom::GamepadCapabilityFlags::Cap_AngularAcceleration;
        controllerState.flags |=
            dom::GamepadCapabilityFlags::Cap_LinearAcceleration;
        controllerState.flags |=
            dom::GamepadCapabilityFlags::Cap_GripSpacePosition;
      }

      if (bNewController || trackingState.HandStatusFlags[handIdx] &
                                ovrStatus_OrientationTracked) {
        controllerState.pose.orientation[0] = pose.ThePose.Orientation.x;
        controllerState.pose.orientation[1] = pose.ThePose.Orientation.y;
        controllerState.pose.orientation[2] = pose.ThePose.Orientation.z;
        controllerState.pose.orientation[3] = pose.ThePose.Orientation.w;
        controllerState.pose.angularVelocity[0] = pose.AngularVelocity.x;
        controllerState.pose.angularVelocity[1] = pose.AngularVelocity.y;
        controllerState.pose.angularVelocity[2] = pose.AngularVelocity.z;
        controllerState.pose.angularAcceleration[0] =
            pose.AngularAcceleration.x;
        controllerState.pose.angularAcceleration[1] =
            pose.AngularAcceleration.y;
        controllerState.pose.angularAcceleration[2] =
            pose.AngularAcceleration.z;
        controllerState.isOrientationValid = true;
      } else {
        controllerState.isOrientationValid = false;
      }
      if (bNewController ||
          trackingState.HandStatusFlags[handIdx] & ovrStatus_PositionTracked) {
        controllerState.pose.position[0] = pose.ThePose.Position.x;
        controllerState.pose.position[1] = pose.ThePose.Position.y;
        controllerState.pose.position[2] = pose.ThePose.Position.z;
        controllerState.pose.linearVelocity[0] = pose.LinearVelocity.x;
        controllerState.pose.linearVelocity[1] = pose.LinearVelocity.y;
        controllerState.pose.linearVelocity[2] = pose.LinearVelocity.z;
        controllerState.pose.linearAcceleration[0] = pose.LinearAcceleration.x;
        controllerState.pose.linearAcceleration[1] = pose.LinearAcceleration.y;
        controllerState.pose.linearAcceleration[2] = pose.LinearAcceleration.z;

        float eyeHeight =
            ovr_GetFloat(mSession, OVR_KEY_EYE_HEIGHT, OVR_DEFAULT_EYE_HEIGHT);
        controllerState.pose.position[1] -= eyeHeight;
        controllerState.isPositionValid = true;
      } else {
        controllerState.isPositionValid = false;
      }
      controllerState.targetRayPose = controllerState.pose;
    }
  }
}

void OculusSession::EnumerateControllers(VRSystemState& aState,
                                         const ovrInputState& aInputState) {
  for (uint32_t handIdx = 0; handIdx < 2; handIdx++) {
    // Left Touch Controller will always be at index 0 and
    // and Right Touch Controller will always be at index 1
    VRControllerState& controllerState = aState.controllerState[handIdx];
    if (aInputState.ControllerType & OculusControllerTypes[handIdx]) {
      // Touch Controller detected
      if (controllerState.controllerName[0] == '\0') {
        // Controller has been just enumerated
        strncpy(controllerState.controllerName.data(),
                OculusControllerNames[handIdx],
                controllerState.controllerName.size());
        controllerState.hand = OculusControllerHand[handIdx];
        controllerState.targetRayMode = gfx::TargetRayMode::TrackedPointer;
        controllerState.numButtons = kNumOculusButtons;
        controllerState.numAxes = kNumOculusAxes;
        controllerState.numHaptics = kNumOculusHaptcs;
        controllerState.type = VRControllerType::OculusTouch;
      }
    } else {
      // Touch Controller not detected
      if (controllerState.controllerName[0] != '\0') {
        // Clear any newly disconnected ontrollers
        memset(&controllerState, 0, sizeof(VRControllerState));
      }
    }
  }
}

void OculusSession::UpdateControllerInputs(VRSystemState& aState,
                                           const ovrInputState& aInputState) {
  const float triggerThreshold =
      StaticPrefs::dom_vr_controller_trigger_threshold();

  for (uint32_t handIdx = 0; handIdx < 2; handIdx++) {
    // Left Touch Controller will always be at index 0 and
    // and Right Touch Controller will always be at index 1
    VRControllerState& controllerState = aState.controllerState[handIdx];
    if (aInputState.ControllerType & OculusControllerTypes[handIdx]) {
      // Update Button States
      controllerState.buttonPressed = 0;
      controllerState.buttonTouched = 0;
      uint32_t buttonIdx = 0;

      // Button 0: Trigger
      VRSession::UpdateTrigger(controllerState, buttonIdx,
                               aInputState.IndexTrigger[handIdx],
                               triggerThreshold);
      ++buttonIdx;
      // Button 1: Grip
      VRSession::UpdateTrigger(controllerState, buttonIdx,
                               aInputState.HandTrigger[handIdx],
                               triggerThreshold);
      ++buttonIdx;
      // Button 2: a placeholder button for trackpad.
      UpdateButton(aInputState, handIdx, buttonIdx, controllerState);
      ++buttonIdx;
      // Button 3: Thumbstick
      UpdateButton(aInputState, handIdx, buttonIdx, controllerState);
      ++buttonIdx;
      // Button 4: A
      UpdateButton(aInputState, handIdx, buttonIdx, controllerState);
      ++buttonIdx;
      // Button 5: B
      UpdateButton(aInputState, handIdx, buttonIdx, controllerState);
      ++buttonIdx;
      // Button 6: ThumbRest
      UpdateButton(aInputState, handIdx, buttonIdx, controllerState);
      ++buttonIdx;

      MOZ_ASSERT(buttonIdx == kNumOculusButtons);

      // Update Thumbstick axis
      uint32_t axisIdx = 0;
      // Axis 0, 1: placeholder axes for trackpad.
      axisIdx += 2;

      // Axis 2, 3: placeholder axes for thumbstick.
      float axisValue = aInputState.Thumbstick[handIdx].x;
      if (abs(axisValue) < 0.0000009f) {
        axisValue = 0.0f;  // Clear noise signal
      }
      controllerState.axisValue[axisIdx] = axisValue;
      axisIdx++;

      // Note that y axis is intentionally inverted!
      axisValue = -aInputState.Thumbstick[handIdx].y;
      if (abs(axisValue) < 0.0000009f) {
        axisValue = 0.0f;  // Clear noise signal
      }
      controllerState.axisValue[axisIdx] = axisValue;
      axisIdx++;

      MOZ_ASSERT(axisIdx == kNumOculusAxes);
    }
    SetControllerSelectionAndSqueezeFrameId(
        controllerState, aState.displayState.lastSubmittedFrameId);
  }
}

void OculusSession::UpdateTelemetry(VRSystemState& aSystemState) {
  if (!mSession) {
    return;
  }
  ovrPerfStats perfStats;
  if (ovr_GetPerfStats(mSession, &perfStats) == ovrSuccess) {
    if (perfStats.FrameStatsCount) {
      aSystemState.displayState.droppedFrameCount =
          perfStats.FrameStats[0].AppDroppedFrameCount;
    }
  }
}

void OculusSession::VibrateHaptic(uint32_t aControllerIdx,
                                  uint32_t aHapticIndex, float aIntensity,
                                  float aDuration) {
  if (!mSession) {
    return;
  }

  if (aDuration <= 0.0f) {
    StopVibrateHaptic(aControllerIdx);
    return;
  }

  // Vibration amplitude in the [0.0, 1.0] range
  MOZ_ASSERT(aControllerIdx >= 0 && aControllerIdx <= 1);
  mHapticPulseIntensity[aControllerIdx] = aIntensity > 1.0 ? 1.0 : aIntensity;
  mRemainingVibrateTime[aControllerIdx] = aDuration;
  ovrControllerType hand = OculusControllerTypes[aControllerIdx];

  // The gamepad extensions API does not yet have independent control
  // of frequency and amplitude.  We are always sending 0.0f (160hz)
  // to the frequency argument.
  ovrResult result = ovr_SetControllerVibration(
      mSession, hand, 0.0f, mHapticPulseIntensity[aControllerIdx]);
  if (result != ovrSuccess) {
    // This may happen if called when not presenting.
    gfxWarning() << "ovr_SetControllerVibration failed.";
  }
}

void OculusSession::StopVibrateHaptic(uint32_t aControllerIdx) {
  if (!mSession) {
    return;
  }
  MOZ_ASSERT(aControllerIdx >= 0 && aControllerIdx <= 1);
  ovrControllerType hand = OculusControllerTypes[aControllerIdx];
  mRemainingVibrateTime[aControllerIdx] = 0.0f;
  mHapticPulseIntensity[aControllerIdx] = 0.0f;

  ovrResult result = ovr_SetControllerVibration(mSession, hand, 0.0f, 0.0f);
  if (result != ovrSuccess) {
    // This may happen if called when not presenting.
    gfxWarning() << "ovr_SetControllerVibration failed.";
  }
}

void OculusSession::StopAllHaptics() {
  // Left Oculus Touch
  StopVibrateHaptic(0);
  // Right Oculus Touch
  StopVibrateHaptic(1);
}

void OculusSession::UpdateHaptics() {
  if (!mSession) {
    return;
  }
  // The Oculus API and hardware takes at least 33ms to respond
  // to haptic state changes, so it is not beneficial to create
  // a dedicated haptic feedback thread and update multiple
  // times per frame.
  // If we wish to support more accurate effects with sub-frame timing,
  // we should use the buffered haptic feedback API's.

  TimeStamp now = TimeStamp::Now();
  if (mLastHapticUpdate.IsNull()) {
    mLastHapticUpdate = now;
    return;
  }
  float deltaTime = (float)(now - mLastHapticUpdate).ToSeconds();
  mLastHapticUpdate = now;
  for (int i = 0; i < 2; i++) {
    if (mRemainingVibrateTime[i] <= 0.0f) {
      continue;
    }
    mRemainingVibrateTime[i] -= deltaTime;
    ovrControllerType hand = OculusControllerTypes[i];
    if (mRemainingVibrateTime[i] > 0.0f) {
      ovrResult result = ovr_SetControllerVibration(mSession, hand, 0.0f,
                                                    mHapticPulseIntensity[i]);
      if (result != ovrSuccess) {
        // This may happen if called when not presenting.
        gfxWarning() << "ovr_SetControllerVibration failed.";
      }
    } else {
      StopVibrateHaptic(i);
    }
  }
}

}  // namespace gfx
}  // namespace mozilla

Messung V0.5 in Prozent
C=89 H=99 G=94

¤ Dauer der Verarbeitung: 0.24 Sekunden  (vorverarbeitet am  2026-04-28) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge