Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 : * This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #include "VRDisplayHost.h"
7 : #include "gfxVR.h"
8 : #include "ipc/VRLayerParent.h"
9 :
10 : #if defined(XP_WIN)
11 :
12 : #include <d3d11.h>
13 : #include "gfxWindowsPlatform.h"
14 : #include "../layers/d3d11/CompositorD3D11.h"
15 : #include "mozilla/layers/TextureD3D11.h"
16 :
17 : #endif
18 :
19 : using namespace mozilla;
20 : using namespace mozilla::gfx;
21 : using namespace mozilla::layers;
22 :
23 0 : VRDisplayHost::VRDisplayHost(VRDeviceType aType)
24 : {
25 0 : MOZ_COUNT_CTOR(VRDisplayHost);
26 0 : mDisplayInfo.mType = aType;
27 0 : mDisplayInfo.mDisplayID = VRSystemManager::AllocateDisplayID();
28 0 : mDisplayInfo.mPresentingGroups = 0;
29 0 : mDisplayInfo.mGroupMask = kVRGroupContent;
30 0 : mDisplayInfo.mFrameId = 0;
31 0 : }
32 :
33 0 : VRDisplayHost::~VRDisplayHost()
34 : {
35 0 : MOZ_COUNT_DTOR(VRDisplayHost);
36 0 : }
37 :
38 : void
39 0 : VRDisplayHost::SetGroupMask(uint32_t aGroupMask)
40 : {
41 0 : mDisplayInfo.mGroupMask = aGroupMask;
42 0 : }
43 :
44 : bool
45 0 : VRDisplayHost::GetIsConnected()
46 : {
47 0 : return mDisplayInfo.mIsConnected;
48 : }
49 :
50 : void
51 0 : VRDisplayHost::AddLayer(VRLayerParent *aLayer)
52 : {
53 0 : mLayers.AppendElement(aLayer);
54 0 : mDisplayInfo.mPresentingGroups |= aLayer->GetGroup();
55 0 : if (mLayers.Length() == 1) {
56 0 : StartPresentation();
57 : }
58 :
59 : // Ensure that the content process receives the change immediately
60 0 : VRManager* vm = VRManager::Get();
61 0 : vm->RefreshVRDisplays();
62 0 : }
63 :
64 : void
65 0 : VRDisplayHost::RemoveLayer(VRLayerParent *aLayer)
66 : {
67 0 : mLayers.RemoveElement(aLayer);
68 0 : if (mLayers.Length() == 0) {
69 0 : StopPresentation();
70 : }
71 0 : mDisplayInfo.mPresentingGroups = 0;
72 0 : for (auto layer : mLayers) {
73 0 : mDisplayInfo.mPresentingGroups |= layer->GetGroup();
74 : }
75 :
76 : // Ensure that the content process receives the change immediately
77 0 : VRManager* vm = VRManager::Get();
78 0 : vm->RefreshVRDisplays();
79 0 : }
80 :
81 : void
82 0 : VRDisplayHost::StartFrame()
83 : {
84 0 : mLastFrameStart = TimeStamp::Now();
85 0 : ++mDisplayInfo.mFrameId;
86 0 : mDisplayInfo.mLastSensorState[mDisplayInfo.mFrameId % kVRMaxLatencyFrames] = GetSensorState();
87 0 : }
88 :
89 : void
90 0 : VRDisplayHost::NotifyVSync()
91 : {
92 : /**
93 : * We will trigger a new frame immediately after a successful frame texture
94 : * submission. If content fails to call VRDisplay.submitFrame after
95 : * kVRDisplayRAFMaxDuration milliseconds has elapsed since the last
96 : * VRDisplay.requestAnimationFrame, we act as a "watchdog" and kick-off
97 : * a new VRDisplay.requestAnimationFrame to avoid a render loop stall and
98 : * to give content a chance to recover.
99 : *
100 : * If the lower level VR platform API's are rejecting submitted frames,
101 : * such as when the Oculus "Health and Safety Warning" is displayed,
102 : * we will not kick off the next frame immediately after VRDisplay.submitFrame
103 : * as it would result in an unthrottled render loop that would free run at
104 : * potentially extreme frame rates. To ensure that content has a chance to
105 : * resume its presentation when the frames are accepted once again, we rely
106 : * on this "watchdog" to act as a VR refresh driver cycling at a rate defined
107 : * by kVRDisplayRAFMaxDuration.
108 : *
109 : * kVRDisplayRAFMaxDuration is the number of milliseconds since last frame
110 : * start before triggering a new frame. When content is failing to submit
111 : * frames on time or the lower level VR platform API's are rejecting frames,
112 : * kVRDisplayRAFMaxDuration determines the rate at which RAF callbacks
113 : * will be called.
114 : *
115 : * This number must be larger than the slowest expected frame time during
116 : * normal VR presentation, but small enough not to break content that
117 : * makes assumptions of reasonably minimal VSync rate.
118 : *
119 : * The slowest expected refresh rate for a VR display currently is an
120 : * Oculus CV1 when ASW (Asynchronous Space Warp) is enabled, at 45hz.
121 : * A kVRDisplayRAFMaxDuration value of 50 milliseconds results in a 20hz
122 : * rate, which avoids inadvertent triggering of the watchdog during
123 : * Oculus ASW even if every second frame is dropped.
124 : */
125 0 : const double kVRDisplayRAFMaxDuration = 50;
126 :
127 0 : bool bShouldStartFrame = false;
128 :
129 0 : if (mDisplayInfo.mPresentingGroups == 0) {
130 : // If this display isn't presenting, refresh the sensors and trigger
131 : // VRDisplay.requestAnimationFrame at the normal 2d display refresh rate.
132 0 : bShouldStartFrame = true;
133 : } else {
134 : // If content fails to call VRDisplay.submitFrame, we must eventually
135 : // time-out and trigger a new frame.
136 0 : if (mLastFrameStart.IsNull()) {
137 0 : bShouldStartFrame = true;
138 : } else {
139 0 : TimeDuration duration = TimeStamp::Now() - mLastFrameStart;
140 0 : if (duration.ToMilliseconds() > kVRDisplayRAFMaxDuration) {
141 0 : bShouldStartFrame = true;
142 : }
143 : }
144 : }
145 :
146 0 : if (bShouldStartFrame) {
147 0 : VRManager *vm = VRManager::Get();
148 0 : MOZ_ASSERT(vm);
149 0 : vm->NotifyVRVsync(mDisplayInfo.mDisplayID);
150 : }
151 0 : }
152 :
153 : #if defined(XP_WIN)
154 :
155 : void
156 : VRDisplayHost::SubmitFrame(VRLayerParent* aLayer, PTextureParent* aTexture,
157 : const gfx::Rect& aLeftEyeRect,
158 : const gfx::Rect& aRightEyeRect)
159 : {
160 : if ((mDisplayInfo.mGroupMask & aLayer->GetGroup()) == 0) {
161 : // Suppress layers hidden by the group mask
162 : return;
163 : }
164 :
165 : TextureHost* th = TextureHost::AsTextureHost(aTexture);
166 : // WebVR doesn't use the compositor to compose the frame, so use
167 : // AutoLockTextureHostWithoutCompositor here.
168 : AutoLockTextureHostWithoutCompositor autoLock(th);
169 : if (autoLock.Failed()) {
170 : NS_WARNING("Failed to lock the VR layer texture");
171 : return;
172 : }
173 :
174 : CompositableTextureSourceRef source;
175 : if (!th->BindTextureSource(source)) {
176 : NS_WARNING("The TextureHost was successfully locked but can't provide a TextureSource");
177 : return;
178 : }
179 : MOZ_ASSERT(source);
180 :
181 : IntSize texSize = source->GetSize();
182 :
183 : TextureSourceD3D11* sourceD3D11 = source->AsSourceD3D11();
184 : if (!sourceD3D11) {
185 : NS_WARNING("WebVR support currently only implemented for D3D11");
186 : return;
187 : }
188 :
189 : if (!SubmitFrame(sourceD3D11, texSize, aLeftEyeRect, aRightEyeRect)) {
190 : return;
191 : }
192 :
193 : /**
194 : * Trigger the next VSync immediately after we are successfully
195 : * submitting frames. As SubmitFrame is responsible for throttling
196 : * the render loop, if we don't successfully call it, we shouldn't trigger
197 : * NotifyVRVsync immediately, as it will run unbounded.
198 : * If NotifyVRVsync is not called here due to SubmitFrame failing, the
199 : * fallback "watchdog" code in VRDisplayHost::NotifyVSync() will cause
200 : * frames to continue at a lower refresh rate until frame submission
201 : * succeeds again.
202 : */
203 : VRManager *vm = VRManager::Get();
204 : MOZ_ASSERT(vm);
205 : vm->NotifyVRVsync(mDisplayInfo.mDisplayID);
206 : }
207 :
208 : #else
209 :
210 : void
211 0 : VRDisplayHost::SubmitFrame(VRLayerParent* aLayer, PTextureParent* aTexture,
212 : const gfx::Rect& aLeftEyeRect,
213 : const gfx::Rect& aRightEyeRect)
214 : {
215 0 : NS_WARNING("WebVR only supported in Windows.");
216 0 : }
217 :
218 : #endif
219 :
220 : bool
221 0 : VRDisplayHost::CheckClearDisplayInfoDirty()
222 : {
223 0 : if (mDisplayInfo == mLastUpdateDisplayInfo) {
224 0 : return false;
225 : }
226 0 : mLastUpdateDisplayInfo = mDisplayInfo;
227 0 : return true;
228 : }
229 :
230 0 : VRControllerHost::VRControllerHost(VRDeviceType aType)
231 0 : : mVibrateIndex(0)
232 : {
233 0 : MOZ_COUNT_CTOR(VRControllerHost);
234 0 : mControllerInfo.mType = aType;
235 0 : mControllerInfo.mControllerID = VRSystemManager::AllocateDisplayID();
236 0 : }
237 :
238 0 : VRControllerHost::~VRControllerHost()
239 : {
240 0 : MOZ_COUNT_DTOR(VRControllerHost);
241 0 : }
242 :
243 : const VRControllerInfo&
244 0 : VRControllerHost::GetControllerInfo() const
245 : {
246 0 : return mControllerInfo;
247 : }
248 :
249 : void
250 0 : VRControllerHost::SetButtonPressed(uint64_t aBit)
251 : {
252 0 : mButtonPressed = aBit;
253 0 : }
254 :
255 : uint64_t
256 0 : VRControllerHost::GetButtonPressed()
257 : {
258 0 : return mButtonPressed;
259 : }
260 :
261 : void
262 0 : VRControllerHost::SetButtonTouched(uint64_t aBit)
263 : {
264 0 : mButtonTouched = aBit;
265 0 : }
266 :
267 : uint64_t
268 0 : VRControllerHost::GetButtonTouched()
269 : {
270 0 : return mButtonTouched;
271 : }
272 :
273 : void
274 0 : VRControllerHost::SetPose(const dom::GamepadPoseState& aPose)
275 : {
276 0 : mPose = aPose;
277 0 : }
278 :
279 : const dom::GamepadPoseState&
280 0 : VRControllerHost::GetPose()
281 : {
282 0 : return mPose;
283 : }
284 :
285 : dom::GamepadHand
286 0 : VRControllerHost::GetHand()
287 : {
288 0 : return mControllerInfo.mHand;
289 : }
290 :
291 : void
292 0 : VRControllerHost::SetVibrateIndex(uint64_t aIndex)
293 : {
294 0 : mVibrateIndex = aIndex;
295 0 : }
296 :
297 : uint64_t
298 0 : VRControllerHost::GetVibrateIndex()
299 : {
300 0 : return mVibrateIndex;
301 : }
|