Example #1
0
void
VRDisplayHost::SubmitFrame(VRLayerParent* aLayer, PTextureParent* aTexture,
                           const gfx::Rect& aLeftEyeRect,
                           const gfx::Rect& aRightEyeRect)
{
  if ((mDisplayInfo.mGroupMask & aLayer->GetGroup()) == 0) {
    // Suppress layers hidden by the group mask
    return;
  }

  TextureHost* th = TextureHost::AsTextureHost(aTexture);
  // WebVR doesn't use the compositor to compose the frame, so use
  // AutoLockTextureHostWithoutCompositor here.
  AutoLockTextureHostWithoutCompositor autoLock(th);
  if (autoLock.Failed()) {
    NS_WARNING("Failed to lock the VR layer texture");
    return;
  }

  CompositableTextureSourceRef source;
  if (!th->BindTextureSource(source)) {
    NS_WARNING("The TextureHost was successfully locked but can't provide a TextureSource");
    return;
  }
  MOZ_ASSERT(source);

  IntSize texSize = source->GetSize();

  TextureSourceD3D11* sourceD3D11 = source->AsSourceD3D11();
  if (!sourceD3D11) {
    NS_WARNING("WebVR support currently only implemented for D3D11");
    return;
  }

  if (!SubmitFrame(sourceD3D11, texSize, aLeftEyeRect, aRightEyeRect)) {
    return;
  }

  /**
   * Trigger the next VSync immediately after we are successfully
   * submitting frames.  As SubmitFrame is responsible for throttling
   * the render loop, if we don't successfully call it, we shouldn't trigger
   * NotifyVRVsync immediately, as it will run unbounded.
   * If NotifyVRVsync is not called here due to SubmitFrame failing, the
   * fallback "watchdog" code in VRDisplayHost::NotifyVSync() will cause
   * frames to continue at a lower refresh rate until frame submission
   * succeeds again.
   */
  VRManager *vm = VRManager::Get();
  MOZ_ASSERT(vm);
  vm->NotifyVRVsync(mDisplayInfo.mDisplayID);
}
Example #2
0
void
VRDisplayOpenVR::SubmitFrame(TextureSourceD3D11* aSource,
  const IntSize& aSize,
  const VRHMDSensorState& aSensorState,
  const gfx::Rect& aLeftEyeRect,
  const gfx::Rect& aRightEyeRect)
{
  if (!mIsPresenting) {
    return;
  }

  ::vr::Texture_t tex;
  tex.handle = (void *)aSource->GetD3D11Texture();
  tex.eType = ::vr::EGraphicsAPIConvention::API_DirectX;
  tex.eColorSpace = ::vr::EColorSpace::ColorSpace_Auto;

  ::vr::VRTextureBounds_t bounds;
  bounds.uMin = aLeftEyeRect.x;
  bounds.vMin = 1.0 - aLeftEyeRect.y;
  bounds.uMax = aLeftEyeRect.x + aLeftEyeRect.width;
  bounds.vMax = 1.0 - aLeftEyeRect.y - aLeftEyeRect.height;

  ::vr::EVRCompositorError err;
  err = mVRCompositor->Submit(::vr::EVREye::Eye_Left, &tex, &bounds);
  if (err != ::vr::EVRCompositorError::VRCompositorError_None) {
    printf_stderr("OpenVR Compositor Submit() failed.\n");
  }

  bounds.uMin = aRightEyeRect.x;
  bounds.vMin = 1.0 - aRightEyeRect.y;
  bounds.uMax = aRightEyeRect.x + aRightEyeRect.width;
  bounds.vMax = 1.0 - aRightEyeRect.y - aRightEyeRect.height;

  err = mVRCompositor->Submit(::vr::EVREye::Eye_Right, &tex, &bounds);
  if (err != ::vr::EVRCompositorError::VRCompositorError_None) {
    printf_stderr("OpenVR Compositor Submit() failed.\n");
  }

  mVRCompositor->PostPresentHandoff();

  // Trigger the next VSync immediately
  VRManager *vm = VRManager::Get();
  MOZ_ASSERT(vm);
  vm->NotifyVRVsync(mDisplayInfo.mDisplayID);
}
Example #3
0
void
VRDisplayPuppet::SubmitFrame(TextureSourceOGL* aSource,
                             const IntSize& aSize,
                             const VRHMDSensorState& aSensorState,
                             const gfx::Rect& aLeftEyeRect,
                             const gfx::Rect& aRightEyeRect)
{
  if (!mIsPresenting) {
    return;
  }

  // TODO: Bug 1343730, Need to block until the next simulated
  // vblank interval and capture frames for use in reftests.

  // Trigger the next VSync immediately
  VRManager *vm = VRManager::Get();
  MOZ_ASSERT(vm);
  vm->NotifyVRVsync(mDisplayInfo.mDisplayID);
}
Example #4
0
void
VRDisplayOculus::SubmitFrame(TextureSourceD3D11* aSource,
  const IntSize& aSize,
  const VRHMDSensorState& aSensorState,
  const gfx::Rect& aLeftEyeRect,
  const gfx::Rect& aRightEyeRect)
{
  if (!mIsPresenting) {
    return;
  }
  if (mRenderTargets.IsEmpty()) {
    /**
     * 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.
     **/
    return;
  }
  MOZ_ASSERT(mDevice);
  MOZ_ASSERT(mContext);

  RefPtr<CompositingRenderTargetD3D11> surface = GetNextRenderTarget();

  surface->BindRenderTarget(mContext);

  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);

  D3D11_VIEWPORT viewport;
  viewport.MinDepth = 0.0f;
  viewport.MaxDepth = 1.0f;
  viewport.Width = aSize.width;
  viewport.Height = aSize.height;
  viewport.TopLeftX = 0;
  viewport.TopLeftY = 0;

  D3D11_RECT scissor;
  scissor.left = 0;
  scissor.right = aSize.width;
  scissor.top = 0;
  scissor.bottom = aSize.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, 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 Oculus");
    return;
  }

  mContext->Draw(4, 0);

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

  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 = aSize.width * aLeftEyeRect.x;
  layer.Viewport[0].Pos.y = aSize.height * aLeftEyeRect.y;
  layer.Viewport[0].Size.w = aSize.width * aLeftEyeRect.width;
  layer.Viewport[0].Size.h = aSize.height * aLeftEyeRect.height;
  layer.Viewport[1].Pos.x = aSize.width * aRightEyeRect.x;
  layer.Viewport[1].Pos.y = aSize.height * aRightEyeRect.y;
  layer.Viewport[1].Size.w = aSize.width * aRightEyeRect.width;
  layer.Viewport[1].Size.h = aSize.height * aRightEyeRect.height;

  const Point3D& l = mDisplayInfo.mEyeTranslation[0];
  const Point3D& r = mDisplayInfo.mEyeTranslation[1];
  const ovrVector3f hmdToEyeViewOffset[2] = { { l.x, l.y, l.z },
                                              { r.x, r.y, r.z } };

  for (uint32_t i = 0; i < 2; ++i) {
    Quaternion o(aSensorState.orientation[0],
      aSensorState.orientation[1],
      aSensorState.orientation[2],
      aSensorState.orientation[3]);
    Point3D vo(hmdToEyeViewOffset[i].x, hmdToEyeViewOffset[i].y, hmdToEyeViewOffset[i].z);
    Point3D p = o.RotatePoint(vo);
    layer.RenderPose[i].Orientation.x = o.x;
    layer.RenderPose[i].Orientation.y = o.y;
    layer.RenderPose[i].Orientation.z = o.z;
    layer.RenderPose[i].Orientation.w = o.w;
    layer.RenderPose[i].Position.x = p.x + aSensorState.position[0];
    layer.RenderPose[i].Position.y = p.y + aSensorState.position[1];
    layer.RenderPose[i].Position.z = p.z + aSensorState.position[2];
  }

  ovrLayerHeader *layers = &layer.Header;
  orv = ovr_SubmitFrame(mSession, aSensorState.inputFrameID, nullptr, &layers, 1);

  if (orv != ovrSuccess) {
    printf_stderr("ovr_SubmitFrame failed.\n");
  }

  // Trigger the next VSync immediately
  VRManager *vm = VRManager::Get();
  MOZ_ASSERT(vm);
  vm->NotifyVRVsync(mDisplayInfo.mDisplayID);
}
Example #5
0
void
VRDisplayHost::NotifyVSync()
{
  /**
   * We will trigger a new frame immediately after a successful frame texture
   * submission.  If content fails to call VRDisplay.submitFrame after
   * kVRDisplayRAFMaxDuration milliseconds has elapsed since the last
   * VRDisplay.requestAnimationFrame, we act as a "watchdog" and kick-off
   * a new VRDisplay.requestAnimationFrame to avoid a render loop stall and
   * to give content a chance to recover.
   *
   * If the lower level VR platform API's are rejecting submitted frames,
   * such as when the Oculus "Health and Safety Warning" is displayed,
   * we will not kick off the next frame immediately after VRDisplay.submitFrame
   * as it would result in an unthrottled render loop that would free run at
   * potentially extreme frame rates.  To ensure that content has a chance to
   * resume its presentation when the frames are accepted once again, we rely
   * on this "watchdog" to act as a VR refresh driver cycling at a rate defined
   * by kVRDisplayRAFMaxDuration.
   *
   * kVRDisplayRAFMaxDuration is the number of milliseconds since last frame
   * start before triggering a new frame.  When content is failing to submit
   * frames on time or the lower level VR platform API's are rejecting frames,
   * kVRDisplayRAFMaxDuration determines the rate at which RAF callbacks
   * will be called.
   *
   * This number must be larger than the slowest expected frame time during
   * normal VR presentation, but small enough not to break content that
   * makes assumptions of reasonably minimal VSync rate.
   *
   * The slowest expected refresh rate for a VR display currently is an
   * Oculus CV1 when ASW (Asynchronous Space Warp) is enabled, at 45hz.
   * A kVRDisplayRAFMaxDuration value of 50 milliseconds results in a 20hz
   * rate, which avoids inadvertent triggering of the watchdog during
   * Oculus ASW even if every second frame is dropped.
   */
  const double kVRDisplayRAFMaxDuration = 50;

  bool bShouldStartFrame = false;

  if (mDisplayInfo.mPresentingGroups == 0) {
    // If this display isn't presenting, refresh the sensors and trigger
    // VRDisplay.requestAnimationFrame at the normal 2d display refresh rate.
    bShouldStartFrame = true;
  } else {
    // If content fails to call VRDisplay.submitFrame, we must eventually
    // time-out and trigger a new frame.
    if (mLastFrameStart.IsNull()) {
      bShouldStartFrame = true;
    } else {
      TimeDuration duration = TimeStamp::Now() - mLastFrameStart;
      if (duration.ToMilliseconds() > kVRDisplayRAFMaxDuration) {
        bShouldStartFrame = true;
      }
    }
  }

  if (bShouldStartFrame) {
    VRManager *vm = VRManager::Get();
    MOZ_ASSERT(vm);
    vm->NotifyVRVsync(mDisplayInfo.mDisplayID);
  }
}