mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-26 03:15:21 +02:00
Merge pull request #11965 from AndrewMeadows/expose-physics-perf-stats-to-js
Cleanup names of debug stats and trace contexts.
This commit is contained in:
commit
cfd2097f36
18 changed files with 262 additions and 118 deletions
|
@ -3222,8 +3222,6 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void Application::keyReleaseEvent(QKeyEvent* event) {
|
void Application::keyReleaseEvent(QKeyEvent* event) {
|
||||||
_keysPressed.remove(event->key());
|
_keysPressed.remove(event->key());
|
||||||
|
|
||||||
|
@ -4848,8 +4846,7 @@ void Application::update(float deltaTime) {
|
||||||
if (_physicsEnabled) {
|
if (_physicsEnabled) {
|
||||||
{
|
{
|
||||||
PROFILE_RANGE(simulation_physics, "PreStep");
|
PROFILE_RANGE(simulation_physics, "PreStep");
|
||||||
|
PerformanceTimer perfTimer("preStep)");
|
||||||
PerformanceTimer perfTimer("updateStates)");
|
|
||||||
static VectorOfMotionStates motionStates;
|
static VectorOfMotionStates motionStates;
|
||||||
_entitySimulation->getObjectsToRemoveFromPhysics(motionStates);
|
_entitySimulation->getObjectsToRemoveFromPhysics(motionStates);
|
||||||
_physicsEngine->removeObjects(motionStates);
|
_physicsEngine->removeObjects(motionStates);
|
||||||
|
@ -4882,22 +4879,22 @@ void Application::update(float deltaTime) {
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
PROFILE_RANGE(simulation_physics, "Step");
|
PROFILE_RANGE(simulation_physics, "Step");
|
||||||
PerformanceTimer perfTimer("stepSimulation");
|
PerformanceTimer perfTimer("step");
|
||||||
getEntities()->getTree()->withWriteLock([&] {
|
getEntities()->getTree()->withWriteLock([&] {
|
||||||
_physicsEngine->stepSimulation();
|
_physicsEngine->stepSimulation();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
PROFILE_RANGE(simulation_physics, "PostStep");
|
PROFILE_RANGE(simulation_physics, "PostStep");
|
||||||
PerformanceTimer perfTimer("harvestChanges");
|
PerformanceTimer perfTimer("postStep");
|
||||||
if (_physicsEngine->hasOutgoingChanges()) {
|
if (_physicsEngine->hasOutgoingChanges()) {
|
||||||
// grab the collision events BEFORE handleOutgoingChanges() because at this point
|
// grab the collision events BEFORE handleOutgoingChanges() because at this point
|
||||||
// we have a better idea of which objects we own or should own.
|
// we have a better idea of which objects we own or should own.
|
||||||
auto& collisionEvents = _physicsEngine->getCollisionEvents();
|
auto& collisionEvents = _physicsEngine->getCollisionEvents();
|
||||||
|
|
||||||
getEntities()->getTree()->withWriteLock([&] {
|
getEntities()->getTree()->withWriteLock([&] {
|
||||||
PROFILE_RANGE(simulation_physics, "Harvest");
|
PROFILE_RANGE(simulation_physics, "HandleChanges");
|
||||||
PerformanceTimer perfTimer("handleOutgoingChanges");
|
PerformanceTimer perfTimer("handleChanges");
|
||||||
|
|
||||||
const VectorOfMotionStates& outgoingChanges = _physicsEngine->getChangedMotionStates();
|
const VectorOfMotionStates& outgoingChanges = _physicsEngine->getChangedMotionStates();
|
||||||
_entitySimulation->handleChangedMotionStates(outgoingChanges);
|
_entitySimulation->handleChangedMotionStates(outgoingChanges);
|
||||||
|
@ -4908,17 +4905,15 @@ void Application::update(float deltaTime) {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!_aboutToQuit) {
|
if (!_aboutToQuit) {
|
||||||
// handleCollisionEvents() AFTER handleOutgoinChanges()
|
// handleCollisionEvents() AFTER handleOutgoingChanges()
|
||||||
{
|
{
|
||||||
PROFILE_RANGE(simulation_physics, "CollisionEvents");
|
PROFILE_RANGE(simulation_physics, "CollisionEvents");
|
||||||
PerformanceTimer perfTimer("entities");
|
|
||||||
avatarManager->handleCollisionEvents(collisionEvents);
|
avatarManager->handleCollisionEvents(collisionEvents);
|
||||||
// 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
|
||||||
// deadlock.)
|
// deadlock.)
|
||||||
_entitySimulation->handleCollisionEvents(collisionEvents);
|
_entitySimulation->handleCollisionEvents(collisionEvents);
|
||||||
}
|
}
|
||||||
|
|
||||||
PROFILE_RANGE(simulation_physics, "UpdateEntities");
|
|
||||||
// NOTE: the getEntities()->update() call below will wait for lock
|
// NOTE: the getEntities()->update() call below will wait for lock
|
||||||
// and will simulate entity motion (the EntityTree has been given an EntitySimulation).
|
// and will simulate entity motion (the EntityTree has been given an EntitySimulation).
|
||||||
getEntities()->update(true); // update the models...
|
getEntities()->update(true); // update the models...
|
||||||
|
@ -4929,7 +4924,8 @@ void Application::update(float deltaTime) {
|
||||||
myAvatar->harvestResultsFromPhysicsSimulation(deltaTime);
|
myAvatar->harvestResultsFromPhysicsSimulation(deltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails) &&
|
if (PerformanceTimer::isActive() &&
|
||||||
|
Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails) &&
|
||||||
Menu::getInstance()->isOptionChecked(MenuOption::ExpandPhysicsSimulationTiming)) {
|
Menu::getInstance()->isOptionChecked(MenuOption::ExpandPhysicsSimulationTiming)) {
|
||||||
_physicsEngine->harvestPerformanceStats();
|
_physicsEngine->harvestPerformanceStats();
|
||||||
}
|
}
|
||||||
|
@ -7506,4 +7502,9 @@ void Application::setAvatarOverrideUrl(const QUrl& url, bool save) {
|
||||||
_avatarOverrideUrl = url;
|
_avatarOverrideUrl = url;
|
||||||
_saveAvatarOverrideUrl = save;
|
_saveAvatarOverrideUrl = save;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Application::saveNextPhysicsStats(QString filename) {
|
||||||
|
_physicsEngine->saveNextPhysicsStats(filename);
|
||||||
|
}
|
||||||
|
|
||||||
#include "Application.moc"
|
#include "Application.moc"
|
||||||
|
|
|
@ -280,6 +280,7 @@ public:
|
||||||
void clearAvatarOverrideUrl() { _avatarOverrideUrl = QUrl(); _saveAvatarOverrideUrl = false; }
|
void clearAvatarOverrideUrl() { _avatarOverrideUrl = QUrl(); _saveAvatarOverrideUrl = false; }
|
||||||
QUrl getAvatarOverrideUrl() { return _avatarOverrideUrl; }
|
QUrl getAvatarOverrideUrl() { return _avatarOverrideUrl; }
|
||||||
bool getSaveAvatarOverrideUrl() { return _saveAvatarOverrideUrl; }
|
bool getSaveAvatarOverrideUrl() { return _saveAvatarOverrideUrl; }
|
||||||
|
void saveNextPhysicsStats(QString filename);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void svoImportRequested(const QString& url);
|
void svoImportRequested(const QString& url);
|
||||||
|
@ -432,6 +433,7 @@ private slots:
|
||||||
|
|
||||||
void handleSandboxStatus(QNetworkReply* reply);
|
void handleSandboxStatus(QNetworkReply* reply);
|
||||||
void switchDisplayMode();
|
void switchDisplayMode();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void initDisplay();
|
static void initDisplay();
|
||||||
void init();
|
void init();
|
||||||
|
|
|
@ -645,7 +645,8 @@ Menu::Menu() {
|
||||||
// Developer > Timing >>>
|
// Developer > Timing >>>
|
||||||
MenuWrapper* timingMenu = developerMenu->addMenu("Timing");
|
MenuWrapper* timingMenu = developerMenu->addMenu("Timing");
|
||||||
MenuWrapper* perfTimerMenu = timingMenu->addMenu("Performance Timer");
|
MenuWrapper* perfTimerMenu = timingMenu->addMenu("Performance Timer");
|
||||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::DisplayDebugTimingDetails, 0, false);
|
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::DisplayDebugTimingDetails, 0, false,
|
||||||
|
qApp, SLOT(enablePerfStats(bool)));
|
||||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::OnlyDisplayTopTen, 0, true);
|
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::OnlyDisplayTopTen, 0, true);
|
||||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandUpdateTiming, 0, false);
|
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandUpdateTiming, 0, false);
|
||||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandMyAvatarTiming, 0, false);
|
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandMyAvatarTiming, 0, false);
|
||||||
|
|
|
@ -11,11 +11,12 @@
|
||||||
#include <QtCore/QLoggingCategory>
|
#include <QtCore/QLoggingCategory>
|
||||||
#include <QtCore/QThread>
|
#include <QtCore/QThread>
|
||||||
|
|
||||||
|
#include <shared/FileUtils.h>
|
||||||
#include <shared/QtHelpers.h>
|
#include <shared/QtHelpers.h>
|
||||||
#include <DependencyManager.h>
|
#include <DependencyManager.h>
|
||||||
#include <Trace.h>
|
|
||||||
#include <StatTracker.h>
|
|
||||||
#include <OffscreenUi.h>
|
#include <OffscreenUi.h>
|
||||||
|
#include <StatTracker.h>
|
||||||
|
#include <Trace.h>
|
||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
|
|
||||||
|
@ -141,6 +142,15 @@ void TestScriptingInterface::endTraceEvent(QString name) {
|
||||||
tracing::traceEvent(trace_test(), name, tracing::DurationEnd);
|
tracing::traceEvent(trace_test(), name, tracing::DurationEnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestScriptingInterface::savePhysicsSimulationStats(QString originalPath) {
|
||||||
|
QString path = FileUtils::replaceDateTimeTokens(originalPath);
|
||||||
|
path = FileUtils::computeDocumentPath(path);
|
||||||
|
if (!FileUtils::canCreateFile(path)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
qApp->saveNextPhysicsStats(path);
|
||||||
|
}
|
||||||
|
|
||||||
void TestScriptingInterface::profileRange(const QString& name, QScriptValue fn) {
|
void TestScriptingInterface::profileRange(const QString& name, QScriptValue fn) {
|
||||||
PROFILE_RANGE(script, name);
|
PROFILE_RANGE(script, name);
|
||||||
fn.call();
|
fn.call();
|
||||||
|
|
|
@ -71,6 +71,11 @@ public slots:
|
||||||
|
|
||||||
void endTraceEvent(QString name);
|
void endTraceEvent(QString name);
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
* Write detailed timing stats of next physics stepSimulation() to filename
|
||||||
|
*/
|
||||||
|
void savePhysicsSimulationStats(QString filename);
|
||||||
|
|
||||||
Q_INVOKABLE void profileRange(const QString& name, QScriptValue function);
|
Q_INVOKABLE void profileRange(const QString& name, QScriptValue function);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -78,6 +78,8 @@ bool Stats::includeTimingRecord(const QString& name) {
|
||||||
return Menu::getInstance()->isOptionChecked(MenuOption::ExpandPaintGLTiming);
|
return Menu::getInstance()->isOptionChecked(MenuOption::ExpandPaintGLTiming);
|
||||||
} else if (name.startsWith("/paintGL/")) {
|
} else if (name.startsWith("/paintGL/")) {
|
||||||
return Menu::getInstance()->isOptionChecked(MenuOption::ExpandPaintGLTiming);
|
return Menu::getInstance()->isOptionChecked(MenuOption::ExpandPaintGLTiming);
|
||||||
|
} else if (name.startsWith("step/")) {
|
||||||
|
return Menu::getInstance()->isOptionChecked(MenuOption::ExpandPhysicsSimulationTiming);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -251,10 +251,10 @@ void EntityTreeRenderer::shutdown() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityTreeRenderer::addPendingEntities(const render::ScenePointer& scene, render::Transaction& transaction) {
|
void EntityTreeRenderer::addPendingEntities(const render::ScenePointer& scene, render::Transaction& transaction) {
|
||||||
PROFILE_RANGE_EX(simulation_physics, "Add", 0xffff00ff, (uint64_t)_entitiesToAdd.size());
|
PROFILE_RANGE_EX(simulation_physics, "AddToScene", 0xffff00ff, (uint64_t)_entitiesToAdd.size());
|
||||||
PerformanceTimer pt("add");
|
PerformanceTimer pt("add");
|
||||||
// Clear any expired entities
|
// Clear any expired entities
|
||||||
// FIXME should be able to use std::remove_if, but it fails due to some
|
// FIXME should be able to use std::remove_if, but it fails due to some
|
||||||
// weird compilation error related to EntityItemID assignment operators
|
// weird compilation error related to EntityItemID assignment operators
|
||||||
for (auto itr = _entitiesToAdd.begin(); _entitiesToAdd.end() != itr; ) {
|
for (auto itr = _entitiesToAdd.begin(); _entitiesToAdd.end() != itr; ) {
|
||||||
if (itr->second.expired()) {
|
if (itr->second.expired()) {
|
||||||
|
@ -272,7 +272,7 @@ void EntityTreeRenderer::addPendingEntities(const render::ScenePointer& scene, r
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Path to the parent transforms is not valid,
|
// Path to the parent transforms is not valid,
|
||||||
// don't add to the scene graph yet
|
// don't add to the scene graph yet
|
||||||
if (!entity->isParentPathComplete()) {
|
if (!entity->isParentPathComplete()) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -296,7 +296,7 @@ void EntityTreeRenderer::addPendingEntities(const render::ScenePointer& scene, r
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene, const ViewFrustum& view, render::Transaction& transaction) {
|
void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene, const ViewFrustum& view, render::Transaction& transaction) {
|
||||||
PROFILE_RANGE_EX(simulation_physics, "Change", 0xffff00ff, (uint64_t)_changedEntities.size());
|
PROFILE_RANGE_EX(simulation_physics, "ChangeInScene", 0xffff00ff, (uint64_t)_changedEntities.size());
|
||||||
PerformanceTimer pt("change");
|
PerformanceTimer pt("change");
|
||||||
std::unordered_set<EntityItemID> changedEntities;
|
std::unordered_set<EntityItemID> changedEntities;
|
||||||
_changedEntitiesGuard.withWriteLock([&] {
|
_changedEntitiesGuard.withWriteLock([&] {
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include <GLMHelpers.h>
|
#include <GLMHelpers.h>
|
||||||
#include <Octree.h>
|
#include <Octree.h>
|
||||||
#include <PhysicsHelpers.h>
|
#include <PhysicsHelpers.h>
|
||||||
|
#include <Profile.h>
|
||||||
#include <RegisteredMetaTypes.h>
|
#include <RegisteredMetaTypes.h>
|
||||||
#include <SharedUtil.h> // usecTimestampNow()
|
#include <SharedUtil.h> // usecTimestampNow()
|
||||||
#include <LogHandler.h>
|
#include <LogHandler.h>
|
||||||
|
@ -984,6 +985,7 @@ void EntityItem::setCollisionSoundURL(const QString& value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityItem::simulate(const quint64& now) {
|
void EntityItem::simulate(const quint64& now) {
|
||||||
|
DETAILED_PROFILE_RANGE(simulation_physics, "Simulate");
|
||||||
if (getLastSimulated() == 0) {
|
if (getLastSimulated() == 0) {
|
||||||
setLastSimulated(now);
|
setLastSimulated(now);
|
||||||
}
|
}
|
||||||
|
@ -1039,6 +1041,7 @@ void EntityItem::simulate(const quint64& now) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EntityItem::stepKinematicMotion(float timeElapsed) {
|
bool EntityItem::stepKinematicMotion(float timeElapsed) {
|
||||||
|
DETAILED_PROFILE_RANGE(simulation_physics, "StepKinematicMotion");
|
||||||
// get all the data
|
// get all the data
|
||||||
Transform transform;
|
Transform transform;
|
||||||
glm::vec3 linearVelocity;
|
glm::vec3 linearVelocity;
|
||||||
|
|
|
@ -29,7 +29,6 @@ void EntitySimulation::setEntityTree(EntityTreePointer tree) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntitySimulation::updateEntities() {
|
void EntitySimulation::updateEntities() {
|
||||||
PROFILE_RANGE(simulation_physics, "ES::updateEntities");
|
|
||||||
QMutexLocker lock(&_mutex);
|
QMutexLocker lock(&_mutex);
|
||||||
quint64 now = usecTimestampNow();
|
quint64 now = usecTimestampNow();
|
||||||
|
|
||||||
|
@ -38,12 +37,7 @@ void EntitySimulation::updateEntities() {
|
||||||
callUpdateOnEntitiesThatNeedIt(now);
|
callUpdateOnEntitiesThatNeedIt(now);
|
||||||
moveSimpleKinematics(now);
|
moveSimpleKinematics(now);
|
||||||
updateEntitiesInternal(now);
|
updateEntitiesInternal(now);
|
||||||
|
sortEntitiesThatMoved();
|
||||||
{
|
|
||||||
PROFILE_RANGE(simulation_physics, "Sort");
|
|
||||||
PerformanceTimer perfTimer("sortingEntities");
|
|
||||||
sortEntitiesThatMoved();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntitySimulation::takeEntitiesToDelete(VectorOfEntities& entitiesToDelete) {
|
void EntitySimulation::takeEntitiesToDelete(VectorOfEntities& entitiesToDelete) {
|
||||||
|
@ -101,6 +95,7 @@ void EntitySimulation::changeEntityInternal(EntityItemPointer entity) {
|
||||||
// protected
|
// protected
|
||||||
void EntitySimulation::expireMortalEntities(const quint64& now) {
|
void EntitySimulation::expireMortalEntities(const quint64& now) {
|
||||||
if (now > _nextExpiry) {
|
if (now > _nextExpiry) {
|
||||||
|
PROFILE_RANGE_EX(simulation_physics, "ExpireMortals", 0xffff00ff, (uint64_t)_mortalEntities.size());
|
||||||
// only search for expired entities if we expect to find one
|
// only search for expired entities if we expect to find one
|
||||||
_nextExpiry = quint64(-1);
|
_nextExpiry = quint64(-1);
|
||||||
QMutexLocker lock(&_mutex);
|
QMutexLocker lock(&_mutex);
|
||||||
|
@ -146,6 +141,7 @@ void EntitySimulation::callUpdateOnEntitiesThatNeedIt(const quint64& now) {
|
||||||
|
|
||||||
// protected
|
// protected
|
||||||
void EntitySimulation::sortEntitiesThatMoved() {
|
void EntitySimulation::sortEntitiesThatMoved() {
|
||||||
|
PROFILE_RANGE_EX(simulation_physics, "SortTree", 0xffff00ff, (uint64_t)_entitiesToSort.size());
|
||||||
// NOTE: this is only for entities that have been moved by THIS EntitySimulation.
|
// NOTE: this is only for entities that have been moved by THIS EntitySimulation.
|
||||||
// External changes to entity position/shape are expected to be sorted outside of the EntitySimulation.
|
// External changes to entity position/shape are expected to be sorted outside of the EntitySimulation.
|
||||||
MovingEntitiesOperator moveOperator;
|
MovingEntitiesOperator moveOperator;
|
||||||
|
@ -265,7 +261,7 @@ void EntitySimulation::clearEntities() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntitySimulation::moveSimpleKinematics(const quint64& now) {
|
void EntitySimulation::moveSimpleKinematics(const quint64& now) {
|
||||||
PROFILE_RANGE_EX(simulation_physics, "Kinematics", 0xffff00ff, (uint64_t)_simpleKinematicEntities.size());
|
PROFILE_RANGE_EX(simulation_physics, "MoveSimples", 0xffff00ff, (uint64_t)_simpleKinematicEntities.size());
|
||||||
SetOfEntities::iterator itemItr = _simpleKinematicEntities.begin();
|
SetOfEntities::iterator itemItr = _simpleKinematicEntities.begin();
|
||||||
while (itemItr != _simpleKinematicEntities.end()) {
|
while (itemItr != _simpleKinematicEntities.end()) {
|
||||||
EntityItemPointer entity = *itemItr;
|
EntityItemPointer entity = *itemItr;
|
||||||
|
|
|
@ -1770,24 +1770,26 @@ void EntityTree::addToNeedsParentFixupList(EntityItemPointer entity) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityTree::update(bool simulate) {
|
void EntityTree::update(bool simulate) {
|
||||||
PROFILE_RANGE(simulation_physics, "ET::update");
|
PROFILE_RANGE(simulation_physics, "UpdateTree");
|
||||||
fixupNeedsParentFixups();
|
fixupNeedsParentFixups();
|
||||||
if (simulate && _simulation) {
|
if (simulate && _simulation) {
|
||||||
withWriteLock([&] {
|
withWriteLock([&] {
|
||||||
_simulation->updateEntities();
|
_simulation->updateEntities();
|
||||||
VectorOfEntities pendingDeletes;
|
{
|
||||||
_simulation->takeEntitiesToDelete(pendingDeletes);
|
PROFILE_RANGE(simulation_physics, "Deletes");
|
||||||
|
VectorOfEntities pendingDeletes;
|
||||||
|
_simulation->takeEntitiesToDelete(pendingDeletes);
|
||||||
|
if (pendingDeletes.size() > 0) {
|
||||||
|
// translate into list of ID's
|
||||||
|
QSet<EntityItemID> idsToDelete;
|
||||||
|
|
||||||
if (pendingDeletes.size() > 0) {
|
for (auto entity : pendingDeletes) {
|
||||||
// translate into list of ID's
|
idsToDelete.insert(entity->getEntityItemID());
|
||||||
QSet<EntityItemID> idsToDelete;
|
}
|
||||||
|
|
||||||
for (auto entity : pendingDeletes) {
|
// delete these things the roundabout way
|
||||||
idsToDelete.insert(entity->getEntityItemID());
|
deleteEntities(idsToDelete, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// delete these things the roundabout way
|
|
||||||
deleteEntities(idsToDelete, true);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,8 +14,9 @@
|
||||||
#include <EntityItem.h>
|
#include <EntityItem.h>
|
||||||
#include <EntityItemProperties.h>
|
#include <EntityItemProperties.h>
|
||||||
#include <EntityEditPacketSender.h>
|
#include <EntityEditPacketSender.h>
|
||||||
#include <PhysicsCollisionGroups.h>
|
|
||||||
#include <LogHandler.h>
|
#include <LogHandler.h>
|
||||||
|
#include <PhysicsCollisionGroups.h>
|
||||||
|
#include <Profile.h>
|
||||||
|
|
||||||
#include "BulletUtil.h"
|
#include "BulletUtil.h"
|
||||||
#include "EntityMotionState.h"
|
#include "EntityMotionState.h"
|
||||||
|
@ -325,6 +326,7 @@ bool EntityMotionState::isCandidateForOwnership() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
||||||
|
DETAILED_PROFILE_RANGE(simulation_physics, "CheckOutOfSync");
|
||||||
// NOTE: we only get here if we think we own the simulation
|
// NOTE: we only get here if we think we own the simulation
|
||||||
assert(_body);
|
assert(_body);
|
||||||
|
|
||||||
|
@ -476,6 +478,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep) {
|
bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep) {
|
||||||
|
DETAILED_PROFILE_RANGE(simulation_physics, "ShouldSend");
|
||||||
// NOTE: we expect _entity and _body to be valid in this context, since shouldSendUpdate() is only called
|
// NOTE: we expect _entity and _body to be valid in this context, since shouldSendUpdate() is only called
|
||||||
// after doesNotNeedToSendUpdate() returns false and that call should return 'true' if _entity or _body are NULL.
|
// after doesNotNeedToSendUpdate() returns false and that call should return 'true' if _entity or _body are NULL.
|
||||||
assert(_entity);
|
assert(_entity);
|
||||||
|
@ -516,6 +519,7 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_t step) {
|
void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_t step) {
|
||||||
|
DETAILED_PROFILE_RANGE(simulation_physics, "Send");
|
||||||
assert(_entity);
|
assert(_entity);
|
||||||
assert(entityTreeIsLocked());
|
assert(entityTreeIsLocked());
|
||||||
|
|
||||||
|
@ -731,6 +735,7 @@ void EntityMotionState::resetMeasuredBodyAcceleration() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityMotionState::measureBodyAcceleration() {
|
void EntityMotionState::measureBodyAcceleration() {
|
||||||
|
DETAILED_PROFILE_RANGE(simulation_physics, "MeasureAccel");
|
||||||
// try to manually measure the true acceleration of the object
|
// try to manually measure the true acceleration of the object
|
||||||
uint32_t thisStep = ObjectMotionState::getWorldSimulationStep();
|
uint32_t thisStep = ObjectMotionState::getWorldSimulationStep();
|
||||||
uint32_t numSubsteps = thisStep - _lastMeasureStep;
|
uint32_t numSubsteps = thisStep - _lastMeasureStep;
|
||||||
|
|
|
@ -10,12 +10,14 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
|
#include "PhysicalEntitySimulation.h"
|
||||||
|
|
||||||
|
#include <Profile.h>
|
||||||
|
|
||||||
#include "PhysicsHelpers.h"
|
#include "PhysicsHelpers.h"
|
||||||
#include "PhysicsLogging.h"
|
#include "PhysicsLogging.h"
|
||||||
#include "ShapeManager.h"
|
#include "ShapeManager.h"
|
||||||
|
|
||||||
#include "PhysicalEntitySimulation.h"
|
|
||||||
|
|
||||||
PhysicalEntitySimulation::PhysicalEntitySimulation() {
|
PhysicalEntitySimulation::PhysicalEntitySimulation() {
|
||||||
}
|
}
|
||||||
|
@ -274,20 +276,24 @@ void PhysicalEntitySimulation::handleDeactivatedMotionStates(const VectorOfMotio
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhysicalEntitySimulation::handleChangedMotionStates(const VectorOfMotionStates& motionStates) {
|
void PhysicalEntitySimulation::handleChangedMotionStates(const VectorOfMotionStates& motionStates) {
|
||||||
|
PROFILE_RANGE_EX(simulation_physics, "ChangedEntities", 0x00000000, (uint64_t)motionStates.size());
|
||||||
QMutexLocker lock(&_mutex);
|
QMutexLocker lock(&_mutex);
|
||||||
|
|
||||||
// walk the motionStates looking for those that correspond to entities
|
// walk the motionStates looking for those that correspond to entities
|
||||||
for (auto stateItr : motionStates) {
|
{
|
||||||
ObjectMotionState* state = &(*stateItr);
|
PROFILE_RANGE_EX(simulation_physics, "Filter", 0x00000000, (uint64_t)motionStates.size());
|
||||||
assert(state);
|
for (auto stateItr : motionStates) {
|
||||||
if (state->getType() == MOTIONSTATE_TYPE_ENTITY) {
|
ObjectMotionState* state = &(*stateItr);
|
||||||
EntityMotionState* entityState = static_cast<EntityMotionState*>(state);
|
assert(state);
|
||||||
EntityItemPointer entity = entityState->getEntity();
|
if (state->getType() == MOTIONSTATE_TYPE_ENTITY) {
|
||||||
assert(entity.get());
|
EntityMotionState* entityState = static_cast<EntityMotionState*>(state);
|
||||||
if (entityState->isCandidateForOwnership()) {
|
EntityItemPointer entity = entityState->getEntity();
|
||||||
_outgoingChanges.insert(entityState);
|
assert(entity.get());
|
||||||
|
if (entityState->isCandidateForOwnership()) {
|
||||||
|
_outgoingChanges.insert(entityState);
|
||||||
|
}
|
||||||
|
_entitiesToSort.insert(entity);
|
||||||
}
|
}
|
||||||
_entitiesToSort.insert(entity);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -302,6 +308,7 @@ void PhysicalEntitySimulation::handleChangedMotionStates(const VectorOfMotionSta
|
||||||
}
|
}
|
||||||
|
|
||||||
// look for entities to prune or update
|
// look for entities to prune or update
|
||||||
|
PROFILE_RANGE_EX(simulation_physics, "Prune/Send", 0x00000000, (uint64_t)_outgoingChanges.size());
|
||||||
QSet<EntityMotionState*>::iterator stateItr = _outgoingChanges.begin();
|
QSet<EntityMotionState*>::iterator stateItr = _outgoingChanges.begin();
|
||||||
while (stateItr != _outgoingChanges.end()) {
|
while (stateItr != _outgoingChanges.end()) {
|
||||||
EntityMotionState* state = *stateItr;
|
EntityMotionState* state = *stateItr;
|
||||||
|
|
|
@ -11,7 +11,12 @@
|
||||||
|
|
||||||
#include <PhysicsCollisionGroups.h>
|
#include <PhysicsCollisionGroups.h>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#include <QFile>
|
||||||
|
|
||||||
#include <PerfStat.h>
|
#include <PerfStat.h>
|
||||||
|
#include <Profile.h>
|
||||||
|
|
||||||
#include "CharacterController.h"
|
#include "CharacterController.h"
|
||||||
#include "ObjectMotionState.h"
|
#include "ObjectMotionState.h"
|
||||||
|
@ -290,6 +295,7 @@ void PhysicsEngine::stepSimulation() {
|
||||||
float timeStep = btMin(dt, MAX_TIMESTEP);
|
float timeStep = btMin(dt, MAX_TIMESTEP);
|
||||||
|
|
||||||
if (_myAvatarController) {
|
if (_myAvatarController) {
|
||||||
|
DETAILED_PROFILE_RANGE(simulation_physics, "avatarController");
|
||||||
BT_PROFILE("avatarController");
|
BT_PROFILE("avatarController");
|
||||||
// TODO: move this stuff outside and in front of stepSimulation, because
|
// TODO: move this stuff outside and in front of stepSimulation, because
|
||||||
// the updateShapeIfNecessary() call needs info from MyAvatar and should
|
// the updateShapeIfNecessary() call needs info from MyAvatar and should
|
||||||
|
@ -328,45 +334,107 @@ void PhysicsEngine::stepSimulation() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class CProfileOperator {
|
||||||
|
public:
|
||||||
|
CProfileOperator() {}
|
||||||
|
void recurse(CProfileIterator* itr, QString context) {
|
||||||
|
// The context string will get too long if we accumulate it properly
|
||||||
|
//QString newContext = context + QString("/") + itr->Get_Current_Parent_Name();
|
||||||
|
// so we use this four-character indentation
|
||||||
|
QString newContext = context + QString(".../");
|
||||||
|
process(itr, newContext);
|
||||||
|
|
||||||
|
// count the children
|
||||||
|
int32_t numChildren = 0;
|
||||||
|
itr->First();
|
||||||
|
while (!itr->Is_Done()) {
|
||||||
|
itr->Next();
|
||||||
|
++numChildren;
|
||||||
|
}
|
||||||
|
|
||||||
|
// recurse the children
|
||||||
|
if (numChildren > 0) {
|
||||||
|
// recurse the children
|
||||||
|
for (int32_t i = 0; i < numChildren; ++i) {
|
||||||
|
itr->Enter_Child(i);
|
||||||
|
recurse(itr, newContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// retreat back to parent
|
||||||
|
itr->Enter_Parent();
|
||||||
|
}
|
||||||
|
virtual void process(CProfileIterator*, QString context) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class StatsHarvester : public CProfileOperator {
|
||||||
|
public:
|
||||||
|
StatsHarvester() {}
|
||||||
|
void process(CProfileIterator* itr, QString context) override {
|
||||||
|
QString name = context + itr->Get_Current_Parent_Name();
|
||||||
|
uint64_t time = (uint64_t)((btScalar)MSECS_PER_SECOND * itr->Get_Current_Parent_Total_Time());
|
||||||
|
PerformanceTimer::addTimerRecord(name, time);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
class StatsWriter : public CProfileOperator {
|
||||||
|
public:
|
||||||
|
StatsWriter(QString filename) : _file(filename) {
|
||||||
|
_file.open(QFile::WriteOnly);
|
||||||
|
if (_file.error() != QFileDevice::NoError) {
|
||||||
|
qCDebug(physics) << "unable to open file " << _file.fileName() << " to save stepSimulation() stats";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
~StatsWriter() {
|
||||||
|
_file.close();
|
||||||
|
}
|
||||||
|
void process(CProfileIterator* itr, QString context) override {
|
||||||
|
QString name = context + itr->Get_Current_Parent_Name();
|
||||||
|
float time = (btScalar)MSECS_PER_SECOND * itr->Get_Current_Parent_Total_Time();
|
||||||
|
if (_file.error() == QFileDevice::NoError) {
|
||||||
|
QTextStream s(&_file);
|
||||||
|
s << name << " = " << time << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
protected:
|
||||||
|
QFile _file;
|
||||||
|
};
|
||||||
|
|
||||||
void PhysicsEngine::harvestPerformanceStats() {
|
void PhysicsEngine::harvestPerformanceStats() {
|
||||||
// unfortunately the full context names get too long for our stats presentation format
|
// 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 = PerformanceTimer::getContextName(); // TODO: how to show full context name?
|
||||||
QString contextName("...");
|
QString contextName("...");
|
||||||
|
|
||||||
CProfileIterator* profileIterator = CProfileManager::Get_Iterator();
|
CProfileIterator* itr = CProfileManager::Get_Iterator();
|
||||||
if (profileIterator) {
|
if (itr) {
|
||||||
// hunt for stepSimulation context
|
// hunt for stepSimulation context
|
||||||
profileIterator->First();
|
itr->First();
|
||||||
for (int32_t childIndex = 0; !profileIterator->Is_Done(); ++childIndex) {
|
for (int32_t childIndex = 0; !itr->Is_Done(); ++childIndex) {
|
||||||
if (QString(profileIterator->Get_Current_Name()) == "stepSimulation") {
|
if (QString(itr->Get_Current_Name()) == "stepSimulation") {
|
||||||
profileIterator->Enter_Child(childIndex);
|
itr->Enter_Child(childIndex);
|
||||||
recursivelyHarvestPerformanceStats(profileIterator, contextName);
|
StatsHarvester harvester;
|
||||||
|
harvester.recurse(itr, "step/");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
profileIterator->Next();
|
itr->Next();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhysicsEngine::recursivelyHarvestPerformanceStats(CProfileIterator* profileIterator, QString contextName) {
|
void PhysicsEngine::printPerformanceStatsToFile(const QString& filename) {
|
||||||
QString parentContextName = contextName + QString("/") + QString(profileIterator->Get_Current_Parent_Name());
|
CProfileIterator* itr = CProfileManager::Get_Iterator();
|
||||||
// get the stats for the children
|
if (itr) {
|
||||||
int32_t numChildren = 0;
|
// hunt for stepSimulation context
|
||||||
profileIterator->First();
|
itr->First();
|
||||||
while (!profileIterator->Is_Done()) {
|
for (int32_t childIndex = 0; !itr->Is_Done(); ++childIndex) {
|
||||||
QString childContextName = parentContextName + QString("/") + QString(profileIterator->Get_Current_Name());
|
if (QString(itr->Get_Current_Name()) == "stepSimulation") {
|
||||||
uint64_t time = (uint64_t)((btScalar)MSECS_PER_SECOND * profileIterator->Get_Current_Total_Time());
|
itr->Enter_Child(childIndex);
|
||||||
PerformanceTimer::addTimerRecord(childContextName, time);
|
StatsWriter writer(filename);
|
||||||
profileIterator->Next();
|
writer.recurse(itr, "");
|
||||||
++numChildren;
|
break;
|
||||||
|
}
|
||||||
|
itr->Next();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// 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) {
|
||||||
|
@ -399,6 +467,7 @@ void PhysicsEngine::doOwnershipInfection(const btCollisionObject* objectA, const
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhysicsEngine::updateContactMap() {
|
void PhysicsEngine::updateContactMap() {
|
||||||
|
DETAILED_PROFILE_RANGE(simulation_physics, "updateContactMap");
|
||||||
BT_PROFILE("updateContactMap");
|
BT_PROFILE("updateContactMap");
|
||||||
++_numContactFrames;
|
++_numContactFrames;
|
||||||
|
|
||||||
|
@ -515,10 +584,21 @@ const VectorOfMotionStates& PhysicsEngine::getChangedMotionStates() {
|
||||||
void PhysicsEngine::dumpStatsIfNecessary() {
|
void PhysicsEngine::dumpStatsIfNecessary() {
|
||||||
if (_dumpNextStats) {
|
if (_dumpNextStats) {
|
||||||
_dumpNextStats = false;
|
_dumpNextStats = false;
|
||||||
|
CProfileManager::Increment_Frame_Counter();
|
||||||
|
if (_saveNextStats) {
|
||||||
|
_saveNextStats = false;
|
||||||
|
printPerformanceStatsToFile(_statsFilename);
|
||||||
|
}
|
||||||
CProfileManager::dumpAll();
|
CProfileManager::dumpAll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PhysicsEngine::saveNextPhysicsStats(QString filename) {
|
||||||
|
_saveNextStats = true;
|
||||||
|
_dumpNextStats = true;
|
||||||
|
_statsFilename = filename;
|
||||||
|
}
|
||||||
|
|
||||||
// Bullet collision flags are as follows:
|
// Bullet collision flags are as follows:
|
||||||
// CF_STATIC_OBJECT= 1,
|
// CF_STATIC_OBJECT= 1,
|
||||||
// CF_KINEMATIC_OBJECT= 2,
|
// CF_KINEMATIC_OBJECT= 2,
|
||||||
|
|
|
@ -62,6 +62,7 @@ public:
|
||||||
|
|
||||||
void stepSimulation();
|
void stepSimulation();
|
||||||
void harvestPerformanceStats();
|
void harvestPerformanceStats();
|
||||||
|
void printPerformanceStatsToFile(const QString& filename);
|
||||||
void updateContactMap();
|
void updateContactMap();
|
||||||
|
|
||||||
bool hasOutgoingChanges() const { return _hasOutgoingChanges; }
|
bool hasOutgoingChanges() const { return _hasOutgoingChanges; }
|
||||||
|
@ -76,6 +77,9 @@ public:
|
||||||
/// \brief prints timings for last frame if stats have been requested.
|
/// \brief prints timings for last frame if stats have been requested.
|
||||||
void dumpStatsIfNecessary();
|
void dumpStatsIfNecessary();
|
||||||
|
|
||||||
|
/// \brief saves timings for last frame in filename
|
||||||
|
void saveNextPhysicsStats(QString filename);
|
||||||
|
|
||||||
/// \param offset position of simulation origin in domain-frame
|
/// \param offset position of simulation origin in domain-frame
|
||||||
void setOriginOffset(const glm::vec3& offset) { _originOffset = offset; }
|
void setOriginOffset(const glm::vec3& offset) { _originOffset = offset; }
|
||||||
|
|
||||||
|
@ -94,7 +98,6 @@ public:
|
||||||
private:
|
private:
|
||||||
QList<EntityDynamicPointer> removeDynamicsForBody(btRigidBody* body);
|
QList<EntityDynamicPointer> removeDynamicsForBody(btRigidBody* body);
|
||||||
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);
|
||||||
|
@ -116,6 +119,7 @@ private:
|
||||||
QHash<QUuid, EntityDynamicPointer> _objectDynamics;
|
QHash<QUuid, EntityDynamicPointer> _objectDynamics;
|
||||||
QHash<btRigidBody*, QSet<QUuid>> _objectDynamicsByBody;
|
QHash<btRigidBody*, QSet<QUuid>> _objectDynamicsByBody;
|
||||||
std::set<btRigidBody*> _activeStaticBodies;
|
std::set<btRigidBody*> _activeStaticBodies;
|
||||||
|
QString _statsFilename;
|
||||||
|
|
||||||
glm::vec3 _originOffset;
|
glm::vec3 _originOffset;
|
||||||
|
|
||||||
|
@ -124,8 +128,9 @@ private:
|
||||||
uint32_t _numContactFrames = 0;
|
uint32_t _numContactFrames = 0;
|
||||||
uint32_t _numSubsteps;
|
uint32_t _numSubsteps;
|
||||||
|
|
||||||
bool _dumpNextStats = false;
|
bool _dumpNextStats { false };
|
||||||
bool _hasOutgoingChanges = false;
|
bool _saveNextStats { false };
|
||||||
|
bool _hasOutgoingChanges { false };
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include <LinearMath/btQuickprof.h>
|
#include <LinearMath/btQuickprof.h>
|
||||||
|
|
||||||
#include "ThreadSafeDynamicsWorld.h"
|
#include "ThreadSafeDynamicsWorld.h"
|
||||||
|
#include "Profile.h"
|
||||||
|
|
||||||
ThreadSafeDynamicsWorld::ThreadSafeDynamicsWorld(
|
ThreadSafeDynamicsWorld::ThreadSafeDynamicsWorld(
|
||||||
btDispatcher* dispatcher,
|
btDispatcher* dispatcher,
|
||||||
|
@ -29,6 +30,7 @@ ThreadSafeDynamicsWorld::ThreadSafeDynamicsWorld(
|
||||||
|
|
||||||
int ThreadSafeDynamicsWorld::stepSimulationWithSubstepCallback(btScalar timeStep, int maxSubSteps,
|
int ThreadSafeDynamicsWorld::stepSimulationWithSubstepCallback(btScalar timeStep, int maxSubSteps,
|
||||||
btScalar fixedTimeStep, SubStepCallback onSubStep) {
|
btScalar fixedTimeStep, SubStepCallback onSubStep) {
|
||||||
|
DETAILED_PROFILE_RANGE(simulation_physics, "stepWithCB");
|
||||||
BT_PROFILE("stepSimulationWithSubstepCallback");
|
BT_PROFILE("stepSimulationWithSubstepCallback");
|
||||||
int subSteps = 0;
|
int subSteps = 0;
|
||||||
if (maxSubSteps) {
|
if (maxSubSteps) {
|
||||||
|
@ -68,11 +70,13 @@ int ThreadSafeDynamicsWorld::stepSimulationWithSubstepCallback(btScalar timeStep
|
||||||
saveKinematicState(fixedTimeStep*clampedSimulationSteps);
|
saveKinematicState(fixedTimeStep*clampedSimulationSteps);
|
||||||
|
|
||||||
{
|
{
|
||||||
|
DETAILED_PROFILE_RANGE(simulation_physics, "applyGravity");
|
||||||
BT_PROFILE("applyGravity");
|
BT_PROFILE("applyGravity");
|
||||||
applyGravity();
|
applyGravity();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i=0;i<clampedSimulationSteps;i++) {
|
for (int i=0;i<clampedSimulationSteps;i++) {
|
||||||
|
DETAILED_PROFILE_RANGE(simulation_physics, "substep");
|
||||||
internalSingleStepSimulation(fixedTimeStep);
|
internalSingleStepSimulation(fixedTimeStep);
|
||||||
onSubStep();
|
onSubStep();
|
||||||
}
|
}
|
||||||
|
@ -118,7 +122,8 @@ void ThreadSafeDynamicsWorld::synchronizeMotionState(btRigidBody* body) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThreadSafeDynamicsWorld::synchronizeMotionStates() {
|
void ThreadSafeDynamicsWorld::synchronizeMotionStates() {
|
||||||
BT_PROFILE("synchronizeMotionStates");
|
PROFILE_RANGE(simulation_physics, "SyncMotionStates");
|
||||||
|
BT_PROFILE("syncMotionStates");
|
||||||
_changedMotionStates.clear();
|
_changedMotionStates.clear();
|
||||||
|
|
||||||
// NOTE: m_synchronizeAllMotionStates is 'false' by default for optimization.
|
// NOTE: m_synchronizeAllMotionStates is 'false' by default for optimization.
|
||||||
|
@ -161,6 +166,7 @@ void ThreadSafeDynamicsWorld::saveKinematicState(btScalar timeStep) {
|
||||||
///would like to iterate over m_nonStaticRigidBodies, but unfortunately old API allows
|
///would like to iterate over m_nonStaticRigidBodies, but unfortunately old API allows
|
||||||
///to switch status _after_ adding kinematic objects to the world
|
///to switch status _after_ adding kinematic objects to the world
|
||||||
///fix it for Bullet 3.x release
|
///fix it for Bullet 3.x release
|
||||||
|
DETAILED_PROFILE_RANGE(simulation_physics, "saveKinematicState");
|
||||||
BT_PROFILE("saveKinematicState");
|
BT_PROFILE("saveKinematicState");
|
||||||
for (int i=0;i<m_collisionObjects.size();i++)
|
for (int i=0;i<m_collisionObjects.size();i++)
|
||||||
{
|
{
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
#include <QtCore/QFileInfo>
|
#include <QtCore/QFileInfo>
|
||||||
#include <QtCore/QDir>
|
#include <QtCore/QDir>
|
||||||
#include <QtCore/QStandardPaths>
|
#include <QtCore/QStandardPaths>
|
||||||
#include <QtCore/QDateTime>
|
|
||||||
|
|
||||||
#include <QtCore/QFile>
|
#include <QtCore/QFile>
|
||||||
#include <QtCore/QFileInfo>
|
#include <QtCore/QFileInfo>
|
||||||
|
@ -31,6 +30,8 @@
|
||||||
|
|
||||||
#include "Gzip.h"
|
#include "Gzip.h"
|
||||||
#include "PortableHighResolutionClock.h"
|
#include "PortableHighResolutionClock.h"
|
||||||
|
#include "SharedLogging.h"
|
||||||
|
#include "shared/FileUtils.h"
|
||||||
#include "shared/GlobalAppProperties.h"
|
#include "shared/GlobalAppProperties.h"
|
||||||
|
|
||||||
using namespace tracing;
|
using namespace tracing;
|
||||||
|
@ -104,30 +105,13 @@ void TraceEvent::writeJson(QTextStream& out) const {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tracer::serialize(const QString& originalPath) {
|
void Tracer::serialize(const QString& filename) {
|
||||||
|
QString fullPath = FileUtils::replaceDateTimeTokens(filename);
|
||||||
QString path = originalPath;
|
fullPath = FileUtils::computeDocumentPath(fullPath);
|
||||||
|
if (!FileUtils::canCreateFile(fullPath)) {
|
||||||
// Filter for specific tokens potentially present in the path:
|
return;
|
||||||
auto now = QDateTime::currentDateTime();
|
|
||||||
|
|
||||||
path = path.replace("{DATE}", now.date().toString("yyyyMMdd"));
|
|
||||||
path = path.replace("{TIME}", now.time().toString("HHmm"));
|
|
||||||
|
|
||||||
// If the filename is relative, turn it into an absolute path relative to the document directory.
|
|
||||||
QFileInfo originalFileInfo(path);
|
|
||||||
if (originalFileInfo.isRelative()) {
|
|
||||||
QString docsLocation = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
|
|
||||||
path = docsLocation + "/" + path;
|
|
||||||
QFileInfo info(path);
|
|
||||||
if (!info.absoluteDir().exists()) {
|
|
||||||
QString originalRelativePath = originalFileInfo.path();
|
|
||||||
QDir(docsLocation).mkpath(originalRelativePath);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
std::list<TraceEvent> currentEvents;
|
std::list<TraceEvent> currentEvents;
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> guard(_eventsMutex);
|
std::lock_guard<std::mutex> guard(_eventsMutex);
|
||||||
|
@ -137,11 +121,6 @@ void Tracer::serialize(const QString& originalPath) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the file exists and we can't remove it, fail early
|
|
||||||
if (QFileInfo(path).exists() && !QFile::remove(path)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we can't open a temp file for writing, fail early
|
// If we can't open a temp file for writing, fail early
|
||||||
QByteArray data;
|
QByteArray data;
|
||||||
{
|
{
|
||||||
|
@ -159,15 +138,16 @@ void Tracer::serialize(const QString& originalPath) {
|
||||||
out << "\n]";
|
out << "\n]";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (path.endsWith(".gz")) {
|
if (fullPath.endsWith(".gz")) {
|
||||||
QByteArray compressed;
|
QByteArray compressed;
|
||||||
gzip(data, compressed);
|
gzip(data, compressed);
|
||||||
data = compressed;
|
data = compressed;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
QFile file(path);
|
QFile file(fullPath);
|
||||||
if (!file.open(QIODevice::WriteOnly)) {
|
if (!file.open(QIODevice::WriteOnly)) {
|
||||||
|
qDebug(shared) << "failed to open file '" << fullPath << "'";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
file.write(data);
|
file.write(data);
|
||||||
|
@ -191,7 +171,6 @@ void Tracer::serialize(const QString& originalPath) {
|
||||||
} }
|
} }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
data = document.toJson(QJsonDocument::Compact);
|
data = document.toJson(QJsonDocument::Compact);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
#include "FileUtils.h"
|
#include "FileUtils.h"
|
||||||
|
|
||||||
|
#include <QtCore/QDateTime>
|
||||||
#include <QtCore/QDir>
|
#include <QtCore/QDir>
|
||||||
#include <QtCore/QFileInfo>
|
#include <QtCore/QFileInfo>
|
||||||
#include <QtCore/QProcess>
|
#include <QtCore/QProcess>
|
||||||
|
@ -20,6 +21,8 @@
|
||||||
#include <QtCore/QRegularExpression>
|
#include <QtCore/QRegularExpression>
|
||||||
#include <QtGui/QDesktopServices>
|
#include <QtGui/QDesktopServices>
|
||||||
|
|
||||||
|
#include "../SharedLogging.h"
|
||||||
|
|
||||||
|
|
||||||
QString FileUtils::readFile(const QString& filename) {
|
QString FileUtils::readFile(const QString& filename) {
|
||||||
QFile file(filename);
|
QFile file(filename);
|
||||||
|
@ -82,20 +85,54 @@ QString FileUtils::standardPath(QString subfolder) {
|
||||||
// standard path
|
// standard path
|
||||||
// Mac: ~/Library/Application Support/Interface
|
// Mac: ~/Library/Application Support/Interface
|
||||||
QString path = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
|
QString path = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
|
||||||
|
|
||||||
if (!subfolder.startsWith("/")) {
|
if (!subfolder.startsWith("/")) {
|
||||||
subfolder.prepend("/");
|
subfolder.prepend("/");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!subfolder.endsWith("/")) {
|
if (!subfolder.endsWith("/")) {
|
||||||
subfolder.append("/");
|
subfolder.append("/");
|
||||||
}
|
}
|
||||||
|
|
||||||
path.append(subfolder);
|
path.append(subfolder);
|
||||||
QDir logDir(path);
|
QDir logDir(path);
|
||||||
if (!logDir.exists(path)) {
|
if (!logDir.exists(path)) {
|
||||||
logDir.mkpath(path);
|
logDir.mkpath(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString FileUtils::replaceDateTimeTokens(const QString& originalPath) {
|
||||||
|
// Filter for specific tokens potentially present in the path:
|
||||||
|
auto now = QDateTime::currentDateTime();
|
||||||
|
QString path = originalPath;
|
||||||
|
path.replace("{DATE}", now.date().toString("yyyyMMdd"));
|
||||||
|
path.replace("{TIME}", now.time().toString("HHmm"));
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QString FileUtils::computeDocumentPath(const QString& originalPath) {
|
||||||
|
// If the filename is relative, turn it into an absolute path relative to the document directory.
|
||||||
|
QString path = originalPath;
|
||||||
|
QFileInfo originalFileInfo(originalPath);
|
||||||
|
if (originalFileInfo.isRelative()) {
|
||||||
|
QString docsLocation = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
|
||||||
|
path = docsLocation + "/" + originalPath;
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileUtils::canCreateFile(const QString& fullPath) {
|
||||||
|
// If the file exists and we can't remove it, fail early
|
||||||
|
QFileInfo fileInfo(fullPath);
|
||||||
|
if (fileInfo.exists() && !QFile::remove(fullPath)) {
|
||||||
|
qDebug(shared) << "unable to overwrite file '" << fullPath << "'";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
QDir dir(fileInfo.absolutePath());
|
||||||
|
if (!dir.exists()) {
|
||||||
|
if (!dir.mkpath(fullPath)) {
|
||||||
|
qDebug(shared) << "unable to create dir '" << dir.absolutePath() << "'";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
@ -21,6 +21,9 @@ public:
|
||||||
static QString standardPath(QString subfolder);
|
static QString standardPath(QString subfolder);
|
||||||
static QString readFile(const QString& filename);
|
static QString readFile(const QString& filename);
|
||||||
static QStringList readLines(const QString& filename, QString::SplitBehavior splitBehavior = QString::KeepEmptyParts);
|
static QStringList readLines(const QString& filename, QString::SplitBehavior splitBehavior = QString::KeepEmptyParts);
|
||||||
|
static QString replaceDateTimeTokens(const QString& path);
|
||||||
|
static QString computeDocumentPath(const QString& path);
|
||||||
|
static bool canCreateFile(const QString& fullPath);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_FileUtils_h
|
#endif // hifi_FileUtils_h
|
||||||
|
|
Loading…
Reference in a new issue