// This code contains NVIDIA Confidential Information and is disclosed to you // under a form of NVIDIA software license agreement provided separately to you. // // Notice // NVIDIA Corporation and its licensors retain all intellectual property and // proprietary rights in and to this software and related documentation and // any modifications thereto. Any use, reproduction, disclosure, or // distribution of this software and related documentation without an express // license agreement from NVIDIA Corporation is strictly prohibited. // // ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES // NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO // THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, // MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. // // Information and code furnished is believed to be accurate and reliable. // However, NVIDIA Corporation assumes no responsibility for the consequences of use of such // information or for any infringement of patents or other rights of third parties that may // result from its use. No license is granted by implication or otherwise under any patent // or patent rights of NVIDIA Corporation. Details are subject to change without notice. // This code supersedes and replaces all information previously supplied. // NVIDIA Corporation products are not authorized for use as critical // components in life support devices or systems without express written approval of // NVIDIA Corporation. // // Copyright (c) 2008-2016 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 "SqSceneQueryManager.h" #include "SqAABBPruner.h" #include "SqBucketPruner.h" #include "SqBounds.h" #include "NpBatchQuery.h" #include "PxFiltering.h" #include "NpRigidDynamic.h" #include "NpRigidStatic.h" #include "NpArticulationLink.h" #include "CmTransformUtils.h" #include "PsAllocator.h" #include "PxSceneDesc.h" #include "ScBodyCore.h" #include "SqPruner.h" #include "GuBounds.h" #include "NpShape.h" using namespace physx; using namespace Sq; using namespace Sc; namespace physx { namespace Sq { OffsetTable gOffsetTable; } } PrunerExt::PrunerExt() : mPruner (NULL), mDirtyList (PX_DEBUG_EXP("SQmDirtyList")), mPrunerType (PxPruningStructureType::eLAST), mTimestamp (0xffffffff) { } PrunerExt::~PrunerExt() { PX_DELETE_AND_RESET(mPruner); } void PrunerExt::init(PxPruningStructureType::Enum type, PxU64 contextID) { mPrunerType = type; mTimestamp = 0; Pruner* pruner = NULL; switch(type) { case PxPruningStructureType::eNONE: { pruner = PX_NEW(BucketPruner); break; } case PxPruningStructureType::eDYNAMIC_AABB_TREE: { pruner = PX_NEW(AABBPruner)(true, contextID); break; } case PxPruningStructureType::eSTATIC_AABB_TREE: { pruner = PX_NEW(AABBPruner)(false, contextID); break; } case PxPruningStructureType::eLAST: break; } mPruner = pruner; } void PrunerExt::preallocate(PxU32 nbShapes) { if(nbShapes > mDirtyMap.size()) mDirtyMap.resize(nbShapes); if(mPruner) mPruner->preallocate(nbShapes); } void PrunerExt::flushMemory() { if(!mDirtyList.size()) mDirtyList.reset(); // PT: TODO: flush bitmap here // PT: TODO: flush pruner here? } void PrunerExt::flushShapes(PxU32 index) { const PxU32 numDirtyList = mDirtyList.size(); if(!numDirtyList) return; const PrunerHandle* const prunerHandles = mDirtyList.begin(); const ComputeBoundsFunc func = gComputeBoundsTable[index]; for(PxU32 i=0; igetPayload(handle, bounds); (func)(*bounds, *(reinterpret_cast(pp.data[0])), *(reinterpret_cast(pp.data[1]))); } // PT: batch update happens after the loop instead of once per loop iteration mPruner->updateObjects(prunerHandles, NULL, numDirtyList); mTimestamp += numDirtyList; mDirtyList.clear(); } // PT: TODO: re-inline this void PrunerExt::addToDirtyList(PrunerHandle handle) { Cm::BitMap& dirtyMap = mDirtyMap; if(!dirtyMap.test(handle)) { dirtyMap.set(handle); mDirtyList.pushBack(handle); mTimestamp++; } } // PT: TODO: re-inline this Ps::IntBool PrunerExt::isDirty(PrunerHandle handle) const { return mDirtyMap.test(handle); } // PT: TODO: re-inline this void PrunerExt::removeFromDirtyList(PrunerHandle handle) { Cm::BitMap& dirtyMap = mDirtyMap; if(dirtyMap.test(handle)) { dirtyMap.reset(handle); mDirtyList.findAndReplaceWithLast(handle); } } // PT: TODO: re-inline this void PrunerExt::growDirtyList(PrunerHandle handle) { // pruners must either provide indices in order or reuse existing indices, so this 'if' is enough to ensure we have space for the new handle // PT: TODO: fix this. There is just no need for any of it. The pruning pool itself could support the feature for free, similar to what we do // in MBP. There would be no need for the bitmap or the dirty list array. However doing this through the virtual interface would be clumsy, // adding the cost of virtual calls for very cheap & simple operations. It would be a lot easier to drop it and go back to what we had before. Cm::BitMap& dirtyMap = mDirtyMap; if(dirtyMap.size() <= handle) dirtyMap.resize(PxMax(dirtyMap.size() * 2, 1024)); PX_ASSERT(handleaddObjects(&handle, &b, &pp, 1, hasPrunerStructure); mPrunerExt[index].invalidateTimestamp(); mPrunerExt[index].growDirtyList(handle); return createPrunerData(index, handle); } const PrunerPayload& SceneQueryManager::getPayload(PrunerData data) const { const PxU32 index = getPrunerIndex(data); const PrunerHandle handle = getPrunerHandle(data); return mPrunerExt[index].pruner()->getPayload(handle); } void SceneQueryManager::removePrunerShape(PrunerData data) { const PxU32 index = getPrunerIndex(data); const PrunerHandle handle = getPrunerHandle(data); PX_ASSERT(mPrunerExt[index].pruner()); mPrunerExt[index].removeFromDirtyList(handle); mPrunerExt[index].invalidateTimestamp(); mPrunerExt[index].pruner()->removeObjects(&handle); } void SceneQueryManager::setDynamicTreeRebuildRateHint(PxU32 rebuildRateHint) { mRebuildRateHint = rebuildRateHint; for(PxU32 i=0;i(mPrunerExt[i].pruner())->setRebuildRateHint(rebuildRateHint); } } static PxBounds3 computeWorldAABB(const Scb::Shape& scbShape, const Sc::BodyCore& bodyCore) { const Gu::GeometryUnion& geom = scbShape.getGeometryUnion(); const PxTransform& shape2Actor = scbShape.getShape2Actor(); PX_ALIGN(16, PxTransform) globalPose; PX_ALIGN(16, PxTransform) kinematicTarget; PxU16 sqktFlags = PxRigidBodyFlag::eKINEMATIC | PxRigidBodyFlag::eUSE_KINEMATIC_TARGET_FOR_SCENE_QUERIES; bool useTarget = (PxU16(bodyCore.getFlags()) & sqktFlags) == sqktFlags; const PxTransform& body2World = (useTarget && bodyCore.getKinematicTarget(kinematicTarget)) ? kinematicTarget : bodyCore.getBody2World(); Cm::getDynamicGlobalPoseAligned(body2World, shape2Actor, bodyCore.getBody2Actor(), globalPose); PxBounds3 tmp; inflateBounds(tmp, Gu::computeBounds(geom.getGeometry(), globalPose, false)); return tmp; } void SceneQueryManager::validateSimUpdates() { if (mPrunerExt[1].type() != PxPruningStructureType::eDYNAMIC_AABB_TREE) return; Sc::BodyCore*const* activeBodies = mScene.getActiveBodiesArray(); const PxU32 nbActiveBodies = mScene.getNumActiveBodies(); for (PxU32 i = 0; i < nbActiveBodies; ++i) { const Sc::BodyCore* bCore = activeBodies[i]; if (bCore->isFrozen()) continue; PxRigidBody* pxBody = static_cast(bCore->getPxActor()); PX_ASSERT(pxBody->getConcreteType() == PxConcreteType::eRIGID_DYNAMIC || pxBody->getConcreteType() == PxConcreteType::eARTICULATION_LINK); NpShapeManager& shapeManager = *NpActor::getShapeManager(*pxBody); const PxU32 nbShapes = shapeManager.getNbShapes(); NpShape* const* shape = shapeManager.getShapes(); for (PxU32 j = 0; jgetScbShape(), *bCore); PxBounds3 prunerAABB = static_cast(mPrunerExt[1].pruner())->getAABB(handle); PX_ASSERT((worldAABB.minimum - prunerAABB.minimum).magnitudeSquared() < 0.005f*mScene.getPxScene()->getPhysics().getTolerancesScale().length); PX_ASSERT((worldAABB.maximum - prunerAABB.maximum).magnitudeSquared() < 0.005f*mScene.getPxScene()->getPhysics().getTolerancesScale().length); PX_UNUSED(worldAABB); PX_UNUSED(prunerAABB); } } } } void SceneQueryManager::processSimUpdates() { PX_PROFILE_ZONE("Sim.updatePruningTrees", mScene.getContextId()); { PX_PROFILE_ZONE("SceneQuery.processActiveShapes", mScene.getContextId()); // update all active objects BodyCore*const* activeBodies = mScene.getScScene().getActiveBodiesArray(); PxU32 nbActiveBodies = mScene.getScScene().getNumActiveBodies(); #define NB_BATCHED_OBJECTS 128 PrunerHandle batchedHandles[NB_BATCHED_OBJECTS]; PxU32 nbBatchedObjects = 0; Pruner* pruner = mPrunerExt[PruningIndex::eDYNAMIC].pruner(); while(nbActiveBodies--) { // PT: TODO: don't put frozen objects in "active bodies" array? After all they // are also not included in the 'active transforms' or 'active actors' arrays. BodyCore* currentBody = *activeBodies++; if(currentBody->isFrozen()) continue; PxActorType::Enum type; PxRigidBody* pxBody = static_cast(getPxActorFromBodyCore(currentBody, type)); PX_ASSERT(pxBody->getConcreteType()==PxConcreteType::eRIGID_DYNAMIC || pxBody->getConcreteType()==PxConcreteType::eARTICULATION_LINK); NpShapeManager* shapeManager; if(type==PxActorType::eRIGID_DYNAMIC) { NpRigidDynamic* rigidDynamic = static_cast(pxBody); shapeManager = &rigidDynamic->getShapeManager(); } else { NpArticulationLink* articulationLink = static_cast(pxBody); shapeManager = &articulationLink->getShapeManager(); } const PxU32 nbShapes = shapeManager->getNbShapes(); for(PxU32 i=0; igetPrunerData(i); if(data!=SQ_INVALID_PRUNER_DATA) { // PT: index can't be zero here! PX_ASSERT(getPrunerIndex(data)==PruningIndex::eDYNAMIC); const PrunerHandle handle = getPrunerHandle(data); if(!mPrunerExt[PruningIndex::eDYNAMIC].isDirty(handle)) // PT: if dirty, will be updated in "flushShapes" { batchedHandles[nbBatchedObjects] = handle; PxBounds3* bounds; const PrunerPayload& pp = pruner->getPayload(handle, bounds); computeDynamicWorldAABB(*bounds, *(reinterpret_cast(pp.data[0])), *(reinterpret_cast(pp.data[1]))); nbBatchedObjects++; if(nbBatchedObjects==NB_BATCHED_OBJECTS) { mPrunerExt[PruningIndex::eDYNAMIC].invalidateTimestamp(); pruner->updateObjects(batchedHandles, NULL, nbBatchedObjects); nbBatchedObjects = 0; } } } } } if(nbBatchedObjects) { mPrunerExt[PruningIndex::eDYNAMIC].invalidateTimestamp(); pruner->updateObjects(batchedHandles, NULL, nbBatchedObjects); } } // flush user modified objects flushShapes(); for(PxU32 i=0;i(mPrunerExt[i].pruner())->buildStep(); mPrunerExt[i].pruner()->commit(); } } void SceneQueryManager::afterSync(bool commit) { PX_PROFILE_ZONE("Sim.sceneQueryBuildStep", mScene.getContextId()); // flush user modified objects flushShapes(); for (PxU32 i = 0; i<2; i++) { if (mPrunerExt[i].pruner() && mPrunerExt[i].type() == PxPruningStructureType::eDYNAMIC_AABB_TREE) static_cast(mPrunerExt[i].pruner())->buildStep(); if (commit) mPrunerExt[i].pruner()->commit(); } } void SceneQueryManager::flushShapes() { PX_PROFILE_ZONE("SceneQuery.flushShapes", mScene.getContextId()); // must already have acquired writer lock here for(PxU32 i=0; icommit(); mSceneQueryLock.unlock(); } void SceneQueryManager::forceDynamicTreeRebuild(bool rebuildStaticStructure, bool rebuildDynamicStructure) { PX_PROFILE_ZONE("SceneQuery.forceDynamicTreeRebuild", mScene.getContextId()); const bool rebuild[PruningIndex::eCOUNT] = { rebuildStaticStructure, rebuildDynamicStructure }; Ps::Mutex::ScopedLock lock(mSceneQueryLock); for(PxU32 i=0; i(mPrunerExt[i].pruner())->purge(); static_cast(mPrunerExt[i].pruner())->commit(); } } } void SceneQueryManager::shiftOrigin(const PxVec3& shift) { for(PxU32 i=0; ishiftOrigin(shift); } void DynamicBoundsSync::sync(const PxU32* sqRefs, const PxU32* indices, const PxBounds3* bounds, PxU32 count) { mPruner->updateObjects(sqRefs, indices, bounds, count); if (count) (*mTimestamp)++; }