瀏覽代碼

Made amount of mutexes in the mutex array dependent on the amount of CPU cores (#9)

jrouwe 3 年之前
父節點
當前提交
ef371411af

+ 38 - 11
Jolt/Core/MutexArray.h

@@ -3,25 +3,49 @@
 
 
 #pragma once
 #pragma once
 
 
+#include <Core/NonCopyable.h>
+
 namespace JPH {
 namespace JPH {
 
 
 /// A mutex array protects a number of resources with a limited amount of mutexes.
 /// A mutex array protects a number of resources with a limited amount of mutexes.
 /// It uses hashing to find the mutex of a particular object.
 /// It uses hashing to find the mutex of a particular object.
 /// The idea is that if the amount of threads is much smaller than the amount of mutexes
 /// The idea is that if the amount of threads is much smaller than the amount of mutexes
 /// that there is a relatively small chance that two different objects map to the same mutex.
 /// that there is a relatively small chance that two different objects map to the same mutex.
-template <class MutexType, int NumMutexesArg>
-class MutexArray
+template <class MutexType>
+class MutexArray : public NonCopyable
 {
 {
 public:
 public:
-	/// Number of mutexes used to protect the underlying resources.
-	static constexpr int	NumMutexes = NumMutexesArg;
+	/// Constructor, constructs an empty mutex array that you need to initialize with Init()
+							MutexArray() = default;
+
+	/// Constructor, constructs an array with inNumMutexes entries
+	explicit				MutexArray(uint inNumMutexes) { Init(inNumMutexes); }
+
+	/// Destructor
+							~MutexArray() { delete [] mMutexStorage; }
+
+	/// Initialization
+	/// @param inNumMutexes The amount of mutexes to allocate
+	void					Init(uint inNumMutexes) 
+	{ 
+		JPH_ASSERT(mMutexStorage == nullptr); 
+		JPH_ASSERT(inNumMutexes > 0 && IsPowerOf2(inNumMutexes));
+
+		mMutexStorage = new MutexStorage[inNumMutexes]; 
+		mNumMutexes = inNumMutexes; 
+	}
+
+	/// Get the number of mutexes that were allocated
+	inline uint				GetNumMutexes() const
+	{
+		return mNumMutexes;
+	}
 
 
 	/// Convert an object index to a mutex index
 	/// Convert an object index to a mutex index
 	inline uint32			GetMutexIndex(uint32 inObjectIndex) const
 	inline uint32			GetMutexIndex(uint32 inObjectIndex) const
 	{
 	{
 		std::hash<uint32> hasher;
 		std::hash<uint32> hasher;
-		static_assert(IsPowerOf2(NumMutexes), "Number of mutexes must be power of 2");
-		return hasher(inObjectIndex) & (NumMutexes - 1);
+		return hasher(inObjectIndex) & (mNumMutexes - 1);
 	}
 	}
 
 
 	/// Get the mutex belonging to a certain object by index
 	/// Get the mutex belonging to a certain object by index
@@ -41,8 +65,9 @@ public:
 	{
 	{
 		JPH_PROFILE_FUNCTION();
 		JPH_PROFILE_FUNCTION();
 
 
-		for (MutexStorage &m : mMutexStorage)
-			m.mMutex.lock();
+		MutexStorage *end = mMutexStorage + mNumMutexes;
+		for (MutexStorage *m = mMutexStorage; m < end; ++m)
+			m->mMutex.lock();
 	}
 	}
 
 
 	/// Unlock all mutexes
 	/// Unlock all mutexes
@@ -50,8 +75,9 @@ public:
 	{
 	{
 		JPH_PROFILE_FUNCTION();
 		JPH_PROFILE_FUNCTION();
 
 
-		for (MutexStorage &m : mMutexStorage)
-			m.mMutex.unlock();
+		MutexStorage *end = mMutexStorage + mNumMutexes;
+		for (MutexStorage *m = mMutexStorage; m < end; ++m)
+			m->mMutex.unlock();
 	}
 	}
 
 
 private:
 private:
@@ -61,7 +87,8 @@ private:
 		MutexType			mMutex;
 		MutexType			mMutex;
 	};
 	};
 
 
-	MutexStorage			mMutexStorage[NumMutexes];
+	MutexStorage *			mMutexStorage = nullptr;
+	uint					mNumMutexes = 0;
 };
 };
 
 
 } // JPH
 } // JPH

+ 6 - 0
Jolt/Physics/Body/BodyLockInterface.h

@@ -30,6 +30,12 @@ public:
 	virtual void				UnlockWrite(SharedMutex *inMutex) const = 0;
 	virtual void				UnlockWrite(SharedMutex *inMutex) const = 0;
 	///@}
 	///@}
 
 
+	/// Get the mask needed to lock all bodies
+	inline MutexMask			GetAllBodiesMutexMask() const
+	{
+		return mBodyManager.GetAllBodiesMutexMask();
+	}
+
 	///@name Batch locking functions
 	///@name Batch locking functions
 	///@{
 	///@{
 	virtual MutexMask			GetMutexMask(const BodyID *inBodies, int inNumber) const = 0;
 	virtual MutexMask			GetMutexMask(const BodyID *inBodies, int inNumber) const = 0;

+ 10 - 4
Jolt/Physics/Body/BodyManager.cpp

@@ -34,10 +34,16 @@ BodyManager::~BodyManager()
 	delete [] mActiveBodies;
 	delete [] mActiveBodies;
 }
 }
 
 
-void BodyManager::Init(uint inMaxBodies)
+void BodyManager::Init(uint inMaxBodies, uint inNumBodyMutexes)
 {
 {
 	UniqueLock lock(mBodiesMutex, EPhysicsLockTypes::BodiesList);
 	UniqueLock lock(mBodiesMutex, EPhysicsLockTypes::BodiesList);
 
 
+	// Num body mutexes must be a power of two and not bigger than our MutexMask
+	uint num_body_mutexes = Clamp<uint>(GetNextPowerOf2(inNumBodyMutexes == 0? 2 * thread::hardware_concurrency() : inNumBodyMutexes), 1, sizeof(MutexMask) * 8);
+
+	// Allocate the body mutexes
+	mBodyMutexes.Init(num_body_mutexes);
+
 	// Allocate space for bodies
 	// Allocate space for bodies
 	mBodies.reserve(inMaxBodies);
 	mBodies.reserve(inMaxBodies);
 
 
@@ -344,12 +350,12 @@ void BodyManager::SetBodyActivationListener(BodyActivationListener *inListener)
 
 
 BodyManager::MutexMask BodyManager::GetMutexMask(const BodyID *inBodies, int inNumber) const
 BodyManager::MutexMask BodyManager::GetMutexMask(const BodyID *inBodies, int inNumber) const
 {
 {
-	static_assert(sizeof(MutexMask) * 8 == BodyMutexes::NumMutexes, "MutexMask must have the same amount of bits");
+	JPH_ASSERT(sizeof(MutexMask) * 8 >= mBodyMutexes.GetNumMutexes(), "MutexMask must have enough bits");
 
 
-	if (inNumber >= BodyMutexes::NumMutexes)
+	if (inNumber >= (int)mBodyMutexes.GetNumMutexes())
 	{
 	{
 		// Just lock everything if there are too many bodies
 		// Just lock everything if there are too many bodies
-		return ~MutexMask(0);
+		return GetAllBodiesMutexMask();
 	}
 	}
 	else
 	else
 	{
 	{

+ 3 - 2
Jolt/Physics/Body/BodyManager.h

@@ -31,7 +31,7 @@ public:
 									~BodyManager();
 									~BodyManager();
 
 
 	/// Initialize the manager
 	/// Initialize the manager
-	void							Init(uint inMaxBodies);
+	void							Init(uint inMaxBodies, uint inNumBodyMutexes);
 
 
 	/// Gets the current amount of bodies that are in the body manager
 	/// Gets the current amount of bodies that are in the body manager
 	uint							GetNumBodies() const;
 	uint							GetNumBodies() const;
@@ -121,6 +121,7 @@ public:
 	
 	
 	///@name Batch body mutex access (do not use directly)
 	///@name Batch body mutex access (do not use directly)
 	///@{
 	///@{
+	MutexMask						GetAllBodiesMutexMask() const				{ return mBodyMutexes.GetNumMutexes() == sizeof(MutexMask) * 8? ~MutexMask(0) : (MutexMask(1) << mBodyMutexes.GetNumMutexes()) - 1; }
 	MutexMask						GetMutexMask(const BodyID *inBodies, int inNumber) const;
 	MutexMask						GetMutexMask(const BodyID *inBodies, int inNumber) const;
 	void							LockRead(MutexMask inMutexMask) const;
 	void							LockRead(MutexMask inMutexMask) const;
 	void							UnlockRead(MutexMask inMutexMask) const;
 	void							UnlockRead(MutexMask inMutexMask) const;
@@ -243,7 +244,7 @@ private:
 	mutable Mutex					mBodiesMutex; 
 	mutable Mutex					mBodiesMutex; 
 
 
 	/// An array of mutexes protecting the bodies in the mBodies array
 	/// An array of mutexes protecting the bodies in the mBodies array
-	using BodyMutexes = MutexArray<SharedMutex, 64>;
+	using BodyMutexes = MutexArray<SharedMutex>;
 	mutable BodyMutexes				mBodyMutexes;
 	mutable BodyMutexes				mBodyMutexes;
 
 
 	/// List of next sequence number for a body ID
 	/// List of next sequence number for a body ID

+ 2 - 2
Jolt/Physics/PhysicsSystem.cpp

@@ -58,13 +58,13 @@ PhysicsSystem::~PhysicsSystem()
 	delete mBroadPhase;
 	delete mBroadPhase;
 }
 }
 
 
-void PhysicsSystem::Init(uint inMaxBodies, uint inMaxBodyPairs, uint inMaxContactConstraints, const ObjectToBroadPhaseLayer &inObjectToBroadPhaseLayer, ObjectVsBroadPhaseLayerFilter inObjectVsBroadPhaseLayerFilter, ObjectLayerPairFilter inObjectLayerPairFilter)
+void PhysicsSystem::Init(uint inMaxBodies, uint inNumBodyMutexes, uint inMaxBodyPairs, uint inMaxContactConstraints, const ObjectToBroadPhaseLayer &inObjectToBroadPhaseLayer, ObjectVsBroadPhaseLayerFilter inObjectVsBroadPhaseLayerFilter, ObjectLayerPairFilter inObjectLayerPairFilter)
 { 
 { 
 	mObjectVsBroadPhaseLayerFilter = inObjectVsBroadPhaseLayerFilter;
 	mObjectVsBroadPhaseLayerFilter = inObjectVsBroadPhaseLayerFilter;
 	mObjectLayerPairFilter = inObjectLayerPairFilter;
 	mObjectLayerPairFilter = inObjectLayerPairFilter;
 
 
 	// Initialize body manager
 	// Initialize body manager
-	mBodyManager.Init(inMaxBodies); 
+	mBodyManager.Init(inMaxBodies, inNumBodyMutexes); 
 
 
 	// Create broadphase
 	// Create broadphase
 	mBroadPhase = new BROAD_PHASE();
 	mBroadPhase = new BROAD_PHASE();

+ 2 - 1
Jolt/Physics/PhysicsSystem.h

@@ -30,11 +30,12 @@ public:
 
 
 	/// Initialize the system.
 	/// Initialize the system.
 	/// @param inMaxBodies Maximum number of bodies to support.
 	/// @param inMaxBodies Maximum number of bodies to support.
+	/// @param inNumBodyMutexes Number of body mutexes to use. Should be a power of 2 in the range [1, 64], use 0 to auto detect.
 	/// @param inMaxBodyPairs Maximum amount of body pairs to process (anything else will fall through the world), this number should generally be much higher than the max amount of contact points as there will be lots of bodies close that are not actually touching
 	/// @param inMaxBodyPairs Maximum amount of body pairs to process (anything else will fall through the world), this number should generally be much higher than the max amount of contact points as there will be lots of bodies close that are not actually touching
 	/// @param inMaxContactConstraints Maximum amount of contact constraints to process (anything else will fall through the world)
 	/// @param inMaxContactConstraints Maximum amount of contact constraints to process (anything else will fall through the world)
 	/// @param inObjectToBroadPhaseLayer Maps object layer to broadphase layer, @see ObjectToBroadPhaseLayer.
 	/// @param inObjectToBroadPhaseLayer Maps object layer to broadphase layer, @see ObjectToBroadPhaseLayer.
 	/// @param inObjectLayerPairFilter Filter callback function that is used to determine if two object layers collide.
 	/// @param inObjectLayerPairFilter Filter callback function that is used to determine if two object layers collide.
-	void						Init(uint inMaxBodies, uint inMaxBodyPairs, uint inMaxContactConstraints, const ObjectToBroadPhaseLayer &inObjectToBroadPhaseLayer, ObjectVsBroadPhaseLayerFilter inObjectVsBroadPhaseLayerFilter, ObjectLayerPairFilter inObjectLayerPairFilter);
+	void						Init(uint inMaxBodies, uint inNumBodyMutexes, uint inMaxBodyPairs, uint inMaxContactConstraints, const ObjectToBroadPhaseLayer &inObjectToBroadPhaseLayer, ObjectVsBroadPhaseLayerFilter inObjectVsBroadPhaseLayerFilter, ObjectLayerPairFilter inObjectLayerPairFilter);
 	
 	
 	/// Listener that is notified whenever a body is activated/deactivated
 	/// Listener that is notified whenever a body is activated/deactivated
 	void						SetBodyActivationListener(BodyActivationListener *inListener) { mBodyManager.SetBodyActivationListener(inListener); }
 	void						SetBodyActivationListener(BodyActivationListener *inListener) { mBodyManager.SetBodyActivationListener(inListener); }

+ 2 - 1
Samples/SamplesApp.cpp

@@ -288,6 +288,7 @@ static TestCategory sAllCategories[] =
 // Configuration
 // Configuration
 //-----------------------------------------------------------------------------
 //-----------------------------------------------------------------------------
 static constexpr uint cNumBodies = 10240;
 static constexpr uint cNumBodies = 10240;
+static constexpr uint cNumBodyMutexes = 0; // Autodetect
 static constexpr uint cMaxBodyPairs = 65536;
 static constexpr uint cMaxBodyPairs = 65536;
 static constexpr uint cMaxContactConstraints = 10240;
 static constexpr uint cMaxContactConstraints = 10240;
 
 
@@ -502,7 +503,7 @@ void SamplesApp::StartTest(const RTTI *inRTTI)
 
 
 	// Create physics system
 	// Create physics system
 	mPhysicsSystem = new PhysicsSystem();
 	mPhysicsSystem = new PhysicsSystem();
-	mPhysicsSystem->Init(cNumBodies, cMaxBodyPairs, cMaxContactConstraints, GetObjectToBroadPhaseLayer(), BroadPhaseCanCollide, ObjectCanCollide);
+	mPhysicsSystem->Init(cNumBodies, cNumBodyMutexes, cMaxBodyPairs, cMaxContactConstraints, GetObjectToBroadPhaseLayer(), BroadPhaseCanCollide, ObjectCanCollide);
 	mPhysicsSystem->SetPhysicsSettings(mPhysicsSettings);
 	mPhysicsSystem->SetPhysicsSettings(mPhysicsSettings);
 #if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED)
 #if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED)
 	mPhysicsSystem->SetBroadPhaseLayerToString(GetBroadPhaseLayerName);
 	mPhysicsSystem->SetBroadPhaseLayerToString(GetBroadPhaseLayerName);

+ 1 - 1
Samples/Tests/BroadPhase/BroadPhaseTest.cpp

@@ -54,7 +54,7 @@ void BroadPhaseTest::Initialize()
 {
 {
 	// Create body manager
 	// Create body manager
 	mBodyManager = new BodyManager();
 	mBodyManager = new BodyManager();
-	mBodyManager->Init(NUM_BODIES);
+	mBodyManager->Init(NUM_BODIES, 0);
 		
 		
 	// Crate broadphase
 	// Crate broadphase
 	mObjectToBroadPhaseLayer = GetObjectToBroadPhaseLayer();
 	mObjectToBroadPhaseLayer = GetObjectToBroadPhaseLayer();

+ 1 - 1
UnitTests/Physics/BroadPhaseTests.cpp

@@ -17,7 +17,7 @@ TEST_SUITE("BroadPhaseTests")
 	{
 	{
 		// Create body manager
 		// Create body manager
 		BodyManager body_manager;
 		BodyManager body_manager;
-		body_manager.Init(1);
+		body_manager.Init(1, 0);
 		
 		
 		// Create quad tree
 		// Create quad tree
 		BroadPhaseQuadTree broadphase;
 		BroadPhaseQuadTree broadphase;

+ 1 - 1
UnitTests/Physics/RayShapeTests.cpp

@@ -274,7 +274,7 @@ TEST_SUITE("RayShapeTests")
 		ObjectToBroadPhaseLayer object_to_broadphase;
 		ObjectToBroadPhaseLayer object_to_broadphase;
 		object_to_broadphase.push_back(BroadPhaseLayer(0));
 		object_to_broadphase.push_back(BroadPhaseLayer(0));
 		PhysicsSystem system;
 		PhysicsSystem system;
-		system.Init(1, 4, 4, object_to_broadphase, [](ObjectLayer, BroadPhaseLayer) { return true; }, [](ObjectLayer, ObjectLayer) { return true; });
+		system.Init(1, 0, 4, 4, object_to_broadphase, [](ObjectLayer, BroadPhaseLayer) { return true; }, [](ObjectLayer, ObjectLayer) { return true; });
 		system.GetBodyInterface().CreateAndAddBody(BodyCreationSettings(inShape, cShapePosition, cShapeRotation, EMotionType::Static, 0), EActivation::DontActivate);
 		system.GetBodyInterface().CreateAndAddBody(BodyCreationSettings(inShape, cShapePosition, cShapeRotation, EMotionType::Static, 0), EActivation::DontActivate);
 			   
 			   
 
 

+ 1 - 1
UnitTests/PhysicsTestContext.cpp

@@ -23,7 +23,7 @@ PhysicsTestContext::PhysicsTestContext(float inDeltaTime, int inCollisionSteps,
 {
 {
 	// Create physics system
 	// Create physics system
 	mSystem = new PhysicsSystem();
 	mSystem = new PhysicsSystem();
-	mSystem->Init(1024, 4096, 1024, GetObjectToBroadPhaseLayer(), BroadPhaseCanCollide, ObjectCanCollide);
+	mSystem->Init(1024, 0, 4096, 1024, GetObjectToBroadPhaseLayer(), BroadPhaseCanCollide, ObjectCanCollide);
 }
 }
 
 
 PhysicsTestContext::~PhysicsTestContext()
 PhysicsTestContext::~PhysicsTestContext()