@ -22,59 +22,87 @@
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 >
class MaybeSharedLock
{
public :
/// @param mutex a shared mutex
/// @param canBeSharedLock decide wether the lock will be shared or exclusive
MaybeSharedLock ( Mutex & mutex , bool canBeSharedLock ) : mMutex ( mutex ) , mCanBeSharedLock ( canBeSharedLock )
/// @param threadCount decide wether the shared lock will be taken
MaybeSharedLock ( Mutex & mutex , int threadCount ) : mMutex ( mutex ) , mThreadCount ( threadCount )
{
if ( mCanBeSharedLock )
assert ( threadCount > = 0 ) ;
if ( mThreadCount > 0 )
mMutex . lock_shared ( ) ;
else
mMutex . lock ( ) ;
}
~ MaybeSharedLock ( )
{
if ( mCanBeSharedLock )
if ( m ThreadCount > 0 )
mMutex . unlock_shared ( ) ;
else
mMutex . unlock ( ) ;
}
private :
Mutex & mMutex ;
bool mCanBeSharedLock ;
unsigned int mThreadCount ;
} ;
bool isUnderWater ( const MWPhysics : : ActorFrameData & actorData )
{
return actorData . mPosition . z ( ) < actorData . mSwimLevel ;
}
void handleFall ( MWPhysics : : ActorFrameData & actorData , bool simulationPerformed )
/// @brief A scoped lock that is either shared, exclusive or inexistent depending on configuration
template < class Mutex >
class MaybeLock
{
const float heightDiff = actorData . mPosition . z ( ) - actorData . mOldHeight ;
const bool isStillOnGround = ( simulationPerformed & & actorData . mWasOnGround & & actorData . mIsOnGround ) ;
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 ( ) ;
}
if ( isStillOnGround | | actorData . mFlying | | isUnderWater ( actorData ) | | actorData . mSlowFall < 1 )
actorData . mNeedLand = true ;
else if ( heightDiff < 0 )
actorData . mFallHeight + = heightDiff ;
}
~ MaybeLock ( )
{
if ( mThreadCount > 1 )
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 ( ) ;
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 ) ;
return actorData . mPosition . z ( ) < actorData . mSwimLevel ;
}
osg : : Vec3f interpolateMovements ( MWPhysics : : Actor & actor , MWPhysics : : ActorFrameData & actorData , float timeAccum , float physicsDt )
@ -85,14 +113,14 @@ namespace
namespace Config
{
/// @return either the number of thread as configured by the user, or 1 if Bullet doesn't support multithreading
int computeNumThreads ( bool & threadSafeBullet )
/// @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 ( )
{
int wantedThread = Settings : : Manager : : getInt ( " async num threads " , " Physics " ) ;
auto broad = std : : make_unique < btDbvtBroadphase > ( ) ;
auto maxSupportedThreads = broad - > m_rayTestStacks . size ( ) ;
threadSafeBullet = ( maxSupportedThreads > 1 ) ;
auto threadSafeBullet = ( maxSupportedThreads > 1 ) ;
if ( ! threadSafeBullet & & wantedThread > 1 )
{
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 )
, mCollisionWorld ( collisionWorld )
, mDebugDrawer ( debugDrawer )
, mNumThreads ( Config : : computeNumThreads ( ) )
, mNumJobs ( 0 )
, mRemainingSteps ( 0 )
, mLOSCacheExpiry ( Settings : : Manager : : getInt ( " lineofsight keep inactive cache " , " Physics " ) )
@ -130,8 +159,6 @@ namespace MWPhysics
, mTimeEnd ( 0 )
, mFrameStart ( 0 )
{
mNumThreads = Config : : computeNumThreads ( mThreadSafeBullet ) ;
if ( mNumThreads > = 1 )
{
for ( int i = 0 ; i < mNumThreads ; + + i )
@ -151,11 +178,12 @@ namespace MWPhysics
PhysicsTaskScheduler : : ~ PhysicsTaskScheduler ( )
{
std : : unique_lock lock ( mSimulationMutex ) ;
mQuit = true ;
mNumJobs = 0 ;
mRemainingSteps = 0 ;
lock . unlock ( ) ;
{
MaybeExclusiveLock lock ( mSimulationMutex , mNumThreads ) ;
mQuit = true ;
mNumJobs = 0 ;
mRemainingSteps = 0 ;
}
mHasJob . notify_all ( ) ;
for ( auto & thread : mThreads )
thread . join ( ) ;
@ -212,7 +240,7 @@ namespace MWPhysics
// This function run in the main thread.
// 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 ( ) ) ;
double timeStart = mTimer - > tick ( ) ;
@ -220,11 +248,8 @@ namespace MWPhysics
// start by finishing previous background computation
if ( mNumThreads ! = 0 )
{
for ( size_t i = 0 ; i < mActors . size ( ) ; + + i )
{
updateMechanics ( * mActors [ i ] , mActorsFrameData [ i ] ) ;
updateActor ( * mActors [ i ] , mActorsFrameData [ i ] , mAdvanceSimulation , mTimeAccum , mPhysicsDt ) ;
}
syncWithMainThread ( ) ;
if ( mAdvanceSimulation )
mAsyncBudget . update ( mTimer - > delta_s ( mAsyncStartTime , mTimeEnd ) , mPrevStepCount , mBudgetCursor ) ;
updateStats ( frameStart , frameNumber , stats ) ;
@ -258,14 +283,14 @@ namespace MWPhysics
if ( mNumThreads = = 0 )
{
syncComputation ( ) ;
doSimulation ( ) ;
syncWithMainThread ( ) ;
if ( mAdvanceSimulation )
mBudget . update ( mTimer - > delta_s ( timeStart , mTimer - > tick ( ) ) , numSteps , mBudgetCursor ) ;
return ;
}
mAsyncStartTime = mTimer - > tick ( ) ;
lock . unlock ( ) ;
mHasJob . notify_all ( ) ;
if ( mAdvanceSimulation )
mBudget . update ( mTimer - > delta_s ( timeStart , mTimer - > tick ( ) ) , 1 , mBudgetCursor ) ;
@ -273,7 +298,7 @@ namespace MWPhysics
void PhysicsTaskScheduler : : resetSimulation ( const ActorMap & actors )
{
std: : unique_lock lock ( mSimulationMutex ) ;
MaybeExclusiveLock lock ( mSimulationMutex , mNumThreads ) ;
mBudget . reset ( mDefaultPhysicsDt ) ;
mAsyncBudget . reset ( 0.0f ) ;
mActors . clear ( ) ;
@ -287,25 +312,25 @@ namespace MWPhysics
void PhysicsTaskScheduler : : rayTest ( const btVector3 & rayFromWorld , const btVector3 & rayToWorld , btCollisionWorld : : RayResultCallback & resultCallback ) const
{
Maybe Shared Lock lock ( mCollisionWorldMutex , m ThreadSafeBullet ) ;
Maybe Lock lock ( mCollisionWorldMutex , m NumThreads ) ;
mCollisionWorld - > rayTest ( rayFromWorld , rayToWorld , resultCallback ) ;
}
void PhysicsTaskScheduler : : convexSweepTest ( const btConvexShape * castShape , const btTransform & from , const btTransform & to , btCollisionWorld : : ConvexResultCallback & resultCallback ) const
{
Maybe Shared Lock lock ( mCollisionWorldMutex , m ThreadSafeBullet ) ;
Maybe Lock lock ( mCollisionWorldMutex , m NumThreads ) ;
mCollisionWorld - > convexSweepTest ( castShape , from , to , resultCallback ) ;
}
void PhysicsTaskScheduler : : contactTest ( btCollisionObject * colObj , btCollisionWorld : : ContactResultCallback & resultCallback )
{
std: : shared_lock lock ( mCollisionWorldMutex ) ;
MaybeSharedLock lock ( mCollisionWorldMutex , mNumThreads ) ;
ContactTestWrapper : : contactTest ( mCollisionWorld , colObj , resultCallback ) ;
}
std : : optional < btVector3 > PhysicsTaskScheduler : : getHitPoint ( const btTransform & from , btCollisionObject * target )
{
Maybe Shared Lock lock ( mCollisionWorldMutex , m ThreadSafeBullet ) ;
Maybe Lock lock ( mCollisionWorldMutex , m NumThreads ) ;
// target the collision object's world origin, this should be the center of the collision object
btTransform rayTo ;
rayTo . setIdentity ( ) ;
@ -322,33 +347,33 @@ namespace MWPhysics
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 ) ;
}
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 ) ;
}
void PhysicsTaskScheduler : : setCollisionFilterMask ( btCollisionObject * collisionObject , int collisionFilterMask )
{
std: : unique_lock lock ( mCollisionWorldMutex ) ;
MaybeExclusiveLock lock ( mCollisionWorldMutex , mNumThreads ) ;
collisionObject - > getBroadphaseHandle ( ) - > m_collisionFilterMask = collisionFilterMask ;
}
void PhysicsTaskScheduler : : addCollisionObject ( btCollisionObject * collisionObject , int collisionFilterGroup , int collisionFilterMask )
{
mCollisionObjects . insert ( collisionObject ) ;
std: : unique_lock lock ( mCollisionWorldMutex ) ;
MaybeExclusiveLock lock ( mCollisionWorldMutex , mNumThreads ) ;
mCollisionWorld - > addCollisionObject ( collisionObject , collisionFilterGroup , collisionFilterMask ) ;
}
void PhysicsTaskScheduler : : removeCollisionObject ( btCollisionObject * collisionObject )
{
mCollisionObjects . erase ( collisionObject ) ;
std: : unique_lock lock ( mCollisionWorldMutex ) ;
MaybeExclusiveLock lock ( mCollisionWorldMutex , mNumThreads ) ;
mCollisionWorld - > removeCollisionObject ( collisionObject ) ;
}
@ -360,14 +385,14 @@ namespace MWPhysics
}
else
{
std: : unique_lock lock ( mUpdateAabbMutex ) ;
MaybeExclusiveLock lock ( mUpdateAabbMutex , mNumThreads ) ;
mUpdateAabb . insert ( std : : move ( ptr ) ) ;
}
}
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 result = std : : find ( mLOSCache . begin ( ) , mLOSCache . end ( ) , req ) ;
@ -383,7 +408,7 @@ namespace MWPhysics
void PhysicsTaskScheduler : : refreshLOSCache ( )
{
std: : shared_lock lock ( mLOSCacheMutex ) ;
MaybeSharedLock lock ( mLOSCacheMutex , mNumThreads ) ;
int job = 0 ;
int numLOS = mLOSCache . size ( ) ;
while ( ( job = mNextLOS . fetch_add ( 1 , std : : memory_order_relaxed ) ) < numLOS )
@ -402,7 +427,7 @@ namespace MWPhysics
void PhysicsTaskScheduler : : updateAabbs ( )
{
std: : scoped_lock lock ( mUpdateAabbMutex ) ;
MaybeExclusiveLock lock ( mUpdateAabbMutex , mNumThreads ) ;
std : : for_each ( mUpdateAabb . begin ( ) , mUpdateAabb . end ( ) ,
[ this ] ( const std : : shared_ptr < PtrHolder > & ptr ) { updatePtrAabb ( ptr ) ; } ) ;
mUpdateAabb . clear ( ) ;
@ -410,7 +435,7 @@ namespace MWPhysics
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 ) )
{
actor - > updateCollisionObjectPosition ( ) ;
@ -436,27 +461,7 @@ namespace MWPhysics
if ( ! mNewFrame )
mHasJob . wait ( lock , [ & ] ( ) { return mQuit | | mNewFrame ; } ) ;
mPreStepBarrier - > wait ( [ this ] { afterPreStep ( ) ; } ) ;
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 ( ) ; } ) ;
}
doSimulation ( ) ;
}
}
@ -466,7 +471,7 @@ namespace MWPhysics
{
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
mActors [ i ] - > updateCollisionObjectPosition ( ) ;
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
{
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 . setLastStuckPosition ( actorData . mLastStuckPosition ) ;
actor . setStuckFrames ( actorData . mStuckFrames ) ;
@ -504,32 +520,29 @@ namespace MWPhysics
resultCallback . m_collisionFilterGroup = 0xFF ;
resultCallback . m_collisionFilterMask = CollisionType_World | CollisionType_HeightMap | CollisionType_Door ;
Maybe Shared Lock lockColWorld ( mCollisionWorldMutex , m ThreadSafeBullet ) ;
Maybe Lock lockColWorld ( mCollisionWorldMutex , m NumThreads ) ;
mCollisionWorld - > rayTest ( pos1 , pos2 , resultCallback ) ;
return ! resultCallback . hasHit ( ) ;
}
void PhysicsTaskScheduler : : syncComput ation( )
void PhysicsTaskScheduler : : doSimul ation( )
{
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 ) ;
MovementSolver : : move ( actorData , mPhysicsDt , mCollisionWorld , * mWorldFrameData ) ;
M aybeLock lockColWorld ( mCollisionWorldMutex , mNumThreads ) ;
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 ( ) ;
mPostSimBarrier - > wait ( [ this ] { afterPostSim ( ) ; } ) ;
}
void PhysicsTaskScheduler : : updateStats ( osg : : Timer_t frameStart , unsigned int frameNumber , osg : : Stats & stats )
@ -549,7 +562,7 @@ namespace MWPhysics
void PhysicsTaskScheduler : : debugDraw ( )
{
std: : shared_lock lock ( mCollisionWorldMutex ) ;
MaybeSharedLock lock ( mCollisionWorldMutex , mNumThreads ) ;
mDebugDrawer - > step ( ) ;
}
@ -575,7 +588,7 @@ namespace MWPhysics
return ;
for ( size_t i = 0 ; i < mActors . size ( ) ; + + i )
{
std: : unique_lock lock ( mCollisionWorldMutex ) ;
MaybeExclusiveLock lock ( mCollisionWorldMutex , mNumThreads ) ;
MovementSolver : : unstuck ( mActorsFrameData [ i ] , mCollisionWorld ) ;
}
}
@ -594,7 +607,7 @@ namespace MWPhysics
{
mNewFrame = false ;
{
std: : unique_lock lock ( mLOSCacheMutex ) ;
MaybeExclusiveLock lock ( mLOSCacheMutex , mNumThreads ) ;
mLOSCache . erase (
std : : remove_if ( mLOSCache . begin ( ) , mLOSCache . end ( ) ,
[ ] ( const LOSRequest & req ) { return req . mStale ; } ) ,
@ -602,4 +615,10 @@ namespace MWPhysics
}
mTimeEnd = mTimer - > tick ( ) ;
}
void PhysicsTaskScheduler : : syncWithMainThread ( )
{
for ( size_t i = 0 ; i < mActors . size ( ) ; + + i )
updateActor ( * mActors [ i ] , mActorsFrameData [ i ] , mAdvanceSimulation , mTimeAccum , mPhysicsDt ) ;
}
}