Chromium Code Reviews| Index: chrome/browser/android/vr_shell/non_presenting_gvr_delegate.cc |
| diff --git a/chrome/browser/android/vr_shell/non_presenting_gvr_delegate.cc b/chrome/browser/android/vr_shell/non_presenting_gvr_delegate.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..be28d03854226db5fecad1c2cb428cdd3ef5fd1b |
| --- /dev/null |
| +++ b/chrome/browser/android/vr_shell/non_presenting_gvr_delegate.cc |
| @@ -0,0 +1,142 @@ |
| +// Copyright 2016 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "chrome/browser/android/vr_shell/non_presenting_gvr_delegate.h" |
| + |
| +#include "chrome/browser/android/vr_shell/vr_shell.h" |
| + |
| +namespace vr_shell { |
| + |
| +namespace { |
| +static constexpr long kPredictionTimeWithoutVsyncNanos = 50000000; |
| +} // namespace |
| + |
| +NonPresentingGvrDelegate::NonPresentingGvrDelegate(long context) |
| + : task_runner_(base::ThreadTaskRunnerHandle::Get()), |
| + binding_(this), |
| + weak_ptr_factory_(this) { |
| + gvr_api_ = gvr::GvrApi::WrapNonOwned(reinterpret_cast<gvr_context*>(context)); |
| +} |
| + |
| +NonPresentingGvrDelegate::~NonPresentingGvrDelegate() { |
| + StopVSyncLoop(); |
| + if (binding_.is_bound()) |
| + binding_.Close(); |
|
dcheng
2017/01/18 23:46:06
Nit: Close() already checks this, so just uncondit
mthiesse
2017/01/19 00:19:05
haha it probably does, I was erring a little too f
|
| +} |
| + |
| +void NonPresentingGvrDelegate::OnVRVsyncProviderRequest( |
| + device::mojom::VRVSyncProviderRequest request) { |
| + if (binding_.is_bound()) |
| + binding_.Close(); |
| + binding_.Bind(std::move(request)); |
| + binding_.set_connection_error_handler( |
| + base::Bind(&NonPresentingGvrDelegate::StopVSyncLoop, |
| + weak_ptr_factory_.GetWeakPtr())); |
| + StartVSyncLoop(); |
| +} |
| + |
| +void NonPresentingGvrDelegate::Pause() { |
| + vsync_task_.Cancel(); |
| + paused_ = true; |
| + gvr_api_->PauseTracking(); |
| +} |
| + |
| +void NonPresentingGvrDelegate::Resume() { |
| + if (!paused_) |
| + return; |
| + paused_ = false; |
| + StartVSyncLoop(); |
| +} |
| + |
| +device::mojom::VRVSyncProviderRequest |
| +NonPresentingGvrDelegate::OnSwitchToPresentingDelegate() { |
| + StopVSyncLoop(); |
| + if (binding_.is_bound()) |
| + return binding_.Unbind(); |
| + return nullptr; |
| +} |
| + |
| +void NonPresentingGvrDelegate::StopVSyncLoop() { |
| + vsync_task_.Cancel(); |
| + if (!callback_.is_null()) |
| + callback_.Run(nullptr, base::TimeDelta()); |
| + callback_.Reset(); |
| + gvr_api_->PauseTracking(); |
| + paused_ = false; |
|
dcheng
2017/01/18 23:46:06
Nit: minor comment about why paused_ = false is co
mthiesse
2017/01/19 00:19:05
Renamed and added clarifying comment.
|
| +} |
| + |
| +void NonPresentingGvrDelegate::StartVSyncLoop() { |
| + vsync_task_.Reset( |
| + base::Bind(&NonPresentingGvrDelegate::OnVSync, base::Unretained(this))); |
| + gvr_api_->RefreshViewerProfile(); |
| + gvr_api_->ResumeTracking(); |
| + OnVSync(); |
| +} |
| + |
| +void NonPresentingGvrDelegate::OnVSync() { |
| + base::TimeTicks now = base::TimeTicks::Now(); |
| + base::TimeTicks target; |
| + |
| + // Don't run the VSync loop if we're not bound. |
| + if (!binding_.is_bound()) { |
| + return; |
| + } |
| + |
| + // Don't send VSyncs until we have a timebase/interval. |
| + if (vsync_interval_.is_zero()) |
| + return; |
| + target = now + vsync_interval_; |
| + int64_t intervals = (target - vsync_timebase_) / vsync_interval_; |
| + target = vsync_timebase_ + intervals * vsync_interval_; |
| + if (!vsync_task_.IsCancelled()) { |
| + task_runner_->PostDelayedTask(FROM_HERE, vsync_task_.callback(), |
| + target - now); |
| + } |
| + |
| + base::TimeDelta time = intervals * vsync_interval_; |
| + if (!callback_.is_null()) { |
| + callback_.Run(GetPose(), time); |
| + callback_.Reset(); |
| + } else { |
| + pending_vsync_ = true; |
| + pending_time_ = time; |
| + } |
| +} |
| + |
| +void NonPresentingGvrDelegate::GetVSync(const GetVSyncCallback& callback) { |
| + if (!pending_vsync_) { |
| + if (!callback_.is_null()) { |
| + mojo::ReportBadMessage("Requested VSync before waiting for response to " |
| + "previous request."); |
| + return; |
| + } |
| + callback_ = std::move(callback); |
|
dcheng
2017/01/18 23:46:06
Nit: |callback| is a const ref, so std::move() doe
mthiesse
2017/01/19 00:19:05
Done.
|
| + return; |
| + } |
| + pending_vsync_ = false; |
| + callback.Run(GetPose(), pending_time_); |
| +} |
| + |
| +void NonPresentingGvrDelegate::UpdateVSyncInterval(long timebase_nanos, |
| + double interval_seconds) { |
| + vsync_timebase_ = base::TimeTicks(); |
| + vsync_timebase_ += base::TimeDelta::FromMicroseconds(timebase_nanos / 1000); |
| + vsync_interval_ = base::TimeDelta::FromSecondsD(interval_seconds); |
| + StartVSyncLoop(); |
| +} |
| + |
| +device::mojom::VRPosePtr NonPresentingGvrDelegate::GetPose() { |
| + gvr::ClockTimePoint target_time = gvr::GvrApi::GetTimePointNow(); |
| + target_time.monotonic_system_time_nanos += kPredictionTimeWithoutVsyncNanos; |
| + |
| + gvr::Mat4f head_mat = |
| + gvr_api_->GetHeadSpaceFromStartSpaceRotation(target_time); |
| + head_mat = gvr_api_->ApplyNeckModel(head_mat, 1.0f); |
| + |
| + uint32_t pose_index = pose_index_++; |
| + |
| + return VrShell::VRPosePtrFromGvrPose(head_mat, pose_index); |
| +} |
| + |
| +} // namespace vr_shell |