IndirectRenderingExampleComponent.cpp 54 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148
  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. }
  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 = aznew RHI::BufferPool();
  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(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 = aznew RHI::IndirectBufferSignature;
  304. RHI::IndirectBufferSignatureDescriptor signatureDescriptor;
  305. signatureDescriptor.m_layout = m_indirectDrawBufferLayout;
  306. signatureDescriptor.m_pipelineState = m_drawPipelineState.get();
  307. result = m_indirectDrawBufferSignature->Init(RHI::MultiDevice::AllDevices, signatureDescriptor);
  308. if (result != RHI::ResultCode::Success)
  309. {
  310. AZ_Assert(false, "Fail to initialize Indirect Buffer Signature");
  311. return;
  312. }
  313. m_sourceIndirectBuffer = aznew RHI::Buffer();
  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. RHI::Ptr<RHI::IndirectBufferWriter> indirectBufferWriter = aznew RHI::IndirectBufferWriter;
  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->BuildBufferView(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 = aznew RHI::Buffer();
  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 = aznew RHI::IndirectBufferSignature;
  401. signatureDescriptor = {};
  402. signatureDescriptor.m_layout = m_indirectDispatchBufferLayout;
  403. result = m_indirectDispatchBufferSignature->Init(RHI::MultiDevice::AllDevices, 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 = aznew RHI::IndirectBufferWriter;
  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 = aznew RHI::BufferPool();
  449. RHI::BufferPoolDescriptor bufferPoolDesc;
  450. bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::ShaderRead;
  451. bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Host;
  452. m_instancesBufferPool->Init(bufferPoolDesc);
  453. m_instancesDataBuffer = aznew RHI::Buffer();
  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->BuildBufferView(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 = aznew RHI::BufferPool();
  474. bufferPoolDesc = {};
  475. bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::CopyRead;
  476. bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Host;
  477. m_copyBufferPool->Init(bufferPoolDesc);
  478. m_resetCounterBuffer = aznew RHI::Buffer();
  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 auto* 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::DeviceCopyItem copyItem(m_copyDescriptor.GetDeviceCopyBufferDescriptor(context.GetDeviceIndex()));
  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(
  570. culledBufferAttachment, RHI::ScopeAttachmentAccess::ReadWrite, RHI::ScopeAttachmentStage::ComputeShader);
  571. if (m_deviceSupportsCountBuffer)
  572. {
  573. // The count buffer that we will be writing the final count of operations.
  574. RHI::BufferScopeAttachmentDescriptor countBufferAttachment;
  575. countBufferAttachment.m_attachmentId = IndirectRendering::CountBufferAttachmentId;
  576. countBufferAttachment.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Load;
  577. countBufferAttachment.m_bufferViewDescriptor = RHI::BufferViewDescriptor::CreateStructured(
  578. 0,
  579. static_cast<uint32_t>(m_resetCounterBuffer->GetDescriptor().m_byteCount / sizeof(uint32_t)),
  580. sizeof(uint32_t));
  581. frameGraph.UseShaderAttachment(
  582. countBufferAttachment, RHI::ScopeAttachmentAccess::ReadWrite, RHI::ScopeAttachmentStage::ComputeShader);
  583. }
  584. };
  585. const auto compileFunction = [this, maxIndirectDrawCount](const RHI::FrameGraphCompileContext& context, [[maybe_unused]] const ScopeData& scopeData)
  586. {
  587. const auto* culledBufferView = context.GetBufferView(RHI::AttachmentId{ IndirectRendering::CulledIndirectBufferAttachmentId });
  588. uint32_t sequenceTypeIndex = static_cast<uint32_t>(m_mode);
  589. auto& indirectCommandsSRG = m_indirectCommandsShaderResourceGroups[sequenceTypeIndex];
  590. indirectCommandsSRG->SetBufferView(m_cullingOutputIndirectBufferIndices[sequenceTypeIndex], culledBufferView);
  591. indirectCommandsSRG->Compile();
  592. if (m_deviceSupportsCountBuffer)
  593. {
  594. const auto* countBufferView = context.GetBufferView(RHI::AttachmentId{ IndirectRendering::CountBufferAttachmentId });
  595. m_cullShaderResourceGroup->SetBufferView(m_cullingCountBufferIndex, countBufferView);
  596. m_drawIndirect.m_countBuffer = countBufferView->GetBuffer();
  597. m_drawIndirect.m_countBufferByteOffset = 0;
  598. }
  599. // Update the cull area
  600. float cullScale = 0.75f;
  601. AZ::Vector2 cullPlane = AZ::Vector2(-m_cullOffset, m_cullOffset) * AZ::Vector2(cullScale) + AZ::Vector2(cullScale - 1.0f);
  602. m_cullShaderResourceGroup->SetConstant(m_cullingOffsetIndex, cullPlane);
  603. m_cullShaderResourceGroup->SetConstant(m_cullingNumCommandsIndex, m_numObjects);
  604. m_cullShaderResourceGroup->SetConstant(m_cullingMaxCommandsIndex, AZStd::min(m_numObjects, maxIndirectDrawCount));
  605. m_cullShaderResourceGroup->Compile();
  606. uint32_t stride = m_indirectDrawBufferSignature->GetByteStride();
  607. m_indirectDrawBufferView =
  608. {
  609. *(culledBufferView->GetBuffer()),
  610. *m_indirectDrawBufferSignature,
  611. 0,
  612. stride * m_numObjects,
  613. stride
  614. };
  615. };
  616. const auto executeFunction = [this](const RHI::FrameGraphExecuteContext& context, [[maybe_unused]] const ScopeData& scopeData)
  617. {
  618. RHI::CommandList* commandList = context.GetCommandList();
  619. RHI::DeviceDispatchItem dispatchItem;
  620. uint32_t numSrgs = 0;
  621. dispatchItem.m_shaderResourceGroups[numSrgs++] = m_cullShaderResourceGroup->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get();
  622. dispatchItem.m_shaderResourceGroups[numSrgs++] = m_sceneShaderResourceGroup->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get();
  623. for (const auto& srg : m_indirectCommandsShaderResourceGroups)
  624. {
  625. dispatchItem.m_shaderResourceGroups[numSrgs++] = srg->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get();
  626. }
  627. // Submit the dispatch in an indirect manner.
  628. // Not really needed but it tests the indirect dispatch code.
  629. RHI::DeviceDispatchIndirect dispatchArgs(
  630. 1, m_indirectDispatchBufferView.GetDeviceIndirectBufferView(context.GetDeviceIndex()), 0);
  631. dispatchItem.m_arguments = dispatchArgs;
  632. dispatchItem.m_pipelineState = m_cullPipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
  633. dispatchItem.m_shaderResourceGroupCount = static_cast<uint8_t>(numSrgs);
  634. commandList->Submit(dispatchItem);
  635. };
  636. m_scopeProducers.emplace_back(
  637. aznew RHI::ScopeProducerFunction<
  638. ScopeData,
  639. decltype(prepareFunction),
  640. decltype(compileFunction),
  641. decltype(executeFunction)>(
  642. RHI::ScopeId{ "IndirecDispatchScope" },
  643. ScopeData{},
  644. prepareFunction,
  645. compileFunction,
  646. executeFunction));
  647. }
  648. void IndirectRenderingExampleComponent::CreateDrawingScope()
  649. {
  650. // Scope responsible for drawing the primitives in an indirect manner
  651. // using an indirect buffer that was populated by a compute shader.
  652. RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
  653. uint32_t maxIndirectDrawCount = device->GetLimits().m_maxIndirectDrawCount;
  654. const auto prepareFunction = [this, maxIndirectDrawCount](RHI::FrameGraphInterface frameGraph, [[maybe_unused]] ScopeData& scopeData)
  655. {
  656. {
  657. // Binds the swap chain as a color attachment.
  658. RHI::ImageScopeAttachmentDescriptor descriptor;
  659. descriptor.m_attachmentId = m_outputAttachmentId;
  660. descriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Load;
  661. frameGraph.UseColorAttachment(descriptor);
  662. }
  663. {
  664. // Binds the transient depth attachment
  665. AZ::RHI::ImageScopeAttachmentDescriptor dsDesc;
  666. dsDesc.m_attachmentId = IndirectRendering::DepthBufferAttachmentId;
  667. dsDesc.m_imageViewDescriptor.m_overrideFormat = AZ::RHI::Format::D32_FLOAT;
  668. dsDesc.m_loadStoreAction.m_clearValue = AZ::RHI::ClearValue::CreateDepth(1.f);
  669. dsDesc.m_loadStoreAction.m_loadAction = AZ::RHI::AttachmentLoadAction::Clear;
  670. frameGraph.UseDepthStencilAttachment(
  671. dsDesc, AZ::RHI::ScopeAttachmentAccess::ReadWrite,
  672. RHI::ScopeAttachmentStage::EarlyFragmentTest | RHI::ScopeAttachmentStage::LateFragmentTest);
  673. }
  674. if (m_deviceSupportsCountBuffer)
  675. {
  676. // Count buffer.
  677. RHI::BufferScopeAttachmentDescriptor descriptor;
  678. descriptor.m_attachmentId = IndirectRendering::CountBufferAttachmentId;
  679. descriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Load;
  680. descriptor.m_loadStoreAction.m_storeAction = RHI::AttachmentStoreAction::Store;
  681. descriptor.m_bufferViewDescriptor = RHI::BufferViewDescriptor::CreateStructured(
  682. 0,
  683. static_cast<uint32_t>(m_resetCounterBuffer->GetDescriptor().m_byteCount / sizeof(uint32_t)),
  684. sizeof(uint32_t));
  685. frameGraph.UseAttachment(
  686. descriptor, RHI::ScopeAttachmentAccess::Read, RHI::ScopeAttachmentUsage::Indirect,
  687. RHI::ScopeAttachmentStage::DrawIndirect);
  688. }
  689. {
  690. // Indirect Buffer with the culled commands.
  691. RHI::BufferScopeAttachmentDescriptor descriptor;
  692. descriptor.m_attachmentId = IndirectRendering::CulledIndirectBufferAttachmentId;
  693. descriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Load;
  694. descriptor.m_loadStoreAction.m_storeAction = RHI::AttachmentStoreAction::Store;
  695. descriptor.m_bufferViewDescriptor = RHI::BufferViewDescriptor::CreateStructured(
  696. 0,
  697. m_numObjects,
  698. m_indirectDrawBufferSignature->GetByteStride());
  699. frameGraph.UseAttachment(
  700. descriptor, RHI::ScopeAttachmentAccess::Read, RHI::ScopeAttachmentUsage::Indirect,
  701. RHI::ScopeAttachmentStage::DrawIndirect);
  702. }
  703. frameGraph.SetEstimatedItemCount(uint32_t(std::ceil(m_numObjects/ float(maxIndirectDrawCount))));
  704. };
  705. RHI::EmptyCompileFunction<ScopeData> compileFunction;
  706. const auto executeFunction = [this, maxIndirectDrawCount](const RHI::FrameGraphExecuteContext& context, [[maybe_unused]] const ScopeData& scopeData)
  707. {
  708. RHI::CommandList* commandList = context.GetCommandList();
  709. // Set persistent viewport and scissor state.
  710. commandList->SetViewports(&m_viewport, 1);
  711. commandList->SetScissors(&m_scissor, 1);
  712. const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = {
  713. m_sceneShaderResourceGroup->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get()
  714. };
  715. // In case multi indirect drawing is not supported
  716. // we need to emit multiple indirect draw calls.
  717. m_drawIndirect.m_countBufferByteOffset = 0;
  718. for (uint32_t i = 0;
  719. i < m_numObjects;
  720. i += maxIndirectDrawCount, m_drawIndirect.m_countBufferByteOffset += sizeof(uint32_t))
  721. {
  722. // Offset the indirect buffer depending on the number of indirect draw calls that we can do.
  723. m_drawIndirect.m_maxSequenceCount = AZStd::min(m_numObjects - i, maxIndirectDrawCount);
  724. m_drawIndirect.m_indirectBufferByteOffset = i * m_indirectDrawBufferView.GetByteStride();
  725. m_drawIndirect.m_indirectBufferView = &m_indirectDrawBufferView;
  726. RHI::DeviceDrawItem drawItem;
  727. drawItem.m_arguments = AZ::RHI::DrawArguments(m_drawIndirect).GetDeviceDrawArguments(context.GetDeviceIndex());
  728. drawItem.m_pipelineState = m_drawPipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
  729. auto deviceIndexBufferView{m_indexBufferViews[0].GetDeviceIndexBufferView(context.GetDeviceIndex())};
  730. drawItem.m_indexBufferView = &deviceIndexBufferView;
  731. drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
  732. drawItem.m_shaderResourceGroups = shaderResourceGroups;
  733. drawItem.m_streamBufferViewCount = 2;
  734. AZStd::array<AZ::RHI::DeviceStreamBufferView, 2> deviceStreamBufferViews{
  735. m_streamBufferViews[0].GetDeviceStreamBufferView(context.GetDeviceIndex()),
  736. m_streamBufferViews[1].GetDeviceStreamBufferView(context.GetDeviceIndex())
  737. };
  738. drawItem.m_streamBufferViews = deviceStreamBufferViews.data();
  739. // Submit the indirect draw item.
  740. commandList->Submit(drawItem);
  741. }
  742. };
  743. m_scopeProducers.emplace_back(
  744. aznew RHI::ScopeProducerFunction<
  745. ScopeData,
  746. decltype(prepareFunction),
  747. decltype(compileFunction),
  748. decltype(executeFunction)>(
  749. RHI::ScopeId{ "IndirectDrawScope" },
  750. ScopeData{},
  751. prepareFunction,
  752. compileFunction,
  753. executeFunction));
  754. }
  755. void IndirectRenderingExampleComponent::Activate()
  756. {
  757. using namespace AZ;
  758. m_numObjects = s_maxNumberOfObjects / 2;
  759. RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
  760. const auto& deviceFeatures = device->GetFeatures();
  761. // Select the commands in the layout depending on the device capabilities.
  762. switch (deviceFeatures.m_indirectCommandTier)
  763. {
  764. case RHI::IndirectCommandTiers::Tier1:
  765. m_mode = SequenceType::DrawOnly;
  766. break;
  767. case RHI::IndirectCommandTiers::Tier2:
  768. m_mode = SequenceType::IARootConstantsDraw;
  769. break;
  770. default:
  771. AZ_Assert(false, "Indirect Rendering is not supported on this platform.");
  772. return;
  773. }
  774. m_deviceSupportsCountBuffer = deviceFeatures.m_indirectDrawCountBufferSupported;
  775. m_assetLoadManager = AZStd::make_unique<AZ::AssetCollectionAsyncLoader>();
  776. m_doneLoadingShaders = false;
  777. m_indirectDrawShaderVariantStableId = AZ::RPI::RootShaderVariantStableId;
  778. m_indirectDispatchShaderVariantStableId = AZ::RPI::RootShaderVariantStableId;
  779. // List of all assets this example needs.
  780. AZStd::vector<AssetCollectionAsyncLoader::AssetToLoadInfo> assetList = {
  781. {IndirectDrawShaderFilePath, azrtti_typeid<RPI::ShaderAsset>()},
  782. {IndirectDispatchShaderFilePath, azrtti_typeid<RPI::ShaderAsset>()},
  783. };
  784. // Configure the imgui progress list widget.
  785. auto onUserCancelledAction = [&]()
  786. {
  787. //AZ_TracePrintf(AsyncCompute::sampleName, "Cancelled by user.\n");
  788. m_assetLoadManager->Cancel();
  789. SampleComponentManagerRequestBus::Broadcast(&SampleComponentManagerRequests::Reset);
  790. };
  791. m_imguiProgressList.OpenPopup("Waiting For Assets...", "Assets pending for processing:", {}, onUserCancelledAction, true /*automaticallyCloseOnAction*/, "Cancel");
  792. AZStd::for_each(assetList.begin(), assetList.end(),
  793. [&](const AssetCollectionAsyncLoader::AssetToLoadInfo& item) { m_imguiProgressList.AddItem(item.m_assetPath); });
  794. m_imguiProgressList.AddItem(IndirectDrawVariantLabel);
  795. m_imguiProgressList.AddItem(IndirectDispatchVariantLabel);
  796. // Kickoff asynchronous asset loading, the activation will continue once all assets are available.
  797. m_assetLoadManager->LoadAssetsAsync(assetList, [&](AZStd::string_view assetName, [[maybe_unused]] bool success, size_t pendingAssetCount)
  798. {
  799. AZ_Error(LogName, success, "Error loading asset %s, a crash will occur when OnAllAssetsReadyActivate() is called!", assetName.data());
  800. m_imguiProgressList.RemoveItem(assetName);
  801. if (!pendingAssetCount && !m_doneLoadingShaders)
  802. {
  803. m_doneLoadingShaders = true;
  804. InitRequestsForShaderVariants();
  805. }
  806. });
  807. }
  808. void IndirectRenderingExampleComponent::OnAllAssetsReadyActivate()
  809. {
  810. InitInputAssemblyResources();
  811. InitShaderResources();
  812. InitIndirectRenderingResources();
  813. InitInstancesDataResources();
  814. // We use 3 scopes.
  815. // The first one is for reseting the count buffer to 0.
  816. // The second one is the compute scope in charge of culling.
  817. // The last one is the graphic scope in charge of rendering the culled primitives.
  818. if (m_deviceSupportsCountBuffer)
  819. {
  820. CreateResetCounterBufferScope();
  821. }
  822. CreateCullingScope();
  823. CreateDrawingScope();
  824. m_imguiSidebar.Activate();
  825. AZ::TickBus::Handler::BusConnect();
  826. AZ::RHI::RHISystemNotificationBus::Handler::BusConnect();
  827. AzFramework::WindowNotificationBus::Handler::BusConnect(m_windowContext->GetWindowHandle());
  828. }
  829. void IndirectRenderingExampleComponent::Deactivate()
  830. {
  831. m_inputAssemblyBufferPool = nullptr;
  832. m_shaderBufferPool = nullptr;
  833. m_instancesBufferPool = nullptr;
  834. m_copyBufferPool = nullptr;
  835. m_inputAssemblyBuffer = nullptr;
  836. m_sourceIndirectBuffer = nullptr;
  837. m_instancesDataBuffer = nullptr;
  838. m_resetCounterBuffer = nullptr;
  839. m_drawPipelineState = nullptr;
  840. m_cullPipelineState = nullptr;
  841. m_sceneShaderResourceGroup = nullptr;
  842. m_cullShaderResourceGroup = nullptr;
  843. m_indirectCommandsShaderResourceGroups.fill(nullptr);
  844. m_sourceIndirectBufferView = nullptr;
  845. m_instancesDataBufferView = nullptr;
  846. m_indirectDispatchWriter = nullptr;
  847. m_indirectDrawBufferSignature = nullptr;
  848. m_indirectDispatchBufferSignature = nullptr;
  849. m_instancesData.clear();
  850. m_imguiSidebar.Deactivate();
  851. AzFramework::WindowNotificationBus::Handler::BusDisconnect();
  852. AZ::RHI::RHISystemNotificationBus::Handler::BusDisconnect();
  853. AZ::TickBus::Handler::BusDisconnect();
  854. AZ::RPI::ShaderReloadNotificationBus::MultiHandler::BusDisconnect();
  855. m_windowContext = nullptr;
  856. m_scopeProducers.clear();
  857. }
  858. void IndirectRenderingExampleComponent::DrawSampleSettings()
  859. {
  860. ImGui::Spacing();
  861. ScriptableImGui::SliderFloat("Cull Offset", &m_cullOffset, 0.f, 1.f);
  862. if (ScriptableImGui::SliderInt("Num Objects", reinterpret_cast<int*>(&m_numObjects), 1, s_maxNumberOfObjects))
  863. {
  864. m_updateIndirectDispatchArguments = true;
  865. }
  866. m_imguiSidebar.End();
  867. }
  868. void IndirectRenderingExampleComponent::UpdateInstancesData(float deltaTime)
  869. {
  870. using namespace AZ;
  871. AZ::SimpleLcgRandom random;
  872. const float offsetBounds = 2.5f;
  873. for (uint32_t i = 0; i < m_numObjects; ++i)
  874. {
  875. m_instancesData[i].m_offset.SetX(m_instancesData[i].m_offset.GetX() + m_instancesData[i].m_velocity.GetX() * deltaTime);
  876. if (m_instancesData[i].m_offset.GetX() > offsetBounds)
  877. {
  878. m_instancesData[i].m_offset.SetX(-offsetBounds);
  879. m_instancesData[i].m_velocity.Set(GetRandomFloat(IndirectRendering::VelocityRange.GetX(), IndirectRendering::VelocityRange.GetY()));
  880. }
  881. }
  882. RHI::BufferMapRequest request(*m_instancesDataBuffer, 0, sizeof(InstanceData) * m_numObjects);
  883. RHI::BufferMapResponse response;
  884. m_instancesBufferPool->MapBuffer(request, response);
  885. if (!response.m_data.empty())
  886. {
  887. for(auto& [_, responseData] : response.m_data)
  888. {
  889. ::memcpy(responseData, m_instancesData.data(), request.m_byteCount);
  890. }
  891. m_instancesBufferPool->UnmapBuffer(*m_instancesDataBuffer);
  892. }
  893. }
  894. void IndirectRenderingExampleComponent::OnWindowResized(uint32_t width, uint32_t height)
  895. {
  896. if (m_sceneShaderResourceGroup)
  897. {
  898. float aspectRatio = width / float(height);
  899. m_sceneShaderResourceGroup->SetConstant(m_sceneMatrixInputIndex, AZ::Matrix4x4::CreateScale(AZ::Vector3(1.f / aspectRatio, 1.f, 1.f)));
  900. m_sceneShaderResourceGroup->Compile();
  901. }
  902. }
  903. void IndirectRenderingExampleComponent::UpdateIndirectDispatchArguments()
  904. {
  905. m_indirectDispatchWriter->Seek(0);
  906. RHI::DispatchDirect args;
  907. args.m_threadsPerGroupX = IndirectRendering::ThreadGroupSize;
  908. args.m_totalNumberOfThreadsX = aznumeric_cast<uint16_t>(m_numObjects);
  909. m_indirectDispatchWriter->Dispatch(args);
  910. m_indirectDispatchWriter->Flush();
  911. }
  912. void IndirectRenderingExampleComponent::InitRequestsForShaderVariants()
  913. {
  914. {
  915. m_indirectDrawShader = LoadShader(*m_assetLoadManager.get(), IndirectDrawShaderFilePath, IndirectRendering::SampleName);
  916. AZ::RPI::ShaderReloadNotificationBus::MultiHandler::BusConnect(m_indirectDrawShader->GetAsset().GetId());
  917. // We use a shader option to tell the shader if the object index must be read from the vertex input or an inline constant.
  918. m_indirectDrawShaderOptionGroup = m_indirectDrawShader->CreateShaderOptionGroup();
  919. m_indirectDrawShaderOptionGroup.SetValue(AZ::Name("o_useRootConstants"), m_mode == SequenceType::IARootConstantsDraw ? AZ::Name("true") : AZ::Name("false"));
  920. //Kickoff the asynchronous loading of the variant tree.
  921. auto shaderVariant = m_indirectDrawShader->GetVariant(m_indirectDrawShaderOptionGroup.GetShaderVariantId());
  922. if (!shaderVariant.IsRootVariant())
  923. {
  924. m_indirectDrawShaderVariantStableId = shaderVariant.GetStableId();
  925. m_imguiProgressList.RemoveItem(IndirectDrawVariantLabel);
  926. AZ::RPI::ShaderReloadNotificationBus::MultiHandler::BusDisconnect(m_indirectDrawShader->GetAsset().GetId());
  927. }
  928. }
  929. {
  930. m_indirectDispatchShader = LoadShader(*m_assetLoadManager.get(), IndirectDispatchShaderFilePath, IndirectRendering::SampleName);
  931. AZ::RPI::ShaderReloadNotificationBus::MultiHandler::BusConnect(m_indirectDispatchShader->GetAsset().GetId());
  932. // We use a shader option to tell the shader which commands are included in the buffer and
  933. // another option to tell the shader if count buffers are supported.
  934. m_indirectDispatchShaderOptionGroup = m_indirectDispatchShader->CreateShaderOptionGroup();
  935. const char* optionValues[] = { "SequenceType::Draw", "SequenceType::IAInlineConstDraw" };
  936. m_indirectDispatchShaderOptionGroup.SetValue(AZ::Name("o_sequenceType"), AZ::Name(optionValues[static_cast<uint32_t>(m_mode)]));
  937. m_indirectDispatchShaderOptionGroup.SetValue(AZ::Name("o_countBufferSupported"), m_deviceSupportsCountBuffer ? AZ::Name("true") : AZ::Name("false"));
  938. //Kickoff the asynchronous loading of the variant tree.
  939. auto shaderVariant = m_indirectDispatchShader->GetVariant(m_indirectDispatchShaderOptionGroup.GetShaderVariantId());
  940. if (!shaderVariant.IsRootVariant())
  941. {
  942. m_indirectDispatchShaderVariantStableId = shaderVariant.GetStableId();
  943. m_imguiProgressList.RemoveItem(IndirectDispatchVariantLabel);
  944. AZ::RPI::ShaderReloadNotificationBus::MultiHandler::BusDisconnect(m_indirectDispatchShader->GetAsset().GetId());
  945. }
  946. }
  947. if ((m_indirectDrawShaderVariantStableId != AZ::RPI::RootShaderVariantStableId) &&
  948. (m_indirectDispatchShaderVariantStableId != AZ::RPI::RootShaderVariantStableId))
  949. {
  950. AZ_TracePrintf(LogName, "Got all variants already from InitRequestsForShaderVariantAssets()\n");
  951. OnAllAssetsReadyActivate();
  952. }
  953. }
  954. ///////////////////////////////////////////////////////////////////////
  955. // ShaderReloadNotificationBus overrides
  956. void IndirectRenderingExampleComponent::OnShaderVariantReinitialized(const AZ::RPI::ShaderVariant& shaderVariant)
  957. {
  958. //AZ_TracePrintf(LogName, "Got variant %s\n", shaderVariantAsset.GetHint().c_str());
  959. const AZ::Data::AssetId* shaderAssetId = AZ::RPI::ShaderReloadNotificationBus::GetCurrentBusId();
  960. if (*shaderAssetId == m_indirectDrawShader->GetAsset().GetId())
  961. {
  962. m_indirectDrawShaderVariantStableId = shaderVariant.GetStableId();
  963. m_imguiProgressList.RemoveItem(IndirectDrawVariantLabel);
  964. }
  965. else if (*shaderAssetId == m_indirectDispatchShader->GetAsset().GetId())
  966. {
  967. m_indirectDispatchShaderVariantStableId = shaderVariant.GetStableId();
  968. m_imguiProgressList.RemoveItem(IndirectDispatchVariantLabel);
  969. }
  970. if ((m_indirectDrawShaderVariantStableId != AZ::RPI::RootShaderVariantStableId) &&
  971. (m_indirectDispatchShaderVariantStableId != AZ::RPI::RootShaderVariantStableId))
  972. {
  973. AZ_TracePrintf(LogName, "Got all variants\n");
  974. OnAllAssetsReadyActivate();
  975. }
  976. }
  977. ///////////////////////////////////////////////////////////////////////
  978. } // namespace AtomSampleViewer