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. |