ProfilingCaptureSystemComponent.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578
  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 "ProfilingCaptureSystemComponent.h"
  9. #include <Atom/RHI/RHIUtils.h>
  10. #include <Atom/RHI/RHISystemInterface.h>
  11. #include <AzCore/Statistics/RunningStatistic.h>
  12. #include <Atom/RPI.Public/GpuQuery/GpuQueryTypes.h>
  13. #include <Atom/RPI.Public/Pass/ParentPass.h>
  14. #include <Atom/RPI.Public/Pass/Pass.h>
  15. #include <Atom/RPI.Public/Pass/PassFilter.h>
  16. #include <AzCore/Serialization/Json/JsonUtils.h>
  17. #include <AzCore/IO/SystemFile.h>
  18. #include <AzCore/RTTI/BehaviorContext.h>
  19. #include <AzCore/Serialization/Json/JsonSerializationSettings.h>
  20. #include <AzCore/Serialization/SerializeContext.h>
  21. #include <AzCore/std/parallel/thread.h>
  22. namespace AZ
  23. {
  24. namespace Render
  25. {
  26. class ProfilingCaptureNotificationBusHandler final
  27. : public ProfilingCaptureNotificationBus::Handler
  28. , public AZ::BehaviorEBusHandler
  29. {
  30. public:
  31. AZ_EBUS_BEHAVIOR_BINDER(ProfilingCaptureNotificationBusHandler, "{E45E4F37-EC1F-4010-994B-4F80998BEF15}", AZ::SystemAllocator,
  32. OnCaptureQueryTimestampFinished,
  33. OnCaptureCpuFrameTimeFinished,
  34. OnCaptureQueryPipelineStatisticsFinished,
  35. OnCaptureBenchmarkMetadataFinished
  36. );
  37. void OnCaptureQueryTimestampFinished(bool result, const AZStd::string& info) override
  38. {
  39. Call(FN_OnCaptureQueryTimestampFinished, result, info);
  40. }
  41. void OnCaptureCpuFrameTimeFinished(bool result, const AZStd::string& info) override
  42. {
  43. Call(FN_OnCaptureCpuFrameTimeFinished, result, info);
  44. }
  45. void OnCaptureQueryPipelineStatisticsFinished(bool result, const AZStd::string& info) override
  46. {
  47. Call(FN_OnCaptureQueryPipelineStatisticsFinished, result, info);
  48. }
  49. void OnCaptureBenchmarkMetadataFinished(bool result, const AZStd::string& info) override
  50. {
  51. Call(FN_OnCaptureBenchmarkMetadataFinished, result, info);
  52. }
  53. static void Reflect(AZ::ReflectContext* context)
  54. {
  55. if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
  56. {
  57. behaviorContext->EBus<ProfilingCaptureNotificationBus>("ProfilingCaptureNotificationBus")
  58. ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
  59. ->Attribute(AZ::Script::Attributes::Module, "atom")
  60. ->Handler<ProfilingCaptureNotificationBusHandler>()
  61. ;
  62. }
  63. }
  64. };
  65. // Intermediate class to serialize pass' Timestamp data.
  66. class TimestampSerializer
  67. {
  68. public:
  69. class TimestampSerializerEntry
  70. {
  71. public:
  72. AZ_TYPE_INFO(TimestampSerializer::TimestampSerializerEntry, "{34C90068-954C-4A07-A265-DB21462A7F9B}");
  73. static void Reflect(AZ::ReflectContext* context);
  74. Name m_passName;
  75. uint64_t m_timestampResultInNanoseconds;
  76. };
  77. AZ_TYPE_INFO(TimestampSerializer, "{FAAD85C2-5948-4D81-B54A-53502D69CBC0}");
  78. static void Reflect(AZ::ReflectContext* context);
  79. TimestampSerializer() = default;
  80. TimestampSerializer(AZStd::vector<const RPI::Pass*>&& pass);
  81. AZStd::vector<TimestampSerializerEntry> m_timestampEntries;
  82. };
  83. // Intermediate class to serialize CPU frame time statistics.
  84. class CpuFrameTimeSerializer
  85. {
  86. public:
  87. AZ_TYPE_INFO(Render::CpuFrameTimeSerializer, "{584B415E-8769-4757-AC64-EA57EDBCBC3E}");
  88. static void Reflect(AZ::ReflectContext* context);
  89. CpuFrameTimeSerializer() = default;
  90. CpuFrameTimeSerializer(double frameTime);
  91. double m_frameTime;
  92. };
  93. // Intermediate class to serialize pass' PipelineStatistics data.
  94. class PipelineStatisticsSerializer
  95. {
  96. public:
  97. class PipelineStatisticsSerializerEntry
  98. {
  99. public:
  100. AZ_TYPE_INFO(PipelineStatisticsSerializer::PipelineStatisticsSerializerEntry, "{7CEF130F-555F-4BC0-9A57-E6912F92599F}");
  101. static void Reflect(AZ::ReflectContext* context);
  102. Name m_passName;
  103. RPI::PipelineStatisticsResult m_pipelineStatisticsResult;
  104. };
  105. AZ_TYPE_INFO(PipelineStatisticsSerializer, "{4972BAB6-98FB-4D3B-9EAC-50FF418E77C0}");
  106. static void Reflect(AZ::ReflectContext* context);
  107. PipelineStatisticsSerializer() = default;
  108. PipelineStatisticsSerializer(AZStd::vector<const RPI::Pass*>&& passes);
  109. AZStd::vector<PipelineStatisticsSerializerEntry> m_pipelineStatisticsEntries;
  110. };
  111. // Intermediate class to serialize benchmark metadata.
  112. class BenchmarkMetadataSerializer
  113. {
  114. public:
  115. class GpuEntry
  116. {
  117. public:
  118. AZ_TYPE_INFO(Render::BenchmarkMetadataSerializer::GpuEntry, "{3D5C2DDE-59FB-4E28-9605-D2A083E34505}");
  119. static void Reflect(AZ::ReflectContext* context);
  120. GpuEntry() = default;
  121. GpuEntry(const RHI::PhysicalDeviceDescriptor& descriptor);
  122. private:
  123. AZStd::string m_description;
  124. uint32_t m_driverVersion;
  125. };
  126. AZ_TYPE_INFO(Render::BenchmarkMetadataSerializer, "{2BC41B6F-528F-4E59-AEDA-3B9D74E323EC}");
  127. static void Reflect(AZ::ReflectContext* context);
  128. BenchmarkMetadataSerializer() = default;
  129. BenchmarkMetadataSerializer(const AZStd::string& benchmarkName, const RHI::PhysicalDeviceDescriptor& gpuDescriptor);
  130. AZStd::string m_benchmarkName;
  131. GpuEntry m_gpuEntry;
  132. };
  133. // --- DelayedQueryCaptureHelper ---
  134. bool DelayedQueryCaptureHelper::StartCapture(CaptureCallback&& captureCallback)
  135. {
  136. if (m_state != DelayedCaptureState::Idle)
  137. {
  138. AZ_Warning("DelayedQueryCaptureHelper", false, "State is not set to idle, another process is in a pending state.");
  139. return false;
  140. }
  141. m_state = DelayedCaptureState::Pending;
  142. m_captureCallback = captureCallback;
  143. m_frameThreshold = FrameThreshold;
  144. return true;
  145. }
  146. void DelayedQueryCaptureHelper::UpdateCapture()
  147. {
  148. if (m_state == DelayedCaptureState::Pending)
  149. {
  150. m_frameThreshold--;
  151. if (m_frameThreshold == 0u)
  152. {
  153. m_captureCallback();
  154. m_state = DelayedCaptureState::Idle;
  155. }
  156. }
  157. }
  158. bool DelayedQueryCaptureHelper::IsIdle() const
  159. {
  160. return m_state == DelayedCaptureState::Idle;
  161. }
  162. // --- TimestampSerializer ---
  163. TimestampSerializer::TimestampSerializer(AZStd::vector<const RPI::Pass*>&& passes)
  164. {
  165. for (const RPI::Pass* pass : passes)
  166. {
  167. m_timestampEntries.push_back({pass->GetName(), pass->GetLatestTimestampResult().GetDurationInNanoseconds()});
  168. }
  169. }
  170. void TimestampSerializer::Reflect(AZ::ReflectContext* context)
  171. {
  172. if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  173. {
  174. serializeContext->Class<TimestampSerializer>()
  175. ->Version(1)
  176. ->Field("timestampEntries", &TimestampSerializer::m_timestampEntries)
  177. ;
  178. }
  179. TimestampSerializerEntry::Reflect(context);
  180. }
  181. // --- TimestampSerializerEntry ---
  182. void TimestampSerializer::TimestampSerializerEntry::Reflect(AZ::ReflectContext* context)
  183. {
  184. if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  185. {
  186. serializeContext->Class<TimestampSerializerEntry>()
  187. ->Version(1)
  188. ->Field("passName", &TimestampSerializerEntry::m_passName)
  189. ->Field("timestampResultInNanoseconds", &TimestampSerializerEntry::m_timestampResultInNanoseconds)
  190. ;
  191. }
  192. }
  193. // --- CpuFrameTimeSerializer ---
  194. CpuFrameTimeSerializer::CpuFrameTimeSerializer(double frameTime)
  195. {
  196. m_frameTime = frameTime;
  197. }
  198. void CpuFrameTimeSerializer::Reflect(AZ::ReflectContext* context)
  199. {
  200. if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  201. {
  202. serializeContext->Class<CpuFrameTimeSerializer>()
  203. ->Version(1)
  204. ->Field("frameTime", &CpuFrameTimeSerializer::m_frameTime)
  205. ;
  206. }
  207. }
  208. // --- PipelineStatisticsSerializer ---
  209. PipelineStatisticsSerializer::PipelineStatisticsSerializer(AZStd::vector<const RPI::Pass*>&& passes)
  210. {
  211. for (const RPI::Pass* pass : passes)
  212. {
  213. m_pipelineStatisticsEntries.push_back({pass->GetName(), pass->GetLatestPipelineStatisticsResult()});
  214. }
  215. }
  216. void PipelineStatisticsSerializer::Reflect(AZ::ReflectContext* context)
  217. {
  218. if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  219. {
  220. serializeContext->Class<PipelineStatisticsSerializer>()
  221. ->Version(1)
  222. ->Field("pipelineStatisticsEntries", &PipelineStatisticsSerializer::m_pipelineStatisticsEntries)
  223. ;
  224. }
  225. PipelineStatisticsSerializerEntry::Reflect(context);
  226. }
  227. // --- PipelineStatisticsSerializerEntry ---
  228. void PipelineStatisticsSerializer::PipelineStatisticsSerializerEntry::Reflect(AZ::ReflectContext* context)
  229. {
  230. if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  231. {
  232. serializeContext->Class<PipelineStatisticsSerializerEntry>()
  233. ->Version(1)
  234. ->Field("passName", &PipelineStatisticsSerializerEntry::m_passName)
  235. ->Field("pipelineStatisticsResult", &PipelineStatisticsSerializerEntry::m_pipelineStatisticsResult)
  236. ;
  237. }
  238. }
  239. // --- BenchmarkMetadataSerializer ---
  240. BenchmarkMetadataSerializer::BenchmarkMetadataSerializer(const AZStd::string& benchmarkName, const RHI::PhysicalDeviceDescriptor& gpuDescriptor)
  241. {
  242. m_benchmarkName = benchmarkName;
  243. m_gpuEntry = GpuEntry(gpuDescriptor);
  244. }
  245. void BenchmarkMetadataSerializer::Reflect(AZ::ReflectContext* context)
  246. {
  247. if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  248. {
  249. serializeContext->Class<BenchmarkMetadataSerializer>()
  250. ->Version(1)
  251. ->Field("benchmarkName", &BenchmarkMetadataSerializer::m_benchmarkName)
  252. ->Field("gpuInfo", &BenchmarkMetadataSerializer::m_gpuEntry)
  253. ;
  254. }
  255. GpuEntry::Reflect(context);
  256. }
  257. // --- GpuEntry ---
  258. BenchmarkMetadataSerializer::GpuEntry::GpuEntry(const RHI::PhysicalDeviceDescriptor& descriptor)
  259. {
  260. m_description = descriptor.m_description;
  261. m_driverVersion = descriptor.m_driverVersion;
  262. }
  263. void BenchmarkMetadataSerializer::GpuEntry::Reflect(AZ::ReflectContext* context)
  264. {
  265. if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  266. {
  267. serializeContext->Class<GpuEntry>()
  268. ->Version(1)
  269. ->Field("description", &GpuEntry::m_description)
  270. ->Field("driverVersion", &GpuEntry::m_driverVersion)
  271. ;
  272. }
  273. }
  274. // --- ProfilingCaptureSystemComponent ---
  275. void ProfilingCaptureSystemComponent::Reflect(AZ::ReflectContext* context)
  276. {
  277. if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  278. {
  279. serializeContext->Class<ProfilingCaptureSystemComponent, AZ::Component>()
  280. ->Version(1)
  281. ;
  282. }
  283. if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
  284. {
  285. behaviorContext->EBus<ProfilingCaptureRequestBus>("ProfilingCaptureRequestBus")
  286. ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
  287. ->Attribute(AZ::Script::Attributes::Module, "atom")
  288. ->Event("CapturePassTimestamp", &ProfilingCaptureRequestBus::Events::CapturePassTimestamp)
  289. ->Event("CaptureCpuFrameTime", &ProfilingCaptureRequestBus::Events::CaptureCpuFrameTime)
  290. ->Event("CapturePassPipelineStatistics", &ProfilingCaptureRequestBus::Events::CapturePassPipelineStatistics)
  291. ->Event("CaptureBenchmarkMetadata", &ProfilingCaptureRequestBus::Events::CaptureBenchmarkMetadata)
  292. ;
  293. ProfilingCaptureNotificationBusHandler::Reflect(context);
  294. }
  295. TimestampSerializer::Reflect(context);
  296. CpuFrameTimeSerializer::Reflect(context);
  297. PipelineStatisticsSerializer::Reflect(context);
  298. BenchmarkMetadataSerializer::Reflect(context);
  299. }
  300. void ProfilingCaptureSystemComponent::Activate()
  301. {
  302. ProfilingCaptureRequestBus::Handler::BusConnect();
  303. }
  304. void ProfilingCaptureSystemComponent::Deactivate()
  305. {
  306. TickBus::Handler::BusDisconnect();
  307. ProfilingCaptureRequestBus::Handler::BusDisconnect();
  308. }
  309. bool ProfilingCaptureSystemComponent::CapturePassTimestamp(const AZStd::string& outputFilePath)
  310. {
  311. RPI::Pass* root = AZ::RPI::PassSystemInterface::Get()->GetRootPass().get();
  312. // Enable all the Timestamp queries in passes.
  313. root->SetTimestampQueryEnabled(true);
  314. const bool captureStarted = m_timestampCapture.StartCapture([this, root, outputFilePath]()
  315. {
  316. JsonSerializerSettings serializationSettings;
  317. serializationSettings.m_keepDefaults = true;
  318. TimestampSerializer timestampSerializer(CollectPassesRecursively(root));
  319. const auto saveResult = JsonSerializationUtils::SaveObjectToFile(&timestampSerializer,
  320. outputFilePath, (TimestampSerializer*)nullptr, &serializationSettings);
  321. AZStd::string captureInfo = outputFilePath;
  322. if (!saveResult.IsSuccess())
  323. {
  324. captureInfo = AZStd::string::format("Failed to save pass' Timestamps to file '%s'. Error: %s",
  325. outputFilePath.c_str(),
  326. saveResult.GetError().c_str());
  327. AZ_Warning("ProfilingCaptureSystemComponent", false, captureInfo.c_str());
  328. }
  329. // Disable all the Timestamp queries in passes.
  330. root->SetTimestampQueryEnabled(false);
  331. // Notify listeners that the pass' Timestamp queries capture has finished.
  332. ProfilingCaptureNotificationBus::Broadcast(&ProfilingCaptureNotificationBus::Events::OnCaptureQueryTimestampFinished,
  333. saveResult.IsSuccess(),
  334. captureInfo);
  335. });
  336. // Start the TickBus.
  337. if (captureStarted)
  338. {
  339. TickBus::Handler::BusConnect();
  340. }
  341. return captureStarted;
  342. }
  343. bool ProfilingCaptureSystemComponent::CaptureCpuFrameTime(const AZStd::string& outputFilePath)
  344. {
  345. const bool captureStarted = m_cpuFrameTimeStatisticsCapture.StartCapture([outputFilePath]()
  346. {
  347. JsonSerializerSettings serializationSettings;
  348. serializationSettings.m_keepDefaults = true;
  349. double frameTime = AZ::RHI::RHISystemInterface::Get()->GetCpuFrameTime();
  350. AZ_Warning("ProfilingCaptureSystemComponent", frameTime > 0, "Failed to get Cpu frame time");
  351. CpuFrameTimeSerializer serializer(frameTime);
  352. const auto saveResult = JsonSerializationUtils::SaveObjectToFile(&serializer,
  353. outputFilePath, (CpuFrameTimeSerializer*)nullptr, &serializationSettings);
  354. AZStd::string captureInfo = outputFilePath;
  355. if (!saveResult.IsSuccess())
  356. {
  357. captureInfo = AZStd::string::format("Failed to save Cpu frame time to file '%s'. Error: %s",
  358. outputFilePath.c_str(),
  359. saveResult.GetError().c_str());
  360. AZ_Warning("ProfilingCaptureSystemComponent", false, captureInfo.c_str());
  361. }
  362. // Notify listeners that the Cpu frame time statistics capture has finished.
  363. ProfilingCaptureNotificationBus::Broadcast(&ProfilingCaptureNotificationBus::Events::OnCaptureCpuFrameTimeFinished,
  364. saveResult.IsSuccess(),
  365. captureInfo);
  366. });
  367. // Start the TickBus.
  368. if (captureStarted)
  369. {
  370. TickBus::Handler::BusConnect();
  371. }
  372. return captureStarted;
  373. }
  374. bool ProfilingCaptureSystemComponent::CapturePassPipelineStatistics(const AZStd::string& outputFilePath)
  375. {
  376. RPI::Pass* root = AZ::RPI::PassSystemInterface::Get()->GetRootPass().get();
  377. // Enable all the PipelineStatistics queries in passes.
  378. root->SetPipelineStatisticsQueryEnabled(true);
  379. const bool captureStarted = m_pipelineStatisticsCapture.StartCapture([this, root, outputFilePath]()
  380. {
  381. JsonSerializerSettings serializationSettings;
  382. serializationSettings.m_keepDefaults = true;
  383. PipelineStatisticsSerializer pipelineStatisticsSerializer(CollectPassesRecursively(root));
  384. const auto saveResult = JsonSerializationUtils::SaveObjectToFile(&pipelineStatisticsSerializer,
  385. outputFilePath, (PipelineStatisticsSerializer*)nullptr, &serializationSettings);
  386. AZStd::string captureInfo = outputFilePath;
  387. if (!saveResult.IsSuccess())
  388. {
  389. captureInfo = AZStd::string::format("Failed to save pass' PipelineStatistics to file '%s'. Error: %s",
  390. outputFilePath.c_str(),
  391. saveResult.GetError().c_str());
  392. AZ_Warning("ProfilingCaptureSystemComponent", false, captureInfo.c_str());
  393. }
  394. // Disable all the PipelineStatistics queries in passes.
  395. root->SetPipelineStatisticsQueryEnabled(false);
  396. // Notify listeners that the pass' PipelineStatistics queries capture has finished.
  397. ProfilingCaptureNotificationBus::Broadcast(&ProfilingCaptureNotificationBus::Events::OnCaptureQueryPipelineStatisticsFinished,
  398. saveResult.IsSuccess(),
  399. captureInfo);
  400. });
  401. // Start the TickBus.
  402. if (captureStarted)
  403. {
  404. TickBus::Handler::BusConnect();
  405. }
  406. return captureStarted;
  407. }
  408. bool ProfilingCaptureSystemComponent::CaptureBenchmarkMetadata(const AZStd::string& benchmarkName, const AZStd::string& outputFilePath)
  409. {
  410. const bool captureStarted = m_benchmarkMetadataCapture.StartCapture([benchmarkName, outputFilePath]()
  411. {
  412. JsonSerializerSettings serializationSettings;
  413. serializationSettings.m_keepDefaults = true;
  414. const RHI::PhysicalDeviceDescriptor& gpuDescriptor = RHI::GetRHIDevice()->GetPhysicalDevice().GetDescriptor();
  415. BenchmarkMetadataSerializer serializer(benchmarkName, gpuDescriptor);
  416. const auto saveResult = JsonSerializationUtils::SaveObjectToFile(&serializer,
  417. outputFilePath, (BenchmarkMetadataSerializer*)nullptr, &serializationSettings);
  418. AZStd::string captureInfo = outputFilePath;
  419. if (!saveResult.IsSuccess())
  420. {
  421. captureInfo = AZStd::string::format("Failed to save benchmark metadata data to file '%s'. Error: %s",
  422. outputFilePath.c_str(),
  423. saveResult.GetError().c_str());
  424. AZ_Warning("ProfilingCaptureSystemComponent", false, captureInfo.c_str());
  425. }
  426. // Notify listeners that the benchmark metadata capture has finished.
  427. ProfilingCaptureNotificationBus::Broadcast(&ProfilingCaptureNotificationBus::Events::OnCaptureBenchmarkMetadataFinished,
  428. saveResult.IsSuccess(),
  429. captureInfo);
  430. });
  431. // Start the TickBus.
  432. if (captureStarted)
  433. {
  434. TickBus::Handler::BusConnect();
  435. }
  436. return captureStarted;
  437. }
  438. AZStd::vector<const RPI::Pass*> ProfilingCaptureSystemComponent::CollectPassesRecursively(const RPI::Pass* root) const
  439. {
  440. AZStd::vector<const RPI::Pass*> passes;
  441. AZStd::function<void(const RPI::Pass*)> collectPass = [&](const RPI::Pass* pass)
  442. {
  443. passes.push_back(pass);
  444. const RPI::ParentPass* asParent = pass->AsParent();
  445. if (asParent)
  446. {
  447. for (const auto& child : asParent->GetChildren())
  448. {
  449. collectPass(child.get());
  450. }
  451. }
  452. };
  453. collectPass(root);
  454. return passes;
  455. }
  456. void ProfilingCaptureSystemComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] ScriptTimePoint time)
  457. {
  458. // Update the delayed captures
  459. m_timestampCapture.UpdateCapture();
  460. m_cpuFrameTimeStatisticsCapture.UpdateCapture();
  461. m_pipelineStatisticsCapture.UpdateCapture();
  462. m_benchmarkMetadataCapture.UpdateCapture();
  463. // Disconnect from the TickBus if all capture states are set to idle.
  464. if (m_timestampCapture.IsIdle() && m_pipelineStatisticsCapture.IsIdle() && m_benchmarkMetadataCapture.IsIdle() && m_cpuFrameTimeStatisticsCapture.IsIdle())
  465. {
  466. TickBus::Handler::BusDisconnect();
  467. }
  468. }
  469. }
  470. }