Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/gfx/vr/VRDisplayHost.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "VRDisplayHost.h"
8
#include "gfxPrefs.h"
9
#include "gfxVR.h"
10
#include "ipc/VRLayerParent.h"
11
#include "mozilla/layers/TextureHost.h"
12
#include "mozilla/dom/GamepadBinding.h" // For GamepadMappingType
13
#include "VRThread.h"
14
15
#if defined(XP_WIN)
16
17
#include <d3d11.h>
18
#include "gfxWindowsPlatform.h"
19
#include "../layers/d3d11/CompositorD3D11.h"
20
#include "mozilla/gfx/DeviceManagerDx.h"
21
#include "mozilla/layers/TextureD3D11.h"
22
23
#elif defined(XP_MACOSX)
24
25
#include "mozilla/gfx/MacIOSurface.h"
26
27
#endif
28
29
#if defined(MOZ_WIDGET_ANDROID)
30
#include "mozilla/layers/CompositorThread.h"
31
// Max frame duration on Android before the watchdog submits a new one.
32
// Probably we can get rid of this when we enforce that SubmitFrame can only be called in a VRDisplay loop.
33
#define ANDROID_MAX_FRAME_DURATION 4000
34
#endif // defined(MOZ_WIDGET_ANDROID)
35
36
37
using namespace mozilla;
38
using namespace mozilla::gfx;
39
using namespace mozilla::layers;
40
41
VRDisplayHost::AutoRestoreRenderState::AutoRestoreRenderState(VRDisplayHost* aDisplay)
42
  : mDisplay(aDisplay)
43
  , mSuccess(true)
44
0
{
45
#if defined(XP_WIN)
46
  ID3D11DeviceContext1* context = mDisplay->GetD3DDeviceContext();
47
  ID3DDeviceContextState* state = mDisplay->GetD3DDeviceContextState();
48
  if (!context || !state) {
49
    mSuccess = false;
50
    return;
51
  }
52
  context->SwapDeviceContextState(state, getter_AddRefs(mPrevDeviceContextState));
53
#endif
54
}
55
56
VRDisplayHost::AutoRestoreRenderState::~AutoRestoreRenderState()
57
0
{
58
#if defined(XP_WIN)
59
  ID3D11DeviceContext1* context = mDisplay->GetD3DDeviceContext();
60
  if (context && mSuccess) {
61
    context->SwapDeviceContextState(mPrevDeviceContextState, nullptr);
62
  }
63
#endif
64
}
65
66
bool
67
VRDisplayHost::AutoRestoreRenderState::IsSuccess()
68
0
{
69
0
  return mSuccess;
70
0
}
71
72
VRDisplayHost::VRDisplayHost(VRDeviceType aType)
73
 : mDisplayInfo{}
74
 , mLastUpdateDisplayInfo{}
75
 , mFrameStarted(false)
76
0
{
77
0
  MOZ_COUNT_CTOR(VRDisplayHost);
78
0
  mDisplayInfo.mType = aType;
79
0
  mDisplayInfo.mDisplayID = VRSystemManager::AllocateDisplayID();
80
0
  mDisplayInfo.mPresentingGroups = 0;
81
0
  mDisplayInfo.mGroupMask = kVRGroupContent;
82
0
  mDisplayInfo.mFrameId = 0;
83
0
  mDisplayInfo.mDisplayState.mPresentingGeneration = 0;
84
0
  mDisplayInfo.mDisplayState.mDisplayName[0] = '\0';
85
0
86
#if defined(MOZ_WIDGET_ANDROID)
87
  mLastSubmittedFrameId = 0;
88
  mLastStartedFrame = 0;
89
#endif // defined(MOZ_WIDGET_ANDROID)
90
}
91
92
VRDisplayHost::~VRDisplayHost()
93
0
{
94
0
  if (mSubmitThread) {
95
0
    mSubmitThread->Shutdown();
96
0
    mSubmitThread = nullptr;
97
0
  }
98
0
  MOZ_COUNT_DTOR(VRDisplayHost);
99
0
}
100
101
#if defined(XP_WIN)
102
bool
103
VRDisplayHost::CreateD3DObjects()
104
{
105
  if (!mDevice) {
106
    RefPtr<ID3D11Device> device = gfx::DeviceManagerDx::Get()->GetVRDevice();
107
    if (!device) {
108
      NS_WARNING("VRDisplayHost::CreateD3DObjects failed to get a D3D11Device");
109
      return false;
110
    }
111
    if (FAILED(device->QueryInterface(__uuidof(ID3D11Device1), getter_AddRefs(mDevice)))) {
112
      NS_WARNING("VRDisplayHost::CreateD3DObjects failed to get a D3D11Device1");
113
      return false;
114
    }
115
  }
116
  if (!mContext) {
117
    mDevice->GetImmediateContext1(getter_AddRefs(mContext));
118
    if (!mContext) {
119
      NS_WARNING("VRDisplayHost::CreateD3DObjects failed to get an immediate context");
120
      return false;
121
    }
122
  }
123
  if (!mDeviceContextState) {
124
    D3D_FEATURE_LEVEL featureLevels[] {
125
      D3D_FEATURE_LEVEL_11_1,
126
      D3D_FEATURE_LEVEL_11_0
127
    };
128
    mDevice->CreateDeviceContextState(0,
129
                                      featureLevels,
130
                                      2,
131
                                      D3D11_SDK_VERSION,
132
                                      __uuidof(ID3D11Device1),
133
                                      nullptr,
134
                                      getter_AddRefs(mDeviceContextState));
135
  }
136
  if (!mDeviceContextState) {
137
    NS_WARNING("VRDisplayHost::CreateD3DObjects failed to get a D3D11DeviceContextState");
138
    return false;
139
  }
140
  return true;
141
}
142
143
ID3D11Device1*
144
VRDisplayHost::GetD3DDevice()
145
{
146
  return mDevice;
147
}
148
149
ID3D11DeviceContext1*
150
VRDisplayHost::GetD3DDeviceContext()
151
{
152
  return mContext;
153
}
154
155
ID3DDeviceContextState*
156
VRDisplayHost::GetD3DDeviceContextState()
157
{
158
  return mDeviceContextState;
159
}
160
161
#endif // defined(XP_WIN)
162
163
void
164
VRDisplayHost::SetGroupMask(uint32_t aGroupMask)
165
0
{
166
0
  mDisplayInfo.mGroupMask = aGroupMask;
167
0
}
168
169
bool
170
VRDisplayHost::GetIsConnected()
171
0
{
172
0
  return mDisplayInfo.mDisplayState.mIsConnected;
173
0
}
174
175
void
176
VRDisplayHost::AddLayer(VRLayerParent *aLayer)
177
0
{
178
0
  mLayers.AppendElement(aLayer);
179
0
  mDisplayInfo.mPresentingGroups |= aLayer->GetGroup();
180
0
  if (mLayers.Length() == 1) {
181
0
    StartPresentation();
182
0
  }
183
0
184
0
  // Ensure that the content process receives the change immediately
185
0
  VRManager* vm = VRManager::Get();
186
0
  vm->RefreshVRDisplays();
187
0
}
188
189
void
190
VRDisplayHost::RemoveLayer(VRLayerParent *aLayer)
191
0
{
192
0
  mLayers.RemoveElement(aLayer);
193
0
  if (mLayers.Length() == 0) {
194
0
    StopPresentation();
195
0
  }
196
0
  mDisplayInfo.mPresentingGroups = 0;
197
0
  for (auto layer : mLayers) {
198
0
    mDisplayInfo.mPresentingGroups |= layer->GetGroup();
199
0
  }
200
0
201
0
  // Ensure that the content process receives the change immediately
202
0
  VRManager* vm = VRManager::Get();
203
0
  vm->RefreshVRDisplays();
204
0
}
205
206
void
207
VRDisplayHost::StartFrame()
208
0
{
209
0
  AUTO_PROFILER_TRACING("VR", "GetSensorState");
210
0
211
#if defined(MOZ_WIDGET_ANDROID)
212
  const bool isPresenting = mLastUpdateDisplayInfo.GetPresentingGroups() != 0;
213
  double duration = mLastFrameStart.IsNull() ? 0.0 : (TimeStamp::Now() - mLastFrameStart).ToMilliseconds();
214
  /**
215
   * Do not start more VR frames until the last submitted frame is already processed.
216
   */
217
  if (isPresenting && mLastStartedFrame > 0 && mDisplayInfo.mDisplayState.mLastSubmittedFrameId < mLastStartedFrame && duration < (double)ANDROID_MAX_FRAME_DURATION) {
218
    return;
219
  }
220
#endif // !defined(MOZ_WIDGET_ANDROID)
221
222
0
  mLastFrameStart = TimeStamp::Now();
223
0
  ++mDisplayInfo.mFrameId;
224
0
  mDisplayInfo.mLastSensorState[mDisplayInfo.mFrameId % kVRMaxLatencyFrames] = GetSensorState();
225
0
  mFrameStarted = true;
226
#if defined(MOZ_WIDGET_ANDROID)
227
  mLastStartedFrame = mDisplayInfo.mFrameId;
228
#endif // !defined(MOZ_WIDGET_ANDROID)  
229
}
230
231
void
232
VRDisplayHost::NotifyVSync()
233
0
{
234
0
  /**
235
0
   * We will trigger a new frame immediately after a successful frame texture
236
0
   * submission.  If content fails to call VRDisplay.submitFrame after
237
0
   * dom.vr.display.rafMaxDuration milliseconds has elapsed since the last
238
0
   * VRDisplay.requestAnimationFrame, we act as a "watchdog" and kick-off
239
0
   * a new VRDisplay.requestAnimationFrame to avoid a render loop stall and
240
0
   * to give content a chance to recover.
241
0
   *
242
0
   * If the lower level VR platform API's are rejecting submitted frames,
243
0
   * such as when the Oculus "Health and Safety Warning" is displayed,
244
0
   * we will not kick off the next frame immediately after VRDisplay.submitFrame
245
0
   * as it would result in an unthrottled render loop that would free run at
246
0
   * potentially extreme frame rates.  To ensure that content has a chance to
247
0
   * resume its presentation when the frames are accepted once again, we rely
248
0
   * on this "watchdog" to act as a VR refresh driver cycling at a rate defined
249
0
   * by dom.vr.display.rafMaxDuration.
250
0
   *
251
0
   * This number must be larger than the slowest expected frame time during
252
0
   * normal VR presentation, but small enough not to break content that
253
0
   * makes assumptions of reasonably minimal VSync rate.
254
0
   *
255
0
   * The slowest expected refresh rate for a VR display currently is an
256
0
   * Oculus CV1 when ASW (Asynchronous Space Warp) is enabled, at 45hz.
257
0
   * A dom.vr.display.rafMaxDuration value of 50 milliseconds results in a 20hz
258
0
   * rate, which avoids inadvertent triggering of the watchdog during
259
0
   * Oculus ASW even if every second frame is dropped.
260
0
   */
261
0
  bool bShouldStartFrame = false;
262
0
263
0
  if (mDisplayInfo.mPresentingGroups == 0) {
264
0
    // If this display isn't presenting, refresh the sensors and trigger
265
0
    // VRDisplay.requestAnimationFrame at the normal 2d display refresh rate.
266
0
    bShouldStartFrame = true;
267
0
  } else {
268
0
    // If content fails to call VRDisplay.submitFrame, we must eventually
269
0
    // time-out and trigger a new frame.
270
0
    if (mLastFrameStart.IsNull()) {
271
0
      bShouldStartFrame = true;
272
0
    } else {
273
0
      TimeDuration duration = TimeStamp::Now() - mLastFrameStart;
274
0
      if (duration.ToMilliseconds() > gfxPrefs::VRDisplayRafMaxDuration()) {
275
0
        bShouldStartFrame = true;
276
0
      }
277
0
    }
278
0
  }
279
0
280
0
  if (bShouldStartFrame) {
281
0
    VRManager *vm = VRManager::Get();
282
0
    MOZ_ASSERT(vm);
283
0
    vm->NotifyVRVsync(mDisplayInfo.mDisplayID);
284
0
  }
285
0
}
286
287
void
288
VRDisplayHost::SubmitFrameInternal(const layers::SurfaceDescriptor &aTexture,
289
                                   uint64_t aFrameId,
290
                                   const gfx::Rect& aLeftEyeRect,
291
                                   const gfx::Rect& aRightEyeRect)
292
0
{
293
0
#if !defined(MOZ_WIDGET_ANDROID)
294
0
  MOZ_ASSERT(mSubmitThread->GetThread() == NS_GetCurrentThread());
295
0
#endif // !defined(MOZ_WIDGET_ANDROID)
296
0
  AUTO_PROFILER_TRACING("VR", "SubmitFrameAtVRDisplayHost");
297
0
298
0
  if (!SubmitFrame(aTexture, aFrameId, aLeftEyeRect, aRightEyeRect)) {
299
0
    return;
300
0
  }
301
0
302
#if defined(XP_WIN) || defined(XP_MACOSX) || defined(MOZ_WIDGET_ANDROID)
303
304
  /**
305
   * Trigger the next VSync immediately after we are successfully
306
   * submitting frames.  As SubmitFrame is responsible for throttling
307
   * the render loop, if we don't successfully call it, we shouldn't trigger
308
   * NotifyVRVsync immediately, as it will run unbounded.
309
   * If NotifyVRVsync is not called here due to SubmitFrame failing, the
310
   * fallback "watchdog" code in VRDisplayHost::NotifyVSync() will cause
311
   * frames to continue at a lower refresh rate until frame submission
312
   * succeeds again.
313
   */
314
  VRManager* vm = VRManager::Get();
315
  MessageLoop* loop = VRListenerThreadHolder::Loop();
316
317
  loop->PostTask(NewRunnableMethod<const uint32_t>(
318
    "gfx::VRManager::NotifyVRVsync",
319
    vm, &VRManager::NotifyVRVsync, mDisplayInfo.mDisplayID
320
  ));
321
#endif
322
}
323
324
void
325
VRDisplayHost::SubmitFrame(VRLayerParent* aLayer,
326
                           const layers::SurfaceDescriptor &aTexture,
327
                           uint64_t aFrameId,
328
                           const gfx::Rect& aLeftEyeRect,
329
                           const gfx::Rect& aRightEyeRect)
330
0
{
331
0
  if ((mDisplayInfo.mGroupMask & aLayer->GetGroup()) == 0) {
332
0
    // Suppress layers hidden by the group mask
333
0
    return;
334
0
  }
335
0
336
0
  // Ensure that we only accept the first SubmitFrame call per RAF cycle.
337
0
  if (!mFrameStarted || aFrameId != mDisplayInfo.mFrameId) {
338
0
    return;
339
0
  }
340
0
341
#if defined(MOZ_WIDGET_ANDROID)
342
  /**
343
   * Do not queue more submit frames until the last submitted frame is already processed 
344
   * and the new WebGL texture is ready.
345
   */
346
  if (mLastSubmittedFrameId > 0 && mLastSubmittedFrameId != mDisplayInfo.mDisplayState.mLastSubmittedFrameId) {
347
    mLastStartedFrame = 0;
348
    return;
349
  }
350
351
  mLastSubmittedFrameId = aFrameId;
352
#endif // !defined(MOZ_WIDGET_ANDROID)
353
354
0
  mFrameStarted = false;
355
0
356
0
  RefPtr<Runnable> submit =
357
0
    NewRunnableMethod<StoreCopyPassByConstLRef<layers::SurfaceDescriptor>, uint64_t,
358
0
      StoreCopyPassByConstLRef<gfx::Rect>, StoreCopyPassByConstLRef<gfx::Rect>>(
359
0
      "gfx::VRDisplayHost::SubmitFrameInternal", this, &VRDisplayHost::SubmitFrameInternal,
360
0
      aTexture, aFrameId, aLeftEyeRect, aRightEyeRect);
361
0
362
0
#if !defined(MOZ_WIDGET_ANDROID)
363
0
  if (!mSubmitThread) {
364
0
    mSubmitThread = new VRThread(NS_LITERAL_CSTRING("VR_SubmitFrame"));
365
0
  }
366
0
  mSubmitThread->Start();
367
0
  mSubmitThread->PostTask(submit.forget());
368
#else
369
  CompositorThreadHolder::Loop()->PostTask(submit.forget());
370
#endif // defined(MOZ_WIDGET_ANDROID)
371
}
372
373
bool
374
VRDisplayHost::CheckClearDisplayInfoDirty()
375
0
{
376
0
  if (mDisplayInfo == mLastUpdateDisplayInfo) {
377
0
    return false;
378
0
  }
379
0
  mLastUpdateDisplayInfo = mDisplayInfo;
380
0
  return true;
381
0
}
382
383
void
384
VRDisplayHost::StartVRNavigation()
385
0
{
386
0
  
387
0
}
388
389
void
390
VRDisplayHost::StopVRNavigation(const TimeDuration& aTimeout)
391
0
{
392
0
  
393
0
}
394
395
VRControllerHost::VRControllerHost(VRDeviceType aType, dom::GamepadHand aHand,
396
                                   uint32_t aDisplayID)
397
 : mControllerInfo{}
398
 , mVibrateIndex(0)
399
0
{
400
0
  MOZ_COUNT_CTOR(VRControllerHost);
401
0
  mControllerInfo.mType = aType;
402
0
  mControllerInfo.mControllerState.hand = aHand;
403
0
  mControllerInfo.mMappingType = dom::GamepadMappingType::_empty;
404
0
  mControllerInfo.mDisplayID = aDisplayID;
405
0
  mControllerInfo.mControllerID = VRSystemManager::AllocateControllerID();
406
0
}
407
408
VRControllerHost::~VRControllerHost()
409
0
{
410
0
  MOZ_COUNT_DTOR(VRControllerHost);
411
0
}
412
413
const VRControllerInfo&
414
VRControllerHost::GetControllerInfo() const
415
0
{
416
0
  return mControllerInfo;
417
0
}
418
419
void
420
VRControllerHost::SetButtonPressed(uint64_t aBit)
421
0
{
422
0
  mControllerInfo.mControllerState.buttonPressed = aBit;
423
0
}
424
425
uint64_t
426
VRControllerHost::GetButtonPressed()
427
0
{
428
0
  return mControllerInfo.mControllerState.buttonPressed;
429
0
}
430
431
void
432
VRControllerHost::SetButtonTouched(uint64_t aBit)
433
0
{
434
0
  mControllerInfo.mControllerState.buttonTouched = aBit;
435
0
}
436
437
uint64_t
438
VRControllerHost::GetButtonTouched()
439
0
{
440
0
  return mControllerInfo.mControllerState.buttonTouched;
441
0
}
442
443
void
444
VRControllerHost::SetPose(const dom::GamepadPoseState& aPose)
445
0
{
446
0
  mPose = aPose;
447
0
}
448
449
const dom::GamepadPoseState&
450
VRControllerHost::GetPose()
451
0
{
452
0
  return mPose;
453
0
}
454
455
dom::GamepadHand
456
VRControllerHost::GetHand()
457
0
{
458
0
  return mControllerInfo.mControllerState.hand;
459
0
}
460
461
void
462
VRControllerHost::SetVibrateIndex(uint64_t aIndex)
463
0
{
464
0
  mVibrateIndex = aIndex;
465
0
}
466
467
uint64_t
468
VRControllerHost::GetVibrateIndex()
469
0
{
470
0
  return mVibrateIndex;
471
0
}
472