Selaa lähdekoodia

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

jrouwe 3 vuotta sitten
vanhempi
commit
ef371411af

+ 38 - 11
Jolt/Core/MutexArray.h

@@ -3,25 +3,49 @@
 
 #pragma once
 
+#include <Core/NonCopyable.h>
+
 namespace JPH {
 
 /// 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.
 /// 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.
-template <class MutexType, int NumMutexesArg>
-class MutexArray
+template <class MutexType>
+class MutexArray : public NonCopyable
 {
 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
 	inline uint32			GetMutexIndex(uint32 inObjectIndex) const
 	{
 		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
@@ -41,8 +65,9 @@ public:
 	{
 		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
@@ -50,8 +75,9 @@ public:
 	{
 		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:
@@ -61,7 +87,8 @@ private:
 		MutexType			mMutex;
 	};
 
-	MutexStorage			mMutexStorage[NumMutexes];
+	MutexStorage *			mMutexStorage = nullptr;
+	uint					mNumMutexes = 0;
 };
 
 } // JPH

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

@@ -30,6 +30,12 @@ public:
 	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
 	///@{
 	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;
 }
 
-void BodyManager::Init(uint inMaxBodies)
+void BodyManager::Init(uint inMaxBodies, uint inNumBodyMutexes)
 {
 	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
 	mBodies.reserve(inMaxBodies);
 
@@ -344,12 +350,12 @@ void BodyManager::SetBodyActivationListener(BodyActivationListener *inListener)
 
 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
-		return ~MutexMask(0);
+		return GetAllBodiesMutexMask();
 	}
 	else
 	{

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

@@ -31,7 +31,7 @@ public:
 									~BodyManager();
 
 	/// 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
 	uint							GetNumBodies() const;
@@ -121,6 +121,7 @@ public:
 	
 	///@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;
 	void							LockRead(MutexMask inMutexMask) const;
 	void							UnlockRead(MutexMask inMutexMask) const;
@@ -243,7 +244,7 @@ private:
 	mutable Mutex					mBodiesMutex; 
 
 	/// An array of mutexes protecting the bodies in the mBodies array
-	using BodyMutexes = MutexArray<SharedMutex, 64>;
+	using BodyMutexes = MutexArray<SharedMutex>;
 	mutable BodyMutexes				mBodyMutexes;
 
 	/// List of next sequence number for a body ID

+ 2 - 2
Jolt/Physics/PhysicsSystem.cpp

@@ -58,13 +58,13 @@ PhysicsSystem::~PhysicsSystem()
 	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;
 	mObjectLayerPairFilter = inObjectLayerPairFilter;
 
 	// Initialize body manager
-	mBodyManager.Init(inMaxBodies); 
+	mBodyManager.Init(inMaxBodies, inNumBodyMutexes); 
 
 	// Create broadphase
 	mBroadPhase = new BROAD_PHASE();

+ 2 - 1
Jolt/Physics/PhysicsSystem.h

@@ -30,11 +30,12 @@ public:
 
 	/// Initialize the system.
 	/// @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 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 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
 	void						SetBodyActivationListener(BodyActivationListener *inListener) { mBodyManager.SetBodyActivationListener(inListener); }

+ 2 - 1
Samples/SamplesApp.cpp

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

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

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

+ 1 - 1
UnitTests/Physics/BroadPhaseTests.cpp

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

+ 1 - 1
UnitTests/Physics/RayShapeTests.cpp

@@ -274,7 +274,7 @@ TEST_SUITE("RayShapeTests")
 		ObjectToBroadPhaseLayer object_to_broadphase;
 		object_to_broadphase.push_back(BroadPhaseLayer(0));
 		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);
 			   
 

+ 1 - 1
UnitTests/PhysicsTestContext.cpp

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