RenderPass.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653
  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 <Atom/RHI/CommandList.h>
  9. #include <Atom/RHI/FrameGraphAttachmentInterface.h>
  10. #include <Atom/RHI/FrameGraphBuilder.h>
  11. #include <Atom/RHI/FrameGraphCompileContext.h>
  12. #include <Atom/RHI/FrameGraphExecuteContext.h>
  13. #include <Atom/RHI/RHIUtils.h>
  14. #include <Atom/RHI.Reflect/ImageScopeAttachmentDescriptor.h>
  15. #include <Atom/RHI.Reflect/Size.h>
  16. #include <Atom/RPI.Public/Base.h>
  17. #include <Atom/RPI.Reflect/Pass/RenderPassData.h>
  18. #include <Atom/RPI.Public/GpuQuery/Query.h>
  19. #include <Atom/RPI.Public/Pass/PassUtils.h>
  20. #include <Atom/RPI.Public/Pass/RenderPass.h>
  21. #include <Atom/RPI.Public/Pass/Specific/ImageAttachmentPreviewPass.h>
  22. #include <Atom/RPI.Public/RenderPipeline.h>
  23. #include <Atom/RPI.Public/Scene.h>
  24. #include <Atom/RPI.Public/View.h>
  25. namespace AZ
  26. {
  27. namespace RPI
  28. {
  29. RenderPass::RenderPass(const PassDescriptor& descriptor)
  30. : Pass(descriptor)
  31. {
  32. m_flags.m_canBecomeASubpass = true;
  33. // Read view tag from pass data
  34. const RenderPassData* passData = PassUtils::GetPassData<RenderPassData>(descriptor);
  35. if (passData)
  36. {
  37. if (!passData->m_pipelineViewTag.IsEmpty())
  38. {
  39. SetPipelineViewTag(passData->m_pipelineViewTag);
  40. }
  41. if (passData->m_bindViewSrg)
  42. {
  43. m_flags.m_bindViewSrg = true;
  44. }
  45. m_flags.m_canBecomeASubpass = passData->m_canBecomeASubpass;
  46. }
  47. }
  48. RenderPass::~RenderPass()
  49. {
  50. }
  51. bool RenderPass::CanBecomeSubpass() const
  52. {
  53. return m_flags.m_canBecomeASubpass;
  54. }
  55. RHI::RenderAttachmentConfiguration RenderPass::GetRenderAttachmentConfiguration()
  56. {
  57. AZ_Assert(
  58. m_renderAttachmentConfiguration.has_value(), "Null RenderAttachmentConfiguration for pass [%s]", GetPathName().GetCStr());
  59. return m_renderAttachmentConfiguration.value();
  60. }
  61. void RenderPass::SetRenderAttachmentConfiguration(
  62. const RHI::RenderAttachmentConfiguration& configuration, const AZ::RHI::ScopeGroupId& subpassGroupId)
  63. {
  64. m_renderAttachmentConfiguration = configuration;
  65. m_subpassGroupId = subpassGroupId;
  66. }
  67. bool RenderPass::BuildSubpassLayout(RHI::RenderAttachmentLayoutBuilder::SubpassAttachmentLayoutBuilder& subpassLayoutBuilder)
  68. {
  69. // Replace all subpass inputs as shader inputs if we are the first subpass in the group.
  70. // This could happen if we have a subpass group that could be merged with other group(s), but it didn't happen
  71. // due to some pass breaking the subpass chaining.
  72. if (m_flags.m_hasSubpassInput && subpassLayoutBuilder.GetSubpassIndex() == 0)
  73. {
  74. ReplaceSubpassInputs(RHI::SubpassInputSupportType::None);
  75. }
  76. int slotIndex = -1;
  77. for (const auto& binding : m_attachmentBindings)
  78. {
  79. slotIndex++;
  80. if (!binding.GetAttachment())
  81. {
  82. continue;
  83. }
  84. // Handle the depth-stencil attachment. There should be only one.
  85. if (binding.m_scopeAttachmentUsage == RHI::ScopeAttachmentUsage::DepthStencil)
  86. {
  87. subpassLayoutBuilder.DepthStencilAttachment(
  88. binding.GetAttachment()->m_descriptor.m_image.m_format,
  89. binding.GetAttachment()->GetAttachmentId(),
  90. binding.m_unifiedScopeDesc.m_loadStoreAction,
  91. binding.GetAttachmentAccess(),
  92. binding.m_scopeAttachmentStage);
  93. continue;
  94. }
  95. // Handle shading rate attachment. There should be only one.
  96. if (binding.m_scopeAttachmentUsage == RHI::ScopeAttachmentUsage::ShadingRate)
  97. {
  98. subpassLayoutBuilder.ShadingRateAttachment(
  99. binding.GetAttachment()->m_descriptor.m_image.m_format, binding.GetAttachment()->GetAttachmentId());
  100. continue;
  101. }
  102. if (binding.m_scopeAttachmentUsage == RHI::ScopeAttachmentUsage::SubpassInput)
  103. {
  104. AZ_Assert(subpassLayoutBuilder.GetSubpassIndex() > 0, "The first subpass can't have attachments used as SubpassInput");
  105. AZ_Assert(
  106. binding.m_unifiedScopeDesc.GetType() == RHI::AttachmentType::Image,
  107. "Only image attachments are allowed as SubpassInput.");
  108. const auto aspectFlags = binding.m_unifiedScopeDesc.GetAsImage().m_imageViewDescriptor.m_aspectFlags;
  109. subpassLayoutBuilder.SubpassInputAttachment(
  110. binding.GetAttachment()->GetAttachmentId(),
  111. aspectFlags,
  112. binding.m_unifiedScopeDesc.m_loadStoreAction);
  113. continue;
  114. }
  115. if (binding.m_scopeAttachmentUsage == RHI::ScopeAttachmentUsage::RenderTarget)
  116. {
  117. RHI::Format format = binding.GetAttachment()->m_descriptor.m_image.m_format;
  118. subpassLayoutBuilder.RenderTargetAttachment(
  119. format,
  120. binding.GetAttachment()->GetAttachmentId(),
  121. binding.m_unifiedScopeDesc.m_loadStoreAction,
  122. false /*resolve*/);
  123. continue;
  124. }
  125. if (binding.m_scopeAttachmentUsage == RHI::ScopeAttachmentUsage::Resolve)
  126. {
  127. // A Resolve attachment must be declared immediately after the RenderTarget it is supposed to resolve.
  128. AZ_Assert(slotIndex > 0, "A Resolve attachment can not be in the first slot binding.");
  129. const auto& renderTargetBinding = m_attachmentBindings[slotIndex - 1];
  130. AZ_Assert(renderTargetBinding.m_scopeAttachmentUsage == RHI::ScopeAttachmentUsage::RenderTarget,
  131. "A Resolve attachment must be declared immediately after a RenderTarget attachment.");
  132. subpassLayoutBuilder.ResolveAttachment(renderTargetBinding.GetAttachment()->GetAttachmentId(), binding.GetAttachment()->GetAttachmentId());
  133. continue;
  134. }
  135. }
  136. return true;
  137. }
  138. void RenderPass::BuildRenderAttachmentConfiguration()
  139. {
  140. if (m_renderAttachmentConfiguration)
  141. {
  142. // Already has a render attachment configuration. Nothing to do.
  143. return;
  144. }
  145. RHI::RenderAttachmentLayoutBuilder builder;
  146. auto* layoutBuilder = builder.AddSubpass();
  147. BuildSubpassLayout(*layoutBuilder);
  148. if (!layoutBuilder->HasAttachments())
  149. {
  150. return;
  151. }
  152. RHI::RenderAttachmentLayout subpassLayout;
  153. [[maybe_unused]] RHI::ResultCode result = builder.End(subpassLayout);
  154. AZ_Assert(
  155. result == RHI::ResultCode::Success, "RenderPass [%s] failed to create render attachment configuration", GetPathName().GetCStr());
  156. m_renderAttachmentConfiguration = RHI::RenderAttachmentConfiguration{ AZStd::move(subpassLayout), 0 };
  157. }
  158. RHI::MultisampleState RenderPass::GetMultisampleState() const
  159. {
  160. RHI::MultisampleState outputMultiSampleState;
  161. bool wasSet = false;
  162. for (const auto& binding : m_attachmentBindings)
  163. {
  164. if (binding.m_slotType != PassSlotType::Output && binding.m_slotType != PassSlotType::InputOutput)
  165. {
  166. continue;
  167. }
  168. if (!binding.GetAttachment())
  169. {
  170. continue;
  171. }
  172. if (binding.m_scopeAttachmentUsage == RHI::ScopeAttachmentUsage::RenderTarget
  173. || binding.m_scopeAttachmentUsage == RHI::ScopeAttachmentUsage::DepthStencil)
  174. {
  175. if (!wasSet)
  176. {
  177. // save multi-sample state found in the first output color attachment
  178. outputMultiSampleState = binding.GetAttachment()->m_descriptor.m_image.m_multisampleState;
  179. wasSet = true;
  180. }
  181. else if (PassValidation::IsEnabled())
  182. {
  183. // return false directly if the current output color attachment has different multi-sample state then previous ones
  184. if (outputMultiSampleState != binding.GetAttachment()->m_descriptor.m_image.m_multisampleState)
  185. {
  186. AZ_Error("RPI", false, "Pass %s has different multi-sample states within its color attachments", GetPathName().GetCStr());
  187. break;
  188. }
  189. }
  190. else
  191. {
  192. break;
  193. }
  194. }
  195. }
  196. return outputMultiSampleState;
  197. }
  198. void RenderPass::InitializeInternal()
  199. {
  200. if (m_shaderResourceGroup != nullptr)
  201. {
  202. Name autoBind = Name("AutoBind");
  203. Name noBind = Name("NoBind");
  204. for (PassAttachmentBinding& binding : m_attachmentBindings)
  205. {
  206. const Name& shaderName = binding.m_shaderInputName;
  207. PassAttachment* attachment = binding.GetAttachment().get();
  208. if (shaderName == autoBind)
  209. {
  210. binding.m_shaderInputIndex = PassAttachmentBinding::ShaderInputAutoBind;
  211. }
  212. else if (shaderName == noBind)
  213. {
  214. binding.m_shaderInputIndex = PassAttachmentBinding::ShaderInputNoBind;
  215. }
  216. else if (attachment)
  217. {
  218. if (attachment->GetAttachmentType() == RHI::AttachmentType::Image)
  219. {
  220. RHI::ShaderInputImageIndex idx = m_shaderResourceGroup->FindShaderInputImageIndex(shaderName);
  221. AZ_Error("Pass System", idx.IsValid(), "[Pass %s] Could not retrieve Shader Image Index for SRG variable'%s'", GetName().GetCStr(), shaderName.GetCStr());
  222. binding.m_shaderInputIndex = idx.IsValid() ? static_cast<int16_t>(idx.GetIndex()) : PassAttachmentBinding::ShaderInputNoBind;
  223. }
  224. else if (attachment->GetAttachmentType() == RHI::AttachmentType::Buffer)
  225. {
  226. RHI::ShaderInputBufferIndex idx = m_shaderResourceGroup->FindShaderInputBufferIndex(shaderName);
  227. AZ_Error("Pass System", idx.IsValid(), "[Pass %s] Could not retrieve Shader Buffer Index for SRG variable '%s'", GetName().GetCStr(), shaderName.GetCStr());
  228. binding.m_shaderInputIndex = idx.IsValid() ? static_cast<int16_t>(idx.GetIndex()) : PassAttachmentBinding::ShaderInputNoBind;
  229. }
  230. }
  231. else
  232. {
  233. AZ_Error( "Pass System", AZ::RHI::IsNullRHI(), "[Pass %s] Could not bind shader buffer index '%s' because it has no attachment.", GetName().GetCStr(), shaderName.GetCStr());
  234. binding.m_shaderInputIndex = PassAttachmentBinding::ShaderInputNoBind;
  235. }
  236. }
  237. }
  238. BuildRenderAttachmentConfiguration();
  239. }
  240. void RenderPass::FrameBeginInternal(FramePrepareParams params)
  241. {
  242. if (IsTimestampQueryEnabled())
  243. {
  244. m_timestampResult = AZ::RPI::TimestampResult();
  245. }
  246. // the pass may potentially migrate between devices dynamically at runtime so the deviceIndex is updated every frame.
  247. if (GetScopeId().IsEmpty() || (ScopeProducer::GetDeviceIndex() != Pass::GetDeviceIndex()))
  248. {
  249. InitScope(RHI::ScopeId(GetPathName()), m_hardwareQueueClass, Pass::GetDeviceIndex());
  250. }
  251. params.m_frameGraphBuilder->ImportScopeProducer(*this);
  252. // Read back the ScopeQueries submitted from previous frames
  253. ReadbackScopeQueryResults();
  254. CollectSrgs();
  255. PassSystemInterface::Get()->IncrementFrameRenderPassCount();
  256. }
  257. void RenderPass::FrameEndInternal()
  258. {
  259. ResetSrgs();
  260. }
  261. void RenderPass::ResetInternal()
  262. {
  263. m_renderAttachmentConfiguration.reset();
  264. m_subpassGroupId = RHI::ScopeGroupId();
  265. }
  266. void RenderPass::SetupFrameGraphDependencies(RHI::FrameGraphInterface frameGraph)
  267. {
  268. DeclareAttachmentsToFrameGraph(frameGraph);
  269. DeclarePassDependenciesToFrameGraph(frameGraph);
  270. AddScopeQueryToFrameGraph(frameGraph);
  271. }
  272. void RenderPass::BuildCommandList(const RHI::FrameGraphExecuteContext& context)
  273. {
  274. BeginScopeQuery(context);
  275. BuildCommandListInternal(context);
  276. EndScopeQuery(context);
  277. m_lastDeviceIndex = context.GetDeviceIndex();
  278. }
  279. void RenderPass::DeclarePassDependenciesToFrameGraph(RHI::FrameGraphInterface frameGraph) const
  280. {
  281. for (Pass* pass : m_executeAfterPasses)
  282. {
  283. RenderPass* renderPass = azrtti_cast<RenderPass*>(pass);
  284. if (renderPass)
  285. {
  286. frameGraph.ExecuteAfter(renderPass->GetScopeId());
  287. }
  288. }
  289. for (Pass* pass : m_executeBeforePasses)
  290. {
  291. RenderPass* renderPass = azrtti_cast<RenderPass*>(pass);
  292. if (renderPass)
  293. {
  294. frameGraph.ExecuteBefore(renderPass->GetScopeId());
  295. }
  296. }
  297. frameGraph.SetGroupId(GetSubpassGroupId());
  298. }
  299. void RenderPass::BindAttachment(const RHI::FrameGraphCompileContext& context, PassAttachmentBinding& binding, int16_t& imageIndex, int16_t& bufferIndex)
  300. {
  301. PassAttachment* attachment = binding.GetAttachment().get();
  302. if (attachment)
  303. {
  304. int16_t inputIndex = binding.m_shaderInputIndex;
  305. uint16_t arrayIndex = binding.m_shaderInputArrayIndex;
  306. if (attachment->GetAttachmentType() == RHI::AttachmentType::Image)
  307. {
  308. if (inputIndex == PassAttachmentBinding::ShaderInputAutoBind)
  309. {
  310. inputIndex = imageIndex;
  311. }
  312. const RHI::ImageView* imageView =
  313. context.GetImageView(attachment->GetAttachmentId(), binding.m_unifiedScopeDesc.GetImageViewDescriptor(), binding.m_scopeAttachmentUsage);
  314. if (binding.m_shaderImageDimensionsNameIndex.HasName())
  315. {
  316. RHI::Size size = attachment->m_descriptor.m_image.m_size;
  317. AZ::Vector4 imageDimensions;
  318. imageDimensions.SetX(float(size.m_width));
  319. imageDimensions.SetY(float(size.m_height));
  320. imageDimensions.SetZ(1.0f / float(size.m_width));
  321. imageDimensions.SetW(1.0f / float(size.m_height));
  322. [[maybe_unused]]
  323. bool success = m_shaderResourceGroup->SetConstant(binding.m_shaderImageDimensionsNameIndex, imageDimensions);
  324. AZ_Assert(success, "Pass [%s] Could not find float4 constant [%s] in Shader Resource Group [%s]",
  325. GetPathName().GetCStr(),
  326. binding.m_shaderImageDimensionsNameIndex.GetNameForDebug().GetCStr(),
  327. m_shaderResourceGroup->GetDatabaseName());
  328. }
  329. if (binding.m_shaderInputIndex != PassAttachmentBinding::ShaderInputNoBind &&
  330. binding.m_scopeAttachmentUsage != RHI::ScopeAttachmentUsage::RenderTarget &&
  331. binding.m_scopeAttachmentUsage != RHI::ScopeAttachmentUsage::DepthStencil &&
  332. binding.m_scopeAttachmentUsage != RHI::ScopeAttachmentUsage::Resolve)
  333. {
  334. m_shaderResourceGroup->SetImageView(RHI::ShaderInputImageIndex(inputIndex), imageView, arrayIndex);
  335. ++imageIndex;
  336. }
  337. }
  338. else if (attachment->GetAttachmentType() == RHI::AttachmentType::Buffer)
  339. {
  340. if (binding.m_shaderInputIndex == PassAttachmentBinding::ShaderInputNoBind)
  341. {
  342. return;
  343. }
  344. if (inputIndex == PassAttachmentBinding::ShaderInputAutoBind)
  345. {
  346. inputIndex = bufferIndex;
  347. }
  348. const RHI::BufferView* bufferView = context.GetBufferView(attachment->GetAttachmentId(), binding.m_scopeAttachmentUsage);
  349. m_shaderResourceGroup->SetBufferView(RHI::ShaderInputBufferIndex(inputIndex), bufferView, arrayIndex);
  350. ++bufferIndex;
  351. }
  352. }
  353. }
  354. void RenderPass::BindPassSrg(const RHI::FrameGraphCompileContext& context, [[maybe_unused]] Data::Instance<ShaderResourceGroup>& shaderResourceGroup)
  355. {
  356. AZ_Assert(m_shaderResourceGroup != nullptr, "Passing a null shader resource group to RenderPass::BindPassSrg");
  357. int16_t imageIndex = 0;
  358. int16_t bufferIndex = 0;
  359. // Bind the input attachments to the SRG
  360. for (uint32_t idx = 0; idx < GetInputCount(); ++idx)
  361. {
  362. PassAttachmentBinding& binding = GetInputBinding(idx);
  363. AZ_Assert(binding.m_scopeAttachmentUsage != RHI::ScopeAttachmentUsage::RenderTarget,
  364. "Attachment bindings that are inputs cannot have their type set to 'RenderTarget'. Binding in question is %s on pass %s.",
  365. binding.m_name.GetCStr(),
  366. GetPathName().GetCStr());
  367. BindAttachment(context, binding, imageIndex, bufferIndex);
  368. }
  369. // Bind the input/output attachments to the SRG
  370. for (uint32_t idx = 0; idx < GetInputOutputCount(); ++idx)
  371. {
  372. PassAttachmentBinding& binding = GetInputOutputBinding(idx);
  373. BindAttachment(context, binding, imageIndex, bufferIndex);
  374. }
  375. // Bind the output attachments to the SRG
  376. for (uint32_t idx = 0; idx < GetOutputCount(); ++idx)
  377. {
  378. PassAttachmentBinding& binding = GetOutputBinding(idx);
  379. BindAttachment(context, binding, imageIndex, bufferIndex);
  380. }
  381. }
  382. ViewPtr RenderPass::GetView() const
  383. {
  384. if (m_pipeline)
  385. {
  386. return m_pipeline->GetFirstView(GetPipelineViewTag());
  387. }
  388. return nullptr;
  389. }
  390. void RenderPass::CollectSrgs()
  391. {
  392. // Scene srg
  393. const RHI::ShaderResourceGroup* sceneSrg = m_pipeline->GetScene()->GetRHIShaderResourceGroup();
  394. BindSrg(sceneSrg);
  395. // View srg
  396. if (m_flags.m_bindViewSrg)
  397. {
  398. ViewPtr view = GetView();
  399. if (view)
  400. {
  401. BindSrg(view->GetRHIShaderResourceGroup());
  402. }
  403. }
  404. // Pass srg
  405. if (m_shaderResourceGroup)
  406. {
  407. BindSrg(m_shaderResourceGroup->GetRHIShaderResourceGroup());
  408. }
  409. }
  410. void RenderPass::ResetSrgs()
  411. {
  412. m_shaderResourceGroupsToBind.clear();
  413. }
  414. void RenderPass::BindSrg(const RHI::ShaderResourceGroup* srg)
  415. {
  416. if (srg)
  417. {
  418. m_shaderResourceGroupsToBind[aznumeric_caster(srg->GetBindingSlot())] = srg;
  419. }
  420. }
  421. void RenderPass::SetSrgsForDraw(const RHI::FrameGraphExecuteContext& context)
  422. {
  423. for (auto itr : m_shaderResourceGroupsToBind)
  424. {
  425. context.GetCommandList()->SetShaderResourceGroupForDraw(*(itr.second->GetDeviceShaderResourceGroup(context.GetDeviceIndex())));
  426. }
  427. }
  428. void RenderPass::SetSrgsForDispatch(const RHI::FrameGraphExecuteContext& context)
  429. {
  430. for (auto itr : m_shaderResourceGroupsToBind)
  431. {
  432. context.GetCommandList()->SetShaderResourceGroupForDispatch(
  433. *(itr.second->GetDeviceShaderResourceGroup(context.GetDeviceIndex())));
  434. }
  435. }
  436. void RenderPass::SetPipelineViewTag(const PipelineViewTag& viewTag)
  437. {
  438. if (m_viewTag != viewTag)
  439. {
  440. m_viewTag = viewTag;
  441. if (m_pipeline)
  442. {
  443. m_pipeline->MarkPipelinePassChanges(PipelinePassChanges::PipelineViewTagChanged);
  444. }
  445. }
  446. m_flags.m_bindViewSrg = !viewTag.IsEmpty();
  447. }
  448. TimestampResult RenderPass::GetTimestampResultInternal() const
  449. {
  450. return m_timestampResult;
  451. }
  452. PipelineStatisticsResult RenderPass::GetPipelineStatisticsResultInternal() const
  453. {
  454. return m_statisticsResult;
  455. }
  456. Data::Instance<RPI::ShaderResourceGroup> RenderPass::GetShaderResourceGroup()
  457. {
  458. return m_shaderResourceGroup;
  459. }
  460. RHI::Ptr<Query> RenderPass::GetQuery(ScopeQueryType queryType)
  461. {
  462. uint32_t typeIndex = static_cast<uint32_t>(queryType);
  463. if (!m_scopeQueries[typeIndex])
  464. {
  465. RHI::Ptr<Query> query;
  466. switch (queryType)
  467. {
  468. case ScopeQueryType::Timestamp:
  469. query = GpuQuerySystemInterface::Get()->CreateQuery(
  470. RHI::QueryType::Timestamp, RHI::QueryPoolScopeAttachmentType::Global, RHI::ScopeAttachmentAccess::Write);
  471. break;
  472. case ScopeQueryType::PipelineStatistics:
  473. query = GpuQuerySystemInterface::Get()->CreateQuery(
  474. RHI::QueryType::PipelineStatistics, RHI::QueryPoolScopeAttachmentType::Global, RHI::ScopeAttachmentAccess::Write);
  475. break;
  476. }
  477. m_scopeQueries[typeIndex] = query;
  478. }
  479. return m_scopeQueries[typeIndex];
  480. }
  481. template<typename Func>
  482. inline void RenderPass::ExecuteOnTimestampQuery(Func&& func)
  483. {
  484. if (IsTimestampQueryEnabled())
  485. {
  486. auto query = GetQuery(ScopeQueryType::Timestamp);
  487. if (query)
  488. {
  489. func(query);
  490. }
  491. }
  492. }
  493. template<typename Func>
  494. inline void RenderPass::ExecuteOnPipelineStatisticsQuery(Func&& func)
  495. {
  496. if (IsPipelineStatisticsQueryEnabled())
  497. {
  498. auto query = GetQuery(ScopeQueryType::PipelineStatistics);
  499. if (query)
  500. {
  501. func(query);
  502. }
  503. }
  504. }
  505. void RenderPass::AddScopeQueryToFrameGraph(RHI::FrameGraphInterface frameGraph)
  506. {
  507. const auto addToFrameGraph = [&frameGraph](RHI::Ptr<Query> query)
  508. {
  509. query->AddToFrameGraph(frameGraph);
  510. };
  511. ExecuteOnTimestampQuery(addToFrameGraph);
  512. ExecuteOnPipelineStatisticsQuery(addToFrameGraph);
  513. }
  514. const AZ::RHI::ScopeGroupId& RenderPass::GetSubpassGroupId() const
  515. {
  516. return m_subpassGroupId;
  517. }
  518. void RenderPass::BeginScopeQuery(const RHI::FrameGraphExecuteContext& context)
  519. {
  520. const auto beginQuery = [&context, this](RHI::Ptr<Query> query)
  521. {
  522. if (query->BeginQuery(context) == QueryResultCode::Fail)
  523. {
  524. AZ_UNUSED(this); // Prevent unused warning in release builds
  525. AZ_WarningOnce("RenderPass", false, "BeginScopeQuery failed. Make sure AddScopeQueryToFrameGraph was called in SetupFrameGraphDependencies"
  526. " for this pass: %s", this->RTTI_GetTypeName());
  527. }
  528. };
  529. if (context.GetCommandListIndex() == 0)
  530. {
  531. ExecuteOnTimestampQuery(beginQuery);
  532. ExecuteOnPipelineStatisticsQuery(beginQuery);
  533. }
  534. }
  535. void RenderPass::EndScopeQuery(const RHI::FrameGraphExecuteContext& context)
  536. {
  537. const auto endQuery = [&context](RHI::Ptr<Query> query)
  538. {
  539. query->EndQuery(context);
  540. };
  541. // This scope query implementation should be replaced by
  542. // [ATOM-5407] [RHI][Core] - Add GPU timestamp and pipeline statistic support for scopes
  543. // For timestamp query, it's okay to execute across different command lists
  544. if (context.GetCommandListIndex() == context.GetCommandListCount() - 1)
  545. {
  546. ExecuteOnTimestampQuery(endQuery);
  547. }
  548. // For all the other types of queries except timestamp, the query start and end has to be in the same command list
  549. // Here only tracks the PipelineStatistics for the first command list due to that we don't know how many queries are
  550. // needed when AddScopeQueryToFrameGraph is called.
  551. // This implementation leads to an issue that we may not get accurate pipeline statistic data
  552. // for passes which were executed with more than one command list
  553. if (context.GetCommandListIndex() == 0)
  554. {
  555. ExecuteOnPipelineStatisticsQuery(endQuery);
  556. }
  557. }
  558. void RenderPass::ReadbackScopeQueryResults()
  559. {
  560. ExecuteOnTimestampQuery([this](RHI::Ptr<Query> query)
  561. {
  562. const uint32_t TimestampResultQueryCount = 2u;
  563. uint64_t timestampResult[TimestampResultQueryCount] = {0};
  564. query->GetLatestResult(&timestampResult, sizeof(uint64_t) * TimestampResultQueryCount, m_lastDeviceIndex);
  565. m_timestampResult = TimestampResult(timestampResult[0], timestampResult[1], RHI::HardwareQueueClass::Graphics);
  566. });
  567. ExecuteOnPipelineStatisticsQuery([this](RHI::Ptr<Query> query)
  568. {
  569. query->GetLatestResult(&m_statisticsResult, sizeof(PipelineStatisticsResult), m_lastDeviceIndex);
  570. });
  571. }
  572. } // namespace RPI
  573. } // namespace AZ