@ -138,7 +138,7 @@ namespace MWPhysics
, mRemainingSteps ( 0 )
, mLOSCacheExpiry ( Settings : : Manager : : getInt ( " lineofsight keep inactive cache " , " Physics " ) )
, mDeferAabbUpdate ( Settings : : Manager : : getBool ( " defer aabb update " , " Physics " ) )
, m NewFrame( false )
, m FrameCounter( 0 )
, mAdvanceSimulation ( false )
, mQuit ( false )
, mNextJob ( 0 )
@ -176,12 +176,13 @@ namespace MWPhysics
PhysicsTaskScheduler : : ~ PhysicsTaskScheduler ( )
{
waitForWorkers ( ) ;
std : : unique_lock lock ( mSimulationMutex ) ;
mQuit = true ;
mNumJobs = 0 ;
mRemainingSteps = 0 ;
lock . unlock ( ) ;
mHasJob . notify_all ( ) ;
lock . unlock ( ) ;
for ( auto & thread : mThreads )
thread . join ( ) ;
}
@ -234,9 +235,10 @@ namespace MWPhysics
const std : : vector < MWWorld : : Ptr > & PhysicsTaskScheduler : : moveActors ( float & timeAccum , std : : vector < ActorFrameData > & & actorsData , osg : : Timer_t frameStart , unsigned int frameNumber , osg : : Stats & stats )
{
waitForWorkers ( ) ;
// This function run in the main thread.
// While the mSimulationMutex is held, background physics threads can't run.
std : : unique_lock lock ( mSimulationMutex ) ;
double timeStart = mTimer - > tick ( ) ;
@ -282,7 +284,7 @@ namespace MWPhysics
mPhysicsDt = newDelta ;
mActorsFrameData = std : : move ( actorsData ) ;
mAdvanceSimulation = ( mRemainingSteps ! = 0 ) ;
mNewFrame = true ;
+ + mFrameCounter ;
mNumJobs = mActorsFrameData . size ( ) ;
mNextLOS . store ( 0 , std : : memory_order_relaxed ) ;
mNextJob . store ( 0 , std : : memory_order_release ) ;
@ -302,8 +304,8 @@ namespace MWPhysics
}
mAsyncStartTime = mTimer - > tick ( ) ;
lock . unlock ( ) ;
mHasJob . notify_all ( ) ;
lock . unlock ( ) ;
if ( mAdvanceSimulation )
mBudget . update ( mTimer - > delta_s ( timeStart , mTimer - > tick ( ) ) , 1 , mBudgetCursor ) ;
return mMovedActors ;
@ -311,6 +313,7 @@ namespace MWPhysics
const std : : vector < MWWorld : : Ptr > & PhysicsTaskScheduler : : resetSimulation ( const ActorMap & actors )
{
waitForWorkers ( ) ;
std : : unique_lock lock ( mSimulationMutex ) ;
mBudget . reset ( mDefaultPhysicsDt ) ;
mAsyncBudget . reset ( 0.0f ) ;
@ -477,11 +480,13 @@ namespace MWPhysics
void PhysicsTaskScheduler : : worker ( )
{
std : : size_t lastFrame = 0 ;
std : : shared_lock lock ( mSimulationMutex ) ;
while ( ! mQuit )
{
if ( ! mNewFrame )
mHasJob . wait ( lock , [ & ] ( ) { return mQuit | | mNewFrame ; } ) ;
if ( mRemainingSteps = = 0 & & lastFrame = = mFrameCounter )
mHasJob . wait ( lock , [ & ] { return mQuit | | lastFrame ! = mFrameCounter ; } ) ;
lastFrame = mFrameCounter ;
mPreStepBarrier - > wait ( [ this ] { afterPreStep ( ) ; } ) ;
@ -618,7 +623,6 @@ namespace MWPhysics
void PhysicsTaskScheduler : : afterPostSim ( )
{
mNewFrame = false ;
if ( mLOSCacheExpiry > = 0 )
{
std : : unique_lock lock ( mLOSCacheMutex ) ;
@ -628,5 +632,23 @@ namespace MWPhysics
mLOSCache . end ( ) ) ;
}
mTimeEnd = mTimer - > tick ( ) ;
std : : unique_lock lock ( mWorkersDoneMutex ) ;
+ + mWorkersFrameCounter ;
mWorkersDone . notify_all ( ) ;
}
// Attempt to acquire unique lock on mSimulationMutex while not all worker
// threads are holding shared lock but will have to may lead to a deadlock because
// C++ standard does not guarantee priority for exclusive and shared locks
// for std::shared_mutex. For example microsoft STL implementation points out
// for the absence of such priority:
// https://docs.microsoft.com/en-us/windows/win32/sync/slim-reader-writer--srw--locks
void PhysicsTaskScheduler : : waitForWorkers ( )
{
if ( mNumThreads = = 0 )
return ;
std : : unique_lock lock ( mWorkersDoneMutex ) ;
if ( mFrameCounter ! = mWorkersFrameCounter )
mWorkersDone . wait ( lock ) ;
}
}