Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(283)

Side by Side Diff: third_party/WebKit/Source/modules/vr/VRDisplay.cpp

Issue 2584343002: WIP: working copy-no-compositor path
Patch Set: StatTracker destructor, delete old magic numbers, mojo export Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "modules/vr/VRDisplay.h" 5 #include "modules/vr/VRDisplay.h"
6 6
7 #include "core/css/StylePropertySet.h" 7 #include "core/css/StylePropertySet.h"
8 #include "core/dom/DOMException.h" 8 #include "core/dom/DOMException.h"
9 #include "core/dom/DocumentUserGestureToken.h" 9 #include "core/dom/DocumentUserGestureToken.h"
10 #include "core/dom/FrameRequestCallback.h" 10 #include "core/dom/FrameRequestCallback.h"
11 #include "core/dom/Fullscreen.h"
12 #include "core/dom/ScriptedAnimationController.h" 11 #include "core/dom/ScriptedAnimationController.h"
12 #include "core/frame/FrameView.h"
13 #include "core/frame/UseCounter.h" 13 #include "core/frame/UseCounter.h"
14 #include "core/inspector/ConsoleMessage.h" 14 #include "core/inspector/ConsoleMessage.h"
15 #include "core/loader/DocumentLoader.h" 15 #include "core/loader/DocumentLoader.h"
16 #include "gpu/command_buffer/client/gles2_interface.h" 16 #include "gpu/command_buffer/client/gles2_interface.h"
17 #include "modules/EventTargetModules.h" 17 #include "modules/EventTargetModules.h"
18 #include "modules/vr/NavigatorVR.h" 18 #include "modules/vr/NavigatorVR.h"
19 #include "modules/vr/VRController.h" 19 #include "modules/vr/VRController.h"
20 #include "modules/vr/VRDisplayCapabilities.h" 20 #include "modules/vr/VRDisplayCapabilities.h"
21 #include "modules/vr/VREyeParameters.h" 21 #include "modules/vr/VREyeParameters.h"
22 #include "modules/vr/VRFrameData.h" 22 #include "modules/vr/VRFrameData.h"
23 #include "modules/vr/VRLayer.h" 23 #include "modules/vr/VRLayer.h"
24 #include "modules/vr/VRPose.h" 24 #include "modules/vr/VRPose.h"
25 #include "modules/vr/VRStageParameters.h" 25 #include "modules/vr/VRStageParameters.h"
26 #include "modules/webgl/WebGLRenderingContextBase.h" 26 #include "modules/webgl/WebGLRenderingContextBase.h"
27 #include "platform/Histogram.h" 27 #include "platform/Histogram.h"
28 #include "platform/UserGestureIndicator.h" 28 #include "platform/UserGestureIndicator.h"
29 #include "platform/instrumentation/tracing/TraceEvent.h"
29 #include "public/platform/Platform.h" 30 #include "public/platform/Platform.h"
30 #include "wtf/AutoReset.h" 31 #include "wtf/AutoReset.h"
31 32
32 #include <array> 33 #include <time.h>
33 34
34 namespace blink { 35 namespace blink {
35 36
36 namespace { 37 namespace {
37 38
38 // Magic numbers used to mark valid pose index values encoded in frame
39 // data. Must match the magic numbers used in vr_shell.cc.
40 static constexpr std::array<uint8_t, 2> kWebVrPosePixelMagicNumbers{{42, 142}};
41
42 VREye stringToVREye(const String& whichEye) { 39 VREye stringToVREye(const String& whichEye) {
43 if (whichEye == "left") 40 if (whichEye == "left")
44 return VREyeLeft; 41 return VREyeLeft;
45 if (whichEye == "right") 42 if (whichEye == "right")
46 return VREyeRight; 43 return VREyeRight;
47 return VREyeNone; 44 return VREyeNone;
48 } 45 }
49 46
50 class VRDisplayFrameRequestCallback : public FrameRequestCallback { 47 class VRDisplayFrameRequestCallback : public FrameRequestCallback {
51 public: 48 public:
52 VRDisplayFrameRequestCallback(VRDisplay* vrDisplay) : m_vrDisplay(vrDisplay) { 49 VRDisplayFrameRequestCallback(VRDisplay* vrDisplay) : m_vrDisplay(vrDisplay) {
53 m_useLegacyTimeBase = true; 50 m_useLegacyTimeBase = true;
54 } 51 }
55 ~VRDisplayFrameRequestCallback() override {} 52 ~VRDisplayFrameRequestCallback() override {}
56 void handleEvent(double highResTimeMs) override { 53 void handleEvent(double highResTimeMs) override {
57 Document* doc = m_vrDisplay->document(); 54 Document* doc = m_vrDisplay->document();
58 if (!doc) 55 if (!doc)
59 return; 56 return;
60 57
61 // Need to divide by 1000 here because serviceScriptedAnimations expects 58 m_vrDisplay->frameTick(highResTimeMs);
62 // time to be given in seconds.
63 m_vrDisplay->serviceScriptedAnimations(
64 doc->loader()->timing().pseudoWallTimeToMonotonicTime(highResTimeMs /
65 1000.0));
66 } 59 }
67 60
68 DEFINE_INLINE_VIRTUAL_TRACE() { 61 DEFINE_INLINE_VIRTUAL_TRACE() {
69 visitor->trace(m_vrDisplay); 62 visitor->trace(m_vrDisplay);
70 63
71 FrameRequestCallback::trace(visitor); 64 FrameRequestCallback::trace(visitor);
72 } 65 }
73 66
74 Member<VRDisplay> m_vrDisplay; 67 Member<VRDisplay> m_vrDisplay;
75 }; 68 };
76 69
77 } // namespace 70 } // namespace
78 71
72 StatTracker::StatTracker(unsigned int capacity) : m_capacity(capacity) {}
73
74 StatTracker::~StatTracker() = default;
75
76 void StatTracker::add(double item) {
77 if (m_items.size() >= m_capacity) {
78 m_items.pop_front();
79 }
80 m_items.push_back(item);
81 }
82
83 void StatTracker::clear() {
84 m_items.clear();
85 }
86
87 bool StatTracker::hasPrediction() {
88 return m_items.size() > 0;
89 }
90
91 double StatTracker::getPrediction() {
92 assert(hasPrediction());
93
94 // If we have 3 or more items, ignore min and max outliers and
95 // average the rest. For 2 or less, minmax.first and minmax.second
96 // will both be m_items.end(), so it's just a plain average.
97 auto minmax = m_items.size() > 2 ?
98 std::minmax_element(m_items.begin(), m_items.end()) :
99 std::minmax_element(m_items.end(), m_items.end());
100
101 double sum = 0.0;
102 int count = 0;
103 //VLOG(2) << __FUNCTION__ << ": stat start";
104 for (auto it = m_items.begin(); it != m_items.end(); ++it) {
105 //VLOG(2) << __FUNCTION__ << ": val=" << *it;
106 if (it == minmax.first || it == minmax.second) continue;
107 sum += *it;
108 ++count;
109 }
110 //VLOG(2) << __FUNCTION__ << ": stat return " << sum / count;
111 return sum / count;
112 }
113
114 static double getMonotonicTimestampMs() {
115 #if defined(OS_ANDROID)
116 // Android surfacetexture timestamp compatible timer? See:
117 // http://androidxref.com/7.0.0_r1/xref/frameworks/native/libs/gui/Surface.cp p#370
118 // http://androidxref.com/7.0.0_r1/xref/frameworks/rs/rsCppUtils.h#162
119 struct timespec t;
120 t.tv_sec = t.tv_nsec = 0;
121 clock_gettime(CLOCK_MONOTONIC, &t);
122 return t.tv_sec * 1e3 + t.tv_nsec * 1e-6;
123 #else
124 return 0.0;
125 #endif
126 }
127
79 VRDisplay::VRDisplay(NavigatorVR* navigatorVR, 128 VRDisplay::VRDisplay(NavigatorVR* navigatorVR,
80 device::mojom::blink::VRDisplayPtr display, 129 device::mojom::blink::VRDisplayPtr display,
81 device::mojom::blink::VRDisplayClientRequest request) 130 device::mojom::blink::VRDisplayClientRequest request)
82 : ContextLifecycleObserver(navigatorVR->document()), 131 : ContextLifecycleObserver(navigatorVR->document()),
83 m_navigatorVR(navigatorVR), 132 m_navigatorVR(navigatorVR),
84 m_isConnected(false), 133 m_isConnected(false),
85 m_isPresenting(false), 134 m_isPresenting(false),
86 m_isValidDeviceForPresenting(true), 135 m_isValidDeviceForPresenting(true),
136 m_framesPending(0),
87 m_canUpdateFramePose(true), 137 m_canUpdateFramePose(true),
138 m_canSubmitFramePose(false),
88 m_capabilities(new VRDisplayCapabilities()), 139 m_capabilities(new VRDisplayCapabilities()),
89 m_eyeParametersLeft(new VREyeParameters()), 140 m_eyeParametersLeft(new VREyeParameters()),
90 m_eyeParametersRight(new VREyeParameters()), 141 m_eyeParametersRight(new VREyeParameters()),
91 m_depthNear(0.01), 142 m_depthNear(0.01),
92 m_depthFar(10000.0), 143 m_depthFar(10000.0),
93 m_fullscreenCheckTimer(this, &VRDisplay::onFullscreenCheck),
94 m_contextGL(nullptr), 144 m_contextGL(nullptr),
95 m_animationCallbackRequested(false), 145 m_animationCallbackRequested(false),
96 m_inAnimationFrame(false), 146 m_inAnimationFrame(false),
147 m_surfaceHandle(0),
97 m_display(std::move(display)), 148 m_display(std::move(display)),
98 m_binding(this, std::move(request)) {} 149 m_binding(this, std::move(request)),
150 m_pose_client_binding(this) {
151 VLOG(1) << __FUNCTION__ << ": CONSTRUCTOR this=" << (void*)this << " m_display =" << m_display << " *********************************************************** ************************************************";
152 }
99 153
100 VRDisplay::~VRDisplay() {} 154 VRDisplay::~VRDisplay() {
155 VLOG(1) << __FUNCTION__ << ": DESTRUCTOR this=" << (void*)this << " m_display= " << m_display << " ************************************************************ ***********************************************";
156 }
101 157
102 VRController* VRDisplay::controller() { 158 VRController* VRDisplay::controller() {
103 return m_navigatorVR->controller(); 159 return m_navigatorVR->controller();
104 } 160 }
105 161
106 void VRDisplay::update(const device::mojom::blink::VRDisplayInfoPtr& display) { 162 void VRDisplay::update(const device::mojom::blink::VRDisplayInfoPtr& display) {
163 VLOG(1) << __FUNCTION__ << ": displayName=" << display->displayName;
107 m_displayId = display->index; 164 m_displayId = display->index;
108 m_displayName = display->displayName; 165 m_displayName = display->displayName;
109 m_isConnected = true; 166 m_isConnected = true;
110 167
111 m_capabilities->setHasOrientation(display->capabilities->hasOrientation); 168 m_capabilities->setHasOrientation(display->capabilities->hasOrientation);
112 m_capabilities->setHasPosition(display->capabilities->hasPosition); 169 m_capabilities->setHasPosition(display->capabilities->hasPosition);
113 m_capabilities->setHasExternalDisplay( 170 m_capabilities->setHasExternalDisplay(
114 display->capabilities->hasExternalDisplay); 171 display->capabilities->hasExternalDisplay);
115 m_capabilities->setCanPresent(display->capabilities->canPresent); 172 m_capabilities->setCanPresent(display->capabilities->canPresent);
116 m_capabilities->setMaxLayers(display->capabilities->canPresent ? 1 : 0); 173 m_capabilities->setMaxLayers(display->capabilities->canPresent ? 1 : 0);
117 174
118 // Ignore non presenting delegate 175 // Ignore non presenting delegate
119 bool isValid = display->leftEye->renderWidth > 0; 176 bool isValid = display->leftEye->renderWidth > 0;
120 bool needOnPresentChange = false; 177 bool needOnPresentChange = false;
178 VLOG(1) << __FUNCTION__ << ": m_isPresenting=" << m_isPresenting << " isValid= " << isValid << " m_isValidDeviceForPresenting=" << m_isValidDeviceForPresenting ;
121 if (m_isPresenting && isValid && !m_isValidDeviceForPresenting) { 179 if (m_isPresenting && isValid && !m_isValidDeviceForPresenting) {
122 needOnPresentChange = true; 180 needOnPresentChange = true;
123 } 181 }
124 m_isValidDeviceForPresenting = isValid; 182 m_isValidDeviceForPresenting = isValid;
125 m_eyeParametersLeft->update(display->leftEye); 183 m_eyeParametersLeft->update(display->leftEye);
126 m_eyeParametersRight->update(display->rightEye); 184 m_eyeParametersRight->update(display->rightEye);
127 185
128 if (!display->stageParameters.is_null()) { 186 if (!display->stageParameters.is_null()) {
129 if (!m_stageParameters) 187 if (!m_stageParameters)
130 m_stageParameters = new VRStageParameters(); 188 m_stageParameters = new VRStageParameters();
131 m_stageParameters->update(display->stageParameters); 189 m_stageParameters->update(display->stageParameters);
132 } else { 190 } else {
133 m_stageParameters = nullptr; 191 m_stageParameters = nullptr;
134 } 192 }
135 193
136 if (needOnPresentChange) { 194 if (needOnPresentChange) {
137 OnPresentChange(); 195 OnPresentChange();
138 } 196 }
197 VLOG(1) << __FUNCTION__ << ": done";
139 } 198 }
140 199
141 void VRDisplay::disconnected() { 200 void VRDisplay::disconnected() {
201 VLOG(1) << __FUNCTION__;
142 if (m_isConnected) 202 if (m_isConnected)
143 m_isConnected = !m_isConnected; 203 m_isConnected = !m_isConnected;
144 } 204 }
145 205
146 bool VRDisplay::getFrameData(VRFrameData* frameData) { 206 bool VRDisplay::getFrameData(VRFrameData* frameData) {
207 TRACE_EVENT1("media", "klausw:getFrameData", "frame", m_framePose ? m_framePos e->poseIndex + 1 : -1);
208 double get_pose_ms = getMonotonicTimestampMs();
147 updatePose(); 209 updatePose();
210 if (m_framePose) {
211 double got_pose_ms = getMonotonicTimestampMs();
212 // TODO(klausw): why do these show 0.1ms deltas while updateFrame shows 1ms+ ?
213 if (m_framePose->ts_getPose == 0.0) {
214 m_framePose->ts_getPose = get_pose_ms;
215 m_framePose->ts_gotPose = got_pose_ms;
216 }
217 }
148 218
149 if (!m_framePose) 219 if (!m_framePose)
150 return false; 220 return false;
151 221
152 if (!frameData) 222 if (!frameData)
153 return false; 223 return false;
154 224
155 if (m_depthNear == m_depthFar) 225 if (m_depthNear == m_depthFar)
156 return false; 226 return false;
157 227
158 return frameData->update(m_framePose, m_eyeParametersLeft, 228 bool ret = frameData->update(m_framePose, m_eyeParametersLeft,
159 m_eyeParametersRight, m_depthNear, m_depthFar); 229 m_eyeParametersRight, m_depthNear, m_depthFar);
230 VLOG_IF(2, m_framePose) << __FUNCTION__ << ": frame " << m_framePose->poseInde x << ", recommended eye renderWidth/Height=" << m_eyeParametersLeft->renderWidth () << "x" << m_eyeParametersLeft->renderHeight();
231
232 return ret;
160 } 233 }
161 234
162 VRPose* VRDisplay::getPose() { 235 VRPose* VRDisplay::getPose() {
236 TRACE_EVENT1("media", "klausw:getPose", "next frame ", m_framePose ? m_framePo se->poseIndex + 1 : -1);
237 double get_pose_ms = getMonotonicTimestampMs();
163 updatePose(); 238 updatePose();
239 double got_pose_ms = getMonotonicTimestampMs();
240 if (!m_framePose) {
241 VLOG(2) << __FUNCTION__ << ": no pose for next frame ";
242 return nullptr;
243 }
164 244
165 if (!m_framePose) 245 if (m_framePose->ts_getPose == 0.0) {
166 return nullptr; 246 m_framePose->ts_getPose = get_pose_ms;
247 m_framePose->ts_gotPose = got_pose_ms;
248 }
167 249
168 VRPose* pose = VRPose::create(); 250 VRPose* pose = VRPose::create();
169 pose->setPose(m_framePose); 251 pose->setPose(m_framePose);
252 VLOG(2) << __FUNCTION__ << ": next frame " << m_framePose->poseIndex;
170 return pose; 253 return pose;
171 } 254 }
172 255
256 void VRDisplay::requestPose() {
257 VLOG(2) << __FUNCTION__;
258 m_poseCallbackPending = true;
259 m_display->GetPose(m_pose_client_binding.CreateInterfacePtrAndBind());
260 }
261
262 void VRDisplay::OnPoseReceived(device::mojom::blink::VRPosePtr pose) {
263 VLOG(2) << __FUNCTION__;
264 m_framePose = std::move(pose);
265 m_poseCallbackPending = false;
266 }
267
173 void VRDisplay::updatePose() { 268 void VRDisplay::updatePose() {
269 VLOG(2) << __FUNCTION__ << ": display=" << m_display << " canUpdateFramePose=" << m_canUpdateFramePose;
174 if (m_displayBlurred) { 270 if (m_displayBlurred) {
175 // WebVR spec says to return a null pose when the display is blurred. 271 // WebVR spec says to return a null pose when the display is blurred.
176 m_framePose = nullptr; 272 m_framePose = nullptr;
177 return; 273 return;
178 } 274 }
179 if (m_canUpdateFramePose) { 275 // If a pose was prefetched and is complete, do nothing.
276 // If prefetch is still pending, wait for it to complete.
277 // If no prefetch was started, start it and wait for result.
278 if (m_canUpdateFramePose || m_poseCallbackPending) {
180 if (!m_display) 279 if (!m_display)
181 return; 280 return;
182 device::mojom::blink::VRPosePtr pose; 281 device::mojom::blink::VRPosePtr pose;
183 m_display->GetPose(&pose); 282 TRACE_EVENT1("media", "klausw:updatePose", "frame", m_framePose ? m_framePos e->poseIndex + 1 : -1);
184 m_framePose = std::move(pose); 283 double get_pose_ms = getMonotonicTimestampMs();
185 if (m_isPresenting) 284 if (!m_poseCallbackPending) {
285 requestPose();
286 }
287 while (m_poseCallbackPending) {
288 {VLOG(2) << __FUNCTION__ << ": wait for pose";}
289 if (!m_pose_client_binding.WaitForIncomingMethodCall()) {
290 LOG(ERROR) << __FUNCTION__ << ": failed to receive a pose";
291 }
292 }
293 if (!m_framePose) {
294 VLOG(2) << __FUNCTION__ << ": did not get a pose";
295 return;
296 }
297 double got_pose_ms = getMonotonicTimestampMs();
298 m_canSubmitFramePose = true;
299 VLOG(2) << __FUNCTION__ << ": updatePose to " << (m_framePose ? m_framePose- >poseIndex : -1) << " took " << got_pose_ms - get_pose_ms << "ms";
300 // For this newly fetched frame, zero the timers and let getPose
301 // or getFrameData update it. This way we can ensure that we
302 // measure the actual JS pause time one time, and don't overwrite
303 // it for redundant calls. If we prefetch the pose, the underlying
304 // fetch time wouldn't matter.
305 m_framePose->ts_getPose = 0.0;
306 m_framePose->ts_gotPose = 0.0;
307 if (m_isPresenting) {
186 m_canUpdateFramePose = false; 308 m_canUpdateFramePose = false;
309 }
187 } 310 }
188 } 311 }
189 312
190 void VRDisplay::resetPose() { 313 void VRDisplay::resetPose() {
191 if (!m_display) 314 if (!m_display)
192 return; 315 return;
193 316
194 m_display->ResetPose(); 317 m_display->ResetPose();
195 } 318 }
196 319
197 VREyeParameters* VRDisplay::getEyeParameters(const String& whichEye) { 320 VREyeParameters* VRDisplay::getEyeParameters(const String& whichEye) {
198 switch (stringToVREye(whichEye)) { 321 switch (stringToVREye(whichEye)) {
199 case VREyeLeft: 322 case VREyeLeft:
323 VLOG(1) << __FUNCTION__ << ": left renderWidth/Height=" << m_eyeParameters Left->renderWidth() << "x" << m_eyeParametersLeft->renderHeight();
200 return m_eyeParametersLeft; 324 return m_eyeParametersLeft;
201 case VREyeRight: 325 case VREyeRight:
326 VLOG(1) << __FUNCTION__ << ": right renderWidth/Height=" << m_eyeParameter sRight->renderWidth() << "x" << m_eyeParametersRight->renderHeight();
202 return m_eyeParametersRight; 327 return m_eyeParametersRight;
203 default: 328 default:
204 return nullptr; 329 return nullptr;
205 } 330 }
206 } 331 }
207 332
208 int VRDisplay::requestAnimationFrame(FrameRequestCallback* callback) { 333 int VRDisplay::requestAnimationFrame(FrameRequestCallback* callback) {
334 TRACE_EVENT1("media", "klausw:rAF", "frame", m_framePose ? m_framePose->poseIn dex : -1);
209 Document* doc = this->document(); 335 Document* doc = this->document();
210 if (!doc) 336 if (!doc)
211 return 0; 337 return 0;
212 338
213 if (!m_animationCallbackRequested) { 339 if (!m_animationCallbackRequested) {
214 doc->requestAnimationFrame(new VRDisplayFrameRequestCallback(this)); 340 doc->requestAnimationFrame(new VRDisplayFrameRequestCallback(this));
215 m_animationCallbackRequested = true; 341 m_animationCallbackRequested = true;
216 } 342 }
217 343
218 callback->m_useLegacyTimeBase = false; 344 callback->m_useLegacyTimeBase = false;
219 return ensureScriptedAnimationController(doc).registerCallback(callback); 345 return ensureScriptedAnimationController(doc).registerCallback(callback);
220 } 346 }
221 347
222 void VRDisplay::cancelAnimationFrame(int id) { 348 void VRDisplay::cancelAnimationFrame(int id) {
223 if (!m_scriptedAnimationController) 349 if (!m_scriptedAnimationController)
224 return; 350 return;
225 m_scriptedAnimationController->cancelCallback(id); 351 m_scriptedAnimationController->cancelCallback(id);
226 } 352 }
227 353
228 void VRDisplay::OnBlur() { 354 void VRDisplay::OnBlur() {
355 VLOG(1) << __FUNCTION__;
229 m_displayBlurred = true; 356 m_displayBlurred = true;
230 357
231 m_navigatorVR->enqueueVREvent(VRDisplayEvent::create( 358 m_navigatorVR->enqueueVREvent(VRDisplayEvent::create(
232 EventTypeNames::vrdisplayblur, true, false, this, "")); 359 EventTypeNames::vrdisplayblur, true, false, this, ""));
233 } 360 }
234 361
235 void VRDisplay::OnFocus() { 362 void VRDisplay::OnFocus() {
363 VLOG(1) << __FUNCTION__;
236 m_displayBlurred = false; 364 m_displayBlurred = false;
237 // Restart our internal doc requestAnimationFrame callback, if it fired while 365 // Restart our internal doc requestAnimationFrame callback, if it fired while
238 // the display was blurred. 366 // the display was blurred.
239 // TODO(bajones): Don't use doc->requestAnimationFrame() at all. Animation 367 // TODO(bajones): Don't use doc->requestAnimationFrame() at all. Animation
240 // frames should be tied to the presenting VR display (e.g. should be serviced 368 // frames should be tied to the presenting VR display (e.g. should be serviced
241 // by GVR library callbacks on Android), and not the doc frame rate. 369 // by GVR library callbacks on Android), and not the doc frame rate.
242 if (!m_animationCallbackRequested) { 370 if (!m_animationCallbackRequested) {
243 Document* doc = this->document(); 371 Document* doc = this->document();
244 if (!doc) 372 if (!doc)
245 return; 373 return;
246 doc->requestAnimationFrame(new VRDisplayFrameRequestCallback(this)); 374 doc->requestAnimationFrame(new VRDisplayFrameRequestCallback(this));
247 } 375 }
248 m_navigatorVR->enqueueVREvent(VRDisplayEvent::create( 376 m_navigatorVR->enqueueVREvent(VRDisplayEvent::create(
249 EventTypeNames::vrdisplayfocus, true, false, this, "")); 377 EventTypeNames::vrdisplayfocus, true, false, this, ""));
250 } 378 }
251 379
252 void VRDisplay::serviceScriptedAnimations(double monotonicAnimationStartTime) { 380 #if 0
381 static void delayTest() {
382 VLOG(2) << __FUNCTION__ << ": delay got executed";
383 }
384 #endif
385
386
387 // TODO(klausw): refactor:
388 // replace serviceScriptedAnimations with new callback chain sequence:
389 // | tick getPose service_rAF | tick
390 // | 0ms 4ms 8ms 16.7ms| 0ms
391 //
392 // // Save tickStart at VRDisplayFrameRequestCallback.handleEvent?
393 // timerTick
394 // tickStart=now()
395 // postDelayedTask(getPoseForFrame, poseOffset)
396 // getPoseForFrame
397 // getPose // TODO: getPoseAsync(gotPoseCallback), then next step in gotPo seCallback
398 // postDelayedTask(service_rAF_offset - (now() - tickStart))
399
400
401 void VRDisplay::frameTick(double highResTimeMs) {
402 m_tickIsScheduled = false;
253 if (!m_scriptedAnimationController) 403 if (!m_scriptedAnimationController)
254 return; 404 return;
405 TRACE_EVENT1("media", "frameTick", "frame", m_framePose ? m_framePose->poseInd ex + 1 : -1);
406
407 #if 0
408 Platform::current()->currentThread()->getWebTaskRunner()->postDelayedTask(
409 BLINK_FROM_HERE, WTF::bind(&delayTest), 8.0);
410 #endif
411
412 double timeNow = getMonotonicTimestampMs();
413 m_frameTickStartMs = timeNow;
414 m_highResTimeMsAtLastFrameTick = highResTimeMs;
415
416 VLOG(2) << __FUNCTION__ << ": next frame " << (m_framePose ? m_framePose->pose Index + 1 : -1) << " pending=" << m_framesPending;
417 bool skipThisTick = false;
418 if (m_framesPending > 1) {
419 LOG(ERROR) << __FUNCTION__ << ": SHOULD NOT HAPPEN: too many frames pending, want <=1, have " << m_framesPending;
420 skipThisTick = true;
421 } else if (m_frameWaitingToSubmit) {
422 VLOG(2) << __FUNCTION__ << ": have frame waiting to be submitted";
423 // Don't pump the timer loop, that will happen when the pending frame comple tes.
424 return;
425 } else if (m_framesPending > 0 &&
426 m_historyPreSubmitTimeMs.hasPrediction() &&
427 m_historyPostSubmitTimeMs.hasPrediction()) {
428 #if 0
429 double expectedRenderTime = m_historyPostSubmitTimeMs.getPrediction();
430 double expectedFinishRender = m_submitExecuteMs + expectedRenderTime - timeN ow;
431 // Simple logic - skip this frame if remaining rendering will take
432 // more than one frame.
433 if (expectedFinishRender > frameTime + 5.0) skipThisTick = true;
434 VLOG(2) << __FUNCTION__ << ": expectedRenderTime=" <<
435 expectedRenderTime << " expectedFinishRender=" <<
436 expectedFinishRender << " frameTime=" << frameTime << " skip=" <<
437 skipThisTick;
438 #endif
439 #if 0
440 // Alternate attempt - try to throttle to 30fps if not hitting 60fps.
441 // Doesn't work great at this point, needs further tuning and testing.
442 double allowedTime = frameTime + 5.0;
443 double expectedSubmit = m_historyPreSubmitTimeMs.getPrediction();
444 if (expectedFinishRender + expectedSubmit > allowedTime) skipThisTick = true ;
445 VLOG(2) << __FUNCTION__ << ": expectedRenderTime=" << expectedRenderTime << " expectedFinishRender=" << expectedFinishRender << " expectedSubmit=" << expect edSubmit << " frameTime=" << frameTime << " skip=" << skipThisTick;
446 #endif
447 }
448 {
449 double frameTimeMs = 1000.0 / 60; // TODO(klausw): measure instead of assumi ng 60fps.
450 if (timeNow - m_submitExecuteMs < frameTimeMs / 2) {
451 // Last submission was less than half a frame ago.
452 skipThisTick = true;
453 VLOG(2) << __FUNCTION__ << ": last submit was " << timeNow - m_submitExecu teMs << "ms ago, skip=" << skipThisTick;
454 }
455 }
456 if (skipThisTick) {
457 VLOG(2) << __FUNCTION__ << ": SKIP FRAME, pending=" << m_framesPending;
458 rescheduleAtNextTick();
459 return;
460 }
461
462 serviceScriptedAnimations();
463 }
464
465 void VRDisplay::rescheduleAtNextTick() {
466 if (m_tickIsScheduled) return;
467 m_tickIsScheduled = true;
468
469 Document* doc = m_navigatorVR->document();
470 if (!doc) {
471 VLOG(2) << __FUNCTION__ << ": cannot skip frame, no document?!";
472 return;
473 }
474 doc->requestAnimationFrame(new VRDisplayFrameRequestCallback(this));
475 }
476
477 void VRDisplay::serviceScriptedAnimations() {
478 if (!m_scriptedAnimationController)
479 return;
480 TRACE_EVENT1("media", "serviceScriptedAnimations", "frame", m_framePose ? m_fr amePose->poseIndex + 1 : -1);
481
255 AutoReset<bool> animating(&m_inAnimationFrame, true); 482 AutoReset<bool> animating(&m_inAnimationFrame, true);
256 m_animationCallbackRequested = false; 483 m_animationCallbackRequested = false;
257 484
258 // We use an internal rAF callback to run the animation loop at the display 485 // We use an internal rAF callback to run the animation loop at the display
259 // speed, and run the user's callback after our internal callback fires. 486 // speed, and run the user's callback after our internal callback fires.
260 // However, when the display is blurred, we want to pause the animation loop, 487 // However, when the display is blurred, we want to pause the animation loop,
261 // so we don't fire the user's callback until the display is focused. 488 // so we don't fire the user's callback until the display is focused.
262 if (m_displayBlurred) 489 if (m_displayBlurred)
263 return; 490 return;
491
492 m_serviceStartMs = getMonotonicTimestampMs();
493
494 // Adjust high res timer since some time may have passed since the frame tick.
495 double highResTimeMs = m_highResTimeMsAtLastFrameTick +
496 m_serviceStartMs - m_frameTickStartMs;
497
498 if (m_isPresenting && m_canUpdateFramePose) {
499 // Prefetch pose asynchronously to avoid waiting for it.
500 // TODO(klausw): align its timing so that it arrives just
501 // in time depending on when JS usually asks for it.
502 m_canUpdateFramePose = false;
503 requestPose();
504 }
505
506 Document* doc = m_navigatorVR->document();
507 if (!doc || !doc->loader()) {
508 VLOG(2) << __FUNCTION__ << ": no document";
509 return;
510 }
511 // Need to divide by 1000 here because serviceScriptedAnimations expects
512 // time to be given in seconds.
513 double monotonicAnimationStartTime =
514 doc->loader()->timing().pseudoWallTimeToMonotonicTime(highResTimeMs /
515 1000.0);
264 m_scriptedAnimationController->serviceScriptedAnimations( 516 m_scriptedAnimationController->serviceScriptedAnimations(
265 monotonicAnimationStartTime); 517 monotonicAnimationStartTime);
518 m_tickIsScheduled = true;
266 } 519 }
267 520
268 void ReportPresentationResult(PresentationResult result) { 521 void ReportPresentationResult(PresentationResult result) {
269 // Note that this is called twice for each call to requestPresent - 522 // Note that this is called twice for each call to requestPresent -
270 // one to declare that requestPresent was called, and one for the 523 // one to declare that requestPresent was called, and one for the
271 // result. 524 // result.
272 DEFINE_STATIC_LOCAL( 525 DEFINE_STATIC_LOCAL(
273 EnumerationHistogram, vrPresentationResultHistogram, 526 EnumerationHistogram, vrPresentationResultHistogram,
274 ("VRDisplayPresentResult", 527 ("VRDisplayPresentResult",
275 static_cast<int>(PresentationResult::PresentationResultMax))); 528 static_cast<int>(PresentationResult::PresentationResultMax)));
276 vrPresentationResultHistogram.count(static_cast<int>(result)); 529 vrPresentationResultHistogram.count(static_cast<int>(result));
277 } 530 }
278 531
279 ScriptPromise VRDisplay::requestPresent(ScriptState* scriptState, 532 ScriptPromise VRDisplay::requestPresent(ScriptState* scriptState,
280 const HeapVector<VRLayer>& layers) { 533 const HeapVector<VRLayer>& layers) {
534 VLOG(1) << __FUNCTION__;
281 ExecutionContext* executionContext = scriptState->getExecutionContext(); 535 ExecutionContext* executionContext = scriptState->getExecutionContext();
282 UseCounter::count(executionContext, UseCounter::VRRequestPresent); 536 UseCounter::count(executionContext, UseCounter::VRRequestPresent);
283 if (!executionContext->isSecureContext()) { 537 if (!executionContext->isSecureContext()) {
284 UseCounter::count(executionContext, 538 UseCounter::count(executionContext,
285 UseCounter::VRRequestPresentInsecureOrigin); 539 UseCounter::VRRequestPresentInsecureOrigin);
286 } 540 }
287 541
288 ReportPresentationResult(PresentationResult::Requested); 542 ReportPresentationResult(PresentationResult::Requested);
289 543
290 ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); 544 ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState);
291 ScriptPromise promise = resolver->promise(); 545 ScriptPromise promise = resolver->promise();
292 546
293 // If the VRDisplay does not advertise the ability to present reject the 547 // If the VRDisplay does not advertise the ability to present reject the
294 // request. 548 // request.
295 if (!m_capabilities->canPresent()) { 549 if (!m_capabilities->canPresent()) {
550 VLOG(1) << __FUNCTION__ << ": REJECT: VRDisplay cannot present";
296 DOMException* exception = 551 DOMException* exception =
297 DOMException::create(InvalidStateError, "VRDisplay cannot present."); 552 DOMException::create(InvalidStateError, "VRDisplay cannot present.");
298 resolver->reject(exception); 553 resolver->reject(exception);
299 ReportPresentationResult(PresentationResult::VRDisplayCannotPresent); 554 ReportPresentationResult(PresentationResult::VRDisplayCannotPresent);
300 return promise; 555 return promise;
301 } 556 }
302 557
303 bool firstPresent = !m_isPresenting; 558 bool firstPresent = !m_isPresenting;
304 559
305 // Initiating VR presentation is only allowed in response to a user gesture. 560 // Initiating VR presentation is only allowed in response to a user gesture.
306 // If the VRDisplay is already presenting, however, repeated calls are 561 // If the VRDisplay is already presenting, however, repeated calls are
307 // allowed outside a user gesture so that the presented content may be 562 // allowed outside a user gesture so that the presented content may be
308 // updated. 563 // updated.
564 #define HACK_DISABLE_USER_GESTURE_REQUIREMENT 0
565 #if !HACK_DISABLE_USER_GESTURE_REQUIREMENT
309 if (firstPresent && !UserGestureIndicator::utilizeUserGesture()) { 566 if (firstPresent && !UserGestureIndicator::utilizeUserGesture()) {
567 VLOG(1) << __FUNCTION__ << ": REJECT: API can only be initiated by a user ge sture";
310 DOMException* exception = DOMException::create( 568 DOMException* exception = DOMException::create(
311 InvalidStateError, "API can only be initiated by a user gesture."); 569 InvalidStateError, "API can only be initiated by a user gesture.");
312 resolver->reject(exception); 570 resolver->reject(exception);
313 ReportPresentationResult(PresentationResult::NotInitiatedByUserGesture); 571 ReportPresentationResult(PresentationResult::NotInitiatedByUserGesture);
314 return promise; 572 return promise;
315 } 573 }
574 #endif
316 575
317 // A valid number of layers must be provided in order to present. 576 // A valid number of layers must be provided in order to present.
318 if (layers.size() == 0 || layers.size() > m_capabilities->maxLayers()) { 577 if (layers.size() == 0 || layers.size() > m_capabilities->maxLayers()) {
578 VLOG(1) << __FUNCTION__ << ": REJECT: Invalid number of layers";
319 forceExitPresent(); 579 forceExitPresent();
320 DOMException* exception = 580 DOMException* exception =
321 DOMException::create(InvalidStateError, "Invalid number of layers."); 581 DOMException::create(InvalidStateError, "Invalid number of layers.");
322 resolver->reject(exception); 582 resolver->reject(exception);
323 ReportPresentationResult(PresentationResult::InvalidNumberOfLayers); 583 ReportPresentationResult(PresentationResult::InvalidNumberOfLayers);
324 return promise; 584 return promise;
325 } 585 }
326 586
327 m_layer = layers[0]; 587 m_layer = layers[0];
328 588
329 if (!m_layer.source()) { 589 if (!m_layer.source()) {
590 VLOG(1) << __FUNCTION__ << ": REJECT: Invalid layer source";
330 forceExitPresent(); 591 forceExitPresent();
331 DOMException* exception = 592 DOMException* exception =
332 DOMException::create(InvalidStateError, "Invalid layer source."); 593 DOMException::create(InvalidStateError, "Invalid layer source.");
333 resolver->reject(exception); 594 resolver->reject(exception);
334 ReportPresentationResult(PresentationResult::InvalidLayerSource); 595 ReportPresentationResult(PresentationResult::InvalidLayerSource);
335 return promise; 596 return promise;
336 } 597 }
337 598
338 CanvasRenderingContext* renderingContext = 599 CanvasRenderingContext* renderingContext =
339 m_layer.source()->renderingContext(); 600 m_layer.source()->renderingContext();
601 if (firstPresent) {
602 m_sourceWidth = m_layer.source()->width();
603 m_sourceHeight = m_layer.source()->height();
604 }
340 605
341 if (!renderingContext || !renderingContext->is3d()) { 606 if (!renderingContext || !renderingContext->is3d()) {
607 VLOG(1) << __FUNCTION__ << ": REJECT: Layer source must have a WebGLRenderin gContext";
342 forceExitPresent(); 608 forceExitPresent();
343 DOMException* exception = DOMException::create( 609 DOMException* exception = DOMException::create(
344 InvalidStateError, "Layer source must have a WebGLRenderingContext"); 610 InvalidStateError, "Layer source must have a WebGLRenderingContext");
345 resolver->reject(exception); 611 resolver->reject(exception);
346 ReportPresentationResult( 612 ReportPresentationResult(
347 PresentationResult::LayerSourceMissingWebGLContext); 613 PresentationResult::LayerSourceMissingWebGLContext);
348 return promise; 614 return promise;
349 } 615 }
350 616
351 // Save the WebGL script and underlying GL contexts for use by submitFrame(). 617 // Save the WebGL script and underlying GL contexts for use by submitFrame().
352 m_renderingContext = toWebGLRenderingContextBase(renderingContext); 618 m_renderingContext = toWebGLRenderingContextBase(renderingContext);
353 m_contextGL = m_renderingContext->contextGL(); 619 m_contextGL = m_renderingContext->contextGL();
354 620
355 if ((m_layer.leftBounds().size() != 0 && m_layer.leftBounds().size() != 4) || 621 if ((m_layer.leftBounds().size() != 0 && m_layer.leftBounds().size() != 4) ||
356 (m_layer.rightBounds().size() != 0 && 622 (m_layer.rightBounds().size() != 0 &&
357 m_layer.rightBounds().size() != 4)) { 623 m_layer.rightBounds().size() != 4)) {
624 VLOG(1) << __FUNCTION__ << ": REJECT: Layer bounds must either be an empty a rray or have 4 values";
358 forceExitPresent(); 625 forceExitPresent();
359 DOMException* exception = DOMException::create( 626 DOMException* exception = DOMException::create(
360 InvalidStateError, 627 InvalidStateError,
361 "Layer bounds must either be an empty array or have 4 values"); 628 "Layer bounds must either be an empty array or have 4 values");
362 resolver->reject(exception); 629 resolver->reject(exception);
363 ReportPresentationResult(PresentationResult::InvalidLayerBounds); 630 ReportPresentationResult(PresentationResult::InvalidLayerBounds);
364 return promise; 631 return promise;
365 } 632 }
366 633
367 if (!m_pendingPresentResolvers.isEmpty()) { 634 if (!m_pendingPresentResolvers.isEmpty()) {
368 // If we are waiting on the results of a previous requestPresent call don't 635 // If we are waiting on the results of a previous requestPresent call don't
369 // fire a new request, just cache the resolver and resolve it when the 636 // fire a new request, just cache the resolver and resolve it when the
370 // original request returns. 637 // original request returns.
371 m_pendingPresentResolvers.append(resolver); 638 m_pendingPresentResolvers.append(resolver);
372 } else if (firstPresent) { 639 } else if (firstPresent) {
373 bool secureContext = scriptState->getExecutionContext()->isSecureContext(); 640 bool secureContext = scriptState->getExecutionContext()->isSecureContext();
374 if (!m_display) { 641 if (!m_display) {
642 VLOG(1) << __FUNCTION__ << ": REJECT: The service is no longer active";
375 forceExitPresent(); 643 forceExitPresent();
376 DOMException* exception = DOMException::create( 644 DOMException* exception = DOMException::create(
377 InvalidStateError, "The service is no longer active."); 645 InvalidStateError, "The service is no longer active.");
378 resolver->reject(exception); 646 resolver->reject(exception);
379 return promise; 647 return promise;
380 } 648 }
381 649
382 m_pendingPresentResolvers.append(resolver); 650 m_pendingPresentResolvers.append(resolver);
383 m_display->RequestPresent(secureContext, convertToBaseCallback(WTF::bind( 651 m_display->RequestPresent(secureContext, convertToBaseCallback(WTF::bind(
384 &VRDisplay::onPresentComplete, 652 &VRDisplay::onPresentComplete,
385 wrapPersistent(this)))); 653 wrapPersistent(this))));
386 } else { 654 } else {
655 m_isPresenting = true;
387 updateLayerBounds(); 656 updateLayerBounds();
388 resolver->resolve(); 657 resolver->resolve();
389 ReportPresentationResult(PresentationResult::SuccessAlreadyPresenting); 658 ReportPresentationResult(PresentationResult::SuccessAlreadyPresenting);
390 } 659 }
391 660
392 return promise; 661 return promise;
393 } 662 }
394 663
395 void VRDisplay::onPresentComplete(bool success) { 664 void VRDisplay::onPresentComplete(bool success) {
665 VLOG(1) << __FUNCTION__;
396 if (success) { 666 if (success) {
397 this->beginPresent(); 667 this->beginPresent();
398 } else { 668 } else {
399 this->forceExitPresent(); 669 this->forceExitPresent();
400 DOMException* exception = DOMException::create( 670 DOMException* exception = DOMException::create(
401 NotAllowedError, "Presentation request was denied."); 671 NotAllowedError, "Presentation request was denied.");
402 672
403 while (!m_pendingPresentResolvers.isEmpty()) { 673 while (!m_pendingPresentResolvers.isEmpty()) {
404 ScriptPromiseResolver* resolver = m_pendingPresentResolvers.takeFirst(); 674 ScriptPromiseResolver* resolver = m_pendingPresentResolvers.takeFirst();
405 resolver->reject(exception); 675 resolver->reject(exception);
406 } 676 }
407 } 677 }
408 } 678 }
409 679
410 ScriptPromise VRDisplay::exitPresent(ScriptState* scriptState) { 680 ScriptPromise VRDisplay::exitPresent(ScriptState* scriptState) {
681 VLOG(1) << __FUNCTION__;
411 ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); 682 ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState);
412 ScriptPromise promise = resolver->promise(); 683 ScriptPromise promise = resolver->promise();
413 684
414 if (!m_isPresenting) { 685 if (!m_isPresenting) {
415 // Can't stop presenting if we're not presenting. 686 // Can't stop presenting if we're not presenting.
416 DOMException* exception = 687 DOMException* exception =
417 DOMException::create(InvalidStateError, "VRDisplay is not presenting."); 688 DOMException::create(InvalidStateError, "VRDisplay is not presenting.");
418 resolver->reject(exception); 689 resolver->reject(exception);
419 return promise; 690 return promise;
420 } 691 }
421 692
422 if (!m_display) { 693 if (!m_display) {
423 DOMException* exception = 694 DOMException* exception =
424 DOMException::create(InvalidStateError, "VRService is not available."); 695 DOMException::create(InvalidStateError, "VRService is not available.");
425 resolver->reject(exception); 696 resolver->reject(exception);
426 return promise; 697 return promise;
427 } 698 }
428 m_display->ExitPresent(); 699 m_display->ExitPresent();
429 700
430 resolver->resolve(); 701 resolver->resolve();
431 702
432 forceExitPresent(); 703 forceExitPresent();
433 704
434 return promise; 705 return promise;
435 } 706 }
436 707
437 void VRDisplay::beginPresent() { 708 void VRDisplay::beginPresent() {
709 VLOG(1) << __FUNCTION__;
438 Document* doc = this->document(); 710 Document* doc = this->document();
439 std::unique_ptr<UserGestureIndicator> gestureIndicator; 711 std::unique_ptr<UserGestureIndicator> gestureIndicator;
440 if (m_capabilities->hasExternalDisplay()) { 712 if (m_capabilities->hasExternalDisplay()) {
441 forceExitPresent(); 713 forceExitPresent();
442 DOMException* exception = DOMException::create( 714 DOMException* exception = DOMException::create(
443 InvalidStateError, 715 InvalidStateError,
444 "VR Presentation not implemented for this VRDisplay."); 716 "VR Presentation not implemented for this VRDisplay.");
445 while (!m_pendingPresentResolvers.isEmpty()) { 717 while (!m_pendingPresentResolvers.isEmpty()) {
446 ScriptPromiseResolver* resolver = m_pendingPresentResolvers.takeFirst(); 718 ScriptPromiseResolver* resolver = m_pendingPresentResolvers.takeFirst();
447 resolver->reject(exception); 719 resolver->reject(exception);
448 } 720 }
449 ReportPresentationResult( 721 ReportPresentationResult(
450 PresentationResult::PresentationNotSupportedByDisplay); 722 PresentationResult::PresentationNotSupportedByDisplay);
451 return; 723 return;
452 } else { 724 } else {
453 // TODO(klausw,crbug.com/655722): Need a proper VR compositor, but
454 // for the moment on mobile we'll just make the canvas fullscreen
455 // so that VrShell can pick it up through the standard (high
456 // latency) compositing path.
457 auto canvas = m_layer.source();
458 auto inlineStyle = canvas->inlineStyle();
459 if (inlineStyle) {
460 // THREE.js's VREffect sets explicit style.width/height on its rendering
461 // canvas based on the non-fullscreen window dimensions, and it keeps
462 // those unchanged when presenting. Unfortunately it appears that a
463 // fullscreened canvas just gets centered if it has explicitly set a
464 // size smaller than the fullscreen dimensions. Manually set size to
465 // 100% in this case and restore it when exiting fullscreen. This is a
466 // stopgap measure since THREE.js's usage appears legal according to the
467 // WebVR API spec. This will no longer be necessary once we can get rid
468 // of this fullscreen hack.
469 m_fullscreenOrigWidth = inlineStyle->getPropertyValue(CSSPropertyWidth);
470 if (!m_fullscreenOrigWidth.isNull()) {
471 canvas->setInlineStyleProperty(CSSPropertyWidth, "100%");
472 }
473 m_fullscreenOrigHeight = inlineStyle->getPropertyValue(CSSPropertyHeight);
474 if (!m_fullscreenOrigHeight.isNull()) {
475 canvas->setInlineStyleProperty(CSSPropertyHeight, "100%");
476 }
477 } else {
478 m_fullscreenOrigWidth = String();
479 m_fullscreenOrigHeight = String();
480 }
481
482 if (doc) {
483 // Since the callback for requestPresent is asynchronous, we've lost our
484 // UserGestureToken, and need to create a new one to enter fullscreen.
485 gestureIndicator = WTF::wrapUnique(
486 new UserGestureIndicator(DocumentUserGestureToken::create(
487 doc, UserGestureToken::Status::PossiblyExistingGesture)));
488 }
489 Fullscreen::requestFullscreen(*canvas);
490
491 // Check to see if the canvas is still the current fullscreen
492 // element once every 2 seconds.
493 m_fullscreenCheckTimer.startRepeating(2.0, BLINK_FROM_HERE);
494 m_reenteredFullscreen = false;
495 } 725 }
496 726
497 if (doc) { 727 if (doc) {
498 Platform::current()->recordRapporURL("VR.WebVR.PresentSuccess", 728 Platform::current()->recordRapporURL("VR.WebVR.PresentSuccess",
499 WebURL(doc->url())); 729 WebURL(doc->url()));
500 } 730 }
501 731
732 // Stop unneeded compositor updates. We do so by hiding the view. We can't
733 // hide the page, doing so causes an assertion failure (!m_isHidden) in
734 // DrawingBuffer::prepareTextureMailboxInternal(). Do this only when we're
735 // actually presenting (m_isPresenting is true), see corresponding show()
736 // in forceExitPresent(). Otherwise the view may remain hidden for failing
737 // DON flow.
738 m_navigatorVR->document()->view()->hide();
739
502 m_isPresenting = true; 740 m_isPresenting = true;
503 ReportPresentationResult(PresentationResult::Success); 741 ReportPresentationResult(PresentationResult::Success);
504 742
505 updateLayerBounds(); 743 updateLayerBounds();
506 744
507 while (!m_pendingPresentResolvers.isEmpty()) { 745 while (!m_pendingPresentResolvers.isEmpty()) {
508 ScriptPromiseResolver* resolver = m_pendingPresentResolvers.takeFirst(); 746 ScriptPromiseResolver* resolver = m_pendingPresentResolvers.takeFirst();
509 resolver->resolve(); 747 resolver->resolve();
510 } 748 }
511 OnPresentChange(); 749 OnPresentChange();
512 } 750 }
513 751
514 void VRDisplay::forceExitPresent() { 752 void VRDisplay::forceExitPresent() {
753 VLOG(1) << __FUNCTION__;
754 if (m_surfaceHandle) {
755 m_renderingContext->setSurfaceHandle(0);
756 m_surfaceHandle = 0;
757 }
758
515 if (m_isPresenting) { 759 if (m_isPresenting) {
516 if (!m_capabilities->hasExternalDisplay()) {
517 auto canvas = m_layer.source();
518 Fullscreen::fullyExitFullscreen(canvas->document());
519 m_fullscreenCheckTimer.stop();
520 if (!m_fullscreenOrigWidth.isNull()) {
521 canvas->setInlineStyleProperty(CSSPropertyWidth, m_fullscreenOrigWidth);
522 m_fullscreenOrigWidth = String();
523 }
524 if (!m_fullscreenOrigHeight.isNull()) {
525 canvas->setInlineStyleProperty(CSSPropertyWidth,
526 m_fullscreenOrigHeight);
527 m_fullscreenOrigHeight = String();
528 }
529 } else {
530 // Can't get into this presentation mode, so nothing to do here.
531 }
532 m_isPresenting = false; 760 m_isPresenting = false;
761 m_canUpdateFramePose = true;
533 OnPresentChange(); 762 OnPresentChange();
763 m_navigatorVR->document()->view()->show();
534 } 764 }
535 765
536 m_renderingContext = nullptr; 766 m_renderingContext = nullptr;
537 m_contextGL = nullptr; 767 m_contextGL = nullptr;
768 m_contextProvider = nullptr;
769
770 VLOG(2) << __FUNCTION__ << ": lost surface, reset m_framesPending " << m_frame sPending << " => " << 0;
771 m_framesPending = 0;
772 if (m_frameWaitingToSubmit) {
773 m_frameWaitingToSubmit = false;
774 // Make sure not to lose a rAF call if we ignore a pending frame.
775 serviceScriptedAnimations();
776 }
538 } 777 }
539 778
540 void VRDisplay::updateLayerBounds() { 779 void VRDisplay::updateLayerBounds() {
541 if (!m_display) 780 if (!m_display)
542 return; 781 return;
543 782
544 // Set up the texture bounds for the provided layer 783 // Set up the texture bounds for the provided layer
545 device::mojom::blink::VRLayerBoundsPtr leftBounds = 784 device::mojom::blink::VRLayerBoundsPtr leftBounds =
546 device::mojom::blink::VRLayerBounds::New(); 785 device::mojom::blink::VRLayerBounds::New();
547 device::mojom::blink::VRLayerBoundsPtr rightBounds = 786 device::mojom::blink::VRLayerBoundsPtr rightBounds =
548 device::mojom::blink::VRLayerBounds::New(); 787 device::mojom::blink::VRLayerBounds::New();
549 788
789 // For which pose should these new bounds take effect?
790 // This depends on if the new layer bounds were applied
791 // before or after updating the pose.
792 uint32_t forPoseIndex = m_framePose ? m_framePose->poseIndex : 0;
793 if (m_canUpdateFramePose) ++forPoseIndex;
794
795 leftBounds->forPoseIndex = forPoseIndex;
796 rightBounds->forPoseIndex = forPoseIndex;
797
550 if (m_layer.leftBounds().size() == 4) { 798 if (m_layer.leftBounds().size() == 4) {
551 leftBounds->left = m_layer.leftBounds()[0]; 799 leftBounds->left = m_layer.leftBounds()[0];
552 leftBounds->top = m_layer.leftBounds()[1]; 800 leftBounds->top = m_layer.leftBounds()[1];
553 leftBounds->width = m_layer.leftBounds()[2]; 801 leftBounds->width = m_layer.leftBounds()[2];
554 leftBounds->height = m_layer.leftBounds()[3]; 802 leftBounds->height = m_layer.leftBounds()[3];
555 } else { 803 } else {
556 // Left eye defaults 804 // Left eye defaults
557 leftBounds->left = 0.0f; 805 leftBounds->left = 0.0f;
558 leftBounds->top = 0.0f; 806 leftBounds->top = 0.0f;
559 leftBounds->width = 0.5f; 807 leftBounds->width = 0.5f;
560 leftBounds->height = 1.0f; 808 leftBounds->height = 1.0f;
561 } 809 }
562 810
563 if (m_layer.rightBounds().size() == 4) { 811 if (m_layer.rightBounds().size() == 4) {
564 rightBounds->left = m_layer.rightBounds()[0]; 812 rightBounds->left = m_layer.rightBounds()[0];
565 rightBounds->top = m_layer.rightBounds()[1]; 813 rightBounds->top = m_layer.rightBounds()[1];
566 rightBounds->width = m_layer.rightBounds()[2]; 814 rightBounds->width = m_layer.rightBounds()[2];
567 rightBounds->height = m_layer.rightBounds()[3]; 815 rightBounds->height = m_layer.rightBounds()[3];
568 } else { 816 } else {
569 // Right eye defaults 817 // Right eye defaults
570 rightBounds->left = 0.5f; 818 rightBounds->left = 0.5f;
571 rightBounds->top = 0.0f; 819 rightBounds->top = 0.0f;
572 rightBounds->width = 0.5f; 820 rightBounds->width = 0.5f;
573 rightBounds->height = 1.0f; 821 rightBounds->height = 1.0f;
574 } 822 }
575 823
824 #ifdef HACK_PSEUDOSCALING
825 // TODO(klausw): this assumes that the eyes are arranged left to right with no gaps.
826 int recWidth = m_eyeParametersLeft->renderWidth() + m_eyeParametersRight->rend erWidth();
827 int recHeight = m_eyeParametersLeft->renderHeight();
828 if (m_sourceWidth != recWidth || m_sourceHeight != recHeight) {
829 VLOG(1) << __FUNCTION__ << ": resize " << m_sourceWidth << "x" << m_sourceHe ight << " to " << recWidth << "x" << recHeight;
830 double scaleX = (double)m_sourceWidth / recWidth;
831 double scaleY = (double)m_sourceHeight / recHeight;
832 leftBounds->left *= scaleX;
833 leftBounds->top *= scaleY;
834 leftBounds->width *= scaleX;
835 leftBounds->height *= scaleY;
836 rightBounds->left *= scaleX;
837 rightBounds->top *= scaleY;
838 rightBounds->width *= scaleX;
839 rightBounds->height *= scaleY;
840 }
841 #endif
842
576 m_display->UpdateLayerBounds(std::move(leftBounds), std::move(rightBounds)); 843 m_display->UpdateLayerBounds(std::move(leftBounds), std::move(rightBounds));
577 } 844 }
578 845
579 HeapVector<VRLayer> VRDisplay::getLayers() { 846 HeapVector<VRLayer> VRDisplay::getLayers() {
580 HeapVector<VRLayer> layers; 847 HeapVector<VRLayer> layers;
581 848
582 if (m_isPresenting) { 849 if (m_isPresenting) {
583 layers.append(m_layer); 850 layers.append(m_layer);
584 } 851 }
585 852
586 return layers; 853 return layers;
587 } 854 }
588 855
589 void VRDisplay::submitFrame() { 856 gpu::gles2::GLES2Interface* VRDisplay::getCompositingContext() {
590 if (!m_display) 857 if (!m_contextProvider) {
591 return; 858 m_contextProvider = WTF::wrapUnique(
592 859 Platform::current()->createSharedOffscreenGraphicsContext3DProvider());
593 Document* doc = this->document();
594 if (!m_isPresenting) {
595 if (doc) {
596 doc->addConsoleMessage(ConsoleMessage::create(
597 RenderingMessageSource, WarningMessageLevel,
598 "submitFrame has no effect when the VRDisplay is not presenting."));
599 }
600 return;
601 } 860 }
602 861
862 gpu::gles2::GLES2Interface* sharedContext = nullptr;
863 if (m_contextProvider) {
864 sharedContext = m_contextProvider->contextGL();
865
866 if (!sharedContext)
867 return nullptr;
868 }
869
870 return sharedContext;
871 }
872
873
874 void VRDisplay::submitFrame() {
875 Document* doc = this->document();
603 if (!m_inAnimationFrame) { 876 if (!m_inAnimationFrame) {
604 if (doc) { 877 if (doc) {
605 doc->addConsoleMessage( 878 doc->addConsoleMessage(
606 ConsoleMessage::create(RenderingMessageSource, WarningMessageLevel, 879 ConsoleMessage::create(RenderingMessageSource, WarningMessageLevel,
607 "submitFrame must be called within a " 880 "submitFrame must be called within a "
608 "VRDisplay.requestAnimationFrame callback.")); 881 "VRDisplay.requestAnimationFrame callback."));
609 } 882 }
610 return; 883 return;
611 } 884 }
612 885
886 if (!m_isPresenting) {
887 if (doc) {
888 doc->addConsoleMessage(ConsoleMessage::create(
889 RenderingMessageSource, WarningMessageLevel,
890 "submitFrame has no effect when the VRDisplay is not presenting."));
891 }
892 return;
893 }
894
895 if (!m_canSubmitFramePose) {
896 if (doc) {
897 doc->addConsoleMessage(ConsoleMessage::create(
898 RenderingMessageSource, WarningMessageLevel,
899 "submitFrame rejected, this pose was already used."));
900 m_canUpdateFramePose = true;
901 }
902 return;
903 }
904
905 submitFrameAnyContext();
906 }
907
908 void VRDisplay::submitFrameAnyContext() {
909 TRACE_EVENT1("media", "klausw:submitFrame", "frame", m_framePose->poseIndex);
910
911 if (!m_display)
912 return;
913
613 if (!m_contextGL) { 914 if (!m_contextGL) {
614 // Something got confused, we can't submit frames without a GL context. 915 // Something got confused, we can't submit frames without a GL context.
615 return; 916 return;
616 } 917 }
617 918
618 // Write the frame number for the pose used into a bottom left pixel block. 919 if (m_framePose) {
619 // It is read by chrome/browser/android/vr_shell/vr_shell.cc to associate 920 m_framePose->ts_frameStart = m_serviceStartMs;
620 // the correct corresponding pose for submission. 921 m_framePose->ts_submit = getMonotonicTimestampMs();
922 }
923
924 GLenum error;
925 (void)error;
926
927 #define DRAW_FRAME_COUNTER_PIXEL_FOR_DEBUGGING 0
928 #if DRAW_FRAME_COUNTER_PIXEL_FOR_DEBUGGING
621 auto gl = m_contextGL; 929 auto gl = m_contextGL;
622
623 // We must ensure that the WebGL app's GL state is preserved. We do this by
624 // calling low-level GL commands directly so that the rendering context's
625 // saved parameters don't get overwritten.
626
627 gl->Enable(GL_SCISSOR_TEST); 930 gl->Enable(GL_SCISSOR_TEST);
628 // Use a few pixels to ensure we get a clean color. The resolution for the 931 gl->Scissor(0, 0, 200, 200);
629 // WebGL buffer may not match the final rendered destination size, and
630 // texture filtering could interfere for single pixels. This isn't visible
631 // since the final rendering hides the edges via a vignette effect.
632 gl->Scissor(0, 0, 4, 4);
633 gl->ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); 932 gl->ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
634 int idx = m_framePose->poseIndex; 933 int idx = m_framePose->poseIndex;
635 // Careful with the arithmetic here. Float color 1.f is equivalent to int 255. 934 // Careful with the arithmetic here. Float color 1.f is equivalent to int 255.
636 // Use the low byte of the index as the red component, and store an arbitrary 935 gl->ClearColor((idx & 255) / 255.0f, ((idx >> 8) & 255) / 255.0f,
637 // magic number in green/blue. This number must match the reading code in 936 ((idx >> 16) & 255) / 255.0f, 1.0f);
638 // vr_shell.cc. Avoid all-black/all-white.
639 gl->ClearColor((idx & 255) / 255.0f, kWebVrPosePixelMagicNumbers[0] / 255.0f,
640 kWebVrPosePixelMagicNumbers[1] / 255.0f, 1.0f);
641 gl->Clear(GL_COLOR_BUFFER_BIT); 937 gl->Clear(GL_COLOR_BUFFER_BIT);
642
643 // Set the GL state back to what was set by the WebVR application. 938 // Set the GL state back to what was set by the WebVR application.
644 m_renderingContext->restoreScissorEnabled(); 939 m_renderingContext->restoreScissorEnabled();
645 m_renderingContext->restoreScissorBox(); 940 m_renderingContext->restoreScissorBox();
646 m_renderingContext->restoreColorMask(); 941 m_renderingContext->restoreColorMask();
647 m_renderingContext->restoreClearColor(); 942 m_renderingContext->restoreClearColor();
648 943 #endif
649 m_display->SubmitFrame(m_framePose.Clone()); 944
945 bool needSurfaceHandle = !m_surfaceHandle;
946
947 int currentWidth = m_layer.source()->width();
948 int currentHeight = m_layer.source()->height();
949 if (currentWidth != m_sourceWidth || currentHeight != m_sourceHeight) {
950 VLOG(1) << __FUNCTION__ << ": source size changed from " <<
951 m_sourceWidth << "x" << m_sourceHeight <<
952 " to " <<
953 currentWidth << "x" << currentHeight <<
954 ", m_surfaceHandleRequested=" << m_surfaceHandleRequested <<
955 ", m_surfaceHandle=" << m_surfaceHandle;
956 if (m_surfaceHandleRequested) {
957 // We can't currently request a new surface, a request is in flight.
958 // Just do nothing, try again next iteration.
959 VLOG(2) << __FUNCTION__ << ": do nothing, request in flight";
960 } else {
961 m_sourceWidth = currentWidth;
962 m_sourceHeight = currentHeight;
963 needSurfaceHandle = true;
964 }
965 }
966
967 if (needSurfaceHandle) {
968 if (!m_surfaceHandleRequested) {
969 auto callback = convertToBaseCallback(WTF::bind(
970 &VRDisplay::onGetSurfaceHandleComplete, wrapPersistent(this)));
971 m_surfaceHandleRequested = true;
972 m_display->GetSurfaceHandle(m_sourceWidth, m_sourceHeight, callback);
973 VLOG(2) << __FUNCTION__ << ": requesting surface handle for size " <<
974 m_sourceWidth << "x" << m_sourceHeight;
975 }
976 VLOG(2) << __FUNCTION__ << ": no surface available yet, discarding frame";
977 m_canUpdateFramePose = true;
978 return;
979 }
980
981 auto framePose = m_framePose.Clone();
982 if (!framePose) {
983 VLOG(2) << __FUNCTION__ << ": no pose!";
984 m_canUpdateFramePose = true;
985 return;
986 }
987
988 auto poseIdx = framePose->poseIndex;
989
990 assert(m_surfaceHandle != 0);
991 assert(poseIdx != 0);
992
993 if (m_framesPending > 0) {
994 VLOG(1) << __FUNCTION__ << ": m_framesPending=" << m_framesPending << ", def erring frame " << poseIdx;
995 m_frameWaitingToSubmit = true;
996 return;
997 }
998
999 // From this point on we're committed to calling SwapBuffers and submitting
1000 // the frame. Don't do any early return from this point onward.
1001 VLOG(2) << __FUNCTION__ << ": decided to submitFrame, have pose for frame " << poseIdx;
1002
1003 m_submitExecuteMs = getMonotonicTimestampMs();
1004 m_historyPreSubmitTimeMs.add(m_submitExecuteMs - m_serviceStartMs);
1005
1006 // This is a load-bearing glFlush(). Removing it breaks rendering. WTF.
1007 m_contextGL->Flush();
1008 m_contextGL->SwapBuffers();
1009 // Use glFinish here to avoid excessive wait-for-completion in
1010 // the single-threaded CrBrowserMain message handler. TODO(klausw):
1011 // use a fence instead?
1012 //m_contextGL->Finish();
1013
1014 auto callback = convertToBaseCallback(WTF::bind(
1015 &VRDisplay::onSubmitFrameComplete, wrapPersistent(this)));
1016
1017 // Update pending frames, we have a surface handle.
1018 VLOG(2) << __FUNCTION__ << ": m_framesPending " << m_framesPending << " => " < < m_framesPending + 1;
1019 ++m_framesPending;
1020
1021 m_display->SubmitFrame(m_surfaceHandle, std::move(framePose), callback);
1022 m_canSubmitFramePose = false; // Illegal to reuse the same pose twice.
650 m_canUpdateFramePose = true; 1023 m_canUpdateFramePose = true;
1024 VLOG(2) << __FUNCTION__ << ": submit done for frame " << poseIdx;
1025 }
1026
1027 void VRDisplay::onGetSurfaceHandleComplete(int32_t surfaceHandle) {
1028 VLOG(1) << __FUNCTION__ << ": VRDisplay new surface handle=" << surfaceHandle;
1029 m_surfaceHandleRequested = false;
1030
1031 if (m_surfaceHandle) {
1032 // We have a surface, disconnect it so that the reconnect
1033 // below resizes it.
1034 //
1035 // TODO(klausw): simplify this, would be nice to do a call
1036 // to m_renderingContext->setSurfaceHandle(current_handle)
1037 // would do an in-place resize. See
1038 // SurfaceTexture.setDefaultBufferSize documentation:
1039 //
1040 // For OpenGL ES, the EGLSurface should be destroyed
1041 // (via eglDestroySurface), made not-current (via
1042 // eglMakeCurrent), and then recreated (via
1043 // eglCreateWindowSurface) to ensure that the new
1044 // default size has taken effect.
1045 VLOG(2) << __FUNCTION__ << ": zero current surface handle to resize";
1046 m_renderingContext->setSurfaceHandle(0);
1047 m_surfaceHandle = 0;
1048 }
1049
1050 // TODO(klausw): special-case in-place resize?
1051 m_renderingContext->setSurfaceHandle(surfaceHandle);
1052 m_surfaceHandle = surfaceHandle;
1053
1054 // Reset stats counters since a new resolution may have
1055 // very different performance.
1056 m_historyPreSubmitTimeMs.clear();
1057 m_historyPostSubmitTimeMs.clear();
1058 updateLayerBounds();
1059 }
1060
1061 void VRDisplay::onSubmitFrameComplete(int32_t surfaceHandle, uint32_t poseIndex, double renderMs) {
1062 TRACE_EVENT1("media", "klausw:onSubmitFrameComplete", "frame", poseIndex);
1063 VLOG(2) << __FUNCTION__ << ": surface " << surfaceHandle << ", frame " << pose Index << ", render time " << renderMs << "ms";
1064 if (!surfaceHandle) {
1065 VLOG(2) << __FUNCTION__ << ": Ignoring callback for invalid surface 0";
1066 return;
1067 }
1068 if (surfaceHandle != m_surfaceHandle) {
1069 VLOG(1) << __FUNCTION__ << ": Ignoring callback, was for surface " << surfac eHandle << " which is not the current surface " << m_surfaceHandle;
1070 return;
1071 }
1072
1073 // Update pending frames, the callback matches our active surface handle.
1074 VLOG(2) << __FUNCTION__ << ": m_framesPending " << m_framesPending << " => " < < m_framesPending - 1;
1075 --m_framesPending; // should now be == 0
1076
1077 m_historyPostSubmitTimeMs.add(getMonotonicTimestampMs() - m_submitExecuteMs);
1078
1079 if (m_frameWaitingToSubmit) {
1080 VLOG(2) << __FUNCTION__ << ": ready to submit deferred frame " << m_framePos e->poseIndex;
1081 submitFrameAnyContext();
1082 // m_framesPending is now == 1
1083 m_frameWaitingToSubmit = false;
1084
1085 // Run next rAF now, don't wait for next timer tick.
1086 // TODO(klausw): try to stay tied to vsync and run at 30fps
1087 // if not keeping up?
1088 bool waitForNextTick = false;
1089 if (m_historyPreSubmitTimeMs.hasPrediction()) {
1090 // a . . b . . c . . d . . e . . f . . g
1091 // | | | | | |
1092 double avgSubmitDuration = m_historyPreSubmitTimeMs.getPrediction();
1093 double frameTimeMs = 1000.0 / 60; // TODO(klausw): measure instead of assu ming 60fps.
1094 double nextTickWaitMs = m_frameTickStartMs + frameTimeMs - m_submitExecute Ms;
1095 if (avgSubmitDuration < frameTimeMs && nextTickWaitMs > 0 /* && nextTickWa itMs < frameTimeMs / 2 */) {
1096 // Last submit took less than a frame, and the next tick is soon. Wait f or it to avoid exceeding 60fps.
1097 VLOG(2) << __FUNCTION__ << ": avg submit took " << avgSubmitDuration <<
1098 "ms, next tick in " << nextTickWaitMs <<
1099 "ms. reschedule at next tick after frame " << m_framePose->poseIndex ;
1100 waitForNextTick = true;
1101 } else {
1102 VLOG(2) << __FUNCTION__ << ": avg submit took " << avgSubmitDuration <<
1103 "ms, next tick in " << nextTickWaitMs <<
1104 "ms. reschedule now after frame " << m_framePose->poseIndex;
1105 }
1106 }
1107 if (waitForNextTick) {
1108 rescheduleAtNextTick();
1109 } else {
1110 // We're backlogged. Run new frame immediately.
1111 serviceScriptedAnimations();
1112 }
1113 }
651 } 1114 }
652 1115
653 Document* VRDisplay::document() { 1116 Document* VRDisplay::document() {
654 return m_navigatorVR->document(); 1117 return m_navigatorVR->document();
655 } 1118 }
656 1119
657 void VRDisplay::OnPresentChange() { 1120 void VRDisplay::OnPresentChange() {
1121 VLOG(1) << __FUNCTION__;
658 if (m_isPresenting && !m_isValidDeviceForPresenting) { 1122 if (m_isPresenting && !m_isValidDeviceForPresenting) {
659 VLOG(1) << __FUNCTION__ << ": device not valid, not sending event"; 1123 VLOG(1) << __FUNCTION__ << ": device not valid, not sending event";
660 return; 1124 return;
661 } 1125 }
662 m_navigatorVR->enqueueVREvent(VRDisplayEvent::create( 1126 m_navigatorVR->enqueueVREvent(VRDisplayEvent::create(
663 EventTypeNames::vrdisplaypresentchange, true, false, this, "")); 1127 EventTypeNames::vrdisplaypresentchange, true, false, this, ""));
664 } 1128 }
665 1129
666 void VRDisplay::OnChanged(device::mojom::blink::VRDisplayInfoPtr display) { 1130 void VRDisplay::OnChanged(device::mojom::blink::VRDisplayInfoPtr display) {
1131 VLOG(1) << __FUNCTION__;
667 update(display); 1132 update(display);
668 } 1133 }
669 1134
670 void VRDisplay::OnExitPresent() { 1135 void VRDisplay::OnExitPresent() {
1136 VLOG(1) << __FUNCTION__;
671 forceExitPresent(); 1137 forceExitPresent();
672 } 1138 }
673 1139
674 void VRDisplay::onConnected() { 1140 void VRDisplay::onConnected() {
1141 VLOG(1) << __FUNCTION__;
675 m_navigatorVR->enqueueVREvent(VRDisplayEvent::create( 1142 m_navigatorVR->enqueueVREvent(VRDisplayEvent::create(
676 EventTypeNames::vrdisplayconnect, true, false, this, "connect")); 1143 EventTypeNames::vrdisplayconnect, true, false, this, "connect"));
677 } 1144 }
678 1145
679 void VRDisplay::onDisconnected() { 1146 void VRDisplay::onDisconnected() {
1147 VLOG(1) << __FUNCTION__;
680 m_navigatorVR->enqueueVREvent(VRDisplayEvent::create( 1148 m_navigatorVR->enqueueVREvent(VRDisplayEvent::create(
681 EventTypeNames::vrdisplaydisconnect, true, false, this, "disconnect")); 1149 EventTypeNames::vrdisplaydisconnect, true, false, this, "disconnect"));
682 } 1150 }
683 1151
684 void VRDisplay::OnActivate(device::mojom::blink::VRDisplayEventReason reason) { 1152 void VRDisplay::OnActivate(device::mojom::blink::VRDisplayEventReason reason) {
1153 VLOG(1) << __FUNCTION__;
685 m_navigatorVR->dispatchVRGestureEvent(VRDisplayEvent::create( 1154 m_navigatorVR->dispatchVRGestureEvent(VRDisplayEvent::create(
686 EventTypeNames::vrdisplayactivate, true, false, this, reason)); 1155 EventTypeNames::vrdisplayactivate, true, false, this, reason));
687 } 1156 }
688 1157
689 void VRDisplay::OnDeactivate( 1158 void VRDisplay::OnDeactivate(
690 device::mojom::blink::VRDisplayEventReason reason) { 1159 device::mojom::blink::VRDisplayEventReason reason) {
1160 VLOG(1) << __FUNCTION__;
691 m_navigatorVR->enqueueVREvent(VRDisplayEvent::create( 1161 m_navigatorVR->enqueueVREvent(VRDisplayEvent::create(
692 EventTypeNames::vrdisplaydeactivate, true, false, this, reason)); 1162 EventTypeNames::vrdisplaydeactivate, true, false, this, reason));
693 } 1163 }
694 1164
695 void VRDisplay::onFullscreenCheck(TimerBase*) {
696 if (!m_isPresenting) {
697 m_fullscreenCheckTimer.stop();
698 return;
699 }
700 // TODO: This is a temporary measure to track if fullscreen mode has been
701 // exited by the UA. If so we need to end VR presentation. Soon we won't
702 // depend on the Fullscreen API to fake VR presentation, so this will
703 // become unnessecary. Until that point, though, this seems preferable to
704 // adding a bunch of notification plumbing to Fullscreen.
705 if (!Fullscreen::isFullscreenElement(*m_layer.source())) {
706 // TODO(mthiesse): Due to asynchronous resizing, we might get kicked out of
707 // fullscreen when changing display parameters upon entering WebVR. So one
708 // time only, we reenter fullscreen after having left it; otherwise we exit
709 // presentation.
710 if (m_reenteredFullscreen) {
711 m_isPresenting = false;
712 OnPresentChange();
713 m_fullscreenCheckTimer.stop();
714 if (m_display)
715 m_display->ExitPresent();
716 return;
717 }
718 m_reenteredFullscreen = true;
719 auto canvas = m_layer.source();
720 Document* doc = this->document();
721 std::unique_ptr<UserGestureIndicator> gestureIndicator;
722 if (doc) {
723 gestureIndicator = WTF::wrapUnique(
724 new UserGestureIndicator(DocumentUserGestureToken::create(
725 doc, UserGestureToken::Status::PossiblyExistingGesture)));
726 }
727 Fullscreen::requestFullscreen(*canvas);
728 }
729 }
730
731 ScriptedAnimationController& VRDisplay::ensureScriptedAnimationController( 1165 ScriptedAnimationController& VRDisplay::ensureScriptedAnimationController(
732 Document* doc) { 1166 Document* doc) {
733 if (!m_scriptedAnimationController) 1167 if (!m_scriptedAnimationController)
734 m_scriptedAnimationController = ScriptedAnimationController::create(doc); 1168 m_scriptedAnimationController = ScriptedAnimationController::create(doc);
735 1169
736 return *m_scriptedAnimationController; 1170 return *m_scriptedAnimationController;
737 } 1171 }
738 1172
739 void VRDisplay::dispose() { 1173 void VRDisplay::dispose() {
740 m_binding.Close(); 1174 m_binding.Close();
(...skipping 25 matching lines...) Expand all
766 visitor->trace(m_stageParameters); 1200 visitor->trace(m_stageParameters);
767 visitor->trace(m_eyeParametersLeft); 1201 visitor->trace(m_eyeParametersLeft);
768 visitor->trace(m_eyeParametersRight); 1202 visitor->trace(m_eyeParametersRight);
769 visitor->trace(m_layer); 1203 visitor->trace(m_layer);
770 visitor->trace(m_renderingContext); 1204 visitor->trace(m_renderingContext);
771 visitor->trace(m_scriptedAnimationController); 1205 visitor->trace(m_scriptedAnimationController);
772 visitor->trace(m_pendingPresentResolvers); 1206 visitor->trace(m_pendingPresentResolvers);
773 } 1207 }
774 1208
775 } // namespace blink 1209 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698