Merge pull request #8879 from AndrewMeadows/show-physics-stats

add Bullet profile info to PerformanceTimer stats
This commit is contained in:
Chris Collins 2016-10-25 06:29:27 -07:00 committed by GitHub
commit 9846614468
8 changed files with 86 additions and 19 deletions

View file

@ -3969,8 +3969,6 @@ void Application::update(float deltaTime) {
auto collisionEvents = _physicsEngine->getCollisionEvents(); auto collisionEvents = _physicsEngine->getCollisionEvents();
avatarManager->handleCollisionEvents(collisionEvents); avatarManager->handleCollisionEvents(collisionEvents);
_physicsEngine->dumpStatsIfNecessary();
if (!_aboutToQuit) { if (!_aboutToQuit) {
PerformanceTimer perfTimer("entities"); PerformanceTimer perfTimer("entities");
// Collision events (and their scripts) must not be handled when we're locked, above. (That would risk // Collision events (and their scripts) must not be handled when we're locked, above. (That would risk
@ -3983,6 +3981,13 @@ void Application::update(float deltaTime) {
} }
myAvatar->harvestResultsFromPhysicsSimulation(deltaTime); myAvatar->harvestResultsFromPhysicsSimulation(deltaTime);
if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails) &&
Menu::getInstance()->isOptionChecked(MenuOption::ExpandPhysicsSimulationTiming)) {
_physicsEngine->harvestPerformanceStats();
}
// NOTE: the PhysicsEngine stats are written to stdout NOT to Qt log framework
_physicsEngine->dumpStatsIfNecessary();
} }
} }
} }

View file

@ -605,6 +605,7 @@ Menu::Menu() {
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandMyAvatarSimulateTiming, 0, false); addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandMyAvatarSimulateTiming, 0, false);
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandOtherAvatarTiming, 0, false); addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandOtherAvatarTiming, 0, false);
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandPaintGLTiming, 0, false); addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandPaintGLTiming, 0, false);
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandPhysicsSimulationTiming, 0, false);
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::FrameTimer); addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::FrameTimer);
addActionToQMenuAndActionHash(timingMenu, MenuOption::RunTimingTests, 0, qApp, SLOT(runTests())); addActionToQMenuAndActionHash(timingMenu, MenuOption::RunTimingTests, 0, qApp, SLOT(runTests()));

View file

@ -104,6 +104,7 @@ namespace MenuOption {
const QString ExpandMyAvatarTiming = "Expand /myAvatar"; const QString ExpandMyAvatarTiming = "Expand /myAvatar";
const QString ExpandOtherAvatarTiming = "Expand /otherAvatar"; const QString ExpandOtherAvatarTiming = "Expand /otherAvatar";
const QString ExpandPaintGLTiming = "Expand /paintGL"; const QString ExpandPaintGLTiming = "Expand /paintGL";
const QString ExpandPhysicsSimulationTiming = "Expand /physics";
const QString ExpandUpdateTiming = "Expand /update"; const QString ExpandUpdateTiming = "Expand /update";
const QString Faceshift = "Faceshift"; const QString Faceshift = "Faceshift";
const QString FirstPerson = "First Person"; const QString FirstPerson = "First Person";

View file

@ -55,7 +55,9 @@ Stats::Stats(QQuickItem* parent) : QQuickItem(parent) {
bool Stats::includeTimingRecord(const QString& name) { bool Stats::includeTimingRecord(const QString& name) {
if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails)) { if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails)) {
if (name.startsWith("/idle/update/")) { if (name.startsWith("/idle/update/")) {
if (name.startsWith("/idle/update/myAvatar/")) { if (name.startsWith("/idle/update/physics/")) {
return Menu::getInstance()->isOptionChecked(MenuOption::ExpandPhysicsSimulationTiming);
} else if (name.startsWith("/idle/update/myAvatar/")) {
if (name.startsWith("/idle/update/myAvatar/simulate/")) { if (name.startsWith("/idle/update/myAvatar/simulate/")) {
return Menu::getInstance()->isOptionChecked(MenuOption::ExpandMyAvatarSimulateTiming); return Menu::getInstance()->isOptionChecked(MenuOption::ExpandMyAvatarSimulateTiming);
} }
@ -369,7 +371,7 @@ void Stats::updateStats(bool force) {
QString functionName = j.value(); QString functionName = j.value();
const PerformanceTimerRecord& record = allRecords.value(functionName); const PerformanceTimerRecord& record = allRecords.value(functionName);
perfLines += QString("%1: %2 [%3]\n"). perfLines += QString("%1: %2 [%3]\n").
arg(QString(qPrintable(functionName)), 90, noBreakingSpace). arg(QString(qPrintable(functionName)), -80, noBreakingSpace).
arg((float)record.getMovingAverage() / (float)USECS_PER_MSEC, 8, 'f', 3, noBreakingSpace). arg((float)record.getMovingAverage() / (float)USECS_PER_MSEC, 8, 'f', 3, noBreakingSpace).
arg((int)record.getCount(), 6, 10, noBreakingSpace); arg((int)record.getCount(), 6, 10, noBreakingSpace);
linesDisplayed++; linesDisplayed++;

View file

@ -11,6 +11,8 @@
#include <PhysicsCollisionGroups.h> #include <PhysicsCollisionGroups.h>
#include <PerfStat.h>
#include "CharacterController.h" #include "CharacterController.h"
#include "ObjectMotionState.h" #include "ObjectMotionState.h"
#include "PhysicsEngine.h" #include "PhysicsEngine.h"
@ -286,6 +288,47 @@ void PhysicsEngine::stepSimulation() {
} }
} }
void PhysicsEngine::harvestPerformanceStats() {
// unfortunately the full context names get too long for our stats presentation format
//QString contextName = PerformanceTimer::getContextName(); // TODO: how to show full context name?
QString contextName("...");
CProfileIterator* profileIterator = CProfileManager::Get_Iterator();
if (profileIterator) {
// hunt for stepSimulation context
profileIterator->First();
for (int32_t childIndex = 0; !profileIterator->Is_Done(); ++childIndex) {
if (QString(profileIterator->Get_Current_Name()) == "stepSimulation") {
profileIterator->Enter_Child(childIndex);
recursivelyHarvestPerformanceStats(profileIterator, contextName);
break;
}
profileIterator->Next();
}
}
}
void PhysicsEngine::recursivelyHarvestPerformanceStats(CProfileIterator* profileIterator, QString contextName) {
QString parentContextName = contextName + QString("/") + QString(profileIterator->Get_Current_Parent_Name());
// get the stats for the children
int32_t numChildren = 0;
profileIterator->First();
while (!profileIterator->Is_Done()) {
QString childContextName = parentContextName + QString("/") + QString(profileIterator->Get_Current_Name());
uint64_t time = (uint64_t)((btScalar)MSECS_PER_SECOND * profileIterator->Get_Current_Total_Time());
PerformanceTimer::addTimerRecord(childContextName, time);
profileIterator->Next();
++numChildren;
}
// recurse the children
for (int32_t i = 0; i < numChildren; ++i) {
profileIterator->Enter_Child(i);
recursivelyHarvestPerformanceStats(profileIterator, contextName);
}
// retreat back to parent
profileIterator->Enter_Parent();
}
void PhysicsEngine::doOwnershipInfection(const btCollisionObject* objectA, const btCollisionObject* objectB) { void PhysicsEngine::doOwnershipInfection(const btCollisionObject* objectA, const btCollisionObject* objectB) {
BT_PROFILE("ownershipInfection"); BT_PROFILE("ownershipInfection");

View file

@ -59,6 +59,7 @@ public:
void reinsertObject(ObjectMotionState* object); void reinsertObject(ObjectMotionState* object);
void stepSimulation(); void stepSimulation();
void harvestPerformanceStats();
void updateContactMap(); void updateContactMap();
bool hasOutgoingChanges() const { return _hasOutgoingChanges; } bool hasOutgoingChanges() const { return _hasOutgoingChanges; }
@ -89,6 +90,7 @@ public:
private: private:
void addObjectToDynamicsWorld(ObjectMotionState* motionState); void addObjectToDynamicsWorld(ObjectMotionState* motionState);
void recursivelyHarvestPerformanceStats(CProfileIterator* profileIterator, QString contextName);
/// \brief bump any objects that touch this one, then remove contact info /// \brief bump any objects that touch this one, then remove contact info
void bumpAndPruneContacts(ObjectMotionState* motionState); void bumpAndPruneContacts(ObjectMotionState* motionState);

View file

@ -31,8 +31,8 @@ bool PerformanceWarning::_suppressShortTimings = false;
// Destructor handles recording all of our stats // Destructor handles recording all of our stats
PerformanceWarning::~PerformanceWarning() { PerformanceWarning::~PerformanceWarning() {
quint64 end = usecTimestampNow(); quint64 end = usecTimestampNow();
quint64 elapsedusec = (end - _start); quint64 elapsedUsec = (end - _start);
double elapsedmsec = elapsedusec / 1000.0; double elapsedmsec = elapsedUsec / 1000.0;
if ((_alwaysDisplay || _renderWarningsOn) && elapsedmsec > 1) { if ((_alwaysDisplay || _renderWarningsOn) && elapsedmsec > 1) {
if (elapsedmsec > 1000) { if (elapsedmsec > 1000) {
double elapsedsec = (end - _start) / 1000000.0; double elapsedsec = (end - _start) / 1000000.0;
@ -53,7 +53,7 @@ PerformanceWarning::~PerformanceWarning() {
} }
// if the caller gave us a pointer to store the running total, track it now. // if the caller gave us a pointer to store the running total, track it now.
if (_runningTotal) { if (_runningTotal) {
*_runningTotal += elapsedusec; *_runningTotal += elapsedUsec;
} }
if (_totalCalls) { if (_totalCalls) {
*_totalCalls += 1; *_totalCalls += 1;
@ -65,11 +65,11 @@ PerformanceWarning::~PerformanceWarning() {
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
const quint64 STALE_STAT_PERIOD = 4 * USECS_PER_SECOND; const quint64 STALE_STAT_PERIOD = 4 * USECS_PER_SECOND;
void PerformanceTimerRecord::tallyResult(const quint64& now) { void PerformanceTimerRecord::tallyResult(const quint64& now) {
if (_numAccumulations > 0) { if (_numAccumulations > 0) {
_numTallies++; _numTallies++;
_movingAverage.updateAverage(_runningTotal - _lastTotal); _movingAverage.updateAverage(_runningTotal - _lastTotal);
_lastTotal = _runningTotal; _lastTotal = _runningTotal;
_numAccumulations = 0; _numAccumulations = 0;
_expiry = now + STALE_STAT_PERIOD; _expiry = now + STALE_STAT_PERIOD;
} }
@ -96,10 +96,10 @@ PerformanceTimer::PerformanceTimer(const QString& name) {
PerformanceTimer::~PerformanceTimer() { PerformanceTimer::~PerformanceTimer() {
if (_isActive && _start != 0) { if (_isActive && _start != 0) {
quint64 elapsedusec = (usecTimestampNow() - _start); quint64 elapsedUsec = (usecTimestampNow() - _start);
QString& fullName = _fullNames[QThread::currentThread()]; QString& fullName = _fullNames[QThread::currentThread()];
PerformanceTimerRecord& namedRecord = _records[fullName]; PerformanceTimerRecord& namedRecord = _records[fullName];
namedRecord.accumulateResult(elapsedusec); namedRecord.accumulateResult(elapsedUsec);
fullName.resize(fullName.size() - (_name.size() + 1)); fullName.resize(fullName.size() - (_name.size() + 1));
} }
} }
@ -109,6 +109,17 @@ bool PerformanceTimer::isActive() {
return _isActive; return _isActive;
} }
// static
QString PerformanceTimer::getContextName() {
return _fullNames[QThread::currentThread()];
}
// static
void PerformanceTimer::addTimerRecord(const QString& fullName, quint64 elapsedUsec) {
PerformanceTimerRecord& namedRecord = _records[fullName];
namedRecord.accumulateResult(elapsedUsec);
}
// static // static
void PerformanceTimer::setActive(bool active) { void PerformanceTimer::setActive(bool active) {
if (active != _isActive) { if (active != _isActive) {
@ -117,7 +128,7 @@ void PerformanceTimer::setActive(bool active) {
_fullNames.clear(); _fullNames.clear();
_records.clear(); _records.clear();
} }
qDebug() << "PerformanceTimer has been turned" << ((active) ? "on" : "off"); qDebug() << "PerformanceTimer has been turned" << ((active) ? "on" : "off");
} }
} }
@ -142,7 +153,7 @@ void PerformanceTimer::dumpAllTimerRecords() {
QMapIterator<QString, PerformanceTimerRecord> i(_records); QMapIterator<QString, PerformanceTimerRecord> i(_records);
while (i.hasNext()) { while (i.hasNext()) {
i.next(); i.next();
qCDebug(shared) << i.key() << ": average " << i.value().getAverage() qCDebug(shared) << i.key() << ": average " << i.value().getAverage()
<< " [" << i.value().getMovingAverage() << "]" << " [" << i.value().getMovingAverage() << "]"
<< "usecs over" << i.value().getCount() << "calls"; << "usecs over" << i.value().getCount() << "calls";
} }

View file

@ -45,7 +45,7 @@ public:
_alwaysDisplay(alwaysDisplay), _alwaysDisplay(alwaysDisplay),
_runningTotal(runningTotal), _runningTotal(runningTotal),
_totalCalls(totalCalls) { } _totalCalls(totalCalls) { }
quint64 elapsed() const { return (usecTimestampNow() - _start); }; quint64 elapsed() const { return (usecTimestampNow() - _start); };
~PerformanceWarning(); ~PerformanceWarning();
@ -56,14 +56,14 @@ public:
class PerformanceTimerRecord { class PerformanceTimerRecord {
public: public:
PerformanceTimerRecord() : _runningTotal(0), _lastTotal(0), _numAccumulations(0), _numTallies(0), _expiry(0) {} PerformanceTimerRecord() : _runningTotal(0), _lastTotal(0), _numAccumulations(0), _numTallies(0), _expiry(0) {}
void accumulateResult(const quint64& elapsed) { _runningTotal += elapsed; ++_numAccumulations; } void accumulateResult(const quint64& elapsed) { _runningTotal += elapsed; ++_numAccumulations; }
void tallyResult(const quint64& now); void tallyResult(const quint64& now);
bool isStale(const quint64& now) const { return now > _expiry; } bool isStale(const quint64& now) const { return now > _expiry; }
quint64 getAverage() const { return (_numTallies == 0) ? 0 : _runningTotal / _numTallies; } quint64 getAverage() const { return (_numTallies == 0) ? 0 : _runningTotal / _numTallies; }
quint64 getMovingAverage() const { return (_numTallies == 0) ? 0 : _movingAverage.getAverage(); } quint64 getMovingAverage() const { return (_numTallies == 0) ? 0 : _movingAverage.getAverage(); }
quint64 getCount() const { return _numTallies; } quint64 getCount() const { return _numTallies; }
private: private:
quint64 _runningTotal; quint64 _runningTotal;
quint64 _lastTotal; quint64 _lastTotal;
@ -81,7 +81,9 @@ public:
static bool isActive(); static bool isActive();
static void setActive(bool active); static void setActive(bool active);
static QString getContextName();
static void addTimerRecord(const QString& fullName, quint64 elapsedUsec);
static const PerformanceTimerRecord& getTimerRecord(const QString& name) { return _records[name]; }; static const PerformanceTimerRecord& getTimerRecord(const QString& name) { return _records[name]; };
static const QMap<QString, PerformanceTimerRecord>& getAllTimerRecords() { return _records; }; static const QMap<QString, PerformanceTimerRecord>& getAllTimerRecords() { return _records; };
static void tallyAllTimerRecords(); static void tallyAllTimerRecords();