Fix the latency model.
We were being too clever in modeling the latency using exponentially
moving average. The issue is that the average changes as new samples come in
which we then use to update the timestamps.
If the sampling rate is high enough (like IMU) then the changes in the average
are close to the delta times between samples. This causes the sample times
to move, and sometimes even change their updated timestamp order. This
causes all kinds of mess when we linearly extrapolate because the slope is bogus.
The fix is to just average a certain number of latency samples and then stick with that
constant average.
Bug: 36997591
Test: Run any 3DOF VR app.
Change-Id: I5411b2a6b7c3f258bf197f0615c0339d68fd2fd7
diff --git a/libs/vr/libvrsensor/include/private/dvr/latency_model.h b/libs/vr/libvrsensor/include/private/dvr/latency_model.h
index 1bb3c4f..40b4638 100644
--- a/libs/vr/libvrsensor/include/private/dvr/latency_model.h
+++ b/libs/vr/libvrsensor/include/private/dvr/latency_model.h
@@ -6,23 +6,21 @@
namespace android {
namespace dvr {
-// This class holds a rolling average of the sensor latency.
+// This class models the latency from sensors. It will look at the first
+// window_size measurements and return their average after that.
class LatencyModel {
public:
- LatencyModel(size_t window_size, double weight_mass_in_window);
+ LatencyModel(size_t window_size);
~LatencyModel() = default;
void AddLatency(int64_t latency_ns);
- int64_t CurrentLatencyEstimate() const {
- return static_cast<int64_t>(rolling_average_);
- }
+ int64_t CurrentLatencyEstimate() const { return latency_; }
private:
- // The rolling average of the latencies.
- double rolling_average_ = 0;
-
- // The alpha parameter for an exponential moving average.
- double alpha_;
+ size_t window_size_;
+ int64_t latency_sum_ = 0;
+ size_t num_summed_ = 0;
+ int64_t latency_ = 0;
};
} // namespace dvr
diff --git a/libs/vr/libvrsensor/latency_model.cpp b/libs/vr/libvrsensor/latency_model.cpp
index 8233889..d3a4521 100644
--- a/libs/vr/libvrsensor/latency_model.cpp
+++ b/libs/vr/libvrsensor/latency_model.cpp
@@ -5,28 +5,19 @@
namespace android {
namespace dvr {
-LatencyModel::LatencyModel(size_t window_size, double weight_mass_in_window) {
- // Compute an alpha so the weight of the last window_size measurements is
- // weight_mass_in_window of the total weights.
-
- // The weight in a series of k measurements:
- // alpha + (1 + (1 - alpha) + (1 - alpha)^2 + ... (1 - alpha)^k-1)
- // = alpha x (1 - (1 - alpha) ^ k) / alpha
- // = 1 - (1 - alpha) ^ k
- // weight_mass_in_window = 1 - (1 - alpha) ^ k / lim_k->inf (1 - alpha) ^ k
- // weight_mass_in_window = 1 - (1 - alpha) ^ k / 1
- // 1 - weight_mass_in_window = (1 - alpha) ^ k
- // log(1 - weight_mass_in_window) = k * log(1 - alpha)
- // 10 ^ (log(1 - weight_mass_in_window) / k) = 1 - alpha
- // alpha = 1 - 10 ^ (log(1 - weight_mass_in_window) / k)
- // alpha = 1 - 10 ^ (log(1 - weight_mass_in_window) / window_size)
-
- alpha_ = 1 - std::pow(10.0, std::log10(1 - weight_mass_in_window) /
- static_cast<double>(window_size));
-}
+LatencyModel::LatencyModel(size_t window_size) : window_size_(window_size) {}
void LatencyModel::AddLatency(int64_t latency_ns) {
- rolling_average_ = latency_ns * alpha_ + rolling_average_ * (1 - alpha_);
+ // Not enough samples yet?
+ if (num_summed_ < window_size_) {
+ // Accumulate.
+ latency_sum_ += latency_ns;
+
+ // Have enough samples for latency estimate?
+ if (++num_summed_ == window_size_) {
+ latency_ = latency_sum_ / window_size_;
+ }
+ }
}
} // namespace dvr
diff --git a/services/vr/sensord/pose_service.cpp b/services/vr/sensord/pose_service.cpp
index 7534732..e3f8171 100644
--- a/services/vr/sensord/pose_service.cpp
+++ b/services/vr/sensord/pose_service.cpp
@@ -65,8 +65,7 @@
static constexpr int kDatasetIdLength = 36;
static constexpr char kDatasetIdChars[] = "0123456789abcdef-";
-static constexpr int kLatencyWindowSize = 100;
-static constexpr double kLatencyWindowMass = 0.5;
+static constexpr int kLatencyWindowSize = 200;
// These are the flags used by BufferProducer::CreatePersistentUncachedBlob,
// plus PRIVATE_ADSP_HEAP to allow access from the DSP.
@@ -115,7 +114,7 @@
photon_timestamp_(0),
// Will be updated by external service, but start with a non-zero value:
display_period_ns_(16000000),
- sensor_latency_(kLatencyWindowSize, kLatencyWindowMass) {
+ sensor_latency_(kLatencyWindowSize) {
last_known_pose_ = {
.orientation = {1.0f, 0.0f, 0.0f, 0.0f},
.translation = {0.0f, 0.0f, 0.0f, 0.0f},