IndirectRenderingExampleComponent.cpp 53 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130
  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 = RHI::Factory::Get().CreateBufferPool();
  119. RHI::BufferPoolDescriptor bufferPoolDesc;
  120. bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::InputAssembly;
  121. bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Device;
  122. m_inputAssemblyBufferPool->Init(*device, 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 = RHI::Factory::Get().CreateBuffer();
  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. }
  211. void IndirectRenderingExampleComponent::InitShaderResources()
  212. {
  213. {
  214. auto shader = m_indirectDrawShader;
  215. if (shader == nullptr)
  216. {
  217. return;
  218. }
  219. AZ::RHI::PipelineStateDescriptorForDraw drawPipelineStateDescriptor;
  220. drawPipelineStateDescriptor.m_inputStreamLayout = m_inputStreamLayout;
  221. shader->GetVariant(m_indirectDrawShaderVariantStableId).ConfigurePipelineState(drawPipelineStateDescriptor);
  222. RHI::RenderAttachmentLayoutBuilder attachmentsBuilder;
  223. attachmentsBuilder.AddSubpass()
  224. ->RenderTargetAttachment(m_outputFormat)
  225. ->DepthStencilAttachment(RHI::Format::D32_FLOAT);
  226. [[maybe_unused]] RHI::ResultCode result = attachmentsBuilder.End(drawPipelineStateDescriptor.m_renderAttachmentConfiguration.m_renderAttachmentLayout);
  227. AZ_Assert(result == RHI::ResultCode::Success, "Failed to create render attachment layout");
  228. drawPipelineStateDescriptor.m_renderStates.m_depthStencilState = AZ::RHI::DepthStencilState::CreateDepth();
  229. m_drawPipelineState = shader->AcquirePipelineState(drawPipelineStateDescriptor);
  230. if (!m_drawPipelineState)
  231. {
  232. AZ_Error(IndirectRendering::SampleName, false, "Failed to acquire default pipeline state for shader '%s'", IndirectDrawShaderFilePath);
  233. return;
  234. }
  235. m_sceneShaderResourceGroup = CreateShaderResourceGroup(shader, "IndirectSceneSrg", IndirectRendering::SampleName);
  236. // Not needed because this example uses static variants (preloaded before this function is called).
  237. // But left here for educational purposes:
  238. // Fallback in case we don't have the exact match.
  239. if (m_sceneShaderResourceGroup->HasShaderVariantKeyFallbackEntry())
  240. {
  241. m_sceneShaderResourceGroup->SetShaderVariantKeyFallbackValue(m_indirectDrawShaderOptionGroup.GetShaderVariantId().m_key);
  242. }
  243. }
  244. {
  245. auto shader = m_indirectDispatchShader;
  246. if (!shader)
  247. {
  248. return;
  249. }
  250. AZ::RHI::PipelineStateDescriptorForDispatch computePipelineStateDescriptor;
  251. shader->GetVariant(m_indirectDispatchShaderVariantStableId).ConfigurePipelineState(computePipelineStateDescriptor);
  252. m_cullPipelineState = shader->AcquirePipelineState(computePipelineStateDescriptor);
  253. if (!m_cullPipelineState)
  254. {
  255. AZ_Error(IndirectRendering::SampleName, false, "Failed to acquire default pipeline state for shader '%s'", IndirectDispatchShaderFilePath);
  256. return;
  257. }
  258. m_cullShaderResourceGroup = CreateShaderResourceGroup(shader, "CullSrg", IndirectRendering::SampleName);
  259. const Name countBufferId{ "m_outNumCommands" };
  260. const Name cullOffsetId{ "m_cullOffset" };
  261. const Name numCommandsId{ "m_inNumCommands" };
  262. const Name maxCommandsId{ "m_maxDrawIndirectCount" };
  263. FindShaderInputIndex(&m_cullingCountBufferIndex, m_cullShaderResourceGroup, countBufferId, IndirectRendering::SampleName);
  264. FindShaderInputIndex(&m_cullingOffsetIndex, m_cullShaderResourceGroup, cullOffsetId, IndirectRendering::SampleName);
  265. FindShaderInputIndex(&m_cullingNumCommandsIndex, m_cullShaderResourceGroup, numCommandsId, IndirectRendering::SampleName);
  266. FindShaderInputIndex(&m_cullingMaxCommandsIndex, m_cullShaderResourceGroup, maxCommandsId, IndirectRendering::SampleName);
  267. const Name inputCommandsId{ "m_inputCommands" };
  268. const Name outputCommandsId{ "m_outputCommands" };
  269. const char* srgNames[] = { "IndirectDrawCommandsSrg", "IndirectIAInlineConstCommandsSrg" };
  270. for (uint32_t i = 0; i < NumSequencesType; ++i)
  271. {
  272. m_indirectCommandsShaderResourceGroups[i] = CreateShaderResourceGroup(shader, srgNames[i], IndirectRendering::SampleName);
  273. FindShaderInputIndex(&m_cullingInputIndirectBufferIndices[i], m_indirectCommandsShaderResourceGroups[i], inputCommandsId, IndirectRendering::SampleName);
  274. FindShaderInputIndex(&m_cullingOutputIndirectBufferIndices[i], m_indirectCommandsShaderResourceGroups[i], outputCommandsId, IndirectRendering::SampleName);
  275. }
  276. }
  277. }
  278. void IndirectRenderingExampleComponent::InitIndirectRenderingResources()
  279. {
  280. RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
  281. RHI::ResultCode result;
  282. m_shaderBufferPool = RHI::Factory::Get().CreateBufferPool();
  283. RHI::BufferPoolDescriptor bufferPoolDesc;
  284. bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::ShaderRead | RHI::BufferBindFlags::Indirect;
  285. bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Device;
  286. m_shaderBufferPool->Init(*device, bufferPoolDesc);
  287. // Create the layout depending on which commands are supported by the device.
  288. m_indirectDrawBufferLayout = RHI::IndirectBufferLayout();
  289. if (m_mode == SequenceType::IARootConstantsDraw)
  290. {
  291. m_indirectDrawBufferLayout.AddIndirectCommand(RHI::IndirectCommandDescriptor(RHI::IndirectCommandType::RootConstants));
  292. m_indirectDrawBufferLayout.AddIndirectCommand(RHI::IndirectCommandDescriptor(RHI::IndirectBufferViewArguments{ 0 }));
  293. m_indirectDrawBufferLayout.AddIndirectCommand(RHI::IndirectCommandDescriptor(RHI::IndirectCommandType::IndexBufferView));
  294. }
  295. m_indirectDrawBufferLayout.AddIndirectCommand(RHI::IndirectCommandDescriptor(RHI::IndirectCommandType::DrawIndexed));
  296. if (!m_indirectDrawBufferLayout.Finalize())
  297. {
  298. AZ_Assert(false, "Fail to finalize Indirect Layout");
  299. return;
  300. }
  301. // Create the signature and pass the pipeline state since we may have
  302. // an inline constants command.
  303. m_indirectDrawBufferSignature = RHI::Factory::Get().CreateIndirectBufferSignature();
  304. RHI::IndirectBufferSignatureDescriptor signatureDescriptor;
  305. signatureDescriptor.m_layout = m_indirectDrawBufferLayout;
  306. signatureDescriptor.m_pipelineState = m_drawPipelineState.get();
  307. result = m_indirectDrawBufferSignature->Init(*device, signatureDescriptor);
  308. if (result != RHI::ResultCode::Success)
  309. {
  310. AZ_Assert(false, "Fail to initialize Indirect Buffer Signature");
  311. return;
  312. }
  313. m_sourceIndirectBuffer = RHI::Factory::Get().CreateBuffer();
  314. uint32_t commandsStride = m_indirectDrawBufferSignature->GetByteStride();
  315. RHI::BufferInitRequest request;
  316. request.m_buffer = m_sourceIndirectBuffer.get();
  317. request.m_descriptor = RHI::BufferDescriptor(
  318. bufferPoolDesc.m_bindFlags,
  319. commandsStride * s_maxNumberOfObjects);
  320. m_shaderBufferPool->InitBuffer(request);
  321. // Create a writer to populate the buffer with the commands.
  322. auto indirectBufferWriter = RHI::Factory::Get().CreateIndirectBufferWriter();
  323. result = indirectBufferWriter->Init(*m_sourceIndirectBuffer, 0, commandsStride, s_maxNumberOfObjects, *m_indirectDrawBufferSignature);
  324. if (result != RHI::ResultCode::Success)
  325. {
  326. AZ_Assert(false, "Fail to initialize Indirect Buffer Writer");
  327. return;
  328. }
  329. RHI::StreamBufferView& triangleStreamBufferView = m_streamBufferViews[0];
  330. RHI::StreamBufferView& quadStreamBufferView = m_streamBufferViews[2];
  331. RHI::IndexBufferView& triangleIndexBufferView = m_indexBufferViews[0];
  332. RHI::IndexBufferView& quadIndexBufferView = m_indexBufferViews[1];
  333. // Write the commands using the IndirectBufferWriter
  334. // We alternate between drawing a triangle and a quad.
  335. for (uint32_t i = 0; i < s_maxNumberOfObjects; ++i)
  336. {
  337. if (i % 2)
  338. {
  339. if (m_mode == SequenceType::IARootConstantsDraw)
  340. {
  341. indirectBufferWriter->SetRootConstants(reinterpret_cast<uint8_t*>(&i), sizeof(i))
  342. ->SetVertexView(0, triangleStreamBufferView)
  343. ->SetIndexView(triangleIndexBufferView);
  344. }
  345. indirectBufferWriter->DrawIndexed(
  346. RHI::DrawIndexed(
  347. 1,
  348. i,
  349. 0,
  350. 3,
  351. 0));
  352. }
  353. else
  354. {
  355. RHI::DrawIndexed arguments(
  356. 1,
  357. i,
  358. 0,
  359. 6,
  360. 0);
  361. switch (m_mode)
  362. {
  363. case SequenceType::IARootConstantsDraw:
  364. indirectBufferWriter->SetRootConstants(reinterpret_cast<uint8_t*>(&i), sizeof(i))
  365. ->SetVertexView(0, quadStreamBufferView)
  366. ->SetIndexView(quadIndexBufferView);
  367. break;
  368. case SequenceType::DrawOnly:
  369. // Since we are using one vertex buffer view and one index buffer view for both type of primitives
  370. // we need to adjust the vertex and index offset so they point to the proper location.
  371. arguments.m_vertexOffset = decltype(BufferData::m_trianglePositions)::array_size;
  372. arguments.m_indexOffset = decltype(BufferData::m_triangleIndices)::array_size;
  373. break;
  374. default:
  375. AZ_Assert(false, "Invalid sequence type");
  376. return;
  377. }
  378. indirectBufferWriter->DrawIndexed(arguments);
  379. }
  380. indirectBufferWriter->NextSequence();
  381. }
  382. indirectBufferWriter->Shutdown();
  383. auto viewDescriptor = RHI::BufferViewDescriptor::CreateStructured(0, s_maxNumberOfObjects, commandsStride);
  384. m_sourceIndirectBufferView = m_sourceIndirectBuffer->GetBufferView(viewDescriptor);
  385. if(!m_sourceIndirectBufferView.get())
  386. {
  387. AZ_Assert(false, "Fail to initialize Indirect Buffer View");
  388. return;
  389. }
  390. // Create the buffer that will contain the compute dispatch arguments.
  391. m_indirectDispatchBuffer = RHI::Factory::Get().CreateBuffer();
  392. {
  393. m_indirectDispatchBufferLayout = RHI::IndirectBufferLayout();
  394. m_indirectDispatchBufferLayout.AddIndirectCommand(RHI::IndirectCommandDescriptor(RHI::IndirectCommandType::Dispatch));
  395. if (!m_indirectDispatchBufferLayout.Finalize())
  396. {
  397. AZ_Assert(false, "Fail to finalize Indirect Layout");
  398. return;
  399. }
  400. m_indirectDispatchBufferSignature = RHI::Factory::Get().CreateIndirectBufferSignature();
  401. signatureDescriptor = {};
  402. signatureDescriptor.m_layout = m_indirectDispatchBufferLayout;
  403. result = m_indirectDispatchBufferSignature->Init(*device, signatureDescriptor);
  404. if (result != RHI::ResultCode::Success)
  405. {
  406. AZ_Assert(false, "Fail to initialize Indirect Buffer Signature");
  407. return;
  408. }
  409. uint32_t indirectDispatchStride = m_indirectDispatchBufferSignature->GetByteStride();
  410. request = {};
  411. request.m_buffer = m_indirectDispatchBuffer.get();
  412. request.m_descriptor = RHI::BufferDescriptor(
  413. bufferPoolDesc.m_bindFlags,
  414. indirectDispatchStride);
  415. m_shaderBufferPool->InitBuffer(request);
  416. m_indirectDispatchBufferView =
  417. {
  418. *m_indirectDispatchBuffer,
  419. *m_indirectDispatchBufferSignature,
  420. 0,
  421. indirectDispatchStride,
  422. indirectDispatchStride
  423. };
  424. m_indirectDispatchWriter = RHI::Factory::Get().CreateIndirectBufferWriter();
  425. result = m_indirectDispatchWriter->Init(*m_indirectDispatchBuffer, 0, indirectDispatchStride, 1, *m_indirectDispatchBufferSignature);
  426. if (result != RHI::ResultCode::Success)
  427. {
  428. AZ_Assert(false, "Fail to initialize Indirect Buffer Writer");
  429. return;
  430. }
  431. UpdateIndirectDispatchArguments();
  432. }
  433. }
  434. void IndirectRenderingExampleComponent::InitInstancesDataResources()
  435. {
  436. RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
  437. uint32_t maxIndirectDrawCount = device->GetLimits().m_maxIndirectDrawCount;
  438. // Populate the data for each instance using some random values.
  439. m_instancesData.resize(s_maxNumberOfObjects);
  440. for (InstanceData& data : m_instancesData)
  441. {
  442. float scale = GetRandomFloat(0.01, 0.1f);
  443. data.m_offset = AZ::Vector4(GetRandomFloat(-4.0f, -2.0f), GetRandomFloat(-1.f, 1.f), GetRandomFloat(0.f, 1.f), 0.f);
  444. data.m_scale = AZ::Vector4(scale, scale, 1.f, 0.f);
  445. data.m_color = AZ::Color(GetRandomFloat(0.5f, 1.0f), GetRandomFloat(0.5f, 1.0f), GetRandomFloat(0.5f, 1.0f), 1.0f);
  446. data.m_velocity.Set(GetRandomFloat(IndirectRendering::VelocityRange.GetX(), IndirectRendering::VelocityRange.GetY()));
  447. }
  448. m_instancesBufferPool = RHI::Factory::Get().CreateBufferPool();
  449. RHI::BufferPoolDescriptor bufferPoolDesc;
  450. bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::ShaderRead;
  451. bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Host;
  452. m_instancesBufferPool->Init(*device, bufferPoolDesc);
  453. m_instancesDataBuffer = RHI::Factory::Get().CreateBuffer();
  454. RHI::BufferInitRequest request;
  455. request.m_buffer = m_instancesDataBuffer.get();
  456. request.m_descriptor = RHI::BufferDescriptor{
  457. RHI::BufferBindFlags::ShaderRead,
  458. sizeof(InstanceData) * s_maxNumberOfObjects };
  459. request.m_initialData = m_instancesData.data();
  460. m_instancesBufferPool->InitBuffer(request);
  461. auto descriptor = RHI::BufferViewDescriptor::CreateStructured(0, static_cast<uint32_t>(m_instancesData.size()), sizeof(InstanceData));
  462. m_instancesDataBufferView = m_instancesDataBuffer->GetBufferView(descriptor);
  463. if(!m_instancesDataBufferView.get())
  464. {
  465. AZ_Assert(false, "Fail to initialize Instances Data Buffer View");
  466. return;
  467. }
  468. // Create the count buffer if the platform supports it.
  469. // The count buffer will contain the actual number of primitives to draw after
  470. // the compute shader has culled the commands.
  471. if (m_deviceSupportsCountBuffer)
  472. {
  473. m_copyBufferPool = RHI::Factory::Get().CreateBufferPool();
  474. bufferPoolDesc = {};
  475. bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::CopyRead;
  476. bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Host;
  477. m_copyBufferPool->Init(*device, bufferPoolDesc);
  478. m_resetCounterBuffer = RHI::Factory::Get().CreateBuffer();
  479. AZStd::vector<uint32_t> initData;
  480. initData.assign(static_cast<size_t>(std::ceil(float(s_maxNumberOfObjects) / maxIndirectDrawCount)), 0);
  481. request = {};
  482. request.m_buffer = m_resetCounterBuffer.get();
  483. request.m_descriptor = RHI::BufferDescriptor
  484. {
  485. RHI::BufferBindFlags::CopyRead,
  486. sizeof(uint32_t) * initData.size()
  487. };
  488. request.m_initialData = initData.data();
  489. m_copyBufferPool->InitBuffer(request);
  490. }
  491. {
  492. const Name instancesDataId{ "m_instancesData" };
  493. const Name matrixId{ "m_matrix" };
  494. FindShaderInputIndex(&m_sceneInstancesDataBufferIndex, m_sceneShaderResourceGroup, instancesDataId, IndirectRendering::SampleName);
  495. FindShaderInputIndex(&m_sceneMatrixInputIndex, m_sceneShaderResourceGroup, matrixId, IndirectRendering::SampleName);
  496. float screenAspect = GetViewportWidth() / GetViewportHeight();
  497. m_sceneShaderResourceGroup->SetBufferView(m_sceneInstancesDataBufferIndex, m_instancesDataBufferView.get());
  498. m_sceneShaderResourceGroup->SetConstant(m_sceneMatrixInputIndex, AZ::Matrix4x4::CreateScale(AZ::Vector3(1.f/ screenAspect, 1.f, 1.f)));
  499. m_sceneShaderResourceGroup->Compile();
  500. }
  501. {
  502. uint32_t sequenceTypeIndex = static_cast<uint32_t>(m_mode);
  503. m_indirectCommandsShaderResourceGroups[sequenceTypeIndex]->SetBufferView(m_cullingInputIndirectBufferIndices[sequenceTypeIndex], m_sourceIndirectBufferView.get());
  504. }
  505. }
  506. void IndirectRenderingExampleComponent::CreateResetCounterBufferScope()
  507. {
  508. // This scope resets the count buffer value to 0 every frame.
  509. // Since we increment it in the shader using InterlockedAdd we need it
  510. // to start with 0.
  511. // To reset it we just copy a buffer with the proper values into the count buffer.
  512. const auto prepareFunction = [this](RHI::FrameGraphInterface frameGraph, [[maybe_unused]] ScopeData& scopeData)
  513. {
  514. {
  515. RHI::BufferScopeAttachmentDescriptor countBufferAttachment;
  516. countBufferAttachment.m_attachmentId = IndirectRendering::CountBufferAttachmentId;
  517. countBufferAttachment.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::DontCare;
  518. countBufferAttachment.m_bufferViewDescriptor = RHI::BufferViewDescriptor::CreateStructured(
  519. 0,
  520. static_cast<uint32_t>(m_resetCounterBuffer->GetDescriptor().m_byteCount / sizeof(uint32_t)),
  521. sizeof(uint32_t));
  522. frameGraph.UseCopyAttachment(countBufferAttachment, RHI::ScopeAttachmentAccess::Write);
  523. }
  524. };
  525. const auto compileFunction = [this](const RHI::FrameGraphCompileContext& context, [[maybe_unused]] const ScopeData& scopeData)
  526. {
  527. const RHI::BufferView* countBufferView = context.GetBufferView(RHI::AttachmentId{ IndirectRendering::CountBufferAttachmentId });
  528. m_copyDescriptor.m_sourceBuffer = m_resetCounterBuffer.get();
  529. m_copyDescriptor.m_sourceOffset = 0;
  530. m_copyDescriptor.m_destinationBuffer = &countBufferView->GetBuffer();
  531. m_copyDescriptor.m_destinationOffset = 0;
  532. m_copyDescriptor.m_size = static_cast<uint32_t>(m_resetCounterBuffer->GetDescriptor().m_byteCount);
  533. };
  534. const auto executeFunction = [this](const RHI::FrameGraphExecuteContext& context, [[maybe_unused]] const ScopeData& scopeData)
  535. {
  536. RHI::CopyItem copyItem(m_copyDescriptor);
  537. context.GetCommandList()->Submit(copyItem);
  538. };
  539. m_scopeProducers.emplace_back(
  540. aznew RHI::ScopeProducerFunction<
  541. ScopeData,
  542. decltype(prepareFunction),
  543. decltype(compileFunction),
  544. decltype(executeFunction)>(
  545. RHI::ScopeId{ "IndirectResetCounterScope" },
  546. ScopeData{},
  547. prepareFunction,
  548. compileFunction,
  549. executeFunction));
  550. }
  551. void IndirectRenderingExampleComponent::CreateCullingScope()
  552. {
  553. // This scopes culls the primitives that are outside of a designated area.
  554. // In order to do this it copies the commands from an input buffer into
  555. // and output one. If count buffers are supported only valid commands are copied.
  556. // Otherwise it copies all commands but it updates the vertex count to 0 so no
  557. // triangles are rendered.
  558. // The dispatch call for this scope is done in an indirect manner. The indirect buffer for this dispatch
  559. // is populated on CPU.
  560. RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
  561. uint32_t maxIndirectDrawCount = device->GetLimits().m_maxIndirectDrawCount;
  562. const auto prepareFunction = [this](RHI::FrameGraphInterface frameGraph, [[maybe_unused]] ScopeData& scopeData)
  563. {
  564. uint32_t commandsStride = m_indirectDrawBufferSignature->GetByteStride();
  565. RHI::BufferScopeAttachmentDescriptor culledBufferAttachment;
  566. culledBufferAttachment.m_attachmentId = IndirectRendering::CulledIndirectBufferAttachmentId;
  567. culledBufferAttachment.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::DontCare;
  568. culledBufferAttachment.m_bufferViewDescriptor = RHI::BufferViewDescriptor::CreateStructured(0, m_numObjects, commandsStride);
  569. frameGraph.UseShaderAttachment(culledBufferAttachment, RHI::ScopeAttachmentAccess::ReadWrite);
  570. if (m_deviceSupportsCountBuffer)
  571. {
  572. // The count buffer that we will be writing the final count of operations.
  573. RHI::BufferScopeAttachmentDescriptor countBufferAttachment;
  574. countBufferAttachment.m_attachmentId = IndirectRendering::CountBufferAttachmentId;
  575. countBufferAttachment.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Load;
  576. countBufferAttachment.m_bufferViewDescriptor = RHI::BufferViewDescriptor::CreateStructured(
  577. 0,
  578. static_cast<uint32_t>(m_resetCounterBuffer->GetDescriptor().m_byteCount / sizeof(uint32_t)),
  579. sizeof(uint32_t));
  580. frameGraph.UseShaderAttachment(countBufferAttachment, RHI::ScopeAttachmentAccess::ReadWrite);
  581. }
  582. };
  583. const auto compileFunction = [this, maxIndirectDrawCount](const RHI::FrameGraphCompileContext& context, [[maybe_unused]] const ScopeData& scopeData)
  584. {
  585. const RHI::BufferView* culledBufferView = context.GetBufferView(RHI::AttachmentId{ IndirectRendering::CulledIndirectBufferAttachmentId });
  586. uint32_t sequenceTypeIndex = static_cast<uint32_t>(m_mode);
  587. auto& indirectCommandsSRG = m_indirectCommandsShaderResourceGroups[sequenceTypeIndex];
  588. indirectCommandsSRG->SetBufferView(m_cullingOutputIndirectBufferIndices[sequenceTypeIndex], culledBufferView);
  589. indirectCommandsSRG->Compile();
  590. if (m_deviceSupportsCountBuffer)
  591. {
  592. const RHI::BufferView* countBufferView = context.GetBufferView(RHI::AttachmentId{ IndirectRendering::CountBufferAttachmentId });
  593. m_cullShaderResourceGroup->SetBufferView(m_cullingCountBufferIndex, countBufferView);
  594. m_drawIndirect.m_countBuffer = &countBufferView->GetBuffer();
  595. m_drawIndirect.m_countBufferByteOffset = 0;
  596. }
  597. // Update the cull area
  598. float cullScale = 0.75f;
  599. AZ::Vector2 cullPlane = AZ::Vector2(-m_cullOffset, m_cullOffset) * AZ::Vector2(cullScale) + AZ::Vector2(cullScale - 1.0f);
  600. m_cullShaderResourceGroup->SetConstant(m_cullingOffsetIndex, cullPlane);
  601. m_cullShaderResourceGroup->SetConstant(m_cullingNumCommandsIndex, m_numObjects);
  602. m_cullShaderResourceGroup->SetConstant(m_cullingMaxCommandsIndex, AZStd::min(m_numObjects, maxIndirectDrawCount));
  603. m_cullShaderResourceGroup->Compile();
  604. uint32_t stride = m_indirectDrawBufferSignature->GetByteStride();
  605. m_indirectDrawBufferView =
  606. {
  607. culledBufferView->GetBuffer(),
  608. *m_indirectDrawBufferSignature,
  609. 0,
  610. stride * m_numObjects,
  611. stride
  612. };
  613. };
  614. const auto executeFunction = [this](const RHI::FrameGraphExecuteContext& context, [[maybe_unused]] const ScopeData& scopeData)
  615. {
  616. RHI::CommandList* commandList = context.GetCommandList();
  617. RHI::DispatchItem dispatchItem;
  618. uint32_t numSrgs = 0;
  619. dispatchItem.m_shaderResourceGroups[numSrgs++] = m_cullShaderResourceGroup->GetRHIShaderResourceGroup();
  620. dispatchItem.m_shaderResourceGroups[numSrgs++] = m_sceneShaderResourceGroup->GetRHIShaderResourceGroup();
  621. for (const auto& srg : m_indirectCommandsShaderResourceGroups)
  622. {
  623. dispatchItem.m_shaderResourceGroups[numSrgs++] = srg->GetRHIShaderResourceGroup();
  624. }
  625. // Submit the dispatch in an indirect manner.
  626. // Not really needed but it tests the indirect dispatch code.
  627. RHI::DispatchIndirect dispatchArgs(1, m_indirectDispatchBufferView, 0);
  628. dispatchItem.m_arguments = dispatchArgs;
  629. dispatchItem.m_pipelineState = m_cullPipelineState.get();
  630. dispatchItem.m_shaderResourceGroupCount = static_cast<uint8_t>(numSrgs);
  631. commandList->Submit(dispatchItem);
  632. };
  633. m_scopeProducers.emplace_back(
  634. aznew RHI::ScopeProducerFunction<
  635. ScopeData,
  636. decltype(prepareFunction),
  637. decltype(compileFunction),
  638. decltype(executeFunction)>(
  639. RHI::ScopeId{ "IndirecDispatchScope" },
  640. ScopeData{},
  641. prepareFunction,
  642. compileFunction,
  643. executeFunction));
  644. }
  645. void IndirectRenderingExampleComponent::CreateDrawingScope()
  646. {
  647. // Scope responsible for drawing the primitives in an indirect manner
  648. // using an indirect buffer that was populated by a compute shader.
  649. RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
  650. uint32_t maxIndirectDrawCount = device->GetLimits().m_maxIndirectDrawCount;
  651. const auto prepareFunction = [this, maxIndirectDrawCount](RHI::FrameGraphInterface frameGraph, [[maybe_unused]] ScopeData& scopeData)
  652. {
  653. {
  654. // Binds the swap chain as a color attachment.
  655. RHI::ImageScopeAttachmentDescriptor descriptor;
  656. descriptor.m_attachmentId = m_outputAttachmentId;
  657. descriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Load;
  658. frameGraph.UseColorAttachment(descriptor);
  659. }
  660. {
  661. // Binds the transient depth attachment
  662. AZ::RHI::ImageScopeAttachmentDescriptor dsDesc;
  663. dsDesc.m_attachmentId = IndirectRendering::DepthBufferAttachmentId;
  664. dsDesc.m_imageViewDescriptor.m_overrideFormat = AZ::RHI::Format::D32_FLOAT;
  665. dsDesc.m_loadStoreAction.m_clearValue = AZ::RHI::ClearValue::CreateDepth(1.f);
  666. dsDesc.m_loadStoreAction.m_loadAction = AZ::RHI::AttachmentLoadAction::Clear;
  667. frameGraph.UseDepthStencilAttachment(dsDesc, AZ::RHI::ScopeAttachmentAccess::ReadWrite);
  668. }
  669. if (m_deviceSupportsCountBuffer)
  670. {
  671. // Count buffer.
  672. RHI::BufferScopeAttachmentDescriptor descriptor;
  673. descriptor.m_attachmentId = IndirectRendering::CountBufferAttachmentId;
  674. descriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Load;
  675. descriptor.m_loadStoreAction.m_storeAction = RHI::AttachmentStoreAction::Store;
  676. descriptor.m_bufferViewDescriptor = RHI::BufferViewDescriptor::CreateStructured(
  677. 0,
  678. static_cast<uint32_t>(m_resetCounterBuffer->GetDescriptor().m_byteCount / sizeof(uint32_t)),
  679. sizeof(uint32_t));
  680. frameGraph.UseAttachment(descriptor, RHI::ScopeAttachmentAccess::Read, RHI::ScopeAttachmentUsage::Indirect);
  681. }
  682. {
  683. // Indirect Buffer with the culled commands.
  684. RHI::BufferScopeAttachmentDescriptor descriptor;
  685. descriptor.m_attachmentId = IndirectRendering::CulledIndirectBufferAttachmentId;
  686. descriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Load;
  687. descriptor.m_loadStoreAction.m_storeAction = RHI::AttachmentStoreAction::Store;
  688. descriptor.m_bufferViewDescriptor = RHI::BufferViewDescriptor::CreateStructured(
  689. 0,
  690. m_numObjects,
  691. m_indirectDrawBufferSignature->GetByteStride());
  692. frameGraph.UseAttachment(descriptor, RHI::ScopeAttachmentAccess::Read, RHI::ScopeAttachmentUsage::Indirect);
  693. }
  694. frameGraph.SetEstimatedItemCount(uint32_t(std::ceil(m_numObjects/ float(maxIndirectDrawCount))));
  695. };
  696. RHI::EmptyCompileFunction<ScopeData> compileFunction;
  697. const auto executeFunction = [this, maxIndirectDrawCount](const RHI::FrameGraphExecuteContext& context, [[maybe_unused]] const ScopeData& scopeData)
  698. {
  699. RHI::CommandList* commandList = context.GetCommandList();
  700. // Set persistent viewport and scissor state.
  701. commandList->SetViewports(&m_viewport, 1);
  702. commandList->SetScissors(&m_scissor, 1);
  703. const RHI::ShaderResourceGroup* shaderResourceGroups[] = { m_sceneShaderResourceGroup->GetRHIShaderResourceGroup() };
  704. // In case multi indirect drawing is not supported
  705. // we need to emit multiple indirect draw calls.
  706. m_drawIndirect.m_countBufferByteOffset = 0;
  707. for (uint32_t i = 0;
  708. i < m_numObjects;
  709. i += maxIndirectDrawCount, m_drawIndirect.m_countBufferByteOffset += sizeof(uint32_t))
  710. {
  711. // Offset the indirect buffer depending on the number of indirect draw calls that we can do.
  712. m_drawIndirect.m_maxSequenceCount = AZStd::min(m_numObjects - i, maxIndirectDrawCount);
  713. m_drawIndirect.m_indirectBufferByteOffset = i * m_indirectDrawBufferView.GetByteStride();
  714. m_drawIndirect.m_indirectBufferView = &m_indirectDrawBufferView;
  715. RHI::DrawItem drawItem;
  716. drawItem.m_arguments = m_drawIndirect;
  717. drawItem.m_pipelineState = m_drawPipelineState.get();
  718. drawItem.m_indexBufferView = &m_indexBufferViews[0];
  719. drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
  720. drawItem.m_shaderResourceGroups = shaderResourceGroups;
  721. drawItem.m_streamBufferViewCount = 2;
  722. drawItem.m_streamBufferViews = m_streamBufferViews.data();
  723. // Submit the indirect draw item.
  724. commandList->Submit(drawItem);
  725. }
  726. };
  727. m_scopeProducers.emplace_back(
  728. aznew RHI::ScopeProducerFunction<
  729. ScopeData,
  730. decltype(prepareFunction),
  731. decltype(compileFunction),
  732. decltype(executeFunction)>(
  733. RHI::ScopeId{ "IndirectDrawScope" },
  734. ScopeData{},
  735. prepareFunction,
  736. compileFunction,
  737. executeFunction));
  738. }
  739. void IndirectRenderingExampleComponent::Activate()
  740. {
  741. using namespace AZ;
  742. m_numObjects = s_maxNumberOfObjects / 2;
  743. RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
  744. const auto& deviceFeatures = device->GetFeatures();
  745. // Select the commands in the layout depending on the device capabilities.
  746. switch (deviceFeatures.m_indirectCommandTier)
  747. {
  748. case RHI::IndirectCommandTiers::Tier1:
  749. m_mode = SequenceType::DrawOnly;
  750. break;
  751. case RHI::IndirectCommandTiers::Tier2:
  752. m_mode = SequenceType::IARootConstantsDraw;
  753. break;
  754. default:
  755. AZ_Assert(false, "Indirect Rendering is not supported on this platform.");
  756. return;
  757. }
  758. m_deviceSupportsCountBuffer = deviceFeatures.m_indirectDrawCountBufferSupported;
  759. m_assetLoadManager = AZStd::make_unique<AZ::AssetCollectionAsyncLoader>();
  760. m_doneLoadingShaders = false;
  761. m_indirectDrawShaderVariantStableId = AZ::RPI::RootShaderVariantStableId;
  762. m_indirectDispatchShaderVariantStableId = AZ::RPI::RootShaderVariantStableId;
  763. // List of all assets this example needs.
  764. AZStd::vector<AssetCollectionAsyncLoader::AssetToLoadInfo> assetList = {
  765. {IndirectDrawShaderFilePath, azrtti_typeid<RPI::ShaderAsset>()},
  766. {IndirectDispatchShaderFilePath, azrtti_typeid<RPI::ShaderAsset>()},
  767. };
  768. // Configure the imgui progress list widget.
  769. auto onUserCancelledAction = [&]()
  770. {
  771. //AZ_TracePrintf(AsyncCompute::sampleName, "Cancelled by user.\n");
  772. m_assetLoadManager->Cancel();
  773. SampleComponentManagerRequestBus::Broadcast(&SampleComponentManagerRequests::Reset);
  774. };
  775. m_imguiProgressList.OpenPopup("Waiting For Assets...", "Assets pending for processing:", {}, onUserCancelledAction, true /*automaticallyCloseOnAction*/, "Cancel");
  776. AZStd::for_each(assetList.begin(), assetList.end(),
  777. [&](const AssetCollectionAsyncLoader::AssetToLoadInfo& item) { m_imguiProgressList.AddItem(item.m_assetPath); });
  778. m_imguiProgressList.AddItem(IndirectDrawVariantLabel);
  779. m_imguiProgressList.AddItem(IndirectDispatchVariantLabel);
  780. // Kickoff asynchronous asset loading, the activation will continue once all assets are available.
  781. m_assetLoadManager->LoadAssetsAsync(assetList, [&](AZStd::string_view assetName, [[maybe_unused]] bool success, size_t pendingAssetCount)
  782. {
  783. AZ_Error(LogName, success, "Error loading asset %s, a crash will occur when OnAllAssetsReadyActivate() is called!", assetName.data());
  784. m_imguiProgressList.RemoveItem(assetName);
  785. if (!pendingAssetCount && !m_doneLoadingShaders)
  786. {
  787. m_doneLoadingShaders = true;
  788. InitRequestsForShaderVariants();
  789. }
  790. });
  791. }
  792. void IndirectRenderingExampleComponent::OnAllAssetsReadyActivate()
  793. {
  794. InitInputAssemblyResources();
  795. InitShaderResources();
  796. InitIndirectRenderingResources();
  797. InitInstancesDataResources();
  798. // We use 3 scopes.
  799. // The first one is for reseting the count buffer to 0.
  800. // The second one is the compute scope in charge of culling.
  801. // The last one is the graphic scope in charge of rendering the culled primitives.
  802. if (m_deviceSupportsCountBuffer)
  803. {
  804. CreateResetCounterBufferScope();
  805. }
  806. CreateCullingScope();
  807. CreateDrawingScope();
  808. m_imguiSidebar.Activate();
  809. AZ::TickBus::Handler::BusConnect();
  810. AZ::RHI::RHISystemNotificationBus::Handler::BusConnect();
  811. AzFramework::WindowNotificationBus::Handler::BusConnect(m_windowContext->GetWindowHandle());
  812. }
  813. void IndirectRenderingExampleComponent::Deactivate()
  814. {
  815. m_inputAssemblyBufferPool = nullptr;
  816. m_shaderBufferPool = nullptr;
  817. m_instancesBufferPool = nullptr;
  818. m_copyBufferPool = nullptr;
  819. m_inputAssemblyBuffer = nullptr;
  820. m_sourceIndirectBuffer = nullptr;
  821. m_instancesDataBuffer = nullptr;
  822. m_resetCounterBuffer = nullptr;
  823. m_drawPipelineState = nullptr;
  824. m_cullPipelineState = nullptr;
  825. m_sceneShaderResourceGroup = nullptr;
  826. m_cullShaderResourceGroup = nullptr;
  827. m_indirectCommandsShaderResourceGroups.fill(nullptr);
  828. m_sourceIndirectBufferView = nullptr;
  829. m_instancesDataBufferView = nullptr;
  830. m_indirectDispatchWriter = nullptr;
  831. m_indirectDrawBufferSignature = nullptr;
  832. m_indirectDispatchBufferSignature = nullptr;
  833. m_instancesData.clear();
  834. m_imguiSidebar.Deactivate();
  835. AzFramework::WindowNotificationBus::Handler::BusDisconnect();
  836. AZ::RHI::RHISystemNotificationBus::Handler::BusDisconnect();
  837. AZ::TickBus::Handler::BusDisconnect();
  838. AZ::RPI::ShaderReloadNotificationBus::MultiHandler::BusDisconnect();
  839. m_windowContext = nullptr;
  840. m_scopeProducers.clear();
  841. }
  842. void IndirectRenderingExampleComponent::DrawSampleSettings()
  843. {
  844. ImGui::Spacing();
  845. ScriptableImGui::SliderFloat("Cull Offset", &m_cullOffset, 0.f, 1.f);
  846. if (ScriptableImGui::SliderInt("Num Objects", reinterpret_cast<int*>(&m_numObjects), 1, s_maxNumberOfObjects))
  847. {
  848. m_updateIndirectDispatchArguments = true;
  849. }
  850. m_imguiSidebar.End();
  851. }
  852. void IndirectRenderingExampleComponent::UpdateInstancesData(float deltaTime)
  853. {
  854. using namespace AZ;
  855. AZ::SimpleLcgRandom random;
  856. const float offsetBounds = 2.5f;
  857. for (uint32_t i = 0; i < m_numObjects; ++i)
  858. {
  859. m_instancesData[i].m_offset.SetX(m_instancesData[i].m_offset.GetX() + m_instancesData[i].m_velocity.GetX() * deltaTime);
  860. if (m_instancesData[i].m_offset.GetX() > offsetBounds)
  861. {
  862. m_instancesData[i].m_offset.SetX(-offsetBounds);
  863. m_instancesData[i].m_velocity.Set(GetRandomFloat(IndirectRendering::VelocityRange.GetX(), IndirectRendering::VelocityRange.GetY()));
  864. }
  865. }
  866. RHI::BufferMapRequest request(*m_instancesDataBuffer, 0, sizeof(InstanceData) * m_numObjects);
  867. RHI::BufferMapResponse response;
  868. m_instancesBufferPool->MapBuffer(request, response);
  869. if (response.m_data)
  870. {
  871. ::memcpy(response.m_data, m_instancesData.data(), request.m_byteCount);
  872. m_instancesBufferPool->UnmapBuffer(*m_instancesDataBuffer);
  873. }
  874. }
  875. void IndirectRenderingExampleComponent::OnWindowResized(uint32_t width, uint32_t height)
  876. {
  877. if (m_sceneShaderResourceGroup)
  878. {
  879. float aspectRatio = width / float(height);
  880. m_sceneShaderResourceGroup->SetConstant(m_sceneMatrixInputIndex, AZ::Matrix4x4::CreateScale(AZ::Vector3(1.f / aspectRatio, 1.f, 1.f)));
  881. m_sceneShaderResourceGroup->Compile();
  882. }
  883. }
  884. void IndirectRenderingExampleComponent::UpdateIndirectDispatchArguments()
  885. {
  886. m_indirectDispatchWriter->Seek(0);
  887. RHI::DispatchDirect args;
  888. args.m_threadsPerGroupX = IndirectRendering::ThreadGroupSize;
  889. args.m_totalNumberOfThreadsX = aznumeric_cast<uint16_t>(m_numObjects);
  890. m_indirectDispatchWriter->Dispatch(args);
  891. m_indirectDispatchWriter->Flush();
  892. }
  893. void IndirectRenderingExampleComponent::InitRequestsForShaderVariants()
  894. {
  895. {
  896. m_indirectDrawShader = LoadShader(*m_assetLoadManager.get(), IndirectDrawShaderFilePath, IndirectRendering::SampleName);
  897. AZ::RPI::ShaderReloadNotificationBus::MultiHandler::BusConnect(m_indirectDrawShader->GetAsset().GetId());
  898. // We use a shader option to tell the shader if the object index must be read from the vertex input or an inline constant.
  899. m_indirectDrawShaderOptionGroup = m_indirectDrawShader->CreateShaderOptionGroup();
  900. m_indirectDrawShaderOptionGroup.SetValue(AZ::Name("o_useRootConstants"), m_mode == SequenceType::IARootConstantsDraw ? AZ::Name("true") : AZ::Name("false"));
  901. //Kickoff the asynchronous loading of the variant tree.
  902. auto shaderVariant = m_indirectDrawShader->GetVariant(m_indirectDrawShaderOptionGroup.GetShaderVariantId());
  903. if (!shaderVariant.IsRootVariant())
  904. {
  905. m_indirectDrawShaderVariantStableId = shaderVariant.GetStableId();
  906. m_imguiProgressList.RemoveItem(IndirectDrawVariantLabel);
  907. AZ::RPI::ShaderReloadNotificationBus::MultiHandler::BusDisconnect(m_indirectDrawShader->GetAsset().GetId());
  908. }
  909. }
  910. {
  911. m_indirectDispatchShader = LoadShader(*m_assetLoadManager.get(), IndirectDispatchShaderFilePath, IndirectRendering::SampleName);
  912. AZ::RPI::ShaderReloadNotificationBus::MultiHandler::BusConnect(m_indirectDispatchShader->GetAsset().GetId());
  913. // We use a shader option to tell the shader which commands are included in the buffer and
  914. // another option to tell the shader if count buffers are supported.
  915. m_indirectDispatchShaderOptionGroup = m_indirectDispatchShader->CreateShaderOptionGroup();
  916. const char* optionValues[] = { "SequenceType::Draw", "SequenceType::IAInlineConstDraw" };
  917. m_indirectDispatchShaderOptionGroup.SetValue(AZ::Name("o_sequenceType"), AZ::Name(optionValues[static_cast<uint32_t>(m_mode)]));
  918. m_indirectDispatchShaderOptionGroup.SetValue(AZ::Name("o_countBufferSupported"), m_deviceSupportsCountBuffer ? AZ::Name("true") : AZ::Name("false"));
  919. //Kickoff the asynchronous loading of the variant tree.
  920. auto shaderVariant = m_indirectDispatchShader->GetVariant(m_indirectDispatchShaderOptionGroup.GetShaderVariantId());
  921. if (!shaderVariant.IsRootVariant())
  922. {
  923. m_indirectDispatchShaderVariantStableId = shaderVariant.GetStableId();
  924. m_imguiProgressList.RemoveItem(IndirectDispatchVariantLabel);
  925. AZ::RPI::ShaderReloadNotificationBus::MultiHandler::BusDisconnect(m_indirectDispatchShader->GetAsset().GetId());
  926. }
  927. }
  928. if ((m_indirectDrawShaderVariantStableId != AZ::RPI::RootShaderVariantStableId) &&
  929. (m_indirectDispatchShaderVariantStableId != AZ::RPI::RootShaderVariantStableId))
  930. {
  931. AZ_TracePrintf(LogName, "Got all variants already from InitRequestsForShaderVariantAssets()\n");
  932. OnAllAssetsReadyActivate();
  933. }
  934. }
  935. ///////////////////////////////////////////////////////////////////////
  936. // ShaderReloadNotificationBus overrides
  937. void IndirectRenderingExampleComponent::OnShaderVariantReinitialized(const AZ::RPI::ShaderVariant& shaderVariant)
  938. {
  939. //AZ_TracePrintf(LogName, "Got variant %s\n", shaderVariantAsset.GetHint().c_str());
  940. const AZ::Data::AssetId* shaderAssetId = AZ::RPI::ShaderReloadNotificationBus::GetCurrentBusId();
  941. if (*shaderAssetId == m_indirectDrawShader->GetAsset().GetId())
  942. {
  943. m_indirectDrawShaderVariantStableId = shaderVariant.GetStableId();
  944. m_imguiProgressList.RemoveItem(IndirectDrawVariantLabel);
  945. }
  946. else if (*shaderAssetId == m_indirectDispatchShader->GetAsset().GetId())
  947. {
  948. m_indirectDispatchShaderVariantStableId = shaderVariant.GetStableId();
  949. m_imguiProgressList.RemoveItem(IndirectDispatchVariantLabel);
  950. }
  951. if ((m_indirectDrawShaderVariantStableId != AZ::RPI::RootShaderVariantStableId) &&
  952. (m_indirectDispatchShaderVariantStableId != AZ::RPI::RootShaderVariantStableId))
  953. {
  954. AZ_TracePrintf(LogName, "Got all variants\n");
  955. OnAllAssetsReadyActivate();
  956. }
  957. }
  958. ///////////////////////////////////////////////////////////////////////
  959. } // namespace AtomSampleViewer