Index: device/vr/android/gvr/gvr_device.cc |
diff --git a/device/vr/android/gvr/gvr_device.cc b/device/vr/android/gvr/gvr_device.cc |
index 9dc6b39c8118bd81fb277020ed7047060d877c2d..a6505d082b495e357a0129f437bfd01561ff1abd 100644 |
--- a/device/vr/android/gvr/gvr_device.cc |
+++ b/device/vr/android/gvr/gvr_device.cc |
@@ -21,14 +21,64 @@ namespace device { |
namespace { |
-static const uint64_t kPredictionTimeWithoutVsyncNanos = 50000000; |
+// TODO(klausw): adjust this based on historic render times |
+static const double kDefaultRenderTimeMillis = 15.0; |
+static const double kPredictionTimeWithoutVsyncMillis = 24.0; |
} // namespace |
-GvrDevice::GvrDevice(GvrDeviceProvider* provider, GvrDelegate* delegate) |
- : VRDevice(), delegate_(delegate), gvr_provider_(provider) {} |
+// THIS IS A COPY, keep in sync with VRDisplay.cpp ? |
+StatTracker::StatTracker(unsigned int capacity) : m_capacity(capacity) {} |
-GvrDevice::~GvrDevice() {} |
+StatTracker::~StatTracker() = default; |
+ |
+void StatTracker::add(double item) { |
+ if (m_items.size() >= m_capacity) { |
+ m_items.pop_front(); |
+ } |
+ m_items.push_back(item); |
+} |
+ |
+void StatTracker::clear() { |
+ m_items.clear(); |
+} |
+ |
+bool StatTracker::hasPrediction() { |
+ return m_items.size() > 0; |
+} |
+ |
+double StatTracker::getPrediction() { |
+ assert(hasPrediction()); |
+ |
+ // If we have 3 or more items, ignore min and max outliers and |
+ // average the rest. For 2 or less, minmax.first and minmax.second |
+ // will both be m_items.end(), so it's just a plain average. |
+ auto minmax = m_items.size() > 2 ? |
+ std::minmax_element(m_items.begin(), m_items.end()) : |
+ std::minmax_element(m_items.end(), m_items.end()); |
+ |
+ double sum = 0.0; |
+ int count = 0; |
+ //VLOG(2) << __FUNCTION__ << ": stat start"; |
+ for (auto it = m_items.begin(); it != m_items.end(); ++it) { |
+ //VLOG(2) << __FUNCTION__ << ": val=" << *it; |
+ if (it == minmax.first || it == minmax.second) continue; |
+ sum += *it; |
+ ++count; |
+ } |
+ //VLOG(2) << __FUNCTION__ << ": stat return " << sum / count; |
+ return sum / count; |
+} |
+// COPY end |
+ |
+GvrDevice::GvrDevice(GvrDeviceProvider* provider, GvrDelegate* delegate) |
+ : VRDevice(), delegate_(delegate), gvr_provider_(provider) { |
+ VLOG(1) << __FUNCTION__ << ": CONSTRUCTOR this=" << (void*)this << " ***********************************************************************************************************"; |
+} |
+ |
+GvrDevice::~GvrDevice() { |
+ VLOG(1) << __FUNCTION__ << ": DESTRUCTOR this=" << (void*)this << " ***********************************************************************************************************"; |
artem.bolgar
2017/02/14 05:04:24
It is possible that callback_map_ is not empty her
|
+} |
mojom::VRDisplayInfoPtr GvrDevice::GetVRDevice() { |
TRACE_EVENT0("input", "GvrDevice::GetVRDevice"); |
@@ -65,9 +115,12 @@ mojom::VRDisplayInfoPtr GvrDevice::GetVRDevice() { |
right_eye->renderHeight = left_eye->renderHeight; |
gvr::GvrApi* gvr_api = GetGvrApi(); |
+ VLOG(1) << __FUNCTION__ << ": gvr_api=" << gvr_api; |
if (!gvr_api) { |
// We may not be able to get an instance of GvrApi right away, so |
// stub in some data till we have one. |
+ |
+ // TODO(klausw): I think we never get here ?! |
device->displayName = "Unknown"; |
left_eye->fieldOfView->upDegrees = 45; |
@@ -88,30 +141,34 @@ mojom::VRDisplayInfoPtr GvrDevice::GetVRDevice() { |
right_eye->offset[1] = 0.0; |
right_eye->offset[2] = 0.03; |
+#if 0 |
// Tell the delegate not to draw yet, to avoid a race condition |
// (and visible wobble) on entering VR. |
if (delegate_) { |
delegate_->SetWebVRRenderSurfaceSize(kInvalidRenderTargetSize.width, |
kInvalidRenderTargetSize.height); |
} |
- |
+#endif |
return device; |
} |
- // In compositor mode, we have to use the current compositor window's |
- // surface size. Would be nice to change it, but that needs more browser |
- // internals to be modified. TODO(klausw,crbug.com/655722): remove this once |
- // we can pick our own surface size. |
- gvr::Sizei compositor_size = delegate_->GetWebVRCompositorSurfaceSize(); |
- left_eye->renderWidth = compositor_size.width / 2; |
- left_eye->renderHeight = compositor_size.height; |
+ render_target_size = gvr_api->GetMaximumEffectiveRenderTargetSize(); |
+ // Render at less than the maximum effective render target size as a |
+ // compromise between image quality and performance, similar to |
+ // current polyfill. TODO(klausw): allow clients to override this to |
+ // get full resolution. |
+ left_eye->renderWidth = render_target_size.width * 55 / 100 / 2; |
+ left_eye->renderHeight = render_target_size.height * 55 / 100; |
right_eye->renderWidth = left_eye->renderWidth; |
right_eye->renderHeight = left_eye->renderHeight; |
+ LOG(INFO) << "klausw: render_target_size=" << render_target_size.width << "x" |
+ << render_target_size.height; |
std::string vendor = gvr_api->GetViewerVendor(); |
std::string model = gvr_api->GetViewerModel(); |
device->displayName = vendor + " " + model; |
+ VLOG(1) << __FUNCTION__ << ": gvr_api creating buffer viewports"; |
gvr::BufferViewportList gvr_buffer_viewports = |
gvr_api->CreateEmptyBufferViewportList(); |
gvr_buffer_viewports.SetToRecommendedBufferViewports(); |
@@ -142,24 +199,24 @@ mojom::VRDisplayInfoPtr GvrDevice::GetVRDevice() { |
right_eye->offset[1] = -right_eye_mat.m[1][3]; |
right_eye->offset[2] = -right_eye_mat.m[2][3]; |
- if (delegate_) { |
- delegate_->SetWebVRRenderSurfaceSize(2 * left_eye->renderWidth, |
- left_eye->renderHeight); |
- } |
- |
return device; |
} |
mojom::VRPosePtr GvrDevice::GetPose() { |
TRACE_EVENT0("input", "GvrDevice::GetSensorState"); |
- mojom::VRPosePtr pose = mojom::VRPose::New(); |
- |
- pose->timestamp = base::Time::Now().ToJsTime(); |
- |
// Increment pose frame counter always, even if it's a faked pose. |
- pose->poseIndex = ++pose_index_; |
+ ++pose_index_; |
+ // Don't allow an actual pose index to be negative or zero, those |
+ // are reserved for error or "no pose" states. |
+ if (pose_index_ <= 0) pose_index_ = 1; |
+ VLOG(2) << __FUNCTION__ << ": frame " << pose_index_; |
+ TRACE_EVENT1("media", "klausw:GetPose", "frame", pose_index_); |
+ |
+ mojom::VRPosePtr pose = mojom::VRPose::New(); |
+ pose->timestamp = base::Time::Now().ToJsTime(); |
+ pose->poseIndex = pose_index_; |
pose->orientation.emplace(4); |
gvr::GvrApi* gvr_api = GetGvrApi(); |
@@ -173,11 +230,26 @@ mojom::VRPosePtr GvrDevice::GetPose() { |
return pose; |
} |
- if (!delegate_) |
+ if (!delegate_) { |
+ VLOG(2) << __FUNCTION__ << ": no delegate, no pose!"; |
return nullptr; |
+ } |
gvr::ClockTimePoint target_time = gvr::GvrApi::GetTimePointNow(); |
- target_time.monotonic_system_time_nanos += kPredictionTimeWithoutVsyncNanos; |
+ int64_t pose_time = target_time.monotonic_system_time_nanos; |
+ |
+#if 0 |
+ int64_t nstime = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now().time_since_epoch()).count(); |
+ LOG(INFO) << "klausw: gvr ns=" << pose_time << " steady ns=" << steady_nanos << " delta=" << pose_time - nstime; |
+#endif |
+ |
+ // TODO(klausw): sanity check |
+ double render_time_ms = last_processing_ms_.hasPrediction() && last_render_ms_.hasPrediction() ? |
+ last_processing_ms_.getPrediction() + last_render_ms_.getPrediction() : |
+ kDefaultRenderTimeMillis; |
+ double predict_time_ms = render_time_ms + kPredictionTimeWithoutVsyncMillis; |
+ VLOG(2) << __FUNCTION__ << ": predict_time_ms=" << predict_time_ms; |
+ target_time.monotonic_system_time_nanos += static_cast<uint64_t>(predict_time_ms * 1e6); |
gvr::Mat4f head_mat = |
gvr_api->GetHeadSpaceFromStartSpaceRotation(target_time); |
@@ -207,7 +279,7 @@ mojom::VRPosePtr GvrDevice::GetPose() { |
// Save the underlying GVR pose for use by rendering. It can't use a |
// VRPosePtr since that's a different data type. |
- delegate_->SetGvrPoseForWebVr(head_mat, pose_index_); |
+ delegate_->SetWebVRGvrPose(head_mat, pose_index_, pose_time); |
return pose; |
} |
@@ -232,13 +304,50 @@ void GvrDevice::SetSecureOrigin(bool secure_origin) { |
} |
void GvrDevice::ExitPresent() { |
+ // Run pending "frame complete" callbacks |
+ for (auto it : callback_map_) { |
artem.bolgar
2017/02/14 05:04:24
This cleanup code block should be moved to GvrDevi
|
+ VLOG(2) << __FUNCTION__ << ": ExitPresent running pending callback for frame: " << it.first; |
+ it.second.Run(0, it.first, -1.0); |
+ } |
+ callback_map_.clear(); |
gvr_provider_->ExitPresent(); |
OnExitPresent(); |
} |
-void GvrDevice::SubmitFrame(mojom::VRPosePtr pose) { |
- if (delegate_) |
- delegate_->SubmitWebVRFrame(); |
+void GvrDevice::SubmitFrame(int32_t surfaceHandle, |
+ mojom::VRPosePtr pose, |
+ const mojom::VRDisplay::SubmitFrameCallback& callback) { |
+ TRACE_EVENT1("media", "klausw:SubmitFrame", "frame", pose->poseIndex); |
+ VLOG(2) << __FUNCTION__ << ": frame " << pose->poseIndex; |
+ if (delegate_) { |
+ VLOG(2) << __FUNCTION__ << ": save callback for frame index " << pose->poseIndex; |
+ callback_map_.insert(std::make_pair(pose->poseIndex, std::move(callback))); |
+ last_processing_ms_.add(pose->ts_submit - pose->ts_getPose); |
+ delegate_->SubmitWebVRFrame(surfaceHandle, std::move(pose)); |
+ } else { |
+ VLOG(2) << __FUNCTION__ << ": No delegate, calling callback now for frame index " << pose->poseIndex; |
+ callback.Run(surfaceHandle, pose->poseIndex, -1.0); |
+ VLOG(2) << __FUNCTION__ << ": run callback done"; |
+ } |
+} |
+ |
+void GvrDevice::OnFrameSubmitted(int32_t surfaceHandle, uint32_t frame_index, double elapsed) { |
+ auto it = callback_map_.find(frame_index); |
+ if (it == callback_map_.end()) { |
+ VLOG(2) << __FUNCTION__ << ": no callback found for frame index " << frame_index; |
+ } else { |
+ VLOG(2) << __FUNCTION__ << ": run callback for frame index " << frame_index; |
+ auto callback = std::move(it->second); |
+ callback.Run(surfaceHandle, frame_index, elapsed); |
+ last_render_ms_.add(elapsed); |
+ VLOG(2) << __FUNCTION__ << ": run callback done, erasing"; |
+ callback_map_.erase(it); |
+ VLOG(2) << __FUNCTION__ << ": erasing done"; |
+ } |
+ |
+ for (auto it : callback_map_) { |
+ VLOG(2) << __FUNCTION__ << ": pending frame: " << it.first; |
+ } |
} |
void GvrDevice::UpdateLayerBounds(mojom::VRLayerBoundsPtr left_bounds, |
@@ -258,7 +367,16 @@ void GvrDevice::UpdateLayerBounds(mojom::VRLayerBoundsPtr left_bounds, |
right_gvr_bounds.right = right_bounds->left + right_bounds->width; |
right_gvr_bounds.bottom = 1.0f - (right_bounds->top + right_bounds->height); |
- delegate_->UpdateWebVRTextureBounds(left_gvr_bounds, right_gvr_bounds); |
+ // TODO(klausw): tie this to pose_index_ for future execution |
+ delegate_->UpdateWebVRTextureBounds(left_bounds->forPoseIndex, left_gvr_bounds, right_gvr_bounds); |
+} |
+ |
+void GvrDevice::GetSurfaceHandle(int32_t width, int32_t height, const mojom::VRDisplay::GetSurfaceHandleCallback& callback) { |
+ // LOG(ERROR) << "klausw:GvrDevice.GetSurfaceHandle, delegate=" << delegate_; |
+ if (!delegate_) |
+ callback.Run(0); |
+ |
+ delegate_->GetWebVRSurfaceHandle(width, height, callback); |
} |
void GvrDevice::SetDelegate(GvrDelegate* delegate) { |