#include <math.h>
-#include <cutils/log.h>
-
-#include <ui/Fence.h>
+#include <algorithm>
+#include <log/log.h>
#include <utils/String8.h>
#include <utils/Thread.h>
#include <utils/Trace.h>
#include <utils/Vector.h>
+#include <ui/FenceTime.h>
+
#include "DispSync.h"
+#include "SurfaceFlinger.h"
#include "EventLog/EventLog.h"
-#include <algorithm>
-
using std::max;
using std::min;
// present time and the nearest software-predicted vsync.
static const nsecs_t kErrorThreshold = 160000000000; // 400 usec squared
-// This is the offset from the present fence timestamps to the corresponding
-// vsync event.
-static const int64_t kPresentTimeOffset = PRESENT_TIME_OFFSET_FROM_VSYNC_NS;
-
#undef LOG_TAG
#define LOG_TAG "DispSyncThread"
class DispSyncThread: public Thread {
public:
- DispSyncThread(const char* name):
+ explicit DispSyncThread(const char* name):
mName(name),
mStop(false),
mPeriod(0),
return BAD_VALUE;
}
- // This method is only here to handle the kIgnorePresentFences case.
+ // This method is only here to handle the !SurfaceFlinger::hasSyncFramework
+ // case.
bool hasAnyEventListeners() {
if (kTraceDetailedInfo) ATRACE_CALL();
Mutex::Autolock lock(mMutex);
DispSync::DispSync(const char* name) :
mName(name),
mRefreshSkipCount(0),
- mThread(new DispSyncThread(name)) {
+ mThread(new DispSyncThread(name)),
+ mIgnorePresentFences(!SurfaceFlinger::hasSyncFramework){
+ mPresentTimeOffset = SurfaceFlinger::dispSyncPresentTimeOffset;
mThread->run("DispSync", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE);
// set DispSync to SCHED_FIFO to minimize jitter
struct sched_param param = {0};
// Even if we're just ignoring the fences, the zero-phase tracing is
// not needed because any time there is an event registered we will
// turn on the HW vsync events.
- if (!kIgnorePresentFences && kEnableZeroPhaseTracer) {
+ if (!mIgnorePresentFences && kEnableZeroPhaseTracer) {
addEventListener("ZeroPhaseTracer", 0, new ZeroPhaseTracer());
}
}
resetErrorLocked();
}
-bool DispSync::addPresentFence(const sp<Fence>& fence) {
+bool DispSync::addPresentFence(const std::shared_ptr<FenceTime>& fenceTime) {
Mutex::Autolock lock(mMutex);
- mPresentFences[mPresentSampleOffset] = fence;
- mPresentTimes[mPresentSampleOffset] = 0;
+ mPresentFences[mPresentSampleOffset] = fenceTime;
mPresentSampleOffset = (mPresentSampleOffset + 1) % NUM_PRESENT_SAMPLES;
mNumResyncSamplesSincePresent = 0;
- for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) {
- const sp<Fence>& f(mPresentFences[i]);
- if (f != NULL) {
- nsecs_t t = f->getSignalTime();
- if (t < INT64_MAX) {
- mPresentFences[i].clear();
- mPresentTimes[i] = t + kPresentTimeOffset;
- }
- }
- }
-
updateErrorLocked();
return !mModelUpdated || mError > kErrorThreshold;
resetErrorLocked();
}
- if (kIgnorePresentFences) {
+ if (mIgnorePresentFences) {
// If we don't have the sync framework we will never have
// addPresentFence called. This means we have no way to know whether
// or not we're synchronized with the HW vsyncs, so we just request
nsecs_t sqErrSum = 0;
for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) {
- nsecs_t sample = mPresentTimes[i] - mReferenceTime;
- if (sample > mPhase) {
- nsecs_t sampleErr = (sample - mPhase) % period;
- if (sampleErr > period / 2) {
- sampleErr -= period;
- }
- sqErrSum += sampleErr * sampleErr;
- numErrSamples++;
+ // Only check for the cached value of signal time to avoid unecessary
+ // syscalls. It is the responsibility of the DispSync owner to
+ // call getSignalTime() periodically so the cache is updated when the
+ // fence signals.
+ nsecs_t time = mPresentFences[i]->getCachedSignalTime();
+ if (time == Fence::SIGNAL_TIME_PENDING ||
+ time == Fence::SIGNAL_TIME_INVALID) {
+ continue;
+ }
+
+ nsecs_t sample = time - mReferenceTime;
+ if (sample <= mPhase) {
+ continue;
+ }
+
+ nsecs_t sampleErr = (sample - mPhase) % period;
+ if (sampleErr > period / 2) {
+ sampleErr -= period;
}
+ sqErrSum += sampleErr * sampleErr;
+ numErrSamples++;
}
if (numErrSamples > 0) {
mError = sqErrSum / numErrSamples;
+ mZeroErrSamplesCount = 0;
} else {
mError = 0;
+ // Use mod ACCEPTABLE_ZERO_ERR_SAMPLES_COUNT to avoid log spam.
+ mZeroErrSamplesCount++;
+ ALOGE_IF(
+ (mZeroErrSamplesCount % ACCEPTABLE_ZERO_ERR_SAMPLES_COUNT) == 0,
+ "No present times for model error.");
}
if (kTraceDetailedInfo) {
void DispSync::resetErrorLocked() {
mPresentSampleOffset = 0;
mError = 0;
+ mZeroErrSamplesCount = 0;
for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) {
- mPresentFences[i].clear();
- mPresentTimes[i] = 0;
+ mPresentFences[i] = FenceTime::NO_FENCE;
}
}
void DispSync::dump(String8& result) const {
Mutex::Autolock lock(mMutex);
result.appendFormat("present fences are %s\n",
- kIgnorePresentFences ? "ignored" : "used");
+ mIgnorePresentFences ? "ignored" : "used");
result.appendFormat("mPeriod: %" PRId64 " ns (%.3f fps; skipCount=%d)\n",
mPeriod, 1000000000.0 / mPeriod, mRefreshSkipCount);
result.appendFormat("mPhase: %" PRId64 " ns\n", mPhase);
previous = sampleTime;
}
- result.appendFormat("mPresentFences / mPresentTimes [%d]:\n",
+ result.appendFormat("mPresentFences [%d]:\n",
NUM_PRESENT_SAMPLES);
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
- previous = 0;
+ previous = Fence::SIGNAL_TIME_INVALID;
for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) {
size_t idx = (i + mPresentSampleOffset) % NUM_PRESENT_SAMPLES;
- bool signaled = mPresentFences[idx] == NULL;
- nsecs_t presentTime = mPresentTimes[idx];
- if (!signaled) {
+ nsecs_t presentTime = mPresentFences[idx]->getSignalTime();
+ if (presentTime == Fence::SIGNAL_TIME_PENDING) {
result.appendFormat(" [unsignaled fence]\n");
- } else if (presentTime == 0) {
- result.appendFormat(" 0\n");
- } else if (previous == 0) {
+ } else if(presentTime == Fence::SIGNAL_TIME_INVALID) {
+ result.appendFormat(" [invalid fence]\n");
+ } else if (previous == Fence::SIGNAL_TIME_PENDING ||
+ previous == Fence::SIGNAL_TIME_INVALID) {
result.appendFormat(" %" PRId64 " (%.3f ms ago)\n", presentTime,
(now - presentTime) / 1000000.0);
} else {