/* * Copyright (c) Contributors to the Open 3D Engine Project * * SPDX-License-Identifier: Apache-2.0 OR MIT * */ #include #include #include #include #include #include #include namespace PhysX::Benchmarks { namespace Utils { AzPhysics::SimulatedBodyHandleList CreateRigidBodies(int numRigidBodies, AzPhysics::Scene* scene, bool enableCCD, GenerateColliderFuncPtr* genColliderFuncPtr /*= nullptr*/, GenerateSpawnPositionFuncPtr* genSpawnPosFuncPtr /*= nullptr*/, GenerateSpawnOrientationFuncPtr* genSpawnOriFuncPtr /*= nullptr*/, GenerateMassFuncPtr* genMassFuncPtr /*= nullptr*/, GenerateEntityIdFuncPtr* genEntityIdFuncPtr /*= nullptr*/ ) { AzPhysics::SimulatedBodyHandleList rigidBodies; rigidBodies.reserve(numRigidBodies); AzPhysics::RigidBodyConfiguration rigidBodyConfig; rigidBodyConfig.m_ccdEnabled = enableCCD; auto rigidBodyColliderConfig = AZStd::make_shared(); auto defaultShapeConfiguration = AZStd::make_shared(AZ::Vector3::CreateOne()); for (int i = 0; i < numRigidBodies; i++) { //call the optional function pointers, otherwise assign a default if (genEntityIdFuncPtr != nullptr) { rigidBodyConfig.m_entityId = (*genEntityIdFuncPtr)(i); } if (genMassFuncPtr != nullptr) { rigidBodyConfig.m_mass = (*genMassFuncPtr)(i); } if (genSpawnPosFuncPtr != nullptr) { rigidBodyConfig.m_position = (*genSpawnPosFuncPtr)(i); } if (genSpawnOriFuncPtr != nullptr) { rigidBodyConfig.m_orientation = (*genSpawnOriFuncPtr)(i); } AZStd::shared_ptr shapeConfig = nullptr; if (genColliderFuncPtr != nullptr) { shapeConfig = (*genColliderFuncPtr)(i); } if (shapeConfig == nullptr) { shapeConfig = defaultShapeConfiguration; } rigidBodyConfig.m_colliderAndShapeData = AzPhysics::ShapeColliderPair(rigidBodyColliderConfig, shapeConfig); AzPhysics::SimulatedBodyHandle simBodyHandle = scene->AddSimulatedBody(&rigidBodyConfig); rigidBodies.push_back(simBodyHandle); } return rigidBodies; } AZStd::vector GetRigidBodiesFromHandles(AzPhysics::Scene* scene, const AzPhysics::SimulatedBodyHandleList& handlesList) { AZStd::vector rigidBodies; rigidBodies.reserve(handlesList.size()); for (auto handle : handlesList) { rigidBodies.push_back(azdynamic_cast(scene->GetSimulatedBodyFromHandle(handle))); } return rigidBodies; } PrePostSimulationEventHandler::PrePostSimulationEventHandler() : m_sceneStartSimHandler([this]( [[maybe_unused]] AzPhysics::SceneHandle sceneHandle, [[maybe_unused]] float fixedDeltaTime) { this->PreTick(); }) , m_sceneFinishSimHandler([this]( [[maybe_unused]] AzPhysics::SceneHandle sceneHandle, [[maybe_unused]] float fixedDeltatime) { this->PostTick(); }) { } void PrePostSimulationEventHandler::Start(AzPhysics::Scene* scene) { m_subTickTimes.clear(); scene->RegisterSceneSimulationStartHandler(m_sceneStartSimHandler); scene->RegisterSceneSimulationFinishHandler(m_sceneFinishSimHandler); } void PrePostSimulationEventHandler::Stop() { m_sceneStartSimHandler.Disconnect(); m_sceneFinishSimHandler.Disconnect(); } void PrePostSimulationEventHandler::PreTick() { m_tickStart = AZStd::chrono::system_clock::now(); } void PrePostSimulationEventHandler::PostTick() { auto tickElapsedMilliseconds = Types::double_milliseconds(AZStd::chrono::system_clock::now() - m_tickStart); m_subTickTimes.emplace_back(tickElapsedMilliseconds.count()); } void ReportFramePercentileCounters(benchmark::State& state, Types::TimeList& frameTimes, Types::TimeList& subTickTimes, const AZStd::vector& requestedPercentiles /*= { {0.5, 0.9, 0.99} }*/) { AZStd::vector framePercentiles = GetPercentiles(requestedPercentiles, frameTimes); AZStd::vector subTickPercentiles = GetPercentiles(requestedPercentiles, subTickTimes); //Report the percentiles, slowest and fastest frame of the run int minRange = static_cast(AZStd::min(requestedPercentiles.size(), framePercentiles.size())); for (int i = 0; i < minRange; i++) { AZStd::string label = AZStd::string::format("Frame-P%d", static_cast(requestedPercentiles[i] * 100.0)); state.counters[label.c_str()] = framePercentiles[i]; } //add fastest and slowest frame time, if it doesn't exist report -1.0 (negative time is impossible, so this denotes an error). std::nth_element(frameTimes.begin(), frameTimes.begin(), frameTimes.end()); state.counters["Frame-Fastest"] = !frameTimes.empty() ? frameTimes.front() : -1.0; std::nth_element(frameTimes.begin(), frameTimes.begin() + (frameTimes.size() - 1), frameTimes.end()); state.counters["Frame-Slowest"] = !frameTimes.empty() ? frameTimes.back() : -1.0; //Report the percentiles, slowest and fastest sub tick of the run if (subTickTimes.empty()) { return; } minRange = static_cast(AZStd::min(requestedPercentiles.size(), subTickPercentiles.size())); for (int i = 0; i < minRange; i++) { AZStd::string label = AZStd::string::format("SubTick-P%d", static_cast(requestedPercentiles[i] * 100.0)); state.counters[label.c_str()] = subTickPercentiles[i]; } //add fastest and slowest frame time, if it doesn't exist report -1.0 (negative time is impossible, so this denotes an error). std::nth_element(subTickTimes.begin(), subTickTimes.begin(), subTickTimes.end()); state.counters["SubTick-Fastest"] = !subTickTimes.empty() ? subTickTimes.front() : -1.0; std::nth_element(subTickTimes.begin(), subTickTimes.begin() + (subTickTimes.size()-1), subTickTimes.end()); state.counters["SubTick-Slowest"] = !subTickTimes.empty() ? subTickTimes.back() : -1.0; } void ReportFrameStandardDeviationAndMeanCounters(benchmark::State& state, const Types::TimeList& frameTimes, const Types::TimeList& subTickTimes) { StandardDeviationAndMeanResults stdivMeanFrameTimes = GetStandardDeviationAndMean(frameTimes); state.counters["Frame-Mean"] = aznumeric_cast(aznumeric_cast(stdivMeanFrameTimes.m_mean * 1000.0)) / 1000.0; //truncate to 3 decimal places state.counters["Frame-StDev"] = aznumeric_cast(aznumeric_cast(stdivMeanFrameTimes.m_standardDeviation * 1000.0)) / 1000.0; StandardDeviationAndMeanResults stdivMeanSubTickTimes = GetStandardDeviationAndMean(subTickTimes); state.counters["SubTick-Mean"] = aznumeric_cast(aznumeric_cast(stdivMeanSubTickTimes.m_mean * 1000.0)) / 1000.0; state.counters["SubTick-StDev"] = aznumeric_cast(aznumeric_cast(stdivMeanSubTickTimes.m_standardDeviation * 1000.0)) / 1000.0; } } // namespace Utils } // namespace PhysX::Benchmarks