/** @file Stopwatch.cpp @maintainer Morgan McGuire, http://graphics.cs.williams.edu @created 2005-10-05 @edited 2009-03-14 Copyright 2000-2009, Morgan McGuire. All rights reserved. */ #include "G3D/Stopwatch.h" #include "G3D/System.h" namespace G3D { Stopwatch::Stopwatch(const std::string& myName) : myName(myName), inBetween(false), lastTockTime(-1), lastDuration(0), lastCycleCount(0), m_fps(0), emwaFPS(0), m_smoothFPS(0), emwaDuration(0) { computeOverhead(); reset(); } void Stopwatch::computeOverhead() { cycleOverhead = 0; tick(); tock(); cycleOverhead = elapsedCycles(); } void Stopwatch::tick() { // This is 'alwaysAssert' instead of 'debugAssert' // since people rarely profile in debug mode. alwaysAssertM(! inBetween, "Stopwatch::tick() called twice in a row."); inBetween = true; // We read RDTSC twice here, but it is more abstract to implement this // way and at least we're reading the cycle count last. timeStart = System::time(); System::beginCycleCount(cycleStart); } void Stopwatch::tock() { System::endCycleCount(cycleStart); RealTime now = System::time(); lastDuration = now - timeStart; if (abs(emwaDuration - lastDuration) > max(emwaDuration, lastDuration) * 0.50) { // Off by more than 50% emwaDuration = lastDuration; } else { emwaDuration = lastDuration * 0.05 + emwaDuration * 0.95; } lastCycleCount = cycleStart - cycleOverhead; if (lastCycleCount < 0) { lastCycleCount = 0; } if (lastTockTime != -1.0) { m_fps = 1.0 / (now - lastTockTime); const double blend = 0.01; emwaFPS = m_fps * blend + emwaFPS * (1.0 - blend); double maxDiscrepancyPercentage = 0.25; if (abs(emwaFPS - m_fps) > max(emwaFPS, m_fps) * maxDiscrepancyPercentage) { // The difference between emwa and m_fps is way off, so // update emwa directly. emwaFPS = m_fps * 0.20 + emwaFPS * 0.80; } // Update m_smoothFPS only when the value varies significantly. // We round so as to not mislead the user as to the accuracy of // the number. if (m_smoothFPS == 0) { m_smoothFPS = m_fps; } else if (emwaFPS <= 20) { if (::fabs(m_smoothFPS - emwaFPS) > 0.75) { // Small number and display is off by more than 0.75; round to the nearest 0.1 m_smoothFPS = floor(emwaFPS * 10.0 + 0.5) / 10.0; } } else if (::fabs(m_smoothFPS - emwaFPS) > 1.25) { // Large number and display is off by more than 1.25; round to the nearest 1.0 m_smoothFPS = floor(emwaFPS + 0.5); } } lastTockTime = now; alwaysAssertM(inBetween, "Stopwatch::tock() called without matching tick."); inBetween = false; } void Stopwatch::reset() { prevTime = startTime = System::time(); prevMark = "start"; } void Stopwatch::after(const std::string& s) { RealTime now = System::time(); if (m_enabled) { debugPrintf("%s: %10s - %8fs since %s (%fs since start)\n", myName.c_str(), s.c_str(), now - prevTime, prevMark.c_str(), now - startTime); } prevTime = now; prevMark = s; } }