BootstrapSystemComponent.cpp 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852
  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 <BootstrapSystemComponent.h>
  9. #include <AzCore/Asset/AssetCommon.h>
  10. #include <AzCore/Component/ComponentApplicationBus.h>
  11. #include <AzCore/Component/ComponentApplicationLifecycle.h>
  12. #include <AzCore/Component/Entity.h>
  13. #include <AzCore/NativeUI/NativeUIRequests.h>
  14. #include <AzCore/Serialization/SerializeContext.h>
  15. #include <AzCore/std/smart_ptr/make_shared.h>
  16. #include <AzCore/Utils/Utils.h>
  17. #include <AzCore/StringFunc/StringFunc.h>
  18. #include <AzFramework/API/ApplicationAPI.h>
  19. #include <AzFramework/Components/TransformComponent.h>
  20. #include <AzFramework/Entity/GameEntityContextBus.h>
  21. #include <AzFramework/Asset/AssetSystemBus.h>
  22. #include <ISystem.h>
  23. #include <Atom/RHI/RHISystemInterface.h>
  24. #include <Atom/RPI.Reflect/Image/AttachmentImageAsset.h>
  25. #include <Atom/RPI.Reflect/Image/AttachmentImageAssetCreator.h>
  26. #include <Atom/RPI.Reflect/Asset/AssetUtils.h>
  27. #include <Atom/RPI.Public/Pass/Pass.h>
  28. #include <Atom/RPI.Public/Pass/PassSystemInterface.h>
  29. #include <Atom/RPI.Public/RenderPipeline.h>
  30. #include <Atom/RPI.Public/ViewportContextBus.h>
  31. #include <Atom/RPI.Public/RPISystemInterface.h>
  32. #include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
  33. #include <Atom/RPI.Public/Shader/ShaderSystem.h>
  34. #include <Atom/Bootstrap/DefaultWindowBus.h>
  35. #include <Atom/Bootstrap/BootstrapNotificationBus.h>
  36. #include <Atom/RPI.Reflect/System/AnyAsset.h>
  37. #include <AzCore/Console/IConsole.h>
  38. #include <BootstrapSystemComponent_Traits_Platform.h>
  39. void cvar_r_renderPipelinePath_Changed(const AZ::CVarFixedString& newPipelinePath)
  40. {
  41. auto viewportContextManager = AZ::Interface<AZ::RPI::ViewportContextRequestsInterface>::Get();
  42. if (!viewportContextManager)
  43. {
  44. return;
  45. }
  46. auto viewportContext = viewportContextManager->GetDefaultViewportContext();
  47. if (!viewportContext)
  48. {
  49. return;
  50. }
  51. AZ::Data::Asset<AZ::RPI::AnyAsset> pipelineAsset =
  52. AZ::RPI::AssetUtils::LoadAssetByProductPath<AZ::RPI::AnyAsset>(newPipelinePath.data(), AZ::RPI::AssetUtils::TraceLevel::Error);
  53. if (pipelineAsset)
  54. {
  55. AZ::RPI::RenderPipelineDescriptor renderPipelineDescriptor =
  56. *AZ::RPI::GetDataFromAnyAsset<AZ::RPI::RenderPipelineDescriptor>(pipelineAsset); // Copy descriptor from asset
  57. AZ::Render::Bootstrap::RequestBus::Broadcast(&AZ::Render::Bootstrap::RequestBus::Events::SwitchRenderPipeline, renderPipelineDescriptor, viewportContext);
  58. }
  59. else
  60. {
  61. AZ_Warning("SetDefaultPipeline", false, "Failed to switch default render pipeline to %s: can't load the asset", newPipelinePath.data());
  62. }
  63. }
  64. void cvar_r_antiAliasing_Changed(const AZ::CVarFixedString& newAntiAliasing)
  65. {
  66. auto viewportContextManager = AZ::Interface<AZ::RPI::ViewportContextRequestsInterface>::Get();
  67. if (!viewportContextManager)
  68. {
  69. return;
  70. }
  71. auto viewportContext = viewportContextManager->GetDefaultViewportContext();
  72. if (!viewportContext)
  73. {
  74. return;
  75. }
  76. AZ::Render::Bootstrap::RequestBus::Broadcast(&AZ::Render::Bootstrap::RequestBus::Events::SwitchAntiAliasing, newAntiAliasing.c_str(), viewportContext);
  77. }
  78. void cvar_r_multiSample_Changed(const uint16_t& newSampleCount)
  79. {
  80. auto viewportContextManager = AZ::Interface<AZ::RPI::ViewportContextRequestsInterface>::Get();
  81. if (!viewportContextManager)
  82. {
  83. return;
  84. }
  85. auto viewportContext = viewportContextManager->GetDefaultViewportContext();
  86. if (!viewportContext)
  87. {
  88. return;
  89. }
  90. if (newSampleCount > 0 && ((newSampleCount & (newSampleCount - 1))) == 0)
  91. {
  92. AZ::Render::Bootstrap::RequestBus::Broadcast(&AZ::Render::Bootstrap::RequestBus::Events::SwitchMultiSample, AZStd::clamp(newSampleCount, uint16_t(1), uint16_t(8)), viewportContext);
  93. }
  94. else
  95. {
  96. AZ_Warning("SetMultiSampleCount", false, "Failed to set multi-sample count %d: invalid multi-sample count", newSampleCount);
  97. }
  98. }
  99. AZ_CVAR(AZ::CVarFixedString, r_renderPipelinePath, AZ_TRAIT_BOOTSTRAPSYSTEMCOMPONENT_PIPELINE_NAME, cvar_r_renderPipelinePath_Changed, AZ::ConsoleFunctorFlags::DontReplicate, "The asset (.azasset) path for default render pipeline");
  100. AZ_CVAR(AZ::CVarFixedString, r_default_openxr_pipeline_name, "passes/MultiViewRenderPipeline.azasset", nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Default openXr render pipeline name");
  101. AZ_CVAR(AZ::CVarFixedString, r_default_openxr_left_pipeline_name, "passes/XRLeftRenderPipeline.azasset", nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Default openXr Left eye render pipeline name");
  102. AZ_CVAR(AZ::CVarFixedString, r_default_openxr_right_pipeline_name, "passes/XRRightRenderPipeline.azasset", nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Default openXr Right eye render pipeline name");
  103. AZ_CVAR(uint32_t, r_width, 1920, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Starting window width in pixels.");
  104. AZ_CVAR(uint32_t, r_height, 1080, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Starting window height in pixels.");
  105. AZ_CVAR(uint32_t, r_fullscreen, false, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Starting fullscreen state.");
  106. AZ_CVAR(uint32_t, r_resolutionMode, 0, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "0: render resolution same as window client area size, 1: render resolution use the values specified by r_width and r_height");
  107. void cvar_r_renderScale_Changed(const float& newRenderScale)
  108. {
  109. AZ_Error("AtomBootstrap", newRenderScale > 0, "RenderScale should be greater than 0");
  110. if (newRenderScale > 0)
  111. {
  112. AzFramework::WindowSize newSize =
  113. AzFramework::WindowSize(static_cast<uint32_t>(r_width * newRenderScale), static_cast<uint32_t>(r_height * newRenderScale));
  114. AzFramework::WindowRequestBus::Broadcast(&AzFramework::WindowRequestBus::Events::SetEnableCustomizedResolution, true);
  115. AzFramework::WindowRequestBus::Broadcast(&AzFramework::WindowRequestBus::Events::SetRenderResolution, newSize);
  116. }
  117. }
  118. AZ_CVAR(float, r_renderScale, 1.0f, cvar_r_renderScale_Changed, AZ::ConsoleFunctorFlags::DontReplicate, "Scale to apply to the window resolution.");
  119. AZ_CVAR(AZ::CVarFixedString, r_antiAliasing, "", cvar_r_antiAliasing_Changed, AZ::ConsoleFunctorFlags::DontReplicate, "The anti-aliasing to be used for the current render pipeline. Available options: MSAA, TAA, SMAA");
  120. AZ_CVAR(uint16_t, r_multiSampleCount, 0, cvar_r_multiSample_Changed, AZ::ConsoleFunctorFlags::DontReplicate, "The multi-sample count to be used for the current render pipeline."); // 0 stands for unchanged, load the default setting from the pipeline itself
  121. namespace AZ
  122. {
  123. namespace Render
  124. {
  125. namespace Bootstrap
  126. {
  127. void BootstrapSystemComponent::Reflect(ReflectContext* context)
  128. {
  129. if (SerializeContext* serialize = azrtti_cast<SerializeContext*>(context))
  130. {
  131. serialize->Class<BootstrapSystemComponent, Component>()
  132. ->Version(1)
  133. ;
  134. if (EditContext* ec = serialize->GetEditContext())
  135. {
  136. ec->Class<BootstrapSystemComponent>("Atom RPI", "Atom Renderer")
  137. ->ClassElement(Edit::ClassElements::EditorData, "")
  138. ->Attribute(Edit::Attributes::AutoExpand, true)
  139. ;
  140. }
  141. }
  142. }
  143. void BootstrapSystemComponent::GetProvidedServices(ComponentDescriptor::DependencyArrayType& provided)
  144. {
  145. provided.push_back(AZ_CRC("BootstrapSystemComponent", 0xb8f32711));
  146. }
  147. void BootstrapSystemComponent::GetRequiredServices(ComponentDescriptor::DependencyArrayType& required)
  148. {
  149. required.push_back(AZ_CRC("RPISystem", 0xf2add773));
  150. required.push_back(AZ_CRC("SceneSystemComponentService", 0xd8975435));
  151. }
  152. void BootstrapSystemComponent::GetDependentServices(ComponentDescriptor::DependencyArrayType& dependent)
  153. {
  154. dependent.push_back(AZ_CRC("ImGuiSystemComponent", 0x2f08b9a7));
  155. dependent.push_back(AZ_CRC("PrimitiveSystemComponent", 0xc860fa59));
  156. dependent.push_back(AZ_CRC("MeshSystemComponent", 0x21e5bbb6));
  157. dependent.push_back(AZ_CRC("CoreLightsService", 0x91932ef6));
  158. dependent.push_back(AZ_CRC("DynamicDrawService", 0x023c1673));
  159. dependent.push_back(AZ_CRC("CommonService", 0x6398eec4));
  160. dependent.push_back(AZ_CRC_CE("HairService"));
  161. }
  162. void BootstrapSystemComponent::GetIncompatibleServices(ComponentDescriptor::DependencyArrayType& incompatible)
  163. {
  164. incompatible.push_back(AZ_CRC("BootstrapSystemComponent", 0xb8f32711));
  165. }
  166. BootstrapSystemComponent::BootstrapSystemComponent()
  167. {
  168. }
  169. BootstrapSystemComponent::~BootstrapSystemComponent()
  170. {
  171. m_viewportContext.reset();
  172. }
  173. //! Helper function that parses the command line arguments
  174. //! looking for r_width, r_height and r_fullscreen.
  175. //! It is important to call this before using r_width, r_height or r_fullscreen
  176. //! because at the moment this system component initializes before Legacy System.cpp gets to parse
  177. //! command line arguments into cvars.
  178. static void UpdateCVarsFromCommandLine()
  179. {
  180. AZ::CommandLine* pCmdLine = nullptr;
  181. ComponentApplicationBus::BroadcastResult(pCmdLine, &AZ::ComponentApplicationBus::Events::GetAzCommandLine);
  182. if (!pCmdLine)
  183. {
  184. return;
  185. }
  186. const AZStd::string fullscreenCvarName("r_fullscreen");
  187. if (pCmdLine->HasSwitch(fullscreenCvarName))
  188. {
  189. auto numValues = pCmdLine->GetNumSwitchValues(fullscreenCvarName);
  190. if (numValues > 0)
  191. {
  192. auto valueStr = pCmdLine->GetSwitchValue(fullscreenCvarName);
  193. if (AZ::StringFunc::LooksLikeBool(valueStr.c_str()))
  194. {
  195. r_fullscreen = AZ::StringFunc::ToBool(valueStr.c_str());
  196. }
  197. }
  198. }
  199. const AZStd::string widthCvarName("r_width");
  200. if (pCmdLine->HasSwitch(widthCvarName))
  201. {
  202. auto numValues = pCmdLine->GetNumSwitchValues(widthCvarName);
  203. if (numValues > 0)
  204. {
  205. auto valueStr = pCmdLine->GetSwitchValue(widthCvarName);
  206. if (AZ::StringFunc::LooksLikeInt(valueStr.c_str()))
  207. {
  208. auto width = AZ::StringFunc::ToInt(valueStr.c_str());
  209. if (width > 0)
  210. {
  211. r_width = width;
  212. }
  213. }
  214. }
  215. }
  216. const AZStd::string heightCvarName("r_height");
  217. if (pCmdLine->HasSwitch(heightCvarName))
  218. {
  219. auto numValues = pCmdLine->GetNumSwitchValues(heightCvarName);
  220. if (numValues > 0)
  221. {
  222. auto valueStr = pCmdLine->GetSwitchValue(heightCvarName);
  223. if (AZ::StringFunc::LooksLikeInt(valueStr.c_str()))
  224. {
  225. auto height = AZ::StringFunc::ToInt(valueStr.c_str());
  226. if (height > 0)
  227. {
  228. r_height = height;
  229. }
  230. }
  231. }
  232. }
  233. const AZStd::string resolutionModeCvarName("r_resolutionMode");
  234. if (pCmdLine->HasSwitch(resolutionModeCvarName))
  235. {
  236. auto numValues = pCmdLine->GetNumSwitchValues(resolutionModeCvarName);
  237. if (numValues > 0)
  238. {
  239. auto valueStr = pCmdLine->GetSwitchValue(resolutionModeCvarName);
  240. if (AZ::StringFunc::LooksLikeInt(valueStr.c_str()))
  241. {
  242. auto resolutionMode = AZ::StringFunc::ToInt(valueStr.c_str());
  243. if (resolutionMode >= 0)
  244. {
  245. r_resolutionMode = resolutionMode;
  246. }
  247. }
  248. }
  249. }
  250. const AZStd::string renderScaleCvarName("r_renderScale");
  251. if (pCmdLine->HasSwitch(renderScaleCvarName))
  252. {
  253. auto numValues = pCmdLine->GetNumSwitchValues(renderScaleCvarName);
  254. if (numValues > 0)
  255. {
  256. auto valueStr = pCmdLine->GetSwitchValue(renderScaleCvarName);
  257. if (AZ::StringFunc::LooksLikeFloat(valueStr.c_str()))
  258. {
  259. auto renderScale = AZ::StringFunc::ToFloat(valueStr.c_str());
  260. if (renderScale > 0)
  261. {
  262. r_renderScale = renderScale;
  263. }
  264. }
  265. }
  266. }
  267. const AZStd::string multiSampleCvarName("r_multiSampleCount");
  268. if (pCmdLine->HasSwitch(multiSampleCvarName))
  269. {
  270. auto numValues = pCmdLine->GetNumSwitchValues(multiSampleCvarName);
  271. if (numValues > 0)
  272. {
  273. auto valueStr = pCmdLine->GetSwitchValue(multiSampleCvarName);
  274. if (AZ::StringFunc::LooksLikeInt(valueStr.c_str()))
  275. {
  276. auto multiSample = AZ::StringFunc::ToInt(valueStr.c_str());
  277. if (multiSample > 0)
  278. {
  279. r_multiSampleCount = static_cast<uint16_t>(multiSample);
  280. }
  281. }
  282. }
  283. }
  284. }
  285. void BootstrapSystemComponent::Activate()
  286. {
  287. // Create a native window only if it's a launcher (or standalone)
  288. // LY editor create its own window which we can get its handle through AzFramework::WindowSystemNotificationBus::Handler's OnWindowCreated() function
  289. AZ::ApplicationTypeQuery appType;
  290. ComponentApplicationBus::Broadcast(&AZ::ComponentApplicationBus::Events::QueryApplicationType, appType);
  291. if (appType.IsHeadless())
  292. {
  293. m_nativeWindow = nullptr;
  294. }
  295. else if (!appType.IsValid() || appType.IsGame())
  296. {
  297. // GFX TODO - investigate window creation being part of the GameApplication.
  298. auto projectTitle = AZ::Utils::GetProjectDisplayName();
  299. // It is important to call this before using r_width, r_height or r_fullscreen
  300. // because at the moment this system component initializes before Legacy System.cpp gets to parse
  301. // command line arguments into cvars.
  302. UpdateCVarsFromCommandLine();
  303. auto windowSize = GetWindowResolution();
  304. m_nativeWindow = AZStd::make_unique<AzFramework::NativeWindow>(projectTitle.c_str(), AzFramework::WindowGeometry(0, 0, windowSize.m_width, windowSize.m_height));
  305. AZ_Assert(m_nativeWindow, "Failed to create the game window\n");
  306. m_nativeWindow->Activate();
  307. m_windowHandle = m_nativeWindow->GetWindowHandle();
  308. }
  309. else
  310. {
  311. // Disable default scene creation for non-games projects
  312. // This can be manually overridden via the DefaultWindowBus.
  313. m_createDefaultScene = false;
  314. }
  315. TickBus::Handler::BusConnect();
  316. // Listen for window system requests (e.g. requests for default window handle)
  317. AzFramework::WindowSystemRequestBus::Handler::BusConnect();
  318. // Listen for window system notifications (e.g. window being created by Editor)
  319. AzFramework::WindowSystemNotificationBus::Handler::BusConnect();
  320. Render::Bootstrap::DefaultWindowBus::Handler::BusConnect();
  321. Render::Bootstrap::RequestBus::Handler::BusConnect();
  322. // Listen for application's window creation/destruction (e.g. window is created/destroyed on Android when suspending the app)
  323. AzFramework::ApplicationLifecycleEvents::Bus::Handler::BusConnect();
  324. // delay one frame for Initialize which asset system is ready by then
  325. AZ::TickBus::QueueFunction(
  326. [this]()
  327. {
  328. Initialize();
  329. SetWindowResolution();
  330. });
  331. }
  332. void BootstrapSystemComponent::Deactivate()
  333. {
  334. AzFramework::ApplicationLifecycleEvents::Bus::Handler::BusDisconnect();
  335. Render::Bootstrap::RequestBus::Handler::BusDisconnect();
  336. Render::Bootstrap::DefaultWindowBus::Handler::BusDisconnect();
  337. AzFramework::WindowSystemRequestBus::Handler::BusDisconnect();
  338. AzFramework::WindowSystemNotificationBus::Handler::BusDisconnect();
  339. TickBus::Handler::BusDisconnect();
  340. m_brdfTexture = nullptr;
  341. RemoveRenderPipeline();
  342. DestroyDefaultScene();
  343. m_viewportContext.reset();
  344. m_nativeWindow = nullptr;
  345. m_windowHandle = nullptr;
  346. }
  347. void BootstrapSystemComponent::Initialize()
  348. {
  349. if (m_isInitialized)
  350. {
  351. return;
  352. }
  353. m_isInitialized = true;
  354. if (!RPI::RPISystemInterface::Get()->IsInitialized())
  355. {
  356. AZ::OSString msgBoxMessage;
  357. msgBoxMessage.append("RPI System could not initialize correctly. Check log for detail.");
  358. AZ::NativeUI::NativeUIRequestBus::Broadcast(
  359. &AZ::NativeUI::NativeUIRequestBus::Events::DisplayOkDialog, "O3DE Fatal Error", msgBoxMessage.c_str(), false);
  360. AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::ExitMainLoop);
  361. return;
  362. }
  363. // In the case of the game we want to call create and register the scene as a soon as we can
  364. // because a level could be loaded in autoexec.cfg and that will assert if there is no scene registered
  365. // to get the feature processors for the components. So we can't wait until the tick (whereas the Editor wants to wait)
  366. if (m_createDefaultScene)
  367. {
  368. CreateDefaultScene();
  369. }
  370. if (m_windowHandle)
  371. {
  372. CreateViewportContext();
  373. if (m_createDefaultScene)
  374. {
  375. CreateDefaultRenderPipeline();
  376. }
  377. }
  378. }
  379. void BootstrapSystemComponent::OnWindowCreated(AzFramework::NativeWindowHandle windowHandle)
  380. {
  381. // only handle the first window (default) created
  382. if (m_windowHandle == nullptr)
  383. {
  384. m_windowHandle = windowHandle;
  385. if (m_isInitialized)
  386. {
  387. CreateViewportContext();
  388. if (m_createDefaultScene)
  389. {
  390. CreateDefaultRenderPipeline();
  391. }
  392. }
  393. SetWindowResolution();
  394. }
  395. }
  396. void BootstrapSystemComponent::OnApplicationWindowCreated()
  397. {
  398. if (!m_nativeWindow)
  399. {
  400. auto projectTitle = AZ::Utils::GetProjectDisplayName();
  401. auto windowSize = GetWindowResolution();
  402. m_nativeWindow = AZStd::make_unique<AzFramework::NativeWindow>(projectTitle.c_str(), AzFramework::WindowGeometry(0, 0, windowSize.m_width, windowSize.m_height));
  403. AZ_Assert(m_nativeWindow, "Failed to create the game window\n");
  404. m_nativeWindow->Activate();
  405. OnWindowCreated(m_nativeWindow->GetWindowHandle());
  406. }
  407. }
  408. void BootstrapSystemComponent::OnApplicationWindowDestroy()
  409. {
  410. m_nativeWindow = nullptr;
  411. }
  412. void BootstrapSystemComponent::CreateViewportContext()
  413. {
  414. RHI::Device* device = RHI::RHISystemInterface::Get()->GetDevice();
  415. RPI::ViewportContextRequestsInterface::CreationParameters params;
  416. params.device = device;
  417. params.windowHandle = m_windowHandle;
  418. params.renderScene = m_defaultScene;
  419. // Setting the default ViewportContextID to an arbitrary and otherwise invalid (negative) value to ensure its uniqueness
  420. params.id = -10;
  421. auto viewContextManager = AZ::Interface<RPI::ViewportContextRequestsInterface>::Get();
  422. m_viewportContext = viewContextManager->CreateViewportContext(
  423. viewContextManager->GetDefaultViewportContextName(), params);
  424. DefaultWindowNotificationBus::Broadcast(&DefaultWindowNotificationBus::Events::DefaultWindowCreated);
  425. // Listen to window notification so we can request exit application when window closes
  426. AzFramework::WindowNotificationBus::Handler::BusConnect(GetDefaultWindowHandle());
  427. }
  428. void BootstrapSystemComponent::SetWindowResolution()
  429. {
  430. if (m_nativeWindow)
  431. {
  432. // wait until swapchain has been created before setting fullscreen state
  433. if (r_resolutionMode > 0u)
  434. {
  435. m_nativeWindow->SetEnableCustomizedResolution(true);
  436. m_nativeWindow->SetRenderResolution(GetWindowResolution());
  437. }
  438. else
  439. {
  440. m_nativeWindow->SetEnableCustomizedResolution(false);
  441. }
  442. m_nativeWindow->SetFullScreenState(r_fullscreen);
  443. }
  444. }
  445. AZ::RPI::ScenePtr BootstrapSystemComponent::GetOrCreateAtomSceneFromAzScene(AzFramework::Scene* scene)
  446. {
  447. // Get or create a weak pointer to our scene
  448. // If it's valid, we're done, if not we need to create an Atom scene and update our scene map
  449. auto& atomSceneHandle = m_azSceneToAtomSceneMap[scene];
  450. if (!atomSceneHandle.expired())
  451. {
  452. return atomSceneHandle.lock();
  453. }
  454. // Create and register a scene with all available feature processors
  455. RPI::SceneDescriptor sceneDesc;
  456. sceneDesc.m_nameId = AZ::Name("Main");
  457. AZ::RPI::ScenePtr atomScene = RPI::Scene::CreateScene(sceneDesc);
  458. atomScene->EnableAllFeatureProcessors();
  459. atomScene->Activate();
  460. // Register scene to RPI system so it will be processed/rendered per tick
  461. RPI::RPISystemInterface::Get()->RegisterScene(atomScene);
  462. scene->SetSubsystem(atomScene);
  463. atomSceneHandle = atomScene;
  464. return atomScene;
  465. }
  466. void BootstrapSystemComponent::CreateDefaultScene()
  467. {
  468. // Bind atomScene to the GameEntityContext's AzFramework::Scene
  469. m_defaultFrameworkScene = AzFramework::SceneSystemInterface::Get()->GetScene(AzFramework::Scene::MainSceneName);
  470. // This should never happen unless scene creation has changed.
  471. AZ_Assert(m_defaultFrameworkScene, "Error: Scenes missing during system component initialization");
  472. m_sceneRemovalHandler = AzFramework::Scene::RemovalEvent::Handler(
  473. [this](AzFramework::Scene&, AzFramework::Scene::RemovalEventType eventType)
  474. {
  475. if (eventType == AzFramework::Scene::RemovalEventType::Zombified)
  476. {
  477. m_defaultFrameworkScene.reset();
  478. }
  479. });
  480. m_defaultFrameworkScene->ConnectToEvents(m_sceneRemovalHandler);
  481. m_defaultScene = GetOrCreateAtomSceneFromAzScene(m_defaultFrameworkScene.get());
  482. }
  483. bool BootstrapSystemComponent::EnsureDefaultRenderPipelineInstalledForScene(AZ::RPI::ScenePtr scene, AZ::RPI::ViewportContextPtr viewportContext)
  484. {
  485. AZ::RPI::XRRenderingInterface* xrSystem = AZ::RPI::RPISystemInterface::Get()->GetXRSystem();
  486. const bool loadDefaultRenderPipeline = !xrSystem || xrSystem->GetRHIXRRenderingInterface()->IsDefaultRenderPipelineNeeded();
  487. AZ::RHI::MultisampleState multisampleState;
  488. // Load the main default pipeline if applicable
  489. if (loadDefaultRenderPipeline)
  490. {
  491. AZ::CVarFixedString pipelineName = static_cast<AZ::CVarFixedString>(r_renderPipelinePath);
  492. if (xrSystem)
  493. {
  494. // When running launcher on PC having an XR system present then the default render pipeline is suppose to reflect
  495. // what's being rendered into XR device. XR render pipeline uses multiview render pipeline.
  496. AZ::ApplicationTypeQuery appType;
  497. ComponentApplicationBus::Broadcast(&AZ::ComponentApplicationBus::Events::QueryApplicationType, appType);
  498. if (appType.IsGame())
  499. {
  500. pipelineName = r_default_openxr_pipeline_name;
  501. }
  502. }
  503. RPI::RenderPipelinePtr renderPipeline = LoadPipeline(scene, viewportContext, pipelineName, AZ::RPI::ViewType::Default, multisampleState);
  504. if (!renderPipeline)
  505. {
  506. return false;
  507. }
  508. AZ::CVarFixedString antiAliasing = static_cast<AZ::CVarFixedString>(r_antiAliasing);
  509. if (antiAliasing != "")
  510. {
  511. renderPipeline->SetActiveAAMethod(antiAliasing.c_str());
  512. }
  513. // As part of our initialization we need to create the BRDF texture generation pipeline
  514. AZ::RPI::RenderPipelineDescriptor pipelineDesc;
  515. pipelineDesc.m_mainViewTagName = "MainCamera";
  516. pipelineDesc.m_name = AZStd::string::format("BRDFTexturePipeline_%i", viewportContext->GetId());
  517. pipelineDesc.m_rootPassTemplate = "BRDFTexturePipeline";
  518. pipelineDesc.m_executeOnce = true;
  519. // Save a reference to the generated BRDF texture so it doesn't get deleted if all the passes refering to it get deleted
  520. // and it's ref count goes to zero
  521. if (!m_brdfTexture)
  522. {
  523. const AZStd::shared_ptr<const RPI::PassTemplate> brdfTextureTemplate =
  524. RPI::PassSystemInterface::Get()->GetPassTemplate(Name("BRDFTextureTemplate"));
  525. Data::Asset<RPI::AttachmentImageAsset> brdfImageAsset = RPI::AssetUtils::LoadAssetById<RPI::AttachmentImageAsset>(
  526. brdfTextureTemplate->m_imageAttachments[0].m_assetRef.m_assetId, RPI::AssetUtils::TraceLevel::Error);
  527. if (brdfImageAsset.IsReady())
  528. {
  529. m_brdfTexture = RPI::AttachmentImage::FindOrCreate(brdfImageAsset);
  530. }
  531. }
  532. if (!scene->GetRenderPipeline(AZ::Name(pipelineDesc.m_name)))
  533. {
  534. RPI::RenderPipelinePtr brdfTexturePipeline = AZ::RPI::RenderPipeline::CreateRenderPipeline(pipelineDesc);
  535. scene->AddRenderPipeline(brdfTexturePipeline);
  536. }
  537. }
  538. // Load XR pipelines if applicable
  539. if (xrSystem)
  540. {
  541. for (AZ::u32 i = 0; i < xrSystem->GetNumViews(); i++)
  542. {
  543. const AZ::RPI::ViewType viewType = (i == 0)
  544. ? AZ::RPI::ViewType::XrLeft
  545. : AZ::RPI::ViewType::XrRight;
  546. const AZStd::string_view xrPipelineAssetName = (viewType == AZ::RPI::ViewType::XrLeft)
  547. ? static_cast<AZ::CVarFixedString>(r_default_openxr_left_pipeline_name)
  548. : static_cast<AZ::CVarFixedString>(r_default_openxr_right_pipeline_name);
  549. if (!LoadPipeline(scene, viewportContext, xrPipelineAssetName, viewType, multisampleState))
  550. {
  551. return false;
  552. }
  553. }
  554. }
  555. // Apply MSAA state to all the render pipelines.
  556. // It's important to do this after all the pipelines have
  557. // been created so the same values are applied to all.
  558. // As it cannot be applied MSAA values per pipeline,
  559. // it's setting the MSAA state from the last pipeline loaded.
  560. const auto cvarMultiSample = static_cast<uint16_t>(r_multiSampleCount);
  561. if (cvarMultiSample > 0 && ((cvarMultiSample & (cvarMultiSample - 1))) == 0)
  562. {
  563. multisampleState.m_samples = static_cast<uint16_t>(cvarMultiSample);
  564. }
  565. AZ::RPI::RPISystemInterface::Get()->SetApplicationMultisampleState(multisampleState);
  566. // Send notification when the scene and its pipeline are ready.
  567. // Use the first created pipeline's scene as our default scene for now to allow
  568. // consumers waiting on scene availability to initialize.
  569. if (!m_defaultSceneReady)
  570. {
  571. m_defaultScene = scene;
  572. Render::Bootstrap::NotificationBus::Broadcast(
  573. &Render::Bootstrap::NotificationBus::Handler::OnBootstrapSceneReady, m_defaultScene.get());
  574. m_defaultSceneReady = true;
  575. }
  576. return true;
  577. }
  578. void BootstrapSystemComponent::SwitchRenderPipeline(const AZ::RPI::RenderPipelineDescriptor& newRenderPipelineDesc, AZ::RPI::ViewportContextPtr viewportContext)
  579. {
  580. AZ::RPI::RenderPipelineDescriptor pipelineDescriptor = newRenderPipelineDesc;
  581. pipelineDescriptor.m_name =
  582. AZStd::string::format("%s_%i", pipelineDescriptor.m_name.c_str(), viewportContext->GetId());
  583. if (pipelineDescriptor.m_renderSettings.m_multisampleState.m_customPositionsCount &&
  584. !RHI::RHISystemInterface::Get()->GetDevice()->GetFeatures().m_customSamplePositions)
  585. {
  586. // Disable custom sample positions because they are not supported
  587. AZ_Warning(
  588. "BootstrapSystemComponent",
  589. false,
  590. "Disabling custom sample positions for pipeline %s because they are not supported on this device",
  591. pipelineDescriptor.m_name.c_str());
  592. pipelineDescriptor.m_renderSettings.m_multisampleState.m_customPositions = {};
  593. pipelineDescriptor.m_renderSettings.m_multisampleState.m_customPositionsCount = 0;
  594. }
  595. // Create new render pipeline
  596. auto oldRenderPipeline = viewportContext->GetRenderScene()->GetDefaultRenderPipeline();
  597. RPI::RenderPipelinePtr newRenderPipeline = RPI::RenderPipeline::CreateRenderPipelineForWindow(
  598. pipelineDescriptor, *viewportContext->GetWindowContext().get(), AZ::RPI::ViewType::Default);
  599. // Switch render pipeline
  600. viewportContext->GetRenderScene()->RemoveRenderPipeline(oldRenderPipeline->GetId());
  601. auto view = oldRenderPipeline->GetDefaultView();
  602. oldRenderPipeline = nullptr;
  603. viewportContext->GetRenderScene()->AddRenderPipeline(newRenderPipeline);
  604. newRenderPipeline->SetDefaultView(view);
  605. AZ::RPI::RPISystemInterface::Get()->SetApplicationMultisampleState(newRenderPipeline->GetRenderSettings().m_multisampleState);
  606. }
  607. void BootstrapSystemComponent::SwitchAntiAliasing(const AZStd::string& newAntiAliasing, AZ::RPI::ViewportContextPtr viewportContext)
  608. {
  609. auto defaultRenderPipeline = viewportContext->GetRenderScene()->GetDefaultRenderPipeline();
  610. defaultRenderPipeline->SetActiveAAMethod(newAntiAliasing);
  611. }
  612. void BootstrapSystemComponent::SwitchMultiSample(const uint16_t newSampleCount, AZ::RPI::ViewportContextPtr viewportContext)
  613. {
  614. auto multiSampleStete = viewportContext->GetRenderScene()->GetDefaultRenderPipeline()->GetRenderSettings().m_multisampleState;
  615. multiSampleStete.m_samples = newSampleCount;
  616. AZ::RPI::RPISystemInterface::Get()->SetApplicationMultisampleState(multiSampleStete);
  617. }
  618. RPI::RenderPipelinePtr BootstrapSystemComponent::LoadPipeline( AZ::RPI::ScenePtr scene, AZ::RPI::ViewportContextPtr viewportContext,
  619. AZStd::string_view pipelineName, AZ::RPI::ViewType viewType, AZ::RHI::MultisampleState& multisampleState)
  620. {
  621. // Create a render pipeline from the specified asset for the window context and add the pipeline to the scene.
  622. // When running with an Asset Processor, this will attempt to compile the asset before loading it.
  623. Data::Asset<RPI::AnyAsset> pipelineAsset =
  624. RPI::AssetUtils::LoadCriticalAsset<RPI::AnyAsset>(pipelineName.data(), RPI::AssetUtils::TraceLevel::Error);
  625. if (pipelineAsset)
  626. {
  627. RPI::RenderPipelineDescriptor renderPipelineDescriptor =
  628. *RPI::GetDataFromAnyAsset<RPI::RenderPipelineDescriptor>(pipelineAsset); // Copy descriptor from asset
  629. pipelineAsset.Release();
  630. renderPipelineDescriptor.m_name =
  631. AZStd::string::format("%s_%i", renderPipelineDescriptor.m_name.c_str(), viewportContext->GetId());
  632. if (renderPipelineDescriptor.m_renderSettings.m_multisampleState.m_customPositionsCount &&
  633. !RHI::RHISystemInterface::Get()->GetDevice()->GetFeatures().m_customSamplePositions)
  634. {
  635. // Disable custom sample positions because they are not supported
  636. AZ_Warning(
  637. "BootstrapSystemComponent",
  638. false,
  639. "Disabling custom sample positions for pipeline %s because they are not supported on this device",
  640. pipelineName.data());
  641. renderPipelineDescriptor.m_renderSettings.m_multisampleState.m_customPositions = {};
  642. renderPipelineDescriptor.m_renderSettings.m_multisampleState.m_customPositionsCount = 0;
  643. }
  644. multisampleState = renderPipelineDescriptor.m_renderSettings.m_multisampleState;
  645. // Create and add render pipeline to the scene (when not added already)
  646. RPI::RenderPipelinePtr renderPipeline = scene->GetRenderPipeline(AZ::Name(renderPipelineDescriptor.m_name));
  647. if (!renderPipeline)
  648. {
  649. renderPipeline = RPI::RenderPipeline::CreateRenderPipelineForWindow(
  650. renderPipelineDescriptor, *viewportContext->GetWindowContext().get(), viewType);
  651. scene->AddRenderPipeline(renderPipeline);
  652. }
  653. return renderPipeline;
  654. }
  655. else
  656. {
  657. AZ_Error("AtomBootstrap", false, "Pipeline file failed to load from path: %s.", pipelineName.data());
  658. return nullptr;
  659. }
  660. }
  661. AzFramework::WindowSize BootstrapSystemComponent::GetWindowResolution() const
  662. {
  663. float scale = AZStd::max(static_cast<float>(r_renderScale), 0.f);
  664. return AzFramework::WindowSize(static_cast<uint32_t>(r_width * scale), static_cast<uint32_t>(r_height * scale));
  665. }
  666. void BootstrapSystemComponent::CreateDefaultRenderPipeline()
  667. {
  668. EnsureDefaultRenderPipelineInstalledForScene(m_defaultScene, m_viewportContext);
  669. const auto pipeline = m_defaultScene->FindRenderPipelineForWindow(m_viewportContext->GetWindowHandle());
  670. if (pipeline)
  671. {
  672. m_renderPipelineId = pipeline->GetId();
  673. }
  674. }
  675. void BootstrapSystemComponent::DestroyDefaultScene()
  676. {
  677. if (m_defaultScene)
  678. {
  679. RPI::RPISystemInterface::Get()->UnregisterScene(m_defaultScene);
  680. // Unbind m_defaultScene to the GameEntityContext's AzFramework::Scene
  681. if (m_defaultFrameworkScene)
  682. {
  683. m_defaultFrameworkScene->UnsetSubsystem(m_defaultScene);
  684. }
  685. m_defaultScene = nullptr;
  686. m_defaultFrameworkScene = nullptr;
  687. }
  688. }
  689. void BootstrapSystemComponent::RemoveRenderPipeline()
  690. {
  691. if (m_defaultScene && m_defaultScene->GetRenderPipeline(m_renderPipelineId))
  692. {
  693. m_defaultScene->RemoveRenderPipeline(m_renderPipelineId);
  694. }
  695. m_renderPipelineId = "";
  696. }
  697. void BootstrapSystemComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] ScriptTimePoint time)
  698. {
  699. // Temp: When running in the launcher without the legacy renderer
  700. // we need to call RenderTick on the viewport context each frame.
  701. if (m_viewportContext)
  702. {
  703. AZ::ApplicationTypeQuery appType;
  704. ComponentApplicationBus::Broadcast(&AZ::ComponentApplicationBus::Events::QueryApplicationType, appType);
  705. if (appType.IsGame())
  706. {
  707. m_viewportContext->RenderTick();
  708. }
  709. }
  710. }
  711. int BootstrapSystemComponent::GetTickOrder()
  712. {
  713. return TICK_LAST;
  714. }
  715. void BootstrapSystemComponent::OnWindowClosed()
  716. {
  717. m_windowHandle = nullptr;
  718. m_viewportContext.reset();
  719. // On some platforms (e.g. Android) the main window is destroyed when the app is suspended
  720. // but this doesn't mean that we need to exit the app. The window will be recreated when the app
  721. // is resumed.
  722. #if AZ_TRAIT_BOOTSTRAPSYSTEMCOMPONENT_EXIT_ON_WINDOW_CLOSE
  723. AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::ExitMainLoop);
  724. #endif
  725. AzFramework::WindowNotificationBus::Handler::BusDisconnect();
  726. }
  727. AzFramework::NativeWindowHandle BootstrapSystemComponent::GetDefaultWindowHandle()
  728. {
  729. return m_windowHandle;
  730. }
  731. AZStd::shared_ptr<RPI::WindowContext> BootstrapSystemComponent::GetDefaultWindowContext()
  732. {
  733. return m_viewportContext ? m_viewportContext->GetWindowContext() : nullptr;
  734. }
  735. void BootstrapSystemComponent::SetCreateDefaultScene(bool create)
  736. {
  737. m_createDefaultScene = create;
  738. }
  739. } // namespace Bootstrap
  740. } // namespace Render
  741. } // namespace AZ