XRSystem.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637
  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 <AzCore/Interface/Interface.h>
  9. #include <AzCore/Debug/Profiler.h>
  10. #include <AzCore/Console/IConsole.h>
  11. #include <AzCore/Settings/SettingsRegistry.h>
  12. #include <XR/XRFactory.h>
  13. #include <XR/XRSystem.h>
  14. #include <XR/XRUtils.h>
  15. #include <Passes/FoveatedImagePass.h>
  16. #include <Atom/RHI/Image.h>
  17. #include <Atom/RHI/ImagePool.h>
  18. #include <Atom/RHI/RHISystemInterface.h>
  19. #include <Atom/RHI.Reflect/VariableRateShadingEnums.h>
  20. #include <Atom/RHI.Reflect/Viewport.h>
  21. #include <Atom/RPI.Public/Image/AttachmentImage.h>
  22. #include <Atom/RPI.Reflect/Asset/AssetUtils.h>
  23. #include <Atom/RPI.Reflect/Image/AttachmentImageAsset.h>
  24. #include <Atom/RPI.Reflect/Image/AttachmentImageAssetCreator.h>
  25. #include <Atom/RPI.Reflect/Pass/PassTemplate.h>
  26. #include <Atom/RPI.Reflect/System/RenderPipelineDescriptor.h>
  27. namespace XR
  28. {
  29. static constexpr const char* const FoveatedAttachmentSlotName = "FoveatedImageInput";
  30. #if AZ_TRAIT_OS_IS_HOST_OS_PLATFORM
  31. AZ_CVAR(
  32. bool,
  33. r_EnableHostRenderPipelineOnXR,
  34. true,
  35. nullptr,
  36. AZ::ConsoleFunctorFlags::Null,
  37. "When an XR system is present in a host platform, this will enable the regular render pipeline on the host PC as well "
  38. "(true by default).");
  39. #endif
  40. AZ_CVAR(AZ::CVarFixedString, r_default_openxr_foveated_pass_template, "MultiViewForwardPassTemplate", nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Pass template to add foveated rendering");
  41. void System::Init(const System::Descriptor& descriptor)
  42. {
  43. m_validationMode = descriptor.m_validationMode;
  44. AZ::SystemTickBus::Handler::BusConnect();
  45. // Check settings registry for the foveated level
  46. if (AZ::SettingsRegistryInterface* settingsRegistry = AZ::SettingsRegistry::Get())
  47. {
  48. if (AZ::u64 foveatedLevel; settingsRegistry->Get(foveatedLevel, AZ::RHI::XRFoveatedLevelKey))
  49. {
  50. AZ_Assert(
  51. foveatedLevel <= static_cast<AZ::u64>(AZ::RHI::XRFoveatedLevel::High),
  52. "Invalid foveated level %d", static_cast<int>(foveatedLevel));
  53. m_foveatedLevel = static_cast<AZ::RHI::XRFoveatedLevel>(foveatedLevel);
  54. }
  55. }
  56. AzFramework::AssetCatalogEventBus::Handler::BusConnect();
  57. }
  58. AZ::RHI::ResultCode System::InitInstance()
  59. {
  60. if (GetInstance())
  61. {
  62. return GetInstance()->Init(m_validationMode);
  63. }
  64. return AZ::RHI::ResultCode::Fail;
  65. }
  66. AZ::u32 System::GetNumPhysicalDevices() const
  67. {
  68. return m_instance->GetNumPhysicalDevices();
  69. }
  70. AZ::RHI::ResultCode System::GetXRPhysicalDevice(AZ::RHI::XRPhysicalDeviceDescriptor* physicalDeviceDescriptor, int32_t index)
  71. {
  72. AZ_Error("XR", physicalDeviceDescriptor, "The descriptor is null");
  73. if (physicalDeviceDescriptor)
  74. {
  75. return GetInstance()->GetXRPhysicalDevice(physicalDeviceDescriptor, index);
  76. }
  77. return AZ::RHI::ResultCode::Fail;
  78. }
  79. AZ::RHI::ResultCode System::CreateDevice(AZ::RHI::XRDeviceDescriptor* instanceDescriptor)
  80. {
  81. if (!m_device)
  82. {
  83. m_device = Factory::Get().CreateDevice();
  84. AZ_Assert(m_device, "XR Device not created");
  85. if (m_device->Init(Device::Descriptor{ m_validationMode, GetInstance()}) == AZ::RHI::ResultCode::Success)
  86. {
  87. return m_device->InitDeviceInternal(instanceDescriptor);
  88. }
  89. }
  90. return AZ::RHI::ResultCode::Fail;
  91. }
  92. AZ::RHI::ResultCode System::CreateSession(AZ::RHI::XRSessionDescriptor* sessionDescriptor)
  93. {
  94. if (!m_session)
  95. {
  96. m_session = Factory::Get().CreateSession();
  97. AZ_Assert(m_session, "Session not created");
  98. AZ::RHI::ResultCode result = m_session->Init(Session::Descriptor{ m_validationMode, m_device, GetInstance() });
  99. if (result == AZ::RHI::ResultCode::Success)
  100. {
  101. return m_session->InitInternal(sessionDescriptor);
  102. }
  103. }
  104. return AZ::RHI::ResultCode::Fail;
  105. }
  106. AZ::RHI::ResultCode System::CreateSwapChain()
  107. {
  108. if (!m_swapChain)
  109. {
  110. m_swapChain = Factory::Get().CreateSwapChain();
  111. AZ_Assert(m_swapChain, "XR SwapChain not created");
  112. return m_swapChain->Init(SwapChain::Descriptor{ m_validationMode, GetInstance(), m_session, m_device });
  113. }
  114. return AZ::RHI::ResultCode::Fail;
  115. }
  116. AZ::RHI::ResultCode System::GetSwapChainImage(AZ::RHI::XRSwapChainDescriptor* swapchainDescriptor) const
  117. {
  118. AZ_Assert(m_swapChain, "SwapChain is null");
  119. if (m_swapChain)
  120. {
  121. return m_swapChain->GetSwapChainImage(swapchainDescriptor);
  122. }
  123. return AZ::RHI::ResultCode::Fail;
  124. }
  125. AZ::u32 System::GetSwapChainWidth(AZ::u32 viewIndex) const
  126. {
  127. AZ_Assert(m_swapChain, "SwapChain is null");
  128. if (m_swapChain)
  129. {
  130. return m_swapChain->GetSwapChainWidth(viewIndex);
  131. }
  132. return 0;
  133. }
  134. AZ::u32 System::GetSwapChainHeight(AZ::u32 viewIndex) const
  135. {
  136. AZ_Assert(m_swapChain, "SwapChain is null");
  137. if (m_swapChain)
  138. {
  139. return m_swapChain->GetSwapChainHeight(viewIndex);
  140. }
  141. return 0;
  142. }
  143. AZ::RHI::Format System::GetSwapChainFormat(AZ::u32 viewIndex) const
  144. {
  145. AZ_Assert(m_swapChain, "SwapChain is null");
  146. if (m_swapChain)
  147. {
  148. return m_swapChain->GetSwapChainFormat(viewIndex);
  149. }
  150. return AZ::RHI::Format::Unknown;
  151. }
  152. void System::OnSystemTick()
  153. {
  154. if (m_session)
  155. {
  156. m_session->PollEvents();
  157. }
  158. }
  159. void System::BeginFrame()
  160. {
  161. if (m_device && m_session && m_session->IsSessionRunning())
  162. {
  163. m_isInFrame = m_device->BeginFrame();
  164. }
  165. }
  166. void System::EndFrame()
  167. {
  168. if (m_isInFrame)
  169. {
  170. m_device->EndFrame(m_swapChain);
  171. m_isInFrame = false;
  172. }
  173. }
  174. void System::PostFrame()
  175. {
  176. if (m_device && m_session && m_session->IsSessionRunning())
  177. {
  178. m_device->PostFrame();
  179. }
  180. }
  181. void System::AcquireSwapChainImage(AZ::u32 viewIndex)
  182. {
  183. if (m_isInFrame && m_device->ShouldRender())
  184. {
  185. m_device->AcquireSwapChainImage(viewIndex, m_swapChain.get());
  186. }
  187. }
  188. AZ::u32 System::GetNumViews() const
  189. {
  190. if (m_swapChain)
  191. {
  192. return m_swapChain->GetNumViews();
  193. }
  194. AZ_Warning("XRSystem", false, "SwapChain is null");
  195. return 0;
  196. }
  197. AZ::u32 System::GetCurrentImageIndex(AZ::u32 viewIndex) const
  198. {
  199. SwapChain::View* viewSwapchain = m_swapChain->GetView(viewIndex);
  200. return viewSwapchain->m_activeImageIndex;
  201. }
  202. bool System::ShouldRender() const
  203. {
  204. if (m_session && m_session->IsSessionRunning())
  205. {
  206. return m_device->ShouldRender();
  207. }
  208. return false;
  209. }
  210. AZ::RHI::ResultCode System::GetViewFov(AZ::u32 viewIndex, AZ::RPI::FovData& outFovData) const
  211. {
  212. return m_device->GetViewFov(viewIndex, outFovData);
  213. }
  214. AZ::RHI::ResultCode System::GetViewPose(AZ::u32 viewIndex, AZ::RPI::PoseData& outPoseData) const
  215. {
  216. return m_device->GetViewPose(viewIndex, outPoseData);
  217. }
  218. AZ::RHI::ResultCode System::GetControllerPose(AZ::u32 handIndex, AZ::RPI::PoseData& outPoseData) const
  219. {
  220. if (m_session->IsSessionRunning())
  221. {
  222. return m_session->GetControllerPose(handIndex, outPoseData);
  223. }
  224. return AZ::RHI::ResultCode::NotReady;
  225. }
  226. AZ::RHI::ResultCode System::GetControllerStagePose(AZ::u32 handIndex, AZ::RPI::PoseData& outPoseData) const
  227. {
  228. if (m_session->IsSessionRunning())
  229. {
  230. return m_session->GetControllerStagePose(handIndex, outPoseData);
  231. }
  232. return AZ::RHI::ResultCode::NotReady;
  233. }
  234. AZ::RHI::ResultCode System::GetViewFrontPose(AZ::RPI::PoseData& outPoseData) const
  235. {
  236. if (m_session->IsSessionRunning())
  237. {
  238. return m_session->GetViewFrontPose(outPoseData);
  239. }
  240. return AZ::RHI::ResultCode::NotReady;
  241. }
  242. AZ::RHI::ResultCode System::GetViewLocalPose(AZ::RPI::PoseData& outPoseData) const
  243. {
  244. if (m_session->IsSessionRunning())
  245. {
  246. return m_session->GetViewLocalPose(outPoseData);
  247. }
  248. return AZ::RHI::ResultCode::NotReady;
  249. }
  250. float System::GetControllerScale(AZ::u32 handIndex) const
  251. {
  252. if (m_session->IsSessionRunning())
  253. {
  254. return m_session->GetControllerScale(handIndex);
  255. }
  256. return 1.0f;
  257. }
  258. float System::GetSqueezeState(AZ::u32 handIndex) const
  259. {
  260. if (m_session->IsSessionRunning())
  261. {
  262. return m_session->GetSqueezeState(handIndex);
  263. }
  264. return 0.0f;
  265. }
  266. float System::GetTriggerState(AZ::u32 handIndex) const
  267. {
  268. if (m_session->IsSessionRunning())
  269. {
  270. return m_session->GetTriggerState(handIndex);
  271. }
  272. return 0.0f;
  273. }
  274. float System::GetXButtonState() const
  275. {
  276. if (m_session->IsSessionRunning())
  277. {
  278. return m_session->GetXButtonState();
  279. }
  280. return 0.0f;
  281. }
  282. float System::GetYButtonState() const
  283. {
  284. if (m_session->IsSessionRunning())
  285. {
  286. return m_session->GetYButtonState();
  287. }
  288. return 0.0f;
  289. }
  290. float System::GetAButtonState() const
  291. {
  292. if (m_session->IsSessionRunning())
  293. {
  294. return m_session->GetAButtonState();
  295. }
  296. return 0.0f;
  297. }
  298. float System::GetBButtonState() const
  299. {
  300. if (m_session->IsSessionRunning())
  301. {
  302. return m_session->GetBButtonState();
  303. }
  304. return 0.0f;
  305. }
  306. float System::GetXJoyStickState(AZ::u32 handIndex) const
  307. {
  308. if (m_session->IsSessionRunning())
  309. {
  310. return m_session->GetXJoyStickState(handIndex);
  311. }
  312. return 0.0f;
  313. }
  314. float System::GetYJoyStickState(AZ::u32 handIndex) const
  315. {
  316. if (m_session->IsSessionRunning())
  317. {
  318. return m_session->GetYJoyStickState(handIndex);
  319. }
  320. return 0.0f;
  321. }
  322. AZ::Matrix4x4 System::CreateStereoscopicProjection(float angleLeft, float angleRight,
  323. float angleBottom, float angleTop,
  324. float nearDist, float farDist, bool reverseDepth)
  325. {
  326. return XR::CreateStereoscopicProjection(angleLeft, angleRight, angleBottom, angleTop, nearDist, farDist, reverseDepth);
  327. }
  328. AZ::RHI::XRRenderingInterface* System::GetRHIXRRenderingInterface()
  329. {
  330. return this;
  331. }
  332. void System::Shutdown()
  333. {
  334. AZ::RPI::PassSystemTemplateNotificationsBus::MultiHandler::BusDisconnect();
  335. AzFramework::AssetCatalogEventBus::Handler::BusDisconnect();
  336. AZ::SystemTickBus::Handler::BusDisconnect();
  337. m_instance = nullptr;
  338. m_device = nullptr;
  339. }
  340. bool System::IsDefaultRenderPipelineNeeded() const
  341. {
  342. // While there is an XR system the default render pipeline is only needed on host platforms,
  343. // in case we also render on PC as well as in the XR device.
  344. #if AZ_TRAIT_OS_IS_HOST_OS_PLATFORM
  345. return true;
  346. #else
  347. return false;
  348. #endif
  349. }
  350. bool System::IsDefaultRenderPipelineEnabledOnHost() const
  351. {
  352. #if AZ_TRAIT_OS_IS_HOST_OS_PLATFORM
  353. return r_EnableHostRenderPipelineOnXR;
  354. #else
  355. return false;
  356. #endif
  357. }
  358. AZ::RHI::ResultCode System::InitVariableRateShadingImageContent(AZ::RHI::Image* image, AZ::RHI::XRFoveatedLevel level) const
  359. {
  360. AZ_Assert(image, "Null variable rate shading image");
  361. const auto& imageDescriptor = image->GetDescriptor();
  362. uint32_t width = imageDescriptor.m_size.m_width;
  363. uint32_t height = imageDescriptor.m_size.m_height;
  364. uint32_t formatSize = GetFormatSize(imageDescriptor.m_format);
  365. uint32_t bufferSize = width * height * formatSize;
  366. // Get a list of supported shading rates so we always write a valid one
  367. const AZ::RHI::Device& device = image->GetDevice();
  368. const auto& features = device.GetFeatures();
  369. AZ::RHI::ShadingRate supportedRates[static_cast<int>(AZ::RHI::ShadingRate::Count)];
  370. AZ::RHI::ShadingRate lastSupported = AZ::RHI::ShadingRate::Rate1x1;
  371. for (int i = 0; i < AZ_ARRAY_SIZE(supportedRates); ++i)
  372. {
  373. if (AZ::RHI::CheckBitsAll(features.m_shadingRateMask, static_cast<AZ::RHI::ShadingRateFlags>(AZ_BIT(i))))
  374. {
  375. supportedRates[i] = static_cast<AZ::RHI::ShadingRate>(i);
  376. lastSupported = supportedRates[i];
  377. }
  378. else
  379. {
  380. supportedRates[i] = lastSupported;
  381. }
  382. }
  383. // Divide the image in a grid of "gridSize" and each cell will have a shading rate.
  384. constexpr uint32_t gridSize = 8;
  385. AZStd::vector<uint8_t> shadingRatePatternData(bufferSize);
  386. AZ::RHI::ShadingRate rateGrid[gridSize][gridSize];
  387. // Initialize the whole image with the normal rate. FYI - memset only works with 0 (i.e Rate1x1).
  388. ::memset(rateGrid, static_cast<int>(AZ::RHI::ShadingRate::Rate1x1), sizeof(rateGrid));
  389. // Helper function to fill up the grid
  390. auto fillFunc = [&](AZ::RHI::ShadingRate rate, int col, int row, int colCount, int rowCount)
  391. {
  392. for (int i = col; i < col + colCount; ++i)
  393. {
  394. for (int ii = row; ii < row + rowCount; ++ii)
  395. {
  396. rateGrid[i][ii] = supportedRates[static_cast<uint32_t>(rate)];
  397. // The image is symmetric on the vertical axis
  398. rateGrid[gridSize - 1 - i][ii] = supportedRates[static_cast<uint32_t>(rate)];
  399. }
  400. }
  401. };
  402. // Each level has it's own shading rates and regions
  403. switch (level)
  404. {
  405. case AZ::RHI::XRFoveatedLevel::Low:
  406. {
  407. // _______________________________________________
  408. // |_4x4_|________________2x2________________|_4x4_|
  409. // | | 1x2 | | 1x2 | |
  410. // | |_____| |_____| |
  411. // | | | |
  412. // | 2x2 | 1x1 | 2x2 |
  413. // | | | |
  414. // | |___________________________________| |
  415. // |______________________2x2______________________|
  416. //
  417. fillFunc(AZ::RHI::ShadingRate::Rate4x4, 0, 0, 1, 1);
  418. fillFunc(AZ::RHI::ShadingRate::Rate2x2, 1, 0, 3, 1);
  419. fillFunc(AZ::RHI::ShadingRate::Rate2x2, 0, 1, 1, gridSize - 1);
  420. fillFunc(AZ::RHI::ShadingRate::Rate1x2, 1, 1, 1, 2);
  421. fillFunc(AZ::RHI::ShadingRate::Rate2x2, 1, gridSize - 1, 3, 1);
  422. }
  423. break;
  424. case AZ::RHI::XRFoveatedLevel::Medium:
  425. {
  426. // 1 2 3 4 5 6 7 8
  427. // _______________________________________________
  428. // |______4x4_______|____2x2____|____4x4___________| 1
  429. // | | | | | | 2
  430. // | | | | | | 3
  431. // | 4x4 | 2x2 | | 2x2 | 4x4 | 4
  432. // | | | 1x1 | | | 5
  433. // | | | | | | 6
  434. // |_____|_____|_______________________|_____|_____| 7
  435. // |_______4x4______|____2x2____|____4x4___________| 8
  436. //
  437. fillFunc(AZ::RHI::ShadingRate::Rate4x4, 0, 0, 3, 1);
  438. fillFunc(AZ::RHI::ShadingRate::Rate4x4, 0, 1, 1, 6);
  439. fillFunc(AZ::RHI::ShadingRate::Rate4x4, 0, gridSize - 1, 3, 1);
  440. fillFunc(AZ::RHI::ShadingRate::Rate2x2, 1, 1, 1, 6);
  441. fillFunc(AZ::RHI::ShadingRate::Rate2x2, 3, 0, 1, 1);
  442. fillFunc(AZ::RHI::ShadingRate::Rate2x2, 3, gridSize - 1, 1, 1);
  443. }
  444. break;
  445. case AZ::RHI::XRFoveatedLevel::High:
  446. {
  447. // 1 2 3 4 5 6 7 8
  448. // _______________________________________________
  449. // |______________________4x4 _____________________| 1
  450. // | | | | | | | | 2
  451. // | | | | | | | | 3
  452. // | 4x4 | 4x2 | 2x2 | | 2x2 | 4x2 | 4x4 | 4
  453. // | | | | 1x1 | | | | 5
  454. // | | | | | | | | 6
  455. // |_____|_____|_____|___________|_____|_____|_____| 7
  456. // |______________________4x4______________________| 8
  457. fillFunc(AZ::RHI::ShadingRate::Rate4x4, 0, 0, 4, 1);
  458. fillFunc(AZ::RHI::ShadingRate::Rate4x4, 0, 1, 1, 6);
  459. fillFunc(AZ::RHI::ShadingRate::Rate4x4, 0, gridSize - 1, 4, 1);
  460. fillFunc(AZ::RHI::ShadingRate::Rate4x2, 1, 1, 1, 6);
  461. fillFunc(AZ::RHI::ShadingRate::Rate2x2, 2, 1, 1, 6);
  462. }
  463. break;
  464. case AZ::RHI::XRFoveatedLevel::None:
  465. // Intentionally leave the rate grid with default values
  466. break;
  467. default:
  468. AZ_Assert(false, "Invalid AZ::RHI::XRFoveatedLevel value %d", level);
  469. return AZ::RHI::ResultCode::InvalidArgument;
  470. }
  471. uint8_t* ptrData = shadingRatePatternData.data();
  472. float widthRegion = width / static_cast<float>(gridSize);
  473. float heightRegion = height / static_cast<float>(gridSize);
  474. for (uint32_t y = 0; y < height; y++)
  475. {
  476. for (uint32_t x = 0; x < width; x++)
  477. {
  478. auto val = device.ConvertShadingRate(rateGrid[static_cast<uint32_t>(x / widthRegion)][static_cast<uint32_t>(y / heightRegion)]);
  479. ::memcpy(ptrData, &val, formatSize);
  480. ptrData += formatSize;
  481. }
  482. }
  483. AZ::RHI::ImageUpdateRequest request;
  484. request.m_image = image;
  485. request.m_sourceData = shadingRatePatternData.data();
  486. request.m_sourceSubresourceLayout =
  487. AZ::RHI::ImageSubresourceLayout(AZ::RHI::Size(width, height, 1), height, width * formatSize, bufferSize, 1, 1);
  488. AZ::RHI::ImagePool* imagePool = azrtti_cast<AZ::RHI::ImagePool*>(image->GetPool());
  489. return imagePool->UpdateImageContents(request);
  490. }
  491. void System::OnCatalogLoaded(const char*)
  492. {
  493. // Check device features first
  494. if (AZ::RHI::Device* device = AZ::RHI::RHISystemInterface::Get()->GetDevice();
  495. m_foveatedLevel != AZ::RHI::XRFoveatedLevel::None &&
  496. AZ::RHI::CheckBitsAll(device->GetFeatures().m_shadingRateTypeMask, AZ::RHI::ShadingRateTypeFlags::PerRegion))
  497. {
  498. // Start listening for the openxr pipeline and the r_default_openxr_foveated_pass_template so we can add the shading rate attachment.
  499. if (ConnectToPipelineTemplateListener("r_default_openxr_left_pipeline_name") &&
  500. ConnectToPipelineTemplateListener("r_default_openxr_right_pipeline_name"))
  501. {
  502. AZ::RPI::PassSystemTemplateNotificationsBus::MultiHandler::BusConnect(AZ::Name(static_cast<AZ::CVarFixedString>(r_default_openxr_foveated_pass_template)));
  503. }
  504. }
  505. }
  506. void System::OnAddingPassTemplate(const AZStd::shared_ptr<AZ::RPI::PassTemplate>& passTemplate)
  507. {
  508. AZ::Name foveatedPassTemplate = AZ::Name(static_cast<AZ::CVarFixedString>(r_default_openxr_foveated_pass_template));
  509. if (passTemplate->m_name == foveatedPassTemplate)
  510. {
  511. // Add the Shading Rate Attachment to the r_default_openxr_foveated_pass_template Pass
  512. AZ::RPI::PassSlot slot;
  513. slot.m_name = AZ::Name(FoveatedAttachmentSlotName);
  514. slot.m_slotType = AZ::RPI::PassSlotType::Input;
  515. slot.m_scopeAttachmentUsage = AZ::RHI::ScopeAttachmentUsage::ShadingRate;
  516. slot.m_loadStoreAction.m_storeAction = AZ::RHI::AttachmentStoreAction::DontCare;
  517. passTemplate->AddSlot(slot);
  518. }
  519. else
  520. {
  521. // Need to add the FoveatedImagePass that is in charge of initialization of the foveated image
  522. auto findIt = AZStd::find_if(passTemplate->m_passRequests.begin(), passTemplate->m_passRequests.end(), [&](AZ::RPI::PassRequest& request)
  523. {
  524. return request.m_templateName == foveatedPassTemplate;
  525. });
  526. if (findIt != passTemplate->m_passRequests.end())
  527. {
  528. // Add the connection between the FoveatedImagePass and the r_default_openxr_foveated_pass_template
  529. AZ::RPI::PassConnection connection;
  530. connection.m_localSlot = AZ::Name(FoveatedAttachmentSlotName);
  531. connection.m_attachmentRef.m_pass = "FoveatedImagePass";
  532. connection.m_attachmentRef.m_attachment = AZ::Name(FoveatedImageSlotName);
  533. findIt->AddInputConnection(connection);
  534. // Add the FoveatedImagePass to the OpenXR pipeline
  535. AZ::RPI::PassRequest request;
  536. request.m_passName = connection.m_attachmentRef.m_pass;
  537. request.m_templateName = AZ::Name(FoveatedImagePassTemplateName);
  538. AZStd::shared_ptr<FoveatedImagePassData> passData = AZStd::make_shared<FoveatedImagePassData>();
  539. passData->m_foveatedLevel = m_foveatedLevel;
  540. request.m_passData = passData;
  541. passTemplate->m_passRequests.insert(findIt, request);
  542. }
  543. }
  544. }
  545. Instance* System::GetInstance()
  546. {
  547. if (!m_instance)
  548. {
  549. m_instance = AZ::Interface<Instance>::Get();
  550. }
  551. return m_instance.get();
  552. }
  553. bool System::ConnectToPipelineTemplateListener(const char* pipelineAsseCvar)
  554. {
  555. // Get the pipeline asset so we can get the root template.
  556. // We will add the FoveatedImagePass to the root template of the pipeline.
  557. auto* console = AZ::Interface<AZ::IConsole>::Get();
  558. AZ::CVarFixedString pipelineName;
  559. auto result = console->GetCvarValue(pipelineAsseCvar, pipelineName);
  560. if (result != AZ::GetValueResult::Success)
  561. {
  562. return false;
  563. }
  564. AZ::Data::Asset<AZ::RPI::AnyAsset> pipelineAsset =
  565. AZ::RPI::AssetUtils::LoadCriticalAsset<AZ::RPI::AnyAsset>(pipelineName.data(), AZ::RPI::AssetUtils::TraceLevel::Error);
  566. if (!pipelineAsset)
  567. {
  568. return false;
  569. }
  570. AZ::RPI::RenderPipelineDescriptor renderPipelineDescriptor =
  571. *AZ::RPI::GetDataFromAnyAsset<AZ::RPI::RenderPipelineDescriptor>(pipelineAsset); // Copy descriptor from asset
  572. pipelineAsset.Release();
  573. AZ::RPI::PassSystemTemplateNotificationsBus::MultiHandler::BusConnect(AZ::Name(renderPipelineDescriptor.m_rootPassTemplate));
  574. return true;
  575. }
  576. }