mirror of
https://github.com/OpenMW/openmw.git
synced 2025-03-27 04:40:26 +00:00
Merge branch 'sync_sync_with_async' into 'master'
Merge logic of sync and async physics simulation See merge request OpenMW/openmw!1250
This commit is contained in:
commit
7af245d205
5 changed files with 133 additions and 115 deletions
|
@ -22,31 +22,82 @@
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
/// @brief A scoped lock that is either shared or exclusive depending on configuration
|
/// @brief A scoped lock that is either exclusive or inexistent depending on configuration
|
||||||
|
template<class Mutex>
|
||||||
|
class MaybeExclusiveLock
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// @param mutex a mutex
|
||||||
|
/// @param threadCount decide wether the excluse lock will be taken
|
||||||
|
MaybeExclusiveLock(Mutex& mutex, int threadCount) : mMutex(mutex), mThreadCount(threadCount)
|
||||||
|
{
|
||||||
|
assert(threadCount >= 0);
|
||||||
|
if (mThreadCount > 0)
|
||||||
|
mMutex.lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
~MaybeExclusiveLock()
|
||||||
|
{
|
||||||
|
if (mThreadCount > 0)
|
||||||
|
mMutex.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Mutex& mMutex;
|
||||||
|
unsigned int mThreadCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @brief A scoped lock that is either shared or inexistent depending on configuration
|
||||||
template<class Mutex>
|
template<class Mutex>
|
||||||
class MaybeSharedLock
|
class MaybeSharedLock
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// @param mutex a shared mutex
|
/// @param mutex a shared mutex
|
||||||
/// @param canBeSharedLock decide wether the lock will be shared or exclusive
|
/// @param threadCount decide wether the shared lock will be taken
|
||||||
MaybeSharedLock(Mutex& mutex, bool canBeSharedLock) : mMutex(mutex), mCanBeSharedLock(canBeSharedLock)
|
MaybeSharedLock(Mutex& mutex, int threadCount) : mMutex(mutex), mThreadCount(threadCount)
|
||||||
{
|
{
|
||||||
if (mCanBeSharedLock)
|
assert(threadCount >= 0);
|
||||||
|
if (mThreadCount > 0)
|
||||||
mMutex.lock_shared();
|
mMutex.lock_shared();
|
||||||
else
|
|
||||||
mMutex.lock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~MaybeSharedLock()
|
~MaybeSharedLock()
|
||||||
{
|
{
|
||||||
if (mCanBeSharedLock)
|
if (mThreadCount > 0)
|
||||||
mMutex.unlock_shared();
|
mMutex.unlock_shared();
|
||||||
else
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Mutex& mMutex;
|
||||||
|
unsigned int mThreadCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @brief A scoped lock that is either shared, exclusive or inexistent depending on configuration
|
||||||
|
template<class Mutex>
|
||||||
|
class MaybeLock
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// @param mutex a shared mutex
|
||||||
|
/// @param threadCount decide wether the lock will be shared, exclusive or inexistent
|
||||||
|
MaybeLock(Mutex& mutex, int threadCount) : mMutex(mutex), mThreadCount(threadCount)
|
||||||
|
{
|
||||||
|
assert(threadCount >= 0);
|
||||||
|
if (mThreadCount > 1)
|
||||||
|
mMutex.lock_shared();
|
||||||
|
else if(mThreadCount == 1)
|
||||||
|
mMutex.lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
~MaybeLock()
|
||||||
|
{
|
||||||
|
if (mThreadCount > 1)
|
||||||
|
mMutex.unlock_shared();
|
||||||
|
else if(mThreadCount == 1)
|
||||||
mMutex.unlock();
|
mMutex.unlock();
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
Mutex& mMutex;
|
Mutex& mMutex;
|
||||||
bool mCanBeSharedLock;
|
unsigned int mThreadCount;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool isUnderWater(const MWPhysics::ActorFrameData& actorData)
|
bool isUnderWater(const MWPhysics::ActorFrameData& actorData)
|
||||||
|
@ -54,29 +105,6 @@ namespace
|
||||||
return actorData.mPosition.z() < actorData.mSwimLevel;
|
return actorData.mPosition.z() < actorData.mSwimLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleFall(MWPhysics::ActorFrameData& actorData, bool simulationPerformed)
|
|
||||||
{
|
|
||||||
const float heightDiff = actorData.mPosition.z() - actorData.mOldHeight;
|
|
||||||
|
|
||||||
const bool isStillOnGround = (simulationPerformed && actorData.mWasOnGround && actorData.mIsOnGround);
|
|
||||||
|
|
||||||
if (isStillOnGround || actorData.mFlying || isUnderWater(actorData) || actorData.mSlowFall < 1)
|
|
||||||
actorData.mNeedLand = true;
|
|
||||||
else if (heightDiff < 0)
|
|
||||||
actorData.mFallHeight += heightDiff;
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateMechanics(MWPhysics::Actor& actor, MWPhysics::ActorFrameData& actorData)
|
|
||||||
{
|
|
||||||
auto ptr = actor.getPtr();
|
|
||||||
|
|
||||||
MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr);
|
|
||||||
if (actorData.mNeedLand)
|
|
||||||
stats.land(ptr == MWMechanics::getPlayer() && (actorData.mFlying || isUnderWater(actorData)));
|
|
||||||
else if (actorData.mFallHeight < 0)
|
|
||||||
stats.addToFallHeight(-actorData.mFallHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
osg::Vec3f interpolateMovements(MWPhysics::Actor& actor, MWPhysics::ActorFrameData& actorData, float timeAccum, float physicsDt)
|
osg::Vec3f interpolateMovements(MWPhysics::Actor& actor, MWPhysics::ActorFrameData& actorData, float timeAccum, float physicsDt)
|
||||||
{
|
{
|
||||||
const float interpolationFactor = std::clamp(timeAccum / physicsDt, 0.0f, 1.0f);
|
const float interpolationFactor = std::clamp(timeAccum / physicsDt, 0.0f, 1.0f);
|
||||||
|
@ -85,14 +113,14 @@ namespace
|
||||||
|
|
||||||
namespace Config
|
namespace Config
|
||||||
{
|
{
|
||||||
/// @return either the number of thread as configured by the user, or 1 if Bullet doesn't support multithreading
|
/// @return either the number of thread as configured by the user, or 1 if Bullet doesn't support multithreading and user requested more than 1 background threads
|
||||||
int computeNumThreads(bool& threadSafeBullet)
|
int computeNumThreads()
|
||||||
{
|
{
|
||||||
int wantedThread = Settings::Manager::getInt("async num threads", "Physics");
|
int wantedThread = Settings::Manager::getInt("async num threads", "Physics");
|
||||||
|
|
||||||
auto broad = std::make_unique<btDbvtBroadphase>();
|
auto broad = std::make_unique<btDbvtBroadphase>();
|
||||||
auto maxSupportedThreads = broad->m_rayTestStacks.size();
|
auto maxSupportedThreads = broad->m_rayTestStacks.size();
|
||||||
threadSafeBullet = (maxSupportedThreads > 1);
|
auto threadSafeBullet = (maxSupportedThreads > 1);
|
||||||
if (!threadSafeBullet && wantedThread > 1)
|
if (!threadSafeBullet && wantedThread > 1)
|
||||||
{
|
{
|
||||||
Log(Debug::Warning) << "Bullet was not compiled with multithreading support, 1 async thread will be used";
|
Log(Debug::Warning) << "Bullet was not compiled with multithreading support, 1 async thread will be used";
|
||||||
|
@ -111,6 +139,7 @@ namespace MWPhysics
|
||||||
, mTimeAccum(0.f)
|
, mTimeAccum(0.f)
|
||||||
, mCollisionWorld(collisionWorld)
|
, mCollisionWorld(collisionWorld)
|
||||||
, mDebugDrawer(debugDrawer)
|
, mDebugDrawer(debugDrawer)
|
||||||
|
, mNumThreads(Config::computeNumThreads())
|
||||||
, mNumJobs(0)
|
, mNumJobs(0)
|
||||||
, mRemainingSteps(0)
|
, mRemainingSteps(0)
|
||||||
, mLOSCacheExpiry(Settings::Manager::getInt("lineofsight keep inactive cache", "Physics"))
|
, mLOSCacheExpiry(Settings::Manager::getInt("lineofsight keep inactive cache", "Physics"))
|
||||||
|
@ -130,8 +159,6 @@ namespace MWPhysics
|
||||||
, mTimeEnd(0)
|
, mTimeEnd(0)
|
||||||
, mFrameStart(0)
|
, mFrameStart(0)
|
||||||
{
|
{
|
||||||
mNumThreads = Config::computeNumThreads(mThreadSafeBullet);
|
|
||||||
|
|
||||||
if (mNumThreads >= 1)
|
if (mNumThreads >= 1)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < mNumThreads; ++i)
|
for (int i = 0; i < mNumThreads; ++i)
|
||||||
|
@ -151,11 +178,12 @@ namespace MWPhysics
|
||||||
|
|
||||||
PhysicsTaskScheduler::~PhysicsTaskScheduler()
|
PhysicsTaskScheduler::~PhysicsTaskScheduler()
|
||||||
{
|
{
|
||||||
std::unique_lock lock(mSimulationMutex);
|
{
|
||||||
|
MaybeExclusiveLock lock(mSimulationMutex, mNumThreads);
|
||||||
mQuit = true;
|
mQuit = true;
|
||||||
mNumJobs = 0;
|
mNumJobs = 0;
|
||||||
mRemainingSteps = 0;
|
mRemainingSteps = 0;
|
||||||
lock.unlock();
|
}
|
||||||
mHasJob.notify_all();
|
mHasJob.notify_all();
|
||||||
for (auto& thread : mThreads)
|
for (auto& thread : mThreads)
|
||||||
thread.join();
|
thread.join();
|
||||||
|
@ -212,7 +240,7 @@ namespace MWPhysics
|
||||||
// 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);
|
MaybeExclusiveLock lock(mSimulationMutex, mNumThreads);
|
||||||
assert(actors.size() == actorsData.size());
|
assert(actors.size() == actorsData.size());
|
||||||
|
|
||||||
double timeStart = mTimer->tick();
|
double timeStart = mTimer->tick();
|
||||||
|
@ -220,11 +248,8 @@ namespace MWPhysics
|
||||||
// start by finishing previous background computation
|
// start by finishing previous background computation
|
||||||
if (mNumThreads != 0)
|
if (mNumThreads != 0)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < mActors.size(); ++i)
|
syncWithMainThread();
|
||||||
{
|
|
||||||
updateMechanics(*mActors[i], mActorsFrameData[i]);
|
|
||||||
updateActor(*mActors[i], 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);
|
||||||
updateStats(frameStart, frameNumber, stats);
|
updateStats(frameStart, frameNumber, stats);
|
||||||
|
@ -258,14 +283,14 @@ namespace MWPhysics
|
||||||
|
|
||||||
if (mNumThreads == 0)
|
if (mNumThreads == 0)
|
||||||
{
|
{
|
||||||
syncComputation();
|
doSimulation();
|
||||||
|
syncWithMainThread();
|
||||||
if(mAdvanceSimulation)
|
if(mAdvanceSimulation)
|
||||||
mBudget.update(mTimer->delta_s(timeStart, mTimer->tick()), numSteps, mBudgetCursor);
|
mBudget.update(mTimer->delta_s(timeStart, mTimer->tick()), numSteps, mBudgetCursor);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mAsyncStartTime = mTimer->tick();
|
mAsyncStartTime = mTimer->tick();
|
||||||
lock.unlock();
|
|
||||||
mHasJob.notify_all();
|
mHasJob.notify_all();
|
||||||
if (mAdvanceSimulation)
|
if (mAdvanceSimulation)
|
||||||
mBudget.update(mTimer->delta_s(timeStart, mTimer->tick()), 1, mBudgetCursor);
|
mBudget.update(mTimer->delta_s(timeStart, mTimer->tick()), 1, mBudgetCursor);
|
||||||
|
@ -273,7 +298,7 @@ namespace MWPhysics
|
||||||
|
|
||||||
void PhysicsTaskScheduler::resetSimulation(const ActorMap& actors)
|
void PhysicsTaskScheduler::resetSimulation(const ActorMap& actors)
|
||||||
{
|
{
|
||||||
std::unique_lock lock(mSimulationMutex);
|
MaybeExclusiveLock lock(mSimulationMutex, mNumThreads);
|
||||||
mBudget.reset(mDefaultPhysicsDt);
|
mBudget.reset(mDefaultPhysicsDt);
|
||||||
mAsyncBudget.reset(0.0f);
|
mAsyncBudget.reset(0.0f);
|
||||||
mActors.clear();
|
mActors.clear();
|
||||||
|
@ -287,25 +312,25 @@ namespace MWPhysics
|
||||||
|
|
||||||
void PhysicsTaskScheduler::rayTest(const btVector3& rayFromWorld, const btVector3& rayToWorld, btCollisionWorld::RayResultCallback& resultCallback) const
|
void PhysicsTaskScheduler::rayTest(const btVector3& rayFromWorld, const btVector3& rayToWorld, btCollisionWorld::RayResultCallback& resultCallback) const
|
||||||
{
|
{
|
||||||
MaybeSharedLock lock(mCollisionWorldMutex, mThreadSafeBullet);
|
MaybeLock lock(mCollisionWorldMutex, mNumThreads);
|
||||||
mCollisionWorld->rayTest(rayFromWorld, rayToWorld, resultCallback);
|
mCollisionWorld->rayTest(rayFromWorld, rayToWorld, resultCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhysicsTaskScheduler::convexSweepTest(const btConvexShape* castShape, const btTransform& from, const btTransform& to, btCollisionWorld::ConvexResultCallback& resultCallback) const
|
void PhysicsTaskScheduler::convexSweepTest(const btConvexShape* castShape, const btTransform& from, const btTransform& to, btCollisionWorld::ConvexResultCallback& resultCallback) const
|
||||||
{
|
{
|
||||||
MaybeSharedLock lock(mCollisionWorldMutex, mThreadSafeBullet);
|
MaybeLock lock(mCollisionWorldMutex, mNumThreads);
|
||||||
mCollisionWorld->convexSweepTest(castShape, from, to, resultCallback);
|
mCollisionWorld->convexSweepTest(castShape, from, to, resultCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhysicsTaskScheduler::contactTest(btCollisionObject* colObj, btCollisionWorld::ContactResultCallback& resultCallback)
|
void PhysicsTaskScheduler::contactTest(btCollisionObject* colObj, btCollisionWorld::ContactResultCallback& resultCallback)
|
||||||
{
|
{
|
||||||
std::shared_lock lock(mCollisionWorldMutex);
|
MaybeSharedLock lock(mCollisionWorldMutex, mNumThreads);
|
||||||
ContactTestWrapper::contactTest(mCollisionWorld, colObj, resultCallback);
|
ContactTestWrapper::contactTest(mCollisionWorld, colObj, resultCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<btVector3> PhysicsTaskScheduler::getHitPoint(const btTransform& from, btCollisionObject* target)
|
std::optional<btVector3> PhysicsTaskScheduler::getHitPoint(const btTransform& from, btCollisionObject* target)
|
||||||
{
|
{
|
||||||
MaybeSharedLock lock(mCollisionWorldMutex, mThreadSafeBullet);
|
MaybeLock lock(mCollisionWorldMutex, mNumThreads);
|
||||||
// target the collision object's world origin, this should be the center of the collision object
|
// target the collision object's world origin, this should be the center of the collision object
|
||||||
btTransform rayTo;
|
btTransform rayTo;
|
||||||
rayTo.setIdentity();
|
rayTo.setIdentity();
|
||||||
|
@ -322,33 +347,33 @@ namespace MWPhysics
|
||||||
|
|
||||||
void PhysicsTaskScheduler::aabbTest(const btVector3& aabbMin, const btVector3& aabbMax, btBroadphaseAabbCallback& callback)
|
void PhysicsTaskScheduler::aabbTest(const btVector3& aabbMin, const btVector3& aabbMax, btBroadphaseAabbCallback& callback)
|
||||||
{
|
{
|
||||||
std::shared_lock lock(mCollisionWorldMutex);
|
MaybeSharedLock lock(mCollisionWorldMutex, mNumThreads);
|
||||||
mCollisionWorld->getBroadphase()->aabbTest(aabbMin, aabbMax, callback);
|
mCollisionWorld->getBroadphase()->aabbTest(aabbMin, aabbMax, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhysicsTaskScheduler::getAabb(const btCollisionObject* obj, btVector3& min, btVector3& max)
|
void PhysicsTaskScheduler::getAabb(const btCollisionObject* obj, btVector3& min, btVector3& max)
|
||||||
{
|
{
|
||||||
std::shared_lock lock(mCollisionWorldMutex);
|
MaybeSharedLock lock(mCollisionWorldMutex, mNumThreads);
|
||||||
obj->getCollisionShape()->getAabb(obj->getWorldTransform(), min, max);
|
obj->getCollisionShape()->getAabb(obj->getWorldTransform(), min, max);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhysicsTaskScheduler::setCollisionFilterMask(btCollisionObject* collisionObject, int collisionFilterMask)
|
void PhysicsTaskScheduler::setCollisionFilterMask(btCollisionObject* collisionObject, int collisionFilterMask)
|
||||||
{
|
{
|
||||||
std::unique_lock lock(mCollisionWorldMutex);
|
MaybeExclusiveLock lock(mCollisionWorldMutex, mNumThreads);
|
||||||
collisionObject->getBroadphaseHandle()->m_collisionFilterMask = collisionFilterMask;
|
collisionObject->getBroadphaseHandle()->m_collisionFilterMask = collisionFilterMask;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhysicsTaskScheduler::addCollisionObject(btCollisionObject* collisionObject, int collisionFilterGroup, int collisionFilterMask)
|
void PhysicsTaskScheduler::addCollisionObject(btCollisionObject* collisionObject, int collisionFilterGroup, int collisionFilterMask)
|
||||||
{
|
{
|
||||||
mCollisionObjects.insert(collisionObject);
|
mCollisionObjects.insert(collisionObject);
|
||||||
std::unique_lock lock(mCollisionWorldMutex);
|
MaybeExclusiveLock lock(mCollisionWorldMutex, mNumThreads);
|
||||||
mCollisionWorld->addCollisionObject(collisionObject, collisionFilterGroup, collisionFilterMask);
|
mCollisionWorld->addCollisionObject(collisionObject, collisionFilterGroup, collisionFilterMask);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhysicsTaskScheduler::removeCollisionObject(btCollisionObject* collisionObject)
|
void PhysicsTaskScheduler::removeCollisionObject(btCollisionObject* collisionObject)
|
||||||
{
|
{
|
||||||
mCollisionObjects.erase(collisionObject);
|
mCollisionObjects.erase(collisionObject);
|
||||||
std::unique_lock lock(mCollisionWorldMutex);
|
MaybeExclusiveLock lock(mCollisionWorldMutex, mNumThreads);
|
||||||
mCollisionWorld->removeCollisionObject(collisionObject);
|
mCollisionWorld->removeCollisionObject(collisionObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -360,14 +385,14 @@ namespace MWPhysics
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::unique_lock lock(mUpdateAabbMutex);
|
MaybeExclusiveLock lock(mUpdateAabbMutex, mNumThreads);
|
||||||
mUpdateAabb.insert(std::move(ptr));
|
mUpdateAabb.insert(std::move(ptr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PhysicsTaskScheduler::getLineOfSight(const std::shared_ptr<Actor>& actor1, const std::shared_ptr<Actor>& actor2)
|
bool PhysicsTaskScheduler::getLineOfSight(const std::shared_ptr<Actor>& actor1, const std::shared_ptr<Actor>& actor2)
|
||||||
{
|
{
|
||||||
std::unique_lock lock(mLOSCacheMutex);
|
MaybeExclusiveLock lock(mLOSCacheMutex, mNumThreads);
|
||||||
|
|
||||||
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);
|
||||||
|
@ -383,7 +408,7 @@ namespace MWPhysics
|
||||||
|
|
||||||
void PhysicsTaskScheduler::refreshLOSCache()
|
void PhysicsTaskScheduler::refreshLOSCache()
|
||||||
{
|
{
|
||||||
std::shared_lock lock(mLOSCacheMutex);
|
MaybeSharedLock lock(mLOSCacheMutex, mNumThreads);
|
||||||
int job = 0;
|
int job = 0;
|
||||||
int numLOS = mLOSCache.size();
|
int numLOS = mLOSCache.size();
|
||||||
while ((job = mNextLOS.fetch_add(1, std::memory_order_relaxed)) < numLOS)
|
while ((job = mNextLOS.fetch_add(1, std::memory_order_relaxed)) < numLOS)
|
||||||
|
@ -402,7 +427,7 @@ namespace MWPhysics
|
||||||
|
|
||||||
void PhysicsTaskScheduler::updateAabbs()
|
void PhysicsTaskScheduler::updateAabbs()
|
||||||
{
|
{
|
||||||
std::scoped_lock lock(mUpdateAabbMutex);
|
MaybeExclusiveLock lock(mUpdateAabbMutex, mNumThreads);
|
||||||
std::for_each(mUpdateAabb.begin(), mUpdateAabb.end(),
|
std::for_each(mUpdateAabb.begin(), mUpdateAabb.end(),
|
||||||
[this](const std::shared_ptr<PtrHolder>& ptr) { updatePtrAabb(ptr); });
|
[this](const std::shared_ptr<PtrHolder>& ptr) { updatePtrAabb(ptr); });
|
||||||
mUpdateAabb.clear();
|
mUpdateAabb.clear();
|
||||||
|
@ -410,7 +435,7 @@ namespace MWPhysics
|
||||||
|
|
||||||
void PhysicsTaskScheduler::updatePtrAabb(const std::shared_ptr<PtrHolder>& ptr)
|
void PhysicsTaskScheduler::updatePtrAabb(const std::shared_ptr<PtrHolder>& ptr)
|
||||||
{
|
{
|
||||||
std::scoped_lock lock(mCollisionWorldMutex);
|
MaybeExclusiveLock lock(mCollisionWorldMutex, mNumThreads);
|
||||||
if (const auto actor = std::dynamic_pointer_cast<Actor>(ptr))
|
if (const auto actor = std::dynamic_pointer_cast<Actor>(ptr))
|
||||||
{
|
{
|
||||||
actor->updateCollisionObjectPosition();
|
actor->updateCollisionObjectPosition();
|
||||||
|
@ -436,27 +461,7 @@ namespace MWPhysics
|
||||||
if (!mNewFrame)
|
if (!mNewFrame)
|
||||||
mHasJob.wait(lock, [&]() { return mQuit || mNewFrame; });
|
mHasJob.wait(lock, [&]() { return mQuit || mNewFrame; });
|
||||||
|
|
||||||
mPreStepBarrier->wait([this] { afterPreStep(); });
|
doSimulation();
|
||||||
|
|
||||||
int job = 0;
|
|
||||||
while (mRemainingSteps && (job = mNextJob.fetch_add(1, std::memory_order_relaxed)) < mNumJobs)
|
|
||||||
{
|
|
||||||
MaybeSharedLock lockColWorld(mCollisionWorldMutex, mThreadSafeBullet);
|
|
||||||
MovementSolver::move(mActorsFrameData[job], mPhysicsDt, mCollisionWorld, *mWorldFrameData);
|
|
||||||
}
|
|
||||||
|
|
||||||
mPostStepBarrier->wait([this] { afterPostStep(); });
|
|
||||||
|
|
||||||
if (!mRemainingSteps)
|
|
||||||
{
|
|
||||||
while ((job = mNextJob.fetch_add(1, std::memory_order_relaxed)) < mNumJobs)
|
|
||||||
{
|
|
||||||
handleFall(mActorsFrameData[job], mAdvanceSimulation);
|
|
||||||
}
|
|
||||||
|
|
||||||
refreshLOSCache();
|
|
||||||
mPostSimBarrier->wait([this] { afterPostSim(); });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -466,7 +471,7 @@ namespace MWPhysics
|
||||||
{
|
{
|
||||||
if (mActors[i]->setPosition(mActorsFrameData[i].mPosition))
|
if (mActors[i]->setPosition(mActorsFrameData[i].mPosition))
|
||||||
{
|
{
|
||||||
std::scoped_lock lock(mCollisionWorldMutex);
|
MaybeExclusiveLock lock(mCollisionWorldMutex, mNumThreads);
|
||||||
mActorsFrameData[i].mPosition = mActors[i]->getPosition(); // account for potential position change made by script
|
mActorsFrameData[i].mPosition = mActors[i]->getPosition(); // account for potential position change made by script
|
||||||
mActors[i]->updateCollisionObjectPosition();
|
mActors[i]->updateCollisionObjectPosition();
|
||||||
mCollisionWorld->updateSingleAabb(mActors[i]->getCollisionObject());
|
mCollisionWorld->updateSingleAabb(mActors[i]->getCollisionObject());
|
||||||
|
@ -476,6 +481,17 @@ namespace MWPhysics
|
||||||
|
|
||||||
void PhysicsTaskScheduler::updateActor(Actor& actor, ActorFrameData& actorData, bool simulationPerformed, float timeAccum, float dt) const
|
void PhysicsTaskScheduler::updateActor(Actor& actor, ActorFrameData& actorData, bool simulationPerformed, float timeAccum, float dt) const
|
||||||
{
|
{
|
||||||
|
auto ptr = actor.getPtr();
|
||||||
|
|
||||||
|
MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr);
|
||||||
|
const float heightDiff = actorData.mPosition.z() - actorData.mOldHeight;
|
||||||
|
const bool isStillOnGround = (simulationPerformed && actorData.mWasOnGround && actorData.mIsOnGround);
|
||||||
|
|
||||||
|
if (isStillOnGround || actorData.mFlying || isUnderWater(actorData) || actorData.mSlowFall < 1)
|
||||||
|
stats.land(ptr == MWMechanics::getPlayer() && (actorData.mFlying || isUnderWater(actorData)));
|
||||||
|
else if (heightDiff < 0)
|
||||||
|
stats.addToFallHeight(-heightDiff);
|
||||||
|
|
||||||
actor.setSimulationPosition(interpolateMovements(actor, actorData, timeAccum, dt));
|
actor.setSimulationPosition(interpolateMovements(actor, actorData, timeAccum, dt));
|
||||||
actor.setLastStuckPosition(actorData.mLastStuckPosition);
|
actor.setLastStuckPosition(actorData.mLastStuckPosition);
|
||||||
actor.setStuckFrames(actorData.mStuckFrames);
|
actor.setStuckFrames(actorData.mStuckFrames);
|
||||||
|
@ -504,32 +520,29 @@ namespace MWPhysics
|
||||||
resultCallback.m_collisionFilterGroup = 0xFF;
|
resultCallback.m_collisionFilterGroup = 0xFF;
|
||||||
resultCallback.m_collisionFilterMask = CollisionType_World|CollisionType_HeightMap|CollisionType_Door;
|
resultCallback.m_collisionFilterMask = CollisionType_World|CollisionType_HeightMap|CollisionType_Door;
|
||||||
|
|
||||||
MaybeSharedLock lockColWorld(mCollisionWorldMutex, mThreadSafeBullet);
|
MaybeLock lockColWorld(mCollisionWorldMutex, mNumThreads);
|
||||||
mCollisionWorld->rayTest(pos1, pos2, resultCallback);
|
mCollisionWorld->rayTest(pos1, pos2, resultCallback);
|
||||||
|
|
||||||
return !resultCallback.hasHit();
|
return !resultCallback.hasHit();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhysicsTaskScheduler::syncComputation()
|
void PhysicsTaskScheduler::doSimulation()
|
||||||
{
|
{
|
||||||
while (mRemainingSteps--)
|
while (mRemainingSteps)
|
||||||
{
|
{
|
||||||
for (auto& actorData : mActorsFrameData)
|
mPreStepBarrier->wait([this] { afterPreStep(); });
|
||||||
|
int job = 0;
|
||||||
|
while ((job = mNextJob.fetch_add(1, std::memory_order_relaxed)) < mNumJobs)
|
||||||
{
|
{
|
||||||
MovementSolver::unstuck(actorData, mCollisionWorld);
|
MaybeLock lockColWorld(mCollisionWorldMutex, mNumThreads);
|
||||||
MovementSolver::move(actorData, mPhysicsDt, mCollisionWorld, *mWorldFrameData);
|
MovementSolver::move(mActorsFrameData[job], mPhysicsDt, mCollisionWorld, *mWorldFrameData);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateActorsPositions();
|
mPostStepBarrier->wait([this] { afterPostStep(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < mActors.size(); ++i)
|
|
||||||
{
|
|
||||||
handleFall(mActorsFrameData[i], mAdvanceSimulation);
|
|
||||||
updateMechanics(*mActors[i], mActorsFrameData[i]);
|
|
||||||
updateActor(*mActors[i], mActorsFrameData[i], mAdvanceSimulation, mTimeAccum, mPhysicsDt);
|
|
||||||
}
|
|
||||||
refreshLOSCache();
|
refreshLOSCache();
|
||||||
|
mPostSimBarrier->wait([this] { afterPostSim(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhysicsTaskScheduler::updateStats(osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats)
|
void PhysicsTaskScheduler::updateStats(osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats)
|
||||||
|
@ -549,7 +562,7 @@ namespace MWPhysics
|
||||||
|
|
||||||
void PhysicsTaskScheduler::debugDraw()
|
void PhysicsTaskScheduler::debugDraw()
|
||||||
{
|
{
|
||||||
std::shared_lock lock(mCollisionWorldMutex);
|
MaybeSharedLock lock(mCollisionWorldMutex, mNumThreads);
|
||||||
mDebugDrawer->step();
|
mDebugDrawer->step();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -575,7 +588,7 @@ namespace MWPhysics
|
||||||
return;
|
return;
|
||||||
for (size_t i = 0; i < mActors.size(); ++i)
|
for (size_t i = 0; i < mActors.size(); ++i)
|
||||||
{
|
{
|
||||||
std::unique_lock lock(mCollisionWorldMutex);
|
MaybeExclusiveLock lock(mCollisionWorldMutex, mNumThreads);
|
||||||
MovementSolver::unstuck(mActorsFrameData[i], mCollisionWorld);
|
MovementSolver::unstuck(mActorsFrameData[i], mCollisionWorld);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -594,7 +607,7 @@ namespace MWPhysics
|
||||||
{
|
{
|
||||||
mNewFrame = false;
|
mNewFrame = false;
|
||||||
{
|
{
|
||||||
std::unique_lock lock(mLOSCacheMutex);
|
MaybeExclusiveLock lock(mLOSCacheMutex, mNumThreads);
|
||||||
mLOSCache.erase(
|
mLOSCache.erase(
|
||||||
std::remove_if(mLOSCache.begin(), mLOSCache.end(),
|
std::remove_if(mLOSCache.begin(), mLOSCache.end(),
|
||||||
[](const LOSRequest& req) { return req.mStale; }),
|
[](const LOSRequest& req) { return req.mStale; }),
|
||||||
|
@ -602,4 +615,10 @@ namespace MWPhysics
|
||||||
}
|
}
|
||||||
mTimeEnd = mTimer->tick();
|
mTimeEnd = mTimer->tick();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PhysicsTaskScheduler::syncWithMainThread()
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < mActors.size(); ++i)
|
||||||
|
updateActor(*mActors[i], mActorsFrameData[i], mAdvanceSimulation, mTimeAccum, mPhysicsDt);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,7 +61,7 @@ namespace MWPhysics
|
||||||
void releaseSharedStates(); // destroy all objects whose destructor can't be safely called from ~PhysicsTaskScheduler()
|
void releaseSharedStates(); // destroy all objects whose destructor can't be safely called from ~PhysicsTaskScheduler()
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void syncComputation();
|
void doSimulation();
|
||||||
void worker();
|
void worker();
|
||||||
void updateActorsPositions();
|
void updateActorsPositions();
|
||||||
void updateActor(Actor& actor, ActorFrameData& actorData, bool simulationPerformed, float timeAccum, float dt) const;
|
void updateActor(Actor& actor, ActorFrameData& actorData, bool simulationPerformed, float timeAccum, float dt) const;
|
||||||
|
@ -74,6 +74,7 @@ namespace MWPhysics
|
||||||
void afterPreStep();
|
void afterPreStep();
|
||||||
void afterPostStep();
|
void afterPostStep();
|
||||||
void afterPostSim();
|
void afterPostSim();
|
||||||
|
void syncWithMainThread();
|
||||||
|
|
||||||
std::unique_ptr<WorldFrameData> mWorldFrameData;
|
std::unique_ptr<WorldFrameData> mWorldFrameData;
|
||||||
std::vector<std::shared_ptr<Actor>> mActors;
|
std::vector<std::shared_ptr<Actor>> mActors;
|
||||||
|
@ -98,7 +99,6 @@ namespace MWPhysics
|
||||||
int mLOSCacheExpiry;
|
int mLOSCacheExpiry;
|
||||||
bool mNewFrame;
|
bool mNewFrame;
|
||||||
bool mAdvanceSimulation;
|
bool mAdvanceSimulation;
|
||||||
bool mThreadSafeBullet;
|
|
||||||
bool mQuit;
|
bool mQuit;
|
||||||
std::atomic<int> mNextJob;
|
std::atomic<int> mNextJob;
|
||||||
std::atomic<int> mNextLOS;
|
std::atomic<int> mNextLOS;
|
||||||
|
|
|
@ -978,14 +978,12 @@ namespace MWPhysics
|
||||||
, mWaterlevel(waterlevel)
|
, mWaterlevel(waterlevel)
|
||||||
, mHalfExtentsZ(actor.getHalfExtents().z())
|
, mHalfExtentsZ(actor.getHalfExtents().z())
|
||||||
, mOldHeight(0)
|
, mOldHeight(0)
|
||||||
, mFallHeight(0)
|
|
||||||
, mStuckFrames(0)
|
, mStuckFrames(0)
|
||||||
, mFlying(MWBase::Environment::get().getWorld()->isFlying(actor.getPtr()))
|
, mFlying(MWBase::Environment::get().getWorld()->isFlying(actor.getPtr()))
|
||||||
, mWasOnGround(actor.getOnGround())
|
, mWasOnGround(actor.getOnGround())
|
||||||
, mIsAquatic(actor.getPtr().getClass().isPureWaterCreature(actor.getPtr()))
|
, mIsAquatic(actor.getPtr().getClass().isPureWaterCreature(actor.getPtr()))
|
||||||
, mWaterCollision(waterCollision)
|
, mWaterCollision(waterCollision)
|
||||||
, mSkipCollisionDetection(actor.skipCollisions() || !actor.getCollisionMode())
|
, mSkipCollisionDetection(actor.skipCollisions() || !actor.getCollisionMode())
|
||||||
, mNeedLand(false)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -97,14 +97,12 @@ namespace MWPhysics
|
||||||
const float mWaterlevel;
|
const float mWaterlevel;
|
||||||
const float mHalfExtentsZ;
|
const float mHalfExtentsZ;
|
||||||
float mOldHeight;
|
float mOldHeight;
|
||||||
float mFallHeight;
|
|
||||||
unsigned int mStuckFrames;
|
unsigned int mStuckFrames;
|
||||||
const bool mFlying;
|
const bool mFlying;
|
||||||
const bool mWasOnGround;
|
const bool mWasOnGround;
|
||||||
const bool mIsAquatic;
|
const bool mIsAquatic;
|
||||||
const bool mWaterCollision;
|
const bool mWaterCollision;
|
||||||
const bool mSkipCollisionDetection;
|
const bool mSkipCollisionDetection;
|
||||||
bool mNeedLand;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct WorldFrameData
|
struct WorldFrameData
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#ifndef OPENMW_BARRIER_H
|
#ifndef OPENMW_BARRIER_H
|
||||||
#define OPENMW_BARRIER_H
|
#define OPENMW_BARRIER_H
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
|
@ -12,7 +13,9 @@ namespace Misc
|
||||||
public:
|
public:
|
||||||
/// @param count number of threads to wait on
|
/// @param count number of threads to wait on
|
||||||
explicit Barrier(int count) : mThreadCount(count), mRendezvousCount(0), mGeneration(0)
|
explicit Barrier(int count) : mThreadCount(count), mRendezvousCount(0), mGeneration(0)
|
||||||
{}
|
{
|
||||||
|
assert(count >= 0);
|
||||||
|
}
|
||||||
|
|
||||||
/// @brief stop execution of threads until count distinct threads reach this point
|
/// @brief stop execution of threads until count distinct threads reach this point
|
||||||
/// @param func callable to be executed once after all threads have met
|
/// @param func callable to be executed once after all threads have met
|
||||||
|
@ -22,8 +25,8 @@ namespace Misc
|
||||||
std::unique_lock lock(mMutex);
|
std::unique_lock lock(mMutex);
|
||||||
|
|
||||||
++mRendezvousCount;
|
++mRendezvousCount;
|
||||||
const int currentGeneration = mGeneration;
|
const unsigned int currentGeneration = mGeneration;
|
||||||
if (mRendezvousCount == mThreadCount)
|
if (mRendezvousCount == mThreadCount || mThreadCount == 0)
|
||||||
{
|
{
|
||||||
++mGeneration;
|
++mGeneration;
|
||||||
mRendezvousCount = 0;
|
mRendezvousCount = 0;
|
||||||
|
@ -37,9 +40,9 @@ namespace Misc
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int mThreadCount;
|
unsigned int mThreadCount;
|
||||||
int mRendezvousCount;
|
unsigned int mRendezvousCount;
|
||||||
int mGeneration;
|
unsigned int mGeneration;
|
||||||
mutable std::mutex mMutex;
|
mutable std::mutex mMutex;
|
||||||
std::condition_variable mRendezvous;
|
std::condition_variable mRendezvous;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue