1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-06-03 08:11:32 +00:00

Use shared_ptr instead of weak_ptr for actors handle inside the

simulation

The purpose of weak_ptr is to avoid performing the simulation on deleted
Actor by promoting it to a shared_ptr via a lock() call. This clutter
the code with a lot of branches, whereas the overwhelmingly common case is for the call to succeed.
Since the simulation is decoupled from the engine state, we can use a shared_ptr instead of a weak_ptr.
This allow us to ignore (ie not handle) the rarer case where the actor is delete from the scene. This means that the simulation
will run for one frame more than before for each actor, whereas the rest of the engine
will be ignorant of this.
This commit is contained in:
fredzio 2021-08-14 23:15:11 +02:00
parent 640998aca0
commit ec871e6bf7
4 changed files with 61 additions and 79 deletions

View file

@ -207,12 +207,13 @@ namespace MWPhysics
return std::make_tuple(numSteps, actualDelta); return std::make_tuple(numSteps, actualDelta);
} }
void PhysicsTaskScheduler::applyQueuedMovements(float & timeAccum, std::vector<std::weak_ptr<Actor>>&& actors, std::vector<ActorFrameData>&& actorsData, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats) void PhysicsTaskScheduler::applyQueuedMovements(float & timeAccum, std::vector<std::shared_ptr<Actor>>&& actors, std::vector<ActorFrameData>&& actorsData, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats)
{ {
// This function run in the main thread. // This function run in the main thread.
// While the mSimulationMutex is held, background physics threads can't run. // While the mSimulationMutex is held, background physics threads can't run.
std::unique_lock lock(mSimulationMutex); std::unique_lock lock(mSimulationMutex);
assert(actors.size() == actorsData.size());
double timeStart = mTimer->tick(); double timeStart = mTimer->tick();
@ -221,11 +222,8 @@ namespace MWPhysics
{ {
for (size_t i = 0; i < mActors.size(); ++i) for (size_t i = 0; i < mActors.size(); ++i)
{ {
if (auto actor = mActors[i].lock()) updateMechanics(*mActors[i], mActorsFrameData[i]);
{ updateActor(*mActors[i], mActorsFrameData[i], mAdvanceSimulation, mTimeAccum, mPhysicsDt);
updateMechanics(*actor, mActorsFrameData[i]);
updateActor(*actor, mActorsFrameData[i], mAdvanceSimulation, mTimeAccum, mPhysicsDt);
}
} }
if(mAdvanceSimulation) if(mAdvanceSimulation)
mAsyncBudget.update(mTimer->delta_s(mAsyncStartTime, mTimeEnd), mPrevStepCount, mBudgetCursor); mAsyncBudget.update(mTimer->delta_s(mAsyncStartTime, mTimeEnd), mPrevStepCount, mBudgetCursor);
@ -238,8 +236,7 @@ namespace MWPhysics
// init // init
for (size_t i = 0; i < actors.size(); ++i) for (size_t i = 0; i < actors.size(); ++i)
{ {
assert(actors[i].lock()); actorsData[i].updatePosition(*actors[i], mCollisionWorld);
actorsData[i].updatePosition(*actors[i].lock(), mCollisionWorld);
} }
mPrevStepCount = numSteps; mPrevStepCount = numSteps;
mRemainingSteps = numSteps; mRemainingSteps = numSteps;
@ -355,7 +352,7 @@ namespace MWPhysics
mCollisionWorld->removeCollisionObject(collisionObject); mCollisionWorld->removeCollisionObject(collisionObject);
} }
void PhysicsTaskScheduler::updateSingleAabb(std::weak_ptr<PtrHolder> ptr, bool immediate) void PhysicsTaskScheduler::updateSingleAabb(std::shared_ptr<PtrHolder> ptr, bool immediate)
{ {
if (immediate || mNumThreads == 0) if (immediate || mNumThreads == 0)
{ {
@ -368,20 +365,15 @@ namespace MWPhysics
} }
} }
bool PhysicsTaskScheduler::getLineOfSight(const std::weak_ptr<Actor>& actor1, const std::weak_ptr<Actor>& actor2) bool PhysicsTaskScheduler::getLineOfSight(const std::shared_ptr<Actor>& actor1, const std::shared_ptr<Actor>& actor2)
{ {
std::unique_lock lock(mLOSCacheMutex); std::unique_lock lock(mLOSCacheMutex);
auto actorPtr1 = actor1.lock();
auto actorPtr2 = actor2.lock();
if (!actorPtr1 || !actorPtr2)
return false;
auto req = LOSRequest(actor1, actor2); auto req = LOSRequest(actor1, actor2);
auto result = std::find(mLOSCache.begin(), mLOSCache.end(), req); auto result = std::find(mLOSCache.begin(), mLOSCache.end(), req);
if (result == mLOSCache.end()) if (result == mLOSCache.end())
{ {
req.mResult = hasLineOfSight(actorPtr1.get(), actorPtr2.get()); req.mResult = hasLineOfSight(actor1.get(), actor2.get());
mLOSCache.push_back(req); mLOSCache.push_back(req);
return req.mResult; return req.mResult;
} }
@ -412,31 +404,28 @@ namespace MWPhysics
{ {
std::scoped_lock lock(mUpdateAabbMutex); std::scoped_lock lock(mUpdateAabbMutex);
std::for_each(mUpdateAabb.begin(), mUpdateAabb.end(), std::for_each(mUpdateAabb.begin(), mUpdateAabb.end(),
[this](const std::weak_ptr<PtrHolder>& ptr) { updatePtrAabb(ptr); }); [this](const std::shared_ptr<PtrHolder>& ptr) { updatePtrAabb(ptr); });
mUpdateAabb.clear(); mUpdateAabb.clear();
} }
void PhysicsTaskScheduler::updatePtrAabb(const std::weak_ptr<PtrHolder>& ptr) void PhysicsTaskScheduler::updatePtrAabb(const std::shared_ptr<PtrHolder>& ptr)
{ {
if (const auto p = ptr.lock()) std::scoped_lock lock(mCollisionWorldMutex);
if (const auto actor = std::dynamic_pointer_cast<Actor>(ptr))
{ {
std::scoped_lock lock(mCollisionWorldMutex); actor->updateCollisionObjectPosition();
if (const auto actor = std::dynamic_pointer_cast<Actor>(p)) mCollisionWorld->updateSingleAabb(actor->getCollisionObject());
{ }
actor->updateCollisionObjectPosition(); else if (const auto object = std::dynamic_pointer_cast<Object>(ptr))
mCollisionWorld->updateSingleAabb(actor->getCollisionObject()); {
} object->commitPositionChange();
else if (const auto object = std::dynamic_pointer_cast<Object>(p)) mCollisionWorld->updateSingleAabb(object->getCollisionObject());
{ }
object->commitPositionChange(); else if (const auto projectile = std::dynamic_pointer_cast<Projectile>(ptr))
mCollisionWorld->updateSingleAabb(object->getCollisionObject()); {
} projectile->commitPositionChange();
else if (const auto projectile = std::dynamic_pointer_cast<Projectile>(p)) mCollisionWorld->updateSingleAabb(projectile->getCollisionObject());
{ }
projectile->commitPositionChange();
mCollisionWorld->updateSingleAabb(projectile->getCollisionObject());
}
};
} }
void PhysicsTaskScheduler::worker() void PhysicsTaskScheduler::worker()
@ -452,11 +441,8 @@ namespace MWPhysics
int job = 0; int job = 0;
while (mRemainingSteps && (job = mNextJob.fetch_add(1, std::memory_order_relaxed)) < mNumJobs) while (mRemainingSteps && (job = mNextJob.fetch_add(1, std::memory_order_relaxed)) < mNumJobs)
{ {
if(const auto actor = mActors[job].lock()) MaybeSharedLock lockColWorld(mCollisionWorldMutex, mThreadSafeBullet);
{ MovementSolver::move(mActorsFrameData[job], mPhysicsDt, mCollisionWorld, *mWorldFrameData);
MaybeSharedLock lockColWorld(mCollisionWorldMutex, mThreadSafeBullet);
MovementSolver::move(mActorsFrameData[job], mPhysicsDt, mCollisionWorld, *mWorldFrameData);
}
} }
mPostStepBarrier->wait([this] { afterPostStep(); }); mPostStepBarrier->wait([this] { afterPostStep(); });
@ -465,10 +451,7 @@ namespace MWPhysics
{ {
while ((job = mNextJob.fetch_add(1, std::memory_order_relaxed)) < mNumJobs) while ((job = mNextJob.fetch_add(1, std::memory_order_relaxed)) < mNumJobs)
{ {
if(const auto actor = mActors[job].lock()) handleFall(mActorsFrameData[job], mAdvanceSimulation);
{
handleFall(mActorsFrameData[job], mAdvanceSimulation);
}
} }
refreshLOSCache(); refreshLOSCache();
@ -481,15 +464,12 @@ namespace MWPhysics
{ {
for (size_t i = 0; i < mActors.size(); ++i) for (size_t i = 0; i < mActors.size(); ++i)
{ {
if(const auto actor = mActors[i].lock()) if (mActors[i]->setPosition(mActorsFrameData[i].mPosition))
{ {
if (actor->setPosition(mActorsFrameData[i].mPosition)) std::scoped_lock lock(mCollisionWorldMutex);
{ mActorsFrameData[i].mPosition = mActors[i]->getPosition(); // account for potential position change made by script
std::scoped_lock lock(mCollisionWorldMutex); mActors[i]->updateCollisionObjectPosition();
mActorsFrameData[i].mPosition = actor->getPosition(); // account for potential position change made by script mCollisionWorld->updateSingleAabb(mActors[i]->getCollisionObject());
actor->updateCollisionObjectPosition();
mCollisionWorld->updateSingleAabb(actor->getCollisionObject());
}
} }
} }
} }
@ -545,11 +525,9 @@ namespace MWPhysics
for (size_t i = 0; i < mActors.size(); ++i) for (size_t i = 0; i < mActors.size(); ++i)
{ {
auto actor = mActors[i].lock();
assert(actor);
handleFall(mActorsFrameData[i], mAdvanceSimulation); handleFall(mActorsFrameData[i], mAdvanceSimulation);
updateMechanics(*actor, mActorsFrameData[i]); updateMechanics(*mActors[i], mActorsFrameData[i]);
updateActor(*actor, mActorsFrameData[i], mAdvanceSimulation, mTimeAccum, mPhysicsDt); updateActor(*mActors[i], mActorsFrameData[i], mAdvanceSimulation, mTimeAccum, mPhysicsDt);
} }
refreshLOSCache(); refreshLOSCache();
} }
@ -583,6 +561,13 @@ namespace MWPhysics
return (*it)->getUserPointer(); return (*it)->getUserPointer();
} }
void PhysicsTaskScheduler::releaseSharedStates()
{
std::scoped_lock lock(mSimulationMutex, mUpdateAabbMutex);
mActors.clear();
mUpdateAabb.clear();
}
void PhysicsTaskScheduler::afterPreStep() void PhysicsTaskScheduler::afterPreStep()
{ {
updateAabbs(); updateAabbs();
@ -590,11 +575,8 @@ namespace MWPhysics
return; return;
for (size_t i = 0; i < mActors.size(); ++i) for (size_t i = 0; i < mActors.size(); ++i)
{ {
if (auto actor = mActors[i].lock()) std::unique_lock lock(mCollisionWorldMutex);
{ MovementSolver::unstuck(mActorsFrameData[i], mCollisionWorld);
std::unique_lock lock(mCollisionWorldMutex);
MovementSolver::unstuck(mActorsFrameData[i], mCollisionWorld);
}
} }
} }

View file

@ -39,7 +39,7 @@ namespace MWPhysics
/// @param timeAccum accumulated time from previous run to interpolate movements /// @param timeAccum accumulated time from previous run to interpolate movements
/// @param actorsData per actor data needed to compute new positions /// @param actorsData per actor data needed to compute new positions
/// @return new position of each actor /// @return new position of each actor
void applyQueuedMovements(float & timeAccum, std::vector<std::weak_ptr<Actor>>&& actors, std::vector<ActorFrameData>&& actorsData, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats); void applyQueuedMovements(float & timeAccum, std::vector<std::shared_ptr<Actor>>&& actors, std::vector<ActorFrameData>&& actorsData, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats);
void resetSimulation(const ActorMap& actors); void resetSimulation(const ActorMap& actors);
@ -53,11 +53,13 @@ namespace MWPhysics
void setCollisionFilterMask(btCollisionObject* collisionObject, int collisionFilterMask); void setCollisionFilterMask(btCollisionObject* collisionObject, int collisionFilterMask);
void addCollisionObject(btCollisionObject* collisionObject, int collisionFilterGroup, int collisionFilterMask); void addCollisionObject(btCollisionObject* collisionObject, int collisionFilterGroup, int collisionFilterMask);
void removeCollisionObject(btCollisionObject* collisionObject); void removeCollisionObject(btCollisionObject* collisionObject);
void updateSingleAabb(std::weak_ptr<PtrHolder> ptr, bool immediate=false); void updateSingleAabb(std::shared_ptr<PtrHolder> ptr, bool immediate=false);
bool getLineOfSight(const std::weak_ptr<Actor>& actor1, const std::weak_ptr<Actor>& actor2); bool getLineOfSight(const std::shared_ptr<Actor>& actor1, const std::shared_ptr<Actor>& actor2);
void debugDraw(); void debugDraw();
void* getUserPointer(const btCollisionObject* object) const; void* getUserPointer(const btCollisionObject* object) const;
void releaseSharedStates(); // destroy all objects whose destructor can't be safely called from ~PhysicsTaskScheduler()
private: private:
void syncComputation(); void syncComputation();
void worker(); void worker();
@ -66,7 +68,7 @@ namespace MWPhysics
bool hasLineOfSight(const Actor* actor1, const Actor* actor2); bool hasLineOfSight(const Actor* actor1, const Actor* actor2);
void refreshLOSCache(); void refreshLOSCache();
void updateAabbs(); void updateAabbs();
void updatePtrAabb(const std::weak_ptr<PtrHolder>& ptr); void updatePtrAabb(const std::shared_ptr<PtrHolder>& ptr);
void updateStats(osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats); void updateStats(osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats);
std::tuple<int, float> calculateStepConfig(float timeAccum) const; std::tuple<int, float> calculateStepConfig(float timeAccum) const;
void afterPreStep(); void afterPreStep();
@ -74,7 +76,7 @@ namespace MWPhysics
void afterPostSim(); void afterPostSim();
std::unique_ptr<WorldFrameData> mWorldFrameData; std::unique_ptr<WorldFrameData> mWorldFrameData;
std::vector<std::weak_ptr<Actor>> mActors; std::vector<std::shared_ptr<Actor>> mActors;
std::vector<ActorFrameData> mActorsFrameData; std::vector<ActorFrameData> mActorsFrameData;
std::unordered_set<const btCollisionObject*> mCollisionObjects; std::unordered_set<const btCollisionObject*> mCollisionObjects;
float mDefaultPhysicsDt; float mDefaultPhysicsDt;
@ -83,7 +85,7 @@ namespace MWPhysics
btCollisionWorld* mCollisionWorld; btCollisionWorld* mCollisionWorld;
MWRender::DebugDrawer* mDebugDrawer; MWRender::DebugDrawer* mDebugDrawer;
std::vector<LOSRequest> mLOSCache; std::vector<LOSRequest> mLOSCache;
std::set<std::weak_ptr<PtrHolder>, std::owner_less<std::weak_ptr<PtrHolder>>> mUpdateAabb; std::set<std::shared_ptr<PtrHolder>> mUpdateAabb;
// TODO: use std::experimental::flex_barrier or std::barrier once it becomes a thing // TODO: use std::experimental::flex_barrier or std::barrier once it becomes a thing
std::unique_ptr<Misc::Barrier> mPreStepBarrier; std::unique_ptr<Misc::Barrier> mPreStepBarrier;

View file

@ -153,6 +153,7 @@ namespace MWPhysics
if (mWaterCollisionObject) if (mWaterCollisionObject)
mTaskScheduler->removeCollisionObject(mWaterCollisionObject.get()); mTaskScheduler->removeCollisionObject(mWaterCollisionObject.get());
mTaskScheduler->releaseSharedStates();
mHeightFields.clear(); mHeightFields.clear();
mObjects.clear(); mObjects.clear();
mActors.clear(); mActors.clear();
@ -373,15 +374,12 @@ namespace MWPhysics
bool PhysicsSystem::getLineOfSight(const MWWorld::ConstPtr &actor1, const MWWorld::ConstPtr &actor2) const bool PhysicsSystem::getLineOfSight(const MWWorld::ConstPtr &actor1, const MWWorld::ConstPtr &actor2) const
{ {
const auto getWeakPtr = [&](const MWWorld::ConstPtr &ptr) -> std::weak_ptr<Actor> const auto it1 = mActors.find(actor1);
{ const auto it2 = mActors.find(actor2);
const auto found = mActors.find(ptr); if (it1 == mActors.end() || it2 == mActors.end())
if (found != mActors.end()) return false;
return { found->second };
return {};
};
return mTaskScheduler->getLineOfSight(getWeakPtr(actor1), getWeakPtr(actor2)); return mTaskScheduler->getLineOfSight(it1->second, it2->second);
} }
bool PhysicsSystem::isOnGround(const MWWorld::Ptr &actor) bool PhysicsSystem::isOnGround(const MWWorld::Ptr &actor)
@ -769,9 +767,9 @@ namespace MWPhysics
actor->setVelocity(osg::Vec3f()); actor->setVelocity(osg::Vec3f());
} }
std::pair<std::vector<std::weak_ptr<Actor>>, std::vector<ActorFrameData>> PhysicsSystem::prepareFrameData(bool willSimulate) std::pair<std::vector<std::shared_ptr<Actor>>, std::vector<ActorFrameData>> PhysicsSystem::prepareFrameData(bool willSimulate)
{ {
std::pair<std::vector<std::weak_ptr<Actor>>, std::vector<ActorFrameData>> framedata; std::pair<std::vector<std::shared_ptr<Actor>>, std::vector<ActorFrameData>> framedata;
framedata.first.reserve(mActors.size()); framedata.first.reserve(mActors.size());
framedata.second.reserve(mActors.size()); framedata.second.reserve(mActors.size());
const MWBase::World *world = MWBase::Environment::get().getWorld(); const MWBase::World *world = MWBase::Environment::get().getWorld();

View file

@ -259,7 +259,7 @@ namespace MWPhysics
void updateWater(); void updateWater();
std::pair<std::vector<std::weak_ptr<Actor>>, std::vector<ActorFrameData>> prepareFrameData(bool willSimulate); std::pair<std::vector<std::shared_ptr<Actor>>, std::vector<ActorFrameData>> prepareFrameData(bool willSimulate);
osg::ref_ptr<SceneUtil::UnrefQueue> mUnrefQueue; osg::ref_ptr<SceneUtil::UnrefQueue> mUnrefQueue;