Files
XCEngine/engine/third_party/physx/source/physxextensions/src/ExtSqManager.cpp

579 lines
16 KiB
C++

// 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 "ExtSqManager.h"
#define SQ_DEBUG_VIZ_STATIC_COLOR PxU32(PxDebugColor::eARGB_BLUE)
#define SQ_DEBUG_VIZ_DYNAMIC_COLOR PxU32(PxDebugColor::eARGB_RED)
#define SQ_DEBUG_VIZ_STATIC_COLOR2 PxU32(PxDebugColor::eARGB_DARKBLUE)
#define SQ_DEBUG_VIZ_DYNAMIC_COLOR2 PxU32(PxDebugColor::eARGB_DARKRED)
#define SQ_DEBUG_VIZ_COMPOUND_COLOR PxU32(PxDebugColor::eARGB_MAGENTA)
#include "GuBounds.h"
using namespace physx;
using namespace Sq;
using namespace Gu;
///////////////////////////////////////////////////////////////////////////////
#include "SqFactory.h"
#include "common/PxProfileZone.h"
#include "common/PxRenderBuffer.h"
#include "GuBVH.h"
#include "foundation/PxAlloca.h"
// PT: this is a customized version of physx::Sq::PrunerManager that supports more than 2 hardcoded pruners.
// It might not be possible to support the whole PxSceneQuerySystem API with an arbitrary number of pruners.
ExtPrunerManager::ExtPrunerManager(PxU64 contextID, float inflation, const Adapter& adapter, bool usesTreeOfPruners) :
mAdapter (adapter),
mTreeOfPruners (NULL),
mContextID (contextID),
mStaticTimestamp (0),
mRebuildRateHint (100),
mInflation (inflation),
mPrunerNeedsUpdating (false),
mTimestampNeedsUpdating (false),
mUsesTreeOfPruners (usesTreeOfPruners)
{
mCompoundPrunerExt.mPruner = createCompoundPruner(contextID);
}
ExtPrunerManager::~ExtPrunerManager()
{
PX_DELETE(mTreeOfPruners);
const PxU32 nb = mPrunerExt.size();
for(PxU32 i=0;i<nb;i++)
{
PrunerExt* pe = mPrunerExt[i];
PX_DELETE(pe);
}
}
PxU32 ExtPrunerManager::addPruner(Pruner* pruner, PxU32 preallocated)
{
const PxU32 index = mPrunerExt.size();
PrunerExt* pe = PX_NEW(PrunerExt);
pe->init(pruner);
if(preallocated)
pe->preallocate(preallocated);
mPrunerExt.pushBack(pe);
return index;
}
void ExtPrunerManager::preallocate(PxU32 prunerIndex, PxU32 nbShapes)
{
const bool preallocateCompoundPruner = prunerIndex==0xffffffff;
if(preallocateCompoundPruner)
{
mCompoundPrunerExt.preallocate(nbShapes);
}
else
{
if(prunerIndex>=mPrunerExt.size())
{
PxGetFoundation().error(PxErrorCode::eINVALID_PARAMETER, PX_FL, "Invalid pruner index");
return;
}
mPrunerExt[prunerIndex]->preallocate(nbShapes);
}
}
void ExtPrunerManager::flushMemory()
{
const PxU32 nb = mPrunerExt.size();
for(PxU32 i=0;i<nb;i++)
{
PrunerExt* pe = mPrunerExt[i];
pe->flushMemory();
}
mCompoundPrunerExt.flushMemory();
}
PrunerHandle ExtPrunerManager::addPrunerShape(const PrunerPayload& payload, PxU32 prunerIndex, bool dynamic, PrunerCompoundId compoundId, const PxBounds3& bounds, const PxTransform& transform, bool hasPruningStructure)
{
if(compoundId==INVALID_COMPOUND_ID && prunerIndex>=mPrunerExt.size())
{
PxGetFoundation().error(PxErrorCode::eINVALID_PARAMETER, PX_FL, "Invalid pruner index");
return INVALID_PRUNERHANDLE;
}
mPrunerNeedsUpdating = true;
if(!dynamic)
invalidateStaticTimestamp();
PrunerHandle handle;
if(compoundId == INVALID_COMPOUND_ID)
{
PX_ASSERT(prunerIndex<mPrunerExt.size());
PX_ASSERT(mPrunerExt[prunerIndex]->pruner());
mPrunerExt[prunerIndex]->pruner()->addObjects(&handle, &bounds, &payload, &transform, 1, hasPruningStructure);
//mPrunerExt[prunerIndex].growDirtyList(handle);
}
else
{
PX_ASSERT(mCompoundPrunerExt.pruner());
mCompoundPrunerExt.pruner()->addObject(compoundId, handle, bounds, payload, transform);
}
return handle;
}
void ExtPrunerManager::removePrunerShape(PxU32 prunerIndex, bool dynamic, PrunerCompoundId compoundId, PrunerHandle shapeHandle, PrunerPayloadRemovalCallback* removalCallback)
{
if(compoundId==INVALID_COMPOUND_ID && prunerIndex>=mPrunerExt.size())
{
PxGetFoundation().error(PxErrorCode::eINVALID_PARAMETER, PX_FL, "Invalid pruner index");
return;
}
mPrunerNeedsUpdating = true;
if(!dynamic)
invalidateStaticTimestamp();
if(compoundId == INVALID_COMPOUND_ID)
{
PX_ASSERT(prunerIndex<mPrunerExt.size());
PX_ASSERT(mPrunerExt[prunerIndex]->pruner());
mPrunerExt[prunerIndex]->removeFromDirtyList(shapeHandle);
mPrunerExt[prunerIndex]->pruner()->removeObjects(&shapeHandle, 1, removalCallback);
}
else
{
mCompoundPrunerExt.removeFromDirtyList(compoundId, shapeHandle);
mCompoundPrunerExt.pruner()->removeObject(compoundId, shapeHandle, removalCallback);
}
}
void ExtPrunerManager::markForUpdate(PxU32 prunerIndex, bool dynamic, PrunerCompoundId compoundId, PrunerHandle shapeHandle, const PxTransform& transform)
{
if(compoundId==INVALID_COMPOUND_ID && prunerIndex>=mPrunerExt.size())
{
PxGetFoundation().error(PxErrorCode::eINVALID_PARAMETER, PX_FL, "Invalid pruner index");
return;
}
mPrunerNeedsUpdating = true;
if(!dynamic)
invalidateStaticTimestamp();
if(compoundId == INVALID_COMPOUND_ID)
{
PX_ASSERT(prunerIndex<mPrunerExt.size());
PX_ASSERT(mPrunerExt[prunerIndex]->pruner());
// PT: TODO: at this point do we still need a dirty list? we could just update the bounds directly?
mPrunerExt[prunerIndex]->addToDirtyList(shapeHandle, dynamic, transform);
}
else
mCompoundPrunerExt.addToDirtyList(compoundId, shapeHandle, transform);
}
void ExtPrunerManager::setDynamicTreeRebuildRateHint(PxU32 rebuildRateHint)
{
mRebuildRateHint = rebuildRateHint;
// PT: we are still using the same rebuild hint for all pruners here, which may or may
// not make sense. We could also have a different build rate for each pruner.
const PxU32 nb = mPrunerExt.size();
for(PxU32 i=0;i<nb;i++)
{
PrunerExt* pe = mPrunerExt[i];
Pruner* pruner = pe->pruner();
if(pruner && pruner->isDynamic())
static_cast<DynamicPruner*>(pruner)->setRebuildRateHint(rebuildRateHint);
}
}
void ExtPrunerManager::afterSync(bool buildStep, bool commit)
{
PX_PROFILE_ZONE("Sim.sceneQueryBuildStep", mContextID);
if(!buildStep && !commit)
{
mPrunerNeedsUpdating = true;
return;
}
// flush user modified objects
flushShapes();
// PT: TODO: with N pruners this part would be worth multi-threading
const PxU32 nb = mPrunerExt.size();
for(PxU32 i=0;i<nb;i++)
{
PrunerExt* pe = mPrunerExt[i];
Pruner* pruner = pe->pruner();
if(pruner)
{
if(pruner->isDynamic())
static_cast<DynamicPruner*>(pruner)->buildStep(true);
if(commit)
pruner->commit();
}
}
if(commit)
{
if(mUsesTreeOfPruners)
createTreeOfPruners();
}
mPrunerNeedsUpdating = !commit;
}
void ExtPrunerManager::flushShapes()
{
PX_PROFILE_ZONE("SceneQuery.flushShapes", mContextID);
// must already have acquired writer lock here
const float inflation = 1.0f + mInflation;
bool mustInvalidateStaticTimestamp = false;
const PxU32 nb = mPrunerExt.size();
for(PxU32 i=0;i<nb;i++)
{
PrunerExt* pe = mPrunerExt[i];
if(pe->processDirtyList(i, mAdapter, inflation))
mustInvalidateStaticTimestamp = true;
}
if(mustInvalidateStaticTimestamp)
invalidateStaticTimestamp();
mCompoundPrunerExt.flushShapes(mAdapter, inflation);
}
void ExtPrunerManager::flushUpdates()
{
PX_PROFILE_ZONE("SceneQuery.flushUpdates", mContextID);
if(mPrunerNeedsUpdating)
{
// no need to take lock if manual sq update is enabled
// as flushUpdates will only be called from NpScene::flushQueryUpdates()
mSQLock.lock();
if(mPrunerNeedsUpdating)
{
flushShapes();
const PxU32 nb = mPrunerExt.size();
for(PxU32 i=0;i<nb;i++)
{
PrunerExt* pe = mPrunerExt[i];
if(pe->pruner())
pe->pruner()->commit();
}
if(mUsesTreeOfPruners)
createTreeOfPruners();
PxMemoryBarrier();
mPrunerNeedsUpdating = false;
}
mSQLock.unlock();
}
}
void ExtPrunerManager::forceRebuildDynamicTree(PxU32 prunerIndex)
{
// PT: beware here when called from the PxScene, the prunerIndex may not match
PX_PROFILE_ZONE("SceneQuery.forceDynamicTreeRebuild", mContextID);
if(prunerIndex>=mPrunerExt.size())
{
PxGetFoundation().error(PxErrorCode::eINVALID_PARAMETER, PX_FL, "Invalid pruner index");
return;
}
PxMutex::ScopedLock lock(mSQLock);
PrunerExt* pe = mPrunerExt[prunerIndex];
Pruner* pruner = pe->pruner();
if(pruner && pruner->isDynamic())
{
static_cast<DynamicPruner*>(pruner)->purge();
static_cast<DynamicPruner*>(pruner)->commit();
}
}
void* ExtPrunerManager::prepareSceneQueriesUpdate(PxU32 prunerIndex)
{
// PT: beware here when called from the PxScene, the prunerIndex may not match
if(prunerIndex>=mPrunerExt.size())
{
PxGetFoundation().error(PxErrorCode::eINVALID_PARAMETER, PX_FL, "Invalid pruner index");
return NULL;
}
PX_ASSERT(mPrunerExt[prunerIndex]->pruner());
bool retVal = false;
Pruner* pruner = mPrunerExt[prunerIndex]->pruner();
if(pruner && pruner->isDynamic())
retVal = static_cast<DynamicPruner*>(pruner)->prepareBuild();
return retVal ? pruner : NULL;
}
void ExtPrunerManager::sceneQueryBuildStep(void* handle)
{
PX_PROFILE_ZONE("SceneQuery.sceneQueryBuildStep", mContextID);
Pruner* pruner = reinterpret_cast<Pruner*>(handle);
if(pruner && pruner->isDynamic())
{
const bool buildFinished = static_cast<DynamicPruner*>(pruner)->buildStep(false);
if(buildFinished)
mPrunerNeedsUpdating = true;
}
}
void ExtPrunerManager::visualize(PxU32 prunerIndex, PxRenderOutput& out) const
{
// PT: beware here when called from the PxScene, the prunerIndex may not match
// This is quite awkward and should be improved.
// PT: problem here is that this function will be called by the regular PhysX scene when plugged to its PxSceneDesc,
// and the calling code only understands the regular PhysX pruners.
const bool visualizeCompoundPruner = prunerIndex==0xffffffff;
if(visualizeCompoundPruner)
{
const CompoundPruner* cp = mCompoundPrunerExt.pruner();
if(cp)
cp->visualizeEx(out, SQ_DEBUG_VIZ_COMPOUND_COLOR, true, true);
}
else
{
if(prunerIndex>=mPrunerExt.size())
{
PxGetFoundation().error(PxErrorCode::eINVALID_PARAMETER, PX_FL, "Invalid pruner index");
return;
}
PrunerExt* pe = mPrunerExt[prunerIndex];
Pruner* pruner = pe->pruner();
if(pruner)
{
// PT: TODO: this doesn't really work: the static color was for static shapes but they
// could still be stored in a dynamic pruner. So this code doesn't use a color scheme
// consistent with what we use in the default code.
if(pruner->isDynamic())
{
//if(visDynamic)
pruner->visualize(out, SQ_DEBUG_VIZ_DYNAMIC_COLOR, SQ_DEBUG_VIZ_DYNAMIC_COLOR2);
}
else
{
//if(visStatic)
pruner->visualize(out, SQ_DEBUG_VIZ_STATIC_COLOR, SQ_DEBUG_VIZ_STATIC_COLOR2);
}
}
}
}
void ExtPrunerManager::shiftOrigin(const PxVec3& shift)
{
const PxU32 nb = mPrunerExt.size();
for(PxU32 i=0;i<nb;i++)
{
PrunerExt* pe = mPrunerExt[i];
Pruner* pruner = pe->pruner();
if(pruner)
pruner->shiftOrigin(shift);
}
mCompoundPrunerExt.pruner()->shiftOrigin(shift);
}
void ExtPrunerManager::addCompoundShape(const PxBVH& pxbvh, PrunerCompoundId compoundId, const PxTransform& compoundTransform, PrunerHandle* prunerHandle, const PrunerPayload* payloads, const PxTransform* transforms, bool isDynamic)
{
const Gu::BVH& bvh = static_cast<const Gu::BVH&>(pxbvh);
PX_ASSERT(mCompoundPrunerExt.mPruner);
mCompoundPrunerExt.mPruner->addCompound(prunerHandle, bvh, compoundId, compoundTransform, isDynamic, payloads, transforms);
if(!isDynamic)
invalidateStaticTimestamp();
}
void ExtPrunerManager::updateCompoundActor(PrunerCompoundId compoundId, const PxTransform& compoundTransform)
{
PX_ASSERT(mCompoundPrunerExt.mPruner);
const bool isDynamic = mCompoundPrunerExt.mPruner->updateCompound(compoundId, compoundTransform);
if(!isDynamic)
invalidateStaticTimestamp();
}
void ExtPrunerManager::removeCompoundActor(PrunerCompoundId compoundId, PrunerPayloadRemovalCallback* removalCallback)
{
PX_ASSERT(mCompoundPrunerExt.mPruner);
const bool isDynamic = mCompoundPrunerExt.mPruner->removeCompound(compoundId, removalCallback);
if(!isDynamic)
invalidateStaticTimestamp();
}
void ExtPrunerManager::sync(PxU32 prunerIndex, const PrunerHandle* handles, const PxU32* boundsIndices, const PxBounds3* bounds, const PxTransform32* transforms, PxU32 count, const PxBitMap& ignoredIndices)
{
if(!count)
return;
if(prunerIndex>=mPrunerExt.size())
{
PxGetFoundation().error(PxErrorCode::eINVALID_PARAMETER, PX_FL, "Invalid pruner index");
return;
}
Pruner* pruner = getPruner(prunerIndex);
if(!pruner)
return;
PxU32 startIndex = 0;
PxU32 numIndices = count;
// if shape sim map is not empty, parse the indices and skip update for the dirty one
if(ignoredIndices.count())
{
// PT: I think this codepath was used with SCB / buffered changes, but it's not needed anymore
numIndices = 0;
for(PxU32 i=0; i<count; i++)
{
// if(ignoredIndices.test(boundsIndices[i]))
if(ignoredIndices.boundedTest(boundsIndices[i]))
{
pruner->updateObjects(handles + startIndex, numIndices, mInflation, boundsIndices + startIndex, bounds, transforms);
numIndices = 0;
startIndex = i + 1;
}
else
numIndices++;
}
// PT: we fallback to the next line on purpose - no "else"
}
pruner->updateObjects(handles + startIndex, numIndices, mInflation, boundsIndices + startIndex, bounds, transforms);
}
PxU32 ExtPrunerManager::startCustomBuildstep()
{
PX_PROFILE_ZONE("SceneQuery.startCustomBuildstep", mContextID);
// flush user modified objects
//flushShapes();
{
PX_PROFILE_ZONE("SceneQuery.flushShapes", mContextID);
// PT: only flush the compound pruner synchronously
// must already have acquired writer lock here
const float inflation = 1.0f + mInflation;
mCompoundPrunerExt.flushShapes(mAdapter, inflation);
}
mTimestampNeedsUpdating = false;
return mPrunerExt.size();
}
void ExtPrunerManager::customBuildstep(PxU32 index)
{
PX_PROFILE_ZONE("SceneQuery.customBuildstep", mContextID);
PX_ASSERT(index<mPrunerExt.size());
PrunerExt* pe = mPrunerExt[index];
Pruner* pruner = pe->pruner();
//void ExtPrunerManager::flushShapes()
{
PX_PROFILE_ZONE("SceneQuery.flushShapes", mContextID);
// must already have acquired writer lock here
const float inflation = 1.0f + mInflation;
if(pe->processDirtyList(index, mAdapter, inflation))
mTimestampNeedsUpdating = true;
}
if(pruner)
{
if(pruner->isDynamic())
static_cast<DynamicPruner*>(pruner)->buildStep(true); // PT: "true" because that parameter was made for PxSceneQuerySystem::sceneQueryBuildStep(), not us
pruner->commit();
}
}
void ExtPrunerManager::finishCustomBuildstep()
{
PX_PROFILE_ZONE("SceneQuery.finishCustomBuildstep", mContextID);
if(mUsesTreeOfPruners)
createTreeOfPruners();
mPrunerNeedsUpdating = false;
if(mTimestampNeedsUpdating)
invalidateStaticTimestamp();
}
void ExtPrunerManager::createTreeOfPruners()
{
PX_PROFILE_ZONE("SceneQuery.createTreeOfPruners", mContextID);
PX_DELETE(mTreeOfPruners);
mTreeOfPruners = PX_NEW(BVH)(NULL);
const PxU32 nb = mPrunerExt.size();
PxBounds3* prunerBounds = reinterpret_cast<PxBounds3*>(PxAlloca(sizeof(PxBounds3)*(nb+1)));
PxU32 nbBounds = 0;
for(PxU32 i=0;i<nb;i++)
{
PrunerExt* pe = mPrunerExt[i];
Pruner* pruner = pe->pruner();
if(pruner)
pruner->getGlobalBounds(prunerBounds[nbBounds++]);
}
mTreeOfPruners->init(nbBounds, NULL, prunerBounds, sizeof(PxBounds3), BVH_SPLATTER_POINTS, 1, 0.01f);
}