You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Gems/PhysX/Code/Tests/Benchmarks/PhysXRigidBodyBenchmarks.cpp

540 lines
25 KiB
C++

/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifdef HAVE_BENCHMARK
#include <benchmark/benchmark.h>
#include <AzTest/AzTest.h>
#include <AzFramework/Physics/Collision/CollisionEvents.h>
#include <AzFramework/Physics/Common/PhysicsEvents.h>
#include <Benchmarks/PhysXBenchmarksUtilities.h>
#include <Benchmarks/PhysXBenchmarksCommon.h>
#include <Benchmarks/PhysXBenchmarkWashingMachine.h>
#include <PhysXTestCommon.h>
#include <PhysXTestUtil.h>
namespace PhysX::Benchmarks
{
namespace RigidBodyConstants
{
//! Controls the simulation length of the test. 30secs at 60fps
static const int GameFramesToSimulate = 1800;
//! The size of safe the test region, used for spawning Rigid bodies
static const float TestRadius = 250.0f;
//! The size of the test terrain
static const float TerrainSize = 1000.0f;
//! Decide if CCD should be on/off for the following tests
static const bool CCDEnabled = true;
//! Constant seed to use with random number generation
static const long long RandGenSeed = 8010412111588; //(Number generated by concatenating 'PhysX' ascii character codes (80 104 121 115 88).
//! Constants for setting up the washing machine
namespace WashingMachine
{
static const float CylinderHeight = 500.0f;
static const float BladeRPM = 10.0f;
} // namespace WashingMachine
//! Constants for setting up the Rigid Bodies
namespace RigidBodys
{
static const float BoxSize = 5.0f;
//! Default starting value of Entity Ids for rigid bodies
static const AZ::u64 RigidBodyEntityIdStart = 2000u;
}
//! Settings used to setup each benchmark
namespace BenchmarkSettings
{
//! Values passed to benchmark to select the number of rigid bodies to spawn during each test
//! Current values will run tests between StartRange to EndRange (inclusive), multiplying by RangeMultiplier each step.
static const int StartRange = 128;
static const int EndRange = 8192;
static const int RangeMultipler = 2;
//!Flags to adjust how the collision handlers benchmark runs
static const int AllCollisionHanders = 0; // create the same number of handlers as rigid bodies
static const int HalfCollisionHandlers = 1; // create half the number of handlers as rigid bodies
static const int NoCollisionHandlers = 2; // create the no handlers
//! Number of iterations for each test
static const int NumIterations = 3;
} // namespace BenchmarkRange
} // namespace RigidBodyConstants
namespace Utils
{
//! Object that will connect to the collision notification bus of the provided entity id
struct SimulatedBodyCollisionHandler
{
SimulatedBodyCollisionHandler(AzPhysics::SceneHandle sceneHandle, AzPhysics::SimulatedBodyHandle bodyHandle)
: m_onCollisionBeginHandler(
[this]([[maybe_unused]] AzPhysics::SimulatedBodyHandle bodyHandle,
[[maybe_unused]] const AzPhysics::CollisionEvent& event)
{
m_beginCount++;
})
, m_onCollisionPersistHandler(
[this]([[maybe_unused]] AzPhysics::SimulatedBodyHandle bodyHandle,
[[maybe_unused]] const AzPhysics::CollisionEvent& event)
{
m_persistCount++;
})
, m_onCollisionEndHandler(
[this]([[maybe_unused]] AzPhysics::SimulatedBodyHandle bodyHandle,
[[maybe_unused]] const AzPhysics::CollisionEvent& event)
{
m_endCount++;
})
{
AzPhysics::SimulatedBodyEvents::RegisterOnCollisionBeginHandler(sceneHandle, bodyHandle, m_onCollisionBeginHandler);
AzPhysics::SimulatedBodyEvents::RegisterOnCollisionBeginHandler(sceneHandle, bodyHandle, m_onCollisionPersistHandler);
AzPhysics::SimulatedBodyEvents::RegisterOnCollisionBeginHandler(sceneHandle, bodyHandle, m_onCollisionEndHandler);
}
SimulatedBodyCollisionHandler(SimulatedBodyCollisionHandler&& other)
{
m_onCollisionBeginHandler = AZStd::move(other.m_onCollisionBeginHandler);
m_onCollisionPersistHandler = AZStd::move(other.m_onCollisionPersistHandler);
m_onCollisionEndHandler = AZStd::move(other.m_onCollisionEndHandler);
m_beginCount = other.m_beginCount;
m_persistCount = other.m_persistCount;
m_endCount = other.m_endCount;
}
~SimulatedBodyCollisionHandler()
{
m_onCollisionBeginHandler.Disconnect();
m_onCollisionPersistHandler.Disconnect();
m_onCollisionEndHandler.Disconnect();
}
int m_beginCount = 0;
int m_persistCount = 0;
int m_endCount = 0;
private:
AzPhysics::SimulatedBodyEvents::OnCollisionBegin::Handler m_onCollisionBeginHandler;
AzPhysics::SimulatedBodyEvents::OnCollisionPersist::Handler m_onCollisionPersistHandler;
AzPhysics::SimulatedBodyEvents::OnCollisionEnd::Handler m_onCollisionEndHandler;
};
} // namespace Utils
//! Rigid body performance fixture.
//! Will create a world, and terrain.
class PhysXRigidbodyBenchmarkFixture
: public PhysXBaseBenchmarkFixture
{
protected:
virtual void internalSetUp()
{
PhysXBaseBenchmarkFixture::SetUpInternal();
//need to get the Physics::System to be able to spawn the rigid bodies
m_system = AZ::Interface<Physics::System>::Get();
m_terrainEntity = PhysX::TestUtils::CreateFlatTestTerrain(m_testSceneHandle, RigidBodyConstants::TerrainSize, RigidBodyConstants::TerrainSize);
}
virtual void internalTearDown()
{
m_terrainEntity = nullptr;
PhysXBaseBenchmarkFixture::TearDownInternal();
}
public:
void SetUp(const benchmark::State&) override
{
internalSetUp();
}
void SetUp(benchmark::State&) override
{
internalSetUp();
}
void TearDown(const benchmark::State&) override
{
internalTearDown();
}
void TearDown(benchmark::State&) override
{
internalTearDown();
}
protected:
// PhysXBaseBenchmarkFixture Interface ---------
AzPhysics::SceneConfiguration GetDefaultSceneConfiguration() override
{
AzPhysics::SceneConfiguration sceneConfig = AzPhysics::SceneConfiguration::CreateDefault();
sceneConfig.m_enableCcd = RigidBodyConstants::CCDEnabled;
return sceneConfig;
}
// PhysXBaseBenchmarkFixture Interface ---------
Physics::System *m_system;
EntityPtr m_terrainEntity;
};
//! BM_RigidBody_AtRest - This test will spawn the requested number of rigid bodies and place them near the ground
//! and the rigid bodies will go 'asleep'
//! The test will run the simulation for ~1800 game frames at 60fps.
BENCHMARK_DEFINE_F(PhysXRigidbodyBenchmarkFixture, BM_RigidBody_AtRest)(benchmark::State& state)
{
//get the request number of rigid bodies and prepare to spawn them
const int numRigidBodies = static_cast<int>(state.range(0));
//common settings for each rigid body
const float boxSizeWithSpacing = RigidBodyConstants::RigidBodys::BoxSize + 2.0f;
const int boxesPerCol = static_cast<const int>(RigidBodyConstants::TerrainSize / boxSizeWithSpacing) - 1;
int spawnColIdx = 0;
int spawnRowIdx = 0;
//function to generate the rigid bodies position / orientation / mass
Utils::GenerateSpawnPositionFuncPtr posGenerator = [boxSizeWithSpacing, boxesPerCol, &spawnColIdx, &spawnRowIdx]([[maybe_unused]] int idx) -> const AZ::Vector3 {
const float x = boxSizeWithSpacing + (boxSizeWithSpacing * spawnColIdx);
const float y = boxSizeWithSpacing + (boxSizeWithSpacing * spawnRowIdx);
const float z = RigidBodyConstants::RigidBodys::BoxSize / 2.0f;
//advance to the next position to spawn the next rigid body
spawnColIdx++;
if (spawnColIdx >= boxesPerCol)
{
spawnColIdx = 0;
spawnRowIdx++;
}
return AZ::Vector3(x, y, z);
};
auto boxShapeConfiguration = AZStd::make_shared<Physics::BoxShapeConfiguration>(AZ::Vector3(RigidBodyConstants::RigidBodys::BoxSize));
Utils::GenerateColliderFuncPtr colliderGenerator = [&boxShapeConfiguration]([[maybe_unused]] int idx)
{
return boxShapeConfiguration;
};
//spawn the rigid bodies
AzPhysics::SimulatedBodyHandleList rigidBodies = Utils::CreateRigidBodies(numRigidBodies, m_defaultScene,
RigidBodyConstants::CCDEnabled, &colliderGenerator, &posGenerator);
//setup the sub tick tracker
Utils::PrePostSimulationEventHandler subTickTracker;
subTickTracker.Start(m_defaultScene);
//setup the frame timer tracker
Types::TimeList tickTimes;
for (auto _ : state)
{
for (AZ::u32 i = 0; i < RigidBodyConstants::GameFramesToSimulate; i++)
{
auto start = AZStd::chrono::system_clock::now();
StepScene1Tick(DefaultTimeStep);
//time each physics tick and store it to analyze
auto tickElapsedMilliseconds = Types::double_milliseconds(AZStd::chrono::system_clock::now() - start);
tickTimes.emplace_back(tickElapsedMilliseconds.count());
}
}
subTickTracker.Stop();
//object clean up
m_defaultScene->RemoveSimulatedBodies(rigidBodies);
rigidBodies.clear();
//sort the frame times and get the P50, P90, P99 percentiles
Utils::ReportFramePercentileCounters(state, tickTimes, subTickTracker.GetSubTickTimes());
Utils::ReportFrameStandardDeviationAndMeanCounters(state, tickTimes, subTickTracker.GetSubTickTimes());
}
//! BM_RigidBody_MovingAndColliding - This test will create the physics washing machine, a cylinder with a spinning blade where
//! it will spawn the requested number of rigid bodies above the machine and let them fall into a spinning blade.
//! The test will run the simulation for ~1800 game frames at 60fps.
BENCHMARK_DEFINE_F(PhysXRigidbodyBenchmarkFixture, BM_RigidBody_MovingAndColliding)(benchmark::State &state)
{
//setup some pieces for the test
AZ::SimpleLcgRandom rand;
rand.SetSeed(RigidBodyConstants::RandGenSeed);
//Create a washing machine of physx objects. This is a cylinder with a spinning blade that rigid bodies are placed inside
const AZ::Vector3 washingMachineCentre(500.0f, 500.0f, 1.0f);
WashingMachine washingMachine;
washingMachine.SetupWashingMachine(
m_testSceneHandle, RigidBodyConstants::TestRadius, RigidBodyConstants::WashingMachine::CylinderHeight,
washingMachineCentre, RigidBodyConstants::WashingMachine::BladeRPM);
//get the request number of rigid bodies and prepare to spawn them
const int numRigidBodies = static_cast<int>(state.range(0));
//add the rigid bodies
//function to generate the rigid bodies position / orientation / mass
Utils::GenerateSpawnPositionFuncPtr posGenerator = [washingMachineCentre, &rand](int idx) -> const AZ::Vector3 {
const float spawnArea = (RigidBodyConstants::TestRadius * 1.5f);
const float x = washingMachineCentre.GetX() + (rand.GetRandomFloat() - 0.5f) * spawnArea;
const float y = washingMachineCentre.GetY() + (rand.GetRandomFloat() - 0.5f) * spawnArea;
const float z = washingMachineCentre.GetZ() + RigidBodyConstants::WashingMachine::CylinderHeight + ((RigidBodyConstants::RigidBodys::BoxSize / 2.0f) * idx);
return AZ::Vector3(x, y, z);
};
Utils::GenerateSpawnOrientationFuncPtr oriGenerator = [&rand]([[maybe_unused]] int idx) -> AZ::Quaternion {
return AZ::CreateRandomQuaternion(rand);
};
Utils::GenerateMassFuncPtr massGenerator = [&rand]([[maybe_unused]] int idx) -> float {
return rand.GetRandomFloat() * 25.0f + 5.0f;
};
auto boxShapeConfiguration = AZStd::make_shared<Physics::BoxShapeConfiguration>(AZ::Vector3(RigidBodyConstants::RigidBodys::BoxSize));
Utils::GenerateColliderFuncPtr colliderGenerator = [&boxShapeConfiguration]([[maybe_unused]] int idx)
{
return boxShapeConfiguration;
};
//spawn the rigid bodies
AzPhysics::SimulatedBodyHandleList rigidBodies = Utils::CreateRigidBodies(numRigidBodies, m_defaultScene,
RigidBodyConstants::CCDEnabled, &colliderGenerator, &posGenerator, &oriGenerator, &massGenerator);
//setup the sub tick tracker
Utils::PrePostSimulationEventHandler subTickTracker;
subTickTracker.Start(m_defaultScene);
//setup the frame timer tracker
AZStd::vector<double> tickTimes;
tickTimes.reserve(RigidBodyConstants::GameFramesToSimulate);
for (auto _ : state)
{
for (AZ::u32 i = 0; i < RigidBodyConstants::GameFramesToSimulate; i++)
{
auto start = AZStd::chrono::system_clock::now();
StepScene1Tick(DefaultTimeStep);
//time each physics tick and store it to analyze
auto tickElapsedMilliseconds = Types::double_milliseconds(AZStd::chrono::system_clock::now() - start);
tickTimes.emplace_back(tickElapsedMilliseconds.count());
}
}
subTickTracker.Stop();
//object clean up
washingMachine.TearDownWashingMachine();
m_defaultScene->RemoveSimulatedBodies(rigidBodies);
rigidBodies.clear();
//sort the frame times and get the P50, P90, P99 percentiles
Utils::ReportFramePercentileCounters(state, tickTimes, subTickTracker.GetSubTickTimes());
Utils::ReportFrameStandardDeviationAndMeanCounters(state, tickTimes, subTickTracker.GetSubTickTimes());
}
//! Same as the PhysXRigidbodyBenchmarkFixture, adds a world event handler to receive collision events
class PhysXRigidbodyCollisionsBenchmarkFixture
: public PhysXRigidbodyBenchmarkFixture
{
void internalSetUp() override
{
PhysXRigidbodyBenchmarkFixture::internalSetUp();
m_collisionBeginCount = 0;
m_collisionPersistCount = 0;
m_collisionEndCount = 0;
m_onSceneCollisionHandler = AzPhysics::SceneEvents::OnSceneCollisionsEvent::Handler(
[this]([[maybe_unused]] AzPhysics::SceneHandle sceneHandle,
const AzPhysics::CollisionEventList& collisionData)
{
//count the events
for (const auto& collisionEvent : collisionData)
{
switch (collisionEvent.m_type)
{
case AzPhysics::CollisionEvent::Type::Begin:
m_collisionBeginCount++;
break;
case AzPhysics::CollisionEvent::Type::Persist:
m_collisionPersistCount++;
break;
case AzPhysics::CollisionEvent::Type::End:
m_collisionEndCount++;
break;
}
}
}
);
m_defaultScene->RegisterSceneCollisionEventHandler(m_onSceneCollisionHandler);
}
void internalTearDown() override
{
m_onSceneCollisionHandler.Disconnect();
PhysXRigidbodyBenchmarkFixture::internalTearDown();
}
public:
void SetUp(const benchmark::State&) override
{
internalSetUp();
}
void SetUp(benchmark::State&) override
{
internalSetUp();
}
void TearDown(const benchmark::State&) override
{
internalTearDown();
}
void TearDown(benchmark::State&) override
{
internalTearDown();
}
protected:
// Collision counters to track and report the number of events
int m_collisionBeginCount;
int m_collisionPersistCount;
int m_collisionEndCount;
AzPhysics::SceneEvents::OnSceneCollisionsEvent::Handler m_onSceneCollisionHandler;
};
//! BM_RigidBody_MovingAndColliding_CollisionHandlers - Runs that same benchmark as BM_RigidBody_MovingAndColliding, under a different fixture that adds world event handler for collisions
//! Will have an equal number of Collision Handlers and rigid bodies. Not all rigid bodies will get a handler, some will have multiple.
//! The test will run the simulation for ~1800 game frames at 60fps.
BENCHMARK_DEFINE_F(PhysXRigidbodyCollisionsBenchmarkFixture, BM_RigidBody_MovingAndColliding_CollisionHandlers)(benchmark::State& state)
{
//setup some pieces for the test
AZ::SimpleLcgRandom rand;
rand.SetSeed(RigidBodyConstants::RandGenSeed);
//Create a washing machine of physx objects. This is a cylinder with a spinning blade that rigid bodies are placed inside
const AZ::Vector3 washingMachineCentre(500.0f, 500.0f, 1.0f);
WashingMachine washingMachine;
washingMachine.SetupWashingMachine(
m_testSceneHandle, RigidBodyConstants::TestRadius, RigidBodyConstants::WashingMachine::CylinderHeight,
washingMachineCentre, RigidBodyConstants::WashingMachine::BladeRPM);
//get the request number of rigid bodies and prepare to spawn them
const int numRigidBodies = static_cast<int>(state.range(0));
//add the rigid bodies
//function to generate the rigid bodies position / orientation / mass
Utils::GenerateSpawnPositionFuncPtr posGenerator = [washingMachineCentre, &rand](int idx) -> const AZ::Vector3 {
const float u = std::sqrt(rand.GetRandomFloat());
const float theta = AZ::Constants::TwoPi * rand.GetRandomFloat();
const float x = washingMachineCentre.GetX() + RigidBodyConstants::TestRadius * u * std::sin(theta);
const float y = washingMachineCentre.GetY() + RigidBodyConstants::TestRadius * u * std::cos(theta);
const float z = washingMachineCentre.GetZ() + RigidBodyConstants::WashingMachine::CylinderHeight + ((RigidBodyConstants::RigidBodys::BoxSize / 2.0f) * idx);
return AZ::Vector3(x, y, z);
};
Utils::GenerateSpawnOrientationFuncPtr oriGenerator = [&rand]([[maybe_unused]] int idx) -> AZ::Quaternion {
return AZ::CreateRandomQuaternion(rand);
};
Utils::GenerateMassFuncPtr massGenerator = [&rand]([[maybe_unused]] int idx) -> float {
return rand.GetRandomFloat() * 25.0f + 5.0f;
};
Utils::GenerateEntityIdFuncPtr entityIdGenerator = [](int idx) -> AZ::EntityId {
return AZ::EntityId(static_cast<AZ::u64>(idx) + RigidBodyConstants::RigidBodys::RigidBodyEntityIdStart);
};
auto boxShapeConfiguration = AZStd::make_shared<Physics::BoxShapeConfiguration>(AZ::Vector3(RigidBodyConstants::RigidBodys::BoxSize));
Utils::GenerateColliderFuncPtr colliderGenerator = [&boxShapeConfiguration]([[maybe_unused]] int idx)
{
return boxShapeConfiguration;
};
//spawn the rigid bodies
AzPhysics::SimulatedBodyHandleList rigidBodies = Utils::CreateRigidBodies(numRigidBodies, m_defaultScene,
RigidBodyConstants::CCDEnabled, &colliderGenerator, &posGenerator, &oriGenerator, &massGenerator);
//create the collision handlers
AZStd::vector<Utils::SimulatedBodyCollisionHandler> collisionHandlers;
int numberCollisionHandlers = 0;
if (static_cast<int>(state.range(1)) == RigidBodyConstants::BenchmarkSettings::AllCollisionHanders)
{
numberCollisionHandlers = numRigidBodies;
}
else if (static_cast<int>(state.range(1)) == RigidBodyConstants::BenchmarkSettings::HalfCollisionHandlers)
{
numberCollisionHandlers = numRigidBodies / 2;
}
collisionHandlers.reserve(numberCollisionHandlers);
for (int i = 0; i < numberCollisionHandlers; i++)
{
const AZ::u64 randIndex = rand.Getu64Random() % numRigidBodies; //randomly select the offset.
collisionHandlers.emplace_back(m_testSceneHandle, rigidBodies[randIndex]);
}
//setup the sub tick tracker
Utils::PrePostSimulationEventHandler subTickTracker;
subTickTracker.Start(m_defaultScene);
//setup the frame timer tracker
AZStd::vector<double> tickTimes;
tickTimes.reserve(RigidBodyConstants::GameFramesToSimulate);
for (auto _ : state)
{
for (AZ::u32 i = 0; i < RigidBodyConstants::GameFramesToSimulate; i++)
{
auto start = AZStd::chrono::system_clock::now();
StepScene1Tick(DefaultTimeStep);
//time each physics tick and store it to analyze
auto tickElapsedMilliseconds = Types::double_milliseconds(AZStd::chrono::system_clock::now() - start);
tickTimes.emplace_back(tickElapsedMilliseconds.count());
}
}
subTickTracker.Stop();
//object clean up
collisionHandlers.clear();
washingMachine.TearDownWashingMachine();
m_defaultScene->RemoveSimulatedBodies(rigidBodies);
rigidBodies.clear();
//sort the frame times and get the P50, P90, P99 percentiles
Utils::ReportFramePercentileCounters(state, tickTimes, subTickTracker.GetSubTickTimes());
Utils::ReportFrameStandardDeviationAndMeanCounters(state, tickTimes, subTickTracker.GetSubTickTimes());
//add the collision counts
state.counters["Collisions-Begin"] = static_cast<double>(m_collisionBeginCount);
state.counters["Collisions-Persist"] = static_cast<double>(m_collisionPersistCount);
state.counters["Collisions-End"] = static_cast<double>(m_collisionEndCount);
}
BENCHMARK_REGISTER_F(PhysXRigidbodyBenchmarkFixture, BM_RigidBody_AtRest)
->RangeMultiplier(RigidBodyConstants::BenchmarkSettings::RangeMultipler)
->Range(RigidBodyConstants::BenchmarkSettings::StartRange, RigidBodyConstants::BenchmarkSettings::EndRange)
->Unit(benchmark::kMillisecond)
->Iterations(RigidBodyConstants::BenchmarkSettings::NumIterations)
;
BENCHMARK_REGISTER_F(PhysXRigidbodyBenchmarkFixture, BM_RigidBody_MovingAndColliding)
->RangeMultiplier(RigidBodyConstants::BenchmarkSettings::RangeMultipler)
->Range(RigidBodyConstants::BenchmarkSettings::StartRange, RigidBodyConstants::BenchmarkSettings::EndRange)
->Unit(benchmark::kMillisecond)
->Iterations(RigidBodyConstants::BenchmarkSettings::NumIterations)
;
BENCHMARK_REGISTER_F(PhysXRigidbodyCollisionsBenchmarkFixture, BM_RigidBody_MovingAndColliding_CollisionHandlers)
->RangeMultiplier(RigidBodyConstants::BenchmarkSettings::RangeMultipler)
->Ranges({ {RigidBodyConstants::BenchmarkSettings::StartRange, RigidBodyConstants::BenchmarkSettings::EndRange}, {RigidBodyConstants::BenchmarkSettings::AllCollisionHanders, RigidBodyConstants::BenchmarkSettings::AllCollisionHanders} })
->Unit(benchmark::kMillisecond)
->Iterations(RigidBodyConstants::BenchmarkSettings::NumIterations)
;
BENCHMARK_REGISTER_F(PhysXRigidbodyCollisionsBenchmarkFixture, BM_RigidBody_MovingAndColliding_CollisionHandlers)
->RangeMultiplier(RigidBodyConstants::BenchmarkSettings::RangeMultipler)
->Ranges({ {RigidBodyConstants::BenchmarkSettings::StartRange, RigidBodyConstants::BenchmarkSettings::EndRange}, {RigidBodyConstants::BenchmarkSettings::HalfCollisionHandlers, RigidBodyConstants::BenchmarkSettings::HalfCollisionHandlers} })
->Unit(benchmark::kMillisecond)
->Iterations(RigidBodyConstants::BenchmarkSettings::NumIterations)
;
BENCHMARK_REGISTER_F(PhysXRigidbodyCollisionsBenchmarkFixture, BM_RigidBody_MovingAndColliding_CollisionHandlers)
->RangeMultiplier(RigidBodyConstants::BenchmarkSettings::RangeMultipler)
->Ranges({ {RigidBodyConstants::BenchmarkSettings::StartRange, RigidBodyConstants::BenchmarkSettings::EndRange}, {RigidBodyConstants::BenchmarkSettings::NoCollisionHandlers, RigidBodyConstants::BenchmarkSettings::NoCollisionHandlers} })
->Unit(benchmark::kMillisecond)
->Iterations(RigidBodyConstants::BenchmarkSettings::NumIterations)
;
} // namespace PhysX::Benchmarks
#endif