// // 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 mCreatedPairs[2]; Ps::Array 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& 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; 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(userData) & 3)); mUserData = userData; } PX_FORCE_INLINE void* getUserData() const { return reinterpret_cast(reinterpret_cast(mUserData)& (~size_t(3))); } PX_FORCE_INLINE void setVolumeType(ElementType::Enum volumeType) { PX_ASSERT(volumeType < 2); mUserData = reinterpret_cast(reinterpret_cast(getUserData()) | static_cast(volumeType)); } PX_FORCE_INLINE ElementType::Enum getVolumeType() const { return ElementType::Enum(reinterpret_cast(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 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(this); const PxU64 value1 = *reinterpret_cast(&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& 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 mPostBroadPhase3; //Cm::DelegateTask 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 mGroups; // NOTE: we stick Bp::FilterGroup::eINVALID in this slot to indicate that the entry is invalid (removed or never inserted.) Ps::Array& mContactDistance; Ps::Array 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 mAddedHandles; Ps::Array mUpdatedHandles; Ps::Array mRemovedHandles; BroadPhase& mBroadPhase; BoundsArray& mBoundsArray; Ps::Array mOutOfBoundsObjects; Ps::Array mOutOfBoundsAggregates; Ps::Array mCreatedOverlaps[ElementType::eCOUNT]; Ps::Array mDestroyedOverlaps[ElementType::eCOUNT]; PxcScratchAllocator* mScratchAllocator; PxBaseTask* mNarrowPhaseUnblockTask; PxU32 mUsedSize; // highest used value + 1 bool mOriginShifted; bool mPersistentStateChanged; PxU32 mNbAggregates; PxU32 mFirstFreeAggregate; Ps::Array mAggregates; // PT: indexed by AggregateHandle Ps::Array mDirtyAggregates; PxU32 mTimestamp; AggPairMap mActorAggregatePairs; AggPairMap mAggregateAggregatePairs; Ps::Array 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 mFreeAggregateGroups; // PT: TODO: remove this useless array #endif Ps::HashSet mCreatedPairs; PxU64 mContextID; Ps::SList mBpThreadContextPool; PX_FORCE_INLINE Aggregate* getAggregateFromHandle(AggregateHandle handle) { PX_ASSERT(handle