Allow the MemoryPool class to start with zero allocated memory and grow when more memory is needed
This commit is contained in:
parent
fcac6457a7
commit
19f7925d47
|
@ -46,8 +46,7 @@ using namespace std;
|
|||
|
||||
// Constructor
|
||||
CollisionDetection::CollisionDetection(PhysicsWorld* world)
|
||||
: world(world), memoryPoolContacts(NB_MAX_CONTACTS), memoryPoolOverlappingPairs(NB_MAX_COLLISION_PAIRS),
|
||||
narrowPhaseGJKAlgorithm(memoryPoolContactInfos), memoryPoolContactInfos(NB_MAX_CONTACTS),
|
||||
: world(world), narrowPhaseGJKAlgorithm(memoryPoolContactInfos),
|
||||
narrowPhaseSphereVsSphereAlgorithm(memoryPoolContactInfos) {
|
||||
|
||||
// Create the broad-phase algorithm that will be used (Sweep and Prune with AABB)
|
||||
|
@ -86,7 +85,7 @@ bool CollisionDetection::computeCollisionDetection() {
|
|||
double startTime = timeValueStart.tv_sec * 1000000.0 + (timeValueStart.tv_usec);
|
||||
double stopTime = timeValueStop.tv_sec * 1000000.0 + (timeValueStop.tv_usec);
|
||||
double deltaTime = stopTime - startTime;
|
||||
printf("Broadphase time : %f micro sec \n", deltaTime);
|
||||
//printf("Broadphase time : %f micro sec \n", deltaTime);
|
||||
|
||||
// Compute the narrow-phase collision detection
|
||||
bool collisionExists = computeNarrowPhase();
|
||||
|
@ -108,10 +107,10 @@ void CollisionDetection::computeBroadPhase() {
|
|||
broadPhaseAlgorithm->updateObject(*it, *((*it)->getAABB()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO : DELETE THIS
|
||||
std::cout << "Nb overlapping pairs : " << overlappingPairs.size() << std::endl;
|
||||
std::cout << "Nb active pairs in pair manager : " << broadPhaseAlgorithm->getNbOverlappingPairs() << std::endl;
|
||||
//std::cout << "Nb overlapping pairs : " << overlappingPairs.size() << std::endl;
|
||||
//std::cout << "Nb active pairs in pair manager : " << broadPhaseAlgorithm->getNbOverlappingPairs() << std::endl;
|
||||
}
|
||||
|
||||
// Compute the narrow-phase collision detection
|
||||
|
@ -165,8 +164,7 @@ bool CollisionDetection::computeNarrowPhase() {
|
|||
// Allow the broadphase to notify the collision detection about an overlapping pair
|
||||
// This method is called by a broad-phase collision detection algorithm
|
||||
void CollisionDetection::broadPhaseNotifyAddedOverlappingPair(const BroadPhasePair* addedPair) {
|
||||
std::cout << "New overlapping pair : id0=" << addedPair->body1->getID() << ", id=" << addedPair->body2->getID() << std::endl;
|
||||
|
||||
|
||||
// Construct the pair of body index
|
||||
pair<bodyindex, bodyindex> indexPair = addedPair->body1->getID() < addedPair->body2->getID() ? make_pair(addedPair->body1->getID(), addedPair->body2->getID()) :
|
||||
make_pair(addedPair->body2->getID(), addedPair->body1->getID());
|
||||
|
|
|
@ -76,7 +76,7 @@ class PairManager {
|
|||
luint computeNextPowerOfTwo(luint number) const; // Return the next power of two
|
||||
void reallocatePairs(); // Reallocate memory for more pairs
|
||||
void shrinkMemory(); // Shrink the allocated memory
|
||||
bodyindex computePairOffset(const BroadPhasePair* pair) const; // Compute the offset of a given pair
|
||||
bodyindex computePairOffset(const BroadPhasePair* pair) const; // Compute the offset of a given pair
|
||||
BroadPhasePair* lookForAPair(bodyindex id1, bodyindex id2,
|
||||
luint hashValue) const; // Look for a pair in the set of overlapping pairs
|
||||
BroadPhasePair* findPairWithHashValue(bodyindex id1, bodyindex id2,
|
||||
|
|
|
@ -70,7 +70,6 @@ const reactphysics3d::decimal PERSISTENT_CONTACT_DIST_THRESHOLD = 0.02; // Dist
|
|||
const int NB_MAX_BODIES = 100000; // Maximum number of bodies
|
||||
const int NB_MAX_CONTACTS = 100000; // Maximum number of contacts (for memory pool allocation)
|
||||
const int NB_MAX_CONSTRAINTS = 100000; // Maximum number of constraints
|
||||
const int NB_MAX_COLLISION_PAIRS = 10000; // Maximum number of collision pairs of bodies (for memory pool allocation)
|
||||
|
||||
// Constraint solver constants
|
||||
const uint DEFAULT_LCP_ITERATIONS = 15; // Number of iterations when solving a LCP problem
|
||||
|
|
|
@ -34,7 +34,7 @@ using namespace std;
|
|||
|
||||
// Constructor
|
||||
PhysicsWorld::PhysicsWorld(const Vector3& gravity)
|
||||
: gravity(gravity), isGravityOn(true), currentBodyID(0), memoryPoolRigidBodies(NB_MAX_BODIES) {
|
||||
: gravity(gravity), isGravityOn(true), currentBodyID(0) {
|
||||
}
|
||||
|
||||
// Destructor
|
||||
|
|
|
@ -36,12 +36,6 @@
|
|||
// TODO : Check that casting is done correctly in this class using
|
||||
// C++ cast operator like reinterpret_cast<>, ...
|
||||
|
||||
// TODO : Rename the variable "maxNbObjects" into "capacity"
|
||||
|
||||
// TODO : Replace uint by luint in this class
|
||||
|
||||
// TODO : Make the memory pool to start without allocated memory and to grow
|
||||
// by using several blocks instead of only one.
|
||||
|
||||
// ReactPhysics3D namespace
|
||||
namespace reactphysics3d {
|
||||
|
@ -58,135 +52,181 @@ template<class T>
|
|||
class MemoryPool {
|
||||
private:
|
||||
|
||||
// Unit of memory
|
||||
struct Unit {
|
||||
struct Unit* pNext; // Pointer to the next memory unit
|
||||
struct Unit* pPrevious; // Pointer to the previous memory unit
|
||||
// MemoryUnit represents a unit of memory
|
||||
struct MemoryUnit {
|
||||
struct MemoryUnit* pNext; // Pointer to the next memory unit
|
||||
struct MemoryUnit* pPrevious; // Pointer to the previous memory unit
|
||||
};
|
||||
|
||||
void* pMemoryBlock; // Pointer to the whole memory block
|
||||
struct Unit* pAllocatedLinkedList; // Pointer to the linked list of allocated memory units
|
||||
struct Unit* pFreeLinkedList; // Pointer to the linked list of free memory units
|
||||
size_t memorySize; // Total allocated memory in the pool
|
||||
uint currentNbObjects; // Current number of objects in the pool
|
||||
const uint maxNbObjects; // Maximum number of objects in the pool
|
||||
// Memory block (that contains several memory units)
|
||||
struct MemoryBlock {
|
||||
struct MemoryBlock* pNext; // Pointer to the next memory block
|
||||
};
|
||||
|
||||
static const uint NB_OBJECTS_FIRST_BLOCK; // Number of objects allocated in the first block
|
||||
void* pBlocks; // Pointer to the first allocated memory block
|
||||
struct MemoryUnit* pAllocatedUnits; // Pointer to the first allocated memory unit
|
||||
struct MemoryUnit* pFreeUnits; // Pointer to the first free memory unit
|
||||
uint currentNbObjects; // Current number of objects in the pool
|
||||
uint capacity; // Current maximum number of objects that can be allocated in the pool
|
||||
uint nbObjectsNextBlock; // Number of objects to allocate in the next block
|
||||
void allocateMemory(); // Allocate more memory (more blocks) when needed
|
||||
|
||||
public:
|
||||
MemoryPool(uint nbObjectsToAllocate) throw(std::bad_alloc); // Constructor
|
||||
~MemoryPool(); // Destructor
|
||||
MemoryPool(uint capacity = 0) throw(std::bad_alloc); // Constructor
|
||||
~MemoryPool(); // Destructor
|
||||
|
||||
uint getMaxNbObjects() const; // Return the maximum number of objects allowed in the pool
|
||||
uint getCapacity() const; // Return the current maximum number of objects allowed in the pool
|
||||
uint getCurrentNbObjects() const; // Return the current number of objects in the pool
|
||||
void* allocateObject(); // Return a pointer to an memory allocated location to store a new object
|
||||
void freeObject(void* pObjectToFree); // Tell the pool that an object doesn't need to be store in the pool anymore
|
||||
};
|
||||
|
||||
// static member definition
|
||||
template<class T> const uint MemoryPool<T>::NB_OBJECTS_FIRST_BLOCK = 100;
|
||||
|
||||
// Constructor
|
||||
// Allocate a large block of memory in order to contain
|
||||
// a given number of object of the template type T
|
||||
template<class T>
|
||||
MemoryPool<T>::MemoryPool(uint nbObjectsToAllocate) throw(std::bad_alloc)
|
||||
: currentNbObjects(0), maxNbObjects(nbObjectsToAllocate) {
|
||||
pMemoryBlock = NULL;
|
||||
pAllocatedLinkedList = NULL;
|
||||
pFreeLinkedList = NULL;
|
||||
|
||||
// Compute the total memory size that need to be allocated
|
||||
memorySize = nbObjectsToAllocate * (sizeof(struct Unit) + sizeof(T));
|
||||
|
||||
// Allocate the whole memory block
|
||||
pMemoryBlock = malloc(memorySize);
|
||||
|
||||
// Check that the allocation didn't fail
|
||||
if (!pMemoryBlock) throw std::bad_alloc();
|
||||
|
||||
// For each allocated memory unit
|
||||
for (uint i=0; i<nbObjectsToAllocate; i++) {
|
||||
// Get the adress of a memory unit
|
||||
struct Unit* currentUnit = (struct Unit*)( (char*)pMemoryBlock + i * (sizeof(T) + sizeof(struct Unit)) );
|
||||
|
||||
currentUnit->pPrevious = NULL;
|
||||
currentUnit->pNext = pFreeLinkedList;
|
||||
|
||||
if (pFreeLinkedList) {
|
||||
pFreeLinkedList->pPrevious = currentUnit;
|
||||
}
|
||||
|
||||
pFreeLinkedList = currentUnit;
|
||||
}
|
||||
MemoryPool<T>::MemoryPool(uint capacity) throw(std::bad_alloc)
|
||||
: currentNbObjects(0), capacity(capacity) {
|
||||
pBlocks = 0;
|
||||
pAllocatedUnits = 0;
|
||||
pFreeUnits = 0;
|
||||
nbObjectsNextBlock = (capacity == 0) ? NB_OBJECTS_FIRST_BLOCK : capacity;
|
||||
|
||||
// Allocate the first memory block if the capacity is
|
||||
// different from zero
|
||||
if (capacity) allocateMemory();
|
||||
}
|
||||
|
||||
// Destructor
|
||||
// Deallocate the block of memory that has been allocated previously
|
||||
// Deallocate the blocks of memory that have been allocated previously
|
||||
template<class T>
|
||||
MemoryPool<T>::~MemoryPool() {
|
||||
|
||||
// Check if we have a memory leak
|
||||
assert(currentNbObjects == 0);
|
||||
|
||||
// Release the whole memory block
|
||||
free(pMemoryBlock);
|
||||
// Release all the allocated memory blocks
|
||||
struct MemoryBlock* currentBlock = (struct MemoryBlock*) pBlocks;
|
||||
while(currentBlock != 0) {
|
||||
MemoryBlock* tempBlock = currentBlock->pNext;
|
||||
free(currentBlock);
|
||||
currentBlock = tempBlock;
|
||||
}
|
||||
}
|
||||
|
||||
// Return a pointer to an memory allocated location to store a new object
|
||||
// This method does not allocate memory because it has already been done at the
|
||||
// beginning but it returns a pointer to a location in the allocated block of
|
||||
// memory where a new object can be stored
|
||||
// Return a pointer to a memory allocated location to store a new object
|
||||
// This method only allocates memory if needed and it returns a pointer
|
||||
// to a location in an allocated block of memory where a new object can be stored
|
||||
template<class T>
|
||||
void* MemoryPool<T>::allocateObject() {
|
||||
// If no memory unit is available in the current allocated memory block
|
||||
assert(currentNbObjects < maxNbObjects);
|
||||
assert(pFreeLinkedList);
|
||||
|
||||
struct Unit* currentUnit = pFreeLinkedList;
|
||||
pFreeLinkedList = currentUnit->pNext;
|
||||
if (pFreeLinkedList) {
|
||||
pFreeLinkedList->pPrevious = NULL;
|
||||
|
||||
// If there is not enough allocated memory in the pool
|
||||
if (currentNbObjects == capacity) {
|
||||
|
||||
// Allocate a new memory block
|
||||
allocateMemory();
|
||||
}
|
||||
|
||||
currentUnit->pNext = pAllocatedLinkedList;
|
||||
if (pAllocatedLinkedList) {
|
||||
pAllocatedLinkedList->pPrevious = currentUnit;
|
||||
assert(currentNbObjects < capacity);
|
||||
assert(pFreeUnits);
|
||||
|
||||
struct MemoryUnit* currentUnit = pFreeUnits;
|
||||
pFreeUnits = currentUnit->pNext;
|
||||
if (pFreeUnits) {
|
||||
pFreeUnits->pPrevious = 0;
|
||||
}
|
||||
pAllocatedLinkedList = currentUnit;
|
||||
|
||||
currentUnit->pNext = pAllocatedUnits;
|
||||
if (pAllocatedUnits) {
|
||||
pAllocatedUnits->pPrevious = currentUnit;
|
||||
}
|
||||
pAllocatedUnits = currentUnit;
|
||||
|
||||
currentNbObjects++;
|
||||
|
||||
// Return a pointer to the allocated memory unit
|
||||
return (void*)((char*)currentUnit + sizeof(struct Unit));
|
||||
return (void*)((char*)currentUnit + sizeof(struct MemoryUnit));
|
||||
}
|
||||
|
||||
// Tell the pool that an object doesn't need to be store in the pool anymore
|
||||
// Tell the pool that an object does not need to be stored in the pool anymore
|
||||
// This method does not deallocate memory because it will be done only at the
|
||||
// end but it informs the memory pool that an object that was stored in the heap
|
||||
// end but it notifies the memory pool that an object that was stored in the pool
|
||||
// does not need to be stored anymore and therefore we can use the corresponding
|
||||
// location in the pool for another object
|
||||
template<class T>
|
||||
void MemoryPool<T>::freeObject(void* pObjectToFree) {
|
||||
|
||||
// The pointer location must be inside the memory block
|
||||
assert(pMemoryBlock<pObjectToFree && pObjectToFree<(void*)((char*)pMemoryBlock + memorySize));
|
||||
//assert(pBlocks<pObjectToFree && pObjectToFree<(void*)((char*)pBlocks + memorySize));
|
||||
|
||||
struct Unit* currentUnit = (struct Unit*)((char*)pObjectToFree - sizeof(struct Unit));
|
||||
pAllocatedLinkedList = currentUnit->pNext;
|
||||
if (pAllocatedLinkedList) {
|
||||
pAllocatedLinkedList->pPrevious = NULL;
|
||||
struct MemoryUnit* currentUnit = (struct MemoryUnit*)((char*)pObjectToFree - sizeof(struct MemoryUnit));
|
||||
pAllocatedUnits = currentUnit->pNext;
|
||||
if (pAllocatedUnits) {
|
||||
pAllocatedUnits->pPrevious = 0;
|
||||
}
|
||||
|
||||
currentUnit->pNext = pFreeLinkedList;
|
||||
if (pFreeLinkedList) {
|
||||
pFreeLinkedList->pPrevious = currentUnit;
|
||||
currentUnit->pNext = pFreeUnits;
|
||||
if (pFreeUnits) {
|
||||
pFreeUnits->pPrevious = currentUnit;
|
||||
}
|
||||
pFreeLinkedList = currentUnit;
|
||||
pFreeUnits = currentUnit;
|
||||
|
||||
currentNbObjects--;
|
||||
}
|
||||
|
||||
// Allocate more memory. This method is called when there are no
|
||||
// free memory units available anymore. Therefore, we need to allocate
|
||||
// a new memory block in order to be able to store several new memory units.
|
||||
template<class T>
|
||||
void MemoryPool<T>::allocateMemory() {
|
||||
|
||||
// Compute the size of the new
|
||||
size_t sizeBlock = nbObjectsNextBlock * (sizeof(struct MemoryUnit) + sizeof(T));
|
||||
|
||||
struct MemoryBlock* tempBlocks = (struct MemoryBlock*) pBlocks;
|
||||
|
||||
// Allocate a new memory block
|
||||
pBlocks = malloc(sizeBlock);
|
||||
|
||||
// Check that the allocation didn't fail
|
||||
if (!pBlocks) throw std::bad_alloc();
|
||||
|
||||
struct MemoryBlock* block = (struct MemoryBlock*) pBlocks;
|
||||
block->pNext = tempBlocks;
|
||||
|
||||
// For each allocated memory unit in the new block
|
||||
for (uint i=0; i<nbObjectsNextBlock; i++) {
|
||||
|
||||
// Get the adress of a memory unit
|
||||
struct MemoryUnit* currentUnit = (struct MemoryUnit*)( (char*)pBlocks + i * (sizeof(T) + sizeof(struct MemoryUnit)) );
|
||||
|
||||
currentUnit->pPrevious = 0;
|
||||
currentUnit->pNext = pFreeUnits;
|
||||
|
||||
if (pFreeUnits) {
|
||||
pFreeUnits->pPrevious = currentUnit;
|
||||
}
|
||||
|
||||
pFreeUnits = currentUnit;
|
||||
}
|
||||
|
||||
// Update the current capacity of the memory pool
|
||||
capacity += nbObjectsNextBlock;
|
||||
|
||||
// The next block will be two times the size of the last
|
||||
// allocated memory block
|
||||
nbObjectsNextBlock *= 2;
|
||||
}
|
||||
|
||||
|
||||
// Return the maximum number of objects allowed in the pool
|
||||
template<class T>
|
||||
uint MemoryPool<T>::getMaxNbObjects() const {
|
||||
return maxNbObjects;
|
||||
uint MemoryPool<T>::getCapacity() const {
|
||||
return capacity;
|
||||
}
|
||||
|
||||
// Return the current number of objects in the pool
|
||||
|
|
Loading…
Reference in New Issue
Block a user