// 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. #include "common/PxProfileZone.h" #include "PxvConfig.h" #include "PxcContactCache.h" #include "PxsRigidBody.h" #include "PxsContactManager.h" #include "PxsContext.h" #include "PxPhysXConfig.h" #include "foundation/PxBitMap.h" #include "CmFlushPool.h" #include "PxsMaterialManager.h" #include "PxSceneDesc.h" #include "PxsCCD.h" #include "PxvGeometry.h" #include "PxvManager.h" #include "PxsSimpleIslandManager.h" #if PX_SUPPORT_GPU_PHYSX #include "PxPhysXGpu.h" #endif #include "PxcNpContactPrepShared.h" #include "PxcNpCache.h" using namespace physx; PxsContext::PxsContext(const PxSceneDesc& desc, PxTaskManager* taskManager, Cm::FlushPool& taskPool, PxCudaContextManager* cudaContextManager, PxU32 poolSlabSize, PxU64 contextID) : mNpThreadContextPool (this), mContactManagerPool ("mContactManagerPool", poolSlabSize), mManifoldPool ("mManifoldPool", poolSlabSize), mSphereManifoldPool ("mSphereManifoldPool", poolSlabSize), mContactModifyCallback (NULL), mNpImplementationContext (NULL), mNpFallbackImplementationContext(NULL), mTaskManager (taskManager), mTaskPool (taskPool), mCudaContextManager (cudaContextManager), mPCM (desc.flags & PxSceneFlag::eENABLE_PCM), mContactCache (false), mCreateAveragePoint (desc.flags & PxSceneFlag::eENABLE_AVERAGE_POINT), mContextID (contextID) { clearManagerTouchEvents(); mVisualizationCullingBox.setEmpty(); PxMemZero(mVisualizationParams, sizeof(PxReal) * PxVisualizationParameter::eNUM_VALUES); mNpMemBlockPool.init(desc.nbContactDataBlocks, desc.maxNbContactDataBlocks); } PxsContext::~PxsContext() { PX_DELETE(mTransformCache); mContactManagerPool.destroy(); //manually destroy the contact manager pool, otherwise pool deletion order is random and we can get into trouble with references into other pools needed during destruction. } // =========================== Create methods namespace physx { const bool gEnablePCMCaching[][PxGeometryType::eGEOMETRY_COUNT] = { //eSPHERE, { false, //eSPHERE false, //ePLANE false, //eCAPSULE false, //eBOX false, //eCONVEX true, //eCONVEXMESH false, //ePARTICLESYSTEM true, //eSOFTBODY, true, //eTRIANGLEMESH true, //eHEIGHTFIELD true, //eCUSTOM }, //ePLANE { false, //eSPHERE false, //ePLANE true, //eCAPSULE true, //eBOX false, //eCONVEX true, //eCONVEXMESH false, //ePARTICLESYSTEM true, //eSOFTBODY, false, //eTRIANGLEMESH false, //eHEIGHTFIELD true, //eCUSTOM }, //eCAPSULE, { false, //eSPHERE true, //ePLANE false, //eCAPSULE true, //eBOX false, //eCONVEX true, //eCONVEXMESH false, //ePARTICLESYSTEM true, //eSOFTBODY, true, //eTRIANGLEMESH true, //eHEIGHTFIELD true, //eCUSTOM }, //eBOX, { false, //eSPHERE true, //ePLANE true, //eCAPSULE true, //eBOX false, //eCONVEX true, //eCONVEXMESH false, //ePARTICLESYSTEM true, //eSOFTBODY, true, //eTRIANGLEMESH true, //eHEIGHTFIELD true, //eCUSTOM }, //eCONVEX, { false, //eSPHERE false, //ePLANE false, //eCAPSULE false, //eBOX false, //eCONVEX false, //eCONVEXMESH false, //ePARTICLESYSTEM false, //eSOFTBODY, false, //eTRIANGLEMESH false, //eHEIGHTFIELD false, //eCUSTOM }, //eCONVEXMESH, { true, //eSPHERE true, //ePLANE true, //eCAPSULE true, //eBOX false, //eCONVEX true, //eCONVEXMESH false, //ePARTICLESYSTEM true, //eSOFTBODY, true, //eTRIANGLEMESH true, //eHEIGHTFIELD true, //eCUSTOM }, //ePARTICLESYSTEM { false, //eSPHERE false, //ePLANE false, //eCAPSULE false, //eBOX false, //eCONVEX false, //eCONVEXMESH false, //ePARTICLESYSTEM false, //eSOFTBODY, false, //eTRIANGLEMESH false, //eHEIGHTFIELD false, //eCUSTOM }, //eSOFTBODY { false, //eSPHERE false, //ePLANE false, //eCAPSULE false, //eBOX false, //eCONVEX false, //eCONVEXMESH false, //ePARTICLESYSTEM false, //eSOFTBODY, false, //eTRIANGLEMESH false, //eHEIGHTFIELD false, //eCUSTOM }, //eTRIANGLEMESH, { true, //eSPHERE false, //ePLANE true, //eCAPSULE true, //eBOX false, //eCONVEX true, //eCONVEXMESH false, //ePARTICLESYSTEM true, //eSOFTBODY, false, //eTRIANGLEMESH false, //eHEIGHTFIELD true, //eCUSTOM }, //eHEIGHTFIELD, { true, //eSPHERE false, //ePLANE true, //eCAPSULE true, //eBOX false, //eCONVEX true, //eCONVEXMESH false, //ePARTICLESYSTEM true, //eSOFTBODY, false, //eTRIANGLEMESH false, //eHEIGHTFIELD true, //eCUSTOM }, //eCUSTOM, { true, //eSPHERE true, //ePLANE true, //eCAPSULE true, //eBOX false, //eCONVEX true, //eCONVEXMESH false, //ePARTICLESYSTEM false, //eSOFTBODY, true, //eTRIANGLEMESH true, //eHEIGHTFIELD true, //eCUSTOM } }; PX_COMPILE_TIME_ASSERT(sizeof(gEnablePCMCaching) / sizeof(gEnablePCMCaching[0]) == PxGeometryType::eGEOMETRY_COUNT); } void PxsContext::createTransformCache(PxVirtualAllocatorCallback& allocatorCallback) { mTransformCache = PX_NEW(PxsTransformCache)(allocatorCallback); } PxsContactManager* PxsContext::createContactManager(PxsContactManager* contactManager, bool useCCD) { PxsContactManager* cm = contactManager? contactManager : mContactManagerPool.get(); if(cm) { cm->getWorkUnit().clearCachedState(); if(!contactManager) setActiveContactManager(cm, useCCD); } else { PX_WARN_ONCE("Reached limit of contact pairs."); } return cm; } void PxsContext::createCache(Gu::Cache& cache, PxGeometryType::Enum geomType0, PxGeometryType::Enum geomType1) { if(mPCM) { if(gEnablePCMCaching[geomType0][geomType1]) { if(geomType0 <= PxGeometryType::eCONVEXMESH && geomType1 <= PxGeometryType::eCONVEXMESH) { if(geomType0 == PxGeometryType::eSPHERE || geomType1 == PxGeometryType::eSPHERE) { Gu::PersistentContactManifold* manifold = mSphereManifoldPool.allocate(); PX_PLACEMENT_NEW(manifold, Gu::SpherePersistentContactManifold()); cache.setManifold(manifold); } else { Gu::PersistentContactManifold* manifold = mManifoldPool.allocate(); PX_PLACEMENT_NEW(manifold, Gu::LargePersistentContactManifold()); cache.setManifold(manifold); } cache.getManifold().clearManifold(); } else { //ML: raised 1 to indicate the manifold is multiManifold which is for contact gen in mesh/height field //cache.manifold = 1; cache.setMultiManifold(NULL); } } else { //cache.manifold = 0; cache.mCachedData = NULL; cache.mManifoldFlags = 0; } } } void PxsContext::destroyContactManager(PxsContactManager* cm) { const PxU32 idx = cm->getIndex(); if(cm->getCCD()) mActiveContactManagersWithCCD.growAndReset(idx); //mActiveContactManager.growAndReset(idx); mContactManagerTouchEvent.growAndReset(idx); mContactManagerPool.put(cm); } void PxsContext::destroyCache(Gu::Cache& cache) { if(cache.isManifold()) { if(!cache.isMultiManifold()) { Gu::PersistentContactManifold& manifold = cache.getManifold(); if(manifold.mCapacity == GU_SPHERE_MANIFOLD_CACHE_SIZE) mSphereManifoldPool.deallocate(static_cast(&manifold)); else mManifoldPool.deallocate(static_cast(&manifold)); } cache.mCachedData = NULL; cache.mManifoldFlags = 0; } } void PxsContext::setScratchBlock(void* addr, PxU32 size) { mScratchAllocator.setBlock(addr, size); } void PxsContext::shiftOrigin(const PxVec3& shift) { // transform cache mTransformCache->shiftTransforms(-shift); #if 0 if (getContactCacheFlag()) { //Iterate all active contact managers PxBitMap::Iterator it(mActiveContactManager); PxU32 index = it.getNext(); while(index != PxBitMap::Iterator::DONE) { PxsContactManager* cm = mContactManagerPool.findByIndexFast(index); PxcNpWorkUnit& npwUnit = cm->getWorkUnit(); // contact cache if(!npwUnit.pairCache.isManifold()) { PxU8* contactCachePtr = npwUnit.pairCache.mCachedData; if (contactCachePtr) { PxcLocalContactsCache* lcc; PxU8* contacts = PxcNpCacheRead(npwUnit.pairCache, lcc); #if PX_DEBUG PxcLocalContactsCache testCache; PxU32 testBytes; const PxU8* testPtr = PxcNpCacheRead2(npwUnit.pairCache, testCache, testBytes); #endif lcc->mTransform0.p -= shift; lcc->mTransform1.p -= shift; const PxU32 nbContacts = lcc->mNbCachedContacts; const bool sameNormal = lcc->mSameNormal; const bool useFaceIndices = lcc->mUseFaceIndices; for(PxU32 i=0; i < nbContacts; i++) { if (i != nbContacts-1) PxPrefetchLine(contacts, 128); if(!i || !sameNormal) contacts += sizeof(PxVec3); PxVec3* cachedPoint = reinterpret_cast(contacts); *cachedPoint -= shift; contacts += sizeof(PxVec3); contacts += sizeof(PxReal); if(useFaceIndices) contacts += 2 * sizeof(PxU32); } #if PX_DEBUG PX_ASSERT(contacts == (testPtr + testBytes)); #endif } } index = it.getNext(); } } #endif // adjust visualization culling box if(!mVisualizationCullingBox.isEmpty()) { mVisualizationCullingBox.minimum -= shift; mVisualizationCullingBox.maximum -= shift; } } void PxsContext::swapStreams() { mNpMemBlockPool.swapNpCacheStreams(); } void PxsContext::mergeCMDiscreteUpdateResults(PxBaseTask* /*continuation*/) { PX_PROFILE_ZONE("Sim.narrowPhaseMerge", mContextID); mNpImplementationContext->appendContactManagers(); //Note: the iterator extracts all the items and returns them to the cache on destruction(for thread safety). PxcThreadCoherentCacheIterator threadContextIt(mNpThreadContextPool); for(PxcNpThreadContext* threadContext = threadContextIt.getNext(); threadContext; threadContext = threadContextIt.getNext()) { mCMTouchEventCount[PXS_LOST_TOUCH_COUNT] += threadContext->getLocalLostTouchCount(); mCMTouchEventCount[PXS_NEW_TOUCH_COUNT] += threadContext->getLocalNewTouchCount(); #if PX_ENABLE_SIM_STATS for(PxU32 i=0;imDiscreteContactPairs[i][j]); #endif for(PxU32 j=i; jmDiscreteContactPairs[i][j]; const PxU32 nbModified = threadContext->mModifiedContactPairs[i][j]; mSimStats.mNbDiscreteContactPairs[i][j] += nb; mSimStats.mNbModifiedContactPairs[i][j] += nbModified; mSimStats.mNbDiscreteContactPairsTotal += nb; } } mSimStats.mNbDiscreteContactPairsWithCacheHits += threadContext->mNbDiscreteContactPairsWithCacheHits; mSimStats.mNbDiscreteContactPairsWithContacts += threadContext->mNbDiscreteContactPairsWithContacts; mSimStats.mTotalCompressedContactSize += threadContext->mCompressedCacheSize; //KS - this data is not available yet //mSimStats.mTotalConstraintSize += threadContext->mConstraintSize; threadContext->clearStats(); #else PX_CATCH_UNDEFINED_ENABLE_SIM_STATS #endif mContactManagerTouchEvent.combineInPlace(threadContext->getLocalChangeTouch()); //mContactManagerPatchChangeEvent.combineInPlace(threadContext->getLocalPatchChangeMap()); mMaxPatches = PxMax(mMaxPatches, threadContext->mMaxPatches); threadContext->mMaxPatches = 0; } } void PxsContext::updateContactManager(PxReal dt, bool hasContactDistanceChanged, PxBaseTask* continuation, PxBaseTask* firstPassContinuation, Cm::FanoutTask* updateBoundAndShapeTask) { PX_ASSERT(mNpImplementationContext); return mNpImplementationContext->updateContactManager(dt, hasContactDistanceChanged, continuation, firstPassContinuation, updateBoundAndShapeTask); } void PxsContext::secondPassUpdateContactManager(PxReal dt, PxBaseTask* continuation) { PX_ASSERT(mNpImplementationContext); mNpImplementationContext->secondPassUpdateContactManager(dt, continuation); } void PxsContext::fetchUpdateContactManager() { PX_ASSERT(mNpImplementationContext); mNpImplementationContext->fetchUpdateContactManager(); mergeCMDiscreteUpdateResults(NULL); } void PxsContext::resetThreadContexts() { //Note: the iterator extracts all the items and returns them to the cache on destruction(for thread safety). PxcThreadCoherentCacheIterator threadContextIt(mNpThreadContextPool); PxcNpThreadContext* threadContext = threadContextIt.getNext(); while(threadContext != NULL) { threadContext->reset(mContactManagerTouchEvent.size()); threadContext = threadContextIt.getNext(); } } bool PxsContext::getManagerTouchEventCount(PxU32* newTouch, PxU32* lostTouch, PxU32* ccdTouch) const { if(newTouch) *newTouch = mCMTouchEventCount[PXS_NEW_TOUCH_COUNT]; if(lostTouch) *lostTouch = mCMTouchEventCount[PXS_LOST_TOUCH_COUNT]; if(ccdTouch) *ccdTouch = mCMTouchEventCount[PXS_CCD_RETOUCH_COUNT]; return true; } void PxsContext::fillManagerTouchEvents(PxvContactManagerTouchEvent* newTouch, PxU32& newTouchCount, PxvContactManagerTouchEvent* lostTouch, PxU32& lostTouchCount, PxvContactManagerTouchEvent* ccdTouch, PxU32& ccdTouchCount) { PX_PROFILE_ZONE("PxsContext::fillManagerTouchEvents", mContextID); const PxvContactManagerTouchEvent* newTouchStart = newTouch; const PxvContactManagerTouchEvent* lostTouchStart = lostTouch; const PxvContactManagerTouchEvent* ccdTouchStart = ccdTouch; const PxvContactManagerTouchEvent* newTouchEnd = newTouch + newTouchCount; const PxvContactManagerTouchEvent* lostTouchEnd = lostTouch + lostTouchCount; const PxvContactManagerTouchEvent* ccdTouchEnd = ccdTouch + ccdTouchCount; PX_UNUSED(newTouchEnd); PX_UNUSED(lostTouchEnd); PX_UNUSED(ccdTouchEnd); const PxU32* bits = mContactManagerTouchEvent.getWords(); if(bits) { // PT: ### bitmap iterator pattern const PxU32 lastSetBit = mContactManagerTouchEvent.findLast(); for(PxU32 w = 0; w <= lastSetBit >> 5; ++w) { for(PxU32 b = bits[w]; b; b &= b-1) { const PxU32 index = PxU32(w<<5|PxLowestSetBit(b)); PxsContactManager* cm = mContactManagerPool.findByIndexFast(index); if(cm->getTouchStatus()) { if(!cm->getHasCCDRetouch()) { PX_ASSERT(newTouch < newTouchEnd); newTouch->setCMTouchEventUserData(cm->getShapeInteraction()); newTouch++; } else { PX_ASSERT(ccdTouch); PX_ASSERT(ccdTouch < ccdTouchEnd); ccdTouch->setCMTouchEventUserData(cm->getShapeInteraction()); cm->clearCCDRetouch(); ccdTouch++; } } else { PX_ASSERT(lostTouch < lostTouchEnd); lostTouch->setCMTouchEventUserData(cm->getShapeInteraction()); lostTouch++; } } } } newTouchCount = PxU32(newTouch - newTouchStart); lostTouchCount = PxU32(lostTouch - lostTouchStart); ccdTouchCount = PxU32(ccdTouch - ccdTouchStart); } void PxsContext::beginUpdate() { #if PX_ENABLE_SIM_STATS mSimStats.clearAll(); #else PX_CATCH_UNDEFINED_ENABLE_SIM_STATS #endif }