IndirectRenderingExampleComponent.cpp 54 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140
  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 <RHI/IndirectRenderingExampleComponent.h>
  9. #include <Utils/Utils.h>
  10. #include <SampleComponentManager.h>
  11. #include <Atom/RHI/CommandList.h>
  12. #include <Atom/RHI/IndirectBufferWriter.h>
  13. #include <Atom/RHI.Reflect/InputStreamLayoutBuilder.h>
  14. #include <Atom/RHI.Reflect/RenderAttachmentLayoutBuilder.h>
  15. #include <Atom/RPI.Public/Shader/Shader.h>
  16. #include <Atom/RPI.Reflect/Shader/ShaderAsset.h>
  17. #include <AzCore/Serialization/SerializeContext.h>
  18. #include <AzCore/Math/MathUtils.h>
  19. #include <AzCore/Math/Random.h>
  20. namespace AtomSampleViewer
  21. {
  22. using namespace AZ;
  23. namespace IndirectRendering
  24. {
  25. const char* SampleName = "IndirectRenderingExample";
  26. const uint32_t ThreadGroupSize = 128;
  27. const char* IndirectBufferAttachmentId = "IndirectBufferAttachmentId";
  28. const char* CulledIndirectBufferAttachmentId = "CulledIndirectBufferAttachmentId";
  29. const char* CountBufferAttachmentId = "CountBufferAttachmentId";
  30. const char* DepthBufferAttachmentId = "DepthBufferAttachmentId";
  31. const AZ::Vector2 VelocityRange(0.1f, 0.3f);
  32. }
  33. float GetRandomFloat(float min, float max)
  34. {
  35. float scale = aznumeric_cast<float>(rand()) / aznumeric_cast<float>(RAND_MAX);
  36. float range = max - min;
  37. return scale * range + min;
  38. }
  39. void IndirectRenderingExampleComponent::Reflect(AZ::ReflectContext* context)
  40. {
  41. if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  42. {
  43. serializeContext->Class<IndirectRenderingExampleComponent, AZ::Component>()
  44. ->Version(0)
  45. ;
  46. }
  47. }
  48. IndirectRenderingExampleComponent::IndirectRenderingExampleComponent()
  49. {
  50. m_supportRHISamplePipeline = true;
  51. }
  52. void IndirectRenderingExampleComponent::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
  53. {
  54. UpdateInstancesData(deltaTime);
  55. if (m_updateIndirectDispatchArguments)
  56. {
  57. UpdateIndirectDispatchArguments();
  58. m_updateIndirectDispatchArguments = false;
  59. }
  60. if (m_imguiSidebar.Begin())
  61. {
  62. DrawSampleSettings();
  63. }
  64. }
  65. void IndirectRenderingExampleComponent::OnFramePrepare(AZ::RHI::FrameGraphBuilder& frameGraphBuilder)
  66. {
  67. using namespace AZ;
  68. if (m_windowContext->GetSwapChain())
  69. {
  70. RHI::FrameGraphAttachmentInterface builder = frameGraphBuilder.GetAttachmentDatabase();
  71. // Create the depth buffer for rendering.
  72. const AZ::RHI::ImageDescriptor imageDescriptor = AZ::RHI::ImageDescriptor::Create2D(
  73. AZ::RHI::ImageBindFlags::DepthStencil,
  74. static_cast<uint32_t>(m_windowContext->GetViewport().m_maxX - m_windowContext->GetViewport().m_minX),
  75. static_cast<uint32_t>(m_windowContext->GetViewport().m_maxY - m_windowContext->GetViewport().m_minY),
  76. AZ::RHI::Format::D32_FLOAT);
  77. const AZ::RHI::TransientImageDescriptor transientImageDescriptor(RHI::AttachmentId{ IndirectRendering::DepthBufferAttachmentId }, imageDescriptor);
  78. builder.CreateTransientImage(transientImageDescriptor);
  79. if (m_deviceSupportsCountBuffer)
  80. {
  81. // Create the count buffer.
  82. RHI::TransientBufferDescriptor countBufferDesc;
  83. countBufferDesc.m_attachmentId = IndirectRendering::CountBufferAttachmentId;
  84. countBufferDesc.m_bufferDescriptor = RHI::BufferDescriptor(
  85. // The count buffer must also have the Indirect BufferBind Flags, even if it doesn't contains indirect commands.
  86. RHI::BufferBindFlags::Indirect | RHI::BufferBindFlags::ShaderReadWrite | RHI::BufferBindFlags::CopyWrite,
  87. m_resetCounterBuffer->GetDescriptor().m_byteCount);
  88. builder.CreateTransientBuffer(countBufferDesc);
  89. }
  90. // Create the indirect buffer that will contains the culled commands.
  91. RHI::TransientBufferDescriptor culledCommandsBufferDesc;
  92. culledCommandsBufferDesc.m_attachmentId = IndirectRendering::CulledIndirectBufferAttachmentId;
  93. culledCommandsBufferDesc.m_bufferDescriptor = RHI::BufferDescriptor(
  94. RHI::BufferBindFlags::Indirect | RHI::BufferBindFlags::ShaderReadWrite,
  95. m_indirectDrawBufferSignature->GetByteStride() * m_numObjects);
  96. builder.CreateTransientBuffer(culledCommandsBufferDesc);
  97. }
  98. BasicRHIComponent::OnFramePrepare(frameGraphBuilder);
  99. }
  100. void IndirectRenderingExampleComponent::InitInputAssemblyResources()
  101. {
  102. RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
  103. RHI::StreamBufferView& triangleStreamBufferView = m_streamBufferViews[0];
  104. RHI::StreamBufferView& instancesIndicesStreamBufferView = m_streamBufferViews[1];
  105. RHI::StreamBufferView& quadStreamBufferView = m_streamBufferViews[2];
  106. RHI::IndexBufferView& triangleIndexBufferView = m_indexBufferViews[0];
  107. RHI::IndexBufferView& quadIndexBufferView = m_indexBufferViews[1];
  108. // We use an index to identify an object at draw time.
  109. // On platforms that support setting inline constant through indirect commands we use an inline constant to set
  110. // the object index. On the other platforms we use a vertex buffer stream that has a per instance step.
  111. // The instance offset is specified as an argument in the indirect buffer. We can't just simply use SV_InstanceID
  112. // in the shader because the value of that variable is not affected by the instance offset.
  113. RHI::InputStreamLayoutBuilder layoutBuilder;
  114. layoutBuilder.AddBuffer()->Channel("POSITION", RHI::Format::R32G32B32_FLOAT);
  115. // Object index
  116. layoutBuilder.AddBuffer(RHI::StreamStepFunction::PerInstance)->Channel("BLENDINDICES0", RHI::Format::R32_UINT);
  117. m_inputStreamLayout = layoutBuilder.End();
  118. m_inputAssemblyBufferPool = aznew RHI::BufferPool();
  119. RHI::BufferPoolDescriptor bufferPoolDesc;
  120. bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::InputAssembly;
  121. bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Device;
  122. m_inputAssemblyBufferPool->Init(bufferPoolDesc);
  123. {
  124. BufferData bufferData;
  125. const float triangleWidth = 1.0f;
  126. SetVertexPosition(bufferData.m_trianglePositions.data(), 0, 0, sqrt(pow(triangleWidth * 2.0f, 2.0f) - pow(triangleWidth, 2.0f)) - triangleWidth, 0.0);
  127. SetVertexPosition(bufferData.m_trianglePositions.data(), 1, -triangleWidth, -triangleWidth, 0.0);
  128. SetVertexPosition(bufferData.m_trianglePositions.data(), 2, triangleWidth, -triangleWidth, 0.0);
  129. SetVertexIndexIncreasing(bufferData.m_triangleIndices.data(), bufferData.m_triangleIndices.size());
  130. SetFullScreenRect(bufferData.m_quadPositions.data(), nullptr, bufferData.m_quadIndices.data());
  131. for (uint32_t i = 0; i < bufferData.m_instanceIndices.size(); ++i)
  132. {
  133. bufferData.m_instanceIndices[i] = i;
  134. }
  135. m_inputAssemblyBuffer = aznew RHI::Buffer();
  136. RHI::BufferInitRequest request;
  137. request.m_buffer = m_inputAssemblyBuffer.get();
  138. request.m_descriptor = RHI::BufferDescriptor{ RHI::BufferBindFlags::InputAssembly, sizeof(bufferData) };
  139. request.m_initialData = &bufferData;
  140. m_inputAssemblyBufferPool->InitBuffer(request);
  141. // If the platform supports setting vertex and index buffers through indirect commands,
  142. // we create separate Vertex Buffer and Index Buffer views for the triangle and quad.
  143. // If not supported, we create one Vertex Buffer and Index Buffer view that contains both
  144. // primitives and we adjust the offset as an argument in the indirect buffer.
  145. switch (m_mode)
  146. {
  147. case SequenceType::IARootConstantsDraw:
  148. {
  149. triangleStreamBufferView =
  150. {
  151. *m_inputAssemblyBuffer,
  152. offsetof(BufferData, m_trianglePositions),
  153. sizeof(BufferData::m_trianglePositions),
  154. sizeof(VertexPosition)
  155. };
  156. triangleIndexBufferView =
  157. {
  158. *m_inputAssemblyBuffer,
  159. offsetof(BufferData, m_triangleIndices),
  160. sizeof(BufferData::m_triangleIndices),
  161. RHI::IndexFormat::Uint16
  162. };
  163. quadStreamBufferView =
  164. {
  165. *m_inputAssemblyBuffer,
  166. offsetof(BufferData, m_quadPositions),
  167. sizeof(BufferData::m_quadPositions),
  168. sizeof(VertexPosition)
  169. };
  170. quadIndexBufferView =
  171. {
  172. *m_inputAssemblyBuffer,
  173. offsetof(BufferData, m_quadIndices),
  174. sizeof(BufferData::m_quadIndices),
  175. RHI::IndexFormat::Uint16
  176. };
  177. break;
  178. }
  179. case SequenceType::DrawOnly:
  180. {
  181. m_streamBufferViews[0] =
  182. {
  183. *m_inputAssemblyBuffer,
  184. offsetof(BufferData, m_trianglePositions),
  185. sizeof(BufferData::m_trianglePositions) + sizeof(BufferData::m_quadPositions),
  186. sizeof(VertexPosition)
  187. };
  188. m_indexBufferViews[0] =
  189. {
  190. *m_inputAssemblyBuffer,
  191. offsetof(BufferData, m_triangleIndices), // Need to offset the index buffer to the proper location
  192. sizeof(BufferData::m_triangleIndices) + sizeof(BufferData::m_quadIndices),
  193. RHI::IndexFormat::Uint16
  194. };
  195. break;
  196. }
  197. default:
  198. AZ_Assert(false, "Invalid Sequence type");
  199. return;
  200. }
  201. instancesIndicesStreamBufferView =
  202. {
  203. *m_inputAssemblyBuffer,
  204. offsetof(BufferData, m_instanceIndices),
  205. sizeof(BufferData::m_instanceIndices),
  206. sizeof(uint32_t)
  207. };
  208. RHI::ValidateStreamBufferViews(m_inputStreamLayout, AZStd::span<const RHI::StreamBufferView>(m_streamBufferViews.data(), 2));
  209. }
  210. for (uint32_t i = 0; i < m_numObjects; ++i)
  211. {
  212. RHI::GeometryView& geoView = m_geometryViews[i];
  213. geoView.SetIndexBufferView(m_indexBufferViews[0]);
  214. geoView.AddStreamBufferView(m_streamBufferViews[0]);
  215. geoView.AddStreamBufferView(m_streamBufferViews[1]);
  216. }
  217. }
  218. void IndirectRenderingExampleComponent::InitShaderResources()
  219. {
  220. {
  221. auto shader = m_indirectDrawShader;
  222. if (shader == nullptr)
  223. {
  224. return;
  225. }
  226. AZ::RHI::PipelineStateDescriptorForDraw drawPipelineStateDescriptor;
  227. drawPipelineStateDescriptor.m_inputStreamLayout = m_inputStreamLayout;
  228. shader->GetVariant(m_indirectDrawShaderVariantStableId).ConfigurePipelineState(drawPipelineStateDescriptor);
  229. RHI::RenderAttachmentLayoutBuilder attachmentsBuilder;
  230. attachmentsBuilder.AddSubpass()
  231. ->RenderTargetAttachment(m_outputFormat)
  232. ->DepthStencilAttachment(RHI::Format::D32_FLOAT);
  233. [[maybe_unused]] RHI::ResultCode result = attachmentsBuilder.End(drawPipelineStateDescriptor.m_renderAttachmentConfiguration.m_renderAttachmentLayout);
  234. AZ_Assert(result == RHI::ResultCode::Success, "Failed to create render attachment layout");
  235. drawPipelineStateDescriptor.m_renderStates.m_depthStencilState = AZ::RHI::DepthStencilState::CreateDepth();
  236. m_drawPipelineState = shader->AcquirePipelineState(drawPipelineStateDescriptor);
  237. if (!m_drawPipelineState)
  238. {
  239. AZ_Error(IndirectRendering::SampleName, false, "Failed to acquire default pipeline state for shader '%s'", IndirectDrawShaderFilePath);
  240. return;
  241. }
  242. m_sceneShaderResourceGroup = CreateShaderResourceGroup(shader, "IndirectSceneSrg", IndirectRendering::SampleName);
  243. // Not needed because this example uses static variants (preloaded before this function is called).
  244. // But left here for educational purposes:
  245. // Fallback in case we don't have the exact match.
  246. if (m_sceneShaderResourceGroup->HasShaderVariantKeyFallbackEntry())
  247. {
  248. m_sceneShaderResourceGroup->SetShaderVariantKeyFallbackValue(m_indirectDrawShaderOptionGroup.GetShaderVariantId().m_key);
  249. }
  250. }
  251. {
  252. auto shader = m_indirectDispatchShader;
  253. if (!shader)
  254. {
  255. return;
  256. }
  257. AZ::RHI::PipelineStateDescriptorForDispatch computePipelineStateDescriptor;
  258. shader->GetVariant(m_indirectDispatchShaderVariantStableId).ConfigurePipelineState(computePipelineStateDescriptor);
  259. m_cullPipelineState = shader->AcquirePipelineState(computePipelineStateDescriptor);
  260. if (!m_cullPipelineState)
  261. {
  262. AZ_Error(IndirectRendering::SampleName, false, "Failed to acquire default pipeline state for shader '%s'", IndirectDispatchShaderFilePath);
  263. return;
  264. }
  265. m_cullShaderResourceGroup = CreateShaderResourceGroup(shader, "CullSrg", IndirectRendering::SampleName);
  266. const Name countBufferId{ "m_outNumCommands" };
  267. const Name cullOffsetId{ "m_cullOffset" };
  268. const Name numCommandsId{ "m_inNumCommands" };
  269. const Name maxCommandsId{ "m_maxDrawIndirectCount" };
  270. FindShaderInputIndex(&m_cullingCountBufferIndex, m_cullShaderResourceGroup, countBufferId, IndirectRendering::SampleName);
  271. FindShaderInputIndex(&m_cullingOffsetIndex, m_cullShaderResourceGroup, cullOffsetId, IndirectRendering::SampleName);
  272. FindShaderInputIndex(&m_cullingNumCommandsIndex, m_cullShaderResourceGroup, numCommandsId, IndirectRendering::SampleName);
  273. FindShaderInputIndex(&m_cullingMaxCommandsIndex, m_cullShaderResourceGroup, maxCommandsId, IndirectRendering::SampleName);
  274. const Name inputCommandsId{ "m_inputCommands" };
  275. const Name outputCommandsId{ "m_outputCommands" };
  276. const char* srgNames[] = { "IndirectDrawCommandsSrg", "IndirectIAInlineConstCommandsSrg" };
  277. for (uint32_t i = 0; i < NumSequencesType; ++i)
  278. {
  279. m_indirectCommandsShaderResourceGroups[i] = CreateShaderResourceGroup(shader, srgNames[i], IndirectRendering::SampleName);
  280. FindShaderInputIndex(&m_cullingInputIndirectBufferIndices[i], m_indirectCommandsShaderResourceGroups[i], inputCommandsId, IndirectRendering::SampleName);
  281. FindShaderInputIndex(&m_cullingOutputIndirectBufferIndices[i], m_indirectCommandsShaderResourceGroups[i], outputCommandsId, IndirectRendering::SampleName);
  282. }
  283. }
  284. }
  285. void IndirectRenderingExampleComponent::InitIndirectRenderingResources()
  286. {
  287. RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
  288. RHI::ResultCode result;
  289. m_shaderBufferPool = aznew RHI::BufferPool();
  290. RHI::BufferPoolDescriptor bufferPoolDesc;
  291. bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::ShaderRead | RHI::BufferBindFlags::Indirect;
  292. bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Device;
  293. m_shaderBufferPool->Init(bufferPoolDesc);
  294. // Create the layout depending on which commands are supported by the device.
  295. m_indirectDrawBufferLayout = RHI::IndirectBufferLayout();
  296. if (m_mode == SequenceType::IARootConstantsDraw)
  297. {
  298. m_indirectDrawBufferLayout.AddIndirectCommand(RHI::IndirectCommandDescriptor(RHI::IndirectCommandType::RootConstants));
  299. m_indirectDrawBufferLayout.AddIndirectCommand(RHI::IndirectCommandDescriptor(RHI::IndirectBufferViewArguments{ 0 }));
  300. m_indirectDrawBufferLayout.AddIndirectCommand(RHI::IndirectCommandDescriptor(RHI::IndirectCommandType::IndexBufferView));
  301. }
  302. m_indirectDrawBufferLayout.AddIndirectCommand(RHI::IndirectCommandDescriptor(RHI::IndirectCommandType::DrawIndexed));
  303. if (!m_indirectDrawBufferLayout.Finalize())
  304. {
  305. AZ_Assert(false, "Fail to finalize Indirect Layout");
  306. return;
  307. }
  308. // Create the signature and pass the pipeline state since we may have
  309. // an inline constants command.
  310. m_indirectDrawBufferSignature = aznew RHI::IndirectBufferSignature;
  311. RHI::IndirectBufferSignatureDescriptor signatureDescriptor;
  312. signatureDescriptor.m_layout = m_indirectDrawBufferLayout;
  313. signatureDescriptor.m_pipelineState = m_drawPipelineState.get();
  314. result = m_indirectDrawBufferSignature->Init(RHI::MultiDevice::AllDevices, signatureDescriptor);
  315. if (result != RHI::ResultCode::Success)
  316. {
  317. AZ_Assert(false, "Fail to initialize Indirect Buffer Signature");
  318. return;
  319. }
  320. m_sourceIndirectBuffer = aznew RHI::Buffer();
  321. uint32_t commandsStride = m_indirectDrawBufferSignature->GetByteStride();
  322. RHI::BufferInitRequest request;
  323. request.m_buffer = m_sourceIndirectBuffer.get();
  324. request.m_descriptor = RHI::BufferDescriptor(
  325. bufferPoolDesc.m_bindFlags,
  326. commandsStride * s_maxNumberOfObjects);
  327. m_shaderBufferPool->InitBuffer(request);
  328. // Create a writer to populate the buffer with the commands.
  329. RHI::Ptr<RHI::IndirectBufferWriter> indirectBufferWriter = aznew RHI::IndirectBufferWriter;
  330. result = indirectBufferWriter->Init(*m_sourceIndirectBuffer, 0, commandsStride, s_maxNumberOfObjects, *m_indirectDrawBufferSignature);
  331. if (result != RHI::ResultCode::Success)
  332. {
  333. AZ_Assert(false, "Fail to initialize Indirect Buffer Writer");
  334. return;
  335. }
  336. RHI::StreamBufferView& triangleStreamBufferView = m_streamBufferViews[0];
  337. RHI::StreamBufferView& quadStreamBufferView = m_streamBufferViews[2];
  338. RHI::IndexBufferView& triangleIndexBufferView = m_indexBufferViews[0];
  339. RHI::IndexBufferView& quadIndexBufferView = m_indexBufferViews[1];
  340. // Write the commands using the IndirectBufferWriter
  341. // We alternate between drawing a triangle and a quad.
  342. for (uint32_t i = 0; i < s_maxNumberOfObjects; ++i)
  343. {
  344. if (i % 2)
  345. {
  346. if (m_mode == SequenceType::IARootConstantsDraw)
  347. {
  348. indirectBufferWriter->SetRootConstants(reinterpret_cast<uint8_t*>(&i), sizeof(i))
  349. ->SetVertexView(0, triangleStreamBufferView)
  350. ->SetIndexView(triangleIndexBufferView);
  351. }
  352. indirectBufferWriter->DrawIndexed(RHI::DrawIndexed(0, 3, 0), RHI::DrawInstanceArguments(1, i));
  353. }
  354. else
  355. {
  356. RHI::DrawIndexed arguments(0, 6, 0);
  357. switch (m_mode)
  358. {
  359. case SequenceType::IARootConstantsDraw:
  360. indirectBufferWriter->SetRootConstants(reinterpret_cast<uint8_t*>(&i), sizeof(i))
  361. ->SetVertexView(0, quadStreamBufferView)
  362. ->SetIndexView(quadIndexBufferView);
  363. break;
  364. case SequenceType::DrawOnly:
  365. // Since we are using one vertex buffer view and one index buffer view for both type of primitives
  366. // we need to adjust the vertex and index offset so they point to the proper location.
  367. arguments.m_vertexOffset = decltype(BufferData::m_trianglePositions)::array_size;
  368. arguments.m_indexOffset = decltype(BufferData::m_triangleIndices)::array_size;
  369. break;
  370. default:
  371. AZ_Assert(false, "Invalid sequence type");
  372. return;
  373. }
  374. indirectBufferWriter->DrawIndexed(arguments, RHI::DrawInstanceArguments(1, i));
  375. }
  376. indirectBufferWriter->NextSequence();
  377. }
  378. indirectBufferWriter->Shutdown();
  379. auto viewDescriptor = RHI::BufferViewDescriptor::CreateStructured(0, s_maxNumberOfObjects, commandsStride);
  380. m_sourceIndirectBufferView = m_sourceIndirectBuffer->BuildBufferView(viewDescriptor);
  381. if(!m_sourceIndirectBufferView.get())
  382. {
  383. AZ_Assert(false, "Fail to initialize Indirect Buffer View");
  384. return;
  385. }
  386. // Create the buffer that will contain the compute dispatch arguments.
  387. m_indirectDispatchBuffer = aznew RHI::Buffer();
  388. {
  389. m_indirectDispatchBufferLayout = RHI::IndirectBufferLayout();
  390. m_indirectDispatchBufferLayout.AddIndirectCommand(RHI::IndirectCommandDescriptor(RHI::IndirectCommandType::Dispatch));
  391. if (!m_indirectDispatchBufferLayout.Finalize())
  392. {
  393. AZ_Assert(false, "Fail to finalize Indirect Layout");
  394. return;
  395. }
  396. m_indirectDispatchBufferSignature = aznew RHI::IndirectBufferSignature;
  397. signatureDescriptor = {};
  398. signatureDescriptor.m_layout = m_indirectDispatchBufferLayout;
  399. result = m_indirectDispatchBufferSignature->Init(RHI::MultiDevice::AllDevices, signatureDescriptor);
  400. if (result != RHI::ResultCode::Success)
  401. {
  402. AZ_Assert(false, "Fail to initialize Indirect Buffer Signature");
  403. return;
  404. }
  405. uint32_t indirectDispatchStride = m_indirectDispatchBufferSignature->GetByteStride();
  406. request = {};
  407. request.m_buffer = m_indirectDispatchBuffer.get();
  408. request.m_descriptor = RHI::BufferDescriptor(
  409. bufferPoolDesc.m_bindFlags,
  410. indirectDispatchStride);
  411. m_shaderBufferPool->InitBuffer(request);
  412. m_indirectDispatchBufferView =
  413. {
  414. *m_indirectDispatchBuffer,
  415. *m_indirectDispatchBufferSignature,
  416. 0,
  417. indirectDispatchStride,
  418. indirectDispatchStride
  419. };
  420. m_indirectDispatchWriter = aznew RHI::IndirectBufferWriter;
  421. result = m_indirectDispatchWriter->Init(*m_indirectDispatchBuffer, 0, indirectDispatchStride, 1, *m_indirectDispatchBufferSignature);
  422. if (result != RHI::ResultCode::Success)
  423. {
  424. AZ_Assert(false, "Fail to initialize Indirect Buffer Writer");
  425. return;
  426. }
  427. UpdateIndirectDispatchArguments();
  428. }
  429. }
  430. void IndirectRenderingExampleComponent::InitInstancesDataResources()
  431. {
  432. RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
  433. uint32_t maxIndirectDrawCount = device->GetLimits().m_maxIndirectDrawCount;
  434. // Populate the data for each instance using some random values.
  435. m_instancesData.resize(s_maxNumberOfObjects);
  436. for (InstanceData& data : m_instancesData)
  437. {
  438. float scale = GetRandomFloat(0.01, 0.1f);
  439. data.m_offset = AZ::Vector4(GetRandomFloat(-4.0f, -2.0f), GetRandomFloat(-1.f, 1.f), GetRandomFloat(0.f, 1.f), 0.f);
  440. data.m_scale = AZ::Vector4(scale, scale, 1.f, 0.f);
  441. data.m_color = AZ::Color(GetRandomFloat(0.5f, 1.0f), GetRandomFloat(0.5f, 1.0f), GetRandomFloat(0.5f, 1.0f), 1.0f);
  442. data.m_velocity.Set(GetRandomFloat(IndirectRendering::VelocityRange.GetX(), IndirectRendering::VelocityRange.GetY()));
  443. }
  444. m_instancesBufferPool = aznew RHI::BufferPool();
  445. RHI::BufferPoolDescriptor bufferPoolDesc;
  446. bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::ShaderRead;
  447. bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Host;
  448. m_instancesBufferPool->Init(bufferPoolDesc);
  449. m_instancesDataBuffer = aznew RHI::Buffer();
  450. RHI::BufferInitRequest request;
  451. request.m_buffer = m_instancesDataBuffer.get();
  452. request.m_descriptor = RHI::BufferDescriptor{
  453. RHI::BufferBindFlags::ShaderRead,
  454. sizeof(InstanceData) * s_maxNumberOfObjects };
  455. request.m_initialData = m_instancesData.data();
  456. m_instancesBufferPool->InitBuffer(request);
  457. auto descriptor = RHI::BufferViewDescriptor::CreateStructured(0, static_cast<uint32_t>(m_instancesData.size()), sizeof(InstanceData));
  458. m_instancesDataBufferView = m_instancesDataBuffer->BuildBufferView(descriptor);
  459. if(!m_instancesDataBufferView.get())
  460. {
  461. AZ_Assert(false, "Fail to initialize Instances Data Buffer View");
  462. return;
  463. }
  464. // Create the count buffer if the platform supports it.
  465. // The count buffer will contain the actual number of primitives to draw after
  466. // the compute shader has culled the commands.
  467. if (m_deviceSupportsCountBuffer)
  468. {
  469. m_copyBufferPool = aznew RHI::BufferPool();
  470. bufferPoolDesc = {};
  471. bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::CopyRead;
  472. bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Host;
  473. m_copyBufferPool->Init(bufferPoolDesc);
  474. m_resetCounterBuffer = aznew RHI::Buffer();
  475. AZStd::vector<uint32_t> initData;
  476. initData.assign(static_cast<size_t>(std::ceil(float(s_maxNumberOfObjects) / maxIndirectDrawCount)), 0);
  477. request = {};
  478. request.m_buffer = m_resetCounterBuffer.get();
  479. request.m_descriptor = RHI::BufferDescriptor
  480. {
  481. RHI::BufferBindFlags::CopyRead,
  482. sizeof(uint32_t) * initData.size()
  483. };
  484. request.m_initialData = initData.data();
  485. m_copyBufferPool->InitBuffer(request);
  486. }
  487. {
  488. const Name instancesDataId{ "m_instancesData" };
  489. const Name matrixId{ "m_matrix" };
  490. FindShaderInputIndex(&m_sceneInstancesDataBufferIndex, m_sceneShaderResourceGroup, instancesDataId, IndirectRendering::SampleName);
  491. FindShaderInputIndex(&m_sceneMatrixInputIndex, m_sceneShaderResourceGroup, matrixId, IndirectRendering::SampleName);
  492. float screenAspect = GetViewportWidth() / GetViewportHeight();
  493. m_sceneShaderResourceGroup->SetBufferView(m_sceneInstancesDataBufferIndex, m_instancesDataBufferView.get());
  494. m_sceneShaderResourceGroup->SetConstant(m_sceneMatrixInputIndex, AZ::Matrix4x4::CreateScale(AZ::Vector3(1.f/ screenAspect, 1.f, 1.f)));
  495. m_sceneShaderResourceGroup->Compile();
  496. }
  497. {
  498. uint32_t sequenceTypeIndex = static_cast<uint32_t>(m_mode);
  499. m_indirectCommandsShaderResourceGroups[sequenceTypeIndex]->SetBufferView(m_cullingInputIndirectBufferIndices[sequenceTypeIndex], m_sourceIndirectBufferView.get());
  500. }
  501. }
  502. void IndirectRenderingExampleComponent::CreateResetCounterBufferScope()
  503. {
  504. // This scope resets the count buffer value to 0 every frame.
  505. // Since we increment it in the shader using InterlockedAdd we need it
  506. // to start with 0.
  507. // To reset it we just copy a buffer with the proper values into the count buffer.
  508. const auto prepareFunction = [this](RHI::FrameGraphInterface frameGraph, [[maybe_unused]] ScopeData& scopeData)
  509. {
  510. {
  511. RHI::BufferScopeAttachmentDescriptor countBufferAttachment;
  512. countBufferAttachment.m_attachmentId = IndirectRendering::CountBufferAttachmentId;
  513. countBufferAttachment.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::DontCare;
  514. countBufferAttachment.m_bufferViewDescriptor = RHI::BufferViewDescriptor::CreateStructured(
  515. 0,
  516. static_cast<uint32_t>(m_resetCounterBuffer->GetDescriptor().m_byteCount / sizeof(uint32_t)),
  517. sizeof(uint32_t));
  518. frameGraph.UseCopyAttachment(countBufferAttachment, RHI::ScopeAttachmentAccess::Write);
  519. }
  520. };
  521. const auto compileFunction = [this](const RHI::FrameGraphCompileContext& context, [[maybe_unused]] const ScopeData& scopeData)
  522. {
  523. const auto* countBufferView = context.GetBufferView(RHI::AttachmentId{ IndirectRendering::CountBufferAttachmentId });
  524. m_copyDescriptor.m_sourceBuffer = m_resetCounterBuffer.get();
  525. m_copyDescriptor.m_sourceOffset = 0;
  526. m_copyDescriptor.m_destinationBuffer = countBufferView->GetBuffer();
  527. m_copyDescriptor.m_destinationOffset = 0;
  528. m_copyDescriptor.m_size = static_cast<uint32_t>(m_resetCounterBuffer->GetDescriptor().m_byteCount);
  529. };
  530. const auto executeFunction = [this](const RHI::FrameGraphExecuteContext& context, [[maybe_unused]] const ScopeData& scopeData)
  531. {
  532. RHI::DeviceCopyItem copyItem(m_copyDescriptor.GetDeviceCopyBufferDescriptor(context.GetDeviceIndex()));
  533. context.GetCommandList()->Submit(copyItem);
  534. };
  535. m_scopeProducers.emplace_back(
  536. aznew RHI::ScopeProducerFunction<
  537. ScopeData,
  538. decltype(prepareFunction),
  539. decltype(compileFunction),
  540. decltype(executeFunction)>(
  541. RHI::ScopeId{ "IndirectResetCounterScope" },
  542. ScopeData{},
  543. prepareFunction,
  544. compileFunction,
  545. executeFunction));
  546. }
  547. void IndirectRenderingExampleComponent::CreateCullingScope()
  548. {
  549. // This scopes culls the primitives that are outside of a designated area.
  550. // In order to do this it copies the commands from an input buffer into
  551. // and output one. If count buffers are supported only valid commands are copied.
  552. // Otherwise it copies all commands but it updates the vertex count to 0 so no
  553. // triangles are rendered.
  554. // The dispatch call for this scope is done in an indirect manner. The indirect buffer for this dispatch
  555. // is populated on CPU.
  556. RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
  557. uint32_t maxIndirectDrawCount = device->GetLimits().m_maxIndirectDrawCount;
  558. const auto prepareFunction = [this](RHI::FrameGraphInterface frameGraph, [[maybe_unused]] ScopeData& scopeData)
  559. {
  560. uint32_t commandsStride = m_indirectDrawBufferSignature->GetByteStride();
  561. RHI::BufferScopeAttachmentDescriptor culledBufferAttachment;
  562. culledBufferAttachment.m_attachmentId = IndirectRendering::CulledIndirectBufferAttachmentId;
  563. culledBufferAttachment.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::DontCare;
  564. culledBufferAttachment.m_bufferViewDescriptor = RHI::BufferViewDescriptor::CreateStructured(0, m_numObjects, commandsStride);
  565. frameGraph.UseShaderAttachment(
  566. culledBufferAttachment, RHI::ScopeAttachmentAccess::ReadWrite, RHI::ScopeAttachmentStage::ComputeShader);
  567. if (m_deviceSupportsCountBuffer)
  568. {
  569. // The count buffer that we will be writing the final count of operations.
  570. RHI::BufferScopeAttachmentDescriptor countBufferAttachment;
  571. countBufferAttachment.m_attachmentId = IndirectRendering::CountBufferAttachmentId;
  572. countBufferAttachment.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Load;
  573. countBufferAttachment.m_bufferViewDescriptor = RHI::BufferViewDescriptor::CreateStructured(
  574. 0,
  575. static_cast<uint32_t>(m_resetCounterBuffer->GetDescriptor().m_byteCount / sizeof(uint32_t)),
  576. sizeof(uint32_t));
  577. frameGraph.UseShaderAttachment(
  578. countBufferAttachment, RHI::ScopeAttachmentAccess::ReadWrite, RHI::ScopeAttachmentStage::ComputeShader);
  579. }
  580. };
  581. const auto compileFunction = [this, maxIndirectDrawCount](const RHI::FrameGraphCompileContext& context, [[maybe_unused]] const ScopeData& scopeData)
  582. {
  583. const auto* culledBufferView = context.GetBufferView(RHI::AttachmentId{ IndirectRendering::CulledIndirectBufferAttachmentId });
  584. uint32_t sequenceTypeIndex = static_cast<uint32_t>(m_mode);
  585. auto& indirectCommandsSRG = m_indirectCommandsShaderResourceGroups[sequenceTypeIndex];
  586. indirectCommandsSRG->SetBufferView(m_cullingOutputIndirectBufferIndices[sequenceTypeIndex], culledBufferView);
  587. indirectCommandsSRG->Compile();
  588. if (m_deviceSupportsCountBuffer)
  589. {
  590. const auto* countBufferView = context.GetBufferView(RHI::AttachmentId{ IndirectRendering::CountBufferAttachmentId });
  591. m_cullShaderResourceGroup->SetBufferView(m_cullingCountBufferIndex, countBufferView);
  592. m_drawIndirect.m_countBuffer = countBufferView->GetBuffer();
  593. m_drawIndirect.m_countBufferByteOffset = 0;
  594. }
  595. // Update the cull area
  596. float cullScale = 0.75f;
  597. AZ::Vector2 cullPlane = AZ::Vector2(-m_cullOffset, m_cullOffset) * AZ::Vector2(cullScale) + AZ::Vector2(cullScale - 1.0f);
  598. m_cullShaderResourceGroup->SetConstant(m_cullingOffsetIndex, cullPlane);
  599. m_cullShaderResourceGroup->SetConstant(m_cullingNumCommandsIndex, m_numObjects);
  600. m_cullShaderResourceGroup->SetConstant(m_cullingMaxCommandsIndex, AZStd::min(m_numObjects, maxIndirectDrawCount));
  601. m_cullShaderResourceGroup->Compile();
  602. uint32_t stride = m_indirectDrawBufferSignature->GetByteStride();
  603. m_indirectDrawBufferView =
  604. {
  605. *(culledBufferView->GetBuffer()),
  606. *m_indirectDrawBufferSignature,
  607. 0,
  608. stride * m_numObjects,
  609. stride
  610. };
  611. };
  612. const auto executeFunction = [this](const RHI::FrameGraphExecuteContext& context, [[maybe_unused]] const ScopeData& scopeData)
  613. {
  614. RHI::CommandList* commandList = context.GetCommandList();
  615. RHI::DeviceDispatchItem dispatchItem;
  616. uint32_t numSrgs = 0;
  617. dispatchItem.m_shaderResourceGroups[numSrgs++] = m_cullShaderResourceGroup->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get();
  618. dispatchItem.m_shaderResourceGroups[numSrgs++] = m_sceneShaderResourceGroup->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get();
  619. for (const auto& srg : m_indirectCommandsShaderResourceGroups)
  620. {
  621. dispatchItem.m_shaderResourceGroups[numSrgs++] = srg->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get();
  622. }
  623. // Submit the dispatch in an indirect manner.
  624. // Not really needed but it tests the indirect dispatch code.
  625. RHI::DeviceDispatchIndirect dispatchArgs(
  626. 1, m_indirectDispatchBufferView.GetDeviceIndirectBufferView(context.GetDeviceIndex()), 0);
  627. dispatchItem.m_arguments = dispatchArgs;
  628. dispatchItem.m_pipelineState = m_cullPipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
  629. dispatchItem.m_shaderResourceGroupCount = static_cast<uint8_t>(numSrgs);
  630. commandList->Submit(dispatchItem);
  631. };
  632. m_scopeProducers.emplace_back(
  633. aznew RHI::ScopeProducerFunction<
  634. ScopeData,
  635. decltype(prepareFunction),
  636. decltype(compileFunction),
  637. decltype(executeFunction)>(
  638. RHI::ScopeId{ "IndirecDispatchScope" },
  639. ScopeData{},
  640. prepareFunction,
  641. compileFunction,
  642. executeFunction));
  643. }
  644. void IndirectRenderingExampleComponent::CreateDrawingScope()
  645. {
  646. // Scope responsible for drawing the primitives in an indirect manner
  647. // using an indirect buffer that was populated by a compute shader.
  648. RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
  649. uint32_t maxIndirectDrawCount = device->GetLimits().m_maxIndirectDrawCount;
  650. const auto prepareFunction = [this, maxIndirectDrawCount](RHI::FrameGraphInterface frameGraph, [[maybe_unused]] ScopeData& scopeData)
  651. {
  652. {
  653. // Binds the swap chain as a color attachment.
  654. RHI::ImageScopeAttachmentDescriptor descriptor;
  655. descriptor.m_attachmentId = m_outputAttachmentId;
  656. descriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Load;
  657. frameGraph.UseColorAttachment(descriptor);
  658. }
  659. {
  660. // Binds the transient depth attachment
  661. AZ::RHI::ImageScopeAttachmentDescriptor dsDesc;
  662. dsDesc.m_attachmentId = IndirectRendering::DepthBufferAttachmentId;
  663. dsDesc.m_imageViewDescriptor.m_overrideFormat = AZ::RHI::Format::D32_FLOAT;
  664. dsDesc.m_loadStoreAction.m_clearValue = AZ::RHI::ClearValue::CreateDepth(1.f);
  665. dsDesc.m_loadStoreAction.m_loadAction = AZ::RHI::AttachmentLoadAction::Clear;
  666. frameGraph.UseDepthStencilAttachment(
  667. dsDesc, AZ::RHI::ScopeAttachmentAccess::ReadWrite,
  668. RHI::ScopeAttachmentStage::EarlyFragmentTest | RHI::ScopeAttachmentStage::LateFragmentTest);
  669. }
  670. if (m_deviceSupportsCountBuffer)
  671. {
  672. // Count buffer.
  673. RHI::BufferScopeAttachmentDescriptor descriptor;
  674. descriptor.m_attachmentId = IndirectRendering::CountBufferAttachmentId;
  675. descriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Load;
  676. descriptor.m_loadStoreAction.m_storeAction = RHI::AttachmentStoreAction::Store;
  677. descriptor.m_bufferViewDescriptor = RHI::BufferViewDescriptor::CreateStructured(
  678. 0,
  679. static_cast<uint32_t>(m_resetCounterBuffer->GetDescriptor().m_byteCount / sizeof(uint32_t)),
  680. sizeof(uint32_t));
  681. frameGraph.UseAttachment(
  682. descriptor, RHI::ScopeAttachmentAccess::Read, RHI::ScopeAttachmentUsage::Indirect,
  683. RHI::ScopeAttachmentStage::DrawIndirect);
  684. }
  685. {
  686. // Indirect Buffer with the culled commands.
  687. RHI::BufferScopeAttachmentDescriptor descriptor;
  688. descriptor.m_attachmentId = IndirectRendering::CulledIndirectBufferAttachmentId;
  689. descriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Load;
  690. descriptor.m_loadStoreAction.m_storeAction = RHI::AttachmentStoreAction::Store;
  691. descriptor.m_bufferViewDescriptor = RHI::BufferViewDescriptor::CreateStructured(
  692. 0,
  693. m_numObjects,
  694. m_indirectDrawBufferSignature->GetByteStride());
  695. frameGraph.UseAttachment(
  696. descriptor, RHI::ScopeAttachmentAccess::Read, RHI::ScopeAttachmentUsage::Indirect,
  697. RHI::ScopeAttachmentStage::DrawIndirect);
  698. }
  699. frameGraph.SetEstimatedItemCount(uint32_t(std::ceil(m_numObjects/ float(maxIndirectDrawCount))));
  700. };
  701. RHI::EmptyCompileFunction<ScopeData> compileFunction;
  702. const auto executeFunction = [this, maxIndirectDrawCount](const RHI::FrameGraphExecuteContext& context, [[maybe_unused]] const ScopeData& scopeData)
  703. {
  704. RHI::CommandList* commandList = context.GetCommandList();
  705. // Set persistent viewport and scissor state.
  706. commandList->SetViewports(&m_viewport, 1);
  707. commandList->SetScissors(&m_scissor, 1);
  708. const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = {
  709. m_sceneShaderResourceGroup->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get()
  710. };
  711. // In case multi indirect drawing is not supported
  712. // we need to emit multiple indirect draw calls.
  713. m_drawIndirect.m_countBufferByteOffset = 0;
  714. for (uint32_t i = 0;
  715. i < m_numObjects;
  716. i += maxIndirectDrawCount, m_drawIndirect.m_countBufferByteOffset += sizeof(uint32_t))
  717. {
  718. // Offset the indirect buffer depending on the number of indirect draw calls that we can do.
  719. m_drawIndirect.m_maxSequenceCount = AZStd::min(m_numObjects - i, maxIndirectDrawCount);
  720. m_drawIndirect.m_indirectBufferByteOffset = i * m_indirectDrawBufferView.GetByteStride();
  721. m_drawIndirect.m_indirectBufferView = &m_indirectDrawBufferView;
  722. m_geometryViews[i].SetDrawArguments(m_drawIndirect);
  723. RHI::DeviceDrawItem drawItem;
  724. drawItem.m_geometryView = m_geometryViews[i].GetDeviceGeometryView(context.GetDeviceIndex());
  725. drawItem.m_streamIndices = m_geometryViews[i].GetFullStreamBufferIndices();
  726. drawItem.m_pipelineState = m_drawPipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
  727. drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
  728. drawItem.m_shaderResourceGroups = shaderResourceGroups;
  729. // Submit the indirect draw item.
  730. commandList->Submit(drawItem);
  731. }
  732. };
  733. m_scopeProducers.emplace_back(
  734. aznew RHI::ScopeProducerFunction<
  735. ScopeData,
  736. decltype(prepareFunction),
  737. decltype(compileFunction),
  738. decltype(executeFunction)>(
  739. RHI::ScopeId{ "IndirectDrawScope" },
  740. ScopeData{},
  741. prepareFunction,
  742. compileFunction,
  743. executeFunction));
  744. }
  745. void IndirectRenderingExampleComponent::Activate()
  746. {
  747. using namespace AZ;
  748. m_numObjects = s_maxNumberOfObjects / 2;
  749. m_geometryViews.resize(m_numObjects);
  750. RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
  751. const auto& deviceFeatures = device->GetFeatures();
  752. // Select the commands in the layout depending on the device capabilities.
  753. switch (deviceFeatures.m_indirectCommandTier)
  754. {
  755. case RHI::IndirectCommandTiers::Tier1:
  756. m_mode = SequenceType::DrawOnly;
  757. break;
  758. case RHI::IndirectCommandTiers::Tier2:
  759. m_mode = SequenceType::IARootConstantsDraw;
  760. break;
  761. default:
  762. AZ_Assert(false, "Indirect Rendering is not supported on this platform.");
  763. return;
  764. }
  765. m_deviceSupportsCountBuffer = deviceFeatures.m_indirectDrawCountBufferSupported;
  766. m_assetLoadManager = AZStd::make_unique<AZ::AssetCollectionAsyncLoader>();
  767. m_doneLoadingShaders = false;
  768. m_indirectDrawShaderVariantStableId = AZ::RPI::RootShaderVariantStableId;
  769. m_indirectDispatchShaderVariantStableId = AZ::RPI::RootShaderVariantStableId;
  770. // List of all assets this example needs.
  771. AZStd::vector<AssetCollectionAsyncLoader::AssetToLoadInfo> assetList = {
  772. {IndirectDrawShaderFilePath, azrtti_typeid<RPI::ShaderAsset>()},
  773. {IndirectDispatchShaderFilePath, azrtti_typeid<RPI::ShaderAsset>()},
  774. };
  775. // Configure the imgui progress list widget.
  776. auto onUserCancelledAction = [&]()
  777. {
  778. //AZ_TracePrintf(AsyncCompute::sampleName, "Cancelled by user.\n");
  779. m_assetLoadManager->Cancel();
  780. SampleComponentManagerRequestBus::Broadcast(&SampleComponentManagerRequests::Reset);
  781. };
  782. m_imguiProgressList.OpenPopup("Waiting For Assets...", "Assets pending for processing:", {}, onUserCancelledAction, true /*automaticallyCloseOnAction*/, "Cancel");
  783. AZStd::for_each(assetList.begin(), assetList.end(),
  784. [&](const AssetCollectionAsyncLoader::AssetToLoadInfo& item) { m_imguiProgressList.AddItem(item.m_assetPath); });
  785. m_imguiProgressList.AddItem(IndirectDrawVariantLabel);
  786. m_imguiProgressList.AddItem(IndirectDispatchVariantLabel);
  787. // Kickoff asynchronous asset loading, the activation will continue once all assets are available.
  788. m_assetLoadManager->LoadAssetsAsync(assetList, [&](AZStd::string_view assetName, [[maybe_unused]] bool success, size_t pendingAssetCount)
  789. {
  790. AZ_Error(LogName, success, "Error loading asset %s, a crash will occur when OnAllAssetsReadyActivate() is called!", assetName.data());
  791. m_imguiProgressList.RemoveItem(assetName);
  792. if (!pendingAssetCount && !m_doneLoadingShaders)
  793. {
  794. m_doneLoadingShaders = true;
  795. InitRequestsForShaderVariants();
  796. }
  797. });
  798. }
  799. void IndirectRenderingExampleComponent::OnAllAssetsReadyActivate()
  800. {
  801. InitInputAssemblyResources();
  802. InitShaderResources();
  803. InitIndirectRenderingResources();
  804. InitInstancesDataResources();
  805. // We use 3 scopes.
  806. // The first one is for reseting the count buffer to 0.
  807. // The second one is the compute scope in charge of culling.
  808. // The last one is the graphic scope in charge of rendering the culled primitives.
  809. if (m_deviceSupportsCountBuffer)
  810. {
  811. CreateResetCounterBufferScope();
  812. }
  813. CreateCullingScope();
  814. CreateDrawingScope();
  815. m_imguiSidebar.Activate();
  816. AZ::TickBus::Handler::BusConnect();
  817. AZ::RHI::RHISystemNotificationBus::Handler::BusConnect();
  818. AzFramework::WindowNotificationBus::Handler::BusConnect(m_windowContext->GetWindowHandle());
  819. }
  820. void IndirectRenderingExampleComponent::Deactivate()
  821. {
  822. m_inputAssemblyBufferPool = nullptr;
  823. m_shaderBufferPool = nullptr;
  824. m_instancesBufferPool = nullptr;
  825. m_copyBufferPool = nullptr;
  826. m_inputAssemblyBuffer = nullptr;
  827. m_sourceIndirectBuffer = nullptr;
  828. m_instancesDataBuffer = nullptr;
  829. m_resetCounterBuffer = nullptr;
  830. m_drawPipelineState = nullptr;
  831. m_cullPipelineState = nullptr;
  832. m_sceneShaderResourceGroup = nullptr;
  833. m_cullShaderResourceGroup = nullptr;
  834. m_indirectCommandsShaderResourceGroups.fill(nullptr);
  835. m_sourceIndirectBufferView = nullptr;
  836. m_instancesDataBufferView = nullptr;
  837. m_indirectDispatchWriter = nullptr;
  838. m_indirectDrawBufferSignature = nullptr;
  839. m_indirectDispatchBufferSignature = nullptr;
  840. m_instancesData.clear();
  841. m_geometryViews.clear();
  842. m_imguiSidebar.Deactivate();
  843. AzFramework::WindowNotificationBus::Handler::BusDisconnect();
  844. AZ::RHI::RHISystemNotificationBus::Handler::BusDisconnect();
  845. AZ::TickBus::Handler::BusDisconnect();
  846. AZ::RPI::ShaderReloadNotificationBus::MultiHandler::BusDisconnect();
  847. m_windowContext = nullptr;
  848. m_scopeProducers.clear();
  849. }
  850. void IndirectRenderingExampleComponent::DrawSampleSettings()
  851. {
  852. ImGui::Spacing();
  853. ScriptableImGui::SliderFloat("Cull Offset", &m_cullOffset, 0.f, 1.f);
  854. if (ScriptableImGui::SliderInt("Num Objects", reinterpret_cast<int*>(&m_numObjects), 1, s_maxNumberOfObjects))
  855. {
  856. m_updateIndirectDispatchArguments = true;
  857. }
  858. m_imguiSidebar.End();
  859. }
  860. void IndirectRenderingExampleComponent::UpdateInstancesData(float deltaTime)
  861. {
  862. using namespace AZ;
  863. AZ::SimpleLcgRandom random;
  864. const float offsetBounds = 2.5f;
  865. for (uint32_t i = 0; i < m_numObjects; ++i)
  866. {
  867. m_instancesData[i].m_offset.SetX(m_instancesData[i].m_offset.GetX() + m_instancesData[i].m_velocity.GetX() * deltaTime);
  868. if (m_instancesData[i].m_offset.GetX() > offsetBounds)
  869. {
  870. m_instancesData[i].m_offset.SetX(-offsetBounds);
  871. m_instancesData[i].m_velocity.Set(GetRandomFloat(IndirectRendering::VelocityRange.GetX(), IndirectRendering::VelocityRange.GetY()));
  872. }
  873. }
  874. RHI::BufferMapRequest request(*m_instancesDataBuffer, 0, sizeof(InstanceData) * m_numObjects);
  875. RHI::BufferMapResponse response;
  876. m_instancesBufferPool->MapBuffer(request, response);
  877. if (!response.m_data.empty())
  878. {
  879. for(auto& [_, responseData] : response.m_data)
  880. {
  881. ::memcpy(responseData, m_instancesData.data(), request.m_byteCount);
  882. }
  883. m_instancesBufferPool->UnmapBuffer(*m_instancesDataBuffer);
  884. }
  885. }
  886. void IndirectRenderingExampleComponent::OnWindowResized(uint32_t width, uint32_t height)
  887. {
  888. if (m_sceneShaderResourceGroup)
  889. {
  890. float aspectRatio = width / float(height);
  891. m_sceneShaderResourceGroup->SetConstant(m_sceneMatrixInputIndex, AZ::Matrix4x4::CreateScale(AZ::Vector3(1.f / aspectRatio, 1.f, 1.f)));
  892. m_sceneShaderResourceGroup->Compile();
  893. }
  894. }
  895. void IndirectRenderingExampleComponent::UpdateIndirectDispatchArguments()
  896. {
  897. m_indirectDispatchWriter->Seek(0);
  898. RHI::DispatchDirect args;
  899. args.m_threadsPerGroupX = IndirectRendering::ThreadGroupSize;
  900. args.m_totalNumberOfThreadsX = aznumeric_cast<uint16_t>(m_numObjects);
  901. m_indirectDispatchWriter->Dispatch(args);
  902. m_indirectDispatchWriter->Flush();
  903. }
  904. void IndirectRenderingExampleComponent::InitRequestsForShaderVariants()
  905. {
  906. {
  907. m_indirectDrawShader = LoadShader(*m_assetLoadManager.get(), IndirectDrawShaderFilePath, IndirectRendering::SampleName);
  908. AZ::RPI::ShaderReloadNotificationBus::MultiHandler::BusConnect(m_indirectDrawShader->GetAsset().GetId());
  909. // We use a shader option to tell the shader if the object index must be read from the vertex input or an inline constant.
  910. m_indirectDrawShaderOptionGroup = m_indirectDrawShader->CreateShaderOptionGroup();
  911. m_indirectDrawShaderOptionGroup.SetValue(AZ::Name("o_useRootConstants"), m_mode == SequenceType::IARootConstantsDraw ? AZ::Name("true") : AZ::Name("false"));
  912. //Kickoff the asynchronous loading of the variant tree.
  913. auto shaderVariant = m_indirectDrawShader->GetVariant(m_indirectDrawShaderOptionGroup.GetShaderVariantId());
  914. if (!shaderVariant.IsRootVariant())
  915. {
  916. m_indirectDrawShaderVariantStableId = shaderVariant.GetStableId();
  917. m_imguiProgressList.RemoveItem(IndirectDrawVariantLabel);
  918. AZ::RPI::ShaderReloadNotificationBus::MultiHandler::BusDisconnect(m_indirectDrawShader->GetAsset().GetId());
  919. }
  920. }
  921. {
  922. m_indirectDispatchShader = LoadShader(*m_assetLoadManager.get(), IndirectDispatchShaderFilePath, IndirectRendering::SampleName);
  923. AZ::RPI::ShaderReloadNotificationBus::MultiHandler::BusConnect(m_indirectDispatchShader->GetAsset().GetId());
  924. // We use a shader option to tell the shader which commands are included in the buffer and
  925. // another option to tell the shader if count buffers are supported.
  926. m_indirectDispatchShaderOptionGroup = m_indirectDispatchShader->CreateShaderOptionGroup();
  927. const char* optionValues[] = { "SequenceType::Draw", "SequenceType::IAInlineConstDraw" };
  928. m_indirectDispatchShaderOptionGroup.SetValue(AZ::Name("o_sequenceType"), AZ::Name(optionValues[static_cast<uint32_t>(m_mode)]));
  929. m_indirectDispatchShaderOptionGroup.SetValue(AZ::Name("o_countBufferSupported"), m_deviceSupportsCountBuffer ? AZ::Name("true") : AZ::Name("false"));
  930. //Kickoff the asynchronous loading of the variant tree.
  931. auto shaderVariant = m_indirectDispatchShader->GetVariant(m_indirectDispatchShaderOptionGroup.GetShaderVariantId());
  932. if (!shaderVariant.IsRootVariant())
  933. {
  934. m_indirectDispatchShaderVariantStableId = shaderVariant.GetStableId();
  935. m_imguiProgressList.RemoveItem(IndirectDispatchVariantLabel);
  936. AZ::RPI::ShaderReloadNotificationBus::MultiHandler::BusDisconnect(m_indirectDispatchShader->GetAsset().GetId());
  937. }
  938. }
  939. if ((m_indirectDrawShaderVariantStableId != AZ::RPI::RootShaderVariantStableId) &&
  940. (m_indirectDispatchShaderVariantStableId != AZ::RPI::RootShaderVariantStableId))
  941. {
  942. AZ_TracePrintf(LogName, "Got all variants already from InitRequestsForShaderVariantAssets()\n");
  943. OnAllAssetsReadyActivate();
  944. }
  945. }
  946. ///////////////////////////////////////////////////////////////////////
  947. // ShaderReloadNotificationBus overrides
  948. void IndirectRenderingExampleComponent::OnShaderVariantReinitialized(const AZ::RPI::ShaderVariant& shaderVariant)
  949. {
  950. //AZ_TracePrintf(LogName, "Got variant %s\n", shaderVariantAsset.GetHint().c_str());
  951. const AZ::Data::AssetId* shaderAssetId = AZ::RPI::ShaderReloadNotificationBus::GetCurrentBusId();
  952. if (*shaderAssetId == m_indirectDrawShader->GetAsset().GetId())
  953. {
  954. m_indirectDrawShaderVariantStableId = shaderVariant.GetStableId();
  955. m_imguiProgressList.RemoveItem(IndirectDrawVariantLabel);
  956. }
  957. else if (*shaderAssetId == m_indirectDispatchShader->GetAsset().GetId())
  958. {
  959. m_indirectDispatchShaderVariantStableId = shaderVariant.GetStableId();
  960. m_imguiProgressList.RemoveItem(IndirectDispatchVariantLabel);
  961. }
  962. if ((m_indirectDrawShaderVariantStableId != AZ::RPI::RootShaderVariantStableId) &&
  963. (m_indirectDispatchShaderVariantStableId != AZ::RPI::RootShaderVariantStableId))
  964. {
  965. AZ_TracePrintf(LogName, "Got all variants\n");
  966. OnAllAssetsReadyActivate();
  967. }
  968. }
  969. ///////////////////////////////////////////////////////////////////////
  970. } // namespace AtomSampleViewer