3
0

SamplePerformanceTests.cpp 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784
  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 <Tests/Game/SampleGameFixture.h>
  9. #include <AzCore/Debug/Timer.h>
  10. #include <AzCore/Math/Random.h>
  11. #include <AzCore/Asset/AssetManager.h>
  12. #include <EMotionFX/Source/Actor.h>
  13. #include <EMotionFX/Source/ActorManager.h>
  14. #include <EMotionFX/Source/AnimGraph.h>
  15. #include <EMotionFX/Source/AnimGraphMotionNode.h>
  16. #include <EMotionFX/Source/AnimGraphStateMachine.h>
  17. #include <EMotionFX/Source/AttachmentSkin.h>
  18. #include <EMotionFX/Source/EMotionFXManager.h>
  19. #include <EMotionFX/Source/MultiThreadScheduler.h>
  20. #include <EMotionFX/Source/MotionSet.h>
  21. #include <EMotionFX/Source/MotionInstance.h>
  22. #include <EMotionFX/Source/MotionInstancePool.h>
  23. #include <EMotionFX/Source/Importer/Importer.h>
  24. #include <EMotionFX/Source/SingleThreadScheduler.h>
  25. #include <EMotionFX/Source/Motion.h>
  26. #include <EMotionFX/Source/Parameter/BoolParameter.h>
  27. #include <EMotionFX/Source/Parameter/IntParameter.h>
  28. #include <EMotionFX/Source/Parameter/FloatParameter.h>
  29. #include <EMotionFX/Source/Parameter/Vector2Parameter.h>
  30. #include <EMotionFX/Source/Parameter/Vector3Parameter.h>
  31. #include <AzFramework/Physics/Utils.h>
  32. #include <AzFramework/IO/LocalFileIO.h>
  33. namespace EMotionFX
  34. {
  35. struct SampleParameterSet
  36. {
  37. std::vector<std::tuple<std::string, AZ::Vector2>> m_vec2Params;
  38. std::vector<std::pair<std::string, bool>> m_boolParams;
  39. std::vector<std::pair<std::string, float>> m_floatParams;
  40. };
  41. struct PerformanceTestParameters
  42. {
  43. const char* m_description;
  44. float m_fps;
  45. float m_motionSamplingRate;
  46. float m_totalTestTimeInSeconds;
  47. size_t m_numInstances;
  48. size_t m_numSkinAttachmentsPerInstance;
  49. AZ::u32 m_lodLevel;
  50. bool m_includeSoftwareSkinning;
  51. bool m_useMultiThreading;
  52. void Print() const
  53. {
  54. printf("-------------------------------\n");
  55. printf("- Performance Test Parameters\n");
  56. printf("- Description: %s\n", m_description);
  57. printf("- FPS: %f\n", m_fps);
  58. printf("- Motion Sampling Rate: %f\n", m_motionSamplingRate);
  59. printf("- Total Time (s): %f\n", m_totalTestTimeInSeconds);
  60. printf("- Frames: %d\n", static_cast<int>(m_totalTestTimeInSeconds * m_fps));
  61. printf("- Num Instances: %zd\n", m_numInstances);
  62. printf("- Num Skin Attachents per Instance: %zd\n", m_numSkinAttachmentsPerInstance);
  63. printf("- LOD Level: %d\n", m_lodLevel);
  64. printf("- Software Skin: %s\n", m_includeSoftwareSkinning ? "True" : "False");
  65. printf("- Multi-threading:%s\n", m_useMultiThreading ? "True" : "False");
  66. }
  67. };
  68. class PerformanceTestFixture
  69. : public SampleGameFixture
  70. , public ::testing::WithParamInterface<PerformanceTestParameters>
  71. {
  72. public:
  73. void SetUp() override
  74. {
  75. SampleGameFixture::SetUp();
  76. m_random = new AZ::SimpleLcgRandom();
  77. m_random->SetSeed(875960);
  78. SampleParameterSet params;
  79. params.m_floatParams.push_back({ "movement_speed", 0.0f });
  80. params.m_vec2Params.push_back({ "movement_direction", AZ::Vector2(0.0f, 0.0f) });
  81. params.m_boolParams.push_back({ "jumping", false });
  82. params.m_boolParams.push_back({ "attacking", true });
  83. m_pyroParameterSet.emplace_back(params);
  84. params.m_floatParams.push_back({ "movement_speed", 1.0f });
  85. params.m_vec2Params.push_back({ "movement_direction", AZ::Vector2(1.0f, 0.0f) });
  86. params.m_boolParams.push_back({ "jumping", false });
  87. params.m_boolParams.push_back({ "attacking", false });
  88. m_pyroParameterSet.emplace_back(params);
  89. params.m_floatParams.push_back({ "movement_speed", 0.5f });
  90. params.m_vec2Params.push_back({ "movement_direction", AZ::Vector2(0.5f, 0.5f) });
  91. params.m_boolParams.push_back({ "jumping", false });
  92. params.m_boolParams.push_back({ "attacking", false });
  93. m_pyroParameterSet.emplace_back(params);
  94. params.m_floatParams.push_back({ "movement_speed", 0.0f });
  95. params.m_vec2Params.push_back({ "movement_direction", AZ::Vector2(0.0f, 0.0f) });
  96. params.m_boolParams.push_back({ "jumping", true });
  97. params.m_boolParams.push_back({ "attacking", false });
  98. m_pyroParameterSet.emplace_back(params);
  99. }
  100. void TearDown() override
  101. {
  102. delete m_random;
  103. SampleGameFixture::TearDown();
  104. }
  105. float RandomRange(float min, float max) const
  106. {
  107. return min + m_random->GetRandomFloat() * (max - min);
  108. }
  109. AZ::Vector3 RandomRangeVec3(float min, float max) const
  110. {
  111. return AZ::Vector3(
  112. RandomRange(min, max),
  113. RandomRange(min, max),
  114. RandomRange(min, max));
  115. }
  116. void RandomizeParameters(const AZStd::vector<ActorInstance*>& actorInstances)
  117. {
  118. for (ActorInstance* actorInstance : actorInstances)
  119. {
  120. AnimGraphInstance* animGraphInstance = actorInstance->GetAnimGraphInstance();
  121. AZ_Assert(animGraphInstance, "Actor instance should have an anim graph instance playing.");
  122. const AnimGraph* animGraph = animGraphInstance->GetAnimGraph();
  123. const size_t parameterSetIndex = static_cast<size_t>(RandomRange(0.0f, static_cast<float>(m_pyroParameterSet.size())-0.0001f));
  124. const SampleParameterSet& params = m_pyroParameterSet[parameterSetIndex];
  125. // Set parameters to default values.
  126. const size_t numParameters = animGraph->GetNumValueParameters();
  127. for (size_t i = 0; i < numParameters; ++i)
  128. {
  129. const ValueParameter* parameter = animGraph->FindValueParameter(i);
  130. MCore::Attribute* attribute = animGraphInstance->GetParameterValue(static_cast<AZ::u32>(i));
  131. if (parameter->RTTI_GetType() == azrtti_typeid<BoolParameter>())
  132. {
  133. const BoolParameter* boolParameter = static_cast<const BoolParameter*>(parameter);
  134. MCore::AttributeBool* boolAttribute = static_cast<MCore::AttributeBool*>(attribute);
  135. boolAttribute->SetValue(boolParameter->GetDefaultValue());
  136. }
  137. if (parameter->RTTI_GetType() == azrtti_typeid<FloatParameter>())
  138. {
  139. const FloatParameter* floatParameter = static_cast<const FloatParameter*>(parameter);
  140. MCore::AttributeFloat* floatAttribute = static_cast<MCore::AttributeFloat*>(attribute);
  141. floatAttribute->SetValue(floatParameter->GetDefaultValue());
  142. }
  143. if (parameter->RTTI_GetType() == azrtti_typeid<Vector2Parameter>())
  144. {
  145. const Vector2Parameter* vec2Parameter = static_cast<const Vector2Parameter*>(parameter);
  146. MCore::AttributeVector2* vec2Attribute = static_cast<MCore::AttributeVector2*>(attribute);
  147. vec2Attribute->SetValue(vec2Parameter->GetDefaultValue());
  148. }
  149. }
  150. // Bool parameters
  151. for (const auto& boolParamPair : params.m_boolParams)
  152. {
  153. const AZ::Outcome<size_t> parameterIndex = animGraph->FindValueParameterIndexByName(boolParamPair.first.c_str());
  154. ASSERT_TRUE(parameterIndex.IsSuccess());
  155. const ValueParameter* parameter = animGraph->FindValueParameter(parameterIndex.GetValue());
  156. ASSERT_NE(parameter, nullptr);
  157. ASSERT_EQ(parameter->GetName(), boolParamPair.first.c_str());
  158. MCore::Attribute* attribute = animGraphInstance->GetParameterValue(static_cast<AZ::u32>(parameterIndex.GetValue()));
  159. MCore::AttributeBool* boolAttribute = static_cast<MCore::AttributeBool*>(attribute);
  160. boolAttribute->SetValue(boolParamPair.second);
  161. }
  162. // Vec2 parameters
  163. for (const auto& vec2ParamTuple : params.m_vec2Params)
  164. {
  165. const AZ::Outcome<size_t> parameterIndex = animGraph->FindValueParameterIndexByName(std::get<0>(vec2ParamTuple).c_str());
  166. ASSERT_TRUE(parameterIndex.IsSuccess());
  167. const ValueParameter* parameter = animGraph->FindValueParameter(parameterIndex.GetValue());
  168. ASSERT_NE(parameter, nullptr);
  169. ASSERT_EQ(parameter->GetName(), std::get<0>(vec2ParamTuple).c_str());
  170. MCore::Attribute* attribute = animGraphInstance->GetParameterValue(static_cast<AZ::u32>(parameterIndex.GetValue()));
  171. MCore::AttributeVector2* vec2Attribute = static_cast<MCore::AttributeVector2*>(attribute);
  172. vec2Attribute->SetValue(std::get<1>(vec2ParamTuple));
  173. }
  174. // Float parameters
  175. for (const auto& floatParamPair : params.m_floatParams)
  176. {
  177. const AZ::Outcome<size_t> parameterIndex = animGraph->FindValueParameterIndexByName(floatParamPair.first.c_str());
  178. ASSERT_TRUE(parameterIndex.IsSuccess());
  179. const ValueParameter* parameter = animGraph->FindValueParameter(parameterIndex.GetValue());
  180. ASSERT_NE(parameter, nullptr);
  181. ASSERT_EQ(parameter->GetName(), floatParamPair.first.c_str());
  182. MCore::Attribute* attribute = animGraphInstance->GetParameterValue(static_cast<AZ::u32>(parameterIndex.GetValue()));
  183. MCore::AttributeFloat* floatAttribute = static_cast<MCore::AttributeFloat*>(attribute);
  184. floatAttribute->SetValue(floatParamPair.second);
  185. }
  186. }
  187. }
  188. static AZStd::tuple<float, float, float, float> CalculateStats(const AZStd::vector<float>& samples)
  189. {
  190. const size_t numSamples = samples.size();
  191. if (numSamples == 0)
  192. {
  193. return { 0.0f, 0.0f, 0.0f, 0.0f };
  194. }
  195. float best = samples[0];
  196. float worst = samples[0];
  197. float accumulated = 0.0f;
  198. for (const float sample : samples)
  199. {
  200. best = AZ::GetMin<float>(best, sample);
  201. worst = AZ::GetMax<float>(worst, sample);
  202. accumulated += sample;
  203. }
  204. const float mean = accumulated / static_cast<float>(numSamples);
  205. float variance = 0.0f;
  206. for (const float sample : samples)
  207. {
  208. variance += powf(sample - mean, 2.0f);
  209. }
  210. variance = variance / static_cast<float>(numSamples);
  211. const float stdDeviation = sqrtf(variance);
  212. return { best, mean, worst, stdDeviation };
  213. }
  214. void PrintReport(const AZStd::vector<float>& transformUpdateFrameTimes,
  215. const AZStd::vector<float>& meshDeformFrameTimes,
  216. float totalTransformUpdateTime,
  217. float totalMeshDeformTime)
  218. {
  219. const PerformanceTestParameters& param = GetParam();
  220. // Totals
  221. printf("----------------------------------------------------\n");
  222. printf("- Performance Test Report -\n");
  223. if (param.m_includeSoftwareSkinning)
  224. {
  225. printf("- Totals:\n");
  226. printf(" Total Time (s): %.4f s\n", totalTransformUpdateTime + totalMeshDeformTime);
  227. printf(" Total Transform Update Time (s): %.4f s\n", totalTransformUpdateTime);
  228. printf(" Total Mesh Deform Time (s): %.4f s\n", totalMeshDeformTime);
  229. printf(" Transform Mesh Ratio: %.4f %%\n", totalTransformUpdateTime / totalMeshDeformTime);
  230. }
  231. else
  232. {
  233. printf("- Total Time (s): %.4f s\n", totalTransformUpdateTime + totalMeshDeformTime);
  234. }
  235. // Transform update
  236. float transformBest;
  237. float transformMean;
  238. float transformWorst;
  239. float transformStdDeviation;
  240. AZStd::tie(transformBest, transformMean, transformWorst, transformStdDeviation) = CalculateStats(transformUpdateFrameTimes);
  241. printf("- Transform update:\n");
  242. printf(" Best Frame: %.4f ms (%.1f FPS)\n", transformBest * 1000.0f, transformBest > 0.0f ? 1.0f / transformBest : 0.0f);
  243. printf(" Mean Frame: %.4f ms (%.1f FPS)\n", transformMean * 1000.0f, transformMean > 0.0f ? 1.0f / transformMean : 0.0f);
  244. printf(" Worst Frame: %.4f ms (%.1f FPS)\n", transformWorst * 1000.0f, transformWorst > 0.0f ? 1.0f / transformWorst : 0.0f);
  245. printf(" Std Deviation: %.4f ms\n", transformStdDeviation * 1000.0f);
  246. // Mesh deforms
  247. if (param.m_includeSoftwareSkinning)
  248. {
  249. float meshDeformBest;
  250. float meshDeformMean;
  251. float meshDeformWorst;
  252. float meshDeformStdDeviation;
  253. AZStd::tie(meshDeformBest, meshDeformMean, meshDeformWorst, meshDeformStdDeviation) = CalculateStats(meshDeformFrameTimes);
  254. printf("- Mesh deforms:\n");
  255. printf(" Best Frame: %.4f ms\n", meshDeformBest * 1000.0f);
  256. printf(" Mean Frame: %.4f ms\n", meshDeformMean * 1000.0f);
  257. printf(" Worst Frame: %.4f ms\n", meshDeformWorst * 1000.0f);
  258. printf(" Std Deviation: %.4f ms\n", meshDeformStdDeviation * 1000.0f);
  259. }
  260. printf("----------------------------------------------------\n");
  261. }
  262. void TestMotionSamplingPerformance(const char* motionFilename)
  263. {
  264. const AZStd::string assetFolder = GetAssetFolder();
  265. GetEMotionFX().SetMediaRootFolder(assetFolder.c_str());
  266. GetEMotionFX().InitAssetFolderPaths();
  267. const char* actorFilename = "@products@\\animationsamples\\advanced_rinlocomotion\\actor\\rinactor.actor";
  268. Importer* importer = GetEMotionFX().GetImporter();
  269. importer->SetLoggingEnabled(false);
  270. const AZStd::string resolvedActorFilename = ResolvePath(actorFilename);
  271. EXPECT_TRUE(AZ::IO::LocalFileIO::GetInstance()->Exists(resolvedActorFilename.c_str()))
  272. << AZStd::string::format("Actor file '%s' does not exist on local hard drive.", resolvedActorFilename.c_str()).c_str();
  273. AZStd::unique_ptr<Actor> actor = importer->LoadActor(resolvedActorFilename);
  274. ASSERT_NE(actor, nullptr) << "Actor failed to load.";
  275. Motion* motion = importer->LoadMotion(ResolvePath(motionFilename));
  276. ASSERT_NE(motion, nullptr) << "Motion failed to load.";
  277. ActorInstance* actorInstance = ActorInstance::Create(actor.get());
  278. const Pose* bindPose = actor->GetBindPose();
  279. Pose outPose;
  280. outPose.InitFromBindPose(actorInstance);
  281. MotionInstance* motionInstance = GetMotionInstancePool().RequestNew(motion, actorInstance);
  282. const size_t numSamples = 100000;
  283. AZ::Debug::Timer timer;
  284. // Sample the same time value.
  285. timer.Stamp();
  286. for (size_t i = 0; i < numSamples; ++i)
  287. {
  288. motionInstance->SetCurrentTimeNormalized(0.33f);
  289. motion->Update(bindPose, &outPose, motionInstance);
  290. }
  291. float activationTime = timer.GetDeltaTimeInSeconds();
  292. printf("Sampling same frame = %.2f ms\n", activationTime * 1000.0f);
  293. // Sample random time values.
  294. timer.Stamp();
  295. for (size_t i = 0; i < numSamples; ++i)
  296. {
  297. motionInstance->SetCurrentTimeNormalized(rand() / static_cast<float>(RAND_MAX));
  298. motion->Update(bindPose, &outPose, motionInstance);
  299. }
  300. activationTime = timer.GetDeltaTimeInSeconds();
  301. printf("Sampling random frame = %.2f ms\n", activationTime * 1000.0f);
  302. // Sample forward.
  303. timer.Stamp();
  304. for (size_t i = 0; i < numSamples; ++i)
  305. {
  306. motionInstance->SetCurrentTimeNormalized(i / static_cast<float>(numSamples));
  307. motion->Update(bindPose, &outPose, motionInstance);
  308. }
  309. activationTime = timer.GetDeltaTimeInSeconds();
  310. printf("Sampling forward sequential = %.2f ms\n", activationTime * 1000.0f);
  311. // Sample backward.
  312. timer.Stamp();
  313. for (size_t i = 0; i < numSamples; ++i)
  314. {
  315. motionInstance->SetCurrentTimeNormalized(1.0f - (i / static_cast<float>(numSamples)));
  316. motion->Update(bindPose, &outPose, motionInstance);
  317. }
  318. activationTime = timer.GetDeltaTimeInSeconds();
  319. printf("Sampling backward sequential = %.2f ms\n", activationTime * 1000.0f);
  320. GetMotionInstancePool().Free(motionInstance);
  321. actorInstance->Destroy();
  322. motion->Destroy();
  323. }
  324. private:
  325. AZ::SimpleLcgRandom* m_random;
  326. std::vector<SampleParameterSet> m_pyroParameterSet;
  327. };
  328. TEST_P(PerformanceTestFixture, DISABLED_PerformanceTest)
  329. {
  330. const PerformanceTestParameters& param = GetParam();
  331. const size_t numIterations = static_cast<size_t>(param.m_totalTestTimeInSeconds * param.m_fps);
  332. const float frameTimeDelta = 1.0f / param.m_fps;
  333. param.Print();
  334. ActorManager* actorManager = GetEMotionFX().GetActorManager();
  335. ActorUpdateScheduler* scheduler = nullptr;
  336. if (param.m_useMultiThreading)
  337. {
  338. scheduler = MultiThreadScheduler::Create();
  339. }
  340. else
  341. {
  342. scheduler = SingleThreadScheduler::Create();
  343. }
  344. actorManager->SetScheduler(scheduler);
  345. const AZStd::string assetFolder = GetAssetFolder();
  346. GetEMotionFX().SetMediaRootFolder(assetFolder.c_str());
  347. GetEMotionFX().InitAssetFolderPaths();
  348. // This path points to assets in the advance rin demo.
  349. // To test different assets, change the path here.
  350. const char* actorFilename = "@products@\\AnimationSamples\\Advanced_RinLocomotion\\Actor\\rinActor.actor";
  351. const char* motionSetFilename = "@products@\\AnimationSamples\\Advanced_RinLocomotion\\AnimationEditorFiles\\Advanced_RinLocomotion.motionset";
  352. const char* animGraphFilename = "@products@\\AnimationSamples\\Advanced_RinLocomotion\\AnimationEditorFiles\\Advanced_RinLocomotion.animgraph";
  353. Importer* importer = GetEMotionFX().GetImporter();
  354. importer->SetLoggingEnabled(false);
  355. AZStd::unique_ptr<Actor> actor = importer->LoadActor(ResolvePath(actorFilename));
  356. ASSERT_NE(actor, nullptr) << "Actor failed to load.";
  357. MotionSet* motionSet = importer->LoadMotionSet(ResolvePath(motionSetFilename));
  358. ASSERT_NE(motionSet, nullptr) << "Motion set failed to load.";
  359. AnimGraph* animGraph = importer->LoadAnimGraph(ResolvePath(animGraphFilename));
  360. ASSERT_NE(animGraph, nullptr) << "Anim graph failed to load.";
  361. // Create instances and start running the anim graphs.
  362. AZStd::vector<ActorInstance*> actorInstances;
  363. AZStd::vector<ActorInstance*> actorInstancesIncludingAttachments;
  364. actorInstances.reserve(param.m_numInstances);
  365. for (size_t i = 0; i < param.m_numInstances; ++i)
  366. {
  367. ActorInstance* actorInstance = ActorInstance::Create(actor.get());
  368. if (!AZ::IsClose(param.m_motionSamplingRate, 0.0f, AZ::Constants::FloatEpsilon))
  369. {
  370. actorInstance->SetMotionSamplingRate(1.0f / param.m_motionSamplingRate);
  371. }
  372. actorInstance->SetLocalSpacePosition(RandomRangeVec3(-100.0f, 100.0f));
  373. actorInstances.emplace_back(actorInstance);
  374. actorInstancesIncludingAttachments.emplace_back(actorInstance);
  375. AnimGraphInstance* animGraphInstance = AnimGraphInstance::Create(animGraph, actorInstance, motionSet);
  376. actorInstance->SetAnimGraphInstance(animGraphInstance);
  377. actorInstance->SetLODLevel(param.m_lodLevel);
  378. actorInstance->UpdateTransformations(0.0f);
  379. // Add skin attachments.
  380. for (size_t attachmentNr = 0; attachmentNr < param.m_numSkinAttachmentsPerInstance; ++attachmentNr)
  381. {
  382. ActorInstance* attachmentActorInstance = ActorInstance::Create(actor.get());
  383. EMotionFX::Attachment* attachment = EMotionFX::AttachmentSkin::Create(/*attachmentTarget*/actorInstance, attachmentActorInstance);
  384. actorInstance->AddAttachment(attachment);
  385. actorInstancesIncludingAttachments.emplace_back(attachmentActorInstance);
  386. }
  387. }
  388. // Preload motions and make sure they got loaded successfully.
  389. motionSet->Preload();
  390. const auto& motionEntries = motionSet->GetMotionEntries();
  391. for (const auto& motionEntryPair : motionEntries)
  392. {
  393. const MotionSet::MotionEntry* motionEntry = motionEntryPair.second;
  394. ASSERT_NE(motionEntry->GetMotion(), nullptr);
  395. }
  396. AZ::Debug::Timer timer;
  397. AZStd::vector<float> transformUpdateFrameTimes;
  398. AZStd::vector<float> meshDeformFrameTimes;
  399. transformUpdateFrameTimes.reserve(numIterations);
  400. meshDeformFrameTimes.reserve(numIterations);
  401. float totalTransformUpdateTime = 0.0f;
  402. float totalMeshDeformTime = 0.0f;
  403. const float randomizeParametersEvery = 1.0; // Change parameters every second
  404. float randomizeParameterTimer = 0.0f;
  405. for (size_t i = 0; i < numIterations; ++i)
  406. {
  407. randomizeParameterTimer += frameTimeDelta;
  408. if (randomizeParameterTimer >= randomizeParametersEvery)
  409. {
  410. RandomizeParameters(actorInstances);
  411. randomizeParameterTimer = 0.0;
  412. }
  413. // Output skeletal poses.
  414. timer.Stamp();
  415. GetEMotionFX().Update(frameTimeDelta);
  416. const float transformUpdateTime = timer.GetDeltaTimeInSeconds();
  417. totalTransformUpdateTime += transformUpdateTime;
  418. transformUpdateFrameTimes.emplace_back(transformUpdateTime);
  419. // Update mesh deformers (software skinning).
  420. if (param.m_includeSoftwareSkinning)
  421. {
  422. timer.Stamp();
  423. for (ActorInstance* actorInstance : actorInstancesIncludingAttachments)
  424. {
  425. actorInstance->UpdateMeshDeformers(frameTimeDelta);
  426. }
  427. const float meshDeformTime = timer.GetDeltaTimeInSeconds();
  428. totalMeshDeformTime += meshDeformTime;
  429. meshDeformFrameTimes.emplace_back(meshDeformTime);
  430. }
  431. }
  432. PrintReport(transformUpdateFrameTimes, meshDeformFrameTimes, totalTransformUpdateTime, totalMeshDeformTime);
  433. for (ActorInstance* actorInstance : actorInstancesIncludingAttachments)
  434. {
  435. actorInstance->Destroy();
  436. }
  437. delete animGraph;
  438. delete motionSet;
  439. }
  440. const float g_updatesPerSec = 60.0f;
  441. const float g_totalTestTime = 60.0f; // 1 minute
  442. const size_t g_numInstances = 100;
  443. std::vector<PerformanceTestParameters> performanceTestData
  444. {
  445. // Baseline
  446. {
  447. "Baseline",
  448. g_updatesPerSec, // fps
  449. 0.0f, // motion sampling rate
  450. g_totalTestTime, // total test time
  451. g_numInstances, // instances
  452. 0, // num skin attachments per instance
  453. 0, // lod level
  454. false, // software skinning
  455. false // multi-threading
  456. },
  457. // Multi-threading
  458. {
  459. "Multi-threading",
  460. g_updatesPerSec, // fps
  461. 0.0f, // motion sampling rate
  462. g_totalTestTime, // total test time
  463. g_numInstances, // instances
  464. 0, // num skin attachments per instance
  465. 0, // lod level
  466. false, // software skinning
  467. true // multi-threading
  468. },
  469. // Multi-threading with 1 skin attachments
  470. {
  471. "Multi-threading with 1 skin attachments",
  472. g_updatesPerSec, // fps
  473. 0.0f, // motion sampling rate
  474. g_totalTestTime, // total test time
  475. g_numInstances, // instances
  476. 1, // num skin attachments per instance
  477. 0, // lod level
  478. false, // software skinning
  479. true // multi-threading
  480. },
  481. // Multi-threading and restricted motion sampling rate
  482. {
  483. "Multi-threading and restricted motion sampling rate",
  484. g_updatesPerSec, // fps
  485. 60.0f, // motion sampling rate
  486. g_totalTestTime, // total test time
  487. g_numInstances, // instances
  488. 0, // num skin attachments per instance
  489. 0, // lod level
  490. false, // software skinning
  491. true // multi-threading
  492. },
  493. // Multi-threading and lower motion sampling rate
  494. {
  495. "Multi-threading and lower motion sampling rate",
  496. g_updatesPerSec, // fps
  497. 30.0f, // motion sampling rate
  498. g_totalTestTime, // total test time
  499. g_numInstances, // instances
  500. 0, // num skin attachments per instance
  501. 0, // lod level
  502. false, // software skinning
  503. true // multi-threading
  504. },
  505. // Multi-threading and lower motion sampling rate
  506. {
  507. "Multi-threading and lower motion sampling rate",
  508. g_updatesPerSec, // fps
  509. 10.0f, // motion sampling rate
  510. g_totalTestTime, // total test time
  511. g_numInstances, // instances
  512. 0, // num skin attachments per instance
  513. 0, // lod level
  514. false, // software skinning
  515. true // multi-threading
  516. },
  517. // Multi-threading at LOD level = 1
  518. {
  519. "Multi-threading at LOD level = 1",
  520. g_updatesPerSec, // fps
  521. 0.0f, // motion sampling rate
  522. g_totalTestTime, // total test time
  523. g_numInstances, // instances
  524. 0, // num skin attachments per instance
  525. 1, // lod level
  526. false, // software skinning
  527. true // multi-threading
  528. },
  529. // Multi-threading at LOD level = 2
  530. {
  531. "Multi-threading at LOD level = 2",
  532. g_updatesPerSec, // fps
  533. 0.0f, // motion sampling rate
  534. g_totalTestTime, // total test time
  535. g_numInstances, // instances
  536. 0, // num skin attachments per instance
  537. 2, // lod level
  538. false, // software skinning
  539. true // multi-threading
  540. },
  541. // Multi-threading at LOD level = 3
  542. {
  543. "Multi-threading at LOD level = 3",
  544. g_updatesPerSec, // fps
  545. 0.0f, // motion sampling rate
  546. g_totalTestTime, // total test time
  547. g_numInstances, // instances
  548. 0, // num skin attachments per instance
  549. 3, // lod level
  550. false, // software skinning
  551. true // multi-threading
  552. },
  553. // Multi-threading at LOD level = 4
  554. {
  555. "Multi-threading at LOD level = 4",
  556. g_updatesPerSec, // fps
  557. 0.0f, // motion sampling rate
  558. g_totalTestTime, // total test time
  559. g_numInstances, // instances
  560. 0, // num skin attachments per instance
  561. 4, // lod level
  562. false, // software skinning
  563. true // multi-threading
  564. },
  565. // Server test: LOD0
  566. {
  567. "Server test: LOD0",
  568. 30.0f, // fps
  569. 0.0f, // motion sampling rate
  570. g_totalTestTime, // total test time
  571. g_numInstances, // instances
  572. 0, // num skin attachments per instance
  573. 0, // lod level
  574. false, // software skinning
  575. false // multi-threading
  576. },
  577. // Server test: LOD4
  578. {
  579. "Server test: LOD4",
  580. 30.0f, // fps
  581. 0.0f, // motion sampling rate
  582. g_totalTestTime, // total test time
  583. g_numInstances, // instances
  584. 0, // num skin attachments per instance
  585. 4, // lod level
  586. false, // software skinning
  587. false // multi-threading
  588. },
  589. // Server test: Server actor optimization (bone removal).
  590. {
  591. "Server test: Server actor optimization (bone removal)",
  592. 30.0f, // fps
  593. 0.0f, // motion sampling rate
  594. g_totalTestTime, // total test time
  595. g_numInstances, // instances
  596. 0, // num skin attachments per instance
  597. 0, // lod level
  598. false, // software skinning
  599. false // multi-threading
  600. }/*,
  601. // Software skinning (this is extremely slow and will take a long time to run)
  602. {
  603. "Software skinning",
  604. g_updatesPerSec, // fps
  605. 0.0f, // motion sampling rate
  606. g_totalTestTime, // total test time
  607. g_numInstances, // instances
  608. 0, // num skin attachments per instance
  609. 0, // lod level
  610. true, // software skinning
  611. false // multi-threading
  612. }*/
  613. };
  614. std::vector<PerformanceTestParameters> debugTestData
  615. {
  616. {
  617. "debug",
  618. g_updatesPerSec, // fps
  619. 60.0f, // motion sampling rate
  620. g_totalTestTime, // total test time
  621. 1, // instances
  622. 0, // num skin attachments per instance
  623. 0, // lod level
  624. false, // software skinning
  625. true // multi-threading
  626. }
  627. };
  628. INSTANTIATE_TEST_CASE_P(PerformanceTests,
  629. PerformanceTestFixture,
  630. ::testing::ValuesIn(performanceTestData));
  631. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  632. TEST_F(PerformanceTestFixture, DISABLED_DeferredInitPerformanceTest)
  633. {
  634. const AZStd::string assetFolder = GetAssetFolder();
  635. GetEMotionFX().SetMediaRootFolder(assetFolder.c_str());
  636. GetEMotionFX().InitAssetFolderPaths();
  637. // This path points to assets in the advance rin demo.
  638. // To test different assets, change the path here.
  639. const char* actorFilename = "@products@\\AnimationSamples\\Advanced_RinLocomotion\\Actor\\rinActor.actor";
  640. const char* motionSetFilename = "@products@\\AnimationSamples\\Advanced_RinLocomotion\\AnimationEditorFiles\\Advanced_RinLocomotion.motionset";
  641. const char* animGraphFilename = "@products@\\AnimationSamples\\Advanced_RinLocomotion\\AnimationEditorFiles\\Advanced_RinLocomotion.animgraph";
  642. Importer* importer = GetEMotionFX().GetImporter();
  643. importer->SetLoggingEnabled(false);
  644. const AZStd::string resolvedActorFilename = ResolvePath(actorFilename);
  645. EXPECT_TRUE(AZ::IO::LocalFileIO::GetInstance()->Exists(resolvedActorFilename.c_str()))
  646. << AZStd::string::format("Actor file '%s' does not exist on local hard drive.", resolvedActorFilename.c_str()).c_str();
  647. AZStd::unique_ptr<Actor> actor = importer->LoadActor(resolvedActorFilename);
  648. ASSERT_NE(actor, nullptr) << "Actor failed to load.";
  649. MotionSet* motionSet = importer->LoadMotionSet(ResolvePath(motionSetFilename));
  650. ASSERT_NE(motionSet, nullptr) << "Motion set failed to load.";
  651. AnimGraph* animGraph = importer->LoadAnimGraph(ResolvePath(animGraphFilename));
  652. ASSERT_NE(animGraph, nullptr) << "Anim graph failed to load.";
  653. // Create instances.
  654. const size_t numInstances = 1000;
  655. AZStd::vector<ActorInstance*> actorInstances;
  656. for (size_t i = 0; i < numInstances; ++i)
  657. {
  658. ActorInstance* actorInstance = ActorInstance::Create(actor.get());
  659. actorInstances.emplace_back(actorInstance);
  660. }
  661. // Preload motions and make sure they got loaded successfully.
  662. motionSet->Preload();
  663. const auto& motionEntries = motionSet->GetMotionEntries();
  664. for (const auto& motionEntryPair : motionEntries)
  665. {
  666. const MotionSet::MotionEntry* motionEntry = motionEntryPair.second;
  667. ASSERT_NE(motionEntry->GetMotion(), nullptr);
  668. }
  669. AZ::Debug::Timer timer;
  670. timer.Stamp();
  671. for (ActorInstance* actorInstance : actorInstances)
  672. {
  673. AnimGraphInstance* animGraphInstance = AnimGraphInstance::Create(animGraph, actorInstance, motionSet);
  674. actorInstance->SetAnimGraphInstance(animGraphInstance);
  675. }
  676. const float activationTime = timer.GetDeltaTimeInSeconds();
  677. printf("Instantiating took = %.2f ms\n", activationTime * 1000.0f);
  678. printf("Activation Time = %.2f ms\n", activationTime * 1000.0f);
  679. for (ActorInstance* actorInstance : actorInstances)
  680. {
  681. actorInstance->Destroy();
  682. }
  683. delete animGraph;
  684. delete motionSet;
  685. }
  686. TEST_F(PerformanceTestFixture, DISABLED_MotionSamplingPerformanceNonUniform)
  687. {
  688. // Make sure that the motion is set to use NonUniform sampling! Change this in the scene settings! Otherwise you get wrong results.
  689. TestMotionSamplingPerformance("@products@\\animationsamples\\advanced_rinlocomotion\\motions\\rin_idle.motion");
  690. }
  691. TEST_F(PerformanceTestFixture, DISABLED_MotionSamplingPerformanceUniform)
  692. {
  693. // Make sure that the motion is set to use Uniform sampling! Change this in the scene settings! Otherwise you get wrong results.
  694. TestMotionSamplingPerformance("@products@\\animationsamples\\advanced_rinlocomotion\\motions\\rin_walk_kick_01.motion");
  695. }
  696. } // namespace EMotionFX