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