Correct DispSync model when registering listeners

* First event should be mapped to a predicted last event time
* Phase offset timing should be lock-step with the period update.

Bug: 124383894
Test: systrace
Change-Id: I742b30a8d28780a44592c4d3077f33d23cf65789
diff --git a/services/surfaceflinger/Scheduler/DispSync.cpp b/services/surfaceflinger/Scheduler/DispSync.cpp
index 798d1a4..5b08aab 100644
--- a/services/surfaceflinger/Scheduler/DispSync.cpp
+++ b/services/surfaceflinger/Scheduler/DispSync.cpp
@@ -201,8 +201,26 @@
         listener.mCallback = callback;
 
         // We want to allow the firstmost future event to fire without
-        // allowing any past events to fire
-        listener.mLastEventTime = systemTime() - mPeriod / 2 + mPhase - mWakeupLatency;
+        // allowing any past events to fire. To do this extrapolate from
+        // mReferenceTime the most recent hardware vsync, and pin the
+        // last event time there.
+        const nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+        if (mPeriod != 0) {
+            const nsecs_t baseTime = now - mReferenceTime;
+            const nsecs_t numPeriodsSinceReference = baseTime / mPeriod;
+            const nsecs_t predictedReference = mReferenceTime + numPeriodsSinceReference * mPeriod;
+            const nsecs_t phaseCorrection = mPhase + listener.mPhase;
+            const nsecs_t predictedLastEventTime = predictedReference + phaseCorrection;
+            if (predictedLastEventTime > now) {
+                // Make sure that the last event time does not exceed the current time.
+                // If it would, then back the last event time by a period.
+                listener.mLastEventTime = predictedLastEventTime - mPeriod;
+            } else {
+                listener.mLastEventTime = predictedLastEventTime;
+            }
+        } else {
+            listener.mLastEventTime = now + mPhase - mWakeupLatency;
+        }
 
         mEventListeners.push_back(listener);