OSDN Git Service

input: Adjust priority
[android-x86/frameworks-native.git] / services / surfaceflinger / DispSync.cpp
1 /*
2  * Copyright (C) 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
18 //#define LOG_NDEBUG 0
19
20 // This is needed for stdint.h to define INT64_MAX in C++
21 #define __STDC_LIMIT_MACROS
22
23 #include <math.h>
24
25 #include <cutils/iosched_policy.h>
26 #include <cutils/log.h>
27
28 #include <ui/Fence.h>
29
30 #include <utils/String8.h>
31 #include <utils/Thread.h>
32 #include <utils/Trace.h>
33 #include <utils/Vector.h>
34
35 #include "DispSync.h"
36 #include "EventLog/EventLog.h"
37
38 #include <algorithm>
39
40 using std::max;
41 using std::min;
42
43 namespace android {
44
45 // Setting this to true enables verbose tracing that can be used to debug
46 // vsync event model or phase issues.
47 static const bool kTraceDetailedInfo = false;
48
49 // Setting this to true adds a zero-phase tracer for correlating with hardware
50 // vsync events
51 static const bool kEnableZeroPhaseTracer = false;
52
53 // This is the threshold used to determine when hardware vsync events are
54 // needed to re-synchronize the software vsync model with the hardware.  The
55 // error metric used is the mean of the squared difference between each
56 // present time and the nearest software-predicted vsync.
57 static const nsecs_t kErrorThreshold = 160000000000;    // 400 usec squared
58
59 // This is the offset from the present fence timestamps to the corresponding
60 // vsync event.
61 static const int64_t kPresentTimeOffset = PRESENT_TIME_OFFSET_FROM_VSYNC_NS;
62
63 #undef LOG_TAG
64 #define LOG_TAG "DispSyncThread"
65 class DispSyncThread: public Thread {
66 public:
67
68     DispSyncThread(const char* name):
69             mName(name),
70             mStop(false),
71             mPeriod(0),
72             mPhase(0),
73             mReferenceTime(0),
74             mWakeupLatency(0),
75             mFrameNumber(0) {}
76
77     virtual ~DispSyncThread() {}
78
79     void updateModel(nsecs_t period, nsecs_t phase, nsecs_t referenceTime) {
80         if (kTraceDetailedInfo) ATRACE_CALL();
81         Mutex::Autolock lock(mMutex);
82         mPeriod = period;
83         mPhase = phase;
84         mReferenceTime = referenceTime;
85         ALOGV("[%s] updateModel: mPeriod = %" PRId64 ", mPhase = %" PRId64
86                 " mReferenceTime = %" PRId64, mName, ns2us(mPeriod),
87                 ns2us(mPhase), ns2us(mReferenceTime));
88         mCond.signal();
89     }
90
91     void stop() {
92         if (kTraceDetailedInfo) ATRACE_CALL();
93         Mutex::Autolock lock(mMutex);
94         mStop = true;
95         mCond.signal();
96     }
97
98     virtual bool threadLoop() {
99         status_t err;
100         nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
101
102         while (true) {
103             Vector<CallbackInvocation> callbackInvocations;
104
105             nsecs_t targetTime = 0;
106
107             { // Scope for lock
108                 Mutex::Autolock lock(mMutex);
109
110                 if (kTraceDetailedInfo) {
111                     ATRACE_INT64("DispSync:Frame", mFrameNumber);
112                 }
113                 ALOGV("[%s] Frame %" PRId64, mName, mFrameNumber);
114                 ++mFrameNumber;
115
116                 if (mStop) {
117                     return false;
118                 }
119
120                 if (mPeriod == 0) {
121                     err = mCond.wait(mMutex);
122                     if (err != NO_ERROR) {
123                         ALOGE("error waiting for new events: %s (%d)",
124                                 strerror(-err), err);
125                         return false;
126                     }
127                     continue;
128                 }
129
130                 targetTime = computeNextEventTimeLocked(now);
131
132                 bool isWakeup = false;
133
134                 if (now < targetTime) {
135                     if (kTraceDetailedInfo) ATRACE_NAME("DispSync waiting");
136
137                     if (targetTime == INT64_MAX) {
138                         ALOGV("[%s] Waiting forever", mName);
139                         err = mCond.wait(mMutex);
140                     } else {
141                         ALOGV("[%s] Waiting until %" PRId64, mName,
142                                 ns2us(targetTime));
143                         err = mCond.waitRelative(mMutex, targetTime - now);
144                     }
145
146                     if (err == TIMED_OUT) {
147                         isWakeup = true;
148                     } else if (err != NO_ERROR) {
149                         ALOGE("error waiting for next event: %s (%d)",
150                                 strerror(-err), err);
151                         return false;
152                     }
153                 }
154
155                 now = systemTime(SYSTEM_TIME_MONOTONIC);
156
157                 // Don't correct by more than 1.5 ms
158                 static const nsecs_t kMaxWakeupLatency = us2ns(1500);
159
160                 if (isWakeup) {
161                     mWakeupLatency = ((mWakeupLatency * 63) +
162                             (now - targetTime)) / 64;
163                     mWakeupLatency = min(mWakeupLatency, kMaxWakeupLatency);
164                     if (kTraceDetailedInfo) {
165                         ATRACE_INT64("DispSync:WakeupLat", now - targetTime);
166                         ATRACE_INT64("DispSync:AvgWakeupLat", mWakeupLatency);
167                     }
168                 }
169
170                 callbackInvocations = gatherCallbackInvocationsLocked(now);
171             }
172
173             if (callbackInvocations.size() > 0) {
174                 fireCallbackInvocations(callbackInvocations);
175             }
176         }
177
178         return false;
179     }
180
181     status_t addEventListener(const char* name, nsecs_t phase,
182             const sp<DispSync::Callback>& callback) {
183         if (kTraceDetailedInfo) ATRACE_CALL();
184         Mutex::Autolock lock(mMutex);
185
186         for (size_t i = 0; i < mEventListeners.size(); i++) {
187             if (mEventListeners[i].mCallback == callback) {
188                 return BAD_VALUE;
189             }
190         }
191
192         EventListener listener;
193         listener.mName = name;
194         listener.mPhase = phase;
195         listener.mCallback = callback;
196
197         // We want to allow the firstmost future event to fire without
198         // allowing any past events to fire
199         listener.mLastEventTime = systemTime() - mPeriod / 2 + mPhase -
200                 mWakeupLatency;
201
202         mEventListeners.push(listener);
203
204         mCond.signal();
205
206         return NO_ERROR;
207     }
208
209     status_t removeEventListener(const sp<DispSync::Callback>& callback) {
210         if (kTraceDetailedInfo) ATRACE_CALL();
211         Mutex::Autolock lock(mMutex);
212
213         for (size_t i = 0; i < mEventListeners.size(); i++) {
214             if (mEventListeners[i].mCallback == callback) {
215                 mEventListeners.removeAt(i);
216                 mCond.signal();
217                 return NO_ERROR;
218             }
219         }
220
221         return BAD_VALUE;
222     }
223
224     // This method is only here to handle the kIgnorePresentFences case.
225     bool hasAnyEventListeners() {
226         if (kTraceDetailedInfo) ATRACE_CALL();
227         Mutex::Autolock lock(mMutex);
228         return !mEventListeners.empty();
229     }
230
231 private:
232
233     struct EventListener {
234         const char* mName;
235         nsecs_t mPhase;
236         nsecs_t mLastEventTime;
237         sp<DispSync::Callback> mCallback;
238     };
239
240     struct CallbackInvocation {
241         sp<DispSync::Callback> mCallback;
242         nsecs_t mEventTime;
243     };
244
245     nsecs_t computeNextEventTimeLocked(nsecs_t now) {
246         if (kTraceDetailedInfo) ATRACE_CALL();
247         ALOGV("[%s] computeNextEventTimeLocked", mName);
248         nsecs_t nextEventTime = INT64_MAX;
249         for (size_t i = 0; i < mEventListeners.size(); i++) {
250             nsecs_t t = computeListenerNextEventTimeLocked(mEventListeners[i],
251                     now);
252
253             if (t < nextEventTime) {
254                 nextEventTime = t;
255             }
256         }
257
258         ALOGV("[%s] nextEventTime = %" PRId64, mName, ns2us(nextEventTime));
259         return nextEventTime;
260     }
261
262     Vector<CallbackInvocation> gatherCallbackInvocationsLocked(nsecs_t now) {
263         if (kTraceDetailedInfo) ATRACE_CALL();
264         ALOGV("[%s] gatherCallbackInvocationsLocked @ %" PRId64, mName,
265                 ns2us(now));
266
267         Vector<CallbackInvocation> callbackInvocations;
268         nsecs_t onePeriodAgo = now - mPeriod;
269
270         for (size_t i = 0; i < mEventListeners.size(); i++) {
271             nsecs_t t = computeListenerNextEventTimeLocked(mEventListeners[i],
272                     onePeriodAgo);
273
274             if (t < now) {
275                 CallbackInvocation ci;
276                 ci.mCallback = mEventListeners[i].mCallback;
277                 ci.mEventTime = t;
278                 ALOGV("[%s] [%s] Preparing to fire", mName,
279                         mEventListeners[i].mName);
280                 callbackInvocations.push(ci);
281                 mEventListeners.editItemAt(i).mLastEventTime = t;
282             }
283         }
284
285         return callbackInvocations;
286     }
287
288     nsecs_t computeListenerNextEventTimeLocked(const EventListener& listener,
289             nsecs_t baseTime) {
290         if (kTraceDetailedInfo) ATRACE_CALL();
291         ALOGV("[%s] [%s] computeListenerNextEventTimeLocked(%" PRId64 ")",
292                 mName, listener.mName, ns2us(baseTime));
293
294         nsecs_t lastEventTime = listener.mLastEventTime + mWakeupLatency;
295         ALOGV("[%s] lastEventTime: %" PRId64, mName, ns2us(lastEventTime));
296         if (baseTime < lastEventTime) {
297             baseTime = lastEventTime;
298             ALOGV("[%s] Clamping baseTime to lastEventTime -> %" PRId64, mName,
299                     ns2us(baseTime));
300         }
301
302         baseTime -= mReferenceTime;
303         ALOGV("[%s] Relative baseTime = %" PRId64, mName, ns2us(baseTime));
304         nsecs_t phase = mPhase + listener.mPhase;
305         ALOGV("[%s] Phase = %" PRId64, mName, ns2us(phase));
306         baseTime -= phase;
307         ALOGV("[%s] baseTime - phase = %" PRId64, mName, ns2us(baseTime));
308
309         // If our previous time is before the reference (because the reference
310         // has since been updated), the division by mPeriod will truncate
311         // towards zero instead of computing the floor. Since in all cases
312         // before the reference we want the next time to be effectively now, we
313         // set baseTime to -mPeriod so that numPeriods will be -1.
314         // When we add 1 and the phase, we will be at the correct event time for
315         // this period.
316         if (baseTime < 0) {
317             ALOGV("[%s] Correcting negative baseTime", mName);
318             baseTime = -mPeriod;
319         }
320
321         nsecs_t numPeriods = baseTime / mPeriod;
322         ALOGV("[%s] numPeriods = %" PRId64, mName, numPeriods);
323         nsecs_t t = (numPeriods + 1) * mPeriod + phase;
324         ALOGV("[%s] t = %" PRId64, mName, ns2us(t));
325         t += mReferenceTime;
326         ALOGV("[%s] Absolute t = %" PRId64, mName, ns2us(t));
327
328         // Check that it's been slightly more than half a period since the last
329         // event so that we don't accidentally fall into double-rate vsyncs
330         if (t - listener.mLastEventTime < (3 * mPeriod / 5)) {
331             t += mPeriod;
332             ALOGV("[%s] Modifying t -> %" PRId64, mName, ns2us(t));
333         }
334
335         t -= mWakeupLatency;
336         ALOGV("[%s] Corrected for wakeup latency -> %" PRId64, mName, ns2us(t));
337
338         return t;
339     }
340
341     void fireCallbackInvocations(const Vector<CallbackInvocation>& callbacks) {
342         if (kTraceDetailedInfo) ATRACE_CALL();
343         for (size_t i = 0; i < callbacks.size(); i++) {
344             callbacks[i].mCallback->onDispSyncEvent(callbacks[i].mEventTime);
345         }
346     }
347
348     const char* const mName;
349
350     bool mStop;
351
352     nsecs_t mPeriod;
353     nsecs_t mPhase;
354     nsecs_t mReferenceTime;
355     nsecs_t mWakeupLatency;
356
357     int64_t mFrameNumber;
358
359     Vector<EventListener> mEventListeners;
360
361     Mutex mMutex;
362     Condition mCond;
363 };
364
365 #undef LOG_TAG
366 #define LOG_TAG "DispSync"
367
368 class ZeroPhaseTracer : public DispSync::Callback {
369 public:
370     ZeroPhaseTracer() : mParity(false) {}
371
372     virtual void onDispSyncEvent(nsecs_t /*when*/) {
373         mParity = !mParity;
374         ATRACE_INT("ZERO_PHASE_VSYNC", mParity ? 1 : 0);
375     }
376
377 private:
378     bool mParity;
379 };
380
381 DispSync::DispSync(const char* name) :
382         mName(name),
383         mRefreshSkipCount(0),
384         mThread(new DispSyncThread(name)) {
385
386     mThread->run("DispSync", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE);
387     android_set_rt_ioprio(mThread->getTid(), 1);
388
389     reset();
390     beginResync();
391
392     if (kTraceDetailedInfo) {
393         // If we're not getting present fences then the ZeroPhaseTracer
394         // would prevent HW vsync event from ever being turned off.
395         // Even if we're just ignoring the fences, the zero-phase tracing is
396         // not needed because any time there is an event registered we will
397         // turn on the HW vsync events.
398         if (!kIgnorePresentFences && kEnableZeroPhaseTracer) {
399             addEventListener("ZeroPhaseTracer", 0, new ZeroPhaseTracer());
400         }
401     }
402 }
403
404 DispSync::~DispSync() {}
405
406 void DispSync::reset() {
407     Mutex::Autolock lock(mMutex);
408
409     mPhase = 0;
410     mReferenceTime = 0;
411     mModelUpdated = false;
412     mNumResyncSamples = 0;
413     mFirstResyncSample = 0;
414     mNumResyncSamplesSincePresent = 0;
415     resetErrorLocked();
416 }
417
418 bool DispSync::addPresentFence(const sp<Fence>& fence) {
419     Mutex::Autolock lock(mMutex);
420
421     mPresentFences[mPresentSampleOffset] = fence;
422     mPresentTimes[mPresentSampleOffset] = 0;
423     mPresentSampleOffset = (mPresentSampleOffset + 1) % NUM_PRESENT_SAMPLES;
424     mNumResyncSamplesSincePresent = 0;
425
426     for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) {
427         const sp<Fence>& f(mPresentFences[i]);
428         if (f != NULL) {
429             nsecs_t t = f->getSignalTime();
430             if (t < INT64_MAX) {
431                 mPresentFences[i].clear();
432                 mPresentTimes[i] = t + kPresentTimeOffset;
433             }
434         }
435     }
436
437     updateErrorLocked();
438
439     return !mModelUpdated || mError > kErrorThreshold;
440 }
441
442 void DispSync::beginResync() {
443     Mutex::Autolock lock(mMutex);
444     ALOGV("[%s] beginResync", mName);
445     mModelUpdated = false;
446     mNumResyncSamples = 0;
447 }
448
449 bool DispSync::addResyncSample(nsecs_t timestamp) {
450     Mutex::Autolock lock(mMutex);
451
452     ALOGV("[%s] addResyncSample(%" PRId64 ")", mName, ns2us(timestamp));
453
454     size_t idx = (mFirstResyncSample + mNumResyncSamples) % MAX_RESYNC_SAMPLES;
455     mResyncSamples[idx] = timestamp;
456     if (mNumResyncSamples == 0) {
457         mPhase = 0;
458         mReferenceTime = timestamp;
459         ALOGV("[%s] First resync sample: mPeriod = %" PRId64 ", mPhase = 0, "
460                 "mReferenceTime = %" PRId64, mName, ns2us(mPeriod),
461                 ns2us(mReferenceTime));
462         mThread->updateModel(mPeriod, mPhase, mReferenceTime);
463     }
464
465     if (mNumResyncSamples < MAX_RESYNC_SAMPLES) {
466         mNumResyncSamples++;
467     } else {
468         mFirstResyncSample = (mFirstResyncSample + 1) % MAX_RESYNC_SAMPLES;
469     }
470
471     updateModelLocked();
472
473     if (mNumResyncSamplesSincePresent++ > MAX_RESYNC_SAMPLES_WITHOUT_PRESENT) {
474         resetErrorLocked();
475     }
476
477     if (kIgnorePresentFences) {
478         // If we don't have the sync framework we will never have
479         // addPresentFence called.  This means we have no way to know whether
480         // or not we're synchronized with the HW vsyncs, so we just request
481         // that the HW vsync events be turned on whenever we need to generate
482         // SW vsync events.
483         return mThread->hasAnyEventListeners();
484     }
485
486     // Check against kErrorThreshold / 2 to add some hysteresis before having to
487     // resync again
488     bool modelLocked = mModelUpdated && mError < (kErrorThreshold / 2);
489     ALOGV("[%s] addResyncSample returning %s", mName,
490             modelLocked ? "locked" : "unlocked");
491     return !modelLocked;
492 }
493
494 void DispSync::endResync() {
495 }
496
497 status_t DispSync::addEventListener(const char* name, nsecs_t phase,
498         const sp<Callback>& callback) {
499     Mutex::Autolock lock(mMutex);
500     return mThread->addEventListener(name, phase, callback);
501 }
502
503 void DispSync::setRefreshSkipCount(int count) {
504     Mutex::Autolock lock(mMutex);
505     ALOGD("setRefreshSkipCount(%d)", count);
506     mRefreshSkipCount = count;
507     updateModelLocked();
508 }
509
510 status_t DispSync::removeEventListener(const sp<Callback>& callback) {
511     Mutex::Autolock lock(mMutex);
512     return mThread->removeEventListener(callback);
513 }
514
515 void DispSync::setPeriod(nsecs_t period) {
516     Mutex::Autolock lock(mMutex);
517     mPeriod = period;
518     mPhase = 0;
519     mReferenceTime = 0;
520     mThread->updateModel(mPeriod, mPhase, mReferenceTime);
521 }
522
523 nsecs_t DispSync::getPeriod() {
524     // lock mutex as mPeriod changes multiple times in updateModelLocked
525     Mutex::Autolock lock(mMutex);
526     return mPeriod;
527 }
528
529 void DispSync::updateModelLocked() {
530     ALOGV("[%s] updateModelLocked %zu", mName, mNumResyncSamples);
531     if (mNumResyncSamples >= MIN_RESYNC_SAMPLES_FOR_UPDATE) {
532         ALOGV("[%s] Computing...", mName);
533         nsecs_t durationSum = 0;
534         nsecs_t minDuration = INT64_MAX;
535         nsecs_t maxDuration = 0;
536         for (size_t i = 1; i < mNumResyncSamples; i++) {
537             size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES;
538             size_t prev = (idx + MAX_RESYNC_SAMPLES - 1) % MAX_RESYNC_SAMPLES;
539             nsecs_t duration = mResyncSamples[idx] - mResyncSamples[prev];
540             durationSum += duration;
541             minDuration = min(minDuration, duration);
542             maxDuration = max(maxDuration, duration);
543         }
544
545         // Exclude the min and max from the average
546         durationSum -= minDuration + maxDuration;
547         mPeriod = durationSum / (mNumResyncSamples - 3);
548
549         ALOGV("[%s] mPeriod = %" PRId64, mName, ns2us(mPeriod));
550
551         double sampleAvgX = 0;
552         double sampleAvgY = 0;
553         double scale = 2.0 * M_PI / double(mPeriod);
554         // Intentionally skip the first sample
555         for (size_t i = 1; i < mNumResyncSamples; i++) {
556             size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES;
557             nsecs_t sample = mResyncSamples[idx] - mReferenceTime;
558             double samplePhase = double(sample % mPeriod) * scale;
559             sampleAvgX += cos(samplePhase);
560             sampleAvgY += sin(samplePhase);
561         }
562
563         sampleAvgX /= double(mNumResyncSamples - 1);
564         sampleAvgY /= double(mNumResyncSamples - 1);
565
566         mPhase = nsecs_t(atan2(sampleAvgY, sampleAvgX) / scale);
567
568         ALOGV("[%s] mPhase = %" PRId64, mName, ns2us(mPhase));
569
570         if (mPhase < -(mPeriod / 2)) {
571             mPhase += mPeriod;
572             ALOGV("[%s] Adjusting mPhase -> %" PRId64, mName, ns2us(mPhase));
573         }
574
575         if (kTraceDetailedInfo) {
576             ATRACE_INT64("DispSync:Period", mPeriod);
577             ATRACE_INT64("DispSync:Phase", mPhase + mPeriod / 2);
578         }
579
580         // Artificially inflate the period if requested.
581         mPeriod += mPeriod * mRefreshSkipCount;
582
583         mThread->updateModel(mPeriod, mPhase, mReferenceTime);
584         mModelUpdated = true;
585     }
586 }
587
588 void DispSync::updateErrorLocked() {
589     if (!mModelUpdated) {
590         return;
591     }
592
593     // Need to compare present fences against the un-adjusted refresh period,
594     // since they might arrive between two events.
595     nsecs_t period = mPeriod / (1 + mRefreshSkipCount);
596
597     int numErrSamples = 0;
598     nsecs_t sqErrSum = 0;
599
600     for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) {
601         nsecs_t sample = mPresentTimes[i] - mReferenceTime;
602         if (sample > mPhase) {
603             nsecs_t sampleErr = (sample - mPhase) % period;
604             if (sampleErr > period / 2) {
605                 sampleErr -= period;
606             }
607             sqErrSum += sampleErr * sampleErr;
608             numErrSamples++;
609         }
610     }
611
612     if (numErrSamples > 0) {
613         mError = sqErrSum / numErrSamples;
614     } else {
615         mError = 0;
616     }
617
618     if (kTraceDetailedInfo) {
619         ATRACE_INT64("DispSync:Error", mError);
620     }
621 }
622
623 void DispSync::resetErrorLocked() {
624     mPresentSampleOffset = 0;
625     mError = 0;
626     for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) {
627         mPresentFences[i].clear();
628         mPresentTimes[i] = 0;
629     }
630 }
631
632 nsecs_t DispSync::computeNextRefresh(int periodOffset) const {
633     Mutex::Autolock lock(mMutex);
634     nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
635     nsecs_t phase = mReferenceTime + mPhase;
636     return (((now - phase) / mPeriod) + periodOffset + 1) * mPeriod + phase;
637 }
638
639 void DispSync::dump(String8& result) const {
640     Mutex::Autolock lock(mMutex);
641     result.appendFormat("present fences are %s\n",
642             kIgnorePresentFences ? "ignored" : "used");
643     result.appendFormat("mPeriod: %" PRId64 " ns (%.3f fps; skipCount=%d)\n",
644             mPeriod, 1000000000.0 / mPeriod, mRefreshSkipCount);
645     result.appendFormat("mPhase: %" PRId64 " ns\n", mPhase);
646     result.appendFormat("mError: %" PRId64 " ns (sqrt=%.1f)\n",
647             mError, sqrt(mError));
648     result.appendFormat("mNumResyncSamplesSincePresent: %d (limit %d)\n",
649             mNumResyncSamplesSincePresent, MAX_RESYNC_SAMPLES_WITHOUT_PRESENT);
650     result.appendFormat("mNumResyncSamples: %zd (max %d)\n",
651             mNumResyncSamples, MAX_RESYNC_SAMPLES);
652
653     result.appendFormat("mResyncSamples:\n");
654     nsecs_t previous = -1;
655     for (size_t i = 0; i < mNumResyncSamples; i++) {
656         size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES;
657         nsecs_t sampleTime = mResyncSamples[idx];
658         if (i == 0) {
659             result.appendFormat("  %" PRId64 "\n", sampleTime);
660         } else {
661             result.appendFormat("  %" PRId64 " (+%" PRId64 ")\n",
662                     sampleTime, sampleTime - previous);
663         }
664         previous = sampleTime;
665     }
666
667     result.appendFormat("mPresentFences / mPresentTimes [%d]:\n",
668             NUM_PRESENT_SAMPLES);
669     nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
670     previous = 0;
671     for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) {
672         size_t idx = (i + mPresentSampleOffset) % NUM_PRESENT_SAMPLES;
673         bool signaled = mPresentFences[idx] == NULL;
674         nsecs_t presentTime = mPresentTimes[idx];
675         if (!signaled) {
676             result.appendFormat("  [unsignaled fence]\n");
677         } else if (presentTime == 0) {
678             result.appendFormat("  0\n");
679         } else if (previous == 0) {
680             result.appendFormat("  %" PRId64 "  (%.3f ms ago)\n", presentTime,
681                     (now - presentTime) / 1000000.0);
682         } else {
683             result.appendFormat("  %" PRId64 " (+%" PRId64 " / %.3f)  (%.3f ms ago)\n",
684                     presentTime, presentTime - previous,
685                     (presentTime - previous) / (double) mPeriod,
686                     (now - presentTime) / 1000000.0);
687         }
688         previous = presentTime;
689     }
690
691     result.appendFormat("current monotonic time: %" PRId64 "\n", now);
692 }
693
694 } // namespace android