Merge branch 'sync_sync_with_async' into 'master'

Merge logic of sync and async physics simulation

See merge request OpenMW/openmw!1250
pull/3152/head
psi29a 3 years ago
commit 7af245d205

@ -22,59 +22,87 @@
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
mMutex.unlock();
} }
private: private:
Mutex& mMutex; Mutex& mMutex;
bool mCanBeSharedLock; unsigned int mThreadCount;
}; };
bool isUnderWater(const MWPhysics::ActorFrameData& actorData) /// @brief A scoped lock that is either shared, exclusive or inexistent depending on configuration
{ template<class Mutex>
return actorData.mPosition.z() < actorData.mSwimLevel; class MaybeLock
}
void handleFall(MWPhysics::ActorFrameData& actorData, bool simulationPerformed)
{ {
const float heightDiff = actorData.mPosition.z() - actorData.mOldHeight; public:
/// @param mutex a shared mutex
const bool isStillOnGround = (simulationPerformed && actorData.mWasOnGround && actorData.mIsOnGround); /// @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();
}
if (isStillOnGround || actorData.mFlying || isUnderWater(actorData) || actorData.mSlowFall < 1) ~MaybeLock()
actorData.mNeedLand = true; {
else if (heightDiff < 0) if (mThreadCount > 1)
actorData.mFallHeight += heightDiff; mMutex.unlock_shared();
} else if(mThreadCount == 1)
mMutex.unlock();
}
private:
Mutex& mMutex;
unsigned int mThreadCount;
};
void updateMechanics(MWPhysics::Actor& actor, MWPhysics::ActorFrameData& actorData) bool isUnderWater(const MWPhysics::ActorFrameData& actorData)
{ {
auto ptr = actor.getPtr(); return actorData.mPosition.z() < actorData.mSwimLevel;
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)
@ -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); {
mQuit = true; MaybeExclusiveLock lock(mSimulationMutex, mNumThreads);
mNumJobs = 0; mQuit = true;
mRemainingSteps = 0; mNumJobs = 0;
lock.unlock(); mRemainingSteps = 0;
}
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…
Cancel
Save