// 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-2025 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 PXG_CONSTRAINT_PARTITION_H #define PXG_CONSTRAINT_PARTITION_H #define PX_PARTITION_COMPACTION 1 #include "foundation/PxPinnedArray.h" #include "foundation/PxSList.h" #include "foundation/PxUserAllocated.h" #include "foundation/PxUtilities.h" #include "PxgSolverBody.h" #include "PxgSolverConstraintDesc.h" #include "PxsSimpleIslandManager.h" #include "PxgDynamicsConfiguration.h" #include "PxgEdgeType.h" #include "PxgPartitionNode.h" #include "PxsPartitionEdge.h" namespace physx { class PxsContactManagerOutputIterator; class PxgBodySimManager; class PxgJointManager; struct PxsContactManagerOutputCounts; namespace Cm { class FlushPool; } #define SLAB_SIZE 512 // PT: defines controlling a large dense array of 32 pointers per node using up a lot of memory. // // STORE_INDICES_IN_NODE_ENTRIES stores indices instead of PartitionEdge ptrs in NodeEntries. // Half the memory usage compared to initial version but more expensive address computation // and we still decode and use a pointer in the end. // // STORE_EDGE_DATA_IN_NODE_ENTRIES stores the edge data directly instead of indices. // One less indirection but same memory usage as initial version. // // Initial version | Less indirection | Better Mem usage // ---------------------------------|-------------------|--------------------- // Initial version | No | No // STORE_INDICES_IN_NODE_ENTRIES | No | Yes // STORE_EDGE_DATA_IN_NODE_ENTRIES | Yes | No // // Jury is still out regarding which one is best for perf. Meanwhile we use the one with best mem usage. #define STORE_INDICES_IN_NODE_ENTRIES 1 #if STORE_INDICES_IN_NODE_ENTRIES #define STORE_EDGE_DATA_IN_NODE_ENTRIES 0 #if STORE_EDGE_DATA_IN_NODE_ENTRIES struct EdgeData { PxU32 mUniqueIndex; PxU32 mNode0Index; }; typedef EdgeData NodeEntryStorage; typedef EdgeData NodeEntryDecoded; PX_FORCE_INLINE void resetNodeEntryStorage(EdgeData& edge) { edge.mUniqueIndex = IG_INVALID_EDGE; } PX_FORCE_INLINE PxU32 getUniqueId(const EdgeData& edge) { return edge.mUniqueIndex; } PX_FORCE_INLINE PxU32 getNode0Index(const EdgeData& edge) { return edge.mNode0Index; } #else typedef PxU32 NodeEntryStorage; typedef const PartitionEdge* NodeEntryDecoded; PX_FORCE_INLINE void resetNodeEntryStorage(PxU32& edge) { edge = IG_INVALID_EDGE; } PX_FORCE_INLINE PxU32 getUniqueId(const PartitionEdge* edge) { return edge->mUniqueIndex; } PX_FORCE_INLINE PxU32 getNode0Index(const PartitionEdge* edge) { return edge->mNode0.index(); } #endif #else typedef const PartitionEdge* NodeEntryStorage; typedef const PartitionEdge* NodeEntryDecoded; PX_FORCE_INLINE void resetNodeEntryStorage(const PartitionEdge*& edge) { edge = NULL; } PX_FORCE_INLINE PxU32 getUniqueId(const PartitionEdge* edge) { return edge->mUniqueIndex; } PX_FORCE_INLINE PxU32 getNode0Index(const PartitionEdge* edge) { return edge->mNode0.index(); } #endif typedef Cm::BlockArray PartitionIndices; //typedef PxArray PartitionIndices; struct PartitionEdgeSlab { PartitionEdge mEdges[SLAB_SIZE]; //! The slabs }; class PartitionEdgeManager { PartitionEdge* mFreeEdges; PxArray mPartitionEdgeSlabs; PxArray mMemory; PxU32 mEdgeCount; PX_NOINLINE void allocateSlab(); public: PartitionEdgeManager(); ~PartitionEdgeManager(); PX_FORCE_INLINE PartitionEdge* getEdge(IG::EdgeIndex index); PX_FORCE_INLINE void putEdge(PartitionEdge* edge); PX_FORCE_INLINE const PartitionEdge* getPartitionEdge(PxU32 uniqueId) const { return &mPartitionEdgeSlabs[uniqueId / SLAB_SIZE]->mEdges[uniqueId & (SLAB_SIZE - 1)]; } PX_FORCE_INLINE PxU32 getEdgeCount() const { return mEdgeCount; } }; struct Partition { PartitionIndices mPartitionIndices[PxgEdgeType::eEDGE_TYPE_COUNT]; Partition() {} //Adds an edge to the partition bool addToPartition(PxU32 uniqueIndex, PartitionIndexData& indexData) { PartitionIndices& indices = mPartitionIndices[indexData.mCType]; indexData.mPartitionEntryIndex = indices.size(); indices.pushBack(uniqueIndex); return true; } void removeFromPartition(PxU32 uniqueIndex, PxPinnedArray& iterator) { const PartitionIndexData& indexData = iterator[uniqueIndex]; PartitionIndices& indices = mPartitionIndices[indexData.mCType]; // AD: defensive coding for OM-90842. If this assert hits, you maybe hit the same issue PxU32 size = indices.size(); if (size == 0) { PxGetFoundation().error(PxErrorCode::eINTERNAL_ERROR, PX_FL, "PxgConstraintPartition: attempting to remove an edge from an empty partition. Skipping."); PX_ASSERT(false); return; } size--; const PxU32 uniqueIdx = indices[size]; const PxU32 partitionEntryIndex = indexData.mPartitionEntryIndex; iterator[uniqueIdx].mPartitionEntryIndex = partitionEntryIndex; indices[partitionEntryIndex] = uniqueIdx; indices.forceSize_Unsafe(size); } }; struct NodeEntries { NodeEntryStorage mEdges[PXG_BATCH_SIZE]; }; struct PartitionSlab : public PxUserAllocated { Partition mPartitions[PXG_BATCH_SIZE]; //! Each slab has 32 partitions PxArray mNodeBitmap; //! Each slab 1 integer per-node, recording the presence of the node in any of the slabs PxArray mNodeEntries; PartitionSlab() {} }; struct PartitionArray : public PxUserAllocated { PxArray mPartitions; PxU32 mAccumulatedPartitionCount; PxU32 mAccumulatedArtiPartitionCount; PartitionArray() : mPartitions(1024), mAccumulatedPartitionCount(0), mAccumulatedArtiPartitionCount(0) { } void clear() { mPartitions.forceSize_Unsafe(0); mAccumulatedPartitionCount = 0; mAccumulatedArtiPartitionCount = 0; } }; class PxgCombinedPartitionSlab : public PxUserAllocated { public: PxU32 mNbPartitions; const PxU32 mUserNbMaxPartitions; PxU32 mNbMaxPartitions; PartitionArray mPartitionArray[32]; PxgCombinedPartitionSlab(PxU32 maxNumPartitions) : mUserNbMaxPartitions(maxNumPartitions), mNbMaxPartitions(maxNumPartitions) { } ~PxgCombinedPartitionSlab() { } void clear() { mNbPartitions = 0; for (PxU32 i = 0; i< 32; ++i) mPartitionArray[i].clear(); } }; class PxgIncrementalPartition { PX_NOCOPY(PxgIncrementalPartition) public: // PT: TODO: revisit after the dust settles PxArray mPartitionSlabs; PartitionEdgeManager mEdgeManager; PxU32 mNodeCount; PxU32 mNbContactBatches; PxU32 mNbConstraintBatches; PxU32 mNbArtiContactBatches; PxU32 mNbArtiConstraintBatches; PxU32 mNbPartitions; PxU32 mTotalContacts; PxU32 mTotalConstraints; PxU32 mTotalArticulationContacts; PxU32 mTotalArticulationConstraints; PxU32 mMaxSlabCount; PxU32 mNbForceThresholds; #if PX_ENABLE_ASSERTS PxArray mAccumulatedPartitionCount; // for contact PxArray mAccumulatedConstraintCount; // for joint PxArray mAccumulatedArtiPartitionCount; //for contact PxArray mAccumulatedArtiConstraintCount; // for constraint #endif PxBitMap mIsDirtyNode; PxArray mNpIndexArray; PxPinnedArray mPartitionIndexArray; PxPinnedArray mPartitionNodeArray; PxPinnedArray mSolverConstants; PxInt32ArrayPinned mNodeInteractionCountArray; PxInt32ArrayPinned mDestroyedContactEdgeIndices; PxInt32ArrayPinned mStartSlabPerPartition; PxInt32ArrayPinned mArticStartSlabPerPartition; PxInt32ArrayPinned mNbJointsPerPartition; PxInt32ArrayPinned mNbArtiJointsPerPartition; PxArray mJointStartIndices; PxArray mContactStartIndices; PxArray mArtiContactStartIndices; PxArray mArtiJointStartIndices; PxgCombinedPartitionSlab mCSlab; const PxU64 mContextID; public: PxgIncrementalPartition(const PxVirtualAllocator& allocator, PxU32 maxNumPartitions, PxU64 contextID); ~PxgIncrementalPartition(); void processLostFoundPatches( Cm::FlushPool& flushPool, PxBaseTask* continuation, IG::IslandSim& islandSim, PxgBodySimManager& bodySimManager, PxgJointManager& jointManager, PxsContactManager** lostFoundPatchManagers, PxU32 nbLostFoundPatchManagers, const PxsContactManagerOutputCounts* lostFoundPairOutputs); void updateIncrementalIslands( IG::IslandSim& islandSim, const IG::AuxCpuData& islandManagerData, Cm::FlushPool* flushPool, PxBaseTask* continuation, PxsContactManagerOutputIterator& iterator, PxgBodySimManager& bodySimManager, PxgJointManager& jointManager); // PT: internal reference versions, exposed for UTs void processLostPatches_Reference( IG::IslandSim& islandSim, PxgBodySimManager& bodySimManager, PxgJointManager& jointManager, PxsContactManager** lostFoundPatchManagers, PxU32 nbLostFoundPatchManagers, const PxsContactManagerOutputCounts* lostFoundPairOutputs); void processFoundPatches_Reference( IG::IslandSim& islandSim, PxgBodySimManager& bodySimManager, PxsContactManager** lostFoundPatchManagers, PxU32 nbLostFoundPatchManagers, const PxsContactManagerOutputCounts* lostFoundPairOutputs); void updateIncrementalIslands_Reference(IG::IslandSim& islandSim, const IG::AuxCpuData& islandManagerData, PxsContactManagerOutputIterator& iterator, PxgBodySimManager& bodySimManager, PxgJointManager& jointManager); // PT: edge data PX_FORCE_INLINE const PxPinnedArray& getSolverConstants() const { return mSolverConstants; } // PT: TODO: what's the difference between mNbPartitions and mCSlab.mNbPartitions ? PX_FORCE_INLINE PxU32 getNbPartitions() const { return mNbPartitions; } PX_FORCE_INLINE PxU32 getCombinedSlabMaxNbPartitions() const { return mCSlab.mNbMaxPartitions; } PX_FORCE_INLINE PxU32 getCombinedSlabNbPartitions() const { return mCSlab.mNbPartitions; } PX_FORCE_INLINE const PxPinnedArray& getPartitionIndexArray() const { return mPartitionIndexArray; } PX_FORCE_INLINE const PxPinnedArray& getPartitionNodeArray() const { return mPartitionNodeArray; } PX_FORCE_INLINE const PxInt32ArrayPinned& getStartSlabPerPartition() const { return mStartSlabPerPartition; } PX_FORCE_INLINE const PxInt32ArrayPinned& getArticStartSlabPerPartition() const { return mArticStartSlabPerPartition; } PX_FORCE_INLINE const PxInt32ArrayPinned& getNbJointsPerPartition() const { return mNbJointsPerPartition; } PX_FORCE_INLINE const PxInt32ArrayPinned& getNbArticJointsPerPartition() const { return mNbArtiJointsPerPartition; } PX_FORCE_INLINE const PxInt32ArrayPinned& getNodeInteractionCountArray() const { return mNodeInteractionCountArray; } PX_FORCE_INLINE const PxInt32ArrayPinned& getDestroyedContactEdgeIndices() const { return mDestroyedContactEdgeIndices; } PX_FORCE_INLINE const PxArray& getNpIndexArray() const { return mNpIndexArray; } PX_FORCE_INLINE const PxArray& getPartitionSlabs() const { return mPartitionSlabs; } PX_FORCE_INLINE const PxArray& getContactStartIndices() const { return mContactStartIndices; } PX_FORCE_INLINE const PxArray& getJointStartIndices() const { return mJointStartIndices; } PX_FORCE_INLINE const PxArray& getArtiContactStartIndices() const { return mArtiContactStartIndices; } PX_FORCE_INLINE const PxArray& getArtiJointStartIndices() const { return mArtiJointStartIndices; } PX_FORCE_INLINE PxU32 getCSlabAccumulatedPartitionCount(PxU32 index) const { PX_ASSERT(index<32); return mCSlab.mPartitionArray[index].mAccumulatedPartitionCount; } PX_FORCE_INLINE PxU32 getCSlabAccumulatedArtiPartitionCount(PxU32 index) const { PX_ASSERT(index<32); return mCSlab.mPartitionArray[index].mAccumulatedArtiPartitionCount; } #if PX_ENABLE_ASSERTS PX_FORCE_INLINE const PxArray& getAccumulatedPartitionCount() const { return mAccumulatedPartitionCount; } PX_FORCE_INLINE const PxArray& getAccumulatedConstraintCount() const { return mAccumulatedConstraintCount; } PX_FORCE_INLINE const PxArray& getAccumulatedArtiPartitionCount() const { return mAccumulatedArtiPartitionCount; } PX_FORCE_INLINE const PxArray& getAccumulatedArtiConstraintCount() const { return mAccumulatedArtiConstraintCount; } #endif PX_FORCE_INLINE PxU32 getNbContactBatches() const { return mNbContactBatches; } PX_FORCE_INLINE PxU32 getNbConstraintBatches() const { return mNbConstraintBatches; } PX_FORCE_INLINE PxU32 getNbArtiContactBatches() const { return mNbArtiContactBatches; } PX_FORCE_INLINE PxU32 getNbArtiConstraintBatches() const { return mNbArtiConstraintBatches; } PX_FORCE_INLINE PxU32 getTotalContacts() const { return mTotalContacts; } PX_FORCE_INLINE PxU32 getTotalConstraints() const { return mTotalConstraints; } PX_FORCE_INLINE PxU32 getTotalArticulationContacts() const { return mTotalArticulationContacts; } PX_FORCE_INLINE PxU32 getTotalArticulationConstraints() const { return mTotalArticulationConstraints; } PX_FORCE_INLINE bool hasForceThresholds() const { return mNbForceThresholds!=0; } //private: // PT: TODO: revisit after the dust settles #if USE_SPLIT_SECOND_PASS_ISLAND_GEN PxBitMap mActiveCMBitmapCopy; #endif void reserveNodes(PxU32 nodeCount); void getPreviousAndNextReferencesInSlab(NodeEntryDecoded& prev, NodeEntryDecoded& next, PxU32 index, PxU32 uniqueId, const PartitionSlab* slab, PxU32 slabMask) const; PartitionEdge* addEdge_Stage1(const IG::IslandSim& islandSim, IG::EdgeIndex edgeIndex, PxU32 patchIndex, PxU32 npIndex, PxNodeIndex node1, PxNodeIndex node2); void addEdge_Stage2(IG::GPUExternalData& islandSimGpuData, IG::EdgeIndex edgeIndex, PartitionEdge* partitionEdge, bool specialHandled, bool doPart1, bool doPart2); bool addJointManager(const PartitionEdge* edge, PxgBodySimManager& bodySimManager); bool addContactManager(PartitionEdge* edge, const PxcNpWorkUnit& unit, PxgBodySimManager& bodySimManager); void removeEdge(PartitionEdge* edge, IG::GPUExternalData& islandSimGpuData, PxgBodySimManager& manager); PX_FORCE_INLINE void removeAllEdges(IG::GPUExternalData& islandSimGpuData, PxgBodySimManager& bodySimManager, PartitionEdge* partitionEdge); void destroyEdges(const IG::CPUExternalData& islandSimCpuData, IG::GPUExternalData& islandSimGpuData, PxgBodySimManager& bodySimManager, PxgJointManager& jointManager, bool clearDestroyedEdges, bool recordDestroyedEdges); void addEdgeInternal(const PartitionEdge* PX_RESTRICT partitionEdge, PartitionSlab* PX_RESTRICT slab, PxU16 id, PxU16 baseId); void removeEdgeInternal(PartitionSlab* PX_RESTRICT slab, const PartitionEdge* PX_RESTRICT edge, PxU32 id); void doCompaction(); #if PX_PARTITION_COMPACTION void pullForwardConstraints(PxU32 nodeIndex); #endif public: // PT: TODO: revisit after the dust settles void updateIncrementalIslands_Part1( IG::IslandSim& islandSim, const IG::AuxCpuData& islandManagerData, PxsContactManagerOutputIterator& iterator, PxgBodySimManager& bodySimManager, PxgJointManager& jointManager); void updateIncrementalIslands_Part2( IG::IslandSim& islandSim, const IG::AuxCpuData& islandManagerData, PxsContactManagerOutputIterator& iterator, PxgBodySimManager& bodySimManager); enum SpecialCase { SPECIAL_CASE_NONE, SPECIAL_CASE_STATIC_RB, SPECIAL_CASE_ARTI_SELF, SPECIAL_CASE_STATIC_ARTI0, SPECIAL_CASE_STATIC_ARTI1 }; struct Part2WorkItem { PxU32 mEdgeID; PxU16 mPatchIndex; PxU16 mSpecialCase; PartitionEdge* mPartitionEdge; }; PxArray mPart2WorkItems; PxArray mPart2EdgeCases; // PT: indices into mPart2WorkItems void updateIncrementalIslands_Part2_0(IG::IslandSim& islandSim, const IG::AuxCpuData& islandManagerData, PxsContactManagerOutputIterator& iterator); void updateIncrementalIslands_Part2_1(PxU32 startIndex, PxU32 nbToProcess, IG::IslandSim& islandSim, const IG::AuxCpuData& islandManagerData); void updateIncrementalIslands_Part2_2(IG::IslandSim& islandSim, PxgBodySimManager& bodySimManager, bool dopart1, bool dopart2, bool dopart3); void updateIncrementalIslands_Part2_2_ProcessEdgeCases(IG::IslandSim& islandSim); void updateIncrementalIslands_Part3(IG::IslandSim& islandSim, PxgJointManager& jointManager); void processLostPatchesMT( IG::IslandSim& islandSim, Cm::FlushPool& flushPool, PxBaseTask* continuation, PxsContactManager** lostFoundPatchManagers, PxU32 nbLostFoundPatchManagers, const PxsContactManagerOutputCounts* lostFoundPairOutputs, PxgBodySimManager& bodySimManager, PxgJointManager& jointManager); struct PartitionEdgeBatch : public PxUserAllocated { PxArray mEdges; }; PxArray mBatches; }; } #endif