New paste Repaste Download
commit b0a679b048f4055649c12280f7257e5f81e8b913
Author: John Galt <johngaltfirstrun@gmail.com>
Date:   Fri Oct 24 11:57:04 2025 -0400
    app/Composer: vsync improvements
    
     - Use CLOCK_MONOTONIC to solve drift (like when closing app and reopening)
     - Implement an adaptive pacing offset logic, decreasing with consecutive
       hits (8) and increasing immediately on miss to restore stability.
       All tunables are documented, and all but decrease step are adaptive
       based on current period.
     - Improve vsyncPeriod with a sanity check and minimize small rounding
       errors.
    
    Always defaults to 60hz until first period.
diff --git a/app/app/src/main/cpp/ComposerImpl.cpp b/app/app/src/main/cpp/ComposerImpl.cpp
index 3c9c06b..ef69571 100644
--- a/app/app/src/main/cpp/ComposerImpl.cpp
+++ b/app/app/src/main/cpp/ComposerImpl.cpp
@@ -3,6 +3,8 @@
#include <aidlcommonsupport/NativeHandle.h>
#include <android/hardware_buffer.h>
#include <android/native_window.h>
+#include <cmath>
+#include <unistd.h>
#include <private/android/AHardwareBufferHelpers.h>
#include <sys/prctl.h>
#include <utils/Log.h>
@@ -132,7 +134,17 @@ ndk::ScopedAStatus ComposerImpl::setBuffer(int64_t in_displayId, const HardwareB
             // ALOGE("%s: Get NativeWindow Failed!", __FUNCTION__);
             return ndk::ScopedAStatus::ok();
         }
-        *_aidl_return = ANativeWindow_queueBuffer(mDisplays[in_displayId]->nativeWindow, buffer, -1);
+        int64_t intendedVsync = 0;
+        if (mDisplays[in_displayId]->pacerConfigured) {
+            auto times = mDisplays[in_displayId]->schedulePresentLL();
+            intendedVsync = times.second;
+        }
+        int fenceFd = in_acquireFence.get();
+        *_aidl_return = ANativeWindow_queueBuffer(mDisplays[in_displayId]->nativeWindow, buffer, fenceFd);
+        sp<Fence> acquireFence = (fenceFd >= 0) ? new Fence(dup(fenceFd)) : Fence::NO_FENCE;
+        if (mDisplays[in_displayId]->pacerConfigured) {
+            mDisplays[in_displayId]->learnFromFenceOrNowLL(acquireFence, intendedVsync);
+        }
     }
     AHardwareBuffer_release(ahwb);
@@ -199,7 +211,12 @@ void ComposerImpl::onSurfaceChanged(int64_t displayId, sp<Surface> surface, ANat
     displayConfig.height = ANativeWindow_getHeight(nativeWindow);
     displayConfig.dpi.x = dpi;
     displayConfig.dpi.y = dpi;
-    displayConfig.vsyncPeriod = 10E8 / refresh;
+    double rate = static_cast<double>(refresh);
+    if (!(rate > 1.0 && rate < 1000.0)) {
+        rate = 60.0; //60hz default
+    }
+    const int64_t periodNs = static_cast<int64_t>(llround(1000000000.0 / rate));
+    displayConfig.vsyncPeriod = periodNs;
     bool needRefresh = false;
     auto display = mDisplays.find(displayId);
@@ -209,9 +226,15 @@ void ComposerImpl::onSurfaceChanged(int64_t displayId, sp<Surface> surface, ANat
              display->second->displayConfig.height != displayConfig.height)) {
             needRefresh = true;
         }
+        const bool periodChanged = (display->second->displayConfig.vsyncPeriod != displayConfig.vsyncPeriod);
         display->second->nativeWindow = nativeWindow;
         display->second->surface = surface;
         display->second->displayConfig = displayConfig;
+        if (periodChanged) {
+            display->second->mVsyncThread.stop();
+            display->second->mVsyncThread.start(0, displayConfig.vsyncPeriod);
+            display->second->configurePacer(displayConfig.vsyncPeriod);
+        }
     } else {
         ComposerDisplay *targetDisplay = new ComposerDisplay();
         targetDisplay->nativeWindow = nativeWindow;
@@ -225,6 +248,7 @@ void ComposerImpl::onSurfaceChanged(int64_t displayId, sp<Surface> surface, ANat
             mCallbacks->onVsyncReceived(mSequenceId, displayId, timestamp);
         });
         targetDisplay->mVsyncThread.start(0, displayConfig.vsyncPeriod);
+        targetDisplay->configurePacer(displayConfig.vsyncPeriod);
         mDisplays[displayId] = targetDisplay;
     }
diff --git a/app/app/src/main/cpp/ComposerImpl.h b/app/app/src/main/cpp/ComposerImpl.h
index 6bdd1ae..5e429c7 100644
--- a/app/app/src/main/cpp/ComposerImpl.h
+++ b/app/app/src/main/cpp/ComposerImpl.h
@@ -1,6 +1,9 @@
#pragma once
#include <condition_variable>
+#include <atomic>
+#include <chrono>
+#include <ctime>
#include <mutex>
#include <thread>
@@ -23,6 +26,7 @@ using android::Mutex;
using android::sp;
using android::Surface;
using android::SurfaceListener;
+using android::Fence;
namespace aidl {
namespace vendor {
@@ -31,6 +35,71 @@ namespace composer {
typedef std::function<void(int64_t)> vsync_callback_t;
+static inline int64_t monotonic_now_ns() {
+    timespec ts;
+    clock_gettime(CLOCK_MONOTONIC, &ts);
+    return static_cast<int64_t>(ts.tv_sec) * 1000000000LL + ts.tv_nsec;
+}
+static inline void sleep_until_abs_ns(int64_t t) {
+    timespec req{.tv_sec = t / 1000000000LL, .tv_nsec = t % 1000000000LL};
+    while (true) {
+        int r = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &req, nullptr);
+        if (r == 0) return;
+        if (r != EINTR) return;
+    }
+}
+
+struct PresentPacerLL {
+    void configurePeriod(int64_t periodNs) {
+        if (periodNs > 0) mPeriod = periodNs;
+    }
+    int64_t nextSubmitNs(int64_t nowNs) const {
+        const int64_t p = mPeriod > 0 ? mPeriod : 16666666; // 60hz default
+        const int64_t shifted = nowNs + mOffset;
+        const int64_t ticks = shifted >= 0 ? (shifted / p) + 1 : 1;
+        const int64_t nextVsync = ticks * p;
+        return nextVsync - mOffset;
+    }
+    void waitUntil(int64_t t) const { sleep_until_abs_ns(t); }
+    void recordOutcome(bool hit) {
+        if (hit) {
+            // After 8 hits, begin decreasing by 0.1ms
+            if (++mHitStreak >= 8) {
+                const int64_t step = stepNs();
+                mOffset = (mOffset - step > kFloor) ? (mOffset - step) : kFloor;
+                mHitStreak = 0;
+            }
+        } else {
+            // Missed: attempt to regain stability
+            mHitStreak = 0;
+            const int64_t step = stepNs();
+            const int64_t ceil = ceilingNs();
+            const int64_t next = mOffset + 4 * step;
+            mOffset = (next < ceil) ? next : ceil;
+        }
+    }
+    int64_t intendedVsyncForSubmit(int64_t submitNs) const { return submitNs + mOffset; }
+
+    // Tunables
+    static constexpr int64_t kFloor = 200000; // 0.2ms min offset
+    int64_t ceilingNs() const {
+        const int64_t halfP = mPeriod > 0 ? (mPeriod / 2) : 8333333;
+        const int64_t fiveMs = 5000000;
+        return (halfP < fiveMs) ? halfP : fiveMs;
+    }
+    int64_t stepNs() const {
+        const int64_t p = (mPeriod > 0) ? mPeriod : 16666666; // 60hz def
+        int64_t s = p / 80; // 1.25% of period
+        if (s < 50000) s = 50000;   // 0.05 ms floor
+        if (s > 200000) s = 200000; // 0.20 ms cap
+        return s;
+    }
+
+    int64_t mPeriod{16666666};
+    int64_t mOffset{kFloor};
+    int     mHitStreak{0};
+};
+
class VsyncThread {
public:
     static int64_t now();
@@ -63,6 +132,28 @@ struct ComposerDisplay {
     bool plugged;
     sp<SurfaceListener> listener;
     VsyncThread mVsyncThread;
+    PresentPacerLL pacer;
+    bool pacerConfigured{false};
+
+    void configurePacer(int64_t periodNs) {
+        pacer.configurePeriod(periodNs);
+        pacerConfigured = true;
+    }
+    std::pair<int64_t,int64_t> schedulePresentLL() {
+        const int64_t now = monotonic_now_ns();
+        const int64_t submitAt = pacer.nextSubmitNs(now);
+        pacer.waitUntil(submitAt);
+        const int64_t intendedVsync = pacer.intendedVsyncForSubmit(submitAt);
+        return {submitAt, intendedVsync};
+    }
+    void learnFromFenceLL(const sp<Fence>& fence, int64_t intendedVsyncNs) {
+        bool hit = false;
+        if (fence.get() != nullptr) {
+            const int64_t st = fence->getSignalTime();
+            hit = (st >= 0 && st <= intendedVsyncNs);
+        }
+        pacer.recordOutcome(hit);
+    }
};
class ComposerImpl : public BnComposer {
Filename: stdin. Size: 8kb. View raw, , hex, or download this file.

This paste expires on 2025-10-31 16:01:57.358535. Pasted through v1-api.