Projekt_Grafika/dependencies/physx-4.1/source/lowlevel/common/src/pipeline/PxcNpMemBlockPool.cpp
Jakub Adamski f5087ee7b6 new repo
2021-01-29 17:02:11 +01:00

352 lines
9.3 KiB
C++

//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "foundation/PxPreprocessor.h"
#include "foundation/PxMath.h"
#include "PxcNpMemBlockPool.h"
#include "PsUserAllocated.h"
#include "PsInlineArray.h"
#include "PsFoundation.h"
using namespace physx;
PxcNpMemBlockPool::PxcNpMemBlockPool(PxcScratchAllocator& allocator):
mConstraints(PX_DEBUG_EXP("PxcNpMemBlockPool::mConstraints")),
mExceptionalConstraints(PX_DEBUG_EXP("PxcNpMemBlockPool::mExceptionalConstraints")),
mNpCacheActiveStream(0),
mFrictionActiveStream(0),
mCCDCacheActiveStream(0),
mContactIndex(0),
mAllocatedBlocks(0),
mMaxBlocks(0),
mUsedBlocks(0),
mMaxUsedBlocks(0),
mScratchBlockAddr(0),
mNbScratchBlocks(0),
mScratchAllocator(allocator),
mPeakConstraintAllocations(0),
mConstraintAllocations(0)
{
}
void PxcNpMemBlockPool::init(PxU32 initialBlockCount, PxU32 maxBlocks)
{
mMaxBlocks = maxBlocks;
mInitialBlocks = initialBlockCount;
PxU32 reserve = PxMax<PxU32>(initialBlockCount, 64);
mConstraints.reserve(reserve);
mExceptionalConstraints.reserve(16);
mFriction[0].reserve(reserve);
mFriction[1].reserve(reserve);
mNpCache[0].reserve(reserve);
mNpCache[1].reserve(reserve);
mUnused.reserve(reserve);
setBlockCount(initialBlockCount);
}
PxU32 PxcNpMemBlockPool::getUsedBlockCount() const
{
return mUsedBlocks;
}
PxU32 PxcNpMemBlockPool::getMaxUsedBlockCount() const
{
return mMaxUsedBlocks;
}
PxU32 PxcNpMemBlockPool::getPeakConstraintBlockCount() const
{
return mPeakConstraintAllocations;
}
void PxcNpMemBlockPool::setBlockCount(PxU32 blockCount)
{
Ps::Mutex::ScopedLock lock(mLock);
PxU32 current = getUsedBlockCount();
for(PxU32 i=current;i<blockCount;i++)
{
mUnused.pushBack(reinterpret_cast<PxcNpMemBlock *>(PX_ALLOC(PxcNpMemBlock::SIZE, "PxcNpMemBlock")));
mAllocatedBlocks++;
}
}
void PxcNpMemBlockPool::releaseUnusedBlocks()
{
Ps::Mutex::ScopedLock lock(mLock);
while(mUnused.size())
{
PX_FREE(mUnused.popBack());
mAllocatedBlocks--;
}
}
PxcNpMemBlockPool::~PxcNpMemBlockPool()
{
// swapping twice guarantees all blocks are released from the stream pairs
swapFrictionStreams();
swapFrictionStreams();
swapNpCacheStreams();
swapNpCacheStreams();
releaseConstraintMemory();
releaseContacts();
releaseContacts();
PX_ASSERT(mUsedBlocks == 0);
flushUnused();
}
void PxcNpMemBlockPool::acquireConstraintMemory()
{
PxU32 size;
void* addr = mScratchAllocator.allocAll(size);
size = size&~(PxcNpMemBlock::SIZE-1);
PX_ASSERT(mScratchBlocks.size()==0);
mScratchBlockAddr = reinterpret_cast<PxcNpMemBlock*>(addr);
mNbScratchBlocks = size/PxcNpMemBlock::SIZE;
mScratchBlocks.resize(mNbScratchBlocks);
for(PxU32 i=0;i<mNbScratchBlocks;i++)
mScratchBlocks[i] = mScratchBlockAddr+i;
}
void PxcNpMemBlockPool::releaseConstraintMemory()
{
Ps::Mutex::ScopedLock lock(mLock);
mPeakConstraintAllocations = mConstraintAllocations = 0;
while(mConstraints.size())
{
PxcNpMemBlock* block = mConstraints.popBack();
if(mScratchAllocator.isScratchAddr(block))
mScratchBlocks.pushBack(block);
else
{
mUnused.pushBack(block);
PX_ASSERT(mUsedBlocks>0);
mUsedBlocks--;
}
}
for(PxU32 i=0;i<mExceptionalConstraints.size();i++)
PX_FREE(mExceptionalConstraints[i]);
mExceptionalConstraints.clear();
PX_ASSERT(mScratchBlocks.size()==mNbScratchBlocks); // check we released them all
mScratchBlocks.clear();
if(mScratchBlockAddr)
{
mScratchAllocator.free(mScratchBlockAddr);
mScratchBlockAddr = 0;
mNbScratchBlocks = 0;
}
}
PxcNpMemBlock* PxcNpMemBlockPool::acquire(PxcNpMemBlockArray& trackingArray, PxU32* allocationCount, PxU32* peakAllocationCount, bool isScratchAllocation)
{
Ps::Mutex::ScopedLock lock(mLock);
if(allocationCount && peakAllocationCount)
{
*peakAllocationCount = PxMax(*allocationCount + 1, *peakAllocationCount);
(*allocationCount)++;
}
// this is a bit of hack - the logic would be better placed in acquireConstraintBlock, but then we'd have to grab the mutex
// once there to check the scratch block array and once here if we fail - or, we'd need a larger refactor to separate out
// locking and acquisition.
if(isScratchAllocation && mScratchBlocks.size()>0)
{
PxcNpMemBlock* block = mScratchBlocks.popBack();
trackingArray.pushBack(block);
return block;
}
if(mUnused.size())
{
PxcNpMemBlock* block = mUnused.popBack();
trackingArray.pushBack(block);
mMaxUsedBlocks = PxMax<PxU32>(mUsedBlocks+1, mMaxUsedBlocks);
mUsedBlocks++;
return block;
}
if(mAllocatedBlocks == mMaxBlocks)
{
#if PX_CHECKED
Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__,
"Reached maximum number of allocated blocks so 16k block allocation will fail!");
#endif
return NULL;
}
#if PX_CHECKED
if(mInitialBlocks)
{
Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__,
"Number of required 16k memory blocks has exceeded the initial number of blocks. Allocator is being called. Consider increasing the number of pre-allocated 16k blocks.");
}
#endif
// increment here so that if we hit the limit in separate threads we won't overallocated
mAllocatedBlocks++;
PxcNpMemBlock* block = reinterpret_cast<PxcNpMemBlock*>(PX_ALLOC(sizeof(PxcNpMemBlock), "PxcNpMemBlock"));
if(block)
{
trackingArray.pushBack(block);
mMaxUsedBlocks = PxMax<PxU32>(mUsedBlocks+1, mMaxUsedBlocks);
mUsedBlocks++;
}
else
mAllocatedBlocks--;
return block;
}
PxU8* PxcNpMemBlockPool::acquireExceptionalConstraintMemory(PxU32 size)
{
PxU8* memory = reinterpret_cast<PxU8*>(PX_ALLOC(size, "PxcNpExceptionalMemory"));
if(memory)
{
Ps::Mutex::ScopedLock lock(mLock);
mExceptionalConstraints.pushBack(memory);
}
return memory;
}
void PxcNpMemBlockPool::release(PxcNpMemBlockArray& deadArray, PxU32* allocationCount)
{
Ps::Mutex::ScopedLock lock(mLock);
PX_ASSERT(mUsedBlocks >= deadArray.size());
mUsedBlocks -= deadArray.size();
if(allocationCount)
{
*allocationCount -= deadArray.size();
}
while(deadArray.size())
{
PxcNpMemBlock* block = deadArray.popBack();
for(PxU32 a = 0; a < mUnused.size(); ++a)
{
PX_ASSERT(mUnused[a] != block);
}
mUnused.pushBack(block);
}
}
void PxcNpMemBlockPool::flushUnused()
{
while(mUnused.size())
PX_FREE(mUnused.popBack());
}
PxcNpMemBlock* PxcNpMemBlockPool::acquireConstraintBlock()
{
// we track the scratch blocks in the constraint block array, because the code in acquireMultipleConstraintBlocks
// assumes that acquired blocks are listed there.
return acquire(mConstraints);
}
PxcNpMemBlock* PxcNpMemBlockPool::acquireConstraintBlock(PxcNpMemBlockArray& memBlocks)
{
return acquire(memBlocks, &mConstraintAllocations, &mPeakConstraintAllocations, true);
}
PxcNpMemBlock* PxcNpMemBlockPool::acquireContactBlock()
{
return acquire(mContacts[mContactIndex], NULL, NULL, true);
}
void PxcNpMemBlockPool::releaseConstraintBlocks(PxcNpMemBlockArray& memBlocks)
{
Ps::Mutex::ScopedLock lock(mLock);
while(memBlocks.size())
{
PxcNpMemBlock* block = memBlocks.popBack();
if(mScratchAllocator.isScratchAddr(block))
mScratchBlocks.pushBack(block);
else
{
mUnused.pushBack(block);
PX_ASSERT(mUsedBlocks>0);
mUsedBlocks--;
}
}
}
void PxcNpMemBlockPool::releaseContacts()
{
//releaseConstraintBlocks(mContacts);
release(mContacts[1-mContactIndex]);
mContactIndex = 1-mContactIndex;
}
PxcNpMemBlock* PxcNpMemBlockPool::acquireFrictionBlock()
{
return acquire(mFriction[mFrictionActiveStream]);
}
void PxcNpMemBlockPool::swapFrictionStreams()
{
release(mFriction[1-mFrictionActiveStream]);
mFrictionActiveStream = 1-mFrictionActiveStream;
}
PxcNpMemBlock* PxcNpMemBlockPool::acquireNpCacheBlock()
{
return acquire(mNpCache[mNpCacheActiveStream]);
}
void PxcNpMemBlockPool::swapNpCacheStreams()
{
release(mNpCache[1-mNpCacheActiveStream]);
mNpCacheActiveStream = 1-mNpCacheActiveStream;
}