GRK/dependencies/physx-4.1/source/lowlevelaabb/include/BpAABBManager.h

611 lines
21 KiB
C
Raw Normal View History

2022-01-12 16:07:16 +01:00
//
// 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.
#ifndef BP_AABBMANAGER_H
#define BP_AABBMANAGER_H
#include "PxvConfig.h"
#include "CmPhysXCommon.h"
#include "BpBroadPhaseUpdate.h"
#include "GuGeometryUnion.h"
#include "CmBitMap.h"
#include "CmTask.h"
#include "PsAllocator.h"
#include "GuBounds.h"
#include "PsHashMap.h"
#include "CmRadixSortBuffered.h"
#include "PsFoundation.h"
#include "BpAABBManagerTasks.h"
#include "PsHashSet.h"
#include "PxFiltering.h"
#include "PsSList.h"
namespace physx
{
class PxcScratchAllocator;
struct PxBroadPhaseType;
namespace Cm
{
class RenderOutput;
class EventProfiler;
class FlushPool;
}
namespace Bp
{
typedef PxU32 BoundsIndex;
typedef PxU32 AggregateHandle; // PT: currently an index in mAggregates array
typedef PxU32 ActorHandle;
struct BroadPhasePair;
struct ElementType
{
enum Enum
{
eSHAPE = 0,
eTRIGGER,
eCOUNT
};
};
PX_COMPILE_TIME_ASSERT(ElementType::eCOUNT <= 4); // 2 bits reserved for type
/**
\brief Changes to the configuration of overlap pairs are reported as void* pairs.
\note Each void* in the pair corresponds to the void* passed to AABBManager::createVolume.
@see AABBManager::createVolume, AABBManager::getCreatedOverlaps, AABBManager::getDestroyedOverlaps
*/
struct AABBOverlap
{
PX_FORCE_INLINE AABBOverlap() {}
PX_FORCE_INLINE AABBOverlap(void* userData0, void* userData1/*, ActorHandle pairHandle*/) : mUserData0(userData0), mUserData1(userData1)/*, mPairHandle(pairHandle)*/ {}
void* mUserData0;
void* mUserData1;
/* union
{
ActorHandle mPairHandle; //For created pairs, this is the index into the pair in the pair manager
void* mUserData; //For deleted pairs, this is the user data written by the application to the pair
};*/
void* mPairUserData; //For deleted pairs, this is the user data written by the application to the pair
};
struct BpCacheData : public Ps::SListEntry
{
Ps::Array<AABBOverlap> mCreatedPairs[2];
Ps::Array<AABBOverlap> mDeletedPairs[2];
void reset()
{
mCreatedPairs[0].resizeUninitialized(0);
mCreatedPairs[1].resizeUninitialized(0);
mDeletedPairs[0].resizeUninitialized(0);
mDeletedPairs[1].resizeUninitialized(0);
}
};
class BoundsArray : public Ps::UserAllocated
{
PX_NOCOPY(BoundsArray)
public:
BoundsArray(Ps::VirtualAllocator& allocator) : mBounds(allocator)
{
}
PX_FORCE_INLINE void initEntry(PxU32 index)
{
index++; // PT: always pretend we need one more entry, to make sure reading the last used entry will be SIMD-safe.
const PxU32 oldCapacity = mBounds.capacity();
if(index>=oldCapacity)
{
const PxU32 newCapacity = Ps::nextPowerOfTwo(index);
mBounds.reserve(newCapacity);
mBounds.forceSize_Unsafe(newCapacity);
}
}
PX_FORCE_INLINE void updateBounds(const PxTransform& transform, const Gu::GeometryUnion& geom, PxU32 index)
{
Gu::computeBounds(mBounds[index], geom.getGeometry(), transform, 0.0f, NULL, 1.0f);
mHasAnythingChanged = true;
}
PX_FORCE_INLINE const PxBounds3& getBounds(PxU32 index) const
{
return mBounds[index];
}
PX_FORCE_INLINE void setBounds(const PxBounds3& bounds, PxU32 index)
{
// PX_CHECK_AND_RETURN(bounds.isValid() && !bounds.isEmpty(), "BoundsArray::setBounds - illegal bounds\n");
mBounds[index] = bounds;
mHasAnythingChanged = true;
}
PX_FORCE_INLINE const PxBounds3* begin() const
{
return mBounds.begin();
}
PX_FORCE_INLINE PxBounds3* begin()
{
return mBounds.begin();
}
PX_FORCE_INLINE Ps::Array<PxBounds3, Ps::VirtualAllocator>& getBounds()
{
return mBounds;
}
PX_FORCE_INLINE PxU32 getCapacity() const
{
return mBounds.size();
}
void shiftOrigin(const PxVec3& shift)
{
// we shift some potential NaNs here because we don't know what's active, but should be harmless
for(PxU32 i=0;i<mBounds.size();i++)
{
mBounds[i].minimum -= shift;
mBounds[i].maximum -= shift;
}
mHasAnythingChanged = true;
}
PX_FORCE_INLINE bool hasChanged() const { return mHasAnythingChanged; }
PX_FORCE_INLINE void resetChangedState() { mHasAnythingChanged = false; }
PX_FORCE_INLINE void setChangedState() { mHasAnythingChanged = true; }
private:
Ps::Array<PxBounds3, Ps::VirtualAllocator> mBounds;
bool mHasAnythingChanged;
};
struct VolumeData
{
PX_FORCE_INLINE void reset()
{
mAggregate = PX_INVALID_U32;
mUserData = NULL;
}
PX_FORCE_INLINE void setSingleActor() { mAggregate = PX_INVALID_U32; }
PX_FORCE_INLINE bool isSingleActor() const { return mAggregate == PX_INVALID_U32; }
PX_FORCE_INLINE void setUserData(void* userData)
{
// PX_ASSERT(!(reinterpret_cast<size_t>(userData) & 3));
mUserData = userData;
}
PX_FORCE_INLINE void* getUserData() const
{
return reinterpret_cast<void*>(reinterpret_cast<size_t>(mUserData)& (~size_t(3)));
}
PX_FORCE_INLINE void setVolumeType(ElementType::Enum volumeType)
{
PX_ASSERT(volumeType < 2);
mUserData = reinterpret_cast<void*>(reinterpret_cast<size_t>(getUserData()) | static_cast<size_t>(volumeType));
}
PX_FORCE_INLINE ElementType::Enum getVolumeType() const
{
return ElementType::Enum(reinterpret_cast<size_t>(mUserData) & 3);
}
PX_FORCE_INLINE void setAggregate(AggregateHandle handle)
{
PX_ASSERT(handle!=PX_INVALID_U32);
mAggregate = (handle<<1)|1;
}
PX_FORCE_INLINE bool isAggregate() const { return !isSingleActor() && ((mAggregate&1)!=0); }
PX_FORCE_INLINE void setAggregated(AggregateHandle handle)
{
PX_ASSERT(handle!=PX_INVALID_U32);
mAggregate = (handle<<1)|0;
}
PX_FORCE_INLINE bool isAggregated() const
{
return !isSingleActor() && ((mAggregate&1)==0);
}
PX_FORCE_INLINE AggregateHandle getAggregateOwner() const { return mAggregate>>1; }
PX_FORCE_INLINE AggregateHandle getAggregate() const { return mAggregate>>1; }
private:
void* mUserData;
// PT: TODO: consider moving this to a separate array, which wouldn't be allocated at all for people not using aggregates.
// PT: current encoding:
// aggregate == PX_INVALID_U32 => single actor
// aggregate != PX_INVALID_U32 => aggregate index<<1|LSB. LSB==1 for aggregates, LSB==0 for aggregated actors.
AggregateHandle mAggregate;
};
// PT: TODO: revisit this.....
class Aggregate;
class PersistentPairs;
class PersistentActorAggregatePair;
class PersistentAggregateAggregatePair;
class PersistentSelfCollisionPairs;
struct AggPair
{
PX_FORCE_INLINE AggPair() {}
PX_FORCE_INLINE AggPair(ShapeHandle index0, ShapeHandle index1) : mIndex0(index0), mIndex1(index1) {}
ShapeHandle mIndex0;
ShapeHandle mIndex1;
PX_FORCE_INLINE bool operator==(const AggPair& p) const
{
return (p.mIndex0 == mIndex0) && (p.mIndex1 == mIndex1);
}
};
typedef Ps::CoalescedHashMap<AggPair, PersistentPairs*> AggPairMap;
// PT: TODO: isn't there a generic pair structure somewhere? refactor with AggPair anyway
struct Pair
{
PX_FORCE_INLINE Pair(PxU32 id0, PxU32 id1) : mID0(id0), mID1(id1) {}
PX_FORCE_INLINE Pair(){}
PX_FORCE_INLINE bool operator<(const Pair& p) const
{
const PxU64 value0 = *reinterpret_cast<const PxU64*>(this);
const PxU64 value1 = *reinterpret_cast<const PxU64*>(&p);
return value0 < value1;
}
PX_FORCE_INLINE bool operator==(const Pair& p) const
{
return (p.mID0 == mID0) && (p.mID1 == mID1);
}
PX_FORCE_INLINE bool operator!=(const Pair& p) const
{
return (p.mID0 != mID0) || (p.mID1 != mID1);
}
PxU32 mID0;
PxU32 mID1;
};
class AABBManager;
class PostBroadPhaseStage2Task : public Cm::Task
{
Cm::FlushPool* mFlushPool;
AABBManager& mManager;
PX_NOCOPY(PostBroadPhaseStage2Task)
public:
PostBroadPhaseStage2Task(PxU64 contextID, AABBManager& manager) : Cm::Task(contextID), mFlushPool(NULL), mManager(manager)
{
}
virtual const char* getName() const { return "PostBroadPhaseStage2Task"; }
void setFlushPool(Cm::FlushPool* pool) { mFlushPool = pool; }
virtual void runInternal();
};
class ProcessAggPairsBase;
/**
\brief A structure responsible for:
* storing an aabb representation for each active shape in the related scene
* managing the creation/removal of aabb representations when their related shapes are created/removed
* updating all aabbs that require an update due to modification of shape geometry or transform
* updating the aabb of all aggregates from the union of the aabbs of all shapes that make up each aggregate
* computing and reporting the incremental changes to the set of overlapping aabb pairs
*/
class AABBManager : public Ps::UserAllocated
{
PX_NOCOPY(AABBManager)
public:
AABBManager(BroadPhase& bp, BoundsArray& boundsArray, Ps::Array<PxReal, Ps::VirtualAllocator>& contactDistance,
PxU32 maxNbAggregates, PxU32 maxNbShapes, Ps::VirtualAllocator& allocator, PxU64 contextID,
PxPairFilteringMode::Enum kineKineFilteringMode, PxPairFilteringMode::Enum staticKineFilteringMode);
void destroy();
AggregateHandle createAggregate(BoundsIndex index, Bp::FilterGroup::Enum group, void* userData, const bool selfCollisions);
bool destroyAggregate(BoundsIndex& index, Bp::FilterGroup::Enum& group, AggregateHandle aggregateHandle);
bool addBounds(BoundsIndex index, PxReal contactDistance, Bp::FilterGroup::Enum group, void* userdata, AggregateHandle aggregateHandle, ElementType::Enum volumeType);
void reserveSpaceForBounds(BoundsIndex index);
void removeBounds(BoundsIndex index);
PX_FORCE_INLINE Ps::IntBool isMarkedForRemove(BoundsIndex index) const { return mRemovedHandleMap.boundedTest(index); }
void setContactOffset(BoundsIndex handle, PxReal offset)
{
// PT: this works even for aggregated shapes, since the corresponding bit will also be set in the 'updated' map.
mContactDistance.begin()[handle] = offset;
mPersistentStateChanged = true;
mChangedHandleMap.growAndSet(handle);
}
void setVolumeType(BoundsIndex handle, ElementType::Enum volumeType)
{
mVolumeData[handle].setVolumeType(volumeType);
}
void setBPGroup(BoundsIndex index, Bp::FilterGroup::Enum group)
{
PX_ASSERT((index + 1) < mVolumeData.size());
PX_ASSERT(group != Bp::FilterGroup::eINVALID); // PT: we use group == Bp::FilterGroup::eINVALID to mark removed/invalid entries
mGroups[index] = group;
}
// PT: TODO: revisit name: we don't "update AABBs" here anymore
void updateAABBsAndBP( PxU32 numCpuTasks,
Cm::FlushPool& flushPool,
PxcScratchAllocator* scratchAllocator,
bool hasContactDistanceUpdated,
PxBaseTask* continuation,
PxBaseTask* narrowPhaseUnlockTask);
void finalizeUpdate( PxU32 numCpuTasks,
PxcScratchAllocator* scratchAllocator,
PxBaseTask* continuation,
PxBaseTask* narrowPhaseUnlockTask);
AABBOverlap* getCreatedOverlaps(ElementType::Enum type, PxU32& count)
{
PX_ASSERT(type < ElementType::eCOUNT);
count = mCreatedOverlaps[type].size();
return mCreatedOverlaps[type].begin();
}
AABBOverlap* getDestroyedOverlaps(ElementType::Enum type, PxU32& count)
{
PX_ASSERT(type < ElementType::eCOUNT);
count = mDestroyedOverlaps[type].size();
return mDestroyedOverlaps[type].begin();
}
void freeBuffers();
void** getOutOfBoundsObjects(PxU32& nbOutOfBoundsObjects)
{
nbOutOfBoundsObjects = mOutOfBoundsObjects.size();
return mOutOfBoundsObjects.begin();
}
void clearOutOfBoundsObjects()
{
mOutOfBoundsObjects.clear();
}
void** getOutOfBoundsAggregates(PxU32& nbOutOfBoundsAggregates)
{
nbOutOfBoundsAggregates = mOutOfBoundsAggregates.size();
return mOutOfBoundsAggregates.begin();
}
void clearOutOfBoundsAggregates()
{
mOutOfBoundsAggregates.clear();
}
void shiftOrigin(const PxVec3& shift);
void visualize(Cm::RenderOutput& out);
PX_FORCE_INLINE BroadPhase* getBroadPhase() const { return &mBroadPhase; }
PX_FORCE_INLINE BoundsArray& getBoundsArray() { return mBoundsArray; }
PX_FORCE_INLINE PxU32 getNbActiveAggregates() const { return mNbAggregates; }
PX_FORCE_INLINE const float* getContactDistances() const { return mContactDistance.begin(); }
PX_FORCE_INLINE Cm::BitMapPinned& getChangedAABBMgActorHandleMap() { return mChangedHandleMap; }
PX_FORCE_INLINE void* getUserData(const BoundsIndex index) const { if (index < mVolumeData.size()) return mVolumeData[index].getUserData(); return NULL; }
PX_FORCE_INLINE PxU64 getContextId() const { return mContextID; }
void postBroadPhase(PxBaseTask*, PxBaseTask* narrowPhaseUnlockTask, Cm::FlushPool& flushPool);
BpCacheData* getBpCacheData();
void putBpCacheData(BpCacheData*);
void resetBpCacheData();
Ps::Mutex mMapLock;
private:
void reserveShapeSpace(PxU32 nbShapes);
void postBpStage2(PxBaseTask*, Cm::FlushPool&);
void postBpStage3(PxBaseTask*);
PostBroadPhaseStage2Task mPostBroadPhase2;
Cm::DelegateTask<AABBManager, &AABBManager::postBpStage3> mPostBroadPhase3;
//Cm::DelegateTask<SimpleAABBManager, &AABBManager::postBroadPhase> mPostBroadPhase;
FinalizeUpdateTask mFinalizeUpdateTask;
// PT: we have bitmaps here probably to quickly handle added/removed objects during same frame.
// PT: TODO: consider replacing with plain arrays (easier to parse, already existing below, etc)
Cm::BitMap mAddedHandleMap; // PT: indexed by BoundsIndex
Cm::BitMap mRemovedHandleMap; // PT: indexed by BoundsIndex
Cm::BitMapPinned mChangedHandleMap;
PX_FORCE_INLINE void removeBPEntry(BoundsIndex index) // PT: only for objects passed to the BP
{
if(mAddedHandleMap.test(index)) // PT: if object had been added this frame...
mAddedHandleMap.reset(index); // PT: ...then simply revert the previous operation locally (it hasn't been passed to the BP yet).
else
mRemovedHandleMap.set(index); // PT: else we need to remove it from the BP
}
PX_FORCE_INLINE void addBPEntry(BoundsIndex index)
{
if(mRemovedHandleMap.test(index))
mRemovedHandleMap.reset(index);
else
mAddedHandleMap.set(index);
}
// PT: TODO: when do we need 'Ps::VirtualAllocator' and when don't we? When memory is passed to GPU BP?
//ML: we create mGroups and mContactDistance in the AABBManager constructor. Ps::Array will take Ps::VirtualAllocator as a parameter. Therefore, if GPU BP is using,
//we will passed a pinned host memory allocator, otherwise, we will just pass a normal allocator.
Ps::Array<Bp::FilterGroup::Enum, Ps::VirtualAllocator> mGroups; // NOTE: we stick Bp::FilterGroup::eINVALID in this slot to indicate that the entry is invalid (removed or never inserted.)
Ps::Array<PxReal, Ps::VirtualAllocator>& mContactDistance;
Ps::Array<VolumeData> mVolumeData;
#ifdef BP_FILTERING_USES_TYPE_IN_GROUP
bool mLUT[Bp::FilterType::COUNT][Bp::FilterType::COUNT];
#endif
PX_FORCE_INLINE void initEntry(BoundsIndex index, PxReal contactDistance, Bp::FilterGroup::Enum group, void* userData)
{
if ((index + 1) >= mVolumeData.size())
reserveShapeSpace(index + 1);
// PT: TODO: why is this needed at all? Why aren't size() and capacity() enough?
mUsedSize = PxMax(index+1, mUsedSize);
PX_ASSERT(group != Bp::FilterGroup::eINVALID); // PT: we use group == Bp::FilterGroup::eINVALID to mark removed/invalid entries
mGroups[index] = group;
mContactDistance.begin()[index] = contactDistance;
mVolumeData[index].setUserData(userData);
}
PX_FORCE_INLINE void resetEntry(BoundsIndex index)
{
mGroups[index] = Bp::FilterGroup::eINVALID;
mContactDistance.begin()[index] = 0.0f;
mVolumeData[index].reset();
}
// PT: TODO: remove confusion between BoundsIndex and ShapeHandle here!
Ps::Array<ShapeHandle, Ps::VirtualAllocator> mAddedHandles;
Ps::Array<ShapeHandle, Ps::VirtualAllocator> mUpdatedHandles;
Ps::Array<ShapeHandle, Ps::VirtualAllocator> mRemovedHandles;
BroadPhase& mBroadPhase;
BoundsArray& mBoundsArray;
Ps::Array<void*> mOutOfBoundsObjects;
Ps::Array<void*> mOutOfBoundsAggregates;
Ps::Array<AABBOverlap> mCreatedOverlaps[ElementType::eCOUNT];
Ps::Array<AABBOverlap> mDestroyedOverlaps[ElementType::eCOUNT];
PxcScratchAllocator* mScratchAllocator;
PxBaseTask* mNarrowPhaseUnblockTask;
PxU32 mUsedSize; // highest used value + 1
bool mOriginShifted;
bool mPersistentStateChanged;
PxU32 mNbAggregates;
PxU32 mFirstFreeAggregate;
Ps::Array<Aggregate*> mAggregates; // PT: indexed by AggregateHandle
Ps::Array<Aggregate*> mDirtyAggregates;
PxU32 mTimestamp;
AggPairMap mActorAggregatePairs;
AggPairMap mAggregateAggregatePairs;
Ps::Array<ProcessAggPairsBase*> mAggPairTasks;
#ifdef BP_USE_AGGREGATE_GROUP_TAIL
// PT: TODO: even in the 3.4 trunk this stuff is a clumsy mess: groups are "BpHandle" suddenly passed
// to BroadPhaseUpdateData as "ShapeHandle".
//Free aggregate group ids.
PxU32 mAggregateGroupTide;
Ps::Array<Bp::FilterGroup::Enum> mFreeAggregateGroups; // PT: TODO: remove this useless array
#endif
Ps::HashSet<Pair> mCreatedPairs;
PxU64 mContextID;
Ps::SList mBpThreadContextPool;
PX_FORCE_INLINE Aggregate* getAggregateFromHandle(AggregateHandle handle)
{
PX_ASSERT(handle<mAggregates.size());
return mAggregates[handle];
}
#ifdef BP_USE_AGGREGATE_GROUP_TAIL
PX_FORCE_INLINE void releaseAggregateGroup(const Bp::FilterGroup::Enum group)
{
PX_ASSERT(group != Bp::FilterGroup::eINVALID);
mFreeAggregateGroups.pushBack(group);
}
PX_FORCE_INLINE Bp::FilterGroup::Enum getAggregateGroup()
{
PxU32 id;
if(mFreeAggregateGroups.size())
id = mFreeAggregateGroups.popBack();
else
{
id = mAggregateGroupTide--;
#ifdef BP_FILTERING_USES_TYPE_IN_GROUP
id<<=2;
id|=FilterType::AGGREGATE;
#endif
}
const Bp::FilterGroup::Enum group = Bp::FilterGroup::Enum(id);
PX_ASSERT(group != Bp::FilterGroup::eINVALID);
return group;
}
#endif
void startAggregateBoundsComputationTasks(PxU32 nbToGo, PxU32 numCpuTasks, Cm::FlushPool& flushPool);
PersistentActorAggregatePair* createPersistentActorAggregatePair(ShapeHandle volA, ShapeHandle volB);
PersistentAggregateAggregatePair* createPersistentAggregateAggregatePair(ShapeHandle volA, ShapeHandle volB);
void updatePairs(PersistentPairs& p, BpCacheData* data = NULL);
void handleOriginShift();
public:
void processBPCreatedPair(const BroadPhasePair& pair);
void processBPDeletedPair(const BroadPhasePair& pair);
// bool checkID(ShapeHandle id);
friend class PersistentActorAggregatePair;
friend class PersistentAggregateAggregatePair;
friend class ProcessSelfCollisionPairsParallel;
friend class PostBroadPhaseStage2Task;
};
} //namespace Bp
} //namespace physx
#endif //BP_AABBMANAGER_H