SF: VSyncReactor add event subscription functions
Adds 3 more functions from the DispSync interface to VSyncReactor,
{add,remove}EventListener and changePhaseOffset.
Bug: 140303479
Test: about 10 new units
Change-Id: I2135f87a21cd0e43613367d31e006bb574380bc6
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index f2a7791..49ab6c1 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -14,7 +14,9 @@
* limitations under the License.
*/
+//#define LOG_NDEBUG 0
#include "VSyncReactor.h"
+#include <log/log.h>
#include "TimeKeeper.h"
#include "VSyncDispatch.h"
#include "VSyncTracker.h"
@@ -30,6 +32,84 @@
mTracker(std::move(tracker)),
mPendingLimit(pendingFenceLimit) {}
+VSyncReactor::~VSyncReactor() = default;
+
+// The DispSync interface has a 'repeat this callback at rate' semantic. This object adapts
+// VSyncDispatch's individually-scheduled callbacks so as to meet DispSync's existing semantic
+// for now.
+class CallbackRepeater {
+public:
+ CallbackRepeater(VSyncDispatch& dispatch, DispSync::Callback* cb, const char* name,
+ nsecs_t period, nsecs_t offset, nsecs_t notBefore)
+ : mCallback(cb),
+ mRegistration(dispatch,
+ std::bind(&CallbackRepeater::callback, this, std::placeholders::_1),
+ std::string(name)),
+ mPeriod(period),
+ mOffset(offset),
+ mLastCallTime(notBefore) {}
+
+ ~CallbackRepeater() {
+ std::lock_guard<std::mutex> lk(mMutex);
+ mRegistration.cancel();
+ }
+
+ void start(nsecs_t offset) {
+ std::lock_guard<std::mutex> lk(mMutex);
+ mStopped = false;
+ mOffset = offset;
+
+ // TODO: (b/145213786) check the return code here sensibly
+ mRegistration.schedule(calculateWorkload(), mLastCallTime);
+ }
+
+ void setPeriod(nsecs_t period) {
+ std::lock_guard<std::mutex> lk(mMutex);
+ if (period == mPeriod) {
+ return;
+ }
+ mPeriod = period;
+ }
+
+ void stop() {
+ std::lock_guard<std::mutex> lk(mMutex);
+ LOG_ALWAYS_FATAL_IF(mStopped, "DispSyncInterface misuse: callback already stopped");
+ mStopped = true;
+ mRegistration.cancel();
+ }
+
+private:
+ void callback(nsecs_t vsynctime) {
+ nsecs_t period = 0;
+ {
+ std::lock_guard<std::mutex> lk(mMutex);
+ period = mPeriod;
+ mLastCallTime = vsynctime;
+ }
+
+ mCallback->onDispSyncEvent(vsynctime - period);
+
+ {
+ std::lock_guard<std::mutex> lk(mMutex);
+ mRegistration.schedule(calculateWorkload(), vsynctime);
+ }
+ }
+
+ // DispSync offsets are defined as time after the vsync before presentation.
+ // VSyncReactor workloads are defined as time before the intended presentation vsync.
+ // Note change in sign between the two defnitions.
+ nsecs_t calculateWorkload() REQUIRES(mMutex) { return mPeriod - mOffset; }
+
+ DispSync::Callback* const mCallback;
+
+ std::mutex mutable mMutex;
+ VSyncCallbackRegistration mRegistration GUARDED_BY(mMutex);
+ bool mStopped GUARDED_BY(mMutex) = false;
+ nsecs_t mPeriod GUARDED_BY(mMutex);
+ nsecs_t mOffset GUARDED_BY(mMutex);
+ nsecs_t mLastCallTime GUARDED_BY(mMutex);
+};
+
bool VSyncReactor::addPresentFence(const std::shared_ptr<FenceTime>& fence) {
if (!fence) {
return false;
@@ -92,6 +172,9 @@
{
std::lock_guard<std::mutex> lk(mMutex);
mPeriodChangeInProgress = true;
+ for (auto& entry : mCallbacks) {
+ entry.second->setPeriod(period);
+ }
}
}
@@ -114,4 +197,47 @@
return false;
}
+status_t VSyncReactor::addEventListener(const char* name, nsecs_t phase,
+ DispSync::Callback* callback,
+ nsecs_t /* lastCallbackTime */) {
+ std::lock_guard<std::mutex> lk(mMutex);
+ auto it = mCallbacks.find(callback);
+ if (it == mCallbacks.end()) {
+ // TODO (b/146557561): resolve lastCallbackTime semantics in DispSync i/f.
+ static auto constexpr maxListeners = 3;
+ if (mCallbacks.size() >= maxListeners) {
+ ALOGE("callback %s not added, exceeded callback limit of %i (currently %zu)", name,
+ maxListeners, mCallbacks.size());
+ return NO_MEMORY;
+ }
+
+ auto const period = mTracker->currentPeriod();
+ auto repeater = std::make_unique<CallbackRepeater>(*mDispatch, callback, name, period,
+ phase, mClock->now());
+ it = mCallbacks.emplace(std::pair(callback, std::move(repeater))).first;
+ }
+
+ it->second->start(phase);
+ return NO_ERROR;
+}
+
+status_t VSyncReactor::removeEventListener(DispSync::Callback* callback,
+ nsecs_t* /* outLastCallback */) {
+ std::lock_guard<std::mutex> lk(mMutex);
+ auto const it = mCallbacks.find(callback);
+ LOG_ALWAYS_FATAL_IF(it == mCallbacks.end(), "callback %p not registered", callback);
+
+ it->second->stop();
+ return NO_ERROR;
+}
+
+status_t VSyncReactor::changePhaseOffset(DispSync::Callback* callback, nsecs_t phase) {
+ std::lock_guard<std::mutex> lk(mMutex);
+ auto const it = mCallbacks.find(callback);
+ LOG_ALWAYS_FATAL_IF(it == mCallbacks.end(), "callback was %p not registered", callback);
+
+ it->second->start(phase);
+ return NO_ERROR;
+}
+
} // namespace android::scheduler