PhysXBenchmarksUtilities.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <Benchmarks/PhysXBenchmarksUtilities.h>
  9. #include <algorithm>
  10. #include <AzCore/std/smart_ptr/make_shared.h>
  11. #include <AzFramework/Physics/SimulatedBodies/RigidBody.h>
  12. #include <AzFramework/Physics/Shape.h>
  13. #include <AzFramework/Physics/PhysicsScene.h>
  14. #include <AzFramework/Physics/Configuration/RigidBodyConfiguration.h>
  15. #include <Benchmarks/PhysXBenchmarksCommon.h>
  16. namespace PhysX::Benchmarks
  17. {
  18. namespace Utils
  19. {
  20. BenchmarkRigidBodies CreateRigidBodies(
  21. int numRigidBodies,
  22. AzPhysics::SceneHandle sceneHandle,
  23. bool enableCCD,
  24. int benchmarkObjectType,
  25. GenerateColliderFuncPtr* genColliderFuncPtr /*= nullptr*/, GenerateSpawnPositionFuncPtr* genSpawnPosFuncPtr /*= nullptr*/,
  26. GenerateSpawnOrientationFuncPtr* genSpawnOriFuncPtr /*= nullptr*/, GenerateMassFuncPtr* genMassFuncPtr /*= nullptr*/,
  27. GenerateEntityIdFuncPtr* genEntityIdFuncPtr /*= nullptr*/,
  28. bool activateEntities /*= true*/
  29. )
  30. {
  31. BenchmarkRigidBodies benchmarkRigidBodies;
  32. if (benchmarkObjectType == PhysX::Benchmarks::RigidBodyApiObject)
  33. {
  34. AzPhysics::SimulatedBodyHandleList rigidBodies;
  35. rigidBodies.reserve(numRigidBodies);
  36. benchmarkRigidBodies = AZStd::move(rigidBodies);
  37. }
  38. else if (benchmarkObjectType == PhysX::Benchmarks::RigidBodyEntity)
  39. {
  40. PhysX::EntityList rigidBodies;
  41. rigidBodies.reserve(numRigidBodies);
  42. benchmarkRigidBodies = AZStd::move(rigidBodies);
  43. }
  44. AzPhysics::RigidBodyConfiguration rigidBodyConfig;
  45. rigidBodyConfig.m_ccdEnabled = enableCCD;
  46. auto rigidBodyColliderConfig = AZStd::make_shared<Physics::ColliderConfiguration>();
  47. auto defaultShapeConfiguration = AZStd::make_shared<Physics::BoxShapeConfiguration>(AZ::Vector3::CreateOne());
  48. for (int i = 0; i < numRigidBodies; i++)
  49. {
  50. //call the optional function pointers, otherwise assign a default
  51. if (genEntityIdFuncPtr != nullptr)
  52. {
  53. rigidBodyConfig.m_entityId = (*genEntityIdFuncPtr)(i);
  54. }
  55. if (genMassFuncPtr != nullptr)
  56. {
  57. rigidBodyConfig.m_mass = (*genMassFuncPtr)(i);
  58. }
  59. if (genSpawnPosFuncPtr != nullptr)
  60. {
  61. rigidBodyConfig.m_position = (*genSpawnPosFuncPtr)(i);
  62. }
  63. if (genSpawnOriFuncPtr != nullptr)
  64. {
  65. rigidBodyConfig.m_orientation = (*genSpawnOriFuncPtr)(i);
  66. }
  67. AZStd::shared_ptr<Physics::ShapeConfiguration> shapeConfig = nullptr;
  68. if (genColliderFuncPtr != nullptr)
  69. {
  70. shapeConfig = (*genColliderFuncPtr)(i);
  71. }
  72. if (shapeConfig == nullptr)
  73. {
  74. shapeConfig = defaultShapeConfiguration;
  75. }
  76. rigidBodyConfig.m_colliderAndShapeData = AzPhysics::ShapeColliderPair(rigidBodyColliderConfig, shapeConfig);
  77. if (benchmarkObjectType == PhysX::Benchmarks::RigidBodyApiObject)
  78. {
  79. AzPhysics::Scene* scene = AZ::Interface<AzPhysics::SystemInterface>::Get()->GetScene(sceneHandle);
  80. AzPhysics::SimulatedBodyHandle simBodyHandle = scene->AddSimulatedBody(&rigidBodyConfig);
  81. AZStd::get_if<AzPhysics::SimulatedBodyHandleList>(&benchmarkRigidBodies)->push_back(simBodyHandle);
  82. }
  83. else if (benchmarkObjectType == PhysX::Benchmarks::RigidBodyEntity)
  84. {
  85. EntityPtr entity;
  86. if (rigidBodyConfig.m_entityId.IsValid())
  87. {
  88. entity = AZStd::make_shared<AZ::Entity>(rigidBodyConfig.m_entityId, "TestEntity");
  89. }
  90. else
  91. {
  92. entity = AZStd::make_shared<AZ::Entity>("TestEntity");
  93. }
  94. auto* transformComponent = entity->CreateComponent<AzFramework::TransformComponent>();
  95. transformComponent->SetWorldTM(
  96. AZ::Transform::CreateFromQuaternionAndTranslation(rigidBodyConfig.m_orientation, rigidBodyConfig.m_position));
  97. auto boxColliderComponent = entity->CreateComponent<PhysX::BoxColliderComponent>();
  98. boxColliderComponent->SetShapeConfigurationList({ AzPhysics::ShapeColliderPair(rigidBodyColliderConfig, shapeConfig) });
  99. entity->CreateComponent<PhysX::RigidBodyComponent>(rigidBodyConfig, sceneHandle);
  100. entity->Init();
  101. if (activateEntities)
  102. {
  103. entity->Activate();
  104. }
  105. AZStd::get_if<PhysX::EntityList>(&benchmarkRigidBodies)->push_back(entity);
  106. }
  107. }
  108. return benchmarkRigidBodies;
  109. }
  110. AZStd::vector<AzPhysics::RigidBody*> GetRigidBodiesFromHandles(
  111. AzPhysics::Scene* scene, const Utils::BenchmarkRigidBodies& benchmarkRigidBodies)
  112. {
  113. AZStd::vector<AzPhysics::RigidBody*> rigidBodies;
  114. if (auto handlesList = AZStd::get_if<AzPhysics::SimulatedBodyHandleList>(&benchmarkRigidBodies))
  115. {
  116. rigidBodies = AZStd::vector<AzPhysics::RigidBody*>(AZStd::from_range, *handlesList | AZStd::views::transform([&scene](const AzPhysics::SimulatedBodyHandle& handle) {
  117. return azrtti_cast<AzPhysics::RigidBody*>(scene->GetSimulatedBodyFromHandle(handle)); }));
  118. }
  119. else if (auto entityList = AZStd::get_if<PhysX::EntityList>(&benchmarkRigidBodies))
  120. {
  121. rigidBodies = AZStd::vector<AzPhysics::RigidBody*>(AZStd::from_range, *entityList | AZStd::views::transform([](const EntityPtr& entity) {
  122. return entity->FindComponent<RigidBodyComponent>()->GetRigidBody(); }));
  123. }
  124. return rigidBodies;
  125. }
  126. PrePostSimulationEventHandler::PrePostSimulationEventHandler()
  127. : m_sceneStartSimHandler([this](
  128. [[maybe_unused]] AzPhysics::SceneHandle sceneHandle,
  129. [[maybe_unused]] float fixedDeltaTime)
  130. {
  131. this->PreTick();
  132. })
  133. , m_sceneFinishSimHandler([this](
  134. [[maybe_unused]] AzPhysics::SceneHandle sceneHandle,
  135. [[maybe_unused]] float fixedDeltatime)
  136. {
  137. this->PostTick();
  138. })
  139. {
  140. }
  141. void PrePostSimulationEventHandler::Start(AzPhysics::Scene* scene)
  142. {
  143. m_subTickTimes.clear();
  144. scene->RegisterSceneSimulationStartHandler(m_sceneStartSimHandler);
  145. scene->RegisterSceneSimulationFinishHandler(m_sceneFinishSimHandler);
  146. }
  147. void PrePostSimulationEventHandler::Stop()
  148. {
  149. m_sceneStartSimHandler.Disconnect();
  150. m_sceneFinishSimHandler.Disconnect();
  151. }
  152. void PrePostSimulationEventHandler::PreTick()
  153. {
  154. m_tickStart = AZStd::chrono::steady_clock::now();
  155. }
  156. void PrePostSimulationEventHandler::PostTick()
  157. {
  158. auto tickElapsedMilliseconds = Types::double_milliseconds(AZStd::chrono::steady_clock::now() - m_tickStart);
  159. m_subTickTimes.emplace_back(tickElapsedMilliseconds.count());
  160. }
  161. void ReportFramePercentileCounters(benchmark::State& state, Types::TimeList& frameTimes, Types::TimeList& subTickTimes, const AZStd::vector<double>& requestedPercentiles /*= { {0.5, 0.9, 0.99} }*/)
  162. {
  163. AZStd::vector<double> framePercentiles = GetPercentiles(requestedPercentiles, frameTimes);
  164. AZStd::vector<double> subTickPercentiles = GetPercentiles(requestedPercentiles, subTickTimes);
  165. //Report the percentiles, slowest and fastest frame of the run
  166. int minRange = static_cast<int>(AZStd::min(requestedPercentiles.size(), framePercentiles.size()));
  167. for (int i = 0; i < minRange; i++)
  168. {
  169. AZStd::string label = AZStd::string::format("Frame-P%d", static_cast<int>(requestedPercentiles[i] * 100.0));
  170. state.counters[label.c_str()] = framePercentiles[i];
  171. }
  172. //add fastest and slowest frame time, if it doesn't exist report -1.0 (negative time is impossible, so this denotes an error).
  173. std::nth_element(frameTimes.begin(), frameTimes.begin(), frameTimes.end());
  174. state.counters["Frame-Fastest"] = !frameTimes.empty() ? frameTimes.front() : -1.0;
  175. std::nth_element(frameTimes.begin(), frameTimes.begin() + (frameTimes.size() - 1), frameTimes.end());
  176. state.counters["Frame-Slowest"] = !frameTimes.empty() ? frameTimes.back() : -1.0;
  177. //Report the percentiles, slowest and fastest sub tick of the run
  178. if (subTickTimes.empty())
  179. {
  180. return;
  181. }
  182. minRange = static_cast<int>(AZStd::min(requestedPercentiles.size(), subTickPercentiles.size()));
  183. for (int i = 0; i < minRange; i++)
  184. {
  185. AZStd::string label = AZStd::string::format("SubTick-P%d", static_cast<int>(requestedPercentiles[i] * 100.0));
  186. state.counters[label.c_str()] = subTickPercentiles[i];
  187. }
  188. //add fastest and slowest frame time, if it doesn't exist report -1.0 (negative time is impossible, so this denotes an error).
  189. std::nth_element(subTickTimes.begin(), subTickTimes.begin(), subTickTimes.end());
  190. state.counters["SubTick-Fastest"] = !subTickTimes.empty() ? subTickTimes.front() : -1.0;
  191. std::nth_element(subTickTimes.begin(), subTickTimes.begin() + (subTickTimes.size()-1), subTickTimes.end());
  192. state.counters["SubTick-Slowest"] = !subTickTimes.empty() ? subTickTimes.back() : -1.0;
  193. }
  194. void ReportFrameStandardDeviationAndMeanCounters(benchmark::State& state, const Types::TimeList& frameTimes, const Types::TimeList& subTickTimes)
  195. {
  196. StandardDeviationAndMeanResults stdivMeanFrameTimes = GetStandardDeviationAndMean(frameTimes);
  197. state.counters["Frame-Mean"] = aznumeric_cast<double>(aznumeric_cast<int64_t>(stdivMeanFrameTimes.m_mean * 1000.0)) / 1000.0; //truncate to 3 decimal places
  198. state.counters["Frame-StDev"] = aznumeric_cast<double>(aznumeric_cast<int64_t>(stdivMeanFrameTimes.m_standardDeviation * 1000.0)) / 1000.0;
  199. StandardDeviationAndMeanResults stdivMeanSubTickTimes = GetStandardDeviationAndMean(subTickTimes);
  200. state.counters["SubTick-Mean"] = aznumeric_cast<double>(aznumeric_cast<int64_t>(stdivMeanSubTickTimes.m_mean * 1000.0)) / 1000.0;
  201. state.counters["SubTick-StDev"] = aznumeric_cast<double>(aznumeric_cast<int64_t>(stdivMeanSubTickTimes.m_standardDeviation * 1000.0)) / 1000.0;
  202. }
  203. } // namespace Utils
  204. } // namespace PhysX::Benchmarks