RPISystemComponent.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  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. /**
  9. * @file RPISystemComponent.cpp
  10. * @brief Contains the definition of the RPISystemComponent methods that aren't defined as inline
  11. */
  12. #include <RPI.Private/RPISystemComponent.h>
  13. #include <Atom/RHI/Factory.h>
  14. #include <AzCore/Asset/AssetManager.h>
  15. #include <AzCore/IO/IOUtils.h>
  16. #include <AzCore/NativeUI/NativeUIRequests.h>
  17. #include <AzCore/Serialization/SerializeContext.h>
  18. #include <AzCore/Settings/SettingsRegistry.h>
  19. #include <AzCore/PlatformId/PlatformId.h>
  20. #include <AzFramework/API/ApplicationAPI.h>
  21. #include <AzFramework/CommandLine/CommandLine.h>
  22. #include <AzFramework/Components/ConsoleBus.h>
  23. #ifdef RPI_EDITOR
  24. #include <Atom/RPI.Edit/Material/MaterialFunctorSourceDataRegistration.h>
  25. #endif
  26. namespace AZ
  27. {
  28. namespace RPI
  29. {
  30. void RPISystemComponent::Reflect(ReflectContext* context)
  31. {
  32. if (auto* serializeContext = azrtti_cast<SerializeContext*>(context))
  33. {
  34. serializeContext
  35. ->Class<RPISystemComponent, Component>()
  36. ->Version(0)
  37. ->Field("RpiDescriptor", &RPISystemComponent::m_rpiDescriptor)
  38. ;
  39. if (AZ::EditContext* ec = serializeContext->GetEditContext())
  40. {
  41. ec->Class<RPISystemComponent>("Atom RPI", "Atom Renderer")
  42. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  43. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  44. ->DataElement(AZ::Edit::UIHandlers::Default, &RPISystemComponent::m_rpiDescriptor, "RPI System Settings", "Settings for create RPI system")
  45. ;
  46. }
  47. }
  48. RPISystem::Reflect(context);
  49. }
  50. void RPISystemComponent::GetRequiredServices(ComponentDescriptor::DependencyArrayType& required)
  51. {
  52. required.push_back(RHI::Factory::GetComponentService());
  53. }
  54. void RPISystemComponent::GetProvidedServices(ComponentDescriptor::DependencyArrayType& provided)
  55. {
  56. provided.push_back(AZ_CRC("RPISystem", 0xf2add773));
  57. }
  58. void RPISystemComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent)
  59. {
  60. dependent.push_back(AZ_CRC_CE("XRSystemService"));
  61. }
  62. RPISystemComponent::RPISystemComponent()
  63. {
  64. #ifdef RPI_EDITOR
  65. AZ_Assert(m_materialFunctorRegistration == nullptr, "Material functor registration should be initialized with nullptr. "
  66. "And allocated depending on the component is in editors or not.");
  67. m_materialFunctorRegistration = aznew MaterialFunctorSourceDataRegistration;
  68. m_materialFunctorRegistration->Init();
  69. #endif
  70. }
  71. RPISystemComponent::~RPISystemComponent()
  72. {
  73. #ifdef RPI_EDITOR
  74. if (m_materialFunctorRegistration)
  75. {
  76. m_materialFunctorRegistration->Shutdown();
  77. delete m_materialFunctorRegistration;
  78. }
  79. #endif
  80. }
  81. void RPISystemComponent::Activate()
  82. {
  83. InitializePerformanceCollector();
  84. auto settingsRegistry = AZ::SettingsRegistry::Get();
  85. const char* settingPath = "/O3DE/Atom/RPI/Initialization";
  86. const char* setregName = "NullRenderer"; // same as serialization context name for RPISystemDescriptor::m_isNullRenderer
  87. AZ::ApplicationTypeQuery appType;
  88. ComponentApplicationBus::Broadcast(&AZ::ComponentApplicationBus::Events::QueryApplicationType, appType);
  89. bool isNullRenderer = false;
  90. if (appType.IsHeadless())
  91. {
  92. // if the application is `headless`, merge `NullRenderer` attribute to the setting registry
  93. isNullRenderer = true;
  94. }
  95. else
  96. {
  97. // Otherwise if the command line contains -NullRenderer merge it to setting registry
  98. const char* nullRendererOption = "NullRenderer"; // command line option name
  99. const AzFramework::CommandLine* commandLine = nullptr;
  100. AzFramework::ApplicationRequests::Bus::BroadcastResult(commandLine, &AzFramework::ApplicationRequests::GetApplicationCommandLine);
  101. if (commandLine->GetNumSwitchValues(nullRendererOption) > 0)
  102. {
  103. isNullRenderer = true;
  104. }
  105. }
  106. if (isNullRenderer)
  107. {
  108. // add it to setting registry
  109. auto overrideArg = AZStd::string::format("%s/%s=true", settingPath, setregName);
  110. settingsRegistry->MergeCommandLineArgument(overrideArg, "");
  111. }
  112. // load rpi desriptor from setting registry
  113. settingsRegistry->GetObject(m_rpiDescriptor, settingPath);
  114. m_rpiSystem.Initialize(m_rpiDescriptor);
  115. // Part of RPI system initialization requires asset system to be ready
  116. // which happens after the game system started
  117. // Use the tick bus to delay this initialization
  118. AZ::TickBus::QueueFunction(
  119. [this]()
  120. {
  121. m_rpiSystem.InitializeSystemAssets();
  122. });
  123. AZ::SystemTickBus::Handler::BusConnect();
  124. AZ::RHI::RHISystemNotificationBus::Handler::BusConnect();
  125. }
  126. void RPISystemComponent::Deactivate()
  127. {
  128. AZ::SystemTickBus::Handler::BusDisconnect();
  129. m_rpiSystem.Shutdown();
  130. AZ::RHI::RHISystemNotificationBus::Handler::BusDisconnect();
  131. }
  132. void RPISystemComponent::OnSystemTick()
  133. {
  134. if (m_performanceCollector)
  135. {
  136. if (m_gpuPassProfiler && !m_performanceCollector->IsWaitingBeforeCapture() && m_gpuPassProfiler->IsGpuTimeMeasurementEnabled())
  137. {
  138. uint64_t durationNanoseconds = m_gpuPassProfiler->MeasureGpuTimeInNanoseconds(AZ::RPI::PassSystemInterface::Get()->GetRootPass());
  139. // The first three frames, it is expected to be zero. So only record non-zero samples.
  140. if (durationNanoseconds > 0)
  141. {
  142. m_performanceCollector->RecordSample(PerformanceSpecGpuTime, AZStd::chrono::microseconds(aznumeric_cast<int64_t>(durationNanoseconds / 1000)));
  143. }
  144. }
  145. m_performanceCollector->RecordPeriodicEvent(PerformanceSpecEngineCpuTime);
  146. m_performanceCollector->FrameTick();
  147. }
  148. {
  149. AZ::Debug::ScopeDuration performanceScopeDuration(m_performanceCollector.get(), PerformanceSpecGraphicsSimulationTime);
  150. m_rpiSystem.SimulationTick();
  151. }
  152. {
  153. AZ::Debug::ScopeDuration performanceScopeDuration(m_performanceCollector.get(), PerformanceSpecGraphicsRenderTime);
  154. m_rpiSystem.RenderTick();
  155. }
  156. }
  157. void RPISystemComponent::OnDeviceRemoved([[maybe_unused]] RHI::Device* device)
  158. {
  159. #if defined(AZ_FORCE_CPU_GPU_INSYNC)
  160. const AZStd::string errorMessage = AZStd::string::format("GPU device was removed while working on pass %s. Check the log file for more detail.", device->GetLastExecutingScope().data());
  161. #else
  162. const AZStd::string errorMessage = "GPU device was removed. Check the log file for more detail.";
  163. #endif
  164. if (auto nativeUI = AZ::Interface<AZ::NativeUI::NativeUIRequests>::Get(); nativeUI != nullptr)
  165. {
  166. nativeUI->DisplayOkDialog("O3DE Fatal Error", errorMessage.c_str(), false);
  167. }
  168. else
  169. {
  170. AZ_Error("Atom", false, "O3DE Fatal Error: %s\n", errorMessage.c_str());
  171. }
  172. // Stop execution since we can't recover from device removal error
  173. Debug::Trace::Instance().Crash();
  174. }
  175. void RPISystemComponent::RegisterXRInterface(XRRenderingInterface* xrSystemInterface)
  176. {
  177. m_rpiSystem.RegisterXRSystem(xrSystemInterface);
  178. }
  179. void RPISystemComponent::UnRegisterXRInterface()
  180. {
  181. m_rpiSystem.UnregisterXRSystem();
  182. }
  183. AZ_CVAR_EXTERNED(AZ::u32, r_metricsNumberOfCaptureBatches);
  184. AZ_CVAR_EXTERNED(AZ::CVarFixedString, r_metricsDataLogType);
  185. AZ_CVAR_EXTERNED(AZ::u32, r_metricsWaitTimePerCaptureBatch);
  186. AZ_CVAR_EXTERNED(AZ::u32, r_metricsFrameCountPerCaptureBatch);
  187. AZ_CVAR_EXTERNED(bool, r_metricsMeasureGpuTime);
  188. AZ_CVAR_EXTERNED(bool, r_metricsQuitUponCompletion);
  189. AZStd::string RPISystemComponent::GetLogCategory()
  190. {
  191. AZStd::string platformName = AZ::GetPlatformName(AZ::g_currentPlatform);
  192. AZ::Name apiName = AZ::RHI::Factory::Get().GetName();
  193. auto logCategory = AZStd::string::format("%.*s-%s-%s", AZ_STRING_ARG(PerformanceLogCategory), platformName.c_str(), apiName.GetCStr());
  194. return logCategory;
  195. }
  196. void RPISystemComponent::InitializePerformanceCollector()
  197. {
  198. auto onBatchCompleteCallback = [&](AZ::u32 pendingBatches) {
  199. AZ_TracePrintf("RPISystem", "Completed a performance batch, still %u batches are pending.\n", pendingBatches);
  200. r_metricsNumberOfCaptureBatches = pendingBatches;
  201. if (pendingBatches == 0)
  202. {
  203. m_gpuPassProfiler->SetGpuTimeMeasurementEnabled(false);
  204. // Force disabling timestamp collection in the root pass.
  205. AZ::RPI::PassSystemInterface::Get()->GetRootPass()->SetTimestampQueryEnabled(false);
  206. }
  207. if (r_metricsQuitUponCompletion && (pendingBatches == 0))
  208. {
  209. AzFramework::ConsoleRequestBus::Broadcast(
  210. &AzFramework::ConsoleRequests::ExecuteConsoleCommand, "quit");
  211. }
  212. };
  213. auto performanceMetrics = AZStd::to_array<AZStd::string_view>({
  214. PerformanceSpecGraphicsSimulationTime,
  215. PerformanceSpecGraphicsRenderTime,
  216. PerformanceSpecEngineCpuTime,
  217. PerformanceSpecGpuTime,
  218. });
  219. AZStd::string logCategory = GetLogCategory();
  220. m_performanceCollector = AZStd::make_unique<AZ::Debug::PerformanceCollector>(
  221. logCategory, performanceMetrics, onBatchCompleteCallback);
  222. m_gpuPassProfiler = AZStd::make_unique<GpuPassProfiler>();
  223. //Feed the CVAR values.
  224. m_gpuPassProfiler->SetGpuTimeMeasurementEnabled(r_metricsMeasureGpuTime);
  225. m_performanceCollector->UpdateDataLogType(GetDataLogTypeFromCVar(r_metricsDataLogType));
  226. m_performanceCollector->UpdateFrameCountPerCaptureBatch(r_metricsFrameCountPerCaptureBatch);
  227. m_performanceCollector->UpdateWaitTimeBeforeEachBatch(AZStd::chrono::seconds(r_metricsWaitTimePerCaptureBatch));
  228. m_performanceCollector->UpdateNumberOfCaptureBatches(r_metricsNumberOfCaptureBatches);
  229. }
  230. } // namespace RPI
  231. } // namespace AZ