Reduce kernel instructions for PowerAdvisor

PowerAdvisor::notifyDisplayUpdateImminent was resetting the screen
update timeout in order to:
1. Ensure some throttling of the power hal so that it is not invoked
   until the display has idled for at least as long as 80ms.
2. Ensure that the expensive rendering hint for the GPU would not be
   unset too early.
Unfortunately this was causing SurfaceFlinger to churn about 3.5% of
total CPU cycles during the bouncy ball test in TouchLatency.apk. The
instructions were coming from posting a semaphore when resetting the
timeout.

We still want to ensure both (1) and (2), so to work around this, we:
a. Keep the screen update timer around, but pull setting
mSendUpdateImminent=false out of the reset callback
b. Try to only reset the screen update timer under two scenarios when we
sent the power hint, which means we're about to update the screen and we
need to reset the timer
c. Keep track of when we last called into notifyDisplayUpdateImminent.
Then when we timeout, sleep for the appropriate amount of time before
disabling expensive rendering.

Effectively the net effect is that we relax the timing requirement for
disabling expensive rendering on the GPU if it was previously enabled,
while clawing back expensive CPU instructions. In a workload emulating a
game or the bouncy ball test, this is the right tradeoff.

Bug: 231628914
Test: simpleperf with bouncy ball
Test: On a Pixel 6, run watch -n 1 adb shell cat
/sys/devices/platform/1c500000.mali/clock_info to ensure that the hint
still gets unapplied.

Change-Id: If8574ddf70a8e88ffdb1cf1bab7fb1c29680071a
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index 20e38d0..1eb8ef3 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -80,22 +80,33 @@
 
 } // namespace
 
-PowerAdvisor::PowerAdvisor(SurfaceFlinger& flinger)
-      : mFlinger(flinger),
-        mUseScreenUpdateTimer(getUpdateTimeout() > 0),
-        mScreenUpdateTimer(
-                "UpdateImminentTimer", OneShotTimer::Interval(getUpdateTimeout()),
-                /* resetCallback */ [this] { mSendUpdateImminent.store(false); },
-                /* timeoutCallback */
-                [this] {
-                    mSendUpdateImminent.store(true);
-                    mFlinger.disableExpensiveRendering();
-                }) {}
+PowerAdvisor::PowerAdvisor(SurfaceFlinger& flinger) : mFlinger(flinger) {
+    if (getUpdateTimeout()) {
+        mScreenUpdateTimer.emplace("UpdateImminentTimer",
+                                   OneShotTimer::Interval(getUpdateTimeout()),
+                                   /* resetCallback */ nullptr,
+                                   /* timeoutCallback */
+                                   [this] {
+                                       const nsecs_t timeSinceLastUpdate =
+                                               systemTime() - mLastScreenUpdatedTime.load();
+                                       if (timeSinceLastUpdate < getUpdateTimeout()) {
+                                           // We may try to disable expensive rendering and allow
+                                           // for sending DISPLAY_UPDATE_IMMINENT hints too early if
+                                           // we idled very shortly after updating the screen, so
+                                           // make sure we wait enough time.
+                                           std::this_thread::sleep_for(std::chrono::nanoseconds(
+                                                   getUpdateTimeout() - timeSinceLastUpdate));
+                                       }
+                                       mSendUpdateImminent.store(true);
+                                       mFlinger.disableExpensiveRendering();
+                                   });
+    }
+}
 
 void PowerAdvisor::init() {
     // Defer starting the screen update timer until SurfaceFlinger finishes construction.
-    if (mUseScreenUpdateTimer) {
-        mScreenUpdateTimer.start();
+    if (mScreenUpdateTimer) {
+        mScreenUpdateTimer->start();
     }
 }
 
@@ -135,7 +146,7 @@
         return;
     }
 
-    if (mSendUpdateImminent.load()) {
+    if (mSendUpdateImminent.exchange(false)) {
         std::lock_guard lock(mPowerHalMutex);
         HalWrapper* const halWrapper = getPowerHal();
         if (halWrapper == nullptr) {
@@ -147,10 +158,18 @@
             mReconnectPowerHal = true;
             return;
         }
+
+        if (mScreenUpdateTimer) {
+            mScreenUpdateTimer->reset();
+        } else {
+            // If we don't have a screen update timer, then we don't throttle power hal calls so
+            // flip this bit back to allow for calling into power hal again.
+            mSendUpdateImminent.store(true);
+        }
     }
 
-    if (mUseScreenUpdateTimer) {
-        mScreenUpdateTimer.reset();
+    if (mScreenUpdateTimer) {
+        mLastScreenUpdatedTime.store(systemTime());
     }
 }