| Index: third_party/WebKit/Source/modules/vr/VRDisplay.cpp | 
| diff --git a/third_party/WebKit/Source/modules/vr/VRDisplay.cpp b/third_party/WebKit/Source/modules/vr/VRDisplay.cpp | 
| index 3201ab4b09aef52ea62219a363d4ac17c5b6a456..7296ffccc326d9a1ee3b610f0237ca7789686bb0 100644 | 
| --- a/third_party/WebKit/Source/modules/vr/VRDisplay.cpp | 
| +++ b/third_party/WebKit/Source/modules/vr/VRDisplay.cpp | 
| @@ -129,12 +129,33 @@ VREyeParameters* VRDisplay::getEyeParameters(const String& which_eye) { | 
| } | 
| } | 
|  | 
| -int VRDisplay::requestAnimationFrame(FrameRequestCallback* callback) { | 
| -  DVLOG(2) << __FUNCTION__; | 
| -  Document* doc = this->GetDocument(); | 
| -  if (!doc) | 
| -    return 0; | 
| -  pending_vrdisplay_raf_ = true; | 
| +void VRDisplay::RequestVSync() { | 
| +  DVLOG(2) << __FUNCTION__ | 
| +           << " start: pending_vrdisplay_raf_=" << pending_vrdisplay_raf_ | 
| +           << " in_animation_frame_=" << in_animation_frame_ | 
| +           << " did_submit_this_frame_=" << did_submit_this_frame_; | 
| + | 
| +  // The logic here is a bit subtle. We get called from one of the following | 
| +  // four contexts: | 
| +  // | 
| +  // (a) from requestAnimationFrame if outside an animating context (i.e. the | 
| +  //     first rAF call from inside a getVRDisplays() promise) | 
| +  // | 
| +  // (b) from requestAnimationFrame in an animating context if the JS code | 
| +  //     calls rAF after submitFrame. | 
| +  // | 
| +  // (c) from submitFrame if that is called after rAF. | 
| +  // | 
| +  // (d) from ProcessScheduledAnimations if a rAF callback finishes without | 
| +  //     submitting a frame. | 
| +  // | 
| +  // These cases are mutually exclusive which prevents duplicate RequestVSync | 
| +  // calls. Case (a) only applies outside an animating context | 
| +  // (in_animation_frame_ is false), and (b,c,d) all require an animating | 
| +  // context. While in an animating context, submitFrame is called either | 
| +  // before rAF (b), after rAF (c), or not at all (d). If rAF isn't called at | 
| +  // all, there won't be future frames. | 
| + | 
| if (!vr_v_sync_provider_.is_bound()) { | 
| ConnectVSyncProvider(); | 
| } else if (!display_blurred_ && !pending_vsync_) { | 
| @@ -142,6 +163,27 @@ int VRDisplay::requestAnimationFrame(FrameRequestCallback* callback) { | 
| vr_v_sync_provider_->GetVSync(ConvertToBaseCallback( | 
| WTF::Bind(&VRDisplay::OnVSync, WrapWeakPersistent(this)))); | 
| } | 
| + | 
| +  DVLOG(2) << __FUNCTION__ << " done:" | 
| +           << " vr_v_sync_provider_.is_bound()=" | 
| +           << vr_v_sync_provider_.is_bound() | 
| +           << " pending_vsync_=" << pending_vsync_; | 
| +} | 
| + | 
| +int VRDisplay::requestAnimationFrame(FrameRequestCallback* callback) { | 
| +  DVLOG(2) << __FUNCTION__; | 
| +  Document* doc = this->GetDocument(); | 
| +  if (!doc) | 
| +    return 0; | 
| +  pending_vrdisplay_raf_ = true; | 
| + | 
| +  // We want to delay the GetVSync call while presenting to ensure it doesn't | 
| +  // arrive earlier than frame submission, but other than that we want to call | 
| +  // it as early as possible. See comments inside RequestVSync() for more | 
| +  // details on the applicable cases. | 
| +  if (!in_animation_frame_ || did_submit_this_frame_) { | 
| +    RequestVSync(); | 
| +  } | 
| callback->use_legacy_time_base_ = false; | 
| return EnsureScriptedAnimationController(doc).RegisterCallback(callback); | 
| } | 
| @@ -487,6 +529,8 @@ HeapVector<VRLayer> VRDisplay::getLayers() { | 
| } | 
|  | 
| void VRDisplay::submitFrame() { | 
| +  DVLOG(2) << __FUNCTION__; | 
| + | 
| if (!display_) | 
| return; | 
| TRACE_EVENT1("gpu", "submitFrame", "frame", vr_frame_id_); | 
| @@ -627,6 +671,11 @@ void VRDisplay::submitFrame() { | 
| gpu::MailboxHolder(mailbox, sync_token, GL_TEXTURE_2D)); | 
| TRACE_EVENT_END0("gpu", "VRDisplay::SubmitFrame"); | 
|  | 
| +  did_submit_this_frame_ = true; | 
| +  // If we were deferring a rAF-triggered vsync request, do this now. | 
| +  if (pending_vrdisplay_raf_) | 
| +    RequestVSync(); | 
| + | 
| // If preserveDrawingBuffer is false, must clear now. Normally this | 
| // happens as part of compositing, but that's not active while | 
| // presenting, so run the responsible code directly. | 
| @@ -647,10 +696,12 @@ void VRDisplay::submitFrame() { | 
| } | 
|  | 
| void VRDisplay::OnSubmitFrameTransferred() { | 
| +  DVLOG(3) << __FUNCTION__; | 
| pending_submit_frame_ = false; | 
| } | 
|  | 
| void VRDisplay::OnSubmitFrameRendered() { | 
| +  DVLOG(3) << __FUNCTION__; | 
| pending_previous_frame_render_ = false; | 
| } | 
|  | 
| @@ -712,6 +763,7 @@ void VRDisplay::StopPresenting() { | 
| context_gl_ = nullptr; | 
| pending_submit_frame_ = false; | 
| pending_previous_frame_render_ = false; | 
| +  did_submit_this_frame_ = false; | 
| } | 
|  | 
| void VRDisplay::OnActivate(device::mojom::blink::VRDisplayEventReason reason, | 
| @@ -776,9 +828,19 @@ void VRDisplay::ProcessScheduledAnimations(double timestamp) { | 
| // that may be called later. | 
| AutoReset<bool> animating(&in_animation_frame_, true); | 
| pending_vrdisplay_raf_ = false; | 
| +    did_submit_this_frame_ = false; | 
| scripted_animation_controller_->ServiceScriptedAnimations(timestamp); | 
| +    if (pending_vrdisplay_raf_ && !did_submit_this_frame_) { | 
| +      DVLOG(2) << __FUNCTION__ << ": vrDisplay.rAF did not submit a frame"; | 
| +      RequestVSync(); | 
| +    } | 
| } | 
|  | 
| +  // Sanity check: If pending_vrdisplay_raf_ is true and the vsync provider | 
| +  // is connected, we must now have a pending vsync. | 
| +  DCHECK(!pending_vrdisplay_raf_ || !vr_v_sync_provider_.is_bound() || | 
| +         pending_vsync_); | 
| + | 
| // For GVR, we shut down normal vsync processing during VR presentation. | 
| // Trigger any callbacks on window.rAF manually so that they run after | 
| // completing the vrDisplay.rAF processing. | 
|  |